点击“蓝字”关注我吧

作者:良知犹存

转载授权以及围观:欢迎添加微信公众号:Conscience_Remains

总述

GPS我们都知道,一种用来全球定位的系统,后来俄罗斯推出了格洛纳斯定位系统,中国推出了北斗定位,欧盟有伽利略,印度与日本也有有发展。所以后来把覆盖全球的自主地利空间定位的卫星系统成为GNSS。

现在卫星定位那么热,那么作为一个嵌入式人怎么获取这些数据为我们所用呢?下面就听作者一一道来。

上一篇文章的传送门从NMEA0183到GNSS定位数据获取(一)原理篇

需要资料和代码的朋友可以关注公众号回复GNSS解析获得自动回复的链接。

三、程序介绍

上一篇文章介绍了NMEA-0183的协议内容,当我们知道数据格式,那对于底层的开发人员来说就是如何把我们需要的数据解析出来。

话不多说上实例来看:

还是这张图,上面可以看到SOC与模块只是串口相连

我们第一步就是先配置通讯IO,STM32和Linux大家自行选择

外设配置好了,接下来就开始对”模块“发过来的数据动手了。

$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//得到整数部分数据  {      ires+=NMEA_Pow(10,ilen-1-i)*(buf[i]-'0');//  }  if(flen>5)flen=5;  //最多取5位小数  *dx=flen;       //小数点位数  for(i=0;i//得到小数部分数据  {      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;}    

还有一个是在例如经纬度的转化的时候需要用到的次方的函数:

//m^n函数//返回值:m^n次方.u32 NMEA_Pow(u8 m,u8 n){  u32 result=1;     while(n--)result*=m;      return result;}

因为项目的需求没有对协议全部的解析,只是针对性的进行了解析。其余大家想要解析的数据也是类似:

下面是以GPGGA 解析为例:其他标志头的数据大家以此类推。

从上面图片可以看到本条信息带有14条信息,但是我只解析了第六个、第七个和第九个字节的数据。虽然这条信息中也有UTC时间,但是一般都是建议在*RMC中获得。

//分析GPGGA信息//gpsx:nmea信息结构体//buf:接收到的GPS数据缓冲区首地址void NMEA_GPGGA_Analysis(nmea_msg *gpsx,u8 *buf){  u8 *p1,dx;         u8 posx;        p1 = (u8*)strstr((const char *)buf,"$GNGGA");//GN 标志开头  if(p1 == NULL)  {    p1=(u8*)strstr((const char *)buf,"$GPGGA");//或者GP开头的标志 你也可以用BD开头的标志  }    posx=NMEA_Comma_Pos(p1,6);                //得到GPS状态  if(posx!=0XFF)    gpsx->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;        } }

这就是我分享的第二篇NMEA-0183到GNSS数据的文章,里面代码都是实践过的。如果大家有什么更好的思路,欢迎分享交流哈。

更多分享,扫码关注我

获取整数的位数_从NMEA0183到GNSS定位数据获取(二)软件篇相关推荐

  1. 从NMEA0183到GNSS定位数据获取(一)原理篇

    总述 GPS我们都知道,一种用来全球定位的系统,后来俄罗斯推出了格洛纳斯定位系统,中国推出了北斗定位,欧盟有伽利略,印度与日本也有有发展.所以后来把覆盖全球的自主地利空间定位的卫星系统成为GNSS. ...

  2. java获取月两位数_用Java显示每月的两位数字

    使用" d"日期转换字符来显示两位数字的月份,例如27.28.20等.System.out.printf("Two-digit day of the month: %td ...

  3. 查找整数c语言编程,关于算法:查找整数的位数

    查找正整数的位数的最佳方法是什么? 我发现了这3种基本方法: 转换为字符串 String s = new Integer(t).toString(); int len = s.length(); fo ...

  4. 输出整数的位数、按位输出(两种)以及逆序输出

    输出整数的位数.按位输出(两种)以及逆序输出 本次数字的基本操作及解释 ①求数字所占位数 ②数字逐位从高位到低位输出 ③数字逐位从低位到高位输出 ④数字逆序输出 数字的按位输出操作,逆序操作,输出位数 ...

  5. Java黑皮书课后题第2章:2.6(求一个整数各位数的和)读取一个0和1000之间的整数,并将给整数的各位数字相加

    2.6(求一个整数各位数的和)读取一个0和1000之间的整数,并将给整数的各位数字相加 题目 题目描述 运行示例 题目槽点与破题 题目槽点 破题 如何提取数字 代码块 方法评价 题目 题目描述 2.6 ...

  6. 求整数的位数及各位数字之和 (15 分)

    7-48 求整数的位数及各位数字之和 (15 分) 对于给定的正整数N,求它的位数及其各位数字之和. 输入格式: 输入在一行中给出一个不超过109的正整数N. 输出格式: 在一行中输出N的位数及其各位 ...

  7. 实验4-1-4 求整数的位数及各位数字之和 (15 分)

    实验4-1-4 求整数的位数及各位数字之和 (15 分) 对于给定的正整数N,求它的位数及其各位数字之和. 输入格式: 输入在一行中给出一个不超过109的正整数N. 输出格式: 在一行中输出N的位数及 ...

  8. python输入一个正整数、计算其各个位的数字之和_C语言程序设计:编写程序,输入一个正整数,统计该整数的位数并计算其各个数位上的数字之和。...

    点击查看C语言程序设计:编写程序,输入一个正整数,统计该整数的位数并计算其各个数位上的数字之和.具体信息 答:#include /*包含头文件*/void main() /*主函数*/{ int n, ...

  9. C语言计算一个整数的位数

    C语言计算一个整数的位数 只需要设计一个计时器,因为C语言中除法只留下整数部分,所以可以拿数字/10:数字位数即为循环次数,待n为个位时n/10=0,然后循环结束. #include<stdio ...

最新文章

  1. HTML !DOCTYPE 标签
  2. 跨界巨头谋定现代农业-农民丰收节交易会:全产业链布局
  3. 新加坡比特币交易平台美女CEO自杀身亡(图)
  4. mysql序列号发生器
  5. java poi之Excel的创建
  6. 13、play中实现信息国际化
  7. 唐山师范学院计算机二级报名,2017年3月唐山师范学院计算机等级考试报名时间(河北)...
  8. Hadoop伪分布安装详解(一)
  9. 从业6年,给你5点建议
  10. 浮点数例外 (核心已转储)_五粮液作为“核心支持企业”在进博会精彩亮相_产业综合_行业...
  11. 当关闭winform窗体时触发
  12. Snagit 截图不清晰问题解决
  13. Outlook读取奇妙清单Wunderlist日历失败的解决办法
  14. Android包管理机制1 PackageInstaller 初始化
  15. excel如何使用函数判断包含某值
  16. Java - 为什么String在Java中是不可变的?
  17. python论文排版格式_论文排版(一):三分钟了解页面设置
  18. ubuntu16.04 安装为知笔记
  19. jbX和finss的一些问题
  20. 泥瓦匠之 Java 的成长感悟

热门文章

  1. wxWidgets:wxSimpleHtmlListBox类用法
  2. boost::range_const_reverse_iterator相关的测试程序
  3. boost::mpl::distance相关的测试程序
  4. boost::lockfree::detail::tagged_ptr用法的测试程序
  5. boost::mpl::integral_c用法的测试程序
  6. boost::graph::distributed::hohberg_biconnected_components用法的测试程序
  7. boost::compressed_sparse_row_graph用法的测试程序
  8. GDCM:gdcm::Codec的测试程序
  9. DCMTK:搜索助手类的测试程序
  10. DCMTK:DSRListOfItems类的测试程序