主要针对NMEA协议进行了整理。分为三个基础数据梳理函数和五个常用GPS解析函数。
三个基础数据处理函数:
1:逗号位置处理函数:
//从buf里面得到第cx个逗号所在的位置
//返回值:0~0XFE,代表逗号所在位置的偏移.
// 0XFF,代表不存在第cx个逗号
u8 NMEA_Comma_Pos(u8 *buf,u8 cx)//buf为指针变量,cx为第几个逗号
2.m^n函数:用于计算m^n次方
//m^n函数
//返回值:m^n次方.
u32 NMEA_Pow(u8 m,u8 n)
3.字符串转换为数字函数
//str转换为数字
//buf:数字存储区
//dx:小数点位数,返回给调用函数 //小数点位数
//返回值:转换后的数值
//应该是数值,不含小数,但是你知道了位数,位数在形参指针里
int NMEA_Str2num(u8 *buf,u8*dx);//buf 和 dx 都是指针变量。
五个解析函数:
1.GPGSV 2.GNGGA 3.GNGSA 4.GNRMC 5.GNVTG
知识点补充:
包含文件:string.h 函数名: strstr
函数原型:extern char *strstr(char *str1, const char *str2);
strstr(str1,str2)
str1: 被查找目标 string expression to search.
str2: 要查找对象 The string expression to find.
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
详解代码
//从buf里面得到第cx个逗号所在的位置
//返回值:0~0XFE,代表逗号所在位置的偏移.
// 0XFF,代表不存在第cx个逗号
u8 NMEA_Comma_Pos(u8 *buf,u8 cx) //buf是指针变量
{
u8 *p=buf;//指针变量地址给了值,p表示啥,p也是指针变量,其实这个是两条语句 u8
while(cx)
{
if(*buf=='*'||*buf<''||*buf>'z') return OXFF;//遇到‘*‘或者非法字符,则不存在第cx个逗号
if(*buf==',')cx--;
buf++;//指针变量移动位置
}
return buf-p;
}
//m^n函数
//返回值:m^n次方.
u32 NMEA_Pow(u8 m,u8 n)
{
u32 result = 1;
while(n--)result*=n;
return result
}
//str转换为数字
//buf:数字存储区
//dx:小数点位数,返回给调用函数 //小数点位数
//返回值:转换后的数值
//应该是数值,不含小数,但是你知道了位数,位数在形参指针里
int NMEA_Str2num(u8 *buf,u8*dx);//buf 和 dx 都是指针变量。res是16位的
{
u8 *p=buf;//p变为指针变量
u32 ires=0,fres=0;
u8 ilen=0,flen=0,i;
u8 mask = 0;//mask = 0x00;
int res;
while(1)//得到整数和小数的长度 不停的进行遍历
{
if(*p=='-'){mask|=0x02;p++;}//是负数。mask=0X02是负数
if(*p==','||(*p=='*')){}break;//遇到结束了
if(*p=='.'){mask|=0x01;p++;}//遇到小数点了。
else if(*p>'9'||(*p<'0')) //有非法字符
{
ilen = 0;
flen = 0;
break;
}
if (mask&0X01)flen++;
else ilen++; //
p++;
}
if(mask&0x02)flen++;//去掉负号,为啥这就是减掉符号了
for(i=0;i10,ilen-1-i)*(buf[i]-'0');
//应该是NMEA_Pow是取几个10,10的多少次方,buf[i]上的值
}
if(flen>5)flen=5; //最多取5位小数
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;
}
【1】分析GPGSV信息
//分析GPGSV信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
//【结果】:得到所有卫星的卫星总数,每个卫星的编号,仰角,方位角,信噪比,对实际没有什么太大用处
$GPGSV,3,1,11,18,73,129,19,10,71,335,40,22,63,323,41,25,49,127,06*78
$GPGSV,3,1,11,[18,73,129,19],[10,71,335,40],[22,63,323,41],[25,49,127,06]*78
/*
(1) GSV 语句总数。【3】
(2) 本句 GSV 的编号。【1】
(3) 可见卫星的总数(00~12,前面的 0 也将被传输)。【11】
(4) 卫星编号(01~32,前面的 0 也将被传输)。【18】 【10】
(5) 卫星仰角(00~90 度,前面的 0 也将被传输)。【73】【71】
(6) 卫星方位角(000~359 度,前面的 0 也将被传输)【129】【335】
(7) 信噪比(00~99dB,没有跟踪到卫星时为空)。【19】【40】
void NMEA_GPGSV_Analysis(nmeg_msg *gpsx,u8 *buf)
{
u8 *p,*p1,dx;
u8 len,i,j,slx=0;
u8 posx;
p=buf;
p1=(u8*)strstr((const char *)p,"$GPGSV");//p1应该是$的指针变量
len=p1[7]-'0';//得到GPGSV的条数,应该是3,看例子
posx=NMEA_Comma_Pos(p1,3); //得到可见卫星总数【11】
if(posx!=0XFF)gpsx->svnum=NMEA_Str2num(p1+posx,&dx);//
for(i=0;iu8*)strstr((const char *)p,"$GPGSV");
for(j=0;j<4;j++)
{
posx=NMEA_Comma_Pos(p1,4+j*4);
if(posx!=0XFF)gpsx->slmsg[slx].num=NMEA_Str2num(p1+posx,&dx); //得到卫星编号
else break;
posx=NMEA_Comma_Pos(p1,5+j*4);
if(posx!=0XFF)gpsx->slmsg[slx].eledeg=NMEA_Str2num(p1+posx,&dx);//得到卫星仰角
else break;
posx=NMEA_Comma_Pos(p1,6+j*4);
if(posx!=0XFF)gpsx->slmsg[slx].azideg=NMEA_Str2num(p1+posx,&dx);//得到卫星方位角
else break;
posx=NMEA_Comma_Pos(p1,7+j*4);
if(posx!=0XFF)gpsx->slmsg[slx].sn=NMEA_Str2num(p1+posx,&dx); //得到卫星信噪比
else break;
slx++;
}
p=p1+1;//切换到下一个GPGSV信息
}
}
【2】GNGGA信息
//分析GNGGA信息,这里就用到了679 如果要用记得自己加
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
/*
$GNGGA 语句的基本格式如下(其中 M 指单位 M, hh 指校验和, CR 和 LF 代表回车
换行,下同):
$GNGGA,(1),(2),(3),(4),(5),(6),(7),(8),(9),M,(10),M,(11),(12)*hh(CR)(LF)
举例如下:
$GNGGA,095528.000,2318.1133,N,11319.7210,E,1,06,3.7,55.1,M,-5.4,M,,0000*69
(1) UTC 时间,格式为 hhmmss.ss;【095528.000】
(2) 纬度,格式为 ddmm.mmmmm(度分格式);【2318.1133】
(3) 纬度半球, N 或 S(北纬或南纬);【N】
(4) 经度,格式为 dddmm.mmmmm(度分格式);【11319.7210】
(5) 经度半球, E 或 W(东经或西经);【E】
(6) GPS 状态, 0=未定位, 1=非差分定位, 2=差分定位;【1】
(7) 正在使用的用于定位的卫星数量(00~12)【06】
(8) HDOP 水平精确度因子(0.5~99.9)【3.7】
(9) 海拔高度(-9999.9 到 9999.9 米)【55.1】
(10) 大地水准面高度(-9999.9 到 9999.9 米)【-5.4】
(11) 差分时间(从最近一次接收到差分信号开始的秒数,非差分定位,此项为空)【空】
(12) 差分参考基站标号(0000 到 1023, 首位 0 也将传送,非差分定位,此项为空)【空】
void NMEA_GNGGA_Analysis(nmea_msg *gpsx,u8 *buf)
{
u8 *p1,dx; //dx不知道干嘛用的
u8 posx; //是变量
p1=(u8*)strstr((const char *)buf,"$GNGGA");
//从首地址放入一般都是GPS所有数据,然后的到p1为$GNGGA的首地址
posx=NMEA_Comma_Pos(p1,6);//得到GPS状态//代表逗号所在位置的偏移
if(posx!=0XFF)gpsx->gpssta=NMEA_Str2num(p1+posx,&dx); buf:数字存储区,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);
}
【3】分析GNGSA信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
/*
$GNGSA 语句的基本格式如下:
$GNGSA,(1),(2),(3),(3),(3),(3),(3),(3),(3),(3),(3),(3),(3),(3),(4),(5),(6)*hh(CR)(LF)
举例如下:
$GNGSA,A,3,14,22,24,12,,,,,,,,,4.2,3.7,2.1*2D
$GNGSA,A,3,209,214,,,,,,,,,,,4.2,3.7,2.1*21
注 1: 精度因子值越小,则准确度越高。
(1) 模式, M = 手动, A = 自动。【A】
(2) 定位类型, 1=未定位, 2=2D 定位, 3=3D 定位。【3】
(3) 正在用于定位的卫星号(01~32)【3】【14】【22】(一共有12个)
(4) PDOP 综合位置精度因子(0.5-99.9)【4.2】
(5) HDOP 水平精度因子 1(0.5-99.9)【3.7】
(6) VDOP 垂直精度因子(0.5-99.9)【2.1】
void NMEA_GNGSA_Analysis(nmea_msg *gpsx,u8 *buf)
{
u8 *p1,dx; //dx为后面用的但没有体现小数点右面保留几位啊
u8 posx; //
u8 i;
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); //将卫星编号逐1手机过来
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);
}
【4】分析GNRMC信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
/*
$GNRMC(推荐定位信息, Recommended Minimum Specific GPS/Transit Data)
$GNRMC 语句的基本格式如下:
$GNRMC,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)*hh(CR)(LF)
举例如下:
$GNRMC,095554.000,A,2318.1327,N,11319.7252,E,000.0,005.7,081215,,,A*73
(1) UTC 时间, hhmmss(时分秒)【095554.000】
(2) 定位状态, A=有效定位, V=无效定位【A】
(3) 纬度 ddmm.mmmmm(度分)【2318.1327】
(4) 纬度半球 N(北半球)或 S(南半球)【N】
(5) 经度 dddmm.mmmmm(度分)【11319.7252】
(6) 经度半球 E(东经)或 W(西经)【E】
(7) 地面速率(000.0~999.9 节)【000.0】
(8) 地面航向(000.0~359.9 度,以真北方为参考基准)【005.7】
(9) UTC 日期, ddmmyy(日月年)【081215】
(10) 磁偏角(000.0~180.0 度,前导位数不足则补 0)【】
(11) 磁偏角方向, E(东)或 W(西)【】
(12) 模式指示(A=自主定位, D=差分, E=估算, N=数据无效)【A】
void NMEA_GNRMC_Analysis(nmea_msg *gpsx,u8 *buf)
{
u8 *p1,dx;
u8 posx;
u32 temp; //32位变量去读取时间数据,格式是不清楚的,实际数字例如095554.000
float rs;
p1=(u8*)strstr((const char *)buf,"$GNRMC");//"$GNRMC",经常有&和GNRMC分开的情况,故只判断GPRMC.
posx=NMEA_Comma_Pos(p1,1); //得到UTC时间
if(posx!=0XFF)
{ //这里数据存在一个算法,我不知道这个时间是什么原理,但是能算出来
temp=NMEA_Str2num(p1+posx,&dx)/NMEA_Pow(10,dx); //得到UTC时间,去掉ms
gpsx->utc.hour=temp/10000;//95554/10000 = 9
gpsx->utc.min=(temp/100)%100;//955%100 = 55
gpsx->utc.sec=temp%100; //54
}
posx=NMEA_Comma_Pos(p1,3); //得到纬度 ddmm.mmmmm(度分)【2318.1327】
if(posx!=0XFF)
{
temp=NMEA_Str2num(p1+posx,&dx);//取整2318
gpsx->latitude=temp/NMEA_Pow(10,dx+2); //得到°//23°
rs=temp%NMEA_Pow(10,dx+2); //得到'//2318%(10^2)=18'
//【这句子历程是5,我跟人感觉好像是6呢,好奇怪,这里怎么算出来的】
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); //得到经度
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,9); //得到UTC日期
if(posx!=0XFF)
{
temp=NMEA_Str2num(p1+posx,&dx); //得到UTC日期
gpsx->utc.date=temp/10000;
gpsx->utc.month=(temp/100)%100;
gpsx->utc.year=2000+temp%100;
}
}
【5】GNVTG(地面速度信息, Track Made Good and Ground Speed)
$GNVTG 语句的基本格式如下:
$GNVTG,(1),T,(2),M,(3),N,(4),K,(5)*hh(CR)(LF)
举例如下:
$GNVTG,005.7,T,,M,000.0,N,000.0,K,A*11
(1)以真北为参考基准的地面航向(000~359 度,前面的 0 也将被传输)【005.7】
(2)以磁北为参考基准的地面航向(000~359 度,前面的 0 也将被传输)【】
(3) 地面速率(000.0~999.9 节,前面的 0 也将被传输)【000.0】
(4) 地面速率(0000.0~1851.8 公里/小时,前面的 0 也将被传输)【000.0】
(5) 模式指示(A=自主定位, D=差分, E=估算, N=数据无效)【A】
*/
//分析GNVTG信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GNVTG_Analysis(nmea_msg *gpsx,u8 *buf)
{
u8 *p1,dx;
u8 posx;
p1=(u8*)strstr((const char *)buf,"$GNVTG");
//$GNVTG,(1),T,(2),M,(3),N,(4),K,(5)*hh(CR)(LF)
//第7个逗号后面是(4)即地面速率
posx=NMEA_Comma_Pos(p1,7); //得到地面速率
if(posx!=0XFF)
{
gpsx->speed=NMEA_Str2num(p1+posx,&dx); //得到速度,后面为啥扩大1000倍表示不理解
if(dx<3)gpsx->speed*=NMEA_Pow(10,3-dx); //确保扩大1000倍
}
}
//提取NMEA-0183信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void GPS_Analysis(nmea_msg *gpsx,u8 *buf)
{
NMEA_GPGSV_Analysis(gpsx,buf); //GPGSV解析
NMEA_BDGSV_Analysis(gpsx,buf); //BDGSV解析
NMEA_GNGGA_Analysis(gpsx,buf); //GNGGA解析
NMEA_GNGSA_Analysis(gpsx,buf); //GPNSA解析
NMEA_GNRMC_Analysis(gpsx,buf); //GPNMC解析
NMEA_GNVTG_Analysis(gpsx,buf); //GPNTG解析
}