# NMEA_GPS_parse **Repository Path**: boo0ood/NMEA_GPS_parse ## Basic Information - **Project Name**: NMEA_GPS_parse - **Description**: C实现GPS数据全解析 - **Primary Language**: C - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 52 - **Forks**: 24 - **Created**: 2022-06-10 - **Last Updated**: 2026-06-20 ## Categories & Tags **Categories**: gis **Tags**: GPS, nmea, stm, stm32 ## README # NMEA GPS PARSE NMEA GPS PARSE 是一个 C99 GPS/NMEA 数据解析库,面向嵌入式和普通 C 工程使用。当前实现采用单次按行扫描、调用方提供输出结构体、固定长度数组保存卫星信息,避免旧版重复拷贝、动态分配和额外大栈帧。 ## 特性 - 支持 GGA、GLL、GSA、GSV、RMC、VTG 和 UTC 派生解析。 - 使用 `gps_data_parse_into()` 直接写入调用方提供的 `GPS` 结构体。 - 单次线性扫描输入数据,避免按语句类型重复搜索整帧数据。 - 不使用 `malloc/free` 保存 GSA/GSV 卫星数据。 - 校验 NMEA checksum,拒绝 checksum 后带尾随污染字符的语句。 - GSA/GSV 数组访问受 `MAX_GSA_CHANNELS` 和 `MAX_SATELLITES` 保护。 - 不依赖 `strtok` 的全局状态,适合高频调用和多实例解析场景。 ## 使用方法 项目主要使用两个文件: - `gps.h`:定义数据结构、语句前缀、解析开关和公开 API。 - `gps.c`:实现解析逻辑。 推荐接口: ```c int gps_data_parse_into(const char *gps_src, const unsigned short gps_src_len, GPS *out); ``` 返回值: - `1`:参数合法,`out` 已写入默认值和解析结果。 - `0`:`out == NULL`,不会写入结果。 示例: ```c #include #include "gps.h" int main(void) { GPS gps; char gps_data[] = "$GNRMC,013300.00,A,2240.84105,N,11402.70763,E,0.007,,220319,,,D*69\r\n" "$GNGGA,013300.00,2240.84105,N,11402.70763,E,2,12,0.59,70.5,M,-2.5,M,,0000*68\r\n"; if (!gps_data_parse_into(gps_data, sizeof(gps_data), &gps)) { return 1; } printf("lat:%f%c\n", gps.rmc_data.lat, gps.rmc_data.lat_dir); printf("lon:%f%c\n", gps.rmc_data.lon, gps.rmc_data.lon_dir); return 0; } ``` ## 配置 根据 GPS 模块输出调整语句前缀: ```c #define PRE_GGA "$GNGGA" #define PRE_GLL "$GNGLL" #define PRE_GSA "$GNGSA" #define PRE_GPGSV "$GPGSV" #define PRE_GNGSV "$GNGSV" #define PRE_GLGSV "$GLGSV" #define PRE_RMC "$GNRMC" #define PRE_VTG "$GNVTG" ``` 按需关闭不使用的语句解析以减小代码体积: ```c #define ENABLE_GGA 1 #define ENABLE_GLL 1 #define ENABLE_GSA 1 #define ENABLE_GSV 1 #define ENABLE_RMC 1 #define ENABLE_VTG 1 #define ENABLE_UTC 1 ``` ## 内存与性能 - `gps_data_parse_into()` 内部只保留单条 NMEA 语句临时缓冲区。 - 解析结果由调用方提供的 `GPS *out` 持有,解析函数内部不再额外持有完整 `GPS` 栈对象。 - `GPS.gps_buffer_data` 保存输入数据的安全截断副本,最多 1023 字节加结尾 `\0`。 - GSA PRN 使用 `GSA.gsa_prn[MAX_GSA_CHANNELS]`,默认最多 36 个信道。 - GSV 卫星信息使用 `GSV.sat_info[MAX_SATELLITES]`,默认最多 32 颗卫星。 - 调用方不需要也不允许对 `gsa_prn` 或 `sat_info` 调用 `free`。 - 复杂度为 `O(n)`,其中 `n` 是输入帧长度;主要成本来自 checksum、字段切分、`strtod/strtol` 数值转换。 ## 安全边界 - 输入可以为空;此时输出结构体会被清零并写入默认值。 - 单条 NMEA 语句超过内部临时缓冲区会被截断,通常会因 checksum 不匹配而拒绝解析。 - checksum 必须是 `*` 后两位十六进制且后面没有额外字符。 - GSV 报文声明的卫星数量超过 `MAX_SATELLITES` 时,`sats` 会被截断到数组容量。 - GSA 多语句聚合只统计通过 checksum 的语句,无效语句不会放大 `gsa_prn[].total`。 - 解析函数会原地切分内部临时行缓冲,不会修改调用方传入的 `gps_src`。 ## 注意事项 1. `gps_src_len` 应传入原始缓冲区实际长度;若传入长度超过 `GPS.gps_buffer_data` 容量,保存副本会被安全截断。 2. 若使用 UTC 派生解析,需同时启用 `ENABLE_RMC` 和 `ENABLE_UTC`。 3. 语句建议以 `\r\n` 分隔;解析器也会跳过连续 CR/LF。 4. 至少保留一个 `ENABLE_*` 解析开关为 `1`。 ## 测试数据 ```c char gps_data[] = "$GNRMC,013300.00,A,2240.84105,N,11402.70763,E,0.007,,220319,,,D*69\r\n" "$GNVTG,,T,,M,0.007,N,0.014,K,D*3A\r\n" "$GNGGA,013300.00,2240.84105,N,11402.70763,E,2,12,0.59,70.5,M,-2.5,M,,0000*68\r\n" "$GNGSA,A,3,10,12,14,20,25,31,32,26,29,40,41,22,1.09,0.59,0.91*1F\r\n" "$GNGSA,A,3,74,70,73,80,69,,,,,,,,1.09,0.59,0.91*17\r\n" "$GPGSV,4,1,16,01,00,300,,10,56,178,51,12,12,038,38,14,47,345,48*79\r\n" "$GPGSV,4,2,16,16,00,207,,18,06,275,30,20,28,165,43,22,10,319,43*76\r\n" "$GPGSV,4,3,16,25,46,050,47,26,29,205,44,29,13,108,45,31,50,296,52*7E\r\n" "$GPGSV,4,4,16,32,56,010,52,40,20,257,40,41,46,237,48,42,46,123,42*77\r\n" "$GLGSV,2,1,06,69,27,136,49,70,76,057,50,71,34,338,50,73,64,276,55*6B\r\n" "$GLGSV,2,2,06,74,24,231,46,80,35,019,46*60\r\n" "$GNGLL,2240.84105,N,11402.70763,E,013300.00,A,D*7C\r\n"; ``` ## NMEA 字段简表 ### RMC 推荐最小定位信息 示例:`$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50` - 字段 1:UTC 时间,`hhmmss.sss` - 字段 2:状态,`A` 为定位有效,`V` 为无效 - 字段 3/4:纬度和半球 - 字段 5/6:经度和半球 - 字段 7:速度,单位节 - 字段 8:方位角,单位度 - 字段 9:UTC 日期,`DDMMYY` - 字段 12:模式指示 - 字段 13:checksum ### GGA GPS 定位数据 示例:`$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,12.2,M,19.7,M,,0000*1F` - 字段 1:UTC 时间 - 字段 2/3:纬度和半球 - 字段 4/5:经度和半球 - 字段 6:定位质量 - 字段 7:使用卫星数量 - 字段 8:HDOP - 字段 9:海拔高度 - 字段 11:大地水准面高度 - 字段 13:差分时间 - 字段 14:差分站 ID - 字段 15:checksum ### GSA 当前卫星信息 示例:`$GPGSA,A,3,01,20,19,13,,,,,,,,,40.4,24.4,32.2*0A` - 字段 1:定位模式,`A` 自动,`M` 手动 - 字段 2:定位类型,`1` 未定位,`2` 2D,`3` 3D - 字段 3-14:最多 12 个正在使用的 PRN - 字段 15:PDOP - 字段 16:HDOP - 字段 17:VDOP - 字段 18:checksum ### GSV 可见卫星信息 示例:`$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70` - 字段 1:本组 GSV 语句总数 - 字段 2:当前语句编号 - 字段 3:当前可见卫星总数 - 字段 4 起:每 4 个字段描述一颗卫星,依次为 PRN、仰角、方位角、信噪比 - 最后字段:checksum ### VTG 地面速度信息 示例:`$GPVTG,89.68,T,,M,0.00,N,0.0,K*5F` - 字段 1:真北参照运动角度 - 字段 3:磁北参照运动角度 - 字段 5:速度,单位节 - 字段 7:速度,单位 km/h - 字段 9:checksum