novatel.c

Go to the documentation of this file.
00001 /**
00002 \file    novatel.c
00003 \brief   GNSS core 'c' function library: decoding/encoding NovAtel data.
00004 \author  Glenn D. MacGougan (GDM)
00005 \date    2007-11-29
00006 \since   2006-08-04
00007 
00008 \b REFERENCES \n
00009 - NovAtel OEM4 Command and Log Reference. www.novatel.com.
00010 
00011 \b "LICENSE INFORMATION" \n
00012 Copyright (c) 2007, refer to 'author' doxygen tags \n
00013 All rights reserved. \n
00014 
00015 Redistribution and use in source and binary forms, with or without
00016 modification, are permitted provided the following conditions are met: \n
00017 
00018 - Redistributions of source code must retain the above copyright
00019   notice, this list of conditions and the following disclaimer. \n
00020 - Redistributions in binary form must reproduce the above copyright
00021   notice, this list of conditions and the following disclaimer in the
00022   documentation and/or other materials provided with the distribution. \n
00023 - The name(s) of the contributor(s) may not be used to endorse or promote 
00024   products derived from this software without specific prior written 
00025   permission. \n
00026 
00027 THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 
00028 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
00029 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00030 DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00031 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00032 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
00033 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
00034 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00035 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
00036 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
00037 SUCH DAMAGE.
00038 */
00039 
00040 #include <stdio.h>
00041 #include "novatel.h"
00042 #include "gps.h"
00043 #include "constants.h"
00044 
00045 /// \brief   Calculate the CRC32 for a Novatel binary message. crcData != NULL.
00046 /// \return  The CRC32 value.
00047 static unsigned NOVATEL_CalculateCRC32(                                   
00048   unsigned char *crcData,          //!< A pointer to the data buffer used in the computation of the crc.
00049   const unsigned short dataLength  //!< The length of the data buffer [bytes].  
00050   );   
00051 
00052 
00053 /// \brief   Calculates the CRC32 for a NOVATEL OEM4 message.
00054 /// \return  TRUE if successful (isCRCValid = TRUE or FALSE), FALSE otherwise, i.e. error condition.
00055 static BOOL NOVATEL_CheckCRC32( 
00056   unsigned char *message,             //!< A pointer to the message buffer beginning with the sync bytes.
00057   const unsigned short messageLength, //!< The length of the message.
00058   const unsigned messageCRC,          //!< The received crc in the message.
00059   BOOL *isCRCValid                    //!< Is the CRC valid? Does it match the calculated value.
00060   );
00061 
00062 
00063 static const unsigned NOVATEL_CRC32_Table[256] =
00064 {
00065   0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL,
00066   0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,
00067   0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L,
00068   0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
00069   0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
00070   0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,
00071   0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL,
00072   0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
00073   0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L,
00074   0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
00075   0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L,
00076   0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
00077   0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL,
00078   0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,
00079   0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
00080   0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
00081   0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L,
00082   0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,
00083   0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL,
00084   0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
00085   0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L,
00086   0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,
00087   0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L,
00088   0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
00089   0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
00090   0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,
00091   0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL,
00092   0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
00093   0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L,
00094   0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
00095   0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L,
00096   0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
00097   0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL,
00098   0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,
00099   0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
00100   0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
00101   0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L,
00102   0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,
00103   0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL,
00104   0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
00105   0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L,
00106   0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,
00107   0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
00108 };
00109 
00110 
00111 // static 
00112 unsigned NOVATEL_CalculateCRC32(                                   
00113   unsigned char *crcData,          //!< A pointer to the data buffer used in the computation of the crc.
00114   const unsigned short dataLength  //!< The length of the data buffer [bytes].  
00115   )   
00116 {
00117   unsigned i;
00118   unsigned crc = 0;  
00119   for( i = 0; i < dataLength; i++ )   
00120     crc = NOVATEL_CRC32_Table[(crc ^ crcData[i]) & 0xFF] ^ (crc >> 8);
00121   return crc;
00122 }
00123 
00124 
00125 // static
00126 BOOL NOVATEL_CheckCRC32( 
00127   unsigned char *message,             //!< A pointer to the message buffer beginning with the sync bytes.
00128   const unsigned short messageLength, //!< The length of the message [bytes].
00129   const unsigned messageCRC,          //!< The received crc in the message.
00130   BOOL *isCRCValid                    //!< Is the CRC valid? Does it match the calculated value.
00131   )
00132 {
00133   unsigned crc = 0; // The calculated crc.
00134 
00135   if( message == NULL )
00136     return FALSE;
00137   
00138   // Calculate the crc.
00139   crc = NOVATEL_CalculateCRC32( message, messageLength-4 );
00140   
00141   *isCRCValid = FALSE;
00142 
00143   if( messageCRC != crc )
00144     *isCRCValid = FALSE;
00145   else
00146     *isCRCValid = TRUE;
00147 
00148   return TRUE;
00149 }
00150 
00151 
00152 BOOL NOVATELOEM4_FindNextMessageInFile(
00153   FILE *fid,                       //!< A file pointer to an open file (input).
00154   unsigned char *message,          //!< A message buffer in which to place the message found (input/output).
00155   const unsigned maxMessageLength, //!< The maximum size of the message buffer (input).
00156   BOOL *wasEndOfFileReached,       //!< Has the end of the file been reached (output).
00157   BOOL *wasMessageFound,           //!< Was a valid message found (output).
00158   unsigned *filePosition,          //!< The file position for the start of the message found (output).
00159   unsigned short *messageLength,   //!< The length of the entire message found and stored in the message buffer (output).
00160   unsigned short *messageID,       //!< The message ID of the message found.
00161   unsigned *numberBadCRC           //!< The number of bad crc values found. (crc fails or mistaken messages).  
00162   )
00163 {
00164   unsigned i = 0;                  // The index into the message buffer.
00165   unsigned char sync[3];           // 0xAA 0x44 0x12
00166   unsigned char headerLength = 0;  // The length of the message header [bytes].
00167   unsigned byteCount = 0;          // This indicates the number of bytes read by fread().
00168   unsigned short dataLength = 0;   // The length of the data portion of the message. i.e. *messageLength - headerLength - CRCLength.
00169   unsigned short msgLength = 0;    // The entire length of the current message being examined.
00170   int fpos = 0;                    // A signed file position (needs sign for error checking).
00171   unsigned messageCRC = 0;         // The message CRC value.
00172   BOOL startSearch = TRUE;         // A boolean to indicate that the sync search is occurring.
00173   BOOL isCRCValid = FALSE;         // A boolean to indicate if the CRC is valid. Does it match the calculated value.
00174 
00175 
00176   // Initialize the output parameters.
00177   *wasEndOfFileReached = FALSE;
00178   *wasMessageFound = 0;
00179   *filePosition    = 0;
00180   *messageLength   = 0;
00181   *messageID       = 0;
00182   *numberBadCRC    = 0;
00183   
00184   // Ensure that the file pointer is valid.
00185   if( fid == NULL )
00186     return FALSE;
00187 
00188   // Ensure that the maximum message length is appropriate.
00189   if( maxMessageLength < 32 ) // at least 3 sync bytes plus 25 header bytes plus the 4 byte CRC.
00190   {
00191     return FALSE;
00192   }
00193   
00194   // Ensure that the file pointer is not at the end of the file.
00195   if( feof(fid) )
00196   {
00197     *wasEndOfFileReached = TRUE;
00198     return TRUE;
00199   }
00200   
00201   while( !(*wasEndOfFileReached) && !(*wasMessageFound) )
00202   {
00203     if( startSearch )
00204     {
00205       // Get the first two sync bytes.
00206       sync[0] = (unsigned char)fgetc( fid );
00207       if( feof( fid ) )
00208       {
00209         *wasEndOfFileReached = TRUE;
00210         return TRUE;
00211       }
00212 
00213       sync[1] = (unsigned char)fgetc( fid );
00214       if( feof( fid ) )
00215       {
00216         *wasEndOfFileReached = TRUE;
00217         return TRUE;
00218       }  
00219 
00220       startSearch = FALSE;
00221     }
00222 
00223     // Get the last sync byte.
00224     sync[2] = (unsigned char)fgetc( fid );
00225     if( feof( fid ) )
00226     {
00227       *wasEndOfFileReached = TRUE;
00228       return TRUE;
00229     }
00230 
00231     // Check the full sync.
00232     if( sync[0] == 0xAA && sync[1] == 0x44 && sync[2] == 0x12 )
00233     {
00234       // Found a potential message.
00235       i = 0;
00236       message[i] = sync[0]; i++;
00237       message[i] = sync[1]; i++;
00238       message[i] = sync[2]; i++;
00239 
00240       // Determine the file position of the start of the message
00241       fpos = ftell(fid);
00242       if( fpos < 2 ) // check less than 2 because we are at least 3 bytes into the file
00243       {
00244         return FALSE;
00245       }
00246       *filePosition = fpos - 3; // The start of the message sync.
00247 
00248       // Get the header length.
00249       headerLength = (unsigned char)fgetc( fid );
00250       if( feof( fid ) )
00251       {
00252         *wasEndOfFileReached = TRUE;
00253         return TRUE;
00254       }
00255       message[i] = headerLength;
00256       i++;
00257 
00258       // Get rest of the header (maximum of 255 bytes).
00259       byteCount = (int)fread( &(message[i]), sizeof(unsigned char), headerLength-4, fid );
00260       if( byteCount+4 != headerLength )
00261       {
00262         *wasEndOfFileReached = TRUE;
00263         return TRUE;
00264       }
00265 
00266       // Determine the 2 byte message ID.
00267       *messageID  = message[i]; 
00268       i++;
00269       *messageID |= message[i] << 8;      
00270       i++;
00271 
00272       // Skip the message type and the port address.
00273       i += 2;
00274 
00275       // Determine the 2 byte data length.
00276       dataLength = message[i];
00277       i++;
00278       dataLength |= message[i] << 8;
00279       i++;
00280 
00281       // Check that the entire message length will fit in the message buffer.
00282       msgLength = headerLength + dataLength + 4; // plus 4 for the CRC.
00283       if( msgLength > maxMessageLength )
00284       {
00285         // Perhaps the data length was bad.
00286         // Start searching again after the sync that was found.
00287         // Set the file position to the last of the three sync bytes.
00288         if( fseek( fid, fpos, SEEK_SET ) != 0 )
00289         {
00290           // The seek failed.
00291           return FALSE;
00292         }
00293         startSearch = TRUE;
00294         continue;
00295       }
00296 
00297       // Get the data part of the message.
00298       // advance i to the start of the data
00299       i = headerLength;
00300       byteCount = (int)fread( &(message[i]), sizeof(unsigned char), dataLength, fid );
00301       if( byteCount != dataLength )
00302       {
00303         // Either a message has an invalid length or a message was cut off.
00304         // We do not want to read over any valid message so start the search again.
00305         // Start searching again after the sync that was found.
00306         // Set the file position to the last of the three sync bytes.
00307         if( fseek( fid, fpos, SEEK_SET ) != 0 )
00308         {
00309           // The seek failed.
00310           return FALSE;
00311         }
00312         startSearch = TRUE;
00313         continue;
00314       }
00315       i += dataLength;
00316 
00317       // Get the CRC.
00318       byteCount = (int)fread( &(message[i]), sizeof(unsigned char), 4, fid );
00319       if( byteCount != 4 )
00320       {
00321         *wasEndOfFileReached = TRUE;
00322         return TRUE;
00323       }
00324       messageCRC  = message[i];
00325       i++;
00326       messageCRC |= message[i] << 8;
00327       i++;
00328       messageCRC |= message[i] << 16;
00329       i++;
00330       messageCRC |= message[i] << 24;
00331       i++;
00332       
00333       // Compare the received message CRC with the calculated CRC.
00334       if( !NOVATEL_CheckCRC32( message, msgLength, messageCRC, &isCRCValid ) )
00335       {
00336         return FALSE;
00337       }
00338       if( !isCRCValid )
00339       {
00340         *numberBadCRC += 1;
00341 
00342         // Start searching again after the sync that was found.
00343         // Set the file position to the last of the three sync bytes.
00344         if( fseek( fid, fpos, SEEK_SET ) != 0 )
00345         {
00346           // The seek failed.
00347           return FALSE;
00348         }
00349         startSearch = TRUE;
00350         continue;
00351       }
00352 
00353       *wasMessageFound = TRUE;
00354       *messageLength = msgLength;
00355       break;
00356     }
00357     else
00358     {
00359       // Shift the first two bytes of the sync.
00360       sync[0] = sync[1];
00361       sync[1] = sync[2];
00362     }
00363   }
00364   return TRUE;
00365 }
00366 
00367 
00368 BOOL NOVATELOEM4_DecodeBinaryMessageHeader(
00369   const unsigned char *message,            //!< The message buffer containing a complete NOVATEL OEM4 binary message (input).
00370   const unsigned short messageLength,      //!< The length of the entire message (input).
00371   NOVATELOEM4_structBinaryHeader *header   //!< A pointer to a NovAtel OEM4 header information struct (output).
00372   )
00373 {
00374   unsigned u32 = 0;
00375   NOVATELOEM4_structRxStatusBitField* ptrRxStatusBitField = NULL;
00376 
00377   // sanity checks
00378   if( message == NULL )
00379     return FALSE;
00380   if( header == NULL )
00381     return FALSE;
00382 
00383   if( messageLength < 4 ) // at least the sync bytes and the header length
00384     return FALSE;
00385 
00386   
00387   // Get the header length;
00388   header->headerLength = message[3];
00389   if( messageLength < header->headerLength )
00390     return FALSE;
00391   
00392   header->messageID  = message[4];
00393   header->messageID |= message[5] << 8;
00394 
00395   header->messageType = message[6];
00396 
00397   header->portAddress = message[7];
00398   
00399   header->dataLength  = message[8];
00400   header->dataLength |= message[9] << 8;
00401 
00402   header->sequenceNr  = message[10];
00403   header->sequenceNr |= message[11] << 8;
00404 
00405   header->idleTime = message[12];
00406   
00407   header->eTimeStatus = (NOVATELOEM4_enumTimeStatus)message[13];
00408 
00409   header->gpsWeek  = message[14];
00410   header->gpsWeek |= message[15] << 8;
00411 
00412   header->gpsMilliSeconds  = message[16];
00413   header->gpsMilliSeconds |= message[17] << 8;
00414   header->gpsMilliSeconds |= message[18] << 16;
00415   header->gpsMilliSeconds |= message[19] << 24;
00416 
00417   u32  = message[20];
00418   u32 |= message[21] << 8;
00419   u32 |= message[22] << 16;
00420   u32 |= message[23] << 24;
00421   ptrRxStatusBitField = (NOVATELOEM4_structRxStatusBitField *)&u32;  
00422   header->receiverStatus = *ptrRxStatusBitField;
00423 
00424   header->reserved  = message[24];
00425   header->reserved |= message[25] << 8;
00426 
00427   header->receiverVersion  = message[26];
00428   header->receiverVersion |= message[27] << 8;
00429 
00430   return TRUE;
00431 }
00432 
00433 
00434 BOOL NOVATELOEM4_DecodeRANGECMPB(
00435   const unsigned char *message,            //!< The message buffer containing a complete RANGEB message (input).
00436   const unsigned short messageLength,      //!< The length of the entire message (input).
00437   NOVATELOEM4_structBinaryHeader* header,  //!< A pointer to a NovAtel OEM4 header information struct (output).
00438   NOVATELOEM4_structObservation* obsArray, //!< A pointer to a user provided array of struct_NOVATELOEM4_RANGE (output).
00439   const unsigned char maxNrObs,            //!< The maximum number of elements in the array provided (input).
00440   unsigned *nrObs                          //!< The number of valid elements set in the array (output).
00441   )
00442 {
00443   unsigned i = 0;                 // A counter.
00444   unsigned index = 0;             // An index into the message buffer.
00445   unsigned char headerLength = 0; // The length of the header only as indicated in the header.
00446   unsigned short dataLength = 0;  // The length of the data part of the message as indicated in the header.
00447   unsigned short msgLength = 0;   // The computed length of the entire message.
00448   unsigned short testLength = 0;  // The expected length of the data given the number of observations.
00449   
00450   // Perform sanity checks.
00451   if( message == NULL )
00452     return FALSE;
00453   if( obsArray == NULL )
00454     return FALSE;
00455   if( message[0] != 0xAA || message[1] != 0x44 || message[2] != 0x12 ) // sync must be present
00456     return FALSE;
00457 
00458   // Decode the binary header.
00459   if( !NOVATELOEM4_DecodeBinaryMessageHeader( message, messageLength, header ) )
00460     return FALSE;
00461 
00462   // Get the header length.
00463   headerLength = message[3];
00464 
00465   // Get the data length.
00466   dataLength = message[8];
00467   dataLength |= message[9] << 8;
00468 
00469   // Check that the data length + header length + 4 (crc) == messageLength provided.
00470   msgLength = dataLength + headerLength + 4;
00471   if( msgLength != messageLength )
00472     return FALSE;
00473 
00474   // Get the number of observations.
00475   index = headerLength;
00476   *nrObs = message[index]; 
00477   index++;
00478   *nrObs |= message[index] << 8;
00479   index++;
00480   *nrObs |= message[index] << 16;
00481   index++;
00482   *nrObs |= message[index] << 24;
00483 
00484   // Check that there is enough room in the array provided.
00485   if( maxNrObs < *nrObs )
00486     return FALSE;
00487 
00488   // Check that the number of observations fits the data length given.
00489   testLength = (unsigned short)(*nrObs*24 + 4);
00490   if( testLength != dataLength )
00491    return FALSE;
00492 
00493   // Decode the data.
00494   index = headerLength+4;
00495   for( i = 0; i < *nrObs; i++ )
00496   {
00497     long long int rangecmp_psr       = 0;  // Pseudorange
00498     long rangecmp_adr                = 0;  // Carrier Phase
00499     double adr_rolls                 = 0;  // ADR Roll Over
00500     long rangecmp_doppler_freq       = 0;  // Doppler Frequency 
00501     unsigned char rangecmp_stdev_psr = 0;  // Standard Deviation of Pseudorange
00502     unsigned char rangecmp_stdev_adr = 0;  // Standard Deviation of Carrier Phase
00503     unsigned long rangecmp_lock_time = 0;  // Lock Time
00504     unsigned long rangecmp_cno       = 0;  // Carier to Noise Density
00505     double dtmp = 0;
00506 
00507     // Tracking Status  (32 bits 0 - 31)   
00508     obsArray[i].rawTrackingStatus  = message[index];       index++;
00509     obsArray[i].rawTrackingStatus |= message[index] <<  8; index++;
00510     obsArray[i].rawTrackingStatus |= message[index] << 16; index++;
00511     obsArray[i].rawTrackingStatus |= message[index] << 24; index++;
00512 
00513     if( !NOVATELOEM4_DecodeTrackingStatus( obsArray[i].rawTrackingStatus, &obsArray[i].trackingStatus ) )    
00514       return FALSE;    
00515   
00516     // Doppler Frequency (28 bits 32-59)
00517     rangecmp_doppler_freq |=  message[index];        index++;
00518     rangecmp_doppler_freq |=  message[index] << 8;   index++;
00519     rangecmp_doppler_freq |=  message[index] << 16;  index++;
00520     rangecmp_doppler_freq |= (message[index] & 0x0F) << 24;  
00521     // check negative value.
00522     if( (rangecmp_doppler_freq >> 27) > 0 )
00523     {    
00524       rangecmp_doppler_freq |= 0xF0000000;      
00525     }
00526     dtmp = (double)(rangecmp_doppler_freq);
00527     dtmp /= 256.0;
00528     obsArray[i].doppler = (float)dtmp;
00529 
00530     // Pseudorange (36 bits 60-95)
00531     rangecmp_psr    |= (message[index]&0xF0)>>4; index++;
00532     rangecmp_psr    |=  message[index]<<4;       index++;
00533     rangecmp_psr    |=  message[index]<<12;      index++;
00534     rangecmp_psr    |=  message[index]<<20;      index++;
00535     rangecmp_psr    |= (long long int)(message[index])<<28; index++;    
00536     // check negative value.
00537     if( ((long long int)(rangecmp_psr)>>35) > 0 )
00538     {
00539       // The checkneg variable was added to avoid compiler warnings with g++ (v4.1.2).
00540       unsigned long int checkneg = 0xFFFFFFF0;
00541       checkneg *= (unsigned long int)(1<<31);
00542       checkneg *= 2; // this is 0xFFFF FFF0 0000 0000
00543       rangecmp_psr |= checkneg;
00544     }
00545     obsArray[i].psr  = (double)rangecmp_psr;
00546     obsArray[i].psr /= 128.0; 
00547 
00548     // Carrier Phase (32 bits 96-127)
00549     rangecmp_adr   = message[index];       index++;
00550     rangecmp_adr  |= message[index] << 8;  index++;
00551     rangecmp_adr  |= message[index] << 16; index++;
00552     rangecmp_adr  |= message[index] << 24; index++;
00553     obsArray[i].adr  = (double) rangecmp_adr;
00554     obsArray[i].adr /= 256.0; 
00555 
00556     // Carrier Phase Roll over Calculations
00557     if (obsArray[i].trackingStatus.eFrequency == NOVATELOEM4_L1)
00558       adr_rolls =(obsArray[i].psr / GPS_WAVELENGTHL1  + obsArray[i].adr) / 8388608.0;
00559     else
00560       adr_rolls =(obsArray[i].psr / GPS_WAVELENGTHL2  + obsArray[i].adr) / 8388608.0;
00561 
00562     if ( adr_rolls <= 0)
00563       adr_rolls = adr_rolls - 0.5;
00564     else
00565       adr_rolls = adr_rolls + 0.5;
00566 
00567     obsArray[i].adr = obsArray[i].adr  - (8388608.0 * (int)adr_rolls);
00568 
00569     // Pseudorange Standard Deviation (4 bits 128-131)
00570     rangecmp_stdev_psr = ((message[index] & 0x0F)); 
00571 
00572     // Refer to page 352 OEMV Family Firmware Version 3.100 Reference Manual Rev 3
00573     // Refer to page 143 OEM4 Family Firmware Version 1.000 Command and Log Reference Manual Rev 4
00574     switch(rangecmp_stdev_psr)
00575     { 
00576     case 0  : obsArray[i].psrstd = 0.050f; break;
00577     case 1  : obsArray[i].psrstd = 0.075f; break;
00578     case 2  : obsArray[i].psrstd = 0.113f; break;
00579     case 3  : obsArray[i].psrstd = 0.169f; break;
00580     case 4  : obsArray[i].psrstd = 0.253f; break;
00581     case 5  : obsArray[i].psrstd = 0.380f; break;
00582     case 6  : obsArray[i].psrstd = 0.570f; break;
00583     case 7  : obsArray[i].psrstd = 0.854f; break;
00584     case 8  : obsArray[i].psrstd = 1.281f; break;
00585     case 9  : obsArray[i].psrstd = 2.375f; break;
00586     case 10 : obsArray[i].psrstd = 4.750f; break;
00587     case 11 : obsArray[i].psrstd = 9.500f; break;
00588     case 12 : obsArray[i].psrstd = 19.000f; break;
00589     case 13 : obsArray[i].psrstd = 38.000f; break;
00590     case 14 : obsArray[i].psrstd = 76.000f; break;
00591     case 15 : obsArray[i].psrstd = 152.000f; break;
00592     };
00593 
00594     // Carrier Phase Standard Deviation (4 bits 132-135)
00595     rangecmp_stdev_adr =  message[index] >> 4 ; 
00596     index++;
00597     obsArray[i].adrstd = (float)(rangecmp_stdev_adr+1)/512;
00598 
00599     // Prn Number (8 bits 136-143)
00600     obsArray[i].prn = (unsigned short)message[index]; 
00601     index++;
00602 
00603     // Lock Time  (21 bits 144-164)
00604     rangecmp_lock_time   |= message[index];      index++;
00605     rangecmp_lock_time   |= message[index] << 8; index++;
00606     rangecmp_lock_time   |= ((message[index] & 0x1F)) << 16;  
00607 
00608     obsArray[i].locktime  = (float)(rangecmp_lock_time);
00609     obsArray[i].locktime /= 32.0;
00610 
00611     // Carrier to Noise Density ( 5 bits 165-169)
00612     rangecmp_cno         = (message[index] & 0xE0 ) >> 5; index++;  
00613     rangecmp_cno        |= (message[index] & 0x03 ) << 3;  
00614 
00615     obsArray[i].cno = (float)rangecmp_cno + 20;
00616 
00617     // Reserved (22 bits 170-191)
00618     obsArray[i].reserved  = (message[index] & 0xFC )>> 5; index ++ ;
00619     obsArray[i].reserved |= message[index] << 8;          index ++ ;
00620     obsArray[i].reserved |= message[index] << 16;         index ++ ;
00621   }
00622 
00623   return TRUE;
00624 }
00625 
00626 BOOL NOVATELOEM4_DecodeRANGEB(
00627   const unsigned char *message,            //!< The message buffer containing a complete RANGEB message (input).
00628   const unsigned short messageLength,      //!< The length of the entire message (input).
00629   NOVATELOEM4_structBinaryHeader* header,  //!< A pointer to a NovAtel OEM4 header information struct (output).
00630   NOVATELOEM4_structObservation* obsArray, //!< A pointer to a user provided array of struct_NOVATELOEM4_RANGE (output).
00631   const unsigned char maxNrObs,            //!< The maximum number of elements in the array provided (input).
00632   unsigned *nrObs                          //!< The number of valid elements set in the array (output).
00633   )
00634 {
00635   unsigned i = 0;                 // A counter.
00636   unsigned j = 0;                 // A counter.
00637   unsigned index = 0;             // An index into the message buffer.
00638   unsigned char headerLength = 0; // The length of the header only as indicated in the header.
00639   unsigned short dataLength = 0;  // The length of the data part of the message as indicated in the header.
00640   unsigned short msgLength = 0;   // The computed length of the entire message.
00641   unsigned short testLength = 0;  // The expected length of the data given the number of observations.
00642   unsigned char dbytes[8];        // Enough bytes to store a double.
00643   unsigned char fbytes[4];        // Enough bytes to store a float.
00644   double *dptr;                   // A pointer to a double.
00645   float *fptr;                    // A pointer to a float.
00646   
00647   // Perform sanity checks.
00648   if( message == NULL )
00649     return FALSE;
00650   if( obsArray == NULL )
00651     return FALSE;
00652   if( message[0] != 0xAA || message[1] != 0x44 || message[2] != 0x12 ) // sync must be present
00653     return FALSE;
00654 
00655   // Decode the binary header.
00656   if( !NOVATELOEM4_DecodeBinaryMessageHeader( message, messageLength, header ) )
00657     return FALSE;
00658 
00659   // Get the header length.
00660   headerLength = message[3];
00661 
00662   // Get the data length.
00663   dataLength = message[8];
00664   dataLength |= message[9] << 8;
00665 
00666   // Check that the data length + header length + 4 (crc) == messageLength provided.
00667   msgLength = dataLength + headerLength + 4;
00668   if( msgLength != messageLength )
00669     return FALSE;
00670 
00671   // Get the number of observations.
00672   index = headerLength;
00673   *nrObs = message[index]; 
00674   index++;
00675   *nrObs |= message[index] << 8;
00676   index++;
00677   *nrObs |= message[index] << 16;
00678   index++;
00679   *nrObs |= message[index] << 24;
00680 
00681   // Check that there is enough room in the array provided.
00682   if( maxNrObs < *nrObs )
00683     return FALSE;
00684 
00685   // Check that the number of observations fits the data length given.
00686   testLength = (unsigned short)(*nrObs*44 + 4);
00687   if( testLength != dataLength )
00688     return FALSE;
00689 
00690   // Decode the data.
00691   index = headerLength+4;
00692   for( i = 0; i < *nrObs; i++ )
00693   {
00694     obsArray[i].prn       = message[index];      index++;
00695     obsArray[i].prn      |= message[index] << 8; index++;
00696     obsArray[i].reserved  = message[index];      index++;
00697     obsArray[i].reserved |= message[index] << 8; index++;
00698     
00699     for( j=0; j<8; j++ ){ dbytes[j] = message[index]; index++; }
00700     dptr = (double*)&dbytes;
00701     obsArray[i].psr = *dptr;
00702 
00703     for( j=0; j<4; j++ ){ fbytes[j] = message[index]; index++; }
00704     fptr = (float*)&fbytes;
00705     obsArray[i].psrstd = *fptr;
00706 
00707     for( j=0; j<8; j++ ){ dbytes[j] = message[index]; index++; }
00708     dptr = (double*)&dbytes;
00709     obsArray[i].adr = *dptr;
00710 
00711     for( j=0; j<4; j++ ){ fbytes[j] = message[index]; index++; }
00712     fptr = (float*)&fbytes;
00713     obsArray[i].adrstd = *fptr;
00714 
00715     for( j=0; j<4; j++ ){ fbytes[j] = message[index]; index++; }
00716     fptr = (float*)&fbytes;
00717     obsArray[i].doppler = *fptr;
00718 
00719     for( j=0; j<4; j++ ){ fbytes[j] = message[index]; index++; }
00720     fptr = (float*)&fbytes;
00721     obsArray[i].cno = *fptr;
00722 
00723     for( j=0; j<4; j++ ){ fbytes[j] = message[index]; index++; }
00724     fptr = (float*)&fbytes;
00725     obsArray[i].locktime = *fptr;
00726 
00727     obsArray[i].rawTrackingStatus  = message[index];       index++;
00728     obsArray[i].rawTrackingStatus |= message[index] <<  8; index++;
00729     obsArray[i].rawTrackingStatus |= message[index] << 16; index++;
00730     obsArray[i].rawTrackingStatus |= message[index] << 24; index++;
00731 
00732     if( !NOVATELOEM4_DecodeTrackingStatus( obsArray[i].rawTrackingStatus, &obsArray[i].trackingStatus ) )    
00733       return FALSE;    
00734   }
00735  
00736   return TRUE;
00737 }
00738 
00739 
00740 BOOL NOVATELOEM4_DecodeTrackingStatus(
00741   const unsigned rawTrackingStatus,                 //!< The raw 32 bit tracking status value.
00742   NOVATELOEM4_structTrackingStatus *trackingStatus  //!< The decoded tracking status information.
00743   )
00744 {
00745   // local typedef
00746   typedef struct
00747   {
00748     unsigned trackingState          :5;
00749     unsigned channelNumber          :5;
00750     unsigned phaseLockFlag          :1;
00751     unsigned parityKnownFlag        :1;
00752     unsigned codeLockedFlag         :1;
00753     unsigned correlatorSpacing      :3;
00754     unsigned satelliteSystem        :3;
00755     unsigned reserved1              :1;
00756     unsigned grouping               :1;
00757     unsigned frequency              :2;
00758     unsigned codeType               :3;
00759     unsigned forwardErrorCorrection :1;
00760     unsigned primaryL1Channel       :1;
00761     unsigned halfCycleFlag          :1;
00762     unsigned reserved3              :1;
00763     unsigned prnLockFlag            :1;
00764     unsigned channelAssignment      :1;
00765   } NOVATELOEM4_channelStatusBitField; // 0-31 bits
00766 
00767   NOVATELOEM4_channelStatusBitField bitField;
00768   NOVATELOEM4_channelStatusBitField *ptrBitField;
00769 
00770   if( trackingStatus == NULL )
00771     return FALSE;
00772 
00773   ptrBitField = (NOVATELOEM4_channelStatusBitField *)&rawTrackingStatus;
00774   bitField = *ptrBitField;
00775 
00776   trackingStatus->eTrackingState     = (NOVATELOEM4_enumTrackingState)bitField.trackingState;
00777   trackingStatus->channelNumber      = bitField.channelNumber;
00778   trackingStatus->isPhaseLocked      = bitField.phaseLockFlag;
00779   trackingStatus->isParityKnown      = bitField.parityKnownFlag;
00780   trackingStatus->isCodeLocked       = bitField.codeLockedFlag;
00781   trackingStatus->eCorrelatorSpacing = (NOVATELOEM4_enumCorrelatorSpacing)bitField.correlatorSpacing;
00782   trackingStatus->eSatelliteSystem   = (NOVATELOEM4_enumSatelliteSystem)bitField.satelliteSystem;
00783   trackingStatus->isGrouped          = bitField.grouping;
00784   trackingStatus->eFrequency         = (NOVATELOEM4_enumFrequency)bitField.frequency;
00785   trackingStatus->eCodeType          = (NOVATELOEM4_enumCodeType)bitField.codeType;
00786   trackingStatus->isFECEnabled       = bitField.forwardErrorCorrection;
00787   trackingStatus->isPrimaryL1Channel = bitField.primaryL1Channel;
00788   trackingStatus->isHalfCycleAdded   = bitField.halfCycleFlag;
00789   trackingStatus->isForcedAssignment = bitField.channelAssignment;
00790 
00791   return TRUE;
00792 }
00793 
00794 
00795 BOOL NOVATELOEM4_DecodeRAWEPHEMB(
00796   const unsigned char *message,           //!< The message buffer containing a complete RANGEB message (input).
00797   const unsigned short messageLength,     //!< The length of the entire message (input).
00798   NOVATELOEM4_structBinaryHeader* header, //!< A pointer to a NovAtel OEM4 header information struct (output).
00799   unsigned *prn,                          //!< The satellite PRN number.
00800   unsigned *reference_week,               //!< The reference GPS week (0-1024+) [weeks].
00801   unsigned *reference_time,               //!< The reference GPS time of week (0-604800) [s].
00802   unsigned       *tow,                    //!< The time of week in subframe1, the time of the leading bit edge of subframe 2 [s]
00803   unsigned short *iodc,                   //!< 10 bit issue of data (clock), 8 LSB bits will match the iode                  []    
00804   unsigned char  *iode,                   //!< 8 bit  issue of data (ephemeris)                                              []
00805   unsigned       *toe,                    //!< reference time ephemeris (0-604800)                                           [s]
00806   unsigned       *toc,                    //!< reference time (clock)   (0-604800)                                           [s]      
00807   unsigned short *week,                   //!< 10 bit gps week 0-1023 (user must account for week rollover )                 [week]    
00808   unsigned char  *health,                 //!< 6 bit health parameter, 0 if healthy, unhealth othersize                      [0=healthy]    
00809   unsigned char  *alert_flag,             //!< 1 = URA may be worse than indicated                                           [0,1]
00810   unsigned char  *anti_spoof,             //!< anti-spoof flag from 0=off, 1=on                                              [0,1]    
00811   unsigned char  *code_on_L2,             //!< 0=reserved, 1=P code on L2, 2=C/A on L2                                       [0,1,2]
00812   unsigned char  *ura,                    //!< User Range Accuracy lookup code, 0 is excellent, 15 is use at own risk        [0-15], see p. 83 GPSICD200C
00813   unsigned char  *L2_P_data_flag,         //!< flag indicating if P is on L2 1=true                                          [0,1]
00814   unsigned char  *fit_interval_flag,      //!< fit interval flag (four hour interval or longer) 0=4 fours, 1=greater         [0,1]
00815   unsigned short *age_of_data_offset,     //!< age of data offset                                                            [s]
00816   double *tgd,     //!< group delay                                                                   [s]
00817   double *af2,     //!< polynomial clock correction coefficient (rate of clock drift)                 [s/s^2]
00818   double *af1,     //!< polynomial clock correction coefficient (clock drift)                         [s/s]
00819   double *af0,     //!< polynomial clock correction coefficient (clock bias)                          [s]    
00820   double *m0,      //!< mean anomaly at reference time                                                [rad]
00821   double *delta_n, //!< mean motion difference from computed value                                    [rad/s]
00822   double *ecc,     //!< eccentricity                                                                  []
00823   double *sqrta,   //!< square root of the semi-major axis                                            [m^(1/2)]
00824   double *omega0,  //!< longitude of ascending node of orbit plane at weekly epoch                    [rad]
00825   double *i0,      //!< inclination angle at reference time                                           [rad]
00826   double *w,       //!< argument of perigee                                                           [rad]
00827   double *omegadot,//!< rate of right ascension                                                       [rad/s]
00828   double *idot,    //!< rate of inclination angle                                                     [rad/s]
00829   double *cuc,     //!< amplitude of the cosine harmonic correction term to the argument of latitude  [rad]
00830   double *cus,     //!< amplitude of the sine harmonic correction term to the argument of latitude    [rad]
00831   double *crc,     //!< amplitude of the cosine harmonic correction term to the orbit radius          [m]
00832   double *crs,     //!< amplitude of the sine harmonic correction term to the orbit radius            [m]
00833   double *cic,     //!< amplitude of the cosine harmonic correction term to the angle of inclination  [rad]
00834   double *cis      //!< amplitude of the sine harmonic correction term to the angle of inclination    [rad]
00835   )
00836 {
00837   unsigned short msgLength; // The computed message length [bytes].
00838   unsigned index = 0;       // An index in to the message.
00839   BOOL result = FALSE;
00840 
00841   // Perform sanity checks.
00842   if( message == NULL )
00843     return FALSE;
00844   if( message[0] != 0xAA || message[1] != 0x44 || message[2] != 0x12 ) // sync must be present
00845     return FALSE;
00846 
00847   // Decode the binary header.
00848   if( !NOVATELOEM4_DecodeBinaryMessageHeader( message, messageLength, header ) )
00849     return FALSE;
00850 
00851   // Check that the length of the message matches the header information.
00852   msgLength = header->headerLength + header->dataLength + 4;
00853   if( msgLength != messageLength )
00854     return FALSE;
00855 
00856   index = header->headerLength;
00857   *prn  = message[index];       index++;
00858   *prn |= message[index] << 8;  index++;
00859   *prn |= message[index] << 16; index++;
00860   *prn |= message[index] << 24; index++;
00861 
00862   *reference_week  = message[index];       index++;
00863   *reference_week |= message[index] << 8;  index++;
00864   *reference_week |= message[index] << 16; index++;
00865   *reference_week |= message[index] << 24; index++;
00866 
00867   *reference_time  = message[index];       index++;
00868   *reference_time |= message[index] << 8;  index++;
00869   *reference_time |= message[index] << 16; index++;
00870   *reference_time |= message[index] << 24; index++;
00871 
00872   result = GPS_DecodeRawGPSEphemeris(
00873     &message[index],
00874     &message[index+30],
00875     &message[index+60],    
00876     (unsigned short)(*prn),
00877     tow,
00878     iodc,
00879     iode,  
00880     toe,   
00881     toc,   
00882     week,  
00883     health,
00884     alert_flag,
00885     anti_spoof,
00886     code_on_L2,
00887     ura,       
00888     L2_P_data_flag,
00889     fit_interval_flag,
00890     age_of_data_offset, 
00891     tgd,                
00892     af2,                
00893     af1,                
00894     af0,                
00895     m0,                
00896     delta_n,           
00897     ecc,               
00898     sqrta,             
00899     omega0,            
00900     i0,                
00901     w,                 
00902     omegadot,          
00903     idot,              
00904     cuc,               
00905     cus,               
00906     crc,               
00907     crs,               
00908     cic,               
00909     cis                
00910     );
00911   if( !result )
00912     return FALSE;
00913 
00914   return TRUE;
00915 }