NMEA(xxGGA)报文解析(FPGA实现)

2023-10-13 19:50

本文主要是介绍NMEA(xxGGA)报文解析(FPGA实现),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近接触GPS,需要使用FPGA进行NMEA报文的解析,以获得经纬度和时间信息,我选用的报文是xxGGA,包含GPGGA(GPS系统的)、GBGGA(北斗系统的)、GLGGA(GLONASS系统的)、GAGGA(伽利略系统的),GNGGA(任意GNSS系统组合)。他们的格式完全相同,不同之处仅在于报文头,xxGGA报文格式如下

$xxGGA,time,lat,NS,lon,EW,quality,numSV,HDOP,alt,altUnit,sep,sepUnit,diffAge,diffStation*cs<CR><LF>

我们需要关注的数据域如下

time:UTC时间,hhmmss.ss格式,如132253.27表示UTC时间 13时22分53.27秒,需要注意的是.ss表示秒的小数域(2位小数),而非毫秒

lat:纬度,ddmm.mmmm格式,如3124.73251表示 31度24.73251分,1度=60分

NS:指示南北半球,北半球为‘N’,南半球为‘S’

lon:经度,dddmm.mmmmm格式,如13424.73251表示 134度24.73251分

EW:指示东西半球,东半球为‘E’,西半球为‘W’

alt:海拔,-9999.9~9999.9

altUnit:海拔单位,‘M’表示以 米 为单位

例如:

$GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,*5B

表示 UTC时间 09 时 27 分 25.00 秒,北纬 47 度 17.11399 分,东经 8 度 33.91590 分,海拔 499.6 米

------------------------------------------------------------------------------分割线---------------------------------------------------------------------------------------------

本xxGGA解析模块使用UART串口数据,以UART模块发出的rx_done信号驱动。

xxGGA报文解析模块:

/******************************FILE HEAD*********************************** file_name         : parseGGA.v* function          : 解析xxGGA报文,获取UTC时间、经纬度、海拔* author            : 今朝无言* version & date    : 2021/10/14 & v1.0*************************************************************************/
module parseGGA(input				rx_done_toUart,	//整个模块由rx_done_toUart驱动input		[7:0]	rddat_toUart,output	reg 		rx_done,		//接收指令结束,上升沿对齐'\n'字符出现时刻,下降沿对齐$xxGGA后面的','的出现时刻的下一时刻output	reg	[4:0]	hh,				//UTC时间,整数,时  0~24output	reg	[5:0]	mm,				//UTC时间,整数,分  0~59output	reg	[5:0]	ss,				//UTC时间,整数,秒  0~59output	reg	[6:0]	ss2,			//UTC时间,小数,秒  2位小数,0~99output	reg	[6:0]	lat,			//纬度  整数部分,度  0~90output	reg	[5:0]	lat2,			//纬度  整数部分,分  0~59output	reg	[16:0]	lat3,			//纬度  小数部分,分  5位小数,0~99999output	reg			NS,				//区分南北纬,北纬标为1,南纬标为0output	reg	[7:0]	lon,			//经度  整数部分,度  0~180output	reg	[5:0]	lon2,			//经度  整数部分,分  0~59output	reg	[16:0]	lon3,			//经度  小数部分,分  5位小数,0~99999output	reg			EW,				//区分东西经,东经标为1,西经标为0output	reg [13:0]	alt,			//海拔  整数部分,moutput	reg [3:0]	alt2			//海拔  小数部分,一位小数  0~9
);
//xxGGA格式: $xxGGA,time,lat,NS,lon,EW,quality,numSV,HDOP,alt,altUnit,sep,sepUnit,diffAge,diffStation*cs<CR><LF>
//time格式: hhmmss.ss
//lat格式: ddmm.mmmmm
//lon格式: dddmm.mmmmm
//alt格式: numeric,一位小数reg		[4:0]	cntField;		//当前读取第几个域,以','分隔
reg 	[3:0]	cntChar;		//当前读取域中的第几个字符reg				start	= 1'b0;	//NMEA报文的接收标志,以$开始,到\n结束
reg		[7:0]	charBuffer;
reg		[1:0]	corrNum	= 2'd0;	//比对是否为xxGGA,当corrNum=3时,表示"GGA"字符通过测试,该条报文即xxGGAwire	[3:0]	num;			//若charBuffer为字符0~9,则将之转换为数字0~9
wire			isnum;reg				afterDot;		//判断是否是"."后面的数字,在解析海拔时用到//将字符0~9转换为数字0~9
Char2Num Char2Num_inst(.Char(charBuffer),.Num(num),.isNum(isnum)
);always @(posedge rx_done_toUart) begincharBuffer	<= rddat_toUart;//-----------------------接收NMEA报文数据-----------------------------if(rddat_toUart == "$") begin			//接收到$,标志着NMEA数据的起始start		<= 1'b1;cntField	<= 5'd0;cntChar		<= 4'd0;corrNum		<= 2'd0;endelse if(start) beginif(rddat_toUart == "\n") begin		//收到\n,标志NMEA报文结束start		<= 1'b0;rx_done		<= 1'b1;endelse if(rddat_toUart == "," || rddat_toUart == "*") begin	//收到','或'*',为域的分隔符cntField	<= cntField + 1'b1;cntChar		<= 4'd0;endelse begin							//收到其他字符cntChar		<= cntChar + 1'b1;endendelse beginstart		<= 1'b0;cntField	<= 5'd0;cntChar		<= 4'd0;corrNum		<= 2'd0;end//------------------------判断是否为xxGGA----------------------------if(cntField == 5'd0) beginif(cntChar == 4'd3 && charBuffer == "G") begincorrNum <= corrNum + 1'b1;endelse if(cntChar == 4'd4 && charBuffer == "G") begincorrNum <= corrNum + 1'b1;endelse if(cntChar == 4'd5 && charBuffer == "A") begincorrNum <= corrNum + 1'b1;endendif(corrNum == 2'd3) begin	//检测到是"xxGGA",开启解析rx_done <= 1'b0;corrNum	<= 2'b0;end//---------------------------解析xxGGA------------------------------if(rx_done == 1'b0) begin//解析UTC时间if(cntField == 5'd1) beginif(cntChar == 4'd1) begin		//UTC-hhhh	<= num*4'd10;endelse if(cntChar == 4'd2) beginhh	<= hh + num;endelse if(cntChar == 4'd3) begin	//UTC-mmmm	<= num*4'd10;endelse if(cntChar == 4'd4) beginmm	<= mm + num;endelse if(cntChar == 4'd5) begin	//UTC-ssss	<= num*4'd10;endelse if(cntChar == 4'd6) beginss	<= ss + num;endelse if(cntChar == 4'd8) begin	//UTC-.ssss2	<= num*4'd10;endelse if(cntChar == 4'd9) beginss2	<= ss2 + num;endend//解析纬度if(cntField == 5'd2) beginif(cntChar == 4'd1) begin		//lat-ddlat		<= num*4'd10;endelse if(cntChar == 4'd2) beginlat		<= lat + num;endelse if(cntChar == 4'd3) begin	//lat-mmlat2	<= num*4'd10;endelse if(cntChar == 4'd4) beginlat2	<= lat2 + num;endelse if(cntChar == 4'd6) begin	//lat-.mmmmmlat3	<= num;endelse if(cntChar == 4'd7 || cntChar == 4'd8 ||cntChar == 4'd9 || cntChar == 4'd10) beginlat3	<= lat3*4'd10 + num;endendif(cntField == 5'd3 && cntChar == 4'd1) begin	//NSif(charBuffer == "N") beginNS	<= 1'b1;endelse beginNS	<= 1'b0;endend//解析经度if(cntField == 5'd4) beginif(cntChar == 4'd1) begin		//lon-dddlon		<= num;endelse if(cntChar == 4'd2 || cntChar == 4'd3) beginlon		<= lon*4'd10 + num;endelse if(cntChar == 4'd4) begin	//lon-mmlon2	<= num*4'd10;endelse if(cntChar == 4'd5) beginlon2	<= lon2 + num;endelse if(cntChar == 4'd7) begin	//lon-.mmmmmlon3	<= num;endelse if(cntChar == 4'd8 || cntChar == 4'd9 ||cntChar == 4'd10 || cntChar == 4'd11) beginlon3	<= lon3*4'd10 + num;endendif(cntField == 5'd5 && cntChar == 4'd1) begin	//EWif(charBuffer == "E") beginEW	<= 1'b1;endelse beginEW	<= 1'b0;endend//解析海拔if(cntField == 5'd9) beginif(cntChar == 4'd1) beginalt			<= num;afterDot	<= 1'b0;endelse if(charBuffer==".") beginafterDot	<= 1'b1;alt2		<= 4'd0;endelse beginif(~afterDot) beginalt		<= alt*4'd10 + num;		//alt-MMMendelse beginalt2	<= alt2*4'd10 +num;		//alt-.Mendendendend
endendmodule
//END OF parseGGA.v FILE***************************************************

字符-数字转换模块:

/******************************FILE HEAD*********************************** file_name         : Char2Num.v* function          : 若Char为字符0~9,将之转化为数字0~9* author            : 今朝无言* version & date    : 2021/10/14 & v1.0*************************************************************************/
module Char2Num(input 		[7:0]	Char,output		[3:0]	Num,output	reg			isNum
);always@(*)begincase(Char)"0": isNum <= 1;"1": isNum <= 1;"2": isNum <= 1;"3": isNum <= 1;"4": isNum <= 1;"5": isNum <= 1;"6": isNum <= 1;"7": isNum <= 1;"8": isNum <= 1;"9": isNum <= 1;default: isNum <= 0;endcase
endassign Num = isNum? Char - "0" : 4'hff;endmodule
//END OF Char2Num.v FILE***************************************************

testbench:

/******************************FILE HEAD*********************************** file_name         : parseGGA_tb.v* function          : 解析xxGGA报文,获取UTC时间、经纬度、海拔* author            : 今朝无言* version & date    : 2021/10/14 & v1.0*************************************************************************/
`default_nettype none
`timescale 1ns/1psmodule parseGGA_tb;reg	[0:75*8-1]data = {"$GNGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,*5B",8'd13,8'd10}; //\r\n, \r=13,\n=10reg				rx_done_toUart;	//整个模块由rx_done_toUart驱动
reg		[7:0]	rddat_toUart;wire 			rx_done;		//接收指令结束,上升沿对齐'\n'字符出现时刻,下降沿对齐$xxGGA后面的','的出现时刻wire	[4:0]	hh;				//UTC时间,整数,时  0~24
wire	[5:0]	mm;				//UTC时间,整数,分  0~59
wire	[5:0]	ss;				//UTC时间,整数,秒  0~59
wire	[6:0]	ss2;			//UTC时间,小数,秒  2位小数,0~99wire	[6:0]	lat;			//纬度  整数部分,度  0~90
wire	[5:0]	lat2;			//纬度  整数部分,分  0~59
wire	[16:0]	lat3;			//纬度  小数部分,分  5位小数,0~99999
wire			NS;				//区分南北纬,北纬标为1,南纬标为0wire	[7:0]	lon;			//经度  整数部分,度  0~180
wire	[5:0]	lon2;			//经度  整数部分,分  0~59
wire	[16:0]	lon3;			//经度  小数部分,分  5位小数,0~99999
wire			EW;				//区分东西经,东经标为1,西经标为0wire	[13:0]	alt;			//海拔  整数部分,m
wire	[3:0]	alt2;			//海拔  小数部分,一位小数  0~9reg		[9:0]	i;
initial beginrx_done_toUart	<= 0;#50;for(i=0;i<=74*8;i=i+8)beginrddat_toUart	<= {data[i],data[i+1],data[i+2],data[i+3],data[i+4],data[i+5],data[i+6],data[i+7]};#5;rx_done_toUart	<= 1;#50;rx_done_toUart	<=0;#50;end#200;$stop;
end//解析xxGGA报文
parseGGA parseGGA_inst(.rx_done_toUart	(rx_done_toUart),.rddat_toUart	(rddat_toUart),.rx_done		(rx_done),.hh				(hh),.mm				(mm),.ss				(ss),.ss2			(ss2),.lat			(lat),.lat2			(lat2),.lat3			(lat3),.NS				(NS),.lon			(lon),.lon2			(lon2),.lon3			(lon3),.EW				(EW),.alt			(alt),.alt2			(alt2)
);endmodule
//END OF parseGGA_tb.v FILE***************************************************

ModelSim仿真结果:

在这里插入图片描述

这篇关于NMEA(xxGGA)报文解析(FPGA实现)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/205498

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现