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

article/2024/12/21 22:33:44

总述

        GPS我们都知道,一种用来全球定位的系统,后来俄罗斯推出了格洛纳斯定位系统,中国推出了北斗定位,欧盟有伽利略,印度与日本也有有发展。所以后来把覆盖全球的自主地利空间定位的卫星系统成为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倍,实际要除以100000u8 nshemi;          //北纬/南纬,N:北纬;S:南纬u32 longitude;          //经度 分扩大100000倍,实际要除以100000u8 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.0u16 hdop;          //水平精度因子 0~500,对应实际值0~50.0u16 vdop;          //垂直精度因子 0~500,对应实际值0~50.0 u16 course;       //航向int altitude;         //海拔高度,放大了10倍,实际除以10.单位:0.1mu32 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 解析为例:其他标志头的数据大家以此类推。

         从上面图片可以看到本条信息带有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"); //GNSSif(p1 == NULL){p1=(u8*)strstr((const char *)buf,"GPRMC");//"$GPRMC",经常有&和GPRMC分开的情况,故只判断GPRMC.}posx=NMEA_Comma_Pos(p1, 1);                //得到UTC时间  hhmmss.ssif(posx!=0XFF){temp=NMEA_Str2num(p1+posx,&dx)/NMEA_Pow(10,dx);     //得到UTC时间,去掉msgpsx->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.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;//转换为°}posx=NMEA_Comma_Pos(p1,4);                //南纬还是北纬if(posx!=0XFF)gpsx->nshemi=*(p1+posx);           posx=NMEA_Comma_Pos(p1,5);                //得到经度 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;//转换为°}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日期 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数据的文章,里面代码都是实践过的。如果大家有什么更好的思路,欢迎分享交流哈。

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

 


http://chatgpt.dhexx.cn/article/BT2a07ZW.shtml

相关文章

c++中拷贝构造函数被调用的时机

1 c中拷贝构造函数被调用的时机 拷贝构造函数被调用的几种情况&#xff1a; &#xff08;1&#xff09;当用类的一个对象去初始化该类的另一个对象时&#xff0c;系统会自动调用拷贝构造函数&#xff1b; &#xff08;2&#xff09;将一个对象作为实参传递给一个非引用类型的…

二说 拷贝构造函数 拷贝赋值函数

文章目录 什么是拷贝构造函数拷贝构造函数的调用时机2.1 当函数的参数为类的对象时2.2 函数的返回值是类的对象2.3 对象需要通过另外一个对象进行初始化 浅拷贝与深拷贝3.1 默认拷贝构造函数3.2 浅拷贝3.3 深拷贝3.4 防止默认拷贝发生 拷贝构造函数的几个细节4.1 为什么拷贝构造…

C++拷贝构造函数、构造函数和析构函数

一、拷贝构造函数 转载自&#xff1a;http://www.cnblogs.com/BlueTzar/articles/1223313.html 1、类对象的拷贝 对于普通类型的对象来说&#xff0c;它们之间的复制是很简单的&#xff0c;例如&#xff1a; int a88; int ba; 而类对象与普通对象不同&#xff0c;类对象内部…

拷贝构造函数起作用的三种情况

拷贝构造函数起作用的三种情况&#xff1a; 1.当用类的对象去初始化同类的另一个对象时。 Date d2(d1); Date d2 d1; //初始化语句&#xff0c;并非赋值语句。 2.当函数的形参是类的对象&#xff0c;调用函数进行形参和实参结合时。 void Func(A a1) //形参是类Date的对象…

【拷贝构造函数】c++类拷贝构造函数详解

【拷贝构造函数】c类拷贝构造函数详解 目录 【拷贝构造函数】c类拷贝构造函数详解一、什么是拷贝构造函数二、拷贝构造函数的几种调用时机1. 当函数的参数为类的对象时2. 函数的返回值是类的对象3. 当成员变量为类类型时4. 普通派生类构造函数的写法 三、浅拷贝与深拷贝1. 默认…

拷贝(复制)构造函数定义及3种调用情况举例

一、拷贝构造函数是一种特殊的构造函数&#xff0c;其形参为本类的对 象引用。 class 类名 { public : 类名&#xff08;形参&#xff09;&#xff1b;//构造函数 类名&#xff08;类名 &对象名&#xff09;&#xff1b;//拷贝构造函数 ... }&#xff1b; //拷贝构造函…

C++——拷贝构造函数详解

C——拷贝构造函数详解 1.拷贝构造函数的特点&#xff1a;2.通过例子引入拷贝构造&#xff1a;3构造对象的时候使用引用返回与不使用引用返回的问题&#xff1a;3.1不使用引用返回&#xff1a;3.2引用返回——从已经死亡的地址接收值不牢靠&#xff1a; 4.缺省的拷贝构造和等号…

C++ 拷贝构造函数详解

C 拷贝构造函数详解 下面的讲解将以C标准库的string类作为讲解对象&#xff0c;string类&#xff1a;class with pointer member(s) 1、拷贝构造函数和拷贝赋值函数 1.1引入 下面是给出的测试函数&#xff0c;也是我们要能在自己设计的myString类中实现的功能&#xff1a; …

详解析构函数、拷贝构造函数

目录 一.析构函数&#xff08;析构器&#xff09; &#xff08;一&#xff09;.使用方式及注意事项 1.使用方式 2.注意事项 &#xff08;二&#xff09;.默认析构函数 二.拷贝构造函数 &#xff08;一&#xff09;.使用方式及注意事项 1.使用方式 2.注意事项 &#xff0…

【深入理解C++】拷贝构造函数

文章目录 1.拷贝构造函数2.默认的拷贝操作3.默认拷贝构造函数4.何时调用拷贝构造函数 1.拷贝构造函数 拷贝构造函数是构造函数的一种。当利用已存在的对象创建一个新对象时&#xff0c;就会调用新对象的拷贝构造函数进行初始化。 拷贝构造函数的格式是固定的&#xff0c;即接…

C++拷贝构造函数详解

一. 什么是拷贝构造函数 首先对于普通类型的对象来说&#xff0c;它们之间的复制是很简单的&#xff0c;例如&#xff1a; int a 100; int b a; 而类对象与普通对象不同&#xff0c;类对象内部结构一般较为复杂&#xff0c;存在各种成员变量。 下面看一个类对象拷贝的简…

c++拷贝构造函数(深拷贝,浅拷贝)详解

一、什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=100; int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。 下面看一个类对象拷贝的简单例子。 #include<iostream> using n…

YOLO 裂缝检测

环境 python3.5 yolov3 opencv keras

基于OpenCV的混凝土裂纹检测

基于OpenCV的混凝土裂纹检测 前言 这是我发的第一次博客&#xff0c;有什么建议大家可以给我留言&#xff0c;感激不尽! 接下来&#xff0c;我们进入正题。 一、使用函数库 numpy, opencv, heapq, skimage.morphology 二、使用步骤 1.初步预处理 初步预处理包括&#xf…

【图像识别】基于计算机视觉实现路面裂缝检测识别系统matlab代码

1 简介 随着公路与铁路事业的飞速发展,各类车辆和里程的增加,铁路的一次次提速,都对路面产生了巨大的压力。不论是公路路面还是铁路路面,路面裂纹都能随处可见,由路面裂纹造成的交通事故时有发生。研究路面裂纹检测方法对于路面维护、交通安全具有极其重大意义。近年来,路面裂…

基于计算机视觉的裂纹检测方案

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达01. 数据集 我们首先需要从互联网上获取包含墙壁裂缝的图像&#xff08;URL格式&#xff09;数据。总共包含1428张图像&#xff1a;其中一半是新的且未损坏的墙壁&#x…

halcon裂纹缺陷检测

针对这一类表面的检测就不能单纯依靠帧差或者背景差来完成&#xff0c;因为背景的纹理不可能和当前图像的纹理完全相同。 方法一—局部阈值分割 一、局部阈值分割 1、gen_sin_bandpass–局部阈值分割 dyn_threshold(OrigImage, ThresholdImage : RegionDynThresh : Offset,…

使用 Python 进行深度学习以进行裂纹检测

使用 Python 进行深度学习以进行裂纹检测 问题陈述数据集准备训练模型结论参考 问题陈述 虽然新技术已经改变了我们生活的方方面面&#xff0c;在建筑领域似乎牛逼正在努力追赶。目前&#xff0c;建筑物的结构状况仍然主要是人工检查。简单来说&#xff0c;即使现在需要检查结…

Halcon-表面检测-----裂纹检测

对应示例程序&#xff1a; detect_mura_defects_blur.hdev 目标&#xff1a;实例实现LCD上有很多污点干扰下&#xff0c;检测LCD的印痕检测。 思路为&#xff1a;对LCD图像进行拆分&#xff0c;提取RGB三个分量。 对B分量进行处理&#xff0c;将其转换为频域内图像&#xff0…

图像中的裂纹检测

01. 数据集 我们首先需要从互联网上获取包含墙壁裂缝的图像(URL格式)数据。总共包含1428张图像:其中一半是新的且未损坏的墙壁;其余部分显示了各种尺寸和类型的裂缝。 第一步:读取图像,并调整大小。 images = []for url in tqdm.tqdm(df[content]): response = req…