32-Bit CRC

The ASCII and Binary OEM7 family and SMART2 message formats all contain a 32-bit CRC for data verification. This allows the user to ensure the data received (or transmitted) is valid with a high level of certainty.

The C functions below may be implemented to generate the CRC of a block of data.

#define CRC32_POLYNOMIAL 0xEDB88320L

/* --------------------------------------------------------------------------

Calculate a CRC value to be used by CRC calculation functions.

-------------------------------------------------------------------------- */

unsigned long CRC32Value(int i) {

     int j;

     unsigned long ulCRC;

     ulCRC = i;

     for ( j = 8 ; j > 0; j-- ) {

          if ( ulCRC & 1 )

               ulCRC = ( ulCRC >> 1 ) ^ CRC32_POLYNOMIAL;

          else

               ulCRC >>= 1;

     }

     return ulCRC;

}

 

/* --------------------------------------------------------------------------

Calculates the CRC-32 of a block of data all at once

ulCount - Number of bytes in the data block

ucBuffer - Data block

-------------------------------------------------------------------------- */

unsigned long CalculateBlockCRC32( unsigned long ulCount, unsigned char *ucBuffer ) {

     unsigned long ulTemp1;

     unsigned long ulTemp2;

     unsigned long ulCRC = 0;

     while ( ulCount-- != 0 ) {

          ulTemp1 = ( ulCRC >> 8 ) & 0x00FFFFFFL;

          ulTemp2 = CRC32Value( ((int) ulCRC ^ *ucBuffer++ ) & 0xFF );

          ulCRC = ulTemp1 ^ ulTemp2;

     }

     return( ulCRC );

}

The NMEA checksum is an XOR of all the bytes (including delimiters such as ',' but excluding the * and $) in the message output. It is therefore an 8-bit and not a 32-bit checksum.

 

Not all logs may be available. Every effort is made to ensure examples are correct, however, a checksum may be created for promptness in publication. In this case it will appear as ‘9999’.

Example:

BESTPOSB and BESTPOSA from an OEM7 or SMART2 receiver.

Binary Log Message:

0xAA, 0x44, 0x12, 0x1C, 0x2A, 0x00, 0x02, 0x20, 0x48, 0x00, 0x00, 0x00, 0x90, 0xB4, 0x93, 0x05, 0xB0, 0xAB, 0xB9, 0x12, 0x00, 0x00, 0x00, 0x00, 0x45, 0x61, 0xBC, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x04, 0x50, 0xB3, 0xF2, 0x8E, 0x49, 0x40, 0x16, 0xFA, 0x6B, 0xBE, 0x7C, 0x82, 0x5C, 0xC0, 0x00, 0x60, 0x76, 0x9F, 0x44, 0x9F, 0x90, 0x40, 0xA6, 0x2A, 0x82, 0xC1, 0x3D, 0x00, 0x00, 0x00, 0x12, 0x5A, 0xCB, 0x3F, 0xCD, 0x9E, 0x98, 0x3F, 0xDB, 0x66, 0x40, 0x40, 0x00, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x0B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03, 0x42, 0xdc, 0x4c,0x48

Below is a demonstration of how to generate the CRC from both ASCII and BINARY messages using the function described above.

When you pass the data into the code that follows, exclude the checksum shown in bold italics above. It is 42dc4c48.

Binary Checksum Calculation:

#include <stdio.h>

#include <string.h>

#include <inttypes.h>

 

void main() {

     // Expect checksum 0x42, 0xDC, 0x4C, 0x48 (42dc4c48)

     unsigned char buffer[] = {0xAA, 0x44, 0x12, 0x1C, 0x2A, 0x00, 0x02, 0x20, 0x48, 0x00,

                              0x00, 0x00, 0x90, 0xB4, 0x93, 0x05, 0xB0, 0xAB, 0xB9, 0x12,

                              0x00, 0x00, 0x00, 0x00, 0x45, 0x61, 0xBC, 0x0A, 0x00, 0x00,

                              0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x04, 0x50, 0xB3,

                              0xF2, 0x8E, 0x49, 0x40, 0x16, 0xFA, 0x6B, 0xBE, 0x7C, 0x82,

                              0x5C, 0xC0, 0x00, 0x60, 0x76, 0x9F, 0x44, 0x9F, 0x90, 0x40,

                              0xA6, 0x2A, 0x82, 0xC1, 0x3D, 0x00, 0x00, 0x00, 0x12, 0x5A,

                              0xCB, 0x3F, 0xCD, 0x9E, 0x98, 0x3F, 0xDB, 0x66, 0x40, 0x40,

                              0x00, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

                              0x00, 0x00, 0x0B, 0x0B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03};

 

     //Note that the CRC on the binary data will be little-endian ordered.

     unsigned long CRCle = CalculateBlockCRC32(sizeof(buffer), buffer);

 

     //big-endian users (such as Atmel AVR32 users) may swap endianness as follows

     unsigned long CRCbe = __builtin_bswap32(CRCle);

 

     printf("\n\n%s %lx \n", "Computed binary checksum (little-endian): ", CRCle);

     printf("%s %" PRIx32 "\n", "Computed binary checksum (big-endian): ", CRCbe);

 

}

 

Note that the above checksum function (CalculateBlockCRC32) must also be included to execute this code.

ASCII Log Message:

#BESTPOSA,COM1,0,78.0,FINESTEERING,1427,325298.000,00000000,6145,2748;
SOL_COMPUTED,SINGLE,51.11678928753,-114.03886216575,1064.3470,-16.2708,
WGS84,2.3434,1.3043,4.7300,"",0.000,0.000,7,7,0,0,0,06,0,03*9c9a92bb

The checksum for this log is given above, it is 9c9a92bb.

ASCII:

#include <stdio.h>

#include <string.h>

 

void main() {

     //Remember to escape " characters as \"

     char *msgBlock = "BESTPOSA,COM1,0,78.0,FINESTEERING,1427,325298.000,00000000,\

6145,2748;SOL_COMPUTED,SINGLE,51.11678928753,-114.03886216575,\

1064.3470,-16.2708,WGS84,2.3434,1.3043,4.7300,\"\",0.000,0.000,7,7,0,0,0,06,0,03";

 

     unsigned long CRC = CalculateBlockCRC32(strlen(msgBlock), (unsigned char*)msgBlock);

 

     printf("\n%s %s\n", "Demonstrating CRC computed for the block:", msgBlock);

     printf("\n\n%s %lu\n", "CRC32 in Decimal is: ", CRC);

     printf("%s %lx\n", "CRC32 in Hex is: ", CRC);

}

 

Note that the above checksum function (CalculateBlockCRC32) must also be included to execute this code.