从NMEA0183到GNSS定位数据获取(二)软件篇

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 从NMEA0183到GNSS定位数据获取(二)软件篇

作者:良知犹存

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

总述

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

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

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

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


三、程序介绍

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

话不多说上实例来看:

4edc953e2c684bbe819ffa954c899c08.png

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

我们第一步就是先配置通讯IO,STM32和Linux大家自行选择4edc953e2c684bbe819ffa954c899c08.png4edc953e2c684bbe819ffa954c899c08.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<ilen;i++)  //得到整数部分数据
  {  
    ires+=NMEA_Pow(10,ilen-1-i)*(buf[i]-'0');//
  }
  if(flen>5)flen=5;  //最多取5位小数
  *dx=flen;       //小数点位数
  for(i=0;i<flen;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 解析为例:其他标志头的数据大家以此类推。4edc953e2c684bbe819ffa954c899c08.png

从上面图片可以看到本条信息带有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数据的文章,里面代码都是实践过的。如果大家有什么更好的思路,欢迎分享交流哈。

目录
相关文章
|
监控 安全 物联网
什么是UWB定位技术?UWB定位的应用场景及功能介绍
uwb定位技术全称Ultra Wide Band,超宽带技术。uwb超宽带技术是一种全新的通信技术,与传统通信技术有极大差异。它不需要使用传统通信体制中的载波,而是通过发送和接收极窄脉冲来实现无线传输,由于脉冲时间宽度极窄,使用的带宽在500MHz以上。 后来,由于uwb定位技术穿透力强、功耗低、安全性高、定位精度高等优势,人们意识到了它在高精度定位领域的价值,uwb在工业定位领域的应用逐渐成为主流。
1778 0
|
数据可视化 定位技术 API
百度地图开发:海量点、测距以及定位聚合功能
百度地图开发:海量点、测距以及定位聚合功能
306 0
|
5月前
|
算法 搜索推荐 物联网
基于iBeacon蓝牙定位技术的反向寻车系统:打造高效智能的停车场导航体验
**基于iBeacon的反向寻车系统利用蓝牙信标实现停车场内车辆精确定位。车主停车时绑定手机,通过APP迅速导航至车辆。系统关键组件包括iBeacon硬件部署、数据处理与用户界面设计,采用高精度定位算法、实时数据处理和智能路径规划。随着技术发展,该系统有望在更多公共场所提升停车体验。**
194 1
基于iBeacon蓝牙定位技术的反向寻车系统:打造高效智能的停车场导航体验
|
5月前
|
监控 安全 自动驾驶
基于java+单体服务 + 硬件(UWB定位基站、卡牌)技术架构开发的UWB室内定位系统源码 UWB定位技术 超宽带定位 高精度定位系统源码
基于java+单体服务 + 硬件(UWB定位基站、卡牌)技术架构开发的UWB室内定位系统源码 UWB定位技术 超宽带定位 高精度定位系统源码
70 3
|
6月前
|
监控 安全 算法
uwb人员定位系统:人员轨迹实时定位
vuwb人员定位系统:人员轨迹实时定位
321 0
uwb人员定位系统:人员轨迹实时定位
|
6月前
|
监控 安全 数据可视化
实时定位系统源码,UWB定位技术的人员定位系统源码
UWB定位系统,实时精确地定位员工、车辆、物品上微标签位置,零延时地将人、车、物的位置信息显示在工厂控制中心,进行安全区域管控、人员在岗监控、车辆实时轨迹监控。精度达到厘米级。借助摄像头可实现视频联动功能,实时查看工厂内各个员工的实时画面。
125 0
实时定位系统源码,UWB定位技术的人员定位系统源码
|
6月前
|
监控 安全 5G
UWB人员精准定位系统源码,实现实时定位、人机料配对、物料标签配置、智慧调度、轨迹追踪
人员定位管理系统通过在厂区、车间部署UWB定位基站,实时采集人员、机具、物料上定位标签回传的位置信息数据,采用多维定位模式,精确定位人、机具、物料的实时位置,实现实时定位、人机料配对、物料标签配置、智慧调度、轨迹追踪、工时统计、区域物料统计、电子围栏等应用功能。
121 1
|
监控 算法 安全
UWB定位技术源码:实现微米级精度的人员定位系统
UWB人员定位系统是一种基于超宽带技术的人员定位系统,它通过发送和接收超短脉冲信号,在测距方面可以达到微米级精度。这种系统通常需要具备高精度的定位能力,通常需要达到微米级别,这样可以在室内和室外的复杂环境中精确定位,为人员的个人安全提供更好地保障。
135 0
UWB定位技术源码:实现微米级精度的人员定位系统
|
6月前
|
监控 安全 数据可视化
化工厂人员定位系统解决方案,UWB定位系统源码
通过融合UWB定位技术,实现室内外定位无缝切换、二三维地图呈现电子围栏、安全预警、位置追踪、轨边回放等功能,可实现化工企业精细化安全管理和生产过程管控,进一步提高生产效率,维护生产秩序的安全,同时也实现了化工企业安全管理和生产效率的提高。
|
5G 定位技术
UWB定位系统的定位维度及功能介绍
室内定位系统方案中,UWB室内定位技术目前应用较多,得益于UWB室内定位10-30厘米的超高定位精度。 UWB全称是超宽带技术,它是一种无线载波通信技术。 UWB不采用载波,而是利用纳秒至微微秒级的非正弦波窄脉冲传输数据。
205 0
UWB定位系统的定位维度及功能介绍