'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 |
