Calculation of Estimated Amount NMEA

I am trying to find the checksum for an NMEA sentence that has already been 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 the last 3 and 4 char - a checksum that is 0F05 but we want to fix the algorithm. Our algorithm we used is as follows

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

which we wrote 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 get 3588, but that should be 3845 (0F05).

Please help us solve this algorithm.

+4
source share
3 answers

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

In the introduction, you will see that each command is autonomous, starting with the $ character and ending with a carriage return / line combination. The checksum, if present, is at the end of the message and is preceded by an asterisk * . You will also see that the checksum is the XOR of all bytes between $ and * , and the checksum in hexadecimal format follows * in ASCII format.

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

 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, ie 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:

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

Please note that in the end you will get 0x58 , which is in the message as 0x35 0x38 . Therefore, once you have correctly formed the message and adjusted the for loop to repeat the bytes with the checksum, the loop body simply becomes:

 chk ^= test[i]; 

After the loop, you need to either convert two chk pieces to ASCII, or compare with a signal checksum, or convert a checksum with signals to a binary value and compare with chk .

+4
source

I used this function in the past to calculate the NMEA checksum to compare 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 value Checksum = Checksum ^ Character; } break; } } // Return the checksum return (Checksum); } // calc_NEMA_Checksum() 

after this calculation, extract the checksum of two bytes from the NMEA message, reformat this six-byte value in bytes to 1 byte, then compare with the value calculated above

+1
source

As follows from the previous answers, you exclude "$" and "*" in the calculation of the checksum. You can use this function from the C libnmea library:

 #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 an argument:

 uint8_t chk = nmea_get_checksum(GPRMCBuf); 
+1
source

All Articles