我正在尝试找到已经由GPS计算的NMEA句子的校验和。
char GPRMCBuf[POS_BUFFER] = {0xA0, 0xA2, 0x00, 0x48, 0xDD,
0x24, 0x47, 0x50, 0x52, 0x4D, 0x43, 0x2C, 0x31, 0x35,
0x30, 0x35, 0x32, 0x30, 0x2E, 0x30, 0x30, 0x30, 0x2C,
0x41, 0x2C, 0x34, 0x31, 0x32, 0x31, 0x2E, 0x37, 0x39,
0x37, 0x37, 0x2C, 0x4E, 0x2C, 0x30, 0x30, 0x32, 0x31,
0x30, 0x2E, 0x39, 0x36, 0x36, 0x37, 0x2C, 0x45, 0x2C,
0x31, 0x2E, 0x35, 0x30, 0x2C, 0x35, 0x38, 0x2E, 0x32,
0x39, 0x2C, 0x32, 0x33, 0x30, 0x37, 0x31, 0x35, 0x2C,
0x2C, 0x2C, 0x41, 0x2A, 0x35, 0x38, 0x0D, 0x0A, 0x0F,
0x05, 0xB0, 0xB3};
听到最后的第3个和第4个字符是0F05的校验和 但我们想要纠正算法。我们使用的算法如下
Index = first,
checkSum = 0,
while index < msgLen,
checkSum = checkSum + message[index],
checkSum = checkSum AND (2^15-1).
increment index.
我们编写的代码如下:
#include<stdio.h>
main()
{
unsigned char i;
unsigned short chk;
char test[]={ 0x47, 0x50, 0x52, 0x4D, 0x43, 0x2C, 0x31,
0x35, 0x30, 0x35, 0x32, 0x30, 0x2E, 0x30, 0x30,
0x30, 0x2C, 0x41, 0x2C, 0x34, 0x31, 0x32, 0x31,
0x2E, 0x37, 0x39, 0x37, 0x37, 0x2C, 0x4E, 0x2C,
0x30, 0x30, 0x32, 0x31, 0x30, 0x2E, 0x39, 0x36,
0x36, 0x37, 0x2C, 0x45, 0x2C, 0x31, 0x2E, 0x35,
0x30, 0x2C, 0x35, 0x38, 0x2E, 0x32, 0x39, 0x2C,
0x32, 0x33, 0x30, 0x37, 0x31, 0x35, 0x2C, 0x2C,
0x2C, 0x41,0x2A, 0x35, 0x38, 0x0D, 0x0A};
chk = 0;
for(i = 0; i < 70; i++)
{
chk = chk + test[i];
chk = chk & 32767;
}
printf("A=%hu\n", chk);
return 0;
}
问题是我们得到的是3588,但它应该是3845(0F05)。
请帮助我们解决此算法。
答案 0 :(得分:4)
你做了一个很好的尝试,但你有一些错误。我认为以下链接是NMEA的一个很好的起点:http://www.gpsinformation.org/dale/nmea.htm
您将在简介中看到每个命令都是自包含的,以$
符号开头,以回车符/换行符组合结束。校验和(如果存在)位于消息的末尾,并以星号*
开头。您还将看到校验和是$
和*
之间所有字节的XOR,校验和(十六进制)遵循ASCII格式的*
。
您的输入数据在开头和结尾也有一些噪音,您需要将其丢弃。让我来诠释你的意见:
char GPRMCBuf[POS_BUFFER] = {
0xA0, 0xA2, 0x00, 0x48, 0xDD, // these bytes are not part of the message
0x24, // this is the '$' character, so this is the message start byte
// checksum calculation starts with the next byte (0x47)
0x47, 0x50, 0x52, 0x4D, 0x43, 0x2C, 0x31, 0x35, // GPRMC,15
0x30, 0x35, 0x32, 0x30, 0x2E, 0x30, 0x30, 0x30, 0x2C, // 0520.000,
0x41, 0x2C, 0x34, 0x31, 0x32, 0x31, 0x2E, 0x37, 0x39, // A,4121.79
0x37, 0x37, 0x2C, 0x4E, 0x2C, 0x30, 0x30, 0x32, 0x31, // 77,N,0021
0x30, 0x2E, 0x39, 0x36, 0x36, 0x37, 0x2C, 0x45, 0x2C, // 0.9667,E,
0x31, 0x2E, 0x35, 0x30, 0x2C, 0x35, 0x38, 0x2E, 0x32, // 1.50,58.2
0x39, 0x2C, 0x32, 0x33, 0x30, 0x37, 0x31, 0x35, 0x2C, // 9,230715,
0x2C, 0x2C, 0x41, // ,,A
// checksum calculation ends here
0x2A, // The '*' character, i.e. message/checksum delimiter
0x35, 0x38, // The checksum, '5' and '8', so the checksum is 0x58
0x0D, 0x0A, // The CR/LF line terminator
0x0F, 0x05, 0xB0, 0xB3 // these bytes are not part of the message
};
因此,校验和计算是:
chk = 0;
chk = chk ^ 0x47; // chk = 0x47
chk = chk ^ 0x50; // chk = 0x17
chk = chk ^ 0x52; // chk = 0x45
...
chk = chk ^ 0x41; // chk = 0x58
请注意,您最终会得到0x58
,该消息在0x35 0x38
消息中。因此,一旦你正确地构造了消息并调整了for循环来迭代校验和字节,循环体就会变成:
chk ^= test[i];
在循环之后,您需要将chk
的两个半字节转换为ASCII并与信号校验和进行比较,或将信号校验和转换为二进制值并与chk
进行比较。
答案 1 :(得分:1)
过去,我曾使用此功能来计算NMEA校验和 为了与NMEA消息中的校验和进行比较。
int calc_NMEA_Checksum( char *buf, int cnt )
{
char Character;
int Checksum = 0;
int i; // loop counter
//foreach(char Character in sentence)
for (i=0;i<cnt;++i)
{
Character = buf[i];
switch(Character)
{
case '$':
// Ignore the dollar sign
break;
case '*':
// Stop processing before the asterisk
i = cnt;
continue;
default:
// Is this the first value for the checksum?
if (Checksum == 0)
{
// Yes. Set the checksum to the value
Checksum = Character;
}
else
{
// No. XOR the checksum with this character's value
Checksum = Checksum ^ Character;
}
break;
}
}
// Return the checksum
return (Checksum);
} // calc_NEMA_Checksum()
在此计算之后,从NMEA消息中提取两个字节的校验和,将该2字节十六进制值重新格式化为1byte值,然后与上面计算的值进行比较
答案 2 :(得分:1)
正如之前的答案所说,你在校验和计算中排除了'$'和'*'。您可以使用C库libnmea中的函数:
#include <stdint.h>
#define NMEA_END_CHAR_1 '\n'
#define NMEA_MAX_LENGTH 70
uint8_t
nmea_get_checksum(const char *sentence)
{
const char *n = sentence + 1; // Plus one, skip '$'
uint8_t chk = 0;
/* While current char isn't '*' or sentence ending (newline) */
while ('*' != *n && NMEA_END_CHAR_1 != *n) {
if ('\0' == *n || n - sentence > NMEA_MAX_LENGTH) {
/* Sentence too long or short */
return 0;
}
chk ^= (uint8_t) *n;
n++;
}
return chk;
}
然后用你的句子字符串作为参数调用函数:
uint8_t chk = nmea_get_checksum(GPRMCBuf);