NMEA校验和计算计算

Dee*_*hia 4 c algorithm gps checksum nmea

我正在尝试查找已经由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};
Run Code Online (Sandbox Code Playgroud)

听到最后3位和4位字符是校验和,即0F05,但我们想更正算法。我们使用的算法如下

Index = first,
checkSum = 0,
while index < msgLen,
checkSum = checkSum + message[index],
checkSum = checkSum AND (2^15-1).
increment index.
Run Code Online (Sandbox Code Playgroud)

我们编写的代码如下:

#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;
}
Run Code Online (Sandbox Code Playgroud)

问题是我们得到3588,但应该是3845(0F05)。

请帮助我们解决此算法。

小智 5

您已经做了很好的尝试,但是有一些错误。我认为以下链接是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
};
Run Code Online (Sandbox Code Playgroud)

因此,校验和计算为:

chk = 0;
chk = chk ^ 0x47; // chk = 0x47
chk = chk ^ 0x50; // chk = 0x17
chk = chk ^ 0x52; // chk = 0x45
...
chk = chk ^ 0x41; // chk = 0x58
Run Code Online (Sandbox Code Playgroud)

请注意,您最终将获得0x58,该消息在中显示为0x35 0x38。因此,一旦您正确地构造了消息并调整了for循环以遍历校验和字节,循环主体就变成了:

chk ^= test[i];
Run Code Online (Sandbox Code Playgroud)

循环之后,您需要将的两个半字节转换chk为ASCII并与信号校验和进行比较,或者将信号校验和转换为二进制值并与进行比较chk