NMEA是一套定义接收机输出的标准信息,有几种不同的格式,每种都是独立相关的ASCII格式,逗点隔开数据流,数据流长度从30-100字符不等,通常以每秒间隔选择输出,最常用的格式为"GGA",它包含了定位时间,纬度,经度,高度,定位所用的卫星数,其他的有速度,跟踪,日期等。
NMEA-0183协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有$GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL等,说明如下:
$GPGGA(定位信息)
eg:$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F
$GPGSA(当前卫星信息)
eg:$GPGSA,A,3,01,20,19,13,,,,,,,,,40.4,24.4,32.2*0A
$GPGSV(可见卫星信息)
eg:$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70
$GPVTG(地面速度信息)
eg:$GPVTG,89.68,T,,M,0.00,N,0.0,K*5F
$GPGLL(地理定位信息)
$GPGLL,4250.5589,S,14718.5084,E,092204.999,A*2D
以下以代码段来说明$GPGGA的解析过程,其他类推。假设已从串口读到字符串。
(1)结构体信息
typedef struct{ int year; int month; int day; int hour; int minute; int second; }date_time; typedef struct{ date_time D;//时间 char status; //接收状态 double latitude; //纬度 double longitude; //经度 char NS; //南北极 char EW; //东西 double speed; //速度 double high; //高度 }GPS_INFO;
(2)得到指定序号的逗号位置,以解析各个定义段
static int GetComma(int num,char *str) { int i,j=0; int len=strlen(str); for(i=0;i<len;i++) { if(str[i]==',')j++; if(j==num)return i+1; //返回当前找到的逗号位置的下一个位置 } return 0; }
得到精度纬度等高精度数据
static double get_double_number(char *s) { char buf[128]; int i; double rev; i=GetComma(1,s); //得到数据长度 strncpy(buf,s,i); buf[i]=0; //加字符串结束标志 rev=atof(buf); //字符串转float return rev; }
将世界时转换为北京时间
static void UTC2BTC(date_time *GPS) { //*************************************************** //如果秒号先出,再出时间数据,则将时间数据+1秒 GPS->second++; //加一秒 if(GPS->second>59){ GPS->second=0; GPS->minute++; if(GPS->minute>59){ GPS->minute=0; GPS->hour++; } } //*************************************************** GPS->hour+=8; //北京时间跟UTC时间相隔8小时 if(GPS->hour>23) { GPS->hour-=24; GPS->day+=1; if(GPS->month==2 ||GPS->month==4 ||GPS->month==6 ||GPS->month==9 ||GPS->month==11 ){ if(GPS->day>30){ //上述几个月份是30天每月,2月份还不足30 GPS->day=1; GPS->month++; } } else{ if(GPS->day>31){ //剩下的几个月份都是31天每月 GPS->day=1; GPS->month++; } } if(GPS->year % 4 == 0 ){// if(GPS->day > 29 && GPS->month ==2){ //闰年的二月是29天 GPS->day=1; GPS->month++; } } else{ if(GPS->day>28 &&GPS->month ==2){ //其他的二月是28天每月 GPS->day=1; GPS->month++; } } if(GPS->month>12){ GPS->month-=12; GPS->year++; } } }
(3)解析GPS数据,以line作为传入的字符串数据,GPS是待赋值的全局变量。以下第一行是字符顺序号
//0 7 0 4 6 0 6 8 0 90 0 3 0 9
//$GPRMC,091400,A,3958.9870,N,11620.3278,E,000.0,000.0,120302,005.6,W*62
//$GPGGA,091400,3958.9870,N,11620.3278,E,1,03,1.9,114.2,M,-8.3,M,,*5E
void gps_parse(char *line,GPS_INFO *GPS) { int i,tmp,start,end; char c; char* buf=line; c=buf[5]; if(c=='C'){//"GPRMC" GPS->D.hour =(buf[ 7]-'0')*10+(buf[ 8]-'0'); GPS->D.minute =(buf[ 9]-'0')*10+(buf[10]-'0'); GPS->D.second =(buf[11]-'0')*10+(buf[12]-'0'); tmp = GetComma(9,buf); //得到第9个逗号的下一字符序号 GPS->D.day =(buf[tmp+0]-'0')*10+(buf[tmp+1]-'0'); GPS->D.month =(buf[tmp+2]-'0')*10+(buf[tmp+3]-'0'); GPS->D.year =(buf[tmp+4]-'0')*10+(buf[tmp+5]-'0')+2000; //------------------------------ GPS->status =buf[GetComma(2,buf)]; //状态 GPS->latitude =get_double_number(&buf[GetComma(3,buf)]); //纬度 GPS->NS =buf[GetComma(4,buf)]; //南北纬 GPS->longitude=get_double_number(&buf[GetComma(5,buf)]); //经度 GPS->EW =buf[GetComma(6,buf)]; //东西经 UTC2BTC(&GPS->D); //转北京时间 } if(c=='A'){ //"$GPGGA" GPS->high = get_double_number(&buf[GetComma(9,buf)]); } }
显示解析结果
void show_gps(GPS_INFO *GPS) { printf("DATE : %ld-%02d-%02d \n",GPS->D.year,GPS->D.month,GPS->D.day); printf("TIME : %02d:%02d:%02d \n",GPS->D.hour,GPS->D.minute,GPS->D.second); printf("Latitude : %10.4f %c\n",GPS->latitude,GPS->NS); printf("Longitude: %10.4f %c\n",GPS->longitude,GPS->EW); printf("high : %10.4f \n",GPS->high); printf("STATUS : %c\n",GPS->status); }