电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
从NMEA0183到GNSS定位数据获取
分 享
扫描二维码分享
从NMEA0183到GNSS定位数据获取
GNSS
450c4aed63d8393c
关注
发布时间: 2020-08-24
丨
阅读: 502
作者:良知犹存 转载授权以及围观:欢迎添加微信公众号:Conscience_Remains 总述 GPS我们都知道,一种用来全球定位的系统,后来俄罗斯推出了格洛纳斯定位系统,中国推出了北斗定位,欧盟有伽利略,印度与日本也有有发展。所以后来把覆盖全球的自主地利空间定位的卫星系统成为GNSS。 现在卫星定位那么热,那么作为一个嵌入式人怎么获取这些数据为我们所用呢?下面就听作者一一道来。 通常GNSS定位的功能有: 精确定时:广泛应用在天文台、通信系统基站、电视台中 工程施工:道路、桥梁、隧道的施工中大量采用GPS设备进行工程测量 勘探测绘:野外勘探及城区规划中都有用到 导航: 武器导航:精确制导**、巡航** 车辆导航:车辆调度、监控系统 船舶导航:远洋导航、港口/内河引水 飞机导航:航线导航、进场着陆控制 星际导航:卫星轨道定位 个人导航:个人旅游及野外探险 定位: 车辆防盗系统 手机,PDA,PPC等通信移动设备防盗,电子地图,定位系统 儿童及特殊人群的防走失系统 精准农业: 农机具导航、自动驾驶,土地高精度平整 提供时间数据:用于给电信基站、电视发射站等提供精确同步时钟源 维基百科 从上面可知GNSS的功能相当丰富,那么作为一个嵌入式开发者怎么获得这些数据呢? 一、寻找合适模块 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOTl6ME8wTDFwbXFmWUU1bm1Ia01XRm8xeElkUDdWUzZWTXVDNGJoTDlvRFlkWUVTY1VMTFVkemJ0c09rOE9oWWFvdnRjcUxBTkFLZ1EvNjQw?x-oss-process=image/format,png) 当然大家可以用各种渠道找到合适的模块,对于嵌入式端我们要选择哪种呢?我推荐了一下本篇所实际应用的模块——中移物联网的M6313模块,GSM与GNSS二合一模块(改天我介绍一下它的GSM功能)。 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOTl6ME8wTDFwbXFmWUU1bm1Ia01XRm95SmpqaEt2RW5nZ3RZcWljRWdhWHpoNElxRE5GcW1Fc1dsZnhNRTlwWFlycjNadmljSmxMSHdXdy82NDA?x-oss-process=image/format,png) 使用也比较方便,利用SOC的串口连接就可以 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOTl6ME8wTDFwbXFmWUU1bm1Ia01XRm83bGNYNGtpY2hTcWRvRkp5NHBpY1U1UlJIbFRkTEJhQzY1OEI3aWNqdngweWxYa05nRVhzd0N0SmcvNjQw?x-oss-process=image/format,png) 二、NMEA-0183协议 模块选好了,SOC也连接完毕,配置好模块,连接好GPS天线,这个时候GNSS模块的串口开始向SOC按照一定频率发送数据。并且猛的一看,我们可能看不懂,例如M6313的示例格式如下: AT+QGNSSC=1 // 开启 GNSS 功能 OK AT+QGNSSRD? +QGNSSRD: $GNRMC,032220.291,V,,,,,0.00,0.00,140716,,,N*5D $GNVTG,0.00,T,,M,0.00,N,0.00,K,N*2C $GNGGA,032220.291,,,,,0,0,,,M,,M,,*5D $GPGSA,A,1,,,,,,,,,,,,,,,*1E $BDGSA,A,1,,,,,,,,,,,,,,,*0F $GPGSV,2,1,07,23,,,31,08,,,49,30,,,33,16,,,45*7E $GPGSV,2,2,07,07,,,44,27,,,49,26,,,43*72 $BDGSV,1,1,03,10,,,47,04,,,40,07,,,48*62 $GNGLL,,,,,032220.291,V,N*6F OK 除去模块的回应的数据,中间那些数据是什么,我们分辨不清,这时候就要请NMEA-0183协议出场了。 NMEA-0183是美国国家海洋电子协会(National Marine Electronics Association)为海用电子设备制定的标准格式。目前业已成了GPS导航设备统一的RTCM(Radio Technical Commission for Maritime services )标准协议。 由于美国GPS推出的时间很早,所以很多GNSS的标准都是依托美国一些协会制定好的标准。 上面看到的诸如$GNRMC打头数据每一个都代表不同的信息: 序号 命令 说明 1 $*GGA 卫星定位信息 2 $*GSA 卫星PRN数据 3 $*GSV 可视卫星信息 4 $*RMC 推荐定位信息 5 $*VTG 地面速度信息 6 $*GLL 地理定位信息 7 $*GPZDA UTC时间和日期 注:* 美国的GPS标准中*代表GP,现在由于其他国家的定位系统,所以扩展到其他字符例如: 标识符 含义 BD BDS,北斗二代卫星系统 GP GPS GL GLONASS GA Galileo GN GNSS,全球导航卫星系统 协议帧总说明: 该协议采用ASCII码,其串行通信默认参数为:M6313的波特率默认为115200bps,数据位=8bit,开始位=1bit,停止位=1bit,无奇偶校验。 帧格式形如:$aaccc ,ddd ,ddd ,...... ,ddd*hh
1、“$”——帧命令起始位 2、aaccc——地址域,前两位为识别符,后三位为语句名 3、ddd...ddd——数据 4、“*”——校验和前缀 5、hhh——校验和(check sum),$与*之间所有字符ASCII码的校验和(各字节做异或运算,得到校验和后,在转换16进制格式的ASCII字符。) 6、
——CR(Carriage Return)+ LF (Line Feed)帧结束,回车换行。 1.Global Positioning Positioning Positioning Positioning System Fix Data(GGA)GPS 定位信息 $GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh 范例数据: $GPGGA,065545.789,2109.9551,N,12023.4047,E,1,9,0.85,18.1,M,-2.2,M,8.0,,*5E 名称 示例 单位 字节位置 消息 ID $GPGGA GGA UTC 065545.789 hhmmss.sss <1> 纬度 2109.9551 ddmm.mmmm <2> N/S 指示 N N=北, S=南 <3> 经度 12023.4047 dddmm.mmmm <4> E/W 指示 E W=西, E=东 <5> 定位指示 1 0:未定位 1:SPS 模式,定位有效 2:差分, SPS 模式,定位有效 3:PPS 模式,定位有效 <6> 星数目 9 范围 0 到 12 <7> HDOP 0.85 水平精度(0.5~99.9) <8> MSL 幅度 (海拔) 18.1 米(-9999.9~99999.9) <9> 单位 M 米 大地 -2.2 米 <10> 单位 M - 差分时间 8.0 秒 <11> 差分 ID 0000 <12> 校验和 *5E
消息结束 2.GPS DOP and Active Satellites Satellites Satellites Satellites(GSA)当前卫星信息 $GPGSA,<1>,<2>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<4>,<5>,<6>*hh 范例数据: $GPGSA,A,3,10,24,12,32,25,21,15,20,31,,,,1.25,0.85,0.91*04 名称 示例 单位 描述 消息 ID $GPGSA GSA 模式 1 A M=手动, 强制在 2D 或 3D 模 式 A=自动 模式 2 3 1:定位无效 2:2D 定位 3:3D 定位 卫星使用 10 通道 1 卫星使用 24 通道 2 卫星使用 12 通道 3 卫星使用 32 通道 4 卫星使用 25 通道 5 卫星使用 21 通道 6 卫星使用 15 通道 7 卫星使用 20 通道 8 ,,, ,,, ,,, ,,, 卫星使用 通道 12 PDOP 1.25 位置精度 HDOP 0.85 水平精度 VDOP 0.91 垂直精度 校验和 *04
消息结束 3. GPS Satellites Satellites Satellites Satellites in View(GSV)可见卫星信息 $GPGSV,<1>,<2>,<3>,<4>,<5>,<6>,<7>,…<4>,<5>,<6>,<7>*hh 范例数据: $GPGSV,3,1,12,14,75,001,31,32,67,111,38,31,57,331,33,26,47,221,20*73 名称 示例 单位 描述 消息 ID $GPGSV GSV 消息数目 3 范围 1 到 3 消息编号 1 范围 1 到 3 卫星数目 12 卫星 ID 14 范围 1 到 32 仰角 75 度 最大 90° 方位角 001 度 范围 0 到 359° 信噪比 31 dBHz 范围 0 到 99,没有跟踪时为 空 卫星 ID 32 范围 1 到 32 仰角 67 度 最大 90° 方位角 111 度 范围 0 到 359° 信噪比 38 dBHz 范围 0 到 99,没有跟踪时为 空 卫星 ID 31 范围 1 到 32 仰角 57 度 最大 90° 方位角 331 度 范围 0 到 359° 信噪比 33 dBHz 范围 0 到 99,没有跟踪时为 空 卫星 ID 26 范围 1 到 32 仰角 47 度 最大 90° 方位角 221 度 范围 0 到 359° 信噪比 20 dBHz 范围 0 到 99,没有跟踪时为 空 校验和 *73
消息结束 4. Recommended Recommended Recommended Recommended Minimum Minimum Minimum Minimum Specific Specific Specific Specific GPS/TRANSIT GPS/TRANSIT GPS/TRANSIT GPS/TRANSIT Data(RMC)推荐定位信息 $GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh 范例数据: $GPRMC,100646.000,A,3109.9704,N,12123.4219,E,0.257,335.62,291216,,,A*59 名称 示例 单位 描述 消息 ID $GPRMC RMC UTC 100646.000 hhmmss.ss 状态 A A=数据有效;V=数据无效 纬度 2109.9704 ddmm.mmmm N/S 指示 N N=北, S=南 经度 11123.4219 dddmm.mmmm E/W 指示 E W=西, E= 地面速度 0.257 Knot(节) 方位 335.62 度 日期 291216 ddmmyy 磁 量 - 校验和 *59
消息结束 5. Track Made Good and Ground Speed(VTG)地面速度信息$GPVTG,<1>,T,<2>,M,<3>,N,<4>,K,<5>*hh 范例数据: $GPVTG,335.62,T,,M,0.257,N,0.477,K,A*38 名称 示例 单位 描述 消息 ID $GPVTG VTG 方位 335.62 度 参考 T True 方位 335.62 度 参考 M Magnetic 速度 0.257 Knot 单位 N 速度 0.477 公里/小时 单位 K 公里/小时 校验和 *10
消息结束 三、程序介绍 上一篇文章介绍了NMEA-0183的协议内容,当我们知道数据格式,那对于底层的开发人员来说就是如何把我们需要的数据解析出来。 话不多说上实例来看: ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOTl6ME8wTDFwbXFmWUU1bm1Ia01XRm83bGNYNGtpY2hTcWRvRkp5NHBpY1U1UlJIbFRkTEJhQzY1OEI3aWNqdngweWxYa05nRVhzd0N0SmcvNjQw?x-oss-process=image/format,png) 还是这张图,上面可以看到SOC与模块只是串口相连 我们第一步就是先配置通讯IO,STM32和Linux大家自行选择 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOWlja0NhUHBGYTZFRVBhVjliR2ljSUFwYnpSNnZOUlpKdVNoUEJtT0RmMlZvQXpEalhZYktKZElTTGxMUTY3a3dCeGtaZ01jajlBbEFXQS82NDA?x-oss-process=image/format,png) ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOWlja0NhUHBGYTZFRVBhVjliR2ljSUFwYnQ0ejNDMjV3Sk1EaHhqbXRleEhIZktQWmlhTnd2TnpDaERpYVRNUEJKVW9Bck1jRGNsN0xpY1Fmdy82NDA?x-oss-process=image/format,png) 外设配置好了,接下来就开始对”模块“发过来的数据动手了。 $GNGGA,032220.291,,,,,0,0,,,M,,M,,*5D $GNRMC,032220.291,V,,,,,0.00,0.00,140716,,,N*5D $GNVTG,0.00,T,,M,0.00,N,0.00,K,N*2C $GPGSA,A,1,,,,,,,,,,,,,,,*1E $BDGSA,A,1,,,,,,,,,,,,,,,*0F $GPGSV,2,1,07,23,,,31,08,,,49,30,,,33,16,,,45*7E $GPGSV,2,2,07,07,,,44,27,,,49,26,,,43*72 $BDGSV,1,1,03,10,,,47,04,,,40,07,,,48*62 $GNGLL,,,,,032220.291,V,N*6F 首先先定义一个结构体,用来对解析好的数据进行存放。 //GPS NMEA-0183协议重要参数结构体定义 //卫星信息 __packed typedef struct { u8 num; //卫星编号 u8 eledeg; //卫星仰角 u16 azideg; //卫星方位角 u8 sn; //信噪比 }nmea_slmsg; //UTC时间信息 __packed typedef struct { u16 year; //年份 u8 month; //月份 u8 date; //日期 u8 hour; //小时 u8 min; //分钟 u8 sec; //秒钟 }nmea_utc_time; //NMEA 0183 协议解析后数据存放结构体 __packed typedef struct { u8 svnum; //可见卫星数 nmea_slmsg slmsg[12]; //最多12颗卫星 nmea_utc_time utc; //UTC时间 u32 latitude; //纬度 分扩大100000倍,实际要除以100000 u8 nshemi; //北纬/南纬,N:北纬;S:南纬 u32 longitude; //经度 分扩大100000倍,实际要除以100000 u8 ewhemi; //东经/西经,E:东经;W:西经 u8 gpssta; //GPS状态:0,未定位;1,非差分定位;2,差分定位;6,正在估算. u8 posslnum; //用于定位的卫星数,0~12. u8 possl[12]; //用于定位的卫星编号 u8 fixmode; //定位类型:1,没有定位;2,2D定位;3,3D定位 u16 pdop; //位置精度因子 0~500,对应实际值0~50.0 u16 hdop; //水平精度因子 0~500,对应实际值0~50.0 u16 vdop; //垂直精度因子 0~500,对应实际值0~50.0 u16 course; //航向 int altitude; //海拔高度,放大了10倍,实际除以10.单位:0.1m u32 speed; //地面速率,放大了1000倍,实际除以10.单位:0.001公里/小时 }nmea_msg; 其次因为协议是以字符串的形式发过来的,我们要把解析的信息进行符号的定义以及字符的转化,准备了以下两个函数: //从buf里面得到第cx个逗号所在的位置 //返回值:0~0XFE,代表逗号所在位置的偏移. // 0XFF,代表不存在第cx个逗号 u8 NMEA_Comma_Pos(u8 *buf,u8 cx) { u8 *p=buf; while(cx) { if(*buf=='*'||*buf<' '||*buf>'z')return 0XFF;//遇到'*'或者非法字符,则不存在第cx个逗号 if(*buf==',')cx--; buf++; } return buf-p; } //str转换为数字,以','或者'*'结束 //buf:数字存储区 //dx:小数点位数,返回给调用函数 //返回值:转换后的数值 /@@*遇到冒号以及竖杠、注释的斜杠的时候进行返回*/ int NMEA_Str2num(u8 *buf,u8*dx) { u8 *p=buf; u32 ires=0,fres=0; u8 ilen=0,flen=0,i; u8 mask=0; int res; while(1) //得到整数和小数的长度 { if(*p=='-'){mask|=0X02;p++;}//是负数 if(*p==','||(*p=='*')||(*p=='|')||(*p==':')\ ||(*p=='!') ||(*p=='/'))break;//遇到结束了 if(*p=='.'){mask|=0X01;p++;}//遇到小数点了 else if(*p == 0)//截至符 0 { break; } else if(*p>'9'||(*p<'0')) //有非法字符 { ilen=0; flen=0; break; } if(mask&0X01)flen++; else ilen++; p++; } if(mask&0X02)buf++; //去掉负号 for(i=0;i
5)flen=5; //最多取5位小数 *dx=flen; //小数点位数 for(i=0;i
gpssta=NMEA_Str2num(p1+posx,&dx); posx=NMEA_Comma_Pos(p1,7); //得到用于定位的卫星数 if(posx!=0XFF) gpsx->posslnum=NMEA_Str2num(p1+posx,&dx); posx=NMEA_Comma_Pos(p1,9); //得到海拔高度 if(posx!=0XFF) gpsx->altitude=NMEA_Str2num(p1+posx,&dx); } //分析GPGSA信息 //gpsx:nmea信息结构体 //buf:接收到的GPS数据缓冲区首地址 void NMEA_GPGSA_Analysis(nmea_msg *gpsx,u8 *buf) { u8 *p1,dx; u8 posx; u8 i; p1=(u8*)strstr((const char *)buf,"$GPGSA"); if(p1 == NULL) p1 = (u8*)strstr((const char *)buf,"$GNGSA"); posx=NMEA_Comma_Pos(p1,2); //得到定位类型 if(posx!=0XFF)gpsx->fixmode=NMEA_Str2num(p1+posx,&dx); for(i=0;i<12;i++) //得到定位卫星编号 { posx=NMEA_Comma_Pos(p1,3+i); if(posx!=0XFF)gpsx->possl[i]=NMEA_Str2num(p1+posx,&dx); else break; } posx=NMEA_Comma_Pos(p1,15); //得到PDOP位置精度因子 if(posx!=0XFF)gpsx->pdop=NMEA_Str2num(p1+posx,&dx); posx=NMEA_Comma_Pos(p1,16); //得到HDOP位置精度因子 if(posx!=0XFF)gpsx->hdop=NMEA_Str2num(p1+posx,&dx); posx=NMEA_Comma_Pos(p1,17); //得到VDOP位置精度因子 if(posx!=0XFF)gpsx->vdop=NMEA_Str2num(p1+posx,&dx); } //分析GPRMC信息 //gpsx:nmea信息结构体 //buf:接收到的GPS数据缓冲区首地址 void NMEA_GPRMC_Analysis(nmea_msg *gpsx,u8 *buf) { u8 *p1,dx; u8 posx; u32 temp; float rs; p1 = (u8*)strstr((const char *)buf,"GNRMC"); //GNSS if(p1 == NULL) { p1=(u8*)strstr((const char *)buf,"GPRMC");//"$GPRMC",经常有&和GPRMC分开的情况,故只判断GPRMC. } posx=NMEA_Comma_Pos(p1, 1); //得到UTC时间 hhmmss.ss if(posx!=0XFF) { temp=NMEA_Str2num(p1+posx,&dx)/NMEA_Pow(10,dx); //得到UTC时间,去掉ms gpsx->utc.hour=temp/10000; gpsx->utc.min=(temp/100)%100; gpsx->utc.sec=temp%100; } posx=NMEA_Comma_Pos(p1,2);/@@*判断RMC数据状态,A=数据有效 V=数据无效*/ if(posx!=0XFF) { u8* p2=(u8*)strstr((const char *)(p1+posx), "A"); if(p2 == NULL) { posx = 0; //数据无效 TODO } } posx=NMEA_Comma_Pos(p1,3); //得到纬度 ddmm.mmmm if(posx!=0XFF) { temp=NMEA_Str2num(p1+posx,&dx); gpsx->latitude=temp/NMEA_Pow(10,dx+2); //得到° rs=temp%NMEA_Pow(10,dx+2); //得到' gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° } posx=NMEA_Comma_Pos(p1,4); //南纬还是北纬 if(posx!=0XFF) gpsx->nshemi=*(p1+posx); posx=NMEA_Comma_Pos(p1,5); //得到经度 dddmm.mmmm if(posx!=0XFF) { temp=NMEA_Str2num(p1+posx,&dx); gpsx->longitude=temp/NMEA_Pow(10,dx+2); //得到° rs=temp%NMEA_Pow(10,dx+2); //得到' gpsx->longitude=gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° } posx=NMEA_Comma_Pos(p1,6); //东经还是西经 if(posx!=0XFF) gpsx->ewhemi=*(p1+posx); posx=NMEA_Comma_Pos(p1,8); //得到方位 度 if(posx!=0XFF) { temp=NMEA_Str2num(p1+posx,&dx); gpsx->course = temp*10; } posx=NMEA_Comma_Pos(p1, 9); //得到UTC日期 ddmmyy if(posx!=0XFF) { temp=NMEA_Str2num(p1+posx, &dx); gpsx->utc.date = temp/10000; gpsx->utc.month = (temp/100)%100; gpsx->utc.year = 2000+temp%100; } }
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
450c4aed63d8393c
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交