[英]NMEA checksum calculation calculation
我正在嘗試找到已經由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)。
請幫我們解決這個算法。
你做了一個很好的嘗試,但你有一些錯誤。 我認為以下鏈接是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
進行比較。
過去,我曾使用此函數來計算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字節十六進制值重新格式化為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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.