簡體   English   中英

NMEA校驗和計算計算

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM