3. NMEA-0183协议入门:听懂GPS模块的“语言”
做嵌入式定位开发,你迟早要跟NMEA-0183打交道。说白了,这就是GPS模块跟单片机“聊天”用的标准语言。不管你是用ublox、中科微还是其他厂家的模块,只要它输出定位数据,十有八九都是NMEA格式。
我个人习惯,拿到一个新模块,第一件事就是接上串口,看看它往外吐什么数据。你看到的那些以$开头、一堆逗号隔开的字符串,就是NMEA语句。今天咱们就把这套“摩斯密码”彻底搞明白。
3.1 NMEA协议是什么?
NMEA-0183是美国国家海洋电子协会制定的标准。最初是为了航海电子设备互联,后来GPS模块全用这套协议输出数据。它定义了数据格式、传输速率(通常是4800或9600波特率)、电气特性等。
一个典型的NMEA语句长这样:
$GPGGA,092725.00,4717.11399,N,00833.91290,E,1,08,1.01,499.6,M,48.0,M,,*5F
嗯,看着有点乱是吧?别急,咱们拆开看,其实结构非常清晰。
3.2 常见语句类型:四种核心“话术”
GPS模块会输出好几种NMEA语句。我最常用的就四种:$GPGGA、$GPRMC、$GPGSA、$GPGSV。它们各自负责不同的信息。
| 语句ID | 全称 | 核心内容 | 我的使用场景 |
|---|---|---|---|
| $GPGGA | Global Positioning System Fix Data | 时间、经纬度、定位质量、海拔 | 获取精确位置和海拔 |
| $GPRMC | Recommended Minimum Specific GNSS Data | 时间、日期、经纬度、地速、航向 | 获取速度、航向、UTC时间 |
| $GPGSA | GNSS DOP and Active Satellites | 定位模式、使用卫星编号、PDOP/HDOP/VDOP | 评估定位精度 |
| $GPGSV | GNSS Satellites in View | 可见卫星编号、仰角、方位角、信噪比 | 查看天空卫星分布 |
我的小经验: 如果你只需要位置和速度,解析$GPRMC就够了,它信息最全。但如果你需要海拔数据,那就必须解析$GPGGA。我曾经在一个无人机项目中只解析了$GPRMC,结果发现高度数据拿不到,又回头加了解析$GPGGA的代码。
3.3 语句结构解析:从帧头到校验和
每条NMEA语句的结构都一样。咱们以$GPGGA为例,一句一句拆开看。
先看完整语句:
$GPGGA,092725.00,4717.11399,N,00833.91290,E,1,08,1.01,499.6,M,48.0,M,,*5F
它由五个部分组成:
- 帧头($GPGGA):
$是起始符,GP表示GPS系统,GGA是语句类型。 - 数据字段:逗号分隔的多个字段,每个字段有特定含义。
- 校验和标识:
*号,表示后面跟着校验值。 - 校验和:
5F,两个十六进制字符。 - 结束符:
<CR><LF>(回车换行)。
咱们重点看$GPGGA的数据字段:
| 字段序号 | 示例值 | 含义 | 说明 |
|---|---|---|---|
| 1 | 092725.00 | UTC时间 | 格式:hhmmss.ss |
| 2 | 4717.11399 | 纬度 | 格式:ddmm.mmmm |
| 3 | N | 北纬/南纬 | N或S |
| 4 | 00833.91290 | 经度 | 格式:dddmm.mmmm |
| 5 | E | 东经/西经 | E或W |
| 6 | 1 | 定位质量 | 0=无效, 1=单点定位, 2=差分定位 |
| 7 | 08 | 使用卫星数 | 参与定位的卫星数量 |
| 8 | 1.01 | 水平精度因子(HDOP) | 值越小精度越高 |
| 9 | 499.6 | 海拔高度 | 单位:米 |
| 10 | M | 海拔单位 | M=米 |
| 11 | 48.0 | 大地水准面高度差 | 单位:米 |
| 12 | M | 高度差单位 | M=米 |
| 13 | (空) | 差分GPS数据龄期 | 无差分时为空 |
| 14 | (空) | 差分参考站ID | 无差分时为空 |
关键点: 纬度格式是“度分”格式,不是十进制。比如4717.11399表示47度17.11399分。转换成十进制是47 + 17.11399/60 = 47.285233度。这个转换我经常在代码里做,千万别忘了。
3.4 校验和:数据完整性的“守护神”
为什么会需要校验和?因为串口通信可能受干扰,数据可能出错。校验和就是用来检查数据在传输过程中有没有被篡改。
计算规则很简单:
- 从
$之后开始,到*之前结束。 - 对每个字符做异或运算(XOR)。
- 结果转换成两个十六进制字符(大写)。
举个例子,计算$GPGGA,092725.00,4717.11399,N,00833.91290,E,1,08,1.01,499.6,M,48.0,M,,*5F的校验和:
// 伪代码实现
uint8_t checksum = 0;
for (int i = 1; i < len; i++) { // 从'G'开始,到最后一个','结束
if (str[i] == '*') break; // 遇到'*'停止
checksum ^= str[i];
}
// 结果checksum应该等于0x5F
我曾经踩过的坑: 有一次我写的解析代码,校验和总是算不对。查了半天发现,我忘了把
$排除在外。记住:从$后面的第一个字符开始算,到*前面的最后一个字符结束。另外,校验和字母必须是大写,小写会被判错。
3.5 知识体系总览
下面这张图把NMEA-0183的核心知识点串起来了。你一看就明白各个部分的关系。
3.6 实际项目中的解析思路
在嵌入式系统里解析NMEA,我一般分三步走:
- 串口接收:用中断或DMA接收完整一行(以
$开头,以<CR><LF>结尾)。 - 校验验证:计算校验和,跟语句里的值比对。不一致就丢弃。
- 字段提取:用逗号分割字符串,按位置提取每个字段的值。
我的建议: 不要用
strtok这类会修改原字符串的函数。我习惯用指针遍历,找到逗号位置后复制子串。这样更安全,也更容易处理空字段。
你想想看,掌握了这些,GPS模块在你手里就不再是黑盒子了。你能读懂它说的每一句话,知道什么时候信号好,什么时候该换天线位置。这就是入门到精通的第一步。