从NMEA0183到GNSS定位数据获取(二)软件篇
總述
? ? ? ? GPS我們都知道,一種用來(lái)全球定位的系統(tǒng),后來(lái)俄羅斯推出了格洛納斯定位系統(tǒng),中國(guó)推出了北斗定位,歐盟有伽利略,印度與日本也有有發(fā)展。所以后來(lái)把覆蓋全球的自主地利空間定位的衛(wèi)星系統(tǒng)成為GNSS。
? ? ? ? 現(xiàn)在衛(wèi)星定位那么熱,那么作為一個(gè)嵌入式人怎么獲取這些數(shù)據(jù)為我們所用呢?下面就聽(tīng)作者一一道來(lái)。
三、程序介紹
? ? ? ? 上一篇文章介紹了NMEA-0183的協(xié)議內(nèi)容,當(dāng)我們知道數(shù)據(jù)格式,那對(duì)于底層的開(kāi)發(fā)人員來(lái)說(shuō)就是如何把我們需要的數(shù)據(jù)解析出來(lái)。
? ? ? ? 話不多說(shuō)上實(shí)例來(lái)看:
? ? ? ? 還是這張圖,上面可以看到SOC與模塊只是串口相連
? ? ? ? 我們第一步就是先配置通訊IO,STM32和Linux大家自行選擇
?
? ? ? ? 外設(shè)配置好了,接下來(lái)就開(kāi)始對(duì)”模塊“發(fā)過(guò)來(lái)的數(shù)據(jù)動(dòng)手了。
$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
? ? ? ? 首先先定義一個(gè)結(jié)構(gòu)體,用來(lái)對(duì)解析好的數(shù)據(jù)進(jìn)行存放。
//GPS NMEA-0183協(xié)議重要參數(shù)結(jié)構(gòu)體定義 //衛(wèi)星信息 __packed typedef struct {u8 num; //衛(wèi)星編號(hào)u8 eledeg; //衛(wèi)星仰角u16 azideg; //衛(wèi)星方位角u8 sn; //信噪比 }nmea_slmsg;//UTC時(shí)間信息 __packed typedef struct {u16 year; //年份u8 month; //月份u8 date; //日期u8 hour; //小時(shí)u8 min; //分鐘u8 sec; //秒鐘 }nmea_utc_time; //NMEA 0183 協(xié)議解析后數(shù)據(jù)存放結(jié)構(gòu)體 __packed typedef struct {u8 svnum; //可見(jiàn)衛(wèi)星數(shù)nmea_slmsg slmsg[12]; //最多12顆衛(wèi)星nmea_utc_time utc; //UTC時(shí)間u32 latitude; //緯度 分?jǐn)U大100000倍,實(shí)際要除以100000u8 nshemi; //北緯/南緯,N:北緯;S:南緯u32 longitude; //經(jīng)度 分?jǐn)U大100000倍,實(shí)際要除以100000u8 ewhemi; //東經(jīng)/西經(jīng),E:東經(jīng);W:西經(jīng)u8 gpssta; //GPS狀態(tài):0,未定位;1,非差分定位;2,差分定位;6,正在估算.u8 posslnum; //用于定位的衛(wèi)星數(shù),0~12.u8 possl[12]; //用于定位的衛(wèi)星編號(hào)u8 fixmode; //定位類型:1,沒(méi)有定位;2,2D定位;3,3D定位u16 pdop; //位置精度因子 0~500,對(duì)應(yīng)實(shí)際值0~50.0u16 hdop; //水平精度因子 0~500,對(duì)應(yīng)實(shí)際值0~50.0u16 vdop; //垂直精度因子 0~500,對(duì)應(yīng)實(shí)際值0~50.0 u16 course; //航向int altitude; //海拔高度,放大了10倍,實(shí)際除以10.單位:0.1mu32 speed; //地面速率,放大了1000倍,實(shí)際除以10.單位:0.001公里/小時(shí) }nmea_msg;?? ? ? ? 因?yàn)閰f(xié)議是以字符串的形式發(fā)過(guò)來(lái)的,我們要把解析的信息進(jìn)行符號(hào)的定義以及字符的轉(zhuǎn)化,準(zhǔn)備了以下兩個(gè)函數(shù):
//從buf里面得到第cx個(gè)逗號(hào)所在的位置 //返回值:0~0XFE,代表逗號(hào)所在位置的偏移. // 0XFF,代表不存在第cx個(gè)逗號(hào) u8 NMEA_Comma_Pos(u8 *buf,u8 cx) {u8 *p=buf;while(cx){if(*buf=='*'||*buf<' '||*buf>'z')return 0XFF;//遇到'*'或者非法字符,則不存在第cx個(gè)逗號(hào)if(*buf==',')cx--;buf++;}return buf-p; } //str轉(zhuǎn)換為數(shù)字,以','或者'*'結(jié)束 //buf:數(shù)字存儲(chǔ)區(qū) //dx:小數(shù)點(diǎn)位數(shù),返回給調(diào)用函數(shù) //返回值:轉(zhuǎn)換后的數(shù)值 /*遇到冒號(hào)以及豎杠、注釋的斜杠的時(shí)候進(jìn)行返回*/ 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) //得到整數(shù)和小數(shù)的長(zhǎng)度{if(*p=='-'){mask|=0X02;p++;}//是負(fù)數(shù)if(*p==','||(*p=='*')||(*p=='|')||(*p==':')\||(*p=='!') ||(*p=='/'))break;//遇到結(jié)束了if(*p=='.'){mask|=0X01;p++;}//遇到小數(shù)點(diǎn)了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++; //去掉負(fù)號(hào)for(i=0;i<ilen;i++) //得到整數(shù)部分?jǐn)?shù)據(jù){ires+=NMEA_Pow(10,ilen-1-i)*(buf[i]-'0');//}if(flen>5)flen=5; //最多取5位小數(shù)*dx=flen; //小數(shù)點(diǎn)位數(shù)for(i=0;i<flen;i++) //得到小數(shù)部分?jǐn)?shù)據(jù){fres+=NMEA_Pow(10,flen-1-i)*(buf[ilen+1+i]-'0');}res=ires*NMEA_Pow(10,flen)+fres;if(mask&0X02)res=-res;return res; }?? ? ? ? 還有一個(gè)是在例如經(jīng)緯度的轉(zhuǎn)化的時(shí)候需要用到的次方的函數(shù):?
//m^n函數(shù) //返回值:m^n次方. u32 NMEA_Pow(u8 m,u8 n) {u32 result=1;while(n--)result*=m;return result; }?? ? ? ? 因?yàn)轫?xiàng)目的需求沒(méi)有對(duì)協(xié)議全部的解析,只是針對(duì)性的進(jìn)行了解析。其余大家想要解析的數(shù)據(jù)也是類似:下面是以GPGGA 解析為例:其他標(biāo)志頭的數(shù)據(jù)大家以此類推。
?? ? ? ? 從上面圖片可以看到本條信息帶有14條信息,但是我只解析了第六個(gè)、第七個(gè)和第九個(gè)字節(jié)的數(shù)據(jù)。雖然這條信息中也有UTC時(shí)間,但是一般都是建議在*RMC中獲得。
//分析GPGGA信息 //gpsx:nmea信息結(jié)構(gòu)體 //buf:接收到的GPS數(shù)據(jù)緩沖區(qū)首地址 void NMEA_GPGGA_Analysis(nmea_msg *gpsx,u8 *buf) {u8 *p1,dx;u8 posx; p1 = (u8*)strstr((const char *)buf,"$GNGGA");//GN 標(biāo)志開(kāi)頭if(p1 == NULL){p1=(u8*)strstr((const char *)buf,"$GPGGA");//或者GP開(kāi)頭的標(biāo)志 你也可以用BD開(kāi)頭的標(biāo)志}posx=NMEA_Comma_Pos(p1,6); //得到GPS狀態(tài)if(posx!=0XFF)gpsx->gpssta=NMEA_Str2num(p1+posx,&dx); posx=NMEA_Comma_Pos(p1,7); //得到用于定位的衛(wèi)星數(shù)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信息結(jié)構(gòu)體 //buf:接收到的GPS數(shù)據(jù)緩沖區(qū)首地址 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++) //得到定位衛(wèi)星編號(hào){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信息結(jié)構(gòu)體 //buf:接收到的GPS數(shù)據(jù)緩沖區(qū)首地址 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"); //GNSSif(p1 == NULL){p1=(u8*)strstr((const char *)buf,"GPRMC");//"$GPRMC",經(jīng)常有&和GPRMC分開(kāi)的情況,故只判斷GPRMC.}posx=NMEA_Comma_Pos(p1, 1); //得到UTC時(shí)間 hhmmss.ssif(posx!=0XFF){temp=NMEA_Str2num(p1+posx,&dx)/NMEA_Pow(10,dx); //得到UTC時(shí)間,去掉msgpsx->utc.hour=temp/10000;gpsx->utc.min=(temp/100)%100;gpsx->utc.sec=temp%100;}posx=NMEA_Comma_Pos(p1,2);/*判斷RMC數(shù)據(jù)狀態(tài),A=數(shù)據(jù)有效 V=數(shù)據(jù)無(wú)效*/if(posx!=0XFF){u8* p2=(u8*)strstr((const char *)(p1+posx), "A");if(p2 == NULL){posx = 0; //數(shù)據(jù)無(wú)效 TODO}}posx=NMEA_Comma_Pos(p1,3); //得到緯度 ddmm.mmmmif(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;//轉(zhuǎn)換為°}posx=NMEA_Comma_Pos(p1,4); //南緯還是北緯if(posx!=0XFF)gpsx->nshemi=*(p1+posx); posx=NMEA_Comma_Pos(p1,5); //得到經(jīng)度 dddmm.mmmmif(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;//轉(zhuǎn)換為°}posx=NMEA_Comma_Pos(p1,6); //東經(jīng)還是西經(jīng)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日期 ddmmyyif(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;} }? ? ? ? ?這就是我分享的第二篇NMEA-0183到GNSS數(shù)據(jù)的文章,里面代碼都是實(shí)踐過(guò)的。如果大家有什么更好的思路,歡迎分享交流哈。
從NMEA0183到GNSS定位數(shù)據(jù)獲取(二)軟件篇
?
總結(jié)
以上是生活随笔為你收集整理的从NMEA0183到GNSS定位数据获取(二)软件篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [html] 怎样去除iOS和Andr
- 下一篇: figma下载_我关于Figma文件封面