NMEA checksum calculation calculation
Asked Answered
B

3

8

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.

Borstal answered 18/8, 2015 at 15:36 Comment(2)
Have you tried to google? rietman.wordpress.com/2008/09/25/…Renowned
Following @EugeneSh. although I tried the correct XOR method (not your addition) between '$' and '*' (you should have stopped before the '*'), I can't get the right checksum. Note, it is supposed to be an 8 bit checksum split into two nibbles, not the 15 bit checksum you have used. I see too that your NMEA message has been bulked up by some other protocol. Maybe it changed the checksum method too, or maybe it got it wrong.Blanketyblank
F
9

You've made a good attempt, but you've got a few things wrong.

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.

Ftlb answered 18/8, 2015 at 21:23 Comment(0)
K
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

Karie answered 18/8, 2015 at 22:30 Comment(1)
FYI: the if (Checksum == 0) check is superfluous, because (0 ^ x === x)Anility
P
2

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);
Polston answered 21/10, 2015 at 7:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.