资讯动态

GPS定位数据解析:从NMEA到实际坐标(5/10)

发布时间:2026/8/6 14:40:02 来源:尧图企业网站定制
GPS定位数据解析从NMEA到实际坐标(5/10)作者宋一平分类嵌入式 / 传感器 / 物联网预计阅读12分钟一、GPS基础知识1.1 GPS工作原理GPSGlobal Positioning System全球定位系统通过三球交汇定位原理确定位置┌─────────────────────────────────────────────────────┐ │ GPS定位原理 │ │ │ │ 卫星1 ◄─────┐ │ │ │ │ │ 卫星2 ◄─────┼─────► 接收机位置交点 │ │ │ │ │ 卫星3 ◄─────┘ │ │ │ │ 原理测量到3颗卫星的距离通过几何计算得出位置 │ │ 实际需要4颗卫星第4颗用于时间校准 │ │ │ └─────────────────────────────────────────────────────┘定位过程接收机接收卫星信号解算到各卫星的距离伪距通过几何计算得出经纬度、高度、时间1.2 NMEA协议介绍NMEA-0183是美国国家海洋电子协会制定的GPS数据格式标准。特点文本格式易于解析以$开头\r\n结尾逗号分隔各字段校验和可选常见语句类型语句全称内容$GNRMCRecommended Minimum推荐最小定位信息时间位置$GNGGAGlobal Positioning Fix Data定位数据高度卫星数$GNGSADOP and Active Satellites卫星精度因子$GPGSVSatellites in View可见卫星信息本项目使用$GNRMC包含时间和位置信息足以满足需求。1.3 $GNRMC语句结构$GNRMC,092846.400,A,3029.7317,N,10404.1784,E,000.0,183.8,190428,,,A*73\r\n字段内容说明$GNRMC语句标识推荐最小定位信息092846.400UTC时间09:28:46.400北京时间需8小时A定位状态A有效V无效3029.7317纬度30°29.7317’N纬度半球N北纬S南纬10404.1784经度104°04.1784’E经度半球E东经W西经000.0地速节1节1.852km/h183.8方位角度190428UTC日期2019年4月28日A模式A自动D差分二、NMEA数据解析2.1 经纬度格式转换NMEA中的经纬度是度分格式需要转换为十进制度原始数据3029.7317,N 含义30度29.7317分 转换公式 十进制度 度 分/60 计算 30 29.7317/60 30 0.49553 30.49553°代码实现// 将度分格式转换为十进制度doubledmm_to_decimal(doubledmm){intdegrees(int)(dmm/100);// 提取度doubleminutesdmm-degrees*100;// 提取分returndegreesminutes/60.0;// 转换为十进制度}// 示例doublelatdmm_to_decimal(3029.7317);// 结果30.495532.2 时间转换UTC时间需要转换为北京时间UTC8// UTC时间转北京时间voidutc_to_beijing(u8*hour){*hour(*hour8)%24;// 加8小时超过24取模}// 示例UTC 09:28:46 → 北京时间 17:28:462.3 解析函数实现核心解析函数按逗号分隔提取各字段/** * brief 从字符串中提取指定字段 * param pstr 源字符串 * param buf 输出缓冲区 * param buflen 缓冲区大小 * param s 起始逗号位置从0开始 * param e 结束逗号位置 * return 字段长度失败返回-1 */intcomma_split(unsignedchar*pstr,char*buf,intbuflen,ints,inte){inti0,k0;intcommaCount0;if(es)return-1;// 找到起始逗号while(pstr[i]!\0){if(pstr[i],)commaCount;if(commaCounts){ki;// 记录起始位置break;}i;}// 找到结束逗号提取字段while(pstr[i]!\0){if(pstr[i],)commaCount;if(commaCounte){if(i-k1buflen)return-1;memcpy(buf,pstrk,i-k);buf[i-k]\0;returni-k;}i;}return-1;}三、代码实现3.1 数据结构定义/** * brief GNRMC解析结果结构体 */structGNRMC_Info{// 时间信息unsignedintyear;// 年unsignedcharmonth;// 月unsignedcharday;// 日unsignedcharhour;// 时已转换为北京时间unsignedcharMinute;// 分unsignedcharsecond;// 秒// 位置信息unsignedintlongitude;// 经度整数部分乘以10000unsignedintlatitude;// 纬度整数部分乘以10000charlongitude_suffix;// 经度半球 E/Wcharlatitude_suffix;// 纬度半球 N/S// 状态unsignedcharposition_valid;// 定位是否有效};// 全局变量structGNRMC_InfoGNRMC;// 解析结果3.2 完整解析函数/** * brief 解析GNRMC语句 * param pstr NMEA字符串 * param pGNRMCInfo 输出结构体 * return 0:成功, 0:失败 */intGNRMC_Parse(unsignedchar*pstr,structGNRMC_Info*pGNRMCInfo){intlen0;intcommaCount0;doubletemp0;charbuf[32]{0};// 1. 统计逗号数量验证格式while(pstr[len]!\0){if(pstr[len],)commaCount;len;}if(commaCount!12)return-1;// GNRMC有12个逗号// 2. 验证起始字符$GNRMCif(strncmp((char*)pstr,$GNRMC,6)!0)return-1;// 3. 验证结束字符\r\nif(pstr[len-2]!\r||pstr[len-1]!\n)return-1;// 4. 解析UTC时间字段1lencomma_split(pstr,buf,sizeof(buf),1,2);if(len0)return-1;// 格式hhmmss.ssspGNRMCInfo-hour((buf[0]-0)*10(buf[1]-0)8)%24;// 转北京时间pGNRMCInfo-Minute(buf[2]-0)*10(buf[3]-0);pGNRMCInfo-second(buf[4]-0)*10(buf[5]-0);// 5. 解析定位状态字段2lencomma_split(pstr,buf,sizeof(buf),2,3);if(len!1)return-1;pGNRMCInfo-position_valid(buf[0]A)?1:0;// 6. 解析纬度字段3lencomma_split(pstr,buf,sizeof(buf),3,4);if(len0)return-1;tempatof(buf);pGNRMCInfo-latitude(unsignedint)(temp*10000);// 7. 解析纬度半球字段4lencomma_split(pstr,buf,sizeof(buf),4,5);if(len!1)return-1;pGNRMCInfo-latitude_suffixbuf[0];// 8. 解析经度字段5lencomma_split(pstr,buf,sizeof(buf),5,6);if(len0)return-1;tempatof(buf);pGNRMCInfo-longitude(unsignedint)(temp*10000);// 9. 解析经度半球字段6lencomma_split(pstr,buf,sizeof(buf),6,7);if(len!1)return-1;pGNRMCInfo-longitude_suffixbuf[0];// 10. 解析UTC日期字段9lencomma_split(pstr,buf,sizeof(buf),9,10);if(len0)return-1;// 格式ddmmyypGNRMCInfo-day(buf[0]-0)*10(buf[1]-0);pGNRMCInfo-month(buf[2]-0)*10(buf[3]-0);pGNRMCInfo-year(buf[4]-0)*10(buf[5]-0)2000;return0;}3.3 GPS数据处理任务u8 gps_recebuf[200];intgps_recelen;/** * brief GPS数据处理函数 */voidgps_process(void){// 仅在非加注状态时处理GPSif(EventSeek(var.Event_1,eSYS_SA|ePRICE_OPT,OR)0){// 切换串口到GPS模式_4052A_OUT0;// 4052模拟开关控制vTaskDelay(5);// 清空串口缓冲while(UARTRead(U_4,gps_recebuf,gps_recelen)0){vTaskDelay(5);}// 等待GPS数据while(UARTRead(U_4,gps_recebuf,gps_recelen)0){vTaskDelay(5);}// 解析GPS数据al_gps(gps_recebuf,gps_recelen);// 清空缓冲区memset(gps_recebuf,0,200);// 切换串口回其他模式_4052A_OUT1;}}/** * brief GPS数据回调函数 */voidal_gps(u8*buf,u8 len){char*prt;// 查找\r\n结尾prtstrstr((char*)buf,\r\n);if(prt!0){*(prt2)0x00;// 截断字符串// 解析GNRMCGNRMC_Parse(buf,GNRMC);if(GNRMC.position_valid0){// 定位有效打印信息printf(时间:%d-%d-%d %d:%d:%d\n,GNRMC.year,GNRMC.month,GNRMC.day,GNRMC.hour,GNRMC.Minute,GNRMC.second);printf(经纬度:%u.%c|%u.%c\n,GNRMC.longitude,GNRMC.longitude_suffix,GNRMC.latitude,GNRMC.latitude_suffix);}}}3.4 使用示例// 测试数据char*test_gps$GNRMC,092846.400,A,3029.7317,N,10404.1784,E,000.0,183.8,190428,,,A*73\r\n;intmain(void){// 解析GPS数据GNRMC_Parse((unsignedchar*)test_gps,GNRMC);// 输出结果if(GNRMC.position_valid){printf(定位有效\n);printf(日期: %d-%02d-%02d\n,GNRMC.year,GNRMC.month,GNRMC.day);printf(时间: %02d:%02d:%02d\n,GNRMC.hour,GNRMC.Minute,GNRMC.second);printf(经度: %u%c\n,GNRMC.longitude,GNRMC.longitude_suffix);printf(纬度: %u%c\n,GNRMC.latitude,GNRMC.latitude_suffix);}return0;}/* 输出 定位有效 日期: 2019-04-28 时间: 17:28:46 经度: 104041784E 纬度: 30297317N */四、实际应用4.1 本项目GPS使用方式玻璃水加注机中GPS用于设备位置上报┌─────────────────────────────────────────────────────┐ │ GPS数据流向 │ │ │ │ GPS模块 ──(串口)──► STM32 ──(GPRS)──► 云服务器 │ │ │ │ │ │ │ │ │ │ │ │ NMEA数据 解析处理 位置上报 │ │ │ └─────────────────────────────────────────────────────┘4.2 上传数据格式// 生成上报字符串voidrecord_make(void){u32 gpsx_xs0;u32 gpsy_xs0;// 经纬度转换gpsx_xs(GNRMC.longitude%1000000)/6;// 小数部分gpsy_xs(GNRMC.latitude%1000000)/6;// 生成上报字符串sprintf((char*)var.record,id%s|total%d|money%d|price%d|GPSX%u.%5u|GPSY%u.%5u|,db_var.id,var.db_tpu/10,(var.db_tpu*var.price)/1000,var.price,GNRMC.longitude/1000000,gpsx_xs,GNRMC.latitude/1000000,gpsy_xs);// 示例输出// id00000001|total20|money200|price10|GPSX104.04178|GPSY30.29731|}4.3 硬件连接本项目使用ATGM336HGPS模块┌─────────────────────────────────────────────────────┐ │ GPS模块连接 │ │ │ │ ATGM336H STM32F103 │ │ ┌────────┐ ┌────────┐ │ │ │ VCC │◄─────────►│ 3.3V │ │ │ │ GND │◄─────────►│ GND │ │ │ │ TXD │──────────►│ PC11 │ (UART4_RX) │ │ │ RXD │◄──────────│ PC10 │ (UART4_TX) │ │ │ PPS │ │ │ (可选时间同步) │ │ └────────┘ └────────┘ │ │ │ │ 注意GPS模块需要室外天线室内信号弱 │ │ │ └─────────────────────────────────────────────────────┘五、精度优化5.1 多次采样取平均#defineGPS_SAMPLE_COUNT5u32get_avg_longitude(void){u32 sum0;u8 valid_count0;for(u8 i0;iGPS_SAMPLE_COUNT;i){gps_process();// 采集GPS数据if(GNRMC.position_valid){sumGNRMC.longitude;valid_count;}vTaskDelay(1000);// 间隔1秒}if(valid_count0)returnsum/valid_count;elsereturn0;}5.2 数据校验/** * brief 校验NMEA数据的校验和 * param pstr NMEA字符串 * return 0:校验通过, 0:校验失败 */intverify_checksum(unsignedchar*pstr){unsignedcharchecksum0;unsignedcharreceived_checksum0;charbuf[3];char*p;// 找到*号pstrchr((char*)pstr,*);if(pNULL)return-1;// 计算$到*之间的异或值for(inti1;i(p-(char*)pstr);i){checksum^pstr[i];}// 读取接收到的校验和buf[0]*(p1);buf[1]*(p2);buf[2]\0;received_checksum(unsignedchar)strtol(buf,NULL,16);// 比较if(checksumreceived_checksum)return0;elsereturn-1;}六、常见问题Q1定位慢怎么办原因分析冷启动模块断电后重新上电需要重新搜星室内环境信号被遮挡天线问题天线位置不当解决方案// 1. 使用热启动// 断电前保存星历数据到EEPROM上电后恢复// 2. 增加定位超时时间#defineGPS_TIMEOUT_MS60000// 60秒超时// 3. 使用AGPS辅助定位// 通过GPRS下载辅助数据加速定位Q2信号弱怎么处理// 1. 检查信号强度通过$GNGGA语句// 2. 增加重试次数// 3. 使用外置天线// 信号质量判断intcheck_gps_quality(void){// 卫星数量 4 才能定位// HDOP 2.0 精度较好if(GNRMC.position_valid0)return-1;// 无定位return0;}Q3室内能定位吗不能。GPS信号无法穿透建筑。替代方案WiFi定位室内有WiFi蓝牙信标定位使用固定坐标设备安装后手动配置// 手动配置GPS坐标if(db_var.gps_mode1)// 手动模式{// 使用预设坐标sprintf((char*)var.record,GPSX%s|GPSY%s|,db_var.gpsx,db_var.gpsy);}Q4定位数据跳变原因GPS定位误差导致解决添加滤波算法// 简单的限幅滤波#defineGPS_MAX_CHANGE100// 最大变化量u32filter_longitude(u32 new_value){staticu32 last_value0;staticu8 first1;if(first){last_valuenew_value;first0;returnnew_value;}// 变化量过大可能是跳变if(abs(new_value-last_value)GPS_MAX_CHANGE){returnlast_value;// 保持上次值}last_valuenew_value;returnnew_value;}七、总结7.1 GPS解析要点要点说明NMEA格式文本协议逗号分隔字段时间转换UTC8 北京时间经纬度转换度分格式 → 十进制度有效性判断position_valid ‘A’校验和可选用于数据完整性校验7.2 本项目GPS应用模块ATGM336H接口UART49600波特率用途设备位置上报频率非加注状态时采集7.3 优化建议多次采样取平均提高精度添加数据校验防止异常数据室外天线保证信号质量支持手动配置坐标解决室内定位问题八、源码获取本文完整源码已收录至「STM32物联网项目资料包」资料包包含✅ GPS解析完整代码GNRMC_Parse✅ 玻璃水加注机完整工程✅ 外设驱动代码模板✅ 调试技巧文档✅ 常见问题FAQ获取方式点赞 收藏本文私信我备注「资料」免费领取作者宋一平 | 嵌入式/物联网技术分享转载请注明出处

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价