rinex.c

Go to the documentation of this file.
00001 /**
00002 \file    rinex.c
00003 \brief   GNSS core 'c' function library: RINEX VERSION 2.11 related functions.
00004 \author  Glenn D. MacGougan (GDM)
00005 \date    2007-12-03
00006 \since   2007-12-02
00007 
00008 \b REFERENCES \n
00009 - http://www.aiub-download.unibe.ch/rinex/rinex211.txt
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 #include <stdio.h>
00040 #include <string.h>
00041 #include <ctype.h>
00042 #include <memory.h>
00043 #include <math.h>
00044 #include "rinex.h"
00045 #include "time_conversion.h"
00046 #include "constants.h"
00047 
00048 #define RINEX_HEADER_SIZE (32768) //!< The maximum size of a RINEX header buffer [bytes].
00049 #define RINEX_LINEBUF_SIZE (8192) //!< The maximum size of a string used in RINEX decoding [bytes].
00050 #define RINEX_MAX_NR_SATS    (64) //!< The maximum array size for "struct_RINEX_satellite RINEX_sat[RINEX_MAX_NR_SATS]".
00051 #define RINEX_MAX_NR_OBS     (64) //!< The maximum array size for "struct_RINEX_obs RINEX_obs[RINEX_MAX_NR_OBS]".
00052 
00053 
00054 
00055 static const double RINEX_MIN_URA[16] = {0.00, 2.40, 3.40, 4.85, 6.85,  9.65, 13.65, 24.00, 48.00, 96.00, 192.00, 384.00,  768.00, 1536.00, 3072.00, 6144.00};
00056 static const double RINEX_MAX_URA[16] = {2.40, 3.40, 4.85, 6.85, 9.65, 13.65, 24.00, 48.00, 96.00, 192.0, 384.00, 768.00, 1536.00, 3072.00, 6144.00, 1.0e100};
00057 
00058 
00059 
00060 /// A container for a single RINEX data observation.
00061 typedef struct
00062 {
00063   unsigned char loss_of_lock_indicator; //!< The loss of lock indicator (0-7).
00064   unsigned char signal_strength;        //!< The sign strength (1-9).
00065   double value;                         //!< The data value of the observation.
00066   RINEX_enumObservationType type;       //!< The type of observation.
00067   GNSS_enumSystem system;               //!< The GNSS system.
00068   unsigned short id;                    //!< The satellite id.
00069   BOOL isValid;                         //!< A boolean indicating if this observation is valid (The value could be zero or blank).
00070 } struct_RINEX_obs;
00071 
00072 /// A container for a single RINEX satellite descriptor.
00073 typedef struct
00074 {
00075   RINEX_enumSatelliteSystemType type;
00076   unsigned short id;
00077 } struct_RINEX_satellite;
00078 
00079 
00080 /// A static function to trim the whitespace from the left and right of a C string.
00081 static BOOL RINEX_trim_left_right(
00082   char* str,            //!< (input/output) The input C string.
00083   unsigned max_length,  //!< (input) The maximum length of the input string.
00084   unsigned *str_length  //!< (output) The length of the string after trimming the whitespace.
00085   );
00086 
00087 /// A static function to get the header lines indicated by the record descriptor.
00088 static BOOL RINEX_get_header_lines(
00089   const char* header,              //!<  (input) The full RINEX header buffer.
00090   const unsigned header_size,      //!<  (input) The size of the valid data in the RINEX header buffer.
00091   const char* record_desciptor,    //!<  (input) The record descriptor. e.g. "RINEX VERSION / TYPE"
00092   char* lines_buffer,              //!< (output) The output buffer. 
00093   const unsigned max_lines_buffer, //!<  (input) The maximum size of the output buffer.
00094   unsigned* nr_lines               //!< (output) The number of lines read that correspond to the record descriptor.
00095   );
00096 
00097 /// A static function to erase a substring from a string.
00098 static BOOL RINEX_erase(
00099   char *erase_me, //!< (input) Erase this string from the input string.
00100   char* str       //!< (input/output) The input C string.  
00101   ); 
00102  
00103 /// A static function to decode the "# / TYPES OF OBSERV" part of the RINEX OBS header.
00104 static BOOL RINEX_GetObservationTypes(
00105   const char* header_buffer,          //!< (input) The full RINEX header buffer.
00106   const unsigned header_buffer_size,  //!< (input) The size of the valid data in the RINEX header buffer.
00107   RINEX_structDecodedHeader* header   //!< (input/output) The container for decoded header information.
00108   );
00109 
00110 
00111 /// \brief  A static function to interpret special records 
00112 /// (embedded Header records within the observation data).
00113 /// This function is called when the epoch flag is greater than 1.
00114 static BOOL RINEX_DealWithSpecialRecords(
00115   FILE* fid,                               //!< (input) An open (not NULL) file pointer to the RINEX data.
00116   RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
00117   BOOL *wasEndOfFileReached,               //!< Has the end of the file been reached (output).
00118   unsigned *filePosition,                  //!< The file position for the start of the message found (output).  
00119   const unsigned nr_special_records        //!< The number of special records.
00120   );
00121 
00122 /// \brief  Decode the next observation set for one satellite from the file.
00123 static BOOL RINEX_GetNextObserationSetForOneSatellite(
00124   FILE* fid,                                   //!< (input) An open (not NULL) file pointer to the RINEX data.
00125   RINEX_structDecodedHeader* RINEX_header,     //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
00126   BOOL *wasEndOfFileReached,                   //!< (input/output) Has the end of the file been reached.
00127   unsigned *filePosition,                      //!< (input/output) The file position for the start of the message found (output).  
00128   struct_RINEX_obs* RINEX_obs,                 //!< (input/output) A pointer to the array of RINEX observations.
00129   const unsigned RINEX_max_nr_obs,             //!< (input) The maximum size of the RINEX_obs array.
00130   unsigned *RINEX_nr_obs,                      //!< (output) The number of valid obs in the RINEX_obs array.  
00131   const RINEX_enumSatelliteSystemType sattype, //!< (input) The satellite type.
00132   const unsigned short id                      //!< (input) The satellite id.
00133   );
00134 
00135 /// \brief  A static function to replace float values exponents denoted with 'D' with 'E'.
00136 static BOOL RINEX_ReplaceDwithE( char *str, const unsigned length );
00137 
00138 
00139 /**
00140 \brief   A static function to convert URA in meters to the URA index.
00141 \author  Glenn D. MacGougan
00142 
00143 \n REFERENCES \n
00144 - GPS ICD 200C, p83. section 20.3.3.3.1.3
00145 */
00146 static BOOL RINEX_ConvertURA_meters_to_URA_index( 
00147   double ura_m,         //!< The user range accuracy [m].
00148   unsigned char *ura    //!< The user range accuracy index.
00149   );
00150 
00151 
00152 
00153 /**
00154 \brief Convert RINEX signal strength to Carrier to Noise Density Ratio (CNo).
00155 The conversion from RINEX signal strength to Carrier to Noise Density Ratio (CNo)
00156 is ad-hoc. We will use the NovAtel OEM4 definition for CNo (dB-Hz). Nominally,
00157 NovAtel maintains phase lock above 28 dB-Hz and a reasonable maximum signal strength
00158 occurs at about 50 dB-Hz. We'll map linearly using 5 to 9 signals strength mapping to
00159 28 to 50 dB-Hz. (50-28)/(9-5) = (y-28)/(x-5) where x is known, solve for y.
00160 y = 5.5x+0.5.
00161 
00162 \author  Glenn D. MacGougan
00163 \return  TRUE(1) if successful, FALSE(0) otherwise.
00164 */
00165 static BOOL RINEX_ConvertSignalStrengthToUsableCNo(
00166   float *cno,  //!< (input/output) The carrier to noise density ratio (dB-Hz)
00167   const unsigned char signal_strength
00168   );
00169   
00170 
00171 
00172 BOOL RINEX_trim_left_right(
00173   char* str,            //!< (input) The input C string.
00174   unsigned max_length,  //!< (input) The maximum length of the input string.
00175   unsigned *str_length  //!< (output) The length of the string after trimming the whitespace.
00176   )
00177 {
00178   int i = 0;
00179   int j = 0;
00180   int start = 0;
00181   size_t length = 0;
00182   if( str == NULL )
00183     return FALSE;
00184   if( str_length == NULL )
00185     return FALSE;
00186 
00187   // Remove leading whitesapce
00188   length = strlen( str );
00189   if( length > max_length )
00190     return FALSE;
00191   if( length == 0 )
00192     return TRUE;
00193 
00194   for( i = 0; i < (int)length; i++ )
00195   {
00196     if( isspace(str[i]) )
00197       continue;
00198     else
00199       break;
00200   }
00201   start = i;
00202   i = 0;
00203   for( j = start; j < (int)length; j++ )
00204   {
00205     str[i] = str[j];
00206     i++;
00207   }
00208   str[i] = '\0';
00209 
00210   // Remove trailing whitespace.
00211   length = strlen( str );
00212   for( i = (int)(length-1); i > 0; i-- )
00213   {
00214     if( isspace(str[i]) )
00215     {
00216       str[i] = '\0';
00217     }      
00218     else
00219     {
00220       break;
00221     }
00222   }
00223 
00224   length = strlen( str );
00225   *str_length = (unsigned)length;
00226   
00227   return TRUE;
00228 }
00229 
00230 
00231 BOOL RINEX_get_header_lines(
00232   const char* header_buffer,       //!<  (input) The full RINEX header buffer.
00233   const unsigned header_size,      //!<  (input) The size of the valid data in the RINEX header buffer.
00234   const char* record_desciptor,    //!<  (input) The record descriptor. e.g. "RINEX VERSION / TYPE"
00235   char* lines_buffer,              //!< (output) The output buffer. 
00236   const unsigned max_lines_buffer, //!<  (input) The maximum size of the output buffer.
00237   unsigned* nr_lines               //!< (output) The number of lines read that correspond to the record descriptor.
00238   )
00239 {
00240   char *pch = NULL;
00241   char *strptr = NULL;
00242   unsigned i = 0;
00243   int j = 0;
00244   int record_descriptor_index = 0;
00245   int scount = 0;
00246   unsigned nr_valid_header_lines = 0;
00247   size_t length_record_desciptor = 0;
00248   
00249   typedef struct
00250   {
00251     char str[128]; // RINEX header strings should be 80 chars.
00252     size_t length;
00253   } struct_header_line_token;
00254 
00255   struct_header_line_token token[256];
00256   char buffer[RINEX_HEADER_SIZE];
00257 
00258   if( header_buffer == NULL )
00259     return FALSE;
00260   if( header_size == 0 )
00261     return FALSE;
00262   if( record_desciptor == NULL )
00263     return FALSE;
00264   if( lines_buffer == NULL )
00265     return FALSE;
00266   if( nr_lines == NULL )
00267     return FALSE;
00268 
00269   if( header_size+1 > RINEX_HEADER_SIZE )
00270     return FALSE;
00271 
00272   memcpy( buffer, header_buffer, header_size );
00273   buffer[header_size] = '\0';
00274 
00275   *nr_lines = 0;
00276 
00277   length_record_desciptor = strlen( record_desciptor );
00278   if( length_record_desciptor == 0 )
00279     return FALSE;
00280 
00281   strptr = strstr( header_buffer, record_desciptor );
00282   if( strptr == NULL )
00283     return TRUE; // No line found with this descriptor at all.
00284 
00285   // Tokenize the copied input buffer.
00286   pch = strtok( buffer,"\r\n");
00287   while( pch != NULL )
00288   {
00289     strptr = strstr( pch, record_desciptor );
00290     if( strptr != NULL )
00291     {
00292       token[i].length = strlen( pch );
00293       if( token[i].length < 128 )
00294       {
00295         strcpy( token[i].str, pch );
00296         i++;
00297       }
00298     }
00299     pch = strtok (NULL, "\r\n");
00300   }
00301   nr_valid_header_lines = i;
00302 
00303   if( nr_valid_header_lines == 0 )
00304     return FALSE;
00305 
00306   // Search the tokenized strings (lines) for the record descriptor.
00307   for( i = 0; i < nr_valid_header_lines; i++ )
00308   {
00309     strptr = strstr( token[i].str, record_desciptor );
00310     if( strptr != NULL )
00311     {
00312       // check where it was found in the line.
00313       record_descriptor_index = (int)(strptr - token[i].str);
00314 
00315       if( record_descriptor_index != 60 )
00316       {
00317         // record decriptor must be at columns 61-80
00318         // either extra/missing space(s) or the record descriptor is
00319         // present within a COMMENT line.
00320 
00321         if( strcmp( record_desciptor, "COMMENT" ) == 0 )
00322         {
00323           // possible COMMENT repeated on same line.
00324           // ok to decode this line for this record descriptor
00325         }
00326         else
00327         {
00328           // Check if within a comment line.
00329           strptr = strstr( token[i].str, "COMMENT" );
00330           if( strptr != NULL )
00331           {
00332             j = (int)(strptr - token[i].str);
00333             if( j == 60 )
00334             {
00335               // This is a comment line.
00336               continue;
00337             }
00338             else
00339             {
00340               // strange record, ignore
00341               continue;
00342             }
00343           }
00344           else
00345           {
00346             // This is likely a few missing or extra spaces.
00347             if( record_descriptor_index > 55 && record_descriptor_index < 65 )
00348             {
00349               // OK to decode this line for this record descriptor.
00350             }
00351             else
00352             {
00353               // too many extra spaces
00354               continue;
00355             }
00356           }
00357         }
00358       }
00359 
00360       // Add this line to the lines_buffer.
00361       if( (scount + token[i].length + 1) >= max_lines_buffer )
00362         return FALSE;
00363 
00364       scount += sprintf( lines_buffer+scount, "%s\n", token[i].str );
00365       *nr_lines += 1;
00366     }
00367   }  
00368   return TRUE;
00369 }
00370 
00371 
00372 BOOL RINEX_erase(
00373   char *erase_me, //!< (input) Erase this string from the input string.
00374   char* str       //!< (input/output) The input C string.
00375   )
00376 {
00377   int i = 0;
00378   int j = 0;
00379   char *strptr = NULL;
00380   size_t len = 0;
00381   size_t len_erase_me = 0;
00382 
00383   if( erase_me == NULL )
00384     return FALSE;
00385   if( str == NULL )
00386     return FALSE;
00387   
00388   len_erase_me = strlen( erase_me );
00389   if( len_erase_me == 0 )
00390     return TRUE;
00391 
00392   len = strlen(str);
00393   if( len == 0 )
00394     return TRUE;
00395 
00396   if( len_erase_me > len )
00397     return TRUE;
00398 
00399   strptr = strstr( str, erase_me );
00400   
00401   while( strptr != NULL )
00402   {
00403     j = (int)(strptr - str);   // start of the string to be erased
00404     i = j + (int)len_erase_me; // end of the string to be erased
00405 
00406     for( i; i < (int)len; i++ )
00407     {
00408       str[j] = str[i];
00409       if( str[j] == '\0' )
00410         break;
00411       j++;
00412     }
00413     str[j] = '\0';
00414     len = strlen(str);  
00415 
00416     strptr = strstr( str, erase_me );
00417   }
00418   return TRUE;
00419 }
00420 
00421 
00422 // static 
00423 BOOL RINEX_GetObservationTypes(
00424   const char* header_buffer,          //!< (input) The full RINEX header buffer.
00425   const unsigned header_buffer_size,  //!< (input) The size of the valid data in the RINEX header buffer.
00426   RINEX_structDecodedHeader* header   //!< (input/output) The container for decoded header information.  
00427   )
00428 {
00429   char lines_buffer[RINEX_LINEBUF_SIZE];
00430   unsigned nr_lines = 0;
00431   BOOL result;
00432   char *pch = NULL;
00433   unsigned count = 0;
00434   char token[128];
00435   unsigned len=0;
00436   BOOL isFirst = TRUE;
00437   
00438   result = RINEX_get_header_lines(
00439     header_buffer,
00440     header_buffer_size,
00441     "# / TYPES OF OBSERV",
00442     lines_buffer,
00443     RINEX_LINEBUF_SIZE,
00444     &nr_lines
00445     );
00446   if( result == FALSE )
00447     return FALSE;  
00448   // strip the record description from the string
00449   result = RINEX_erase( "# / TYPES OF OBSERV", lines_buffer );
00450   if( result == FALSE )
00451     return FALSE;
00452   
00453   // Determine the number of observation types.
00454   if( sscanf( lines_buffer, "%u", &(header->nr_obs_types) ) != 1 )
00455     return FALSE;
00456   
00457   // Clean up the string a little.
00458   result = RINEX_trim_left_right( lines_buffer, RINEX_LINEBUF_SIZE, &len );
00459   if( result == FALSE )
00460     return FALSE;
00461 
00462   // Tokenize the string.
00463   pch = strtok( lines_buffer, " \t\r\n\f" );
00464   while( pch != NULL && count < header->nr_obs_types )
00465   {
00466     if( !isFirst )
00467     {
00468       strcpy( token, pch );
00469       result = RINEX_trim_left_right( token, 128, &len );
00470       if( result == FALSE )
00471         return FALSE;
00472       if( strlen(token) > 0 )
00473       {
00474         if( strcmp( token, "L1" ) == 0 )
00475           header->obs_types[count] =  RINEX_OBS_TYPE_L1;
00476         else if( strcmp( token, "L2" ) == 0 )
00477           header->obs_types[count] =  RINEX_OBS_TYPE_L2;
00478         else if( strcmp( token, "C1" ) == 0 )
00479           header->obs_types[count] =  RINEX_OBS_TYPE_C1;
00480         else if( strcmp( token, "P1" ) == 0 )
00481           header->obs_types[count] =  RINEX_OBS_TYPE_P1;
00482         else if( strcmp( token, "P2" ) == 0 )
00483           header->obs_types[count] =  RINEX_OBS_TYPE_P2;
00484         else if( strcmp( token, "D1" ) == 0 )
00485           header->obs_types[count] =  RINEX_OBS_TYPE_D1;
00486         else if( strcmp( token, "D2" ) == 0 )
00487           header->obs_types[count] =  RINEX_OBS_TYPE_D2;
00488         else if( strcmp( token, "T1" ) == 0 )
00489           header->obs_types[count] =  RINEX_OBS_TYPE_T1;
00490         else if( strcmp( token, "T2" ) == 0 )
00491           header->obs_types[count] =  RINEX_OBS_TYPE_T2;
00492         else if( strcmp( token, "S1" ) == 0 )
00493           header->obs_types[count] =  RINEX_OBS_TYPE_S1;
00494         else if( strcmp( token, "S2" ) == 0 )
00495           header->obs_types[count] =  RINEX_OBS_TYPE_S2;
00496         else
00497           header->obs_types[count] =  RINEX_OBS_TYPE_UNKNOWN;
00498 
00499         count++;
00500       }
00501     }
00502     pch = strtok (NULL, " ,.-");    
00503     isFirst = FALSE;
00504   }
00505 
00506   if( count != header->nr_obs_types )
00507     return FALSE;
00508 
00509   return TRUE;
00510 }
00511 
00512 
00513 BOOL RINEX_DealWithSpecialRecords(
00514   FILE* fid,                               //!< (input) An open (not NULL) file pointer to the RINEX data.
00515   RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
00516   BOOL *wasEndOfFileReached,               //!< Has the end of the file been reached (output).
00517   unsigned *filePosition,                  //!< The file position for the start of the message found (output).  
00518   const unsigned nr_special_records        //!< The number of special records.
00519   )
00520 {
00521   char line_buffer[RINEX_LINEBUF_SIZE];
00522   BOOL result = FALSE;
00523   size_t length = 0;
00524   unsigned i = 0;
00525 
00526   if( fid == NULL )
00527     return FALSE;
00528   if( RINEX_header == NULL )
00529     return FALSE;
00530   if( wasEndOfFileReached == NULL )
00531     return FALSE;
00532   if( filePosition == NULL )
00533     return FALSE;
00534 
00535   // check nothing to do.
00536   if( nr_special_records == 0 )
00537     return TRUE;
00538 
00539   for( i = 0; i < nr_special_records; i++ )
00540   {
00541 
00542     if( fgets( line_buffer, RINEX_LINEBUF_SIZE, fid ) == NULL )
00543     {
00544       if( feof(fid) )
00545       {
00546         *wasEndOfFileReached = TRUE;
00547         return TRUE;
00548       }
00549       else
00550       {
00551         return FALSE;
00552       }
00553     }
00554     *filePosition = ftell(fid);
00555 
00556     if( strstr(line_buffer, "COMMENT") != NULL )
00557     {
00558       // This line is a comment. Ignore and continue.
00559     }
00560     else if( strstr(line_buffer, "WAVELENGTH FACT L1/2") != NULL )
00561     {
00562       // The wavelength factors have changed for some satellites.
00563 
00564       // GDM todo deal with these changes
00565     }
00566     else if( strstr(line_buffer, "MARKER NAME") != NULL )
00567     {
00568       // The marker name has changed.
00569       result = RINEX_erase("MARKER NAME", line_buffer);
00570       if( result == FALSE )
00571         return FALSE;
00572       result = RINEX_trim_left_right(line_buffer, RINEX_LINEBUF_SIZE, &length );
00573       if( result == FALSE )
00574         return FALSE;
00575       if( length < 64 )
00576       {
00577         strcpy(RINEX_header->marker_name, line_buffer);
00578       }
00579       else
00580       {
00581         return FALSE;
00582       }
00583     }
00584     else if( strstr(line_buffer, "MARKER NUMBER") != NULL )
00585     {
00586       // ignore for now
00587     }
00588     else if( strstr(line_buffer, "ANTENNA: DELTA H/E/N") != NULL )
00589     {
00590       if( sscanf( line_buffer, "%Lf %Lf %Lf", 
00591         &(RINEX_header->antenna_delta_h), 
00592         &(RINEX_header->antenna_ecc_e), 
00593         &(RINEX_header->antenna_ecc_n) ) != 3 )
00594       {
00595         return FALSE;
00596       }
00597     }
00598     else if( strstr(line_buffer, "APPROX POSITION XYZ") != NULL )
00599     {
00600       if( sscanf( line_buffer, "%Lf %Lf %Lf", 
00601         &(RINEX_header->x), 
00602         &(RINEX_header->y), 
00603         &(RINEX_header->z) ) != 3 )
00604       {
00605         return FALSE;
00606       }
00607     }
00608     else 
00609     {
00610       // The rest not handled yet.
00611     }
00612   }
00613   return TRUE;
00614 }
00615 
00616 
00617 //static 
00618 BOOL RINEX_GetNextObserationSetForOneSatellite(
00619   FILE* fid,                                   //!< (input) An open (not NULL) file pointer to the RINEX data.
00620   RINEX_structDecodedHeader* RINEX_header,     //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
00621   BOOL *wasEndOfFileReached,                   //!< (input/output) Has the end of the file been reached.
00622   unsigned *filePosition,                      //!< (input/output) The file position for the start of the message found (output).  
00623   struct_RINEX_obs* RINEX_obs,                 //!< (input/output) A pointer to the array of RINEX observations.
00624   const unsigned RINEX_max_nr_obs,             //!< (input) The maximum size of the RINEX_obs array.
00625   unsigned *RINEX_nr_obs,                      //!< (output) The number of valid obs in the RINEX_obs array.  
00626   const RINEX_enumSatelliteSystemType sattype, //!< (input) The satellite type.
00627   const unsigned short id                      //!< (input) The satellite id.
00628   )
00629 {
00630   unsigned i = 0;
00631   char line_buffer[RINEX_LINEBUF_SIZE];
00632   unsigned count = 0;
00633   char str_a[15];
00634   char str_b[15];
00635   char str_c[15];
00636   char str_d[15];
00637   char str_e[15];
00638 
00639   memset( str_a, 0, 15 );
00640   memset( str_b, 0, 15 );
00641   memset( str_c, 0, 15 );
00642   memset( str_d, 0, 15 );
00643   memset( str_e, 0, 15 );
00644 
00645   // Check input
00646   if( fid == NULL )
00647     return FALSE;
00648   if( RINEX_header == NULL )
00649     return FALSE;
00650   if( wasEndOfFileReached == NULL )
00651     return FALSE;
00652   if( filePosition == NULL )
00653     return FALSE;
00654   if( RINEX_obs == NULL )
00655     return FALSE;
00656   if( RINEX_max_nr_obs == 0 )
00657     return FALSE;
00658   if( RINEX_nr_obs == NULL )
00659     return FALSE;
00660   if( RINEX_header->nr_obs_types >= RINEX_max_nr_obs )
00661     return FALSE;
00662 
00663 
00664   // Get the next line from the file.
00665   if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
00666   {
00667     if( feof(fid) )
00668     {
00669       *wasEndOfFileReached = TRUE;
00670       return TRUE;
00671     }
00672     else
00673     {
00674       return FALSE;
00675     }
00676   }
00677   *filePosition = ftell(fid);
00678 
00679   // Set the default values to zero.
00680   memset( RINEX_obs, 0, sizeof(struct_RINEX_obs)*RINEX_header->nr_obs_types );
00681 
00682   switch( RINEX_header->nr_obs_types )
00683   {
00684   case 1:
00685     {
00686       count = sscanf( line_buffer, "%14c%c%c", 
00687         str_a,
00688         &RINEX_obs[0].loss_of_lock_indicator, 
00689         &RINEX_obs[0].signal_strength
00690         );        
00691       sscanf( str_a, "%Lf", &(RINEX_obs[0].value) );
00692       break;
00693     }
00694   case 2:
00695     {
00696       count = sscanf( line_buffer, "%14c%c%c%14c%c%c", 
00697         str_a,
00698         &RINEX_obs[0].loss_of_lock_indicator, 
00699         &RINEX_obs[0].signal_strength,
00700         str_b,
00701         &RINEX_obs[1].loss_of_lock_indicator, 
00702         &RINEX_obs[1].signal_strength 
00703         );
00704       sscanf( str_a, "%Lf", &(RINEX_obs[0].value) );
00705       sscanf( str_b, "%Lf", &(RINEX_obs[1].value) );
00706       break;
00707     }
00708   case 3:
00709     {
00710       count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c", 
00711         str_a,
00712         &RINEX_obs[0].loss_of_lock_indicator, 
00713         &RINEX_obs[0].signal_strength,
00714         str_b,
00715         &RINEX_obs[1].loss_of_lock_indicator, 
00716         &RINEX_obs[1].signal_strength,
00717         str_c,
00718         &RINEX_obs[2].loss_of_lock_indicator, 
00719         &RINEX_obs[2].signal_strength
00720       );
00721       sscanf( str_a, "%Lf", &(RINEX_obs[0].value) );
00722       sscanf( str_b, "%Lf", &(RINEX_obs[1].value) );
00723       sscanf( str_c, "%Lf", &(RINEX_obs[2].value) );      
00724       break;
00725     }
00726   case 4:
00727     {
00728       count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c", 
00729         str_a, 
00730         &RINEX_obs[0].loss_of_lock_indicator, 
00731         &RINEX_obs[0].signal_strength,
00732         str_b, 
00733         &RINEX_obs[1].loss_of_lock_indicator, 
00734         &RINEX_obs[1].signal_strength,
00735         str_c, 
00736         &RINEX_obs[2].loss_of_lock_indicator, 
00737         &RINEX_obs[2].signal_strength,
00738         str_d, 
00739         &RINEX_obs[3].loss_of_lock_indicator, 
00740         &RINEX_obs[3].signal_strength
00741       );
00742       sscanf( str_a, "%Lf", &(RINEX_obs[0].value) );
00743       sscanf( str_b, "%Lf", &(RINEX_obs[1].value) );
00744       sscanf( str_c, "%Lf", &(RINEX_obs[2].value) );
00745       sscanf( str_d ,"%Lf", &(RINEX_obs[3].value) );
00746       break;
00747     }
00748     case 5:
00749     {
00750       count = sscanf( line_buffer, "%14Lf%c%c%14Lf%c%c%14Lf%c%c%14Lf%c%c%14Lf%c%c", 
00751         str_a,
00752         &RINEX_obs[0].loss_of_lock_indicator, 
00753         &RINEX_obs[0].signal_strength,
00754         str_b,
00755         &RINEX_obs[1].loss_of_lock_indicator, 
00756         &RINEX_obs[1].signal_strength,
00757         str_c,
00758         &RINEX_obs[2].loss_of_lock_indicator, 
00759         &RINEX_obs[2].signal_strength,
00760         str_d,
00761         &RINEX_obs[3].loss_of_lock_indicator, 
00762         &RINEX_obs[3].signal_strength,
00763         str_e,
00764         &RINEX_obs[4].loss_of_lock_indicator, 
00765         &RINEX_obs[4].signal_strength
00766       );
00767       sscanf( str_a, "%Lf", &(RINEX_obs[0].value) );
00768       sscanf( str_b, "%Lf", &(RINEX_obs[1].value) );
00769       sscanf( str_c, "%Lf", &(RINEX_obs[2].value) );
00770       sscanf( str_d ,"%Lf", &(RINEX_obs[3].value) );
00771       sscanf( str_e ,"%Lf", &(RINEX_obs[4].value) );      
00772       break;
00773     }
00774     case 6:
00775     case 7:
00776     case 8:
00777     case 9:
00778     case 10: // intentional fall thru.   
00779     {
00780 
00781       count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c", 
00782         str_a, 
00783         &RINEX_obs[0].loss_of_lock_indicator, 
00784         &RINEX_obs[0].signal_strength,
00785         str_b, 
00786         &RINEX_obs[1].loss_of_lock_indicator, 
00787         &RINEX_obs[1].signal_strength,
00788         str_c, 
00789         &RINEX_obs[2].loss_of_lock_indicator, 
00790         &RINEX_obs[2].signal_strength,
00791         str_d, 
00792         &RINEX_obs[3].loss_of_lock_indicator, 
00793         &RINEX_obs[3].signal_strength,
00794         str_e, 
00795         &RINEX_obs[4].loss_of_lock_indicator, 
00796         &RINEX_obs[4].signal_strength
00797       );
00798       sscanf( str_a, "%Lf", &(RINEX_obs[0].value) );
00799       sscanf( str_b, "%Lf", &(RINEX_obs[1].value) );
00800       sscanf( str_c, "%Lf", &(RINEX_obs[2].value) );
00801       sscanf( str_d ,"%Lf", &(RINEX_obs[3].value) );
00802       sscanf( str_e ,"%Lf", &(RINEX_obs[4].value) );      
00803 
00804       memset( str_a, 0, 15 );
00805       memset( str_b, 0, 15 );
00806       memset( str_c, 0, 15 );
00807       memset( str_d, 0, 15 );
00808       memset( str_e, 0, 15 );
00809       
00810       // Get the next line from the file.
00811       if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
00812       {
00813         if( feof(fid) )
00814         {
00815           *wasEndOfFileReached = TRUE;
00816           return TRUE;
00817         }
00818         else
00819         {
00820           return FALSE;
00821         }      
00822       }
00823 
00824       switch( RINEX_header->nr_obs_types-5 )
00825       {
00826       case 1:
00827         {
00828           count = sscanf( line_buffer, "%14c%c%c", 
00829             str_a, 
00830             &RINEX_obs[5].loss_of_lock_indicator, 
00831             &RINEX_obs[5].signal_strength
00832             );        
00833           sscanf( str_a, "%Lf", &(RINEX_obs[5].value) );
00834           break;
00835         }
00836       case 2:
00837         {
00838           count = sscanf( line_buffer, "%14c%c%c%14c%c%c", 
00839             str_a, 
00840             &RINEX_obs[5].loss_of_lock_indicator, 
00841             &RINEX_obs[5].signal_strength,
00842             str_b, 
00843             &RINEX_obs[6].loss_of_lock_indicator, 
00844             &RINEX_obs[6].signal_strength 
00845             );
00846           sscanf( str_a, "%Lf", &(RINEX_obs[5].value) );
00847           sscanf( str_b, "%Lf", &(RINEX_obs[6].value) );          
00848           break;
00849         }
00850       case 3:
00851         {
00852           count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c", 
00853             str_a, 
00854             &RINEX_obs[5].loss_of_lock_indicator, 
00855             &RINEX_obs[5].signal_strength,
00856             str_b, 
00857             &RINEX_obs[6].loss_of_lock_indicator, 
00858             &RINEX_obs[6].signal_strength,
00859             str_c, 
00860             &RINEX_obs[7].loss_of_lock_indicator, 
00861             &RINEX_obs[7].signal_strength
00862             );
00863           sscanf( str_a, "%Lf", &(RINEX_obs[5].value) );
00864           sscanf( str_b, "%Lf", &(RINEX_obs[6].value) );
00865           sscanf( str_c, "%Lf", &(RINEX_obs[7].value) );          
00866           break;
00867         }
00868       case 4:
00869         {
00870           count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c", 
00871             str_a, 
00872             &RINEX_obs[5].loss_of_lock_indicator, 
00873             &RINEX_obs[5].signal_strength,
00874             str_b, 
00875             &RINEX_obs[6].loss_of_lock_indicator, 
00876             &RINEX_obs[6].signal_strength,
00877             str_c, 
00878             &RINEX_obs[7].loss_of_lock_indicator, 
00879             &RINEX_obs[7].signal_strength,
00880             str_d, 
00881             &RINEX_obs[8].loss_of_lock_indicator, 
00882             &RINEX_obs[8].signal_strength
00883             );
00884           sscanf( str_a, "%Lf", &(RINEX_obs[5].value) );
00885           sscanf( str_b, "%Lf", &(RINEX_obs[6].value) );
00886           sscanf( str_c, "%Lf", &(RINEX_obs[7].value) );
00887           sscanf( str_d ,"%Lf", &(RINEX_obs[8].value) );          
00888           break;
00889         }
00890       case 5:
00891         {
00892           count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c", 
00893             str_a, 
00894             &RINEX_obs[5].loss_of_lock_indicator, 
00895             &RINEX_obs[5].signal_strength,
00896             str_b, 
00897             &RINEX_obs[6].loss_of_lock_indicator, 
00898             &RINEX_obs[6].signal_strength,
00899             str_c, 
00900             &RINEX_obs[7].loss_of_lock_indicator, 
00901             &RINEX_obs[7].signal_strength,
00902             str_d, 
00903             &RINEX_obs[8].loss_of_lock_indicator, 
00904             &RINEX_obs[8].signal_strength,
00905             str_e, 
00906             &RINEX_obs[9].loss_of_lock_indicator, 
00907             &RINEX_obs[9].signal_strength
00908             );
00909           sscanf( str_a, "%Lf", &(RINEX_obs[5].value) );
00910           sscanf( str_b, "%Lf", &(RINEX_obs[6].value) );
00911           sscanf( str_c, "%Lf", &(RINEX_obs[7].value) );
00912           sscanf( str_d ,"%Lf", &(RINEX_obs[8].value) );
00913           sscanf( str_e ,"%Lf", &(RINEX_obs[9].value) );      
00914           break;
00915         }
00916       default:
00917         {
00918           return FALSE;
00919           break;
00920         }
00921       }
00922       break;
00923     }
00924     case 11:
00925     {
00926       count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c", 
00927         str_a, 
00928         &RINEX_obs[0].loss_of_lock_indicator, 
00929         &RINEX_obs[0].signal_strength,
00930         str_b, 
00931         &RINEX_obs[1].loss_of_lock_indicator, 
00932         &RINEX_obs[1].signal_strength,
00933         str_c, 
00934         &RINEX_obs[2].loss_of_lock_indicator, 
00935         &RINEX_obs[2].signal_strength,
00936         str_d, 
00937         &RINEX_obs[3].loss_of_lock_indicator, 
00938         &RINEX_obs[3].signal_strength,
00939         str_e, 
00940         &RINEX_obs[4].loss_of_lock_indicator, 
00941         &RINEX_obs[4].signal_strength
00942         );
00943       sscanf( str_a, "%Lf", &(RINEX_obs[0].value) );
00944       sscanf( str_b, "%Lf", &(RINEX_obs[1].value) );
00945       sscanf( str_c, "%Lf", &(RINEX_obs[2].value) );
00946       sscanf( str_d ,"%Lf", &(RINEX_obs[3].value) );
00947       sscanf( str_e ,"%Lf", &(RINEX_obs[4].value) );      
00948 
00949       memset( str_a, 0, 15 );
00950       memset( str_b, 0, 15 );
00951       memset( str_c, 0, 15 );
00952       memset( str_d, 0, 15 );
00953       memset( str_e, 0, 15 );
00954 
00955       // Get the next line from the file.
00956       if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
00957       {
00958         if( feof(fid) )
00959         {
00960           *wasEndOfFileReached = TRUE;
00961           return TRUE;
00962         }
00963         else
00964         {
00965           return FALSE;
00966         }            
00967       }
00968 
00969       count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c", 
00970         str_a,
00971         &RINEX_obs[5].loss_of_lock_indicator, 
00972         &RINEX_obs[5].signal_strength,
00973         str_b,
00974         &RINEX_obs[6].loss_of_lock_indicator, 
00975         &RINEX_obs[6].signal_strength,
00976         str_c,
00977         &RINEX_obs[7].loss_of_lock_indicator, 
00978         &RINEX_obs[7].signal_strength,
00979         str_d,
00980         &RINEX_obs[8].loss_of_lock_indicator, 
00981         &RINEX_obs[8].signal_strength,
00982         str_e,
00983         &RINEX_obs[9].loss_of_lock_indicator, 
00984         &RINEX_obs[9].signal_strength
00985         );
00986       sscanf( str_a, "%Lf", &(RINEX_obs[5].value) );
00987       sscanf( str_b, "%Lf", &(RINEX_obs[6].value) );
00988       sscanf( str_c, "%Lf", &(RINEX_obs[7].value) );
00989       sscanf( str_d ,"%Lf", &(RINEX_obs[8].value) );
00990       sscanf( str_e ,"%Lf", &(RINEX_obs[9].value) );
00991     
00992       memset( str_a, 0, 15 );
00993 
00994       // Get the next line from the file.
00995       if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
00996       {
00997         if( feof(fid) )
00998         {
00999           *wasEndOfFileReached = TRUE;
01000           return TRUE;
01001         }
01002         else
01003         {
01004           return FALSE;
01005         }            
01006       }
01007 
01008       count = sscanf( line_buffer, "%14c%c%c", 
01009         str_a,
01010         &RINEX_obs[10].loss_of_lock_indicator, 
01011         &RINEX_obs[10].signal_strength
01012         );  
01013       sscanf( str_a, "%Lf", &(RINEX_obs[10].value) );
01014       break;
01015     }
01016   default:
01017     {
01018       return FALSE;
01019       break;
01020     }
01021   }
01022 
01023   for( i = 0; i < RINEX_header->nr_obs_types; i++ )
01024   {
01025     // Zero values denote invalid observations.
01026     if( RINEX_obs[i].value == 0 )
01027     {
01028       RINEX_obs[i].isValid = FALSE;
01029     }
01030     else
01031     {
01032       RINEX_obs[i].isValid = TRUE;
01033     }
01034 
01035     switch( RINEX_obs[i].loss_of_lock_indicator )
01036     {
01037     case '0': RINEX_obs[i].loss_of_lock_indicator = 0; break;
01038     case '1': RINEX_obs[i].loss_of_lock_indicator = 1; break;
01039     case '2': RINEX_obs[i].loss_of_lock_indicator = 2; break;
01040     case '3': RINEX_obs[i].loss_of_lock_indicator = 3; break;
01041     case '4': RINEX_obs[i].loss_of_lock_indicator = 4; break;
01042     case '5': RINEX_obs[i].loss_of_lock_indicator = 5; break;
01043     case '6': RINEX_obs[i].loss_of_lock_indicator = 6; break;
01044     case '7': RINEX_obs[i].loss_of_lock_indicator = 7; break;
01045     default:  RINEX_obs[i].loss_of_lock_indicator = 0; break;
01046     }
01047 
01048     switch( RINEX_obs[i].signal_strength )
01049     {
01050     case '0': RINEX_obs[i].signal_strength = 0; break;
01051     case '1': RINEX_obs[i].signal_strength = 1; break;
01052     case '2': RINEX_obs[i].signal_strength = 2; break;
01053     case '3': RINEX_obs[i].signal_strength = 3; break;
01054     case '4': RINEX_obs[i].signal_strength = 4; break;
01055     case '5': RINEX_obs[i].signal_strength = 5; break;
01056     case '6': RINEX_obs[i].signal_strength = 6; break;
01057     case '7': RINEX_obs[i].signal_strength = 7; break;
01058     case '8': RINEX_obs[i].signal_strength = 8; break;
01059     case '9': RINEX_obs[i].signal_strength = 9; break;
01060     default:  RINEX_obs[i].signal_strength = 0; break;
01061     }
01062 
01063     RINEX_obs[i].type = RINEX_header->obs_types[i];
01064 
01065     switch( sattype )
01066     {
01067     case RINEX_SATELLITE_SYSTEM_GPS:
01068       {
01069         RINEX_obs[i].system = GNSS_GPS;
01070         RINEX_obs[i].id = id;
01071         break;
01072       }
01073     case RINEX_SATELLITE_SYSTEM_GLO:
01074       {
01075         RINEX_obs[i].system = GNSS_GLONASS;
01076         RINEX_obs[i].id = id; // GLONASS slot number.
01077         break;
01078       }
01079     case RINEX_SATELLITE_SYSTEM_GEO:
01080       {
01081         RINEX_obs[i].system = GNSS_WAAS;    
01082         RINEX_obs[i].id = id + 100;
01083         break;
01084       }
01085     case RINEX_SATELLITE_SYSTEM_NSS:
01086       {
01087         continue; break; // Not supported. Ignore the data from this source. Continue to outer for loop.
01088       }
01089     default:
01090       {
01091         continue; break; // Not supported. Ignore the data from this source. Continue to outer for loop.
01092       }
01093     }
01094   }
01095 
01096   *RINEX_nr_obs = RINEX_header->nr_obs_types;
01097 
01098   return TRUE;
01099 }
01100 
01101 
01102 BOOL RINEX_ConvertSignalStrengthToUsableCNo(
01103   float *cno,                          //!< (input/output) The carrier to noise density ratio (dB-Hz)
01104   const unsigned char signal_strength  //!< (input) The RINEX signal strength indicator (0-9).
01105   )
01106 {
01107   if( cno == NULL )
01108     return FALSE;
01109 
01110   if( signal_strength > 0 && signal_strength < 10 )
01111   {
01112     *cno = 5.5f*signal_strength + 0.5f;
01113   }
01114   return TRUE;
01115 }
01116 
01117 
01118 BOOL RINEX_GetHeader( 
01119   const char* filepath,           //!< Path to the RINEX file.
01120   char* buffer,                   //!< (input/output) A character buffer in which to place the RINEX header.
01121   const unsigned buffer_max_size, //!< (input)  The maximum size of the buffer [bytes]. This value should be large enough to hold the entire header, (8192 to 16384).
01122   unsigned *buffer_size,          //!< (output) The length of the header data placed in the buffer [bytes].
01123   double *version,                //!< (output) The RINEX version number. e.g. 1.0, 2.0, 2.2, 3.0, etc.
01124   RINEX_enumFileType *file_type   //!< (output) The RINEX file type. 
01125   )
01126 {
01127   FILE* fid = NULL;                 // A file pointer for the RINEX file.
01128   char line_buffer[1024];               // A container for one line of the header.
01129   char *strptr = NULL;              // A pointer to a string.
01130   BOOL end_of_header_found = FALSE; // A boolean to indicate if the end of header was found.
01131   char type_char;
01132   
01133   size_t line_length = 0; // The length of one line.
01134   unsigned scount = 0;      // A counter/index used to compose the header buffer.
01135 
01136   fid = fopen( filepath, "r" );
01137   if( fid == NULL )
01138     return FALSE;
01139 
01140   // The first line of the file must be the RINEX VERSION / TYPE
01141   if( fgets( line_buffer, 1024, fid ) == NULL )
01142     return FALSE;
01143   strptr = strstr( line_buffer, "RINEX VERSION / TYPE" );
01144   if( strptr == NULL )
01145     return FALSE;
01146 
01147   // Add the first line to the buffer
01148   line_length = strlen( line_buffer );
01149   if( scount+line_length >= buffer_max_size )    
01150     return FALSE;    
01151   scount += sprintf( buffer+scount, "%s", line_buffer );
01152 
01153   // Extract the RINEX version and type.
01154   if( sscanf( line_buffer, "%Lf %c", version, &type_char ) != 2 )
01155     return FALSE;
01156   *file_type = (RINEX_enumFileType)type_char;
01157 
01158   do
01159   {
01160     if( fgets( line_buffer, 1024, fid ) == NULL )
01161       break;
01162 
01163     if( strstr( line_buffer, "END OF HEADER" ) != NULL )
01164     {
01165       end_of_header_found = TRUE;
01166     }
01167 
01168     // Add the line of the buffer.
01169     line_length = strlen( line_buffer );
01170     if( scount+line_length >= buffer_max_size )   
01171       return FALSE;    
01172     scount += sprintf( buffer+scount, "%s", line_buffer );
01173 
01174   }while( !end_of_header_found );
01175 
01176   if( end_of_header_found )
01177   {
01178     *buffer_size = scount;
01179     return TRUE;
01180   }
01181   else
01182   {
01183     return FALSE;
01184   }
01185 }
01186 
01187 
01188 
01189 BOOL RINEX_DecodeHeader_ObservationFile(
01190   const char* header_buffer,         //!< (input) The character buffer containing the RINEX header.
01191   const unsigned header_buffer_size, //!< (input) The size of the character buffer containing the RINEX header [bytes]. Not the maximum size, the size of the valid data in the buffer.
01192   RINEX_structDecodedHeader* header  //!< (output) The decoded header data.
01193   )
01194 {
01195   BOOL result = FALSE;
01196   char lines_buffer[RINEX_LINEBUF_SIZE];
01197   unsigned nr_lines = 0;
01198   char rinex_type_char = 0;
01199   char time_system_str[128];
01200   unsigned len = 0;
01201   int itmp[5];  
01202   
01203   if( header_buffer == NULL )
01204     return FALSE;
01205   if( header_buffer_size == 0 )
01206     return FALSE;
01207   if( header == NULL )
01208     return FALSE;
01209 
01210   memset( header, 0, sizeof(RINEX_structDecodedHeader) );
01211 
01212   result = RINEX_get_header_lines(
01213     header_buffer,
01214     header_buffer_size,
01215     "RINEX VERSION / TYPE",
01216     lines_buffer,
01217     RINEX_LINEBUF_SIZE,
01218     &nr_lines
01219     );
01220   if( result == FALSE )
01221     return FALSE;
01222   if( nr_lines != 1 )
01223     return FALSE;
01224   if( sscanf( lines_buffer, "%Lf %c", &(header->version), &rinex_type_char ) != 2 )
01225     return FALSE;
01226   header->type = (RINEX_enumFileType)rinex_type_char;
01227 
01228 
01229   result = RINEX_get_header_lines(
01230     header_buffer,
01231     header_buffer_size,
01232     "MARKER NAME",
01233     lines_buffer,
01234     RINEX_LINEBUF_SIZE,
01235     &nr_lines
01236     );
01237   if( result == FALSE )
01238     return FALSE;
01239   if( nr_lines != 1 )
01240     return FALSE;
01241   result = RINEX_erase("MARKER NAME", lines_buffer);
01242   if( result == FALSE )
01243     return FALSE;
01244   result = RINEX_trim_left_right(lines_buffer, RINEX_LINEBUF_SIZE, &len );
01245   if( result == FALSE )
01246     return FALSE;
01247   if( len < 64 )
01248   {
01249     strcpy(header->marker_name, lines_buffer);
01250   }
01251   else
01252   {
01253     return FALSE;
01254   }
01255 
01256   result = RINEX_get_header_lines(
01257     header_buffer,
01258     header_buffer_size,
01259     "APPROX POSITION XYZ",
01260     lines_buffer,
01261     RINEX_LINEBUF_SIZE,
01262     &nr_lines
01263     );
01264   if( result == FALSE )
01265     return FALSE;  
01266   if( nr_lines == 1 )
01267   {    
01268     if( sscanf( lines_buffer, "%Lf %Lf %Lf", &(header->x), &(header->y), &(header->z) ) != 3 )
01269       return FALSE;
01270   }
01271 
01272   result = RINEX_get_header_lines(
01273     header_buffer,
01274     header_buffer_size,
01275     "ANTENNA: DELTA H/E/N",
01276     lines_buffer,
01277     RINEX_LINEBUF_SIZE,
01278     &nr_lines
01279     );
01280   if( result == FALSE )
01281     return FALSE;
01282   if( nr_lines != 1 )
01283     return FALSE;
01284   if( sscanf( lines_buffer, "%Lf %Lf %Lf", &(header->antenna_delta_h), &(header->antenna_ecc_e), &(header->antenna_ecc_n) ) != 3 )
01285     return FALSE;
01286 
01287   
01288   result = RINEX_get_header_lines(
01289     header_buffer,
01290     header_buffer_size,
01291     "WAVELENGTH FACT L1/2",
01292     lines_buffer,
01293     RINEX_LINEBUF_SIZE,
01294     &nr_lines
01295     );
01296   if( result == FALSE )
01297     return FALSE;  
01298   if( nr_lines == 1 )
01299   {
01300     // Only default values specified.
01301     if( sscanf( lines_buffer, "%d %d", &(header->default_wavefactor_L1), &(header->default_wavefactor_L2) ) != 2 )
01302       return FALSE;
01303   }
01304   else
01305   {
01306     // First read the default values specified.
01307     if( sscanf( lines_buffer, "%d %d", &(header->default_wavefactor_L1), &(header->default_wavefactor_L2) ) != 2 )
01308       return FALSE;
01309 
01310     //GDM_TODO deal with multiline WAVELENTH FACT L1/2
01311   }
01312 
01313   result = RINEX_GetObservationTypes( header_buffer, header_buffer_size, header );
01314   if( result == FALSE )
01315     return FALSE;
01316 
01317 
01318 
01319   result = RINEX_get_header_lines(
01320     header_buffer,
01321     header_buffer_size,
01322     "INTERVAL",
01323     lines_buffer,
01324     RINEX_LINEBUF_SIZE,
01325     &nr_lines
01326     );
01327   if( result == FALSE )
01328     return FALSE;
01329   if( nr_lines == 1 )
01330   {    
01331     if( sscanf( lines_buffer, "%Lf", &(header->interval) ) != 1 )
01332       return FALSE;
01333 
01334     if( header->interval <= 0 )
01335       header->interval = -1;  // Set to unknown value.
01336 
01337   }
01338   else
01339   {
01340     header->interval = -1; // Set to unknown value.
01341   }
01342 
01343 
01344 
01345   result = RINEX_get_header_lines(
01346     header_buffer,
01347     header_buffer_size,
01348     "TIME OF FIRST OBS",
01349     lines_buffer,
01350     RINEX_LINEBUF_SIZE,
01351     &nr_lines
01352     );
01353   if( result == FALSE )
01354     return FALSE; 
01355 
01356   if( sscanf( lines_buffer, "%d %d %d %d %d %f %s", 
01357     &(itmp[0]),
01358     &(itmp[1]),
01359     &(itmp[2]),
01360     &(itmp[3]),
01361     &(itmp[4]),
01362     &(header->time_of_first_obs.seconds),
01363     time_system_str ) != 7 )
01364   {
01365     return FALSE;
01366   }  
01367   header->time_of_first_obs.year   = (unsigned short)itmp[0];
01368   header->time_of_first_obs.month  = (unsigned char)itmp[1];
01369   header->time_of_first_obs.day    = (unsigned char)itmp[2];
01370   header->time_of_first_obs.hour   = (unsigned char)itmp[3];
01371   header->time_of_first_obs.minute = (unsigned char)itmp[4];
01372    
01373     
01374   result = RINEX_trim_left_right( time_system_str, 128, &len );
01375   if( result == FALSE )
01376     return FALSE; 
01377   if( strcmp( time_system_str, "TIME") == 0  ) // no string present, defaults to GPS
01378     header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_GPS;
01379   else if( strcmp( time_system_str, "GPS" ) == 0 )
01380     header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_GPS;
01381   else if( strcmp( time_system_str, "GLO" ) == 0 )
01382     header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_GLO;
01383   else
01384     header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_UNKNOWN;
01385 
01386   return TRUE;
01387 }
01388 
01389 
01390 
01391 
01392 BOOL RINEX_GetNextObservationSet(
01393   FILE* fid,                               //!< (input) An open (not NULL) file pointer to the RINEX data.
01394   RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
01395   BOOL *wasEndOfFileReached,               //!< Has the end of the file been reached (output).
01396   BOOL *wasObservationFound,               //!< Was a valid observation found (output).
01397   unsigned *filePosition,                  //!< The file position for the start of the message found (output).  
01398   GNSS_structMeasurement* obsArray,        //!< A pointer to a user provided array of GNSS_structMeasurement (input/output).
01399   const unsigned char maxNrObs,            //!< The maximum number of elements in the array provided (input).
01400   unsigned *nrObs,                         //!< The number of valid elements set in the array (output).
01401   unsigned short* rx_gps_week,             //!< The receiver GPS week (0-1024+) [weeks].
01402   double* rx_gps_tow                       //!< The receiver GPS time of week (0-603799.99999) [s].
01403   )
01404 {
01405   char line_buffer[RINEX_LINEBUF_SIZE]; // A character buffer to hold a line from the RINEX file.
01406   size_t length = 0;                // A string length.
01407   RINEX_TIME epoch;                 // The RINEX time.
01408   RINEX_enumEpochFlag epoch_flag;   // A RINEX epoch flag.
01409   char *pch = NULL;                 // A string pointer used in tokenizing a C string.
01410   unsigned count = 0;               // A counter.
01411   int itmp = 0;                     // A temporary integer.
01412   int i = 0;                        // A counter.
01413   int j = 0;                        // A counter.
01414   int obsArray_index = 0;           // A counter.
01415   char numstr[64];                  // A string to hold a number.
01416   BOOL isL1data_present = FALSE;
01417   BOOL isL2data_present = FALSE;
01418   BOOL overwriteCNoL1 = TRUE;
01419   BOOL overwriteCNoL2 = TRUE;
01420   BOOL isEpochValidToDecode = FALSE;
01421   int nr_special_records = 0;
01422   
01423   double tow = 0; // A time of week (0-604399.99999) [s].
01424   unsigned short week = 0; // The GPS week (0-1024+) [weeks].
01425 
01426   RINEX_enumSatelliteSystemType next_sat_type = RINEX_SATELLITE_SYSTEM_UNKNOWN;
01427   
01428 
01429   typedef struct
01430   {
01431     char str[128];
01432     size_t length;
01433     BOOL isValid;
01434   } struct_token;
01435   struct_token token[64]; // An array of string tokens.
01436   unsigned nr_tokens = 0; // The number of valid tokens.
01437 
01438   struct_RINEX_satellite RINEX_sat[RINEX_MAX_NR_SATS];
01439   unsigned RINEX_sat_index = 0;      // The index into RINEX_sat.
01440   unsigned RINEX_nr_satellites = 0;  // The number of valid values in the RINEX_sat array.
01441   
01442   struct_RINEX_obs RINEX_obs[RINEX_MAX_NR_OBS];
01443   unsigned RINEX_obs_index = 0; // The index into RINEX_obs.
01444   unsigned RINEX_nr_obs = 0;    // The number of valid obs in RINEX_obs array.
01445 
01446   BOOL result;                       
01447 
01448   numstr[0] = '\0';
01449   numstr[1] = '\0';
01450   numstr[2] = '\0';
01451   numstr[3] = '\0';
01452 
01453   // Check the input.
01454   if( fid == NULL )
01455     return FALSE;
01456   if( wasEndOfFileReached == NULL )
01457     return FALSE;
01458   if( wasObservationFound == NULL )
01459     return FALSE;
01460   if( filePosition == NULL )
01461     return FALSE;
01462   if( obsArray == NULL )
01463     return FALSE;
01464   if( nrObs == NULL )
01465     return FALSE;
01466   if( RINEX_header->type != RINEX_FILE_TYPE_OBS )
01467     return FALSE;
01468 
01469   *wasObservationFound = FALSE;
01470   *wasEndOfFileReached = FALSE; 
01471 
01472   // Set the token array to zero.
01473   memset( token, 0, sizeof(struct_token)*RINEX_MAX_NR_SATS );
01474 
01475   // Set the RINEX_obs array to zero.
01476   memset( RINEX_obs, 0, sizeof(struct_RINEX_obs)*RINEX_MAX_NR_OBS );
01477   
01478   // The epoch's tme system type is the same as the header's time of first RINEX_obs.
01479   epoch.time_system = RINEX_header->time_of_first_obs.time_system;
01480 
01481   if( epoch.time_system != RINEX_TIME_SYSTEM_GPS )
01482   {
01483     // Not supported for now!
01484     return FALSE;
01485   }
01486 
01487   // Read in the RINEX epoch, epoch flag, and satellite ids string.
01488   do
01489   {
01490     do // advance over empty lines if any
01491     {
01492       if( fgets( line_buffer, RINEX_LINEBUF_SIZE, fid ) == NULL )
01493       {
01494         if( feof(fid) )
01495         {
01496           *wasEndOfFileReached = TRUE;
01497           return TRUE;
01498         }
01499         else
01500         {
01501           return FALSE;
01502         }
01503       }
01504       result = RINEX_trim_left_right( line_buffer, RINEX_LINEBUF_SIZE, &length );
01505       if( result == FALSE )
01506         return FALSE;
01507     }while( length == 0 );
01508   
01509     if( length == 0 )
01510       return FALSE;
01511 
01512     // To make life easier later:
01513     // Search and replace "G ", "R ", "T ", "S " with
01514     //                    "G_", "R_", "T_", "S_"
01515     // These are the satellite ids.
01516     for( i = 0; i < (int)(length-1); i++ )
01517     {
01518       if( line_buffer[i] == 'G' || line_buffer[i] == 'R' || line_buffer[i] == 'T' || line_buffer[i] == 'S' )
01519       {
01520         if( line_buffer[i+1] == ' ' )
01521         {
01522           line_buffer[i+1] = '_';
01523         }
01524       }
01525     }
01526   
01527     // Tokenize the input line buffer.
01528     pch = strtok( line_buffer, " \t\r\n\f" );  
01529     nr_tokens = 0;
01530     while( pch != NULL && count < 64 )
01531     {
01532       token[nr_tokens].length = strlen(pch);
01533       if( token[nr_tokens].length < 128 )
01534       {
01535         strcpy( token[nr_tokens].str, pch );
01536         token[nr_tokens].isValid = TRUE;
01537       }
01538       else
01539       {
01540         return FALSE;
01541       }
01542 
01543       pch = strtok( NULL, " \t\r\n\f" );
01544       nr_tokens++;
01545     }
01546     if( nr_tokens >= 64 )
01547     {
01548       return FALSE;
01549     }
01550 
01551     // For events without significant epoch the epoch fields can be left blank.
01552     // Thus, there are a few cases: 
01553     // (a) No epoch information,  2 integers (an event flag and the nr_special_records to follow 0-999).
01554     // (b) All the epoch information + (a) (year month day hour minute seconds epoch_flag nr_special_records)
01555     // (c) All the epoch information, epoch flag, number of satellites, the satellite list, and optionally the rx clock offset.
01556     if( nr_tokens == 2 )
01557     {
01558       // This can only indicate a epoch flag with a number of special records to follow.
01559       // Read in the epoch flag and the number of special records to follow.
01560       if( sscanf( token[0].str, "%d", &itmp ) != 1 )
01561         return FALSE;
01562       epoch_flag = (RINEX_enumEpochFlag)itmp;        
01563 
01564       if( sscanf( token[1].str, "%d", &nr_special_records ) != 1 )
01565         return FALSE;
01566 
01567       // Deal with special records if any
01568       result = RINEX_DealWithSpecialRecords(
01569         fid,
01570         RINEX_header, 
01571         wasEndOfFileReached,
01572         filePosition,
01573         nr_special_records 
01574         );
01575 
01576       continue;
01577     }
01578     else 
01579     {
01580       // The number of tokens must now contain
01581       // year month day hour minute seconds epoch_flag --other stuff--,
01582       // --other stuff-- depending on the epoch flag
01583       if( nr_tokens < 8 )
01584         return FALSE;  
01585 
01586       // Exract the epoch information from the tokenized line buffer.
01587       i = 0;
01588       if( sscanf( token[i].str, "%d", &itmp ) != 1 ) 
01589         return FALSE; 
01590       epoch.year = (unsigned short)itmp;
01591       i++;
01592       if( sscanf( token[i].str, "%d", &itmp ) != 1 ) 
01593         return FALSE; 
01594       epoch.month = (unsigned char)itmp;
01595       i++;
01596       if( sscanf( token[i].str, "%d", &itmp ) != 1 ) 
01597         return FALSE; 
01598       epoch.day = (unsigned char)itmp;
01599       i++;
01600       if( sscanf( token[i].str, "%d", &itmp ) != 1 ) 
01601         return FALSE; 
01602       epoch.hour = (unsigned char)itmp;
01603       i++;
01604       if( sscanf( token[i].str, "%d", &itmp ) != 1 ) 
01605         return FALSE; 
01606       epoch.minute = (unsigned char)itmp;
01607       i++;
01608       if( sscanf( token[i].str, "%f", &(epoch.seconds) ) != 1 ) 
01609         return FALSE; 
01610       i++;
01611       if( sscanf( token[i].str, "%d", &(itmp) ) != 1 ) 
01612         return FALSE; 
01613       i++;
01614       epoch_flag = (RINEX_enumEpochFlag)itmp;
01615 
01616       if( epoch_flag == 6 )
01617       {
01618         isEpochValidToDecode = TRUE;        
01619         // Cycle slip correction observations to follow.
01620         // This is dealt with later below.
01621       }
01622       else if( epoch_flag > 1 )
01623       {
01624         if( sscanf( token[i].str, "%d", &nr_special_records ) != 1 )
01625           return FALSE;       
01626 
01627         // Deal with special records if any
01628         result = RINEX_DealWithSpecialRecords(
01629           fid,
01630           RINEX_header, 
01631           wasEndOfFileReached,
01632           filePosition,
01633           nr_special_records 
01634           );
01635         continue;
01636       }
01637       else
01638       {
01639         isEpochValidToDecode = TRUE;
01640       }
01641     }
01642 
01643   }while( !isEpochValidToDecode );
01644 
01645   if( token[7].length == 0 )
01646     return FALSE;
01647   
01648   // The eighth token contains the number of satellites and there id's
01649   for( i = 0; i < (int)token[7].length; i++ )
01650   {
01651     // Satellite can be denoted by the following letters (RINEX_v_ 2.1)
01652     // 'G': GPS
01653     // 'R': GLONASS
01654     // 'S': Geostationary signal payload
01655     // 'T': NNSS Transit
01656     //
01657     // e.g. string here is 5G_8G12G13R_8S20 means 5 satellite observations, 
01658     // with GPS PRN's 8, 12, 14, GLONASS id 7, and SBAS id 20 (by the way: making up the id's here).
01659     if( token[7].str[i] == '_' )
01660     {
01661       continue;
01662     }  
01663     if( token[7].str[i] == '-' || token[7].str[i] == '+' || token[7].str[i] == '.' || token[7].str[i] == 'E' || token[7].str[i] == 'e' )
01664     {
01665       // Any float numbers should not be present on this line.
01666       return FALSE;
01667     }
01668 
01669     if( isdigit( token[7].str[i] ) )
01670     {
01671       numstr[j] = token[7].str[i];
01672       j++;
01673     }
01674     else
01675     {
01676       if( token[7].str[i] != RINEX_SATELLITE_SYSTEM_GPS && 
01677         token[7].str[i] != RINEX_SATELLITE_SYSTEM_GLO && 
01678         token[7].str[i] != RINEX_SATELLITE_SYSTEM_GEO && 
01679         token[7].str[i] != RINEX_SATELLITE_SYSTEM_NSS )
01680       {
01681         return FALSE;
01682       }
01683 
01684       numstr[j] = '\0';
01685       j = 0;
01686       // A number always precedes a non-number here. Decode the number.
01687       if( sscanf( numstr, "%d", &itmp ) != 1 )
01688         return FALSE;
01689       if( count == 0 )
01690       {
01691         // This is the number of observations        
01692         RINEX_nr_satellites = itmp;
01693         if( RINEX_nr_satellites >= 64 )
01694         {
01695           return FALSE; // a very unlikely error condition.
01696         }
01697       }
01698       else
01699       {
01700         // This is a satellite id of type (current value of) next_sat_type.
01701         RINEX_sat[count-1].id = (unsigned short)itmp;
01702         RINEX_sat[count-1].type = next_sat_type;
01703       }
01704       count++;
01705       if( count > RINEX_nr_satellites+1 )
01706       {
01707         return FALSE; // a very unlikely error condition.
01708       }
01709       next_sat_type = (RINEX_enumSatelliteSystemType)token[7].str[i];
01710     }
01711   }
01712   if( count == 0 )
01713   {
01714     return FALSE;
01715   }
01716 
01717   // The last satellite id must still be interpreted.
01718   numstr[j] = '\0';
01719   j = 0;
01720   if( sscanf( numstr, "%d", &itmp ) != 1 )
01721     return FALSE;
01722   RINEX_sat[count-1].id = (unsigned short)itmp;
01723   RINEX_sat[count-1].type = next_sat_type;
01724   count++;
01725 
01726   if( count > RINEX_nr_satellites+1 )
01727   {
01728     // Error in number of satellite ids read compared to RINEX_nr_obs.
01729     return FALSE;
01730   }
01731 
01732   if( count != RINEX_nr_satellites+1 )
01733   {
01734     if( RINEX_nr_obs <= 12 )
01735     {
01736       return FALSE;
01737     }
01738     else
01739     {
01740       // Get the next line from the file.
01741       if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
01742       {
01743         if( feof(fid) )
01744         {
01745           *wasEndOfFileReached = TRUE;
01746           return TRUE;
01747         }
01748         else
01749         {
01750           return FALSE;
01751         }
01752       }
01753 
01754       length = strlen(line_buffer);
01755       if( length == 0 )
01756         return FALSE;
01757 
01758       if( RINEX_trim_left_right( line_buffer, RINEX_LINEBUF_SIZE, &length ) == FALSE )
01759         return FALSE;
01760 
01761       for( i = 0; i < (int)length; i++ )
01762       {
01763         if( isspace(line_buffer[i]) )
01764         {
01765           continue;
01766         }  
01767         if( line_buffer[i] == '-' || line_buffer[i] == '+' || line_buffer[i] == '.' || line_buffer[i] == 'E' || line_buffer[i] == 'e' )
01768         {
01769           // Any float numbers should not be present on this line.
01770           return FALSE;
01771         }
01772 
01773         if( !isdigit( line_buffer[i] ) ) // In this case the satellite system type letter is first.
01774         {
01775           if( line_buffer[i] != RINEX_SATELLITE_SYSTEM_GPS && 
01776             line_buffer[i] != RINEX_SATELLITE_SYSTEM_GLO && 
01777             line_buffer[i] != RINEX_SATELLITE_SYSTEM_GEO && 
01778             line_buffer[i] != RINEX_SATELLITE_SYSTEM_NSS &&
01779             line_buffer[i] != RINEX_SATELLITE_SYSTEM_MIXED )
01780           {
01781             return FALSE;
01782           }
01783 
01784           if( j != 0 )
01785           {
01786             numstr[j] = '\0';
01787             j = 0;
01788             if( sscanf( numstr, "%d", &itmp ) != 1 )
01789             {
01790               return FALSE;
01791             }
01792             RINEX_sat[count-1].id = (unsigned short)itmp;
01793             RINEX_sat[count-1].type = next_sat_type;
01794 
01795             count++;
01796             if( count > RINEX_nr_satellites+1 )
01797             {
01798               return FALSE; // a very unlikely error condition.
01799             }
01800           }          
01801           next_sat_type = (RINEX_enumSatelliteSystemType)line_buffer[i];
01802         }
01803         else
01804         {
01805           numstr[j] = line_buffer[i];
01806           j++;
01807         }
01808       }
01809     }
01810     // The last satellite id must still be interpreted.
01811     numstr[j] = '\0';
01812     j = 0;
01813     if( sscanf( numstr, "%d", &itmp ) != 1 )
01814     {
01815       return FALSE;
01816     }
01817     RINEX_sat[count-1].id = (unsigned short)itmp;
01818     RINEX_sat[count-1].type = next_sat_type;
01819     count++;
01820 
01821     if( count != RINEX_nr_satellites+1 )
01822     {
01823       return FALSE;
01824     }
01825   }
01826 
01827   if( RINEX_header->nr_obs_types >= 64 )    
01828     return FALSE; // A very unlikely condition.
01829 
01830 
01831 
01832   // TIME: The time of the measurement is the receiver time of the received signals.
01833   // It is identical for the phase and range measurements and is identical for
01834   // all satellites observed at that epoch. It is expressed in GPS time (not
01835   // Universal Time). 
01836   //
01837   // It is stored in UTC style (year, month, day, etc) BUT is receiver time.
01838 
01839   if( epoch.year >= 80 && epoch.year < 2000 )
01840   {
01841     epoch.year += 1900;
01842   }
01843   else if( epoch.year >= 0 && epoch.year < 79 )
01844   {
01845     epoch.year += 2000;
01846   }
01847   else
01848   {
01849     return FALSE;
01850   }
01851   TIMECONV_GetGPSTimeFromRinexTime(
01852     epoch.year,
01853     epoch.month,
01854     epoch.day,
01855     epoch.hour,
01856     epoch.minute,
01857     epoch.seconds,
01858     &week,
01859     &tow 
01860     );
01861 
01862   // Set the receiver time values.
01863   *rx_gps_week = week;
01864   *rx_gps_tow = tow;
01865   
01866 
01867   obsArray_index = 0;
01868   for( RINEX_sat_index = 0; RINEX_sat_index < (int)RINEX_nr_satellites; RINEX_sat_index++ )
01869   {
01870     isL1data_present = FALSE;
01871     isL2data_present = FALSE;
01872     overwriteCNoL1 = TRUE;
01873     overwriteCNoL2 = TRUE;  
01874 
01875     // Set measurement data default to 0.
01876     memset( &(obsArray[obsArray_index]), 0, sizeof(GNSS_structMeasurement) );
01877 
01878     result = RINEX_GetNextObserationSetForOneSatellite(
01879       fid,
01880       RINEX_header,
01881       wasEndOfFileReached,
01882       filePosition,      
01883       RINEX_obs,
01884       RINEX_MAX_NR_OBS,
01885       &RINEX_nr_obs,
01886       RINEX_sat[RINEX_sat_index].type,
01887       RINEX_sat[RINEX_sat_index].id
01888       );
01889     if( result == FALSE )
01890       return FALSE;
01891 
01892     if( epoch_flag == 6 ) 
01893     {
01894       // Cycle slip style observations are present.
01895 
01896       // GDM - ignore for now.
01897       continue;
01898     }
01899 
01900     // Set the time.
01901     obsArray[obsArray_index].tow  =  tow;
01902     obsArray[obsArray_index].week = week;
01903 
01904     // The channel index is simply the order of the data in this case.
01905     obsArray[obsArray_index].channel = (unsigned short)obsArray_index;
01906 
01907     obsArray[obsArray_index].id = RINEX_sat[RINEX_sat_index].id;
01908 
01909     // Set default validity flags.
01910     obsArray[obsArray_index].flags.isEphemerisValid        = 0; // not yet known
01911     obsArray[obsArray_index].flags.isAlmanacValid          = 0; // not yet known
01912     obsArray[obsArray_index].flags.isAboveElevationMask    = 0; // not yet known
01913     obsArray[obsArray_index].flags.isAboveCNoMask          = 0; // not yet known
01914     obsArray[obsArray_index].flags.isAboveLockTimeMask     = 0; // not yet known
01915     obsArray[obsArray_index].flags.isNotUserRejected       = 1; // assume not rejected
01916     obsArray[obsArray_index].flags.isNotPsrRejected        = 1; // assume not rejected
01917     obsArray[obsArray_index].flags.isNotAdrRejected        = 1; // assume not rejected
01918     obsArray[obsArray_index].flags.isNotDopplerRejected    = 1; // assume not rejected
01919     obsArray[obsArray_index].flags.isNoCycleSlipDetected   = 1; // assume no slip
01920     obsArray[obsArray_index].flags.isPsrUsedInSolution     = 0; // not yet known
01921     obsArray[obsArray_index].flags.isDopplerUsedInSolution = 0; // not yet known
01922     obsArray[obsArray_index].flags.isAdrUsedInSolution     = 0; // not yet known
01923     obsArray[obsArray_index].flags.useTropoCorrection          = 1; // default to yes
01924     obsArray[obsArray_index].flags.useBroadcastIonoCorrection  = 1; // default to yes
01925 
01926 
01927 
01928     // The GNSS observation array is channel based. 
01929     // We must look for matching observation sets to place within the channel based container.
01930     // e.g. L1, P1, C1, D1 and S1
01931     // first look for L1, P1, C1, D1 and S1
01932     
01933     // Deal with S1 measurements first, so that if present, the RINEX signal strength values
01934     // are not interpretted.
01935     for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
01936     {
01937       if( RINEX_obs[RINEX_obs_index].type == RINEX_OBS_TYPE_S1 && RINEX_obs[RINEX_obs_index].isValid )
01938       {
01939         obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
01940         obsArray[obsArray_index].freqType = GNSS_GPSL1; 
01941         
01942         overwriteCNoL1 = FALSE;
01943 
01944         // GDM_TODO - A receiver dependant look up table is needed here to convert to 
01945         // Carrier to noise density ratio values in dB-Hz.
01946         obsArray[obsArray_index].cno = (float)RINEX_obs[RINEX_obs_index].value; // [receiver dependant!]
01947 
01948         isL1data_present = TRUE;
01949       }
01950     }
01951 
01952     for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
01953     {
01954       if( !RINEX_obs[RINEX_obs_index].isValid )
01955         continue;
01956 
01957       switch(RINEX_obs[RINEX_obs_index].type)
01958       {
01959       case RINEX_OBS_TYPE_L1:
01960         {
01961           obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
01962           obsArray[obsArray_index].freqType = GNSS_GPSL1; 
01963 
01964           obsArray[obsArray_index].adr = RINEX_obs[RINEX_obs_index].value; // cycles
01965 
01966           // Set the validity flags
01967           obsArray[obsArray_index].flags.isActive       = TRUE;
01968           obsArray[obsArray_index].flags.isCodeLocked   = TRUE;
01969           obsArray[obsArray_index].flags.isPhaseLocked  = TRUE;
01970           obsArray[obsArray_index].flags.isParityValid  = TRUE; // Assume valid. No half cycle slips (invalid parity changes to valid partiy causes 1/2 cycle jump).
01971           obsArray[obsArray_index].flags.isAdrValid     = TRUE;
01972           obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
01973           obsArray[obsArray_index].flags.isNoCycleSlipDetected = TRUE;
01974 
01975           isL1data_present = TRUE;
01976 
01977           // Loss of lock indicator really pertains to the phase only.
01978           switch( RINEX_obs[RINEX_obs_index].loss_of_lock_indicator )
01979           {
01980           case 0:
01981             {
01982               // OK 
01983               break;
01984             }
01985           case 1: // Loss lock between previous and current observation: cycle clip possible.
01986             {
01987               // Assume cycle slip took place.
01988               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
01989               break;
01990             }
01991           case 2:
01992             {
01993               // Opposite wavelength factor to the
01994               // one defined for the satellite by a 
01995               // previous WAVELENGTH FACT L1/2 line.
01996               // Valid for the current epoch only.  
01997               // GDM_TODO parity failure here?
01998               break;
01999             }
02000           case 3:
02001             { 
02002               // both 2 and 1
02003               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
02004               // GDM_TODO 
02005               break;
02006             }
02007           case 5:
02008             {
02009               // both 4 and 1
02010               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
02011               // Intentional fall thru.
02012             }
02013           case 4:
02014             { 
02015               // Observation under Antispoofing (may suffer from increased noise).
02016               // GDM_TODO 
02017               break;
02018             }
02019           case 6:
02020             {
02021               // both 4 and 2
02022               // GDM_TODO 
02023               break;
02024             }
02025           case 7:
02026             {
02027               // both 4, 2 and 1
02028               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
02029               // GDM_TODO 
02030               break;
02031             }
02032           default:
02033             {
02034               // could be 'blank' or whitespace like '\r' or '\n'
02035               // ignore
02036               break;
02037             }
02038           }
02039 
02040           if( overwriteCNoL1 )
02041           {
02042             result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
02043             if( result == FALSE )
02044               return FALSE;
02045           }
02046 
02047           break;  
02048         }
02049       case RINEX_OBS_TYPE_C1:
02050         {
02051           obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
02052           obsArray[obsArray_index].freqType = GNSS_GPSL1; 
02053           obsArray[obsArray_index].codeType = GNSS_CACode; 
02054 
02055           obsArray[obsArray_index].psr = RINEX_obs[RINEX_obs_index].value; // m
02056 
02057           // The observation time convention is 'transmit' time.
02058           obsArray[obsArray_index].tow  =  tow - obsArray[obsArray_index].psr/LIGHTSPEED;
02059 
02060           // Set the validity flags
02061           obsArray[obsArray_index].flags.isActive       = TRUE;
02062           obsArray[obsArray_index].flags.isCodeLocked   = TRUE;
02063           obsArray[obsArray_index].flags.isPsrValid     = TRUE;
02064           obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
02065           isL1data_present = TRUE;
02066 
02067           if( overwriteCNoL1 )
02068           {
02069             result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
02070             if( result == FALSE )
02071               return FALSE;
02072           }
02073 
02074           break;
02075         }
02076       case RINEX_OBS_TYPE_P1:
02077         {
02078           obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
02079           obsArray[obsArray_index].freqType = GNSS_GPSL1; 
02080           obsArray[obsArray_index].codeType = GNSS_PCode; 
02081 
02082           obsArray[obsArray_index].psr = RINEX_obs[RINEX_obs_index].value; // m
02083 
02084           
02085           // The observation time convention is 'tranmsit' time.
02086           obsArray[obsArray_index].tow  =  tow - obsArray[obsArray_index].psr/LIGHTSPEED;
02087 
02088           // Set the validity flags
02089           obsArray[obsArray_index].flags.isActive       = TRUE;
02090           obsArray[obsArray_index].flags.isCodeLocked   = TRUE;
02091           obsArray[obsArray_index].flags.isPsrValid     = TRUE;
02092           obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
02093           isL1data_present = TRUE;
02094 
02095           if( overwriteCNoL1 )
02096           {
02097             result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
02098             if( result == FALSE )
02099               return FALSE;
02100           }
02101 
02102           break;
02103         }
02104       case RINEX_OBS_TYPE_D1:
02105         {
02106           obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
02107           obsArray[obsArray_index].freqType = GNSS_GPSL1; 
02108           obsArray[obsArray_index].doppler = (float)RINEX_obs[RINEX_obs_index].value; // m
02109 
02110           // Set the validity flags
02111           obsArray[obsArray_index].flags.isDopplerValid = TRUE;
02112           isL1data_present = TRUE;
02113           break;
02114         }
02115       default:
02116         {
02117           break;
02118         }
02119       }
02120     }
02121     if( isL1data_present )
02122     {
02123       // Check if no information about cno is present for L1.
02124       if( obsArray[obsArray_index].cno == 0.0 )
02125       {
02126         obsArray[obsArray_index].cno = 32; // A nominally low but useable value [dB-Hz].
02127       }
02128 
02129       obsArray_index++;
02130       if( obsArray_index >= maxNrObs )
02131         return FALSE;
02132       
02133       // Set measurement data default to 0.
02134       memset( &(obsArray[obsArray_index]), 0, sizeof(GNSS_structMeasurement) );
02135 
02136       // Set the time.
02137       obsArray[obsArray_index].tow  =  tow;
02138       obsArray[obsArray_index].week = week;
02139 
02140       obsArray[obsArray_index].id = RINEX_sat[RINEX_sat_index].id;
02141 
02142       // The channel index is simply the order of the data in this case.
02143       obsArray[obsArray_index].channel = (unsigned short)obsArray_index;
02144 
02145       // Set default validity flags.
02146       obsArray[obsArray_index].flags.isEphemerisValid        = 0; // not yet known
02147       obsArray[obsArray_index].flags.isAlmanacValid          = 0; // not yet known
02148       obsArray[obsArray_index].flags.isAboveElevationMask    = 0; // not yet known
02149       obsArray[obsArray_index].flags.isAboveCNoMask          = 0; // not yet known
02150       obsArray[obsArray_index].flags.isAboveLockTimeMask     = 0; // not yet known
02151       obsArray[obsArray_index].flags.isNotUserRejected       = 1; // assume not rejected
02152       obsArray[obsArray_index].flags.isNotPsrRejected        = 1; // assume not rejected
02153       obsArray[obsArray_index].flags.isNotAdrRejected        = 1; // assume not rejected
02154       obsArray[obsArray_index].flags.isNotDopplerRejected    = 1; // assume not rejected
02155       obsArray[obsArray_index].flags.isNoCycleSlipDetected   = 1; // assume no slip
02156       obsArray[obsArray_index].flags.isPsrUsedInSolution     = 0; // not yet known
02157       obsArray[obsArray_index].flags.isDopplerUsedInSolution = 0; // not yet known
02158       obsArray[obsArray_index].flags.isAdrUsedInSolution     = 0; // not yet known
02159       obsArray[obsArray_index].flags.useTropoCorrection      = 1; // default to yes
02160       obsArray[obsArray_index].flags.useBroadcastIonoCorrection  = 1; // default to yes
02161     }
02162 
02163     
02164     // Deal with S2 measurements first, so that if present, the RINEX signal strength values
02165     // are not interpretted.
02166     for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
02167     {
02168       if( RINEX_obs[RINEX_obs_index].type == RINEX_OBS_TYPE_S2 && RINEX_obs[RINEX_obs_index].isValid )
02169       {
02170         obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
02171         obsArray[obsArray_index].freqType = GNSS_GPSL1; 
02172         obsArray[obsArray_index].id       = RINEX_obs[RINEX_obs_index].id;
02173 
02174         overwriteCNoL2 = FALSE;
02175 
02176         // GDM_TODO - A receiver dependant look up table is needed here to convert to 
02177         // Carrier to noise density ratio values in dB-Hz.
02178         obsArray[obsArray_index].cno = (float)RINEX_obs[RINEX_obs_index].value; // [receiver dependant!]
02179 
02180         isL2data_present = TRUE;
02181       }
02182     }
02183 
02184 
02185     // Look for L2, P2, D2 and S2
02186     for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
02187     {
02188       if( !RINEX_obs[RINEX_obs_index].isValid )
02189         continue;
02190 
02191       switch(RINEX_obs[RINEX_obs_index].type)
02192       {
02193       case RINEX_OBS_TYPE_L2:
02194         {
02195           obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
02196           obsArray[obsArray_index].freqType = GNSS_GPSL2; 
02197           obsArray[obsArray_index].adr = RINEX_obs[RINEX_obs_index].value; // cycles
02198 
02199           // Set the validity flags
02200           obsArray[obsArray_index].flags.isActive       = TRUE;
02201           obsArray[obsArray_index].flags.isCodeLocked   = TRUE;
02202           obsArray[obsArray_index].flags.isPhaseLocked  = TRUE;
02203           obsArray[obsArray_index].flags.isParityValid  = TRUE; // Assume valid. No half cycle slips (invalid parity changes to valid partiy causes 1/2 cycle jump).
02204           obsArray[obsArray_index].flags.isAdrValid     = TRUE;
02205           obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
02206           obsArray[obsArray_index].flags.isNoCycleSlipDetected = TRUE;
02207           isL2data_present = TRUE;
02208 
02209           // Loss of lock indicator really pertains to the phase only.
02210           switch( RINEX_obs[RINEX_obs_index].loss_of_lock_indicator )
02211           {
02212           case 0: 
02213             {
02214               break; // OK 
02215             }
02216           case 1: // Loss lock between previous and current observation: cycle clip possible.
02217             {
02218               // Assume cycle slip took place.
02219               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
02220               break;
02221             }
02222           case 2:
02223             {
02224               // Opposite wavelength factor to the
02225               // one defined for the satellite by a 
02226               // previous WAVELENGTH FACT L1/2 line.
02227               // Valid for the current epoch only.  
02228               // GDM_TODO parity failure here?
02229               break;
02230             }
02231           case 3:
02232             { 
02233               // both 2 and 1
02234               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
02235               // GDM_TODO 
02236               break;
02237             }
02238           case 5:
02239             {
02240               // both 4 and 1
02241               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
02242               // Intentional fall thru.
02243             }
02244           case 4:
02245             { 
02246               // Observation under Antispoofing (may suffer from increased noise).
02247               // GDM_TODO 
02248               break;
02249             }
02250           case 6:
02251             {
02252               // both 4 and 2
02253               // GDM_TODO 
02254               break;
02255             }
02256           case 7:
02257             {
02258               // both 4, 2 and 1
02259               obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
02260               // GDM_TODO 
02261               break;
02262             }
02263           default:
02264             {
02265               // could be 'blank' or whitespace like '\r' or '\n'
02266               // ignore
02267               break;
02268             }
02269           }
02270 
02271           if( overwriteCNoL2 )
02272           {
02273             result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
02274             if( result == FALSE )
02275               return FALSE;
02276           }
02277 
02278           break;  
02279         }
02280       case RINEX_OBS_TYPE_P2:
02281         {
02282           obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
02283           obsArray[obsArray_index].freqType = GNSS_GPSL2; 
02284           obsArray[obsArray_index].codeType = GNSS_PCode; 
02285 
02286           obsArray[obsArray_index].psr = RINEX_obs[RINEX_obs_index].value; // m
02287 
02288           // The observation time convention is 'tranmsit' time.
02289           obsArray[obsArray_index].tow  =  tow - obsArray[obsArray_index].psr/LIGHTSPEED;
02290 
02291           // Set the validity flags
02292           obsArray[obsArray_index].flags.isActive       = TRUE;
02293           obsArray[obsArray_index].flags.isCodeLocked   = TRUE;
02294           obsArray[obsArray_index].flags.isPsrValid     = TRUE;
02295           obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
02296           isL2data_present = TRUE;
02297 
02298           if( overwriteCNoL2 )
02299           {
02300             result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
02301             if( result == FALSE )
02302               return FALSE;
02303           }
02304 
02305           break;
02306         }
02307       case RINEX_OBS_TYPE_D2:
02308         {
02309           obsArray[obsArray_index].system   = RINEX_obs[RINEX_obs_index].system;
02310           obsArray[obsArray_index].freqType = GNSS_GPSL2; 
02311           obsArray[obsArray_index].doppler = (float)RINEX_obs[RINEX_obs_index].value; // m
02312 
02313           // Set the validity flags
02314           obsArray[obsArray_index].flags.isDopplerValid = TRUE;
02315           isL2data_present = TRUE;
02316           break;
02317         }
02318       default:
02319         {
02320           break;
02321         }
02322       }
02323     }
02324 
02325     if( isL2data_present )
02326     {
02327       // Check if no information about cno is present for L2.
02328       if( obsArray[obsArray_index].cno == 0.0 )
02329       {
02330         obsArray[obsArray_index].cno = 32; // A nominally low but useable value [dB-Hz].
02331       }
02332 
02333       obsArray_index++;
02334       if( obsArray_index >= maxNrObs )
02335         return FALSE;
02336     }
02337 
02338     // Note that T1 and T2 measurements are not supported.
02339   }
02340 
02341   *nrObs = obsArray_index;
02342   *wasObservationFound = TRUE;
02343 
02344   return TRUE;
02345 }
02346 
02347 
02348 BOOL RINEX_DecodeGPSNavigationFile(
02349   const char *filepath,                          //!< (input) The file path to the GPS Navigation message file.
02350   GNSS_structKlobuchar *iono_model,              //!< (input/output) A pointer to the ionospheric parameters struct.
02351   GPS_structEphemeris *ephemeris_array,          //!< (input/output) A pointer to the GPS ephemeris array.
02352   const unsigned int max_length_ephemeris_array, //!< (input) The maximum size of the GPS ephemeris array.
02353   unsigned int *length_ephemeris_array           //!< (input/output) The length of the GPS ephemeris array after decoding. The number of valid items.
02354   )
02355 {
02356   char RINEX_header[RINEX_HEADER_SIZE];
02357   unsigned RINEX_header_length = 0;
02358   double version = 0.0;
02359   RINEX_enumFileType file_type = RINEX_FILE_TYPE_UNKNOWN;
02360   char line_buffer[RINEX_LINEBUF_SIZE];
02361   unsigned nr_lines = 0;
02362   BOOL result;
02363   FILE* fid = NULL;
02364   GPS_structEphemeris eph; // A single ephemeris record.
02365   RINEX_TIME epoch;
02366   unsigned i = 0;
02367   unsigned count = 0;
02368   double tow = 0;
02369   int itmp = 0;
02370   int itmp2 = 0;
02371   double dtmp = 0.0;
02372   unsigned short week = 0;
02373   unsigned ephemeris_array_index = 0;
02374   size_t length = 0;
02375 
02376   char station_name[5];
02377   unsigned short dayofyear = 0;
02378   unsigned char file_sequence_nr = 0;
02379   unsigned short year = 0;
02380 
02381   double header_A0; // DELTA-UTC: A0,A1,T,W
02382   double header_A1; // DELTA-UTC: A0,A1,T,W
02383   int header_week;  // DELTA-UTC: A0,A1,T,W
02384   int header_tow;   // DELTA-UTC: A0,A1,T,W
02385   
02386   char str[10][20]; // A 2D array of strings of length 20;
02387 
02388   memset( &eph, 0, sizeof(GPS_structEphemeris) );
02389 
02390   epoch.time_system = RINEX_TIME_SYSTEM_GPS;
02391   
02392   if( filepath == NULL )
02393     return FALSE;
02394   if( iono_model == NULL )
02395     return FALSE;
02396   if( ephemeris_array == NULL )
02397     return FALSE;
02398   if( length_ephemeris_array == NULL )
02399     return FALSE;
02400   if( max_length_ephemeris_array == 0 )
02401     return FALSE;
02402 
02403   iono_model->isValid = FALSE;
02404 
02405   result = RINEX_GetHeader( 
02406     filepath,
02407     RINEX_header,
02408     RINEX_HEADER_SIZE,
02409     &RINEX_header_length,
02410     &version,
02411     &file_type
02412     );
02413   if( result == FALSE )
02414     return FALSE;
02415 
02416   if( file_type != RINEX_FILE_TYPE_GPS_NAV )
02417     return FALSE;
02418 
02419   if( fabs( version - 2.1 ) < 1e-06 || 
02420     fabs( version - 2.0 ) < 1e-06 ||
02421     fabs( version - 2.11 ) < 1e-06 )
02422   {
02423     // this version is valid for decoding
02424   }
02425   else
02426   {    
02427     return FALSE;
02428   }
02429 
02430   result = RINEX_get_header_lines(
02431     RINEX_header,
02432     RINEX_header_length,
02433     "ION ALPHA",
02434     line_buffer,
02435     RINEX_LINEBUF_SIZE,
02436     &nr_lines
02437     );
02438   if( result == FALSE )
02439     return FALSE;
02440   if( nr_lines == 1 )
02441   {
02442     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02443     if( result == FALSE )
02444       return FALSE;
02445 
02446     if( sscanf( line_buffer, "%Lf %Lf %Lf %Lf", 
02447       &(iono_model->alpha0), 
02448       &(iono_model->alpha1),
02449       &(iono_model->alpha2),
02450       &(iono_model->alpha3) ) != 4 )
02451     {
02452       return FALSE; // bad header?
02453     }
02454     result = RINEX_get_header_lines(
02455       RINEX_header,
02456       RINEX_header_length,
02457       "ION BETA",
02458       line_buffer,
02459       RINEX_LINEBUF_SIZE,
02460       &nr_lines
02461       );
02462     if( result == FALSE )
02463       return FALSE;
02464     if( nr_lines != 1 )
02465       return FALSE; // weird header
02466 
02467     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02468     if( result == FALSE )
02469       return FALSE;
02470 
02471     if( sscanf( line_buffer, "%Lf %Lf %Lf %Lf", 
02472       &(iono_model->beta0), 
02473       &(iono_model->beta1),
02474       &(iono_model->beta2),
02475       &(iono_model->beta3) ) != 4 )
02476     {
02477       return FALSE; // bad header?
02478     }
02479 
02480     result = RINEX_get_header_lines(
02481       RINEX_header,
02482       RINEX_header_length,
02483       "DELTA-UTC: A0,A1,T,W",
02484       line_buffer,
02485       RINEX_LINEBUF_SIZE,
02486       &nr_lines
02487       );
02488     if( result == FALSE )
02489       return FALSE;
02490     if( nr_lines == 1 )
02491     {
02492       result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02493       if( result == FALSE )
02494         return FALSE;
02495 
02496       if( sscanf( line_buffer, "%Lf %Lf %d %d",
02497         &header_A0,
02498         &header_A1,
02499         &header_tow,
02500         &header_week ) == 4 )
02501       {
02502         iono_model->week = (unsigned short) header_week;
02503         iono_model->tow = header_tow;
02504         iono_model->isValid = TRUE;
02505       }
02506     }
02507 
02508     if( iono_model->isValid == FALSE )
02509     {
02510       // Decode the year and day of year from the file name.
02511       result = RINEX_DecodeFileName( filepath, station_name, &dayofyear, &file_sequence_nr, &year, &file_type );
02512       if( result == TRUE )
02513       {
02514         result = TIMECONV_GetGPSTimeFromYearAndDayOfYear(
02515           year,
02516           dayofyear,
02517           &(iono_model->week),
02518           &dtmp
02519           );
02520         if( result == FALSE )
02521           return FALSE;
02522         iono_model->tow = (unsigned)dtmp;
02523         iono_model->isValid = TRUE;
02524       }
02525       else
02526       {
02527         // Get the Iono model time from the first ephemeris record in the file if there is one.
02528         iono_model->isValid = FALSE;
02529       }
02530     }
02531   }
02532 
02533   fid = fopen( filepath, "r" );
02534   if( fid == NULL )
02535     return FALSE;
02536 
02537   do
02538   {
02539     // Get the next line from the file.
02540     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02541     {
02542       if( feof(fid) )
02543       {      
02544         return TRUE;
02545       }
02546       else
02547       {
02548         return FALSE;
02549       }
02550     }
02551   }
02552   while( strstr( line_buffer, "END OF HEADER" ) == NULL );
02553 
02554 
02555   while( !feof(fid) && !ferror(fid) && ephemeris_array_index < max_length_ephemeris_array )
02556   {
02557     // Get the next line from the file.
02558     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02559     {
02560       if( feof(fid) )
02561       {      
02562         break;
02563       }
02564       else
02565       {
02566         return FALSE;
02567       }
02568     }
02569     // Check that this line is now empty.
02570     length = strlen(line_buffer);
02571     for( i = 0; i < length; i++ )
02572     {
02573       if( isalnum(line_buffer[i]) )
02574         break;
02575     }
02576     if( i == length )
02577       continue; // This string is empty.
02578 
02579     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)length );
02580     if( result == FALSE )
02581       return FALSE;
02582 
02583     memset( str, 0, sizeof(char)*10*20 );  
02584     count = sscanf( line_buffer, "%2c%*c%2c%*c%2c%*c%2c%*c%2c%*c%2c%5c%19c%19c%19c",
02585       str[0],
02586       str[1],
02587       str[2],
02588       str[3],
02589       str[4],
02590       str[5],
02591       str[6],
02592       str[7],
02593       str[8],
02594       str[9]
02595       );
02596 
02597     if( count != 10 )
02598       return FALSE; // bad record
02599 
02600     i = 0;
02601     if( sscanf( str[i], "%d", &itmp ) != 1 )
02602       return FALSE;
02603     eph.prn = (unsigned short)itmp;
02604     i++;
02605     if( sscanf( str[i], "%d", &itmp  ) != 1 )
02606       return FALSE;
02607     epoch.year = (unsigned short)itmp;
02608     i++;
02609     if( sscanf( str[i], "%d", &itmp  ) != 1 )
02610       return FALSE;
02611     epoch.month = (unsigned char)itmp;
02612     i++;
02613     if( sscanf( str[i], "%d", &itmp  ) != 1 )
02614       return FALSE;
02615     epoch.day = (unsigned char)itmp;
02616     i++;
02617     if( sscanf( str[i], "%d", &itmp  ) != 1 )
02618       return FALSE;
02619     epoch.hour = (unsigned char)itmp;
02620     i++;
02621     if( sscanf( str[i], "%d", &epoch.minute ) != 1 )
02622       return FALSE;
02623     epoch.minute = (unsigned char)itmp;
02624     i++;
02625     if( sscanf( str[i], "%f", &epoch.seconds ) != 1 )
02626       return FALSE;
02627     i++;
02628     if( sscanf( str[i], "%Lf", &eph.af0 ) != 1 )
02629       return FALSE;
02630     i++;
02631     if( sscanf( str[i], "%Lf", &eph.af1 ) != 1 )
02632       return FALSE;
02633     i++;
02634     if( sscanf( str[i], "%Lf", &eph.af2 ) != 1 )
02635       return FALSE;
02636 
02637     if( epoch.year >= 80 && epoch.year < 2000 )
02638     {
02639       epoch.year += 1900;
02640     }
02641     else if( epoch.year >= 0 && epoch.year < 79 )
02642     {
02643       epoch.year += 2000;
02644     }
02645     else
02646     {
02647       return FALSE;
02648     }
02649     result = TIMECONV_GetGPSTimeFromRinexTime(
02650       epoch.year,
02651       epoch.month,
02652       epoch.day,
02653       epoch.hour,
02654       epoch.minute,
02655       epoch.seconds,
02656       &week,
02657       &tow 
02658       );
02659     if( result == FALSE )
02660       return FALSE;
02661     eph.toc = (unsigned)tow;
02662 
02663 
02664     // -------------------
02665     // BROADCAST ORBIT - 1
02666 
02667     // Get the next line from the file.
02668     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02669     {
02670       if( feof(fid) )
02671       {      
02672         break;
02673       }
02674       else
02675       {
02676         return FALSE;
02677       }
02678     }
02679     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02680     if( result == FALSE )
02681       return FALSE;
02682 
02683     memset( str, 0, sizeof(char)*10*20 );  
02684     count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
02685       str[0],
02686       str[1],
02687       str[2],
02688       str[3]
02689       );
02690     if( count != 4 )
02691       return FALSE;
02692 
02693     i = 0;
02694     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02695       return FALSE;
02696     eph.iode = (unsigned char)dtmp;
02697     i++;
02698     if( sscanf( str[i], "%Lf", &eph.crs ) != 1 )
02699       return FALSE;
02700     i++;
02701     if( sscanf( str[i], "%Lf", &eph.delta_n ) != 1 )
02702       return FALSE;
02703     i++;
02704     if( sscanf( str[i], "%Lf", &eph.m0 ) != 1 )
02705       return FALSE;
02706     i++;
02707 
02708     // -------------------
02709     // BROADCAST ORBIT - 2
02710 
02711     // Get the next line from the file.
02712     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02713     {
02714       if( feof(fid) )
02715       {      
02716         break;
02717       }
02718       else
02719       {
02720         return FALSE;
02721       }
02722     }
02723     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02724     if( result == FALSE )
02725       return FALSE;
02726 
02727     memset( str, 0, sizeof(char)*10*20 );  
02728     count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
02729       str[0],
02730       str[1],
02731       str[2],
02732       str[3]
02733       );
02734     if( count != 4 )
02735       return FALSE;
02736 
02737     i = 0;
02738     if( sscanf( str[i], "%Lf", &eph.cuc ) != 1 )
02739       return FALSE;    
02740     i++;
02741     if( sscanf( str[i], "%Lf", &eph.ecc ) != 1 )
02742       return FALSE;
02743     i++;
02744     if( sscanf( str[i], "%Lf", &eph.cus ) != 1 )
02745       return FALSE;
02746     i++;
02747     if( sscanf( str[i], "%Lf", &eph.sqrta ) != 1 )
02748       return FALSE;
02749     i++;
02750 
02751 
02752     // -------------------
02753     // BROADCAST ORBIT - 3
02754 
02755     // Get the next line from the file.
02756     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02757     {
02758       if( feof(fid) )
02759       {      
02760         break;
02761       }
02762       else
02763       {
02764         return FALSE;
02765       }
02766     }
02767     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02768     if( result == FALSE )
02769       return FALSE;
02770 
02771     memset( str, 0, sizeof(char)*10*20 );  
02772     count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
02773       str[0],
02774       str[1],
02775       str[2],
02776       str[3]
02777       );
02778     if( count != 4 )
02779       return FALSE;
02780 
02781     i = 0;
02782     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02783       return FALSE;    
02784     eph.toe = (unsigned)dtmp;
02785     i++;
02786     if( sscanf( str[i], "%Lf", &eph.cic ) != 1 )
02787       return FALSE;
02788     i++;
02789     if( sscanf( str[i], "%Lf", &eph.omega0 ) != 1 )
02790       return FALSE;
02791     i++;
02792     if( sscanf( str[i], "%Lf", &eph.cis ) != 1 )
02793       return FALSE;
02794     i++;
02795 
02796 
02797     // -------------------
02798     // BROADCAST ORBIT - 4
02799 
02800     // Get the next line from the file.
02801     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02802     {
02803       if( feof(fid) )
02804       {      
02805         break;
02806       }
02807       else
02808       {
02809         return FALSE;
02810       }
02811     }
02812     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02813     if( result == FALSE )
02814       return FALSE;
02815 
02816     memset( str, 0, sizeof(char)*10*20 );  
02817     count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
02818       str[0],
02819       str[1],
02820       str[2],
02821       str[3]
02822       );
02823     if( count != 4 )
02824       return FALSE;
02825 
02826     i = 0;
02827     if( sscanf( str[i], "%Lf", &eph.i0 ) != 1 )
02828       return FALSE;    
02829     i++;
02830     if( sscanf( str[i], "%Lf", &eph.crc ) != 1 )
02831       return FALSE;
02832     i++;
02833     if( sscanf( str[i], "%Lf", &eph.w ) != 1 )
02834       return FALSE;
02835     i++;
02836     if( sscanf( str[i], "%Lf", &eph.omegadot ) != 1 )
02837       return FALSE;
02838     i++;
02839 
02840 
02841     // -------------------
02842     // BROADCAST ORBIT - 5
02843 
02844     // Get the next line from the file.
02845     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02846     {
02847       if( feof(fid) )
02848       {      
02849         break;
02850       }
02851       else
02852       {
02853         return FALSE;
02854       }
02855     }
02856     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02857     if( result == FALSE )
02858       return FALSE;
02859 
02860     memset( str, 0, sizeof(char)*10*20 );  
02861     count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
02862       str[0],
02863       str[1],
02864       str[2],
02865       str[3]
02866       );
02867     if( count != 4 )
02868       return FALSE;
02869 
02870     i = 0;
02871     if( sscanf( str[i], "%Lf", &eph.idot ) != 1 )
02872       return FALSE;    
02873     i++;
02874     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02875       return FALSE;
02876     eph.code_on_L2 = (unsigned char)dtmp;
02877     i++;
02878     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02879       return FALSE;    
02880     eph.week = (unsigned short)dtmp;
02881     i++;
02882     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02883       return FALSE;
02884     eph.L2_P_data_flag = (unsigned char)dtmp;
02885     i++;
02886 
02887     // -------------------
02888     // BROADCAST ORBIT - 6
02889 
02890     // Get the next line from the file.
02891     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02892     {
02893       if( feof(fid) )
02894       {      
02895         break;
02896       }
02897       else
02898       {
02899         return FALSE;
02900       }
02901     }
02902     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02903     if( result == FALSE )
02904       return FALSE;
02905 
02906     memset( str, 0, sizeof(char)*10*20 );  
02907     count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
02908       str[0],
02909       str[1],
02910       str[2],
02911       str[3]
02912       );
02913     if( count != 4 )
02914       return FALSE;
02915 
02916     i = 0;
02917     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02918       return FALSE;    
02919     i++;
02920     // This ura is in meters and the GPS_ephemeris is a ura index. Convert it.
02921     result = RINEX_ConvertURA_meters_to_URA_index( dtmp, &eph.ura ); 
02922     if( result == FALSE )
02923       return FALSE;  
02924 
02925     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02926       return FALSE;
02927     eph.health = (unsigned char)dtmp;
02928     i++;
02929     if( sscanf( str[i], "%Lf", &eph.tgd ) != 1 )
02930       return FALSE;    
02931     i++;
02932     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02933       return FALSE;
02934     eph.iodc = (unsigned short)dtmp;
02935     i++;
02936 
02937     // -------------------
02938     // BROADCAST ORBIT - 7
02939 
02940     // Get the next line from the file.
02941     if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
02942     {
02943       if( feof(fid) )
02944       {      
02945         break;
02946       }
02947       else
02948       {
02949         return FALSE;
02950       }
02951     }
02952     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
02953     if( result == FALSE )
02954       return FALSE;
02955 
02956     memset( str, 0, sizeof(char)*10*20 );  
02957     count = sscanf( line_buffer, "%*3c%19c%19c",
02958       str[0],
02959       str[1]
02960       );
02961     if( count != 2 )
02962       return FALSE;
02963 
02964     i = 0;
02965     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02966       return FALSE;    
02967     
02968     eph.tow = (unsigned)dtmp;
02969     itmp  = (int)eph.tow;
02970     itmp2 = (int)eph.toe;
02971     if( (itmp-itmp2) < -4*SECONDS_IN_DAY )
02972     {
02973       eph.tow_week = eph.week+1;
02974     }
02975     else
02976     {
02977       eph.tow_week = eph.week;
02978     }
02979 
02980     i++;
02981     if( sscanf( str[i], "%Lf", &dtmp ) != 1 )
02982       return FALSE;
02983     itmp = (int)dtmp;
02984     if( itmp <= 4 )
02985       eph.fit_interval_flag = 0;
02986     else
02987       eph.fit_interval_flag = 1;
02988     
02989     ephemeris_array[ephemeris_array_index] = eph;
02990     ephemeris_array_index++;    
02991 
02992     if( ephemeris_array_index == 0 && iono_model->isValid == FALSE )
02993     {
02994       // Get the model time from the first ephemeris data for the iono model.
02995       iono_model->week = eph.week;
02996       iono_model->tow = eph.toe;
02997       iono_model->isValid = TRUE;
02998     }    
02999   }
03000 
03001   *length_ephemeris_array = ephemeris_array_index;
03002 
03003   return TRUE;
03004 }
03005 
03006 
03007 // static
03008 BOOL RINEX_ReplaceDwithE( char *str, const unsigned length )
03009 {
03010   unsigned i = 0;
03011   if( str == NULL )
03012     return FALSE;
03013   if( length == 0 )
03014     return FALSE;
03015 
03016   for( i = 0; i < length; i++ )
03017   {
03018     if( str[i] == 'D' || str[i] == 'd' )
03019       str[i] = 'E';
03020   }
03021   return TRUE;
03022 }
03023 
03024 // static
03025 BOOL RINEX_ConvertURA_meters_to_URA_index( 
03026   double ura_m,         //!< The user range accuracy [m].
03027   unsigned char *ura    //!< The user range accuracy index.
03028   )
03029 {
03030   unsigned char i = 0;
03031 
03032   if( ura == FALSE )
03033     return FALSE;
03034 
03035   *ura = 15; // by default.
03036 
03037   for( i = 0; i < 16; i++ )
03038   {
03039     if( ura_m >= RINEX_MIN_URA[i] && ura_m < RINEX_MAX_URA[i] )
03040     {
03041       *ura = i;
03042       break;
03043     }
03044   }
03045 
03046   return TRUE;
03047 }
03048 
03049 
03050 BOOL RINEX_DecodeFileName(
03051   const char *filepath,            //!< (input) A full filepath.
03052   char *station_name,              //!< (output) A 5 character C string. char station_name[5]. In which to place the 4-character station name designator. This must be at least 5 characters.
03053   unsigned short *dayofyear,       //!< (output) The day of year.
03054   unsigned char *file_sequence_nr, //!< (output) file sequence number within day. 0: file contains all the existing data of the current day.
03055   unsigned short *year,            //!< (output) The full year. e.g. 1999, 2001.
03056   RINEX_enumFileType *filetype     //!< (output) The RINEX file type.
03057   )
03058 {
03059   char str[RINEX_LINEBUF_SIZE];
03060   char filename[RINEX_LINEBUF_SIZE];
03061   size_t length = 0;
03062   char *pch = NULL;
03063   char filetype_char = 0;
03064   unsigned count = 0;
03065   int itmp[3];
03066 
03067   // Check input.
03068   if( filepath == NULL )
03069     return FALSE;
03070   if( station_name == NULL )
03071     return FALSE;
03072   if( dayofyear == NULL )
03073     return FALSE;
03074   if( file_sequence_nr == NULL )
03075     return FALSE;
03076   if( year == NULL )
03077     return FALSE;
03078   if( filetype == NULL )
03079     return FALSE;
03080 
03081   station_name[0] = station_name[1] = station_name[2] = station_name[3] = station_name[4]  = '\0';
03082 
03083   length = strlen( filepath );
03084   if( length >= RINEX_LINEBUF_SIZE )
03085     return FALSE;
03086 
03087   // Copy the file path.
03088   strcpy( str, filepath );
03089 
03090   // Tokenize the string copy.
03091   pch = strtok( str,"\\/" );
03092   while (pch != NULL)
03093   {
03094     strcpy( filename, pch );
03095     pch = strtok( NULL, "\\/" );
03096   }
03097 
03098   length = strlen( filename );
03099   if( length != 12 )
03100     return FALSE;
03101 
03102   // Decode the filename.
03103   count = sscanf( filename, "%4c%3d%1d%*c%2d%c", 
03104     station_name,
03105     &(itmp[0]),
03106     &(itmp[1]),
03107     &(itmp[2]),
03108     &filetype_char
03109     );
03110   if( count != 5 )
03111     return FALSE;
03112 
03113   filetype_char = (char)toupper(filetype_char);
03114 
03115   *dayofyear = (unsigned short)itmp[0];
03116   *file_sequence_nr = (unsigned char)itmp[1];
03117   *year = (unsigned short)itmp[2];
03118   
03119 
03120   if( *year >= 80 && *year < 2000 )
03121   {
03122     *year += 1900;
03123   }
03124   else if( *year >= 0 && *year < 79 )
03125   {
03126     *year += 2000;
03127   }
03128   else
03129   {
03130     return FALSE;
03131   }
03132 
03133   *filetype = (RINEX_enumFileType)filetype_char;
03134 
03135   switch( *filetype )
03136   {
03137   case RINEX_FILE_TYPE_OBS:
03138   case RINEX_FILE_TYPE_GPS_NAV:
03139   case RINEX_FILE_TYPE_MET:
03140   case RINEX_FILE_TYPE_GLO_NAV:
03141   case RINEX_FILE_TYPE_GEO_NAV:
03142   case RINEX_FILE_TYPE_GAL_NAV:
03143   case RINEX_FILE_TYPE_MIXED_NAV:
03144   case RINEX_FILE_TYPE_SBAS:
03145   case RINEX_FILE_TYPE_CLK:
03146   case RINEX_FILE_TYPE_SUMMARY:
03147     {
03148     return TRUE;
03149     }
03150   default:
03151     {
03152       return FALSE;
03153     }
03154   }
03155 }
03156 
03157 
03158 BOOL RINEX_GetKlobucharIonoParametersFromNavFile(
03159   const char *filepath,             //!< (input) The file path to the GPS Navigation message file.
03160   GNSS_structKlobuchar *iono_model  //!< (input/output) A pointer to the ionospheric parameters struct.
03161   )
03162 {
03163   char RINEX_header[RINEX_HEADER_SIZE];
03164   unsigned RINEX_header_length = 0;
03165   double version = 0.0;
03166   RINEX_enumFileType file_type = RINEX_FILE_TYPE_UNKNOWN;
03167   char line_buffer[RINEX_LINEBUF_SIZE];
03168   unsigned nr_lines = 0;
03169   BOOL result;
03170   double dtmp = 0.0;
03171   
03172   char station_name[5];
03173   unsigned short dayofyear = 0;
03174   unsigned char file_sequence_nr = 0;
03175   unsigned short year = 0;
03176 
03177   double header_A0; // DELTA-UTC: A0,A1,T,W
03178   double header_A1; // DELTA-UTC: A0,A1,T,W
03179   int header_week;  // DELTA-UTC: A0,A1,T,W
03180   int header_tow;   // DELTA-UTC: A0,A1,T,W
03181   
03182   memset( iono_model, 0, sizeof(GNSS_structKlobuchar) );
03183 
03184   if( filepath == NULL )
03185     return FALSE;
03186   if( iono_model == NULL )
03187     return FALSE;
03188 
03189 
03190   result = RINEX_GetHeader( 
03191     filepath,
03192     RINEX_header,
03193     RINEX_HEADER_SIZE,
03194     &RINEX_header_length,
03195     &version,
03196     &file_type
03197     );
03198   if( result == FALSE )
03199     return FALSE;
03200 
03201   if( file_type != RINEX_FILE_TYPE_GPS_NAV )
03202     return FALSE;
03203 
03204   result = RINEX_get_header_lines(
03205     RINEX_header,
03206     RINEX_header_length,
03207     "ION ALPHA",
03208     line_buffer,
03209     RINEX_LINEBUF_SIZE,
03210     &nr_lines
03211     );
03212   if( result == FALSE )
03213     return FALSE;
03214   if( nr_lines == 1 )
03215   {
03216     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
03217     if( result == FALSE )
03218       return FALSE;
03219 
03220     if( sscanf( line_buffer, "%Lf %Lf %Lf %Lf", 
03221       &(iono_model->alpha0), 
03222       &(iono_model->alpha1),
03223       &(iono_model->alpha2),
03224       &(iono_model->alpha3) ) != 4 )
03225     {
03226       return FALSE; // bad header?
03227     }
03228     result = RINEX_get_header_lines(
03229       RINEX_header,
03230       RINEX_header_length,
03231       "ION BETA",
03232       line_buffer,
03233       RINEX_LINEBUF_SIZE,
03234       &nr_lines
03235       );
03236     if( result == FALSE )
03237       return FALSE;
03238     if( nr_lines != 1 )
03239       return FALSE; // weird header
03240 
03241     result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
03242     if( result == FALSE )
03243       return FALSE;
03244 
03245     if( sscanf( line_buffer, "%Lf %Lf %Lf %Lf", 
03246       &(iono_model->beta0), 
03247       &(iono_model->beta1),
03248       &(iono_model->beta2),
03249       &(iono_model->beta3) ) != 4 )
03250     {
03251       return FALSE; // bad header?
03252     }
03253 
03254     result = RINEX_get_header_lines(
03255       RINEX_header,
03256       RINEX_header_length,
03257       "DELTA-UTC: A0,A1,T,W",
03258       line_buffer,
03259       RINEX_LINEBUF_SIZE,
03260       &nr_lines
03261       );
03262     if( result == FALSE )
03263       return FALSE;
03264     if( nr_lines == 1 )
03265     {
03266       result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
03267       if( result == FALSE )
03268         return FALSE;
03269 
03270       if( sscanf( line_buffer, "%Lf %Lf %d %d",
03271         &header_A0,
03272         &header_A1,
03273         &header_tow,
03274         &header_week ) == 4 )
03275       {
03276         iono_model->week = (unsigned short) header_week;
03277         iono_model->tow = header_tow;
03278         iono_model->isValid = TRUE;
03279       }
03280     }
03281 
03282     if( iono_model->isValid == FALSE )
03283     {
03284       // Decode the year and day of year from the file name.
03285       result = RINEX_DecodeFileName( filepath, station_name, &dayofyear, &file_sequence_nr, &year, &file_type );
03286       if( result == TRUE )
03287       {
03288         result = TIMECONV_GetGPSTimeFromYearAndDayOfYear(
03289           year,
03290           dayofyear,
03291           &(iono_model->week),
03292           &dtmp
03293           );
03294         if( result == FALSE )
03295           return FALSE;
03296         iono_model->tow = (unsigned)dtmp;
03297         iono_model->isValid = TRUE;
03298       }
03299       else
03300       {
03301         // Get the Iono model time from the first ephemeris record in the file if there is one.
03302         iono_model->isValid = FALSE;
03303       }
03304     }
03305   }
03306 
03307   return TRUE;
03308 }
03309