4. 校验和计算与验证实战

在NMEA 0183报文中,校验和(Checksum)是判断一条语句在传输过程中是否发生位错误的最直接手段。它位于语句末尾的“*”之后,由两位十六进制大写字符组成。理解它的算法并能够手工复算,是识读报文的基本功——当设备显示的数据异常时,第一步往往就是确认这条报文本身是否完整可信。

异或校验算法:逐字符异或

NMEA校验和的计算范围有严格约定:从起始符“$”之后第一个字符开始,到“*”之前最后一个字符结束,不包含“$”和“*”本身。算法是把范围内所有字符的ASCII码逐字节进行按位异或(XOR),得到一个8位结果,再格式化为两位大写十六进制数。

异或运算有三个便于手工计算的性质:相同两数异或为0;任何数与0异或等于其本身;异或满足交换律,因此字符顺序不影响结果,但漏算或多算一个字符一定会改变结果。计算时通常维护一个累加变量,初值取0,每读入一个字符就执行一次“累加值 = 累加值 XOR 字符ASCII码”。

示例语句(GGA定位数据):
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47

计算范围($ 与 * 之间):
GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,

逐字符异或过程(ASCII码,十六进制):
G(47) → 47
P(50) → 47^50 = 17
G(47) → 17^47 = 50
G(47) → 50^47 = 17
A(41) → 17^41 = 56
,(2C) → 56^2C = 7A
1(31) → 7A^31 = 4B
2(32) → 4B^32 = 79
3(33) → 79^33 = 4A
...(中间字符略)
最终累加结果 = 0x47

校验和字段写作:*47

上例中的“47”是用于讲解算法流程的示例值,实际复算时应以完整字符序列为准。手工计算时建议把字符按每5~8个一组分段,每段算完记录中间值,最后再合并,这样比一口气算完整串更不容易出错。对于较长的语句,可以借助计算器或脚本,但必须清楚输入范围是“$”之后到“*”之前。

验证流程:先定位范围,再比对结果

验证一条报文时,操作顺序应当是:找到“$”和“*”,截取中间子串;对子串逐字符异或;把结果格式化为两位大写十六进制;与“*”后两位逐字符比对。若一致,说明该语句在字符层面完整;若不一致,则该语句应被丢弃,不能把其中的字段当作有效数据使用。

校验和只保证字符传输的完整性,不保证语义正确。一条校验和正确的报文,其字段仍可能因设备配置错误而出现不合理数值,例如经纬度格式错误或定位质量标志异常。因此校验通过只是“可信”的第一道门槛,字段合理性还需另行判断。

常见校验错误原因

实际排查中,校验和不匹配通常集中在以下几类原因:

  • 计算范围错误:把“$”或“*”计入范围,或把“*”后的校验和字符也算了进去,这是最常见的人为错误。
  • 字符被改动:串口参数不匹配、线路干扰、电平转换异常导致个别字符丢失或被替换,尤其是逗号、数字被误读。
  • 大小写与格式问题:校验和应为两位大写十六进制,若程序输出小写或只输出一位(如“*7”而非“*07”),严格解析器会判为不合法。
  • 报文被截断或拼接:接收缓冲区处理不当,导致一条语句尾部缺失,或两条语句首尾粘连,校验范围自然错位。
  • 换行符混入:部分实现误把CR/LF计入计算范围,或发送端在“*”前多加了空格。

排查时可采取“由外向内”的策略:先确认报文边界是否完整(是否有“$”开头、“*”加两位十六进制结尾),再确认计算范围,最后才怀疑算法实现。若同一设备的多条语句全部校验失败,优先怀疑串口参数或线路;若仅个别语句失败,则更可能是该条语句在生成或传输中被改动。

工具辅助验证方法

工程上常用三类手段辅助验证。第一类是串口调试工具,如常见的串口助手,可设置波特率4800或38400、8位数据位、无校验、1位停止位,观察原始字节流,确认报文边界与字符是否完整。第二类是脚本复算,用几行代码即可批量校验,适合分析录制的日志文件:

def nmea_checksum(sentence):
    body = sentence.strip().lstrip('$').split('*')[0]
    cs = 0
    for ch in body:
        cs ^= ord(ch)
    return f"{cs:02X}"

# 示例:返回 "47"
print(nmea_checksum("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"))

第三类是协议分析仪或带NMEA解析功能的导航测试软件,它们会在解析前自动校验并标记异常语句,便于快速定位问题报文。无论使用哪种工具,都应保留原始报文记录,避免工具“自动纠正”掩盖真实问题。

NMEA 0183 校验和计算与验证路径 报文结构定位 $ GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,, * 47 ← 参与异或计算的字符范围 → 起始符 分隔符 校验和 逐字符异或计算 累加值初值 0x00 读入一个字符 累加值 = 累加值 XOR ASCII 是否到“*”前 最后一个字符? 结果格式化为 两位大写十六进制 未结束,继续读下一字符 结果比对与判断 计算值 vs 报文“*”后两位 逐字符比对 一致 语句完整,可解析字段 不一致 丢弃语句,排查原因 进入字段合理性检查 (校验通过 ≠ 数据语义正确) 排查:范围错误 / 字符改动 / 截断 / 格式 串口参数 → 报文边界 → 计算范围 → 算法实现

实战中的判断要点

把上述流程落到实际识读中,可以形成一条清晰的判断链:先看报文是否以“$”开头、以“*”加两位十六进制结尾,确认边界完整;再按“$”之后到“*”之前的范围复算;比对一致后,才进入字段解析与合理性判断。若不一致,按“串口参数与线路—报文边界—计算范围—算法实现”的顺序逐层排查,而不是直接怀疑设备故障。掌握这套方法后,面对任何一条陌生的NMEA语句,都能先独立判断其可信度,再决定是否采信其中的定位、航向或时间数据。