如何计算GPS的NMEA校验码

众所周知,GPS芯片输出的格式,是符合NMEA0183标准的。每一个语句的结尾,都要加上一个校验码。

如果校验码错误,上位机发给GPS芯片的命令,GPS芯片将直接抛弃;而上位机也不会理校验码错误的语句。但是很多开发者并不知道校验码是如何计算的,这对开发造成了一定的影响。所以本文就实操一下,给大家讲解如何计算。

首先我们来看一下一个标准的语句是什么样的:
$GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,*5b\r\n

我们参与校验值计算的部分,是从 $ 符号后起,到最后的不含 \r\n 的部分止。以上边的语句为例,参与计算的部分是:
GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,

我们接下来就以它为基础,看计算出来的结果是不是 5b

##原理
NMEA通讯协议所规定的通讯语句都已是以ASCII码为基础的,NMEA-0183协议语句的数据格式如下:“$”为语句起始标志;“,”为域分隔符;“ ”为校验和识别符,其后面的两位数为校验和,代表了“$”和“”之间所有字符的按位异或值(不包括这两个字符);“/”为终止符,所有的语句必须以来结束,也就是ASCII 字符的“回车”(十六进制的0D)和“换行”(十六进制的0A)
摘自 GPS的NMEA协议数据校验和是怎么算的

##Python
如何在上位机检查校验码是否合法呢?不多说,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re

def checksum(sentence):
sentence = sentence.replace("\r", "").replace("\n", "").replace("$", "")
nmeadata,cksum = re.split('\*', sentence)

calc_cksum = 0
for s in nmeadata:
calc_cksum ^= ord(s)

return nmeadata,'0x'+cksum,hex(calc_cksum)

if __name__=='__main__':
line = "$GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,*5b\n"
data, cksum, calc_cksum = checksum(line)

if cksum != calc_cksum:
print("Error in checksum for: %s" % (data))
print("Checksums are %s and %s" % (cksum, calc_cksum))
else:
print("checksum is: %s" % (calc_cksum))

运行一下看看结果如何:

正如所料,输出5b;校验ok。

##Lua
如何使用Lua给GPS芯片发控制指令呢?实际上gps.lua已经写好了相关的函数,开发者只需要直接调用即可:

1
2
require"gps"
gps.write("xxxx", false)

函数原型如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- GPS串口写命令操作
-- @string cmd,GPS指令(cmd格式:"$PGKC149,1,115200*"或者"$PGKC149,1,115200*XX\r\n")
-- @bool isFull,cmd是否为完整的指令格式,包括校验和以及\r\n;true表示完整,false或者nil为不完整
-- @return nil
-- @usage gps.writeCmd(cmd)
function writeCmd(cmd,isFull)
local tmp = cmd
if not isFull then
tmp = 0
for i=2,cmd:len()-1 do
tmp = bit.bxor(tmp,cmd:byte(i))
end
tmp = cmd..(string.format("%02X",tmp)):upper().."\r\n"
end
uart.write(uartID,tmp)
log.info("gps.writecmd",tmp)
--log.info("gps.writecmd",tmp:toHex())
end

开发者也可以使用以下代码测试校验值:

1
2
3
4
5
6
7
8
9
10
11
12
13
function checkSum(cmd)
local tmp = cmd

tmp = 0
for i=1,cmd:len() do
tmp = bit.bxor(tmp,cmd:byte(i))
end
tmp = cmd..(string.format("%02X",tmp)):upper().."\r\n"

return tmp
end

print(checkSum("GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,"))

下载到模块跑一下,看看输出的结果:

正如所料,输出5b;校验ok。

##c
感谢开发者 维维宁(Q号940791105) 贡献代码:

上次更新 2021-01-28