一、定位数据坐标系
目前国内主要有以下三种坐标系:
WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。
GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
buff:
$GPGSA,A,2,05,,,,,,,,,,,,4.7,4.6,1.0*36
buff:
$GPGSV,3,1,12,05,11,108,13,24,67,144,26,29,03,206,29,10,15,306,*73
buff:
$GPGSV,3,2,12,23,42,320,,32,00,250,,39,,,37,40,,,34*76
buff:
$GPGSV,3,3,12,41,,,44,43,,,38,45,,,38,49,,,34*73
buff:
$GPGGA,044843.00,3150.789943,N,11717.308136,E,1,01,4.3,27.9,M,-2.0,M,,*47
buff:
$GPVTG,50.5,T,54.9,M,1.4,N,2.7,K,A*2B
buff:
$GPRMC,044843.00,A,3150.789943,N,11717.308136,E,1.4,50.5,031120,4.4,W,A*1D
==========================================================
== Global Navigation Satellite System
== Author:mate
== Email:mate20201321@aliyun.com
== Platform:A3352+EC20 R2.1
==========================================================
== GPS state bit : A [A:有效状态 V:无效状态]
== GPS mode bit : A [A:自主定位 D:差分定位]
== Date : 2020-11-03
== Time : 12:48:43
== 纬度 : 北纬:31度50分47秒
== 经度 : 东经:117度17分18秒
== 速度 : 1.400 kn(节)
==
==========================================================
buff:
$GPGSA,A,2,05,,,,,,,,,,,,4.4,4.3,1.0*30
buff:
buff:
$GPGSV,3,1,12,05,11,108,13,24,67,144,26,29,03,206,29,10,15,306,*73
buff:
buff:
$GPGSV,3,2,12,23,42,320,,32,00,250,,39,,,37,40,,,34*76
buff:
buff:
$GPGSV,3,3,12,41,,,44,43,,,38,45,,,38,49,,,34*73
buff:
buff:
$GPGGA,044844.00,3150.790220,N,11717.308554,E,1,01,500.0,27.9,M,-2.0,M,,*44
buff:
buff:
$GPVTG,50.1,T,54.5,M,1.4,N,2.7,K,A*23
buff:
buff:
$GPRMC,044844.00,A,3150.790220,N,11717.308554,E,1.4,50.1,031120,4.4,W,A*18
==========================================================
== Global Navigation Satellite System
== Author:mate
== Email:mate20201321@aliyun.com
== Platform:A3352+EC20 R2.1
==========================================================
== GPS state bit : A [A:有效状态 V:无效状态]
== GPS mode bit : A [A:自主定位 D:差分定位]
== Date : 2020-11-03
== Time : 12:48:44
== 纬度 : 北纬:31度50分47秒
== 经度 : 东经:117度17分18秒
== 速度 : 1.400 kn(节)
==
==========================================================
二、GPS模组数据处理
GPS上电后,每隔一定的时间就会返回一定格式的数据,数据格式为:
$信息类型,x,x,x,x,x,x,x,x,x,x,x,x,x
每行开头的字符都是‘$’,接着是信息类型,后面是数据,以逗号分隔开。一行完整的数据如下:
$GNGGA,093100.000,3151.10397,N,11707.63497,E,1,11,2.6,214.7,M,-5.0,M,,*50
$GNGLL,3151.10397,N,11707.63497,E,093100.000,A,A*49
$GNGSA,A,3,02,05,12,20,25,,,,,,,,3.9,2.6,2.9,1*35
$GNGSA,A,3,10,13,28,33,38,41,,,,,,,3.9,2.6,2.9,4*36
$GPGSV,3,1,10,02,60,349,39,05,38,247,41,06,49,057,27,12,30,263,31,0*65
$GPGSV,3,2,10,13,,,24,17,17,143,,19,39,141,29,20,21,248,39,0*5F
$GPGSV,3,3,10,25,15,299,44,195,,,26,0*6C
$BDGSV,3,1,09,05,,,35,10,37,218,28,13,52,318,41,27,,,36,0*72
$BDGSV,3,2,09,28,48,323,46,33,73,210,33,38,61,342,42,40,,,37,0*42
$BDGSV,3,3,09,41,20,218,40,0*45
$GNRMC,093100.000,A,3151.10397,N,11707.63497,E,0.00,0.00,280521,,,A,V*08
$GNVTG,0.00,T,,M,0.00,N,0.00,K,A*23
$GNZDA,093100.000,28,05,2021,00,00*4D
$GPTXT,01,01,01,ANTENNA OK*35
1、NMEA-0183协议
NMEA-0183协议是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine Electronics Associa-tion)制定的一套通讯协议。GPS接收机根据NMEA-0183协议的标准规范,将位置、速度等信息通过串口传送到PC机、PDA等设备。
NMEA-0183协议是GPS接收机应当遵守的标准协议,也是目前GPS接收机上使用最广泛的协议,大多数常见的GPS接收机、GPS数据处理软件、导航 软件都遵守或者至少兼容这个协议。
NMEA-0183协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有$GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL等。
(1) GPS DOP and Active Satellites(GSA)当前卫星信息
$GPGSA,<1>,<2>,<3>,<3>,,,,,<3>,<3>,<3>,<4>,<5>,<6>,<7>
<1>模式 :M =手动, A =自动。
<2>定位型式 1 =未定位, 2 =二维定位, 3 =三维定位。
<3>PRN 数字:01至 32表天空使用中的卫星编号,最多可接收12颗卫星信息。
<4> PDOP位置精度因子(0.5-99.9)
<5> HDOP水平精度因子(0.5-99.9)
<6> VDOP垂直精度因子(0.5-99.9)
<7> Checksum.(检查位).
这里整条语句是一个文本行,行中以逗号“,”隔开各个字段,每个字段的大小(长度)不一,这里的示例只是一种可能,并不能认为字段的大小就如上述例句一样。
(2)GPS Satellites in View(GSV)可见卫星信息
$GPGSV, <1>,<2>,<3>,<4>,<5>,<6>,<7>,?<4>,<5>,<6>,<7>,<8>
<1> GSV语句的总数
<2> 本句GSV的编号
<3> 可见卫星的总数,00至12。
<4> 卫星编号, 01至32。
<5>卫星仰角, 00至90度。
<6>卫星方位角, 000至359度。实际值。
<7>讯号噪声比(C/No),00 至99 dB;无表未接收到讯号。
<8>Checksum.(检查位).
第<4>,<5>,<6>,<7>项个别卫星会重复出现,每行最多有四颗卫星。
其余卫星信息会于次一行出现,若未使用,这些字段会空白。
(3)Global Positioning System Fix Data(GGA)GPS定位信息
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh
<1> UTC时间,hhmmss(时分秒)格式
<2> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
<3> 纬度半球N(北半球)或S(南半球)
<4> 经度dddmm.mmmm(度分)格式(前面的0也将被传输)
<5> 经度半球E(东经)或W(西经)
<6> GPS状态:0=未定位,1=非差分定位,2=差分定位,6=正在估算
<7> 正在使用解算位置的卫星数量(00-12)(前面的0也将被传输)
<8> HDOP水平精度因子(0.5-99.9)
<9> 海拔高度(-9999.9-99999.9)
<10> 地球椭球面相对大地水准面的高度
<11> 差分时间(从最近一次接收到差分信号开始的秒数,如果不是差分定位将为空)
<12> 差分站ID号0000~1023(前面的0也将被传输,如果不是差分定位将为空)
hh——校验和(check sum),$与*之间的(不包括这两个字符)所有字符ASCII码的校验和(各字节做异或运算,得到校验和后,再转换16进制格式的ASCII字符。)
(4)Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐定位信息
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
<1> UTC时间,hhmmss(时分秒)格式
<2> 定位状态,A=有效定位,V=无效定位
<3> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
<4> 纬度半球N(北半球)或S(南半球)
<5> 经度dddmm.mmmm(度分)格式(前面的0也将被传输)
<6> 经度半球E(东经)或W(西经)
<7> 地面速率(000.0-99.9节,前面的0也将被传输)
<8> 地面航向(000.0-359.9度,以真北为参考基准,前面的0也将被传输)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0-180.0度,前面的0也将被传输)
<11> 磁偏角方向,E(东)或W(西)
<12> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)
hh——校验和(check sum),$与*之间的(不包括这两个字符)所有字符ASCII码的校验和(各字节做异或运算,得到校验和后,再转换16进制格式的ASCII字符。)
(5)Track Made Good and Ground Speed(VTG)地面速度信息
$GPVTG,<1>,T,<2>,M,<3>,N,<4>,K,<5>*hh
<1> 以真北为参考基准的地面航向(000-359度,前面的0也将被传输)
<2> 以磁北为参考基准的地面航向(000-359度,前面的0也将被传输)
<3> 地面速率(000.0-999.9节,前面的0也将被传输)
<4> 地面速率(0000.0-1851.8公里/小时,前面的0也将被传输)
<5> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)
hh——校验和(check sum),$与*之间的(不包括这两个字符)所有字符ASCII码的校验和(各字节做异或运算,得到校验和后,再转换16进制格式的ASCII字符。)
2、GPS数据获取与解析
(1)根据协议定义GPS数据结构体
typedef unsigned int UINT;
/*$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)*/
typedef struct __gprmc__
{
UINT time_GT; /*<1> UTC时间,hhmmss(时分秒)格式 */
char pos_state; /*<2> 定位状态,A=有效定位,V=无效定位 */
float latitude; /*<3> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)) */
char NorS; /*<4> 纬度半球N(北半球)或S(南半球) */
float longitude; /*<5> 经度dddmm.mmmm(度分)格式(前面的0也将被传输) */
char EorW; /*<6> 经度半球E(东经)或W(西经) */
float speed; /*<7> 地面速率(000.0~999.9节,前面的0也将被传输) */
float direction; /*<8> 地面航向(000.0~359.9度,以真北为参考基准,前面的0也将被传输) */
UINT Recdate; /*<9> UTC日期,ddmmyy(日月年)格式 */
float declination; /*<10> 磁偏角(000.0~180.0度,前面的0也将被传输) */
char dd; /*<11> 磁偏角方向,E(东)或W(西) */
char mode; /*<12> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)*/
}GPRMC;
/*$GPGGA,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)*/
typedef struct __gpgga__
{
UINT time_GT; /*<1> UTC时间,hhmmss(时分秒)格式 */
float latitude; /*<2> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)) */
char NorS; /*<3> 纬度半球N(北半球)或S(南半球) */
float longitude; /*<4> 经度dddmm.mmmm(度分)格式(前面的0也将被传输) */
char EorW; /*<5> 经度半球E(东经)或W(西经) */
unsigned char pos_state; /*<6> 定位状态,1=有效定位,0=无效定位 */
unsigned char NumofSat; /*<7> 使用卫星数量,从00到12(第一个零也将传送) */
float Lev_accu; /*<8> 水平精确度,0.5到99.9 */
float Sea_level; /*<9> 天线离海平面的高度,-9999.9到9999.9米M指单位米 */
float Geoid; /*<10> 大地水准面高度,-9999.9到9999.9米M指单位米 */
char data_dura; /*<11> 差分GPS数据期限(RTCMSC-104),最后设立RTCM传送的秒数量 */
char station_label; /*<12> 差分参考基站标号,从0000到1023(首位0也将传送)*/
}GPGGA;
(2)通过串口获取GPS字符串数据
int Get_GPS_Data(int fd_read, unsigned char *SavdBuf, size_t SavdBufSize )
{
char ReadBuf[GPS_LEN];
GPRMC gprmc;
memset(&gprmc, 0, sizeof(GPRMC));
struct pollfd PollFd;
PollFd.fd = fd_read;
PollFd.events = POLLIN;
if(poll(&PollFd,1,1) && (PollFd.revents & POLLIN) ) //等待数据s
{
memset(ReadBuf, 0, sizeof(ReadBuf));
size_t ReadSize = read(PollFd.fd, ReadBuf, sizeof(ReadBuf));
if(ReadSize <0)
{
perror("read error");
return -1;
}
#if IS_DEBUG
printf("ReadBuf:\n");
printf("%s",ReadBuf);
#endif
memset(&gprmc, 0 , sizeof(gprmc));
if( (Analyse_GPRMC_GPS(ReadBuf, ReadSize, &gprmc)== 0 ) && (gprmc.pos_state == 'A') )
{
GPS_Data_Resolve ( &gprmc , SavdBuf, SavdBufSize );
#if IS_DEBUG
Print_GPRMC_GPS(&gprmc);
printf("纬度 = %f\n", Bytes2Float ( SavdBuf , sizeof(float)));
printf("精度 = %f\n", Bytes2Float ( SavdBuf+sizeof(float)+sizeof(char) , sizeof(float)));
#endif
return 0;
}
else
printf("Invalid location!\n");
return -1;
}
else
printf("NO GPS DATA!\n");
return -1;
}
(3)解析GPS数据字符串
int Analyse_GPRMC_GPS (char *ReadBuf, size_t ReadSize, GPRMC *gps_data)
{
char *ptr=NULL;
if(gps_data==NULL)
return -1;
if(strlen(ReadBuf)<10)
return -1;
/* 如果buff字符串中包含字符"$GPRMC"则将$GPRMC的地址赋值给ptr */
if((ptr=strstr(ReadBuf,"$GPRMC"))==NULL)
return -1;
int i=0;
for( i=0; i<ReadSize-(ptr-ReadBuf);i++) //查找一行$GPRMC数据的结尾(换行符)
{
if(ptr[i] == '\n') //说明是完整的一组"$GPRMC"数据,否则就丢弃
break;
}
if(i == ReadSize-(ptr-ReadBuf))
return -1;
/* sscanf函数为从字符串输入,意思是将ptr内存单元的值作为输入分别输入到后面的结构体成员 */
sscanf(ptr,"$GPRMC,%d.00,%c,%f,%c,%f,%c,%f,%f,%d,%f,%c,%c*",
&(gps_data->time_GT), &(gps_data->pos_state),
&(gps_data->latitude),&(gps_data->NorS), &(gps_data->longitude), &(gps_data->EorW),
&(gps_data->speed), &(gps_data->direction), &(gps_data->Recdate),
&(gps_data->declination), &(gps_data->dd), &(gps_data->mode));
return 0;
}
(4)GPS数据转为通信协议格式
ssize_t GPS_Data_Resolve (GPRMC *gps_data, unsigned char *ResolvedBuf, size_t ResolvedBufMaxSize )
{
size_t PackagedDataSize = 0;
unsigned char tempbuf[sizeof(float)] = { 0 };
//纬度转为字节
Float2Bytes ( gps_data->latitude, tempbuf ,sizeof(float));
PackagedDataSize = PackingData( 1, ResolvedBuf, ResolvedBufMaxSize, PackagedDataSize ,tempbuf ,sizeof(float) );
//保存南北纬字符
ResolvedBuf[PackagedDataSize] = gps_data->NorS;
PackagedDataSize++;
//经度转为字节
memset(tempbuf, 0, sizeof(float));
Float2Bytes ( gps_data->longitude, tempbuf ,sizeof(float));
PackagedDataSize = PackingData( 1, ResolvedBuf, ResolvedBufMaxSize, PackagedDataSize ,tempbuf ,sizeof(float) );
//保存东西经字符
ResolvedBuf[PackagedDataSize] = gps_data->EorW;
PackagedDataSize++;
//速度转为字节
memset(tempbuf, 0, sizeof(float));
Int2Bytes ((long)(gps_data->speed * 10), tempbuf , 2);
PackagedDataSize = PackingData( 1, ResolvedBuf, ResolvedBufMaxSize, PackagedDataSize ,tempbuf ,2);
return PackagedDataSize;
}
(5)显示解析结果
int Print_GPRMC_GPS (GPRMC *gps_data)
{
printf("\n\n");
printf("==========================================================\n");
printf("== Global Navigation Satellite System \n");
printf("== Author : mate \n");
printf("== Email : mate20201321@aliyun.com \n");
printf("== Platform : A3352+EC20 R2.1 \n");
printf("==========================================================\n");
printf("== GPS state : %c [A:Available V:Void] \n", gps_data->pos_state);
printf("== GPS mode : %c [A:Autonomic D:DGPS] \n", gps_data->mode);
printf("== Date : 20%02d-%02d-%02d \n", gps_data->Recdate%100,(gps_data->Recdate%10000)/100,gps_data->Recdate/10000);
printf("== Time : %02d:%02d:%02d \n", (gps_data->time_GT/10000+8)%24,(gps_data->time_GT%10000)/100,gps_data->time_GT%100);
printf("== latitude : %c %d° %d' %d\" \n", gps_data->NorS, ((int)gps_data->latitude) / 100, (int)(gps_data->latitude - ((int)gps_data->latitude / 100 * 100)), (int)(((gps_data->latitude - ((int)gps_data->latitude / 100 * 100)) - ((int)gps_data->latitude - ((int)gps_data->latitude / 100 * 100))) * 60.0));
printf("== longitude : %c %d° %d' %d\" \n", gps_data->EorW, ((int)gps_data->longitude) / 100, (int)(gps_data->longitude - ((int)gps_data->longitude / 100 * 100)), (int)(((gps_data->longitude - ((int)gps_data->longitude / 100 * 100)) - ((int)gps_data->longitude - ((int)gps_data->longitude / 100 * 100))) * 60.0));
printf("== velocity : %.3f kn \n", gps_data->speed); //1kn = 0.5144444 m/s
printf("==\n");
printf("==========================================================\n");
return 0;
}