#include "NMEA.h"
#include "stdio.h"
#include "stdarg.h"
#include "string.h"
#include "math.h"
//////////////////////////////////////////////////////////////////////////////////
//BC20 NMEA解析
//////////////////////////////////////////////////////////////////////////////////
//从buf里面得到第cx个逗号所在的位置
//返回值:0~0XFE,代表逗号所在位置的偏移.
// 0XFF,代表不存在第cx个逗号
uint8_t NMEA_Comma_Pos(uint8_t *buf,uint8_t cx)
{
uint8_t *p=buf;
while(cx)
{
if(*buf=='*'||*buf<' '||*buf>'z')return 0XFF;//遇到'*'或者非法字符,则不存在第cx个逗号
if(*buf==',')cx--;
buf++;
}
return buf-p;
}
//m^n函数
//返回值:m^n次方.
uint32_t NMEA_Pow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;
return result;
}
//str转换为数字,以','或者'*'结束
//buf:数字存储区
//dx:小数点位数,返回给调用函数
//返回值:转换后的数值
int NMEA_Str2num(uint8_t *buf,uint8_t*dx)
{
uint8_t *p=buf;
uint32_t ires=0,fres=0;
uint8_t ilen=0,flen=0,i;
uint8_t mask=0;
int res;
while(1) //得到整数和小数的长度
{
if(*p=='-'){mask|=0X02;p++;}//是负数
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)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;
}
//分析GPGSV信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GPGSV_Analysis(nmea_msg *gpsx,uint8_t *buf)
{
uint8_t *p,*p1,dx;
uint8_t len,i,j,slx=0;
uint8_t posx;
p=buf;
p1=(uint8_t*)strstr((const char *)p,"$GPGSV");
len=p1[7]-'0'; //得到GPGSV语句条数
posx=NMEA_Comma_Pos(p1,3); //得到可见卫星总数
if(posx!=0XFF)gpsx->svnum=NMEA_Str2num(p1+posx,&dx);
for(i=0;i<len;i++)
{
p1=(uint8_t*)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信息
}
}
//分析GBGSV信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GBGSV_Analysis(nmea_msg *gpsx,uint8_t *buf)
{
uint8_t *p,*p1,dx;
uint8_t len,i,j,slx=0;
uint8_t posx;
p=buf;
p1=(uint8_t*)strstr((const char *)p,"$GBGSV");
len=p1[7]-'0'; //得到GBGSV的条数
posx=NMEA_Comma_Pos(p1,3); //得到可见北斗卫星总数
if(posx!=0XFF)gpsx->beidou_svnum=NMEA_Str2num(p1+posx,&dx);
for(i=0;i<len;i++)
{
p1=(uint8_t*)strstr((const char *)p,"$GBGSV");
for(j=0;j<4;j++)
{
posx=NMEA_Comma_Pos(p1,4+j*4);
if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_num=NMEA_Str2num(p1+posx,&dx); //得到卫星编号
else break;
posx=NMEA_Comma_Pos(p1,5+j*4);
if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_eledeg=NMEA_Str2num(p1+posx,&dx);//得到卫星仰角
else break;
posx=NMEA_Comma_Pos(p1,6+j*4);
if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_azideg=NMEA_Str2num(p1+posx,&dx);//得到卫星方位角
else break;
posx=NMEA_Comma_Pos(p1,7+j*4);
if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_sn=NMEA_Str2num(p1+posx,&dx); //得到卫星信噪比
else break;
slx++;
}
p=p1+1;//切换到下一个BDGSV信息
}
}
//分析GNGGA信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GNGGA_Analysis(nmea_msg *gpsx,uint8_t *buf)
{
uint8_t *p1,dx;
uint8_t posx;
p1=(uint8_t*)strstr((const char *)buf,"$GNGGA");
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);
}
//分析GNGSA信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GNGSA_Analysis(nmea_msg *gpsx,uint8_t *buf)
{
uint8_t *p1,dx;
uint8_t posx;
uint8_t i;
p1=(uint8_t*)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);
}
//分析GNRMC信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GNRMC_Analysis(nmea_msg *gpsx,uint8_t *buf)
{
uint8_t *p1,dx;
uint8_t posx;
uint32_t temp;
float rs;
p1=(uint8_t*)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;
gpsx->utc.min=(temp/100)%100;
gpsx->utc.sec=temp%100;
}
posx=NMEA_Comma_Pos(p1,3); //得到纬度
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); //得到经度
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;
}
}
//分析GNVTG信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GNVTG_Analysis(nmea_msg *gpsx,uint8_t *buf)
{
uint8_t *p1,dx;
uint8_t posx;
p1=(uint8_t*)strstr((const char *)buf,"$GNVTG");
posx=NMEA_Comma_Pos(p1,7); //得到地面速率
if(posx!=0XFF)
{
gpsx->speed=NMEA_Str2num(p1+posx,&dx);
if(dx<3)gpsx->speed*=NMEA_Pow(10,3-dx); //确保扩大1000倍
}
}
//提取NMEA-0183信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_Analysis(nmea_msg *gpsx,uint8_t *buf)
{
NMEA_GNRMC_Analysis(gpsx,buf); //GPNMC解析
NMEA_GPGSV_Analysis(gpsx,buf); //GPGSV解析
NMEA_GBGSV_Analysis(gpsx,buf); //GBGSV解析
NMEA_GNGGA_Analysis(gpsx,buf); //GNGGA解析
NMEA_GNGSA_Analysis(gpsx,buf); //GPNSA解析
NMEA_GNVTG_Analysis(gpsx,buf); //GPNTG解析
}
头文件
#ifndef __NMEA_H
#define __NMEA_H
#include "stm32l0xx_hal.h"
//GPS NMEA-0183协议重要参数结构体定义
//卫星信息
__packed typedef struct
{
uint8_t num; //卫星编号
uint8_t eledeg; //卫星仰角
uint16_t azideg; //卫星方位角
uint8_t sn; //信噪比
}nmea_satellitemsg;
//北斗 NMEA-0183协议重要参数结构体定义
//卫星信息
__packed typedef struct
{
uint8_t beidou_num; //卫星编号
uint8_t beidou_eledeg; //卫星仰角
uint16_t beidou_azideg; //卫星方位角
uint8_t beidou_sn; //信噪比
}beidou_nmea_satellitemsg;
//UTC时间信息
__packed typedef struct
{
uint16_t year; //年份
uint8_t month; //月份
uint8_t date; //日期
uint8_t hour; //小时
uint8_t min; //分钟
uint8_t sec; //秒钟
}nmea_utc_time;
//NMEA 0183 协议解析后数据存放结构体
__packed typedef struct
{
uint8_t svnum; //可见GPS卫星数
uint8_t beidou_svnum; //可见GPS卫星数
nmea_satellitemsg slmsg[12]; //最多12颗GPS卫星
beidou_nmea_satellitemsg beidou_slmsg[12]; //暂且算最多12颗北斗卫星
nmea_utc_time utc; //UTC时间
uint32_t latitude; //纬度 分扩大100000倍,实际要除以100000
uint8_t nshemi; //北纬/南纬,N:北纬;S:南纬
uint32_t longitude; //经度 分扩大100000倍,实际要除以100000
uint8_t ewhemi; //东经/西经,E:东经;W:西经
uint8_t gpssta; //GPS状态:0,未定位;1,非差分定位;2,差分定位;6,正在估算.
uint8_t posslnum; //用于定位的GPS卫星数,0~12.
uint8_t possl[12]; //用于定位的卫星编号
uint8_t fixmode; //定位类型:1,没有定位;2,2D定位;3,3D定位
uint16_t pdop; //位置精度因子 0~500,对应实际值0~50.0
uint16_t hdop; //水平精度因子 0~500,对应实际值0~50.0
uint16_t vdop; //垂直精度因子 0~500,对应实际值0~50.0
int altitude; //海拔高度,放大了10倍,实际除以10.单位:0.1m
uint16_t speed; //地面速率,放大了1000倍,实际除以10.单位:0.001公里/小时
}nmea_msg;
int NMEA_Str2num(uint8_t *buf,uint8_t*dx);
void NMEA_Analysis(nmea_msg *gpsx,uint8_t *buf);
void NMEA_GPGSV_Analysis(nmea_msg *gpsx,uint8_t *buf);
void NMEA_BDGSV_Analysis(nmea_msg *gpsx,uint8_t *buf);
void NMEA_GNGGA_Analysis(nmea_msg *gpsx,uint8_t *buf);
void NMEA_GNGSA_Analysis(nmea_msg *gpsx,uint8_t *buf);
void NMEA_GNGSA_Analysis(nmea_msg *gpsx,uint8_t *buf);
void NMEA_GNRMC_Analysis(nmea_msg *gpsx,uint8_t *buf);
void NMEA_GNVTG_Analysis(nmea_msg *gpsx,uint8_t *buf);
#endif