'NMEA checksum calculation calculation

I am trying to find checksum for NMEA sentence which is already calculated by GPS.

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};

hear last 3rd and 4th char are checksum that is 0F05 but we want to correct algorithm. Our algorithm which we used is as follows

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

code we have written is as follows:

#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;
}

problem is that we are getting 3588 but it should be 3845(0F05).

Please help us to solve this algorithm.



Solution 1:[1]

You've made a good attempt, but you've got a few things wrong. I think the following link is a good starting point for NMEA: http://www.gpsinformation.org/dale/nmea.htm

You'll see in the introduction that each command is self-contained, starts with a $ symbol and ends with a carriage return/line feed combination. The checksum, when present, is at the end of the message and preceded by an asterisk *. You'll also see that the checksum is an XOR of all bytes between the $ and the *, and the checksum, in hexadecimal, follows the * in ASCII format.

Your input data also has some noise at the beginning and end, which you need to discard. Let me annotate your input:

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
};

So, the checksum calculation is:

chk = 0;
chk = chk ^ 0x47; // chk = 0x47
chk = chk ^ 0x50; // chk = 0x17
chk = chk ^ 0x52; // chk = 0x45
...
chk = chk ^ 0x41; // chk = 0x58

Note that you'll end up with 0x58, which is in the message as 0x35 0x38. So once you've correctly framed the message and adjusted the for loop to iterate over the checksummed bytes, the loop body simply becomes:

chk ^= test[i];

After the loop you then need to either convert the two nibbles of chk into ASCII and compare with the signalled checksum, or convert the signalled checksum to a binary value and compare with chk.

Solution 2:[2]

I have used this function, in the past, to calculate a NMEA checksum for the purpose of comparing with the checksum in the NMEA message.

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()

after this calculation, extract the two byte checksum from the NMEA message, re-format that 2 byte hex value into a 1byte value, then compare with the above calculated value

Solution 3:[3]

As previous answers are saying, you have exclude the '$' and '*' in the checksum calculation. You could use the function from the C library 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;
}

Then call the function with your sentence string as argument:

uint8_t chk = nmea_get_checksum(GPRMCBuf);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sigve Kolbeinson
Solution 2 user3629249
Solution 3 jack-xtal