kopia lustrzana https://github.com/sp9skp/spdxl
4078 wiersze
113 KiB
C
4078 wiersze
113 KiB
C
/**
|
|
\file rinex.c
|
|
\brief GNSS core 'c' function library: RINEX VERSION 2.11 related functions.
|
|
\author Glenn D. MacGougan (GDM)
|
|
\date 2007-12-03
|
|
\since 2007-12-02
|
|
|
|
\b REFERENCES \n
|
|
- http://www.aiub-download.unibe.ch/rinex/rinex211.txt
|
|
|
|
\b "LICENSE INFORMATION" \n
|
|
Copyright (c) 2007, refer to 'author' doxygen tags \n
|
|
All rights reserved. \n
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided the following conditions are met: \n
|
|
|
|
- Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer. \n
|
|
- Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution. \n
|
|
- The name(s) of the contributor(s) may not be used to endorse or promote
|
|
products derived from this software without specific prior written
|
|
permission. \n
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
|
|
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
SUCH DAMAGE.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <memory.h>
|
|
#include <math.h>
|
|
#include "rinex.h"
|
|
#include "gnss_error.h"
|
|
#include "time_conversion.h"
|
|
#include "constants.h"
|
|
|
|
#define RINEX_HEADER_SIZE (32768) //!< The maximum size of a RINEX header buffer [bytes].
|
|
#define RINEX_LINEBUF_SIZE (8192) //!< The maximum size of a string used in RINEX decoding [bytes].
|
|
#define RINEX_MAX_NR_SATS (64) //!< The maximum array size for "struct_RINEX_satellite RINEX_sat[RINEX_MAX_NR_SATS]".
|
|
#define RINEX_MAX_NR_OBS (64) //!< The maximum array size for "struct_RINEX_obs RINEX_obs[RINEX_MAX_NR_OBS]".
|
|
|
|
|
|
|
|
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};
|
|
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};
|
|
|
|
|
|
|
|
/// A container for a single RINEX data observation.
|
|
typedef struct
|
|
{
|
|
unsigned char loss_of_lock_indicator; //!< The loss of lock indicator (0-7).
|
|
unsigned char signal_strength; //!< The sign strength (1-9).
|
|
double value; //!< The data value of the observation.
|
|
RINEX_enumObservationType type; //!< The type of observation.
|
|
GNSS_enumSystem system; //!< The GNSS system.
|
|
unsigned short id; //!< The satellite id.
|
|
BOOL isValid; //!< A boolean indicating if this observation is valid (The value could be zero or blank).
|
|
} struct_RINEX_obs;
|
|
|
|
/// A container for a single RINEX satellite descriptor.
|
|
typedef struct
|
|
{
|
|
RINEX_enumSatelliteSystemType type;
|
|
unsigned short id;
|
|
} struct_RINEX_satellite;
|
|
|
|
|
|
/// A static function to trim the whitespace from the left and right of a C string.
|
|
static BOOL RINEX_trim_left_right(
|
|
char* str, //!< (input/output) The input C string.
|
|
unsigned max_length, //!< (input) The maximum length of the input string.
|
|
unsigned *str_length //!< (output) The length of the string after trimming the whitespace.
|
|
);
|
|
|
|
/// A static function to get the header lines indicated by the record descriptor.
|
|
static BOOL RINEX_get_header_lines(
|
|
const char* header, //!< (input) The full RINEX header buffer.
|
|
const unsigned header_size, //!< (input) The size of the valid data in the RINEX header buffer.
|
|
const char* record_desciptor, //!< (input) The record descriptor. e.g. "RINEX VERSION / TYPE"
|
|
char* lines_buffer, //!< (output) The output buffer.
|
|
const unsigned max_lines_buffer, //!< (input) The maximum size of the output buffer.
|
|
unsigned* nr_lines //!< (output) The number of lines read that correspond to the record descriptor.
|
|
);
|
|
|
|
/// A static function to erase a substring from a string.
|
|
static BOOL RINEX_erase(
|
|
char *erase_me, //!< (input) Erase this string from the input string.
|
|
char* str //!< (input/output) The input C string.
|
|
);
|
|
|
|
/// A static function to decode the "# / TYPES OF OBSERV" part of the RINEX OBS header.
|
|
static BOOL RINEX_GetObservationTypes(
|
|
const char* header_buffer, //!< (input) The full RINEX header buffer.
|
|
const unsigned header_buffer_size, //!< (input) The size of the valid data in the RINEX header buffer.
|
|
RINEX_structDecodedHeader* header //!< (input/output) The container for decoded header information.
|
|
);
|
|
|
|
|
|
/// \brief A static function to interpret special records
|
|
/// (embedded Header records within the observation data).
|
|
/// This function is called when the epoch flag is greater than 1.
|
|
static BOOL RINEX_DealWithSpecialRecords(
|
|
FILE* fid, //!< (input) An open (not NULL) file pointer to the RINEX data.
|
|
RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
|
|
BOOL *wasEndOfFileReached, //!< Has the end of the file been reached (output).
|
|
unsigned *filePosition, //!< The file position for the start of the message found (output).
|
|
const unsigned nr_special_records //!< The number of special records.
|
|
);
|
|
|
|
/// \brief Decode the next observation set for one satellite from the file.
|
|
static BOOL RINEX_GetNextObserationSetForOneSatellite(
|
|
FILE* fid, //!< (input) An open (not NULL) file pointer to the RINEX data.
|
|
RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
|
|
BOOL *wasEndOfFileReached, //!< (input/output) Has the end of the file been reached.
|
|
unsigned *filePosition, //!< (input/output) The file position for the start of the message found (output).
|
|
struct_RINEX_obs* RINEX_obs, //!< (input/output) A pointer to the array of RINEX observations.
|
|
const unsigned RINEX_max_nr_obs, //!< (input) The maximum size of the RINEX_obs array.
|
|
unsigned *RINEX_nr_obs, //!< (output) The number of valid obs in the RINEX_obs array.
|
|
const RINEX_enumSatelliteSystemType sattype, //!< (input) The satellite type.
|
|
const unsigned short id //!< (input) The satellite id.
|
|
);
|
|
|
|
/// \brief A static function to replace float values exponents denoted with 'D' with 'E'.
|
|
static BOOL RINEX_ReplaceDwithE( char *str, const unsigned length );
|
|
|
|
|
|
/**
|
|
\brief A static function to convert URA in meters to the URA index.
|
|
\author Glenn D. MacGougan
|
|
|
|
\n REFERENCES \n
|
|
- GPS ICD 200C, p83. section 20.3.3.3.1.3
|
|
*/
|
|
static BOOL RINEX_ConvertURA_meters_to_URA_index(
|
|
double ura_m, //!< The user range accuracy [m].
|
|
unsigned char *ura //!< The user range accuracy index.
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
\brief Convert RINEX signal strength to Carrier to Noise Density Ratio (CNo).
|
|
The conversion from RINEX signal strength to Carrier to Noise Density Ratio (CNo)
|
|
is ad-hoc. We will use the NovAtel OEM4 definition for CNo (dB-Hz). Nominally,
|
|
NovAtel maintains phase lock above 28 dB-Hz and a reasonable maximum signal strength
|
|
occurs at about 50 dB-Hz. We'll map linearly using 5 to 9 signals strength mapping to
|
|
28 to 50 dB-Hz. (50-28)/(9-5) = (y-28)/(x-5) where x is known, solve for y.
|
|
y = 5.5x+0.5.
|
|
|
|
\author Glenn D. MacGougan
|
|
\return TRUE(1) if successful, FALSE(0) otherwise.
|
|
*/
|
|
static BOOL RINEX_ConvertSignalStrengthToUsableCNo(
|
|
float *cno, //!< (input/output) The carrier to noise density ratio (dB-Hz)
|
|
const unsigned char signal_strength
|
|
);
|
|
|
|
|
|
|
|
BOOL RINEX_trim_left_right(
|
|
char* str, //!< (input) The input C string.
|
|
unsigned max_length, //!< (input) The maximum length of the input string.
|
|
unsigned *str_length //!< (output) The length of the string after trimming the whitespace.
|
|
)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
int start = 0;
|
|
size_t length = 0;
|
|
if( str == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( str == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( str_length == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( str_length == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Remove leading whitesapce
|
|
length = strlen( str );
|
|
if( length > max_length )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length > max_length )" );
|
|
return FALSE;
|
|
}
|
|
if( length == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length == 0 )" );
|
|
return TRUE;
|
|
}
|
|
|
|
for( i = 0; i < (int)length; i++ )
|
|
{
|
|
if( isspace(str[i]) )
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
start = i;
|
|
i = 0;
|
|
for( j = start; j < (int)length; j++ )
|
|
{
|
|
str[i] = str[j];
|
|
i++;
|
|
}
|
|
str[i] = '\0';
|
|
|
|
// Remove trailing whitespace.
|
|
length = strlen( str );
|
|
for( i = (int)(length-1); i > 0; i-- )
|
|
{
|
|
if( isspace(str[i]) )
|
|
{
|
|
str[i] = '\0';
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
length = strlen( str );
|
|
*str_length = (unsigned)length;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL RINEX_get_header_lines(
|
|
const char* header_buffer, //!< (input) The full RINEX header buffer.
|
|
const unsigned header_size, //!< (input) The size of the valid data in the RINEX header buffer.
|
|
const char* record_desciptor, //!< (input) The record descriptor. e.g. "RINEX VERSION / TYPE"
|
|
char* lines_buffer, //!< (output) The output buffer.
|
|
const unsigned max_lines_buffer, //!< (input) The maximum size of the output buffer.
|
|
unsigned* nr_lines //!< (output) The number of lines read that correspond to the record descriptor.
|
|
)
|
|
{
|
|
char *pch = NULL;
|
|
char *strptr = NULL;
|
|
unsigned i = 0;
|
|
int j = 0;
|
|
int record_descriptor_index = 0;
|
|
int scount = 0;
|
|
unsigned nr_valid_header_lines = 0;
|
|
size_t length_record_desciptor = 0;
|
|
|
|
typedef struct
|
|
{
|
|
char str[128]; // RINEX header strings should be 80 chars.
|
|
size_t length;
|
|
} struct_header_line_token;
|
|
|
|
struct_header_line_token token[256];
|
|
char buffer[RINEX_HEADER_SIZE];
|
|
|
|
if( header_buffer == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( header_buffer == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( header_size == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( header_size == 0 )" );
|
|
return FALSE;
|
|
}
|
|
if( record_desciptor == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( record_desciptor == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( lines_buffer == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( lines_buffer == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_lines == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
if( header_size+1 > RINEX_HEADER_SIZE )
|
|
{
|
|
GNSS_ERROR_MSG( "if( header_size+1 > RINEX_HEADER_SIZE )" );
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy( buffer, header_buffer, header_size );
|
|
buffer[header_size] = '\0';
|
|
|
|
*nr_lines = 0;
|
|
|
|
length_record_desciptor = strlen( record_desciptor );
|
|
if( length_record_desciptor == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length_record_desciptor == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
strptr = strstr( header_buffer, record_desciptor );
|
|
if( strptr == NULL )
|
|
{
|
|
return TRUE; // No line found with this descriptor at all.
|
|
}
|
|
|
|
// Tokenize the copied input buffer.
|
|
pch = strtok( buffer,"\r\n");
|
|
while( pch != NULL )
|
|
{
|
|
strptr = strstr( pch, record_desciptor );
|
|
if( strptr != NULL )
|
|
{
|
|
token[i].length = strlen( pch );
|
|
if( token[i].length < 128 )
|
|
{
|
|
strcpy( token[i].str, pch );
|
|
i++;
|
|
}
|
|
}
|
|
pch = strtok (NULL, "\r\n");
|
|
}
|
|
nr_valid_header_lines = i;
|
|
|
|
if( nr_valid_header_lines == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_valid_header_lines == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Search the tokenized strings (lines) for the record descriptor.
|
|
for( i = 0; i < nr_valid_header_lines; i++ )
|
|
{
|
|
strptr = strstr( token[i].str, record_desciptor );
|
|
if( strptr != NULL )
|
|
{
|
|
// check where it was found in the line.
|
|
record_descriptor_index = (int)(strptr - token[i].str);
|
|
|
|
if( record_descriptor_index != 60 )
|
|
{
|
|
// record decriptor must be at columns 61-80
|
|
// either extra/missing space(s) or the record descriptor is
|
|
// present within a COMMENT line.
|
|
|
|
if( strcmp( record_desciptor, "COMMENT" ) == 0 )
|
|
{
|
|
// possible COMMENT repeated on same line.
|
|
// ok to decode this line for this record descriptor
|
|
}
|
|
else
|
|
{
|
|
// Check if within a comment line.
|
|
strptr = strstr( token[i].str, "COMMENT" );
|
|
if( strptr != NULL )
|
|
{
|
|
j = (int)(strptr - token[i].str);
|
|
if( j == 60 )
|
|
{
|
|
// This is a comment line.
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// strange record, ignore
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is likely a few missing or extra spaces.
|
|
if( record_descriptor_index > 55 && record_descriptor_index < 65 )
|
|
{
|
|
// OK to decode this line for this record descriptor.
|
|
}
|
|
else
|
|
{
|
|
// too many extra spaces
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add this line to the lines_buffer.
|
|
if( (scount + token[i].length + 1) >= max_lines_buffer )
|
|
{
|
|
GNSS_ERROR_MSG( "if( (scount + token[i].length + 1) >= max_lines_buffer )" );
|
|
return FALSE;
|
|
}
|
|
|
|
scount += sprintf( lines_buffer+scount, "%s\n", token[i].str );
|
|
*nr_lines += 1;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL RINEX_erase(
|
|
char *erase_me, //!< (input) Erase this string from the input string.
|
|
char* str //!< (input/output) The input C string.
|
|
)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
char *strptr = NULL;
|
|
size_t len = 0;
|
|
size_t len_erase_me = 0;
|
|
|
|
if( erase_me == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( erase_me == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( str == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( str == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
len_erase_me = strlen( erase_me );
|
|
if( len_erase_me == 0 )
|
|
return TRUE;
|
|
|
|
len = strlen(str);
|
|
if( len == 0 )
|
|
return TRUE;
|
|
|
|
if( len_erase_me > len )
|
|
return TRUE;
|
|
|
|
strptr = strstr( str, erase_me );
|
|
|
|
while( strptr != NULL )
|
|
{
|
|
j = (int)(strptr - str); // start of the string to be erased
|
|
i = j + (int)len_erase_me; // end of the string to be erased
|
|
|
|
for( ; i < (int)len; i++ )
|
|
{
|
|
str[j] = str[i];
|
|
if( str[j] == '\0' )
|
|
break;
|
|
j++;
|
|
}
|
|
str[j] = '\0';
|
|
len = strlen(str);
|
|
|
|
strptr = strstr( str, erase_me );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// static
|
|
BOOL RINEX_GetObservationTypes(
|
|
const char* header_buffer, //!< (input) The full RINEX header buffer.
|
|
const unsigned header_buffer_size, //!< (input) The size of the valid data in the RINEX header buffer.
|
|
RINEX_structDecodedHeader* header //!< (input/output) The container for decoded header information.
|
|
)
|
|
{
|
|
char lines_buffer[RINEX_LINEBUF_SIZE];
|
|
unsigned nr_lines = 0;
|
|
BOOL result;
|
|
char *pch = NULL;
|
|
unsigned count = 0;
|
|
char token[128];
|
|
unsigned len=0;
|
|
BOOL isFirst = TRUE;
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"# / TYPES OF OBSERV",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
// strip the record description from the string
|
|
result = RINEX_erase( "# / TYPES OF OBSERV", lines_buffer );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_erase returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
// Determine the number of observation types.
|
|
if( sscanf( lines_buffer, "%u", &(header->nr_obs_types) ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
|
|
// Clean up the string a little.
|
|
result = RINEX_trim_left_right( lines_buffer, RINEX_LINEBUF_SIZE, &len );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_trim_left_right returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
// Tokenize the string.
|
|
pch = strtok( lines_buffer, " \t\r\n\f" );
|
|
while( pch != NULL && count < header->nr_obs_types )
|
|
{
|
|
if( !isFirst )
|
|
{
|
|
strcpy( token, pch );
|
|
result = RINEX_trim_left_right( token, 128, &len );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_trim_left_right returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( strlen(token) > 0 )
|
|
{
|
|
if( strcmp( token, "L1" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_L1;
|
|
else if( strcmp( token, "L2" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_L2;
|
|
else if( strcmp( token, "C1" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_C1;
|
|
else if( strcmp( token, "P1" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_P1;
|
|
else if( strcmp( token, "P2" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_P2;
|
|
else if( strcmp( token, "D1" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_D1;
|
|
else if( strcmp( token, "D2" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_D2;
|
|
else if( strcmp( token, "T1" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_T1;
|
|
else if( strcmp( token, "T2" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_T2;
|
|
else if( strcmp( token, "S1" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_S1;
|
|
else if( strcmp( token, "S2" ) == 0 )
|
|
header->obs_types[count] = RINEX_OBS_TYPE_S2;
|
|
else
|
|
header->obs_types[count] = RINEX_OBS_TYPE_UNKNOWN;
|
|
|
|
count++;
|
|
}
|
|
}
|
|
pch = strtok (NULL, " ,.-");
|
|
isFirst = FALSE;
|
|
}
|
|
|
|
if( count != header->nr_obs_types )
|
|
{
|
|
GNSS_ERROR_MSG( "if( count != header->nr_obs_types )" );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL RINEX_DealWithSpecialRecords(
|
|
FILE* fid, //!< (input) An open (not NULL) file pointer to the RINEX data.
|
|
RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
|
|
BOOL *wasEndOfFileReached, //!< Has the end of the file been reached (output).
|
|
unsigned *filePosition, //!< The file position for the start of the message found (output).
|
|
const unsigned nr_special_records //!< The number of special records.
|
|
)
|
|
{
|
|
char line_buffer[RINEX_LINEBUF_SIZE];
|
|
BOOL result = FALSE;
|
|
size_t length = 0;
|
|
unsigned i = 0;
|
|
|
|
if( fid == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( fid == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( RINEX_header == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_header == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( wasEndOfFileReached == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( wasEndOfFileReached == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( filePosition == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( filePosition == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// check nothing to do.
|
|
if( nr_special_records == 0 )
|
|
return TRUE;
|
|
|
|
for( i = 0; i < nr_special_records; i++ )
|
|
{
|
|
|
|
if( fgets( line_buffer, RINEX_LINEBUF_SIZE, fid ) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
*wasEndOfFileReached = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
*filePosition = ftell(fid);
|
|
|
|
if( strstr(line_buffer, "COMMENT") != NULL )
|
|
{
|
|
// This line is a comment. Ignore and continue.
|
|
}
|
|
else if( strstr(line_buffer, "WAVELENGTH FACT L1/2") != NULL )
|
|
{
|
|
// The wavelength factors have changed for some satellites.
|
|
|
|
// GDM todo deal with these changes
|
|
}
|
|
else if( strstr(line_buffer, "MARKER NAME") != NULL )
|
|
{
|
|
// The marker name has changed.
|
|
result = RINEX_erase("MARKER NAME", line_buffer);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_erase returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
result = RINEX_trim_left_right(line_buffer, RINEX_LINEBUF_SIZE, (unsigned *)&length );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_trim_left_right returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( length < 64 )
|
|
{
|
|
strcpy(RINEX_header->marker_name, line_buffer);
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "length > 64" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if( strstr(line_buffer, "MARKER NUMBER") != NULL )
|
|
{
|
|
// ignore for now
|
|
}
|
|
else if( strstr(line_buffer, "ANTENNA: DELTA H/E/N") != NULL )
|
|
{
|
|
if( sscanf( line_buffer, "%lf %lf %lf",
|
|
&(RINEX_header->antenna_delta_h),
|
|
&(RINEX_header->antenna_ecc_e),
|
|
&(RINEX_header->antenna_ecc_n) ) != 3 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if( strstr(line_buffer, "APPROX POSITION XYZ") != NULL )
|
|
{
|
|
if( sscanf( line_buffer, "%lf %lf %lf",
|
|
&(RINEX_header->x),
|
|
&(RINEX_header->y),
|
|
&(RINEX_header->z) ) != 3 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The rest not handled yet.
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//static
|
|
BOOL RINEX_GetNextObserationSetForOneSatellite(
|
|
FILE* fid, //!< (input) An open (not NULL) file pointer to the RINEX data.
|
|
RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
|
|
BOOL *wasEndOfFileReached, //!< (input/output) Has the end of the file been reached.
|
|
unsigned *filePosition, //!< (input/output) The file position for the start of the message found (output).
|
|
struct_RINEX_obs* RINEX_obs, //!< (input/output) A pointer to the array of RINEX observations.
|
|
const unsigned RINEX_max_nr_obs, //!< (input) The maximum size of the RINEX_obs array.
|
|
unsigned *RINEX_nr_obs, //!< (output) The number of valid obs in the RINEX_obs array.
|
|
const RINEX_enumSatelliteSystemType sattype, //!< (input) The satellite type.
|
|
const unsigned short id //!< (input) The satellite id.
|
|
)
|
|
{
|
|
unsigned i = 0;
|
|
char line_buffer[RINEX_LINEBUF_SIZE];
|
|
unsigned count = 0;
|
|
char str_a[15];
|
|
char str_b[15];
|
|
char str_c[15];
|
|
char str_d[15];
|
|
char str_e[15];
|
|
|
|
memset( str_a, 0, 15 );
|
|
memset( str_b, 0, 15 );
|
|
memset( str_c, 0, 15 );
|
|
memset( str_d, 0, 15 );
|
|
memset( str_e, 0, 15 );
|
|
|
|
// Check input
|
|
if( fid == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( fid == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( RINEX_header == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_header == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( wasEndOfFileReached == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( wasEndOfFileReached == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( filePosition == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( filePosition == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( RINEX_obs == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_obs == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( RINEX_max_nr_obs == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_max_nr_obs == 0 )" );
|
|
return FALSE;
|
|
}
|
|
if( RINEX_nr_obs == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_nr_obs == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( RINEX_header->nr_obs_types >= RINEX_max_nr_obs )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_header->nr_obs_types >= RINEX_max_nr_obs )" );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
*wasEndOfFileReached = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
*filePosition = ftell(fid);
|
|
|
|
// Set the default values to zero.
|
|
memset( RINEX_obs, 0, sizeof(struct_RINEX_obs)*RINEX_header->nr_obs_types );
|
|
|
|
switch( RINEX_header->nr_obs_types )
|
|
{
|
|
case 1:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[0].loss_of_lock_indicator,
|
|
&RINEX_obs[0].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[0].value) );
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[0].loss_of_lock_indicator,
|
|
&RINEX_obs[0].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[1].loss_of_lock_indicator,
|
|
&RINEX_obs[1].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[0].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[1].value) );
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[0].loss_of_lock_indicator,
|
|
&RINEX_obs[0].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[1].loss_of_lock_indicator,
|
|
&RINEX_obs[1].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[2].loss_of_lock_indicator,
|
|
&RINEX_obs[2].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[0].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[1].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[2].value) );
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[0].loss_of_lock_indicator,
|
|
&RINEX_obs[0].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[1].loss_of_lock_indicator,
|
|
&RINEX_obs[1].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[2].loss_of_lock_indicator,
|
|
&RINEX_obs[2].signal_strength,
|
|
str_d,
|
|
&RINEX_obs[3].loss_of_lock_indicator,
|
|
&RINEX_obs[3].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[0].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[1].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[2].value) );
|
|
sscanf( str_d ,"%lf", &(RINEX_obs[3].value) );
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[0].loss_of_lock_indicator,
|
|
&RINEX_obs[0].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[1].loss_of_lock_indicator,
|
|
&RINEX_obs[1].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[2].loss_of_lock_indicator,
|
|
&RINEX_obs[2].signal_strength,
|
|
str_d,
|
|
&RINEX_obs[3].loss_of_lock_indicator,
|
|
&RINEX_obs[3].signal_strength,
|
|
str_e,
|
|
&RINEX_obs[4].loss_of_lock_indicator,
|
|
&RINEX_obs[4].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[0].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[1].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[2].value) );
|
|
sscanf( str_d ,"%lf", &(RINEX_obs[3].value) );
|
|
sscanf( str_e ,"%lf", &(RINEX_obs[4].value) );
|
|
break;
|
|
}
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10: // intentional fall thru.
|
|
{
|
|
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[0].loss_of_lock_indicator,
|
|
&RINEX_obs[0].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[1].loss_of_lock_indicator,
|
|
&RINEX_obs[1].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[2].loss_of_lock_indicator,
|
|
&RINEX_obs[2].signal_strength,
|
|
str_d,
|
|
&RINEX_obs[3].loss_of_lock_indicator,
|
|
&RINEX_obs[3].signal_strength,
|
|
str_e,
|
|
&RINEX_obs[4].loss_of_lock_indicator,
|
|
&RINEX_obs[4].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[0].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[1].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[2].value) );
|
|
sscanf( str_d ,"%lf", &(RINEX_obs[3].value) );
|
|
sscanf( str_e ,"%lf", &(RINEX_obs[4].value) );
|
|
|
|
memset( str_a, 0, 15 );
|
|
memset( str_b, 0, 15 );
|
|
memset( str_c, 0, 15 );
|
|
memset( str_d, 0, 15 );
|
|
memset( str_e, 0, 15 );
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
*wasEndOfFileReached = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
switch( RINEX_header->nr_obs_types-5 )
|
|
{
|
|
case 1:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[5].loss_of_lock_indicator,
|
|
&RINEX_obs[5].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[5].value) );
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[5].loss_of_lock_indicator,
|
|
&RINEX_obs[5].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[6].loss_of_lock_indicator,
|
|
&RINEX_obs[6].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[5].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[6].value) );
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[5].loss_of_lock_indicator,
|
|
&RINEX_obs[5].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[6].loss_of_lock_indicator,
|
|
&RINEX_obs[6].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[7].loss_of_lock_indicator,
|
|
&RINEX_obs[7].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[5].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[6].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[7].value) );
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[5].loss_of_lock_indicator,
|
|
&RINEX_obs[5].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[6].loss_of_lock_indicator,
|
|
&RINEX_obs[6].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[7].loss_of_lock_indicator,
|
|
&RINEX_obs[7].signal_strength,
|
|
str_d,
|
|
&RINEX_obs[8].loss_of_lock_indicator,
|
|
&RINEX_obs[8].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[5].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[6].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[7].value) );
|
|
sscanf( str_d ,"%lf", &(RINEX_obs[8].value) );
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[5].loss_of_lock_indicator,
|
|
&RINEX_obs[5].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[6].loss_of_lock_indicator,
|
|
&RINEX_obs[6].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[7].loss_of_lock_indicator,
|
|
&RINEX_obs[7].signal_strength,
|
|
str_d,
|
|
&RINEX_obs[8].loss_of_lock_indicator,
|
|
&RINEX_obs[8].signal_strength,
|
|
str_e,
|
|
&RINEX_obs[9].loss_of_lock_indicator,
|
|
&RINEX_obs[9].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[5].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[6].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[7].value) );
|
|
sscanf( str_d ,"%lf", &(RINEX_obs[8].value) );
|
|
sscanf( str_e ,"%lf", &(RINEX_obs[9].value) );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected default case" );
|
|
return FALSE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 11:
|
|
{
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[0].loss_of_lock_indicator,
|
|
&RINEX_obs[0].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[1].loss_of_lock_indicator,
|
|
&RINEX_obs[1].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[2].loss_of_lock_indicator,
|
|
&RINEX_obs[2].signal_strength,
|
|
str_d,
|
|
&RINEX_obs[3].loss_of_lock_indicator,
|
|
&RINEX_obs[3].signal_strength,
|
|
str_e,
|
|
&RINEX_obs[4].loss_of_lock_indicator,
|
|
&RINEX_obs[4].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[0].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[1].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[2].value) );
|
|
sscanf( str_d ,"%lf", &(RINEX_obs[3].value) );
|
|
sscanf( str_e ,"%lf", &(RINEX_obs[4].value) );
|
|
|
|
memset( str_a, 0, 15 );
|
|
memset( str_b, 0, 15 );
|
|
memset( str_c, 0, 15 );
|
|
memset( str_d, 0, 15 );
|
|
memset( str_e, 0, 15 );
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
*wasEndOfFileReached = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
count = sscanf( line_buffer, "%14c%c%c%14c%c%c%14c%c%c%14c%c%c%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[5].loss_of_lock_indicator,
|
|
&RINEX_obs[5].signal_strength,
|
|
str_b,
|
|
&RINEX_obs[6].loss_of_lock_indicator,
|
|
&RINEX_obs[6].signal_strength,
|
|
str_c,
|
|
&RINEX_obs[7].loss_of_lock_indicator,
|
|
&RINEX_obs[7].signal_strength,
|
|
str_d,
|
|
&RINEX_obs[8].loss_of_lock_indicator,
|
|
&RINEX_obs[8].signal_strength,
|
|
str_e,
|
|
&RINEX_obs[9].loss_of_lock_indicator,
|
|
&RINEX_obs[9].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[5].value) );
|
|
sscanf( str_b, "%lf", &(RINEX_obs[6].value) );
|
|
sscanf( str_c, "%lf", &(RINEX_obs[7].value) );
|
|
sscanf( str_d ,"%lf", &(RINEX_obs[8].value) );
|
|
sscanf( str_e ,"%lf", &(RINEX_obs[9].value) );
|
|
|
|
memset( str_a, 0, 15 );
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
*wasEndOfFileReached = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
count = sscanf( line_buffer, "%14c%c%c",
|
|
str_a,
|
|
&RINEX_obs[10].loss_of_lock_indicator,
|
|
&RINEX_obs[10].signal_strength
|
|
);
|
|
sscanf( str_a, "%lf", &(RINEX_obs[10].value) );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected default case" );
|
|
return FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < RINEX_header->nr_obs_types; i++ )
|
|
{
|
|
// Zero values denote invalid observations.
|
|
if( RINEX_obs[i].value == 0 )
|
|
{
|
|
RINEX_obs[i].isValid = FALSE;
|
|
}
|
|
else
|
|
{
|
|
RINEX_obs[i].isValid = TRUE;
|
|
}
|
|
|
|
switch( RINEX_obs[i].loss_of_lock_indicator )
|
|
{
|
|
case '0': RINEX_obs[i].loss_of_lock_indicator = 0; break;
|
|
case '1': RINEX_obs[i].loss_of_lock_indicator = 1; break;
|
|
case '2': RINEX_obs[i].loss_of_lock_indicator = 2; break;
|
|
case '3': RINEX_obs[i].loss_of_lock_indicator = 3; break;
|
|
case '4': RINEX_obs[i].loss_of_lock_indicator = 4; break;
|
|
case '5': RINEX_obs[i].loss_of_lock_indicator = 5; break;
|
|
case '6': RINEX_obs[i].loss_of_lock_indicator = 6; break;
|
|
case '7': RINEX_obs[i].loss_of_lock_indicator = 7; break;
|
|
default: RINEX_obs[i].loss_of_lock_indicator = 0; break;
|
|
}
|
|
|
|
switch( RINEX_obs[i].signal_strength )
|
|
{
|
|
case '0': RINEX_obs[i].signal_strength = 0; break;
|
|
case '1': RINEX_obs[i].signal_strength = 1; break;
|
|
case '2': RINEX_obs[i].signal_strength = 2; break;
|
|
case '3': RINEX_obs[i].signal_strength = 3; break;
|
|
case '4': RINEX_obs[i].signal_strength = 4; break;
|
|
case '5': RINEX_obs[i].signal_strength = 5; break;
|
|
case '6': RINEX_obs[i].signal_strength = 6; break;
|
|
case '7': RINEX_obs[i].signal_strength = 7; break;
|
|
case '8': RINEX_obs[i].signal_strength = 8; break;
|
|
case '9': RINEX_obs[i].signal_strength = 9; break;
|
|
default: RINEX_obs[i].signal_strength = 0; break;
|
|
}
|
|
|
|
RINEX_obs[i].type = RINEX_header->obs_types[i];
|
|
|
|
switch( sattype )
|
|
{
|
|
case RINEX_SATELLITE_SYSTEM_GPS:
|
|
{
|
|
RINEX_obs[i].system = GNSS_GPS;
|
|
RINEX_obs[i].id = id;
|
|
break;
|
|
}
|
|
case RINEX_SATELLITE_SYSTEM_GLO:
|
|
{
|
|
RINEX_obs[i].system = GNSS_GLONASS;
|
|
RINEX_obs[i].id = id; // GLONASS slot number.
|
|
break;
|
|
}
|
|
case RINEX_SATELLITE_SYSTEM_GEO:
|
|
{
|
|
RINEX_obs[i].system = GNSS_WAAS;
|
|
RINEX_obs[i].id = id + 100;
|
|
break;
|
|
}
|
|
case RINEX_SATELLITE_SYSTEM_NSS:
|
|
{
|
|
continue; break; // Not supported. Ignore the data from this source. Continue to outer for loop.
|
|
}
|
|
default:
|
|
{
|
|
continue; break; // Not supported. Ignore the data from this source. Continue to outer for loop.
|
|
}
|
|
}
|
|
}
|
|
|
|
*RINEX_nr_obs = RINEX_header->nr_obs_types;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL RINEX_ConvertSignalStrengthToUsableCNo(
|
|
float *cno, //!< (input/output) The carrier to noise density ratio (dB-Hz)
|
|
const unsigned char signal_strength //!< (input) The RINEX signal strength indicator (0-9).
|
|
)
|
|
{
|
|
if( cno == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( cno == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
if( signal_strength > 0 && signal_strength < 10 )
|
|
{
|
|
*cno = 5.5f*signal_strength + 0.5f;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL RINEX_GetHeader(
|
|
const char* filepath, //!< Path to the RINEX file.
|
|
char* buffer, //!< (input/output) A character buffer in which to place the RINEX header.
|
|
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).
|
|
unsigned *buffer_size, //!< (output) The length of the header data placed in the buffer [bytes].
|
|
double *version, //!< (output) The RINEX version number. e.g. 1.0, 2.0, 2.2, 3.0, etc.
|
|
RINEX_enumFileType *file_type //!< (output) The RINEX file type.
|
|
)
|
|
{
|
|
FILE* fid = NULL; // A file pointer for the RINEX file.
|
|
char line_buffer[1024]; // A container for one line of the header.
|
|
char *strptr = NULL; // A pointer to a string.
|
|
BOOL end_of_header_found = FALSE; // A boolean to indicate if the end of header was found.
|
|
char type_char;
|
|
|
|
size_t line_length = 0; // The length of one line.
|
|
unsigned scount = 0; // A counter/index used to compose the header buffer.
|
|
|
|
fid = fopen( filepath, "r" );
|
|
if( fid == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( fid == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// The first line of the file must be the RINEX VERSION / TYPE
|
|
if( fgets( line_buffer, 1024, fid ) == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "fgets failed" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
strptr = strstr( line_buffer, "RINEX VERSION / TYPE" );
|
|
if( strptr == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "strstr failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
// Add the first line to the buffer
|
|
line_length = strlen( line_buffer );
|
|
if( scount+line_length >= buffer_max_size )
|
|
{
|
|
GNSS_ERROR_MSG( "if( scount+line_length >= buffer_max_size )" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
scount += sprintf( buffer+scount, "%s", line_buffer );
|
|
|
|
// Extract the RINEX version and type.
|
|
if( sscanf( line_buffer, "%lf %c", version, &type_char ) != 2 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
*file_type = (RINEX_enumFileType)type_char;
|
|
|
|
do
|
|
{
|
|
if( fgets( line_buffer, 1024, fid ) == NULL )
|
|
break;
|
|
|
|
if( strstr( line_buffer, "END OF HEADER" ) != NULL )
|
|
{
|
|
end_of_header_found = TRUE;
|
|
}
|
|
|
|
// Add the line of the buffer.
|
|
line_length = strlen( line_buffer );
|
|
if( scount+line_length >= buffer_max_size )
|
|
{
|
|
GNSS_ERROR_MSG( "if( scount+line_length >= buffer_max_size )" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
scount += sprintf( buffer+scount, "%s", line_buffer );
|
|
|
|
}while( !end_of_header_found );
|
|
|
|
if( end_of_header_found )
|
|
{
|
|
*buffer_size = scount;
|
|
fclose(fid);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "End of RINEX header not found." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL RINEX_DecodeHeader_ObservationFile(
|
|
const char* header_buffer, //!< (input) The character buffer containing the RINEX header.
|
|
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.
|
|
RINEX_structDecodedHeader* header //!< (output) The decoded header data.
|
|
)
|
|
{
|
|
BOOL result = FALSE;
|
|
char lines_buffer[RINEX_LINEBUF_SIZE];
|
|
unsigned nr_lines = 0;
|
|
char rinex_type_char = 0;
|
|
char time_system_str[128];
|
|
unsigned len = 0;
|
|
int itmp[5];
|
|
|
|
if( header_buffer == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( header_buffer == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( header_buffer_size == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( header_buffer_size == 0 )" );
|
|
return FALSE;
|
|
}
|
|
if( header == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( header == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
memset( header, 0, sizeof(RINEX_structDecodedHeader) );
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"RINEX VERSION / TYPE",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_lines != 1 )" );
|
|
return FALSE;
|
|
}
|
|
if( sscanf( lines_buffer, "%lf %c", &(header->version), &rinex_type_char ) != 2 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed" );
|
|
return FALSE;
|
|
}
|
|
header->type = (RINEX_enumFileType)rinex_type_char;
|
|
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"MARKER NAME",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_lines != 1 )" );
|
|
return FALSE;
|
|
}
|
|
result = RINEX_erase("MARKER NAME", lines_buffer);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_erase returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
result = RINEX_trim_left_right(lines_buffer, RINEX_LINEBUF_SIZE, &len );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_trim_left_right returned FALSE" );
|
|
return FALSE;
|
|
}
|
|
if( len < 64 )
|
|
{
|
|
strcpy(header->marker_name, lines_buffer);
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "if( len < 64 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"APPROX POSITION XYZ",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines" );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == 1 )
|
|
{
|
|
if( sscanf( lines_buffer, "%lf %lf %lf", &(header->x), &(header->y), &(header->z) ) != 3 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"ANTENNA: DELTA H/E/N",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_lines != 1 )" );
|
|
return FALSE;
|
|
}
|
|
if( sscanf( lines_buffer, "%lf %lf %lf", &(header->antenna_delta_h), &(header->antenna_ecc_e), &(header->antenna_ecc_n) ) != 3 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"WAVELENGTH FACT L1/2",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == 1 )
|
|
{
|
|
// Only default values specified.
|
|
if( sscanf( lines_buffer, "%d %d", (int *)&(header->default_wavefactor_L1), (int *)&(header->default_wavefactor_L2) ) != 2 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// First read the default values specified.
|
|
if( sscanf( lines_buffer, "%d %d", (int *)&(header->default_wavefactor_L1), (int *)&(header->default_wavefactor_L2) ) != 2 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
|
|
//GDM_TODO deal with multiline WAVELENTH FACT L1/2
|
|
}
|
|
|
|
result = RINEX_GetObservationTypes( header_buffer, header_buffer_size, header );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_GetObservationTypes returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"INTERVAL",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == 1 )
|
|
{
|
|
if( sscanf( lines_buffer, "%lf", &(header->interval) ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
if( header->interval <= 0 )
|
|
header->interval = -1; // Set to unknown value.
|
|
}
|
|
else
|
|
{
|
|
header->interval = -1; // Set to unknown value.
|
|
}
|
|
|
|
|
|
|
|
result = RINEX_get_header_lines(
|
|
header_buffer,
|
|
header_buffer_size,
|
|
"TIME OF FIRST OBS",
|
|
lines_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( lines_buffer, "%d %d %d %d %d %f %s",
|
|
&(itmp[0]),
|
|
&(itmp[1]),
|
|
&(itmp[2]),
|
|
&(itmp[3]),
|
|
&(itmp[4]),
|
|
&(header->time_of_first_obs.seconds),
|
|
time_system_str ) != 7 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
header->time_of_first_obs.year = (unsigned short)itmp[0];
|
|
header->time_of_first_obs.month = (unsigned char)itmp[1];
|
|
header->time_of_first_obs.day = (unsigned char)itmp[2];
|
|
header->time_of_first_obs.hour = (unsigned char)itmp[3];
|
|
header->time_of_first_obs.minute = (unsigned char)itmp[4];
|
|
|
|
|
|
result = RINEX_trim_left_right( time_system_str, 128, &len );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_trim_left_right returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( strcmp( time_system_str, "TIME") == 0 ) // no string present, defaults to GPS
|
|
header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_GPS;
|
|
else if( strcmp( time_system_str, "GPS" ) == 0 )
|
|
header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_GPS;
|
|
else if( strcmp( time_system_str, "GLO" ) == 0 )
|
|
header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_GLO;
|
|
else
|
|
header->time_of_first_obs.time_system = RINEX_TIME_SYSTEM_UNKNOWN;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL RINEX_GetNextObservationSet(
|
|
FILE* fid, //!< (input) An open (not NULL) file pointer to the RINEX data.
|
|
RINEX_structDecodedHeader* RINEX_header, //!< (input/output) The decoded RINEX header information. The wavelength markers can change as data is decoded.
|
|
BOOL *wasEndOfFileReached, //!< Has the end of the file been reached (output).
|
|
BOOL *wasObservationFound, //!< Was a valid observation found (output).
|
|
unsigned *filePosition, //!< The file position for the start of the message found (output).
|
|
GNSS_structMeasurement* obsArray, //!< A pointer to a user provided array of GNSS_structMeasurement (input/output).
|
|
const unsigned char maxNrObs, //!< The maximum number of elements in the array provided (input).
|
|
unsigned *nrObs, //!< The number of valid elements set in the array (output).
|
|
unsigned short* rx_gps_week, //!< The receiver GPS week (0-1024+) [weeks].
|
|
double* rx_gps_tow //!< The receiver GPS time of week (0-603799.99999) [s].
|
|
)
|
|
{
|
|
char line_buffer[RINEX_LINEBUF_SIZE]; // A character buffer to hold a line from the RINEX file.
|
|
size_t length = 0; // A string length.
|
|
RINEX_TIME epoch; // The RINEX time.
|
|
RINEX_enumEpochFlag epoch_flag; // A RINEX epoch flag.
|
|
char *pch = NULL; // A string pointer used in tokenizing a C string.
|
|
unsigned count = 0; // A counter.
|
|
int itmp = 0; // A temporary integer.
|
|
int i = 0; // A counter.
|
|
int j = 0; // A counter.
|
|
int obsArray_index = 0; // A counter.
|
|
char numstr[64]; // A string to hold a number.
|
|
char tmpstr[128]; // A temporary string.
|
|
BOOL isL1data_present = FALSE;
|
|
BOOL isL2data_present = FALSE;
|
|
BOOL overwriteCNoL1 = TRUE;
|
|
BOOL overwriteCNoL2 = TRUE;
|
|
BOOL isEpochValidToDecode = FALSE;
|
|
BOOL isContinuationLinePresent = FALSE;
|
|
int nr_special_records = 0;
|
|
|
|
double tow = 0; // A time of week (0-604399.99999) [s].
|
|
unsigned short week = 0; // The GPS week (0-1024+) [weeks].
|
|
|
|
RINEX_enumSatelliteSystemType next_sat_type = RINEX_SATELLITE_SYSTEM_UNKNOWN;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char str[128];
|
|
size_t length;
|
|
BOOL isValid;
|
|
} struct_token;
|
|
struct_token token[64]; // An array of string tokens.
|
|
unsigned nr_tokens = 0; // The number of valid tokens.
|
|
|
|
struct_RINEX_satellite RINEX_sat[RINEX_MAX_NR_SATS];
|
|
unsigned RINEX_sat_index = 0; // The index into RINEX_sat.
|
|
unsigned RINEX_nr_satellites = 0; // The number of valid values in the RINEX_sat array.
|
|
|
|
struct_RINEX_obs RINEX_obs[RINEX_MAX_NR_OBS];
|
|
unsigned RINEX_obs_index = 0; // The index into RINEX_obs.
|
|
unsigned RINEX_nr_obs = 0; // The number of valid obs in RINEX_obs array.
|
|
|
|
BOOL result;
|
|
|
|
numstr[0] = '\0';
|
|
numstr[1] = '\0';
|
|
numstr[2] = '\0';
|
|
numstr[3] = '\0';
|
|
|
|
// Check the input.
|
|
if( fid == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( fid == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( wasEndOfFileReached == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( wasEndOfFileReached == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( wasObservationFound == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( wasObservationFound == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( filePosition == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( filePosition == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( obsArray == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( obsArray == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( nrObs == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nrObs == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( RINEX_header->type != RINEX_FILE_TYPE_OBS )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_header->type != RINEX_FILE_TYPE_OBS )" );
|
|
return FALSE;
|
|
}
|
|
|
|
*wasObservationFound = FALSE;
|
|
*wasEndOfFileReached = FALSE;
|
|
|
|
// Set the token array to zero.
|
|
memset( token, 0, sizeof(struct_token)*RINEX_MAX_NR_SATS );
|
|
|
|
// Set the RINEX_obs array to zero.
|
|
memset( RINEX_obs, 0, sizeof(struct_RINEX_obs)*RINEX_MAX_NR_OBS );
|
|
|
|
// The epoch's tme system type is the same as the header's time of first RINEX_obs.
|
|
epoch.time_system = RINEX_header->time_of_first_obs.time_system;
|
|
|
|
if( epoch.time_system != RINEX_TIME_SYSTEM_GPS )
|
|
{
|
|
// Not supported for now!
|
|
GNSS_ERROR_MSG( "if( epoch.time_system != RINEX_TIME_SYSTEM_GPS ) - NOT SUPPORTED YET." );
|
|
return FALSE;
|
|
}
|
|
|
|
// Read in the RINEX epoch, epoch flag, and satellite ids string.
|
|
do
|
|
{
|
|
do // advance over empty lines if any
|
|
{
|
|
if( fgets( line_buffer, RINEX_LINEBUF_SIZE, fid ) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
*wasEndOfFileReached = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_trim_left_right( line_buffer, RINEX_LINEBUF_SIZE, (unsigned *)&length );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_trim_left_right returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
}while( length == 0 );
|
|
|
|
if( length == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// To make life easier later:
|
|
// Search and replace "G ", "R ", "T ", "S " with
|
|
// "G_", "R_", "T_", "S_"
|
|
// These are the satellite ids.
|
|
for( i = 0; i < (int)(length-1); i++ )
|
|
{
|
|
if( line_buffer[i] == 'G' || line_buffer[i] == 'R' || line_buffer[i] == 'T' || line_buffer[i] == 'S' )
|
|
{
|
|
if( line_buffer[i+1] == ' ' )
|
|
{
|
|
line_buffer[i+1] = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tokenize the input line buffer.
|
|
pch = strtok( line_buffer, " \t\r\n\f" );
|
|
nr_tokens = 0;
|
|
while( pch != NULL && count < 64 )
|
|
{
|
|
token[nr_tokens].length = strlen(pch);
|
|
if( token[nr_tokens].length < 128 )
|
|
{
|
|
strcpy( token[nr_tokens].str, pch );
|
|
token[nr_tokens].isValid = TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected." );
|
|
return FALSE;
|
|
}
|
|
|
|
pch = strtok( NULL, " \t\r\n\f" );
|
|
nr_tokens++;
|
|
}
|
|
if( nr_tokens >= 64 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_tokens >= 64 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// For events without significant epoch the epoch fields can be left blank.
|
|
// Thus, there are a few cases:
|
|
// (a) No epoch information, 2 integers (an event flag and the nr_special_records to follow 0-999).
|
|
// (b) All the epoch information + (a) (year month day hour minute seconds epoch_flag nr_special_records)
|
|
// (c) All the epoch information, epoch flag, number of satellites, the satellite list, and optionally the rx clock offset.
|
|
if( nr_tokens == 2 )
|
|
{
|
|
// This can only indicate a epoch flag with a number of special records to follow.
|
|
// Read in the epoch flag and the number of special records to follow.
|
|
if( sscanf( token[0].str, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
epoch_flag = (RINEX_enumEpochFlag)itmp;
|
|
|
|
if( sscanf( token[1].str, "%d", &nr_special_records ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
|
|
// Deal with special records if any
|
|
result = RINEX_DealWithSpecialRecords(
|
|
fid,
|
|
RINEX_header,
|
|
wasEndOfFileReached,
|
|
filePosition,
|
|
nr_special_records
|
|
);
|
|
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// The number of tokens must now contain
|
|
// year month day hour minute seconds epoch_flag --other stuff--,
|
|
// --other stuff-- depending on the epoch flag
|
|
if( nr_tokens < 8 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_tokens < 8 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Exract the epoch information from the tokenized line buffer.
|
|
i = 0;
|
|
if( sscanf( token[i].str, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
epoch.year = (unsigned short)itmp;
|
|
i++;
|
|
if( sscanf( token[i].str, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
epoch.month = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( token[i].str, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
epoch.day = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( token[i].str, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
epoch.hour = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( token[i].str, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
epoch.minute = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( token[i].str, "%f", &(epoch.seconds) ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( token[i].str, "%d", &(itmp) ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
epoch_flag = (RINEX_enumEpochFlag)itmp;
|
|
|
|
if( epoch_flag == 6 )
|
|
{
|
|
isEpochValidToDecode = TRUE;
|
|
// Cycle slip correction observations to follow.
|
|
// This is dealt with later below.
|
|
}
|
|
else if( epoch_flag > 1 )
|
|
{
|
|
if( sscanf( token[i].str, "%d", &nr_special_records ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
|
|
// Deal with special records if any
|
|
result = RINEX_DealWithSpecialRecords(
|
|
fid,
|
|
RINEX_header,
|
|
wasEndOfFileReached,
|
|
filePosition,
|
|
nr_special_records
|
|
);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
isEpochValidToDecode = TRUE;
|
|
}
|
|
}
|
|
|
|
}while( !isEpochValidToDecode );
|
|
|
|
if( token[7].length == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( token[7].length == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Some RINEX data has a clock offset term following the number of satellites and there id's
|
|
// Sometimes, this value is part of the eighth token
|
|
j = 0;
|
|
for( i = 0; i < (int)token[7].length; i++ )
|
|
{
|
|
if( token[7].str[i] == '-' || token[7].str[i] == '+' )
|
|
{
|
|
// ignore the rest of the token string
|
|
tmpstr[j] = '\0';
|
|
j++;
|
|
break;
|
|
}
|
|
if( j < 128 )
|
|
{
|
|
tmpstr[j] = token[7].str[i];
|
|
j++;
|
|
}
|
|
}
|
|
if( j < 128 )
|
|
{
|
|
tmpstr[j] = '\0';
|
|
j++;
|
|
}
|
|
strcpy( token[7].str, tmpstr );
|
|
token[7].length = strlen( token[7].str );
|
|
|
|
// The eighth token contains the number of satellites and there id's
|
|
j = 0;
|
|
for( i = 0; i < (int)token[7].length; i++ )
|
|
{
|
|
// Satellite can be denoted by the following letters (RINEX_v_ 2.1)
|
|
// 'G': GPS
|
|
// 'R': GLONASS
|
|
// 'S': Geostationary signal payload
|
|
// 'T': NNSS Transit
|
|
//
|
|
// e.g. string here is 5G_8G12G13R_8S20 means 5 satellite observations,
|
|
// with GPS PRN's 8, 12, 14, GLONASS id 7, and SBAS id 20 (by the way: making up the id's here).
|
|
if( token[7].str[i] == '_' )
|
|
{
|
|
continue;
|
|
}
|
|
if( token[7].str[i] == '-' || token[7].str[i] == '+' || token[7].str[i] == '.' || token[7].str[i] == 'E' || token[7].str[i] == 'e' )
|
|
{
|
|
// Any float numbers should not be present on this line.
|
|
// unless the clock offset is output (e.g. Trimble R8 receivers)
|
|
GNSS_ERROR_MSG( "float numbers should not be present on this line." );
|
|
break;
|
|
//return FALSE;
|
|
}
|
|
|
|
if( isdigit( token[7].str[i] ) )
|
|
{
|
|
numstr[j] = token[7].str[i];
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
if( token[7].str[i] != RINEX_SATELLITE_SYSTEM_GPS &&
|
|
token[7].str[i] != RINEX_SATELLITE_SYSTEM_GLO &&
|
|
token[7].str[i] != RINEX_SATELLITE_SYSTEM_GEO &&
|
|
token[7].str[i] != RINEX_SATELLITE_SYSTEM_NSS )
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
|
|
numstr[j] = '\0';
|
|
j = 0;
|
|
// A number always precedes a non-number here. Decode the number.
|
|
if( sscanf( numstr, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
if( count == 0 )
|
|
{
|
|
// This is the number of observations
|
|
RINEX_nr_satellites = itmp;
|
|
if( RINEX_nr_satellites > 12 )
|
|
{
|
|
// A continuation line will be used!
|
|
isContinuationLinePresent = TRUE;
|
|
}
|
|
|
|
if( RINEX_nr_satellites >= 64 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_nr_satellites >= 64 )" );
|
|
return FALSE; // a very unlikely error condition.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is a satellite id of type (current value of) next_sat_type.
|
|
RINEX_sat[count-1].id = (unsigned short)itmp;
|
|
RINEX_sat[count-1].type = next_sat_type;
|
|
}
|
|
count++;
|
|
if( count > RINEX_nr_satellites+1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( count > RINEX_nr_satellites+1 )" );
|
|
return FALSE; // a very unlikely error condition.
|
|
}
|
|
next_sat_type = (RINEX_enumSatelliteSystemType)token[7].str[i];
|
|
}
|
|
}
|
|
if( count == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( count == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// The last satellite id must still be interpreted.
|
|
numstr[j] = '\0';
|
|
j = 0;
|
|
if( sscanf( numstr, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed" );
|
|
return FALSE;
|
|
}
|
|
RINEX_sat[count-1].id = (unsigned short)itmp;
|
|
RINEX_sat[count-1].type = next_sat_type;
|
|
count++;
|
|
|
|
if( count > RINEX_nr_satellites+1 )
|
|
{
|
|
// Error in number of satellite ids read compared to RINEX_nr_obs.
|
|
GNSS_ERROR_MSG( "if( count > RINEX_nr_satellites+1 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Deal with the possibility of continuation lines.
|
|
if( count != RINEX_nr_satellites+1 )
|
|
{
|
|
if( RINEX_nr_satellites <= 12 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_nr_satellites <= 12 )" );
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
*wasEndOfFileReached = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
length = strlen(line_buffer);
|
|
if( length == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
if( RINEX_trim_left_right( line_buffer, RINEX_LINEBUF_SIZE, (unsigned *)&length ) == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_trim_left_right returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
for( i = 0; i < (int)length; i++ )
|
|
{
|
|
if( isspace(line_buffer[i]) )
|
|
{
|
|
continue;
|
|
}
|
|
if( line_buffer[i] == '-' || line_buffer[i] == '+' || line_buffer[i] == '.' || line_buffer[i] == 'E' || line_buffer[i] == 'e' )
|
|
{
|
|
// Any float numbers should not be present on this line.
|
|
GNSS_ERROR_MSG( "float numbers should not be present on this line." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( !isdigit( line_buffer[i] ) ) // In this case the satellite system type letter is first.
|
|
{
|
|
if( line_buffer[i] != RINEX_SATELLITE_SYSTEM_GPS &&
|
|
line_buffer[i] != RINEX_SATELLITE_SYSTEM_GLO &&
|
|
line_buffer[i] != RINEX_SATELLITE_SYSTEM_GEO &&
|
|
line_buffer[i] != RINEX_SATELLITE_SYSTEM_NSS &&
|
|
line_buffer[i] != RINEX_SATELLITE_SYSTEM_MIXED )
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
|
|
if( j != 0 )
|
|
{
|
|
numstr[j] = '\0';
|
|
j = 0;
|
|
if( sscanf( numstr, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
RINEX_sat[count-1].id = (unsigned short)itmp;
|
|
RINEX_sat[count-1].type = next_sat_type;
|
|
|
|
count++;
|
|
if( count > RINEX_nr_satellites+1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( count > RINEX_nr_satellites+1 )" );
|
|
return FALSE; // a very unlikely error condition.
|
|
}
|
|
}
|
|
next_sat_type = (RINEX_enumSatelliteSystemType)line_buffer[i];
|
|
}
|
|
else
|
|
{
|
|
numstr[j] = line_buffer[i];
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
// The last satellite id must still be interpreted.
|
|
numstr[j] = '\0';
|
|
j = 0;
|
|
if( sscanf( numstr, "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
RINEX_sat[count-1].id = (unsigned short)itmp;
|
|
RINEX_sat[count-1].type = next_sat_type;
|
|
count++;
|
|
|
|
if( count != RINEX_nr_satellites+1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( count != RINEX_nr_satellites+1 )" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if( RINEX_header->nr_obs_types >= 64 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( RINEX_header->nr_obs_types >= 64 )" );
|
|
return FALSE; // A very unlikely condition.
|
|
}
|
|
|
|
|
|
|
|
// TIME: The time of the measurement is the receiver time of the received signals.
|
|
// It is identical for the phase and range measurements and is identical for
|
|
// all satellites observed at that epoch. It is expressed in GPS time (not
|
|
// Universal Time).
|
|
//
|
|
// It is stored in UTC style (year, month, day, etc) BUT is receiver time.
|
|
|
|
if( epoch.year >= 80 && epoch.year < 2000 )
|
|
{
|
|
epoch.year += 1900;
|
|
}
|
|
else if( epoch.year >= 0 && epoch.year < 79 )
|
|
{
|
|
epoch.year += 2000;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
TIMECONV_GetGPSTimeFromRinexTime(
|
|
epoch.year,
|
|
epoch.month,
|
|
epoch.day,
|
|
epoch.hour,
|
|
epoch.minute,
|
|
epoch.seconds,
|
|
&week,
|
|
&tow
|
|
);
|
|
|
|
// Set the receiver time values.
|
|
*rx_gps_week = week;
|
|
*rx_gps_tow = tow;
|
|
|
|
|
|
obsArray_index = 0;
|
|
for( RINEX_sat_index = 0; RINEX_sat_index < (int)RINEX_nr_satellites; RINEX_sat_index++ )
|
|
{
|
|
isL1data_present = FALSE;
|
|
isL2data_present = FALSE;
|
|
overwriteCNoL1 = TRUE;
|
|
overwriteCNoL2 = TRUE;
|
|
|
|
// Set measurement data default to 0.
|
|
memset( &(obsArray[obsArray_index]), 0, sizeof(GNSS_structMeasurement) );
|
|
|
|
result = RINEX_GetNextObserationSetForOneSatellite(
|
|
fid,
|
|
RINEX_header,
|
|
wasEndOfFileReached,
|
|
filePosition,
|
|
RINEX_obs,
|
|
RINEX_MAX_NR_OBS,
|
|
&RINEX_nr_obs,
|
|
RINEX_sat[RINEX_sat_index].type,
|
|
RINEX_sat[RINEX_sat_index].id
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_GetNextObserationSetForOneSatellite returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( epoch_flag == 6 )
|
|
{
|
|
// Cycle slip style observations are present.
|
|
|
|
// GDM - ignore for now.
|
|
continue;
|
|
}
|
|
|
|
// Set the time.
|
|
obsArray[obsArray_index].tow = tow;
|
|
obsArray[obsArray_index].week = week;
|
|
|
|
// The channel index is simply the order of the data in this case.
|
|
obsArray[obsArray_index].channel = (unsigned short)obsArray_index;
|
|
|
|
obsArray[obsArray_index].id = RINEX_sat[RINEX_sat_index].id;
|
|
|
|
// Set default validity flags.
|
|
obsArray[obsArray_index].flags.isEphemerisValid = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAlmanacValid = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAboveElevationMask = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAboveCNoMask = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAboveLockTimeMask = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isNotUserRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNotPsrRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNotAdrRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNotDopplerRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = 1; // assume no slip
|
|
obsArray[obsArray_index].flags.isPsrUsedInSolution = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isDopplerUsedInSolution = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAdrUsedInSolution = 0; // not yet known
|
|
obsArray[obsArray_index].flags.useTropoCorrection = 1; // default to yes
|
|
obsArray[obsArray_index].flags.useBroadcastIonoCorrection = 1; // default to yes
|
|
|
|
|
|
|
|
// The GNSS observation array is channel based.
|
|
// We must look for matching observation sets to place within the channel based container.
|
|
// e.g. L1, P1, C1, D1 and S1
|
|
// first look for L1, P1, C1, D1 and S1
|
|
|
|
// Deal with S1 measurements first, so that if present, the RINEX signal strength values
|
|
// are not interpretted.
|
|
for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
|
|
{
|
|
if( RINEX_obs[RINEX_obs_index].type == RINEX_OBS_TYPE_S1 && RINEX_obs[RINEX_obs_index].isValid )
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL1;
|
|
|
|
overwriteCNoL1 = FALSE;
|
|
|
|
// GDM_TODO - A receiver dependant look up table is needed here to convert to
|
|
// Carrier to noise density ratio values in dB-Hz.
|
|
obsArray[obsArray_index].cno = (float)RINEX_obs[RINEX_obs_index].value; // [receiver dependant!]
|
|
|
|
isL1data_present = TRUE;
|
|
}
|
|
}
|
|
|
|
for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
|
|
{
|
|
if( !RINEX_obs[RINEX_obs_index].isValid )
|
|
continue;
|
|
|
|
switch(RINEX_obs[RINEX_obs_index].type)
|
|
{
|
|
case RINEX_OBS_TYPE_L1:
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL1;
|
|
|
|
obsArray[obsArray_index].adr = RINEX_obs[RINEX_obs_index].value; // cycles
|
|
|
|
// Set the validity flags
|
|
obsArray[obsArray_index].flags.isActive = TRUE;
|
|
obsArray[obsArray_index].flags.isCodeLocked = TRUE;
|
|
obsArray[obsArray_index].flags.isPhaseLocked = TRUE;
|
|
obsArray[obsArray_index].flags.isParityValid = TRUE; // Assume valid. No half cycle slips (invalid parity changes to valid partiy causes 1/2 cycle jump).
|
|
obsArray[obsArray_index].flags.isAdrValid = TRUE;
|
|
obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = TRUE;
|
|
|
|
isL1data_present = TRUE;
|
|
|
|
// Loss of lock indicator really pertains to the phase only.
|
|
switch( RINEX_obs[RINEX_obs_index].loss_of_lock_indicator )
|
|
{
|
|
case 0:
|
|
{
|
|
// OK
|
|
break;
|
|
}
|
|
case 1: // Loss lock between previous and current observation: cycle clip possible.
|
|
{
|
|
// Assume cycle slip took place.
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
// Opposite wavelength factor to the
|
|
// one defined for the satellite by a
|
|
// previous WAVELENGTH FACT L1/2 line.
|
|
// Valid for the current epoch only.
|
|
// GDM_TODO parity failure here?
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
// both 2 and 1
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
// both 4 and 1
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
// Intentional fall thru.
|
|
}
|
|
case 4:
|
|
{
|
|
// Observation under Antispoofing (may suffer from increased noise).
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
// both 4 and 2
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
case 7:
|
|
{
|
|
// both 4, 2 and 1
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// could be 'blank' or whitespace like '\r' or '\n'
|
|
// ignore
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( overwriteCNoL1 )
|
|
{
|
|
result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ConvertSignalStrengthToUsableCNo returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case RINEX_OBS_TYPE_C1:
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL1;
|
|
obsArray[obsArray_index].codeType = GNSS_CACode;
|
|
|
|
obsArray[obsArray_index].psr = RINEX_obs[RINEX_obs_index].value; // m
|
|
|
|
// The observation time convention is 'transmit' time.
|
|
obsArray[obsArray_index].tow = tow - obsArray[obsArray_index].psr/LIGHTSPEED;
|
|
|
|
// Set the validity flags
|
|
obsArray[obsArray_index].flags.isActive = TRUE;
|
|
obsArray[obsArray_index].flags.isCodeLocked = TRUE;
|
|
obsArray[obsArray_index].flags.isPsrValid = TRUE;
|
|
obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
|
|
isL1data_present = TRUE;
|
|
|
|
if( overwriteCNoL1 )
|
|
{
|
|
result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ConvertSignalStrengthToUsableCNo returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case RINEX_OBS_TYPE_P1:
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL1;
|
|
obsArray[obsArray_index].codeType = GNSS_PCode;
|
|
|
|
obsArray[obsArray_index].psr = RINEX_obs[RINEX_obs_index].value; // m
|
|
|
|
|
|
// The observation time convention is 'tranmsit' time.
|
|
obsArray[obsArray_index].tow = tow - obsArray[obsArray_index].psr/LIGHTSPEED;
|
|
|
|
// Set the validity flags
|
|
obsArray[obsArray_index].flags.isActive = TRUE;
|
|
obsArray[obsArray_index].flags.isCodeLocked = TRUE;
|
|
obsArray[obsArray_index].flags.isPsrValid = TRUE;
|
|
obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
|
|
isL1data_present = TRUE;
|
|
|
|
if( overwriteCNoL1 )
|
|
{
|
|
result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ConvertSignalStrengthToUsableCNo returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case RINEX_OBS_TYPE_D1:
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL1;
|
|
obsArray[obsArray_index].doppler = (float)RINEX_obs[RINEX_obs_index].value; // m
|
|
|
|
// Trimble R8 receiver data when converted to RINEX have epochs of invalid doppler
|
|
// where the value output is 0.0. To compensate for this all value of exactly zero
|
|
// are deemed invalid doppler.
|
|
|
|
// Set the validity flags
|
|
if( obsArray[obsArray_index].doppler == 0.0 )
|
|
{
|
|
obsArray[obsArray_index].flags.isDopplerValid = FALSE;
|
|
}
|
|
else
|
|
{
|
|
obsArray[obsArray_index].flags.isDopplerValid = TRUE;
|
|
isL1data_present = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if( isL1data_present )
|
|
{
|
|
// Check if no information about cno is present for L1.
|
|
if( obsArray[obsArray_index].cno == 0.0 )
|
|
{
|
|
obsArray[obsArray_index].cno = 32; // A nominally low but useable value [dB-Hz].
|
|
}
|
|
|
|
obsArray_index++;
|
|
if( obsArray_index >= maxNrObs )
|
|
{
|
|
GNSS_ERROR_MSG( "if( obsArray_index >= maxNrObs )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Set measurement data default to 0.
|
|
memset( &(obsArray[obsArray_index]), 0, sizeof(GNSS_structMeasurement) );
|
|
|
|
// Set the time.
|
|
obsArray[obsArray_index].tow = tow;
|
|
obsArray[obsArray_index].week = week;
|
|
|
|
obsArray[obsArray_index].id = RINEX_sat[RINEX_sat_index].id;
|
|
|
|
// The channel index is simply the order of the data in this case.
|
|
obsArray[obsArray_index].channel = (unsigned short)obsArray_index;
|
|
|
|
// Set default validity flags.
|
|
obsArray[obsArray_index].flags.isEphemerisValid = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAlmanacValid = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAboveElevationMask = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAboveCNoMask = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAboveLockTimeMask = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isNotUserRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNotPsrRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNotAdrRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNotDopplerRejected = 1; // assume not rejected
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = 1; // assume no slip
|
|
obsArray[obsArray_index].flags.isPsrUsedInSolution = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isDopplerUsedInSolution = 0; // not yet known
|
|
obsArray[obsArray_index].flags.isAdrUsedInSolution = 0; // not yet known
|
|
obsArray[obsArray_index].flags.useTropoCorrection = 1; // default to yes
|
|
obsArray[obsArray_index].flags.useBroadcastIonoCorrection = 1; // default to yes
|
|
}
|
|
|
|
|
|
// Deal with S2 measurements first, so that if present, the RINEX signal strength values
|
|
// are not interpretted.
|
|
for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
|
|
{
|
|
if( RINEX_obs[RINEX_obs_index].type == RINEX_OBS_TYPE_S2 && RINEX_obs[RINEX_obs_index].isValid )
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL2;
|
|
obsArray[obsArray_index].id = RINEX_obs[RINEX_obs_index].id;
|
|
|
|
overwriteCNoL2 = FALSE;
|
|
|
|
// GDM_TODO - A receiver dependant look up table is needed here to convert to
|
|
// Carrier to noise density ratio values in dB-Hz.
|
|
obsArray[obsArray_index].cno = (float)RINEX_obs[RINEX_obs_index].value; // [receiver dependant!]
|
|
|
|
isL2data_present = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// Look for L2, P2, D2 and S2
|
|
for( RINEX_obs_index = 0; RINEX_obs_index < RINEX_nr_obs; RINEX_obs_index++ )
|
|
{
|
|
if( !RINEX_obs[RINEX_obs_index].isValid )
|
|
continue;
|
|
|
|
switch(RINEX_obs[RINEX_obs_index].type)
|
|
{
|
|
case RINEX_OBS_TYPE_L2:
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL2;
|
|
obsArray[obsArray_index].adr = RINEX_obs[RINEX_obs_index].value; // cycles
|
|
|
|
// Set the validity flags
|
|
obsArray[obsArray_index].flags.isActive = TRUE;
|
|
obsArray[obsArray_index].flags.isCodeLocked = TRUE;
|
|
obsArray[obsArray_index].flags.isPhaseLocked = TRUE;
|
|
obsArray[obsArray_index].flags.isParityValid = TRUE; // Assume valid. No half cycle slips (invalid parity changes to valid partiy causes 1/2 cycle jump).
|
|
obsArray[obsArray_index].flags.isAdrValid = TRUE;
|
|
obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = TRUE;
|
|
isL2data_present = TRUE;
|
|
|
|
// Loss of lock indicator really pertains to the phase only.
|
|
switch( RINEX_obs[RINEX_obs_index].loss_of_lock_indicator )
|
|
{
|
|
case 0:
|
|
{
|
|
break; // OK
|
|
}
|
|
case 1: // Loss lock between previous and current observation: cycle clip possible.
|
|
{
|
|
// Assume cycle slip took place.
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
// Opposite wavelength factor to the
|
|
// one defined for the satellite by a
|
|
// previous WAVELENGTH FACT L1/2 line.
|
|
// Valid for the current epoch only.
|
|
// GDM_TODO parity failure here?
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
// both 2 and 1
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
// both 4 and 1
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
// Intentional fall thru.
|
|
}
|
|
case 4:
|
|
{
|
|
// Observation under Antispoofing (may suffer from increased noise).
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
// both 4 and 2
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
case 7:
|
|
{
|
|
// both 4, 2 and 1
|
|
obsArray[obsArray_index].flags.isNoCycleSlipDetected = FALSE;
|
|
// GDM_TODO
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// could be 'blank' or whitespace like '\r' or '\n'
|
|
// ignore
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( overwriteCNoL2 )
|
|
{
|
|
result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ConvertSignalStrengthToUsableCNo returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case RINEX_OBS_TYPE_P2:
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL2;
|
|
obsArray[obsArray_index].codeType = GNSS_PCode;
|
|
|
|
obsArray[obsArray_index].psr = RINEX_obs[RINEX_obs_index].value; // m
|
|
|
|
// The observation time convention is 'tranmsit' time.
|
|
obsArray[obsArray_index].tow = tow - obsArray[obsArray_index].psr/LIGHTSPEED;
|
|
|
|
// Set the validity flags
|
|
obsArray[obsArray_index].flags.isActive = TRUE;
|
|
obsArray[obsArray_index].flags.isCodeLocked = TRUE;
|
|
obsArray[obsArray_index].flags.isPsrValid = TRUE;
|
|
obsArray[obsArray_index].flags.isAutoAssigned = TRUE; // Assumed.
|
|
isL2data_present = TRUE;
|
|
|
|
if( overwriteCNoL2 )
|
|
{
|
|
result = RINEX_ConvertSignalStrengthToUsableCNo( &(obsArray[obsArray_index].cno), RINEX_obs[RINEX_obs_index].signal_strength );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ConvertSignalStrengthToUsableCNo returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case RINEX_OBS_TYPE_D2:
|
|
{
|
|
obsArray[obsArray_index].system = RINEX_obs[RINEX_obs_index].system;
|
|
obsArray[obsArray_index].freqType = GNSS_GPSL2;
|
|
obsArray[obsArray_index].doppler = (float)RINEX_obs[RINEX_obs_index].value; // m
|
|
|
|
// Trimble R8 receiver data when converted to RINEX have epochs of invalid doppler
|
|
// where the value output is 0.0. To compensate for this all value of exactly zero
|
|
// are deemed invalid doppler.
|
|
|
|
// Set the validity flags
|
|
if( obsArray[obsArray_index].doppler == 0.0 )
|
|
{
|
|
obsArray[obsArray_index].flags.isDopplerValid = FALSE;
|
|
}
|
|
else
|
|
{
|
|
obsArray[obsArray_index].flags.isDopplerValid = TRUE;
|
|
isL2data_present = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( isL2data_present )
|
|
{
|
|
// Check if no information about cno is present for L2.
|
|
if( obsArray[obsArray_index].cno == 0.0 )
|
|
{
|
|
obsArray[obsArray_index].cno = 32; // A nominally low but useable value [dB-Hz].
|
|
}
|
|
|
|
obsArray_index++;
|
|
if( obsArray_index >= maxNrObs )
|
|
{
|
|
GNSS_ERROR_MSG( "if( obsArray_index >= maxNrObs )" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Note that T1 and T2 measurements are not supported.
|
|
}
|
|
|
|
*nrObs = obsArray_index;
|
|
*wasObservationFound = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL RINEX_DecodeGPSNavigationFile(
|
|
const char *filepath, //!< (input) The file path to the GPS Navigation message file.
|
|
GNSS_structKlobuchar *iono_model, //!< (input/output) A pointer to the ionospheric parameters struct.
|
|
GPS_structEphemeris *ephemeris_array, //!< (input/output) A pointer to the GPS ephemeris array.
|
|
const unsigned int max_length_ephemeris_array, //!< (input) The maximum size of the GPS ephemeris array.
|
|
unsigned int *length_ephemeris_array //!< (input/output) The length of the GPS ephemeris array after decoding. The number of valid items.
|
|
)
|
|
{
|
|
char RINEX_header[RINEX_HEADER_SIZE];
|
|
unsigned RINEX_header_length = 0;
|
|
double version = 0.0;
|
|
RINEX_enumFileType file_type = RINEX_FILE_TYPE_UNKNOWN;
|
|
char line_buffer[RINEX_LINEBUF_SIZE];
|
|
unsigned nr_lines = 0;
|
|
BOOL result;
|
|
FILE* fid = NULL;
|
|
GPS_structEphemeris eph; // A single ephemeris record.
|
|
RINEX_TIME epoch;
|
|
unsigned i = 0;
|
|
unsigned count = 0;
|
|
double tow = 0;
|
|
int itmp = 0;
|
|
int itmp2 = 0;
|
|
double dtmp = 0.0;
|
|
unsigned short week = 0;
|
|
unsigned ephemeris_array_index = 0;
|
|
size_t length = 0;
|
|
|
|
char station_name[5];
|
|
unsigned short dayofyear = 0;
|
|
unsigned char file_sequence_nr = 0;
|
|
unsigned short year = 0;
|
|
|
|
double header_A0; // DELTA-UTC: A0,A1,T,W
|
|
double header_A1; // DELTA-UTC: A0,A1,T,W
|
|
int header_week; // DELTA-UTC: A0,A1,T,W
|
|
int header_tow; // DELTA-UTC: A0,A1,T,W
|
|
|
|
char str[10][20]; // A 2D array of strings of length 20;
|
|
|
|
memset( &eph, 0, sizeof(GPS_structEphemeris) );
|
|
|
|
epoch.time_system = RINEX_TIME_SYSTEM_GPS;
|
|
|
|
if( filepath == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( filepath == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( iono_model == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( iono_model == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( ephemeris_array == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( ephemeris_array == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( length_ephemeris_array == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length_ephemeris_array == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( max_length_ephemeris_array == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( max_length_ephemeris_array == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
iono_model->isValid = FALSE;
|
|
|
|
result = RINEX_GetHeader(
|
|
filepath,
|
|
RINEX_header,
|
|
RINEX_HEADER_SIZE,
|
|
&RINEX_header_length,
|
|
&version,
|
|
&file_type
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_GetHeader returned FALSE." );
|
|
FILE *ftoold=fopen("/tmp/toold", "ab+");
|
|
if(ftoold) fclose(ftoold);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if( file_type != RINEX_FILE_TYPE_GPS_NAV )
|
|
{
|
|
GNSS_ERROR_MSG( "if( file_type != RINEX_FILE_TYPE_GPS_NAV )" );
|
|
return FALSE;
|
|
}
|
|
|
|
if( fabs( version - 2.1 ) < 1e-06 ||
|
|
fabs( version - 2.0 ) < 1e-06 ||
|
|
fabs( version - 2.11 ) < 1e-06 ||
|
|
fabs( version - 2.12 ) < 1e-06 )
|
|
{
|
|
// this version is valid for decoding
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX version not supported." );
|
|
return FALSE;
|
|
}
|
|
|
|
result = RINEX_get_header_lines(
|
|
RINEX_header,
|
|
RINEX_header_length,
|
|
"ION ALPHA",
|
|
line_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == 1 )
|
|
{
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( line_buffer, "%lf %lf %lf %lf",
|
|
&(iono_model->alpha0),
|
|
&(iono_model->alpha1),
|
|
&(iono_model->alpha2),
|
|
&(iono_model->alpha3) ) != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf returned FALSE." );
|
|
return FALSE; // bad header?
|
|
}
|
|
result = RINEX_get_header_lines(
|
|
RINEX_header,
|
|
RINEX_header_length,
|
|
"ION BETA",
|
|
line_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_lines != 1 )" );
|
|
return FALSE; // weird header
|
|
}
|
|
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( line_buffer, "%lf %lf %lf %lf",
|
|
&(iono_model->beta0),
|
|
&(iono_model->beta1),
|
|
&(iono_model->beta2),
|
|
&(iono_model->beta3) ) != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE; // bad header?
|
|
}
|
|
|
|
result = RINEX_get_header_lines(
|
|
RINEX_header,
|
|
RINEX_header_length,
|
|
"DELTA-UTC: A0,A1,T,W",
|
|
line_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == 1 )
|
|
{
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( line_buffer, "%lf %lf %d %d",
|
|
&header_A0,
|
|
&header_A1,
|
|
&header_tow,
|
|
&header_week ) == 4 )
|
|
{
|
|
iono_model->week = (unsigned short) header_week;
|
|
iono_model->tow = header_tow;
|
|
iono_model->isValid = TRUE;
|
|
}
|
|
}
|
|
|
|
if( iono_model->isValid == FALSE )
|
|
{
|
|
// Decode the year and day of year from the file name.
|
|
result = RINEX_DecodeFileName( filepath, station_name, &dayofyear, &file_sequence_nr, &year, &file_type );
|
|
if( result == TRUE )
|
|
{
|
|
result = TIMECONV_GetGPSTimeFromYearAndDayOfYear(
|
|
year,
|
|
dayofyear,
|
|
&(iono_model->week),
|
|
&dtmp
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "TIMECONV_GetGPSTimeFromYearAndDayOfYear returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
iono_model->tow = (unsigned)dtmp;
|
|
iono_model->isValid = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Get the Iono model time from the first ephemeris record in the file if there is one.
|
|
iono_model->isValid = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
fid = fopen( filepath, "r" );
|
|
if( fid == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( fid == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
fclose(fid);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
while( strstr( line_buffer, "END OF HEADER" ) == NULL );
|
|
|
|
|
|
while( !feof(fid) && !ferror(fid) && ephemeris_array_index < max_length_ephemeris_array )
|
|
{
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
// Check that this line is now empty.
|
|
length = strlen(line_buffer);
|
|
for( i = 0; i < length; i++ )
|
|
{
|
|
if( isalnum(line_buffer[i]) )
|
|
break;
|
|
}
|
|
if( i == length )
|
|
continue; // This string is empty.
|
|
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)length );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%2c%*c%2c%*c%2c%*c%2c%*c%2c%*c%2c%5c%19c%19c%19c",
|
|
str[0],
|
|
str[1],
|
|
str[2],
|
|
str[3],
|
|
str[4],
|
|
str[5],
|
|
str[6],
|
|
str[7],
|
|
str[8],
|
|
str[9]
|
|
);
|
|
|
|
if( count != 10 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE; // bad record
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.prn = (unsigned short)itmp;
|
|
i++;
|
|
if( sscanf( str[i], "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
epoch.year = (unsigned short)itmp;
|
|
i++;
|
|
if( sscanf( str[i], "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
epoch.month = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( str[i], "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
epoch.day = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( str[i], "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
epoch.hour = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( str[i], "%d", &itmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
epoch.minute = (unsigned char)itmp;
|
|
i++;
|
|
if( sscanf( str[i], "%f", &epoch.seconds ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.af0 ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.af1 ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.af2 ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
if( epoch.year >= 80 && epoch.year < 2000 )
|
|
{
|
|
epoch.year += 1900;
|
|
}
|
|
else if( epoch.year >= 0 && epoch.year < 79 )
|
|
{
|
|
epoch.year += 2000;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
result = TIMECONV_GetGPSTimeFromRinexTime(
|
|
epoch.year,
|
|
epoch.month,
|
|
epoch.day,
|
|
epoch.hour,
|
|
epoch.minute,
|
|
epoch.seconds,
|
|
&week,
|
|
&tow
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "TIMECONV_GetGPSTimeFromRinexTime returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.toc = (unsigned)tow;
|
|
|
|
|
|
// -------------------
|
|
// BROADCAST ORBIT - 1
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
|
|
str[0],
|
|
str[1],
|
|
str[2],
|
|
str[3]
|
|
);
|
|
if( count != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.iode = (unsigned char)dtmp;
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.crs ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.delta_n ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.m0 ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
|
|
// -------------------
|
|
// BROADCAST ORBIT - 2
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
|
|
str[0],
|
|
str[1],
|
|
str[2],
|
|
str[3]
|
|
);
|
|
if( count != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%lf", &eph.cuc ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.ecc ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.cus ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.sqrta ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
|
|
|
|
// -------------------
|
|
// BROADCAST ORBIT - 3
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
|
|
str[0],
|
|
str[1],
|
|
str[2],
|
|
str[3]
|
|
);
|
|
if( count != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.toe = (unsigned)dtmp;
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.cic ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.omega0 ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.cis ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
|
|
|
|
// -------------------
|
|
// BROADCAST ORBIT - 4
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
|
|
str[0],
|
|
str[1],
|
|
str[2],
|
|
str[3]
|
|
);
|
|
if( count != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%lf", &eph.i0 ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.crc ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.w ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.omegadot ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
|
|
|
|
// -------------------
|
|
// BROADCAST ORBIT - 5
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
|
|
str[0],
|
|
str[1],
|
|
str[2],
|
|
str[3]
|
|
);
|
|
if( count != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%lf", &eph.idot ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.code_on_L2 = (unsigned char)dtmp;
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.week = (unsigned short)dtmp;
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.L2_P_data_flag = (unsigned char)dtmp;
|
|
i++;
|
|
|
|
// -------------------
|
|
// BROADCAST ORBIT - 6
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%*3c%19c%19c%19c%19c",
|
|
str[0],
|
|
str[1],
|
|
str[2],
|
|
str[3]
|
|
);
|
|
if( count != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
// This ura is in meters and the GPS_ephemeris is a ura index. Convert it.
|
|
result = RINEX_ConvertURA_meters_to_URA_index( dtmp, &eph.ura );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ConvertURA_meters_to_URA_index returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.health = (unsigned char)dtmp;
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &eph.tgd ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
eph.iodc = (unsigned short)dtmp;
|
|
i++;
|
|
|
|
// -------------------
|
|
// BROADCAST ORBIT - 7
|
|
|
|
// Get the next line from the file.
|
|
if( fgets(line_buffer, RINEX_LINEBUF_SIZE, fid) == NULL )
|
|
{
|
|
if( feof(fid) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
memset( str, 0, sizeof(char)*10*20 );
|
|
count = sscanf( line_buffer, "%*3c%19c%19c",
|
|
str[0],
|
|
str[1]
|
|
);
|
|
if( count != 2 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
|
|
eph.tow = (unsigned)dtmp;
|
|
itmp = (int)eph.tow;
|
|
itmp2 = (int)eph.toe;
|
|
if( (itmp-itmp2) < -4*SECONDS_IN_DAY )
|
|
{
|
|
eph.tow_week = eph.week+1;
|
|
}
|
|
else
|
|
{
|
|
eph.tow_week = eph.week;
|
|
}
|
|
/*
|
|
i++;
|
|
if( sscanf( str[i], "%lf", &dtmp ) != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf returned FALSE." );
|
|
fclose(fid);
|
|
return FALSE;
|
|
}
|
|
itmp = (int)dtmp;
|
|
if( itmp <= 4 )
|
|
eph.fit_interval_flag = 0;
|
|
else
|
|
eph.fit_interval_flag = 1;
|
|
*/
|
|
ephemeris_array[ephemeris_array_index] = eph;
|
|
ephemeris_array_index++;
|
|
|
|
if( ephemeris_array_index == 0 && iono_model->isValid == FALSE )
|
|
{
|
|
// Get the model time from the first ephemeris data for the iono model.
|
|
iono_model->week = eph.week;
|
|
iono_model->tow = eph.toe;
|
|
iono_model->isValid = TRUE;
|
|
}
|
|
}
|
|
|
|
*length_ephemeris_array = ephemeris_array_index;
|
|
fclose(fid);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// static
|
|
BOOL RINEX_ReplaceDwithE( char *str, const unsigned length )
|
|
{
|
|
unsigned i = 0;
|
|
if( str == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( str == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( length == 0 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length == 0 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
for( i = 0; i < length; i++ )
|
|
{
|
|
if( str[i] == 'D' || str[i] == 'd' )
|
|
str[i] = 'E';
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
BOOL RINEX_ConvertURA_meters_to_URA_index(
|
|
double ura_m, //!< The user range accuracy [m].
|
|
unsigned char *ura //!< The user range accuracy index.
|
|
)
|
|
{
|
|
unsigned char i = 0;
|
|
|
|
if( ura == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "if( ura == FALSE )" );
|
|
return FALSE;
|
|
}
|
|
|
|
*ura = 15; // by default.
|
|
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
if( ura_m >= RINEX_MIN_URA[i] && ura_m < RINEX_MAX_URA[i] )
|
|
{
|
|
*ura = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL RINEX_DecodeFileName(
|
|
const char *filepath, //!< (input) A full filepath.
|
|
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.
|
|
unsigned short *dayofyear, //!< (output) The day of year.
|
|
unsigned char *file_sequence_nr, //!< (output) file sequence number within day. 0: file contains all the existing data of the current day.
|
|
unsigned short *year, //!< (output) The full year. e.g. 1999, 2001.
|
|
RINEX_enumFileType *filetype //!< (output) The RINEX file type.
|
|
)
|
|
{
|
|
char str[RINEX_LINEBUF_SIZE];
|
|
char filename[RINEX_LINEBUF_SIZE];
|
|
size_t length = 0;
|
|
char *pch = NULL;
|
|
char filetype_char = 0;
|
|
unsigned count = 0;
|
|
int itmp[3];
|
|
|
|
// Check input.
|
|
if( filepath == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( filepath == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( station_name == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( station_name == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( dayofyear == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( dayofyear == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( file_sequence_nr == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( file_sequence_nr == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( year == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( year == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( filetype == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( filetype == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
station_name[0] = station_name[1] = station_name[2] = station_name[3] = station_name[4] = '\0';
|
|
|
|
length = strlen( filepath );
|
|
if( length >= RINEX_LINEBUF_SIZE )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length >= RINEX_LINEBUF_SIZE )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Copy the file path.
|
|
strcpy( str, filepath );
|
|
|
|
// Tokenize the string copy.
|
|
pch = strtok( str,"\\/" );
|
|
while (pch != NULL)
|
|
{
|
|
strcpy( filename, pch );
|
|
pch = strtok( NULL, "\\/" );
|
|
}
|
|
|
|
length = strlen( filename );
|
|
if( length != 12 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( length != 12 )" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Decode the filename.
|
|
count = sscanf( filename, "%4c%3d%1d%*c%2d%c",
|
|
station_name,
|
|
&(itmp[0]),
|
|
&(itmp[1]),
|
|
&(itmp[2]),
|
|
&filetype_char
|
|
);
|
|
if( count != 5 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed." );
|
|
return FALSE;
|
|
}
|
|
|
|
filetype_char = (char)toupper(filetype_char);
|
|
|
|
*dayofyear = (unsigned short)itmp[0];
|
|
*file_sequence_nr = (unsigned char)itmp[1];
|
|
*year = (unsigned short)itmp[2];
|
|
|
|
|
|
if( *year >= 80 && *year < 2000 )
|
|
{
|
|
*year += 1900;
|
|
}
|
|
else if( *year >= 0 && *year < 79 )
|
|
{
|
|
*year += 2000;
|
|
}
|
|
else
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected" );
|
|
return FALSE;
|
|
}
|
|
|
|
*filetype = (RINEX_enumFileType)filetype_char;
|
|
|
|
switch( *filetype )
|
|
{
|
|
case RINEX_FILE_TYPE_OBS:
|
|
case RINEX_FILE_TYPE_GPS_NAV:
|
|
case RINEX_FILE_TYPE_MET:
|
|
case RINEX_FILE_TYPE_GLO_NAV:
|
|
case RINEX_FILE_TYPE_GEO_NAV:
|
|
case RINEX_FILE_TYPE_GAL_NAV:
|
|
case RINEX_FILE_TYPE_MIXED_NAV:
|
|
case RINEX_FILE_TYPE_SBAS:
|
|
case RINEX_FILE_TYPE_CLK:
|
|
case RINEX_FILE_TYPE_SUMMARY:
|
|
{
|
|
return TRUE;
|
|
}
|
|
default:
|
|
{
|
|
GNSS_ERROR_MSG( "unexpected default case" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL RINEX_GetKlobucharIonoParametersFromNavFile(
|
|
const char *filepath, //!< (input) The file path to the GPS Navigation message file.
|
|
GNSS_structKlobuchar *iono_model //!< (input/output) A pointer to the ionospheric parameters struct.
|
|
)
|
|
{
|
|
char RINEX_header[RINEX_HEADER_SIZE];
|
|
unsigned RINEX_header_length = 0;
|
|
double version = 0.0;
|
|
RINEX_enumFileType file_type = RINEX_FILE_TYPE_UNKNOWN;
|
|
char line_buffer[RINEX_LINEBUF_SIZE];
|
|
unsigned nr_lines = 0;
|
|
BOOL result;
|
|
double dtmp = 0.0;
|
|
|
|
char station_name[5];
|
|
unsigned short dayofyear = 0;
|
|
unsigned char file_sequence_nr = 0;
|
|
unsigned short year = 0;
|
|
|
|
double header_A0; // DELTA-UTC: A0,A1,T,W
|
|
double header_A1; // DELTA-UTC: A0,A1,T,W
|
|
int header_week; // DELTA-UTC: A0,A1,T,W
|
|
int header_tow; // DELTA-UTC: A0,A1,T,W
|
|
|
|
memset( iono_model, 0, sizeof(GNSS_structKlobuchar) );
|
|
|
|
if( filepath == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( filepath == NULL )" );
|
|
return FALSE;
|
|
}
|
|
if( iono_model == NULL )
|
|
{
|
|
GNSS_ERROR_MSG( "if( iono_model == NULL )" );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
result = RINEX_GetHeader(
|
|
filepath,
|
|
RINEX_header,
|
|
RINEX_HEADER_SIZE,
|
|
&RINEX_header_length,
|
|
&version,
|
|
&file_type
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_GetHeader returned FALSE." );
|
|
FILE *ftoold=fopen("/tmp/toold", "ab+");
|
|
if(ftoold) fclose(ftoold);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if( file_type != RINEX_FILE_TYPE_GPS_NAV )
|
|
{
|
|
GNSS_ERROR_MSG( "if( file_type != RINEX_FILE_TYPE_GPS_NAV )" );
|
|
return FALSE;
|
|
}
|
|
|
|
result = RINEX_get_header_lines(
|
|
RINEX_header,
|
|
RINEX_header_length,
|
|
"ION ALPHA",
|
|
line_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == 1 )
|
|
{
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( line_buffer, "%lf %lf %lf %lf",
|
|
&(iono_model->alpha0),
|
|
&(iono_model->alpha1),
|
|
&(iono_model->alpha2),
|
|
&(iono_model->alpha3) ) != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed" );
|
|
return FALSE; // bad header?
|
|
}
|
|
result = RINEX_get_header_lines(
|
|
RINEX_header,
|
|
RINEX_header_length,
|
|
"ION BETA",
|
|
line_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines != 1 )
|
|
{
|
|
GNSS_ERROR_MSG( "if( nr_lines != 1 )" );
|
|
return FALSE; // weird header
|
|
}
|
|
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( line_buffer, "%lf %lf %lf %lf",
|
|
&(iono_model->beta0),
|
|
&(iono_model->beta1),
|
|
&(iono_model->beta2),
|
|
&(iono_model->beta3) ) != 4 )
|
|
{
|
|
GNSS_ERROR_MSG( "sscanf failed" );
|
|
return FALSE; // bad header?
|
|
}
|
|
|
|
result = RINEX_get_header_lines(
|
|
RINEX_header,
|
|
RINEX_header_length,
|
|
"DELTA-UTC: A0,A1,T,W",
|
|
line_buffer,
|
|
RINEX_LINEBUF_SIZE,
|
|
&nr_lines
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_get_header_lines returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
if( nr_lines == 1 )
|
|
{
|
|
result = RINEX_ReplaceDwithE( line_buffer, (unsigned)strlen(line_buffer) );
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "RINEX_ReplaceDwithE returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
|
|
if( sscanf( line_buffer, "%lf %lf %d %d",
|
|
&header_A0,
|
|
&header_A1,
|
|
&header_tow,
|
|
&header_week ) == 4 )
|
|
{
|
|
iono_model->week = (unsigned short) header_week;
|
|
iono_model->tow = header_tow;
|
|
iono_model->isValid = TRUE;
|
|
}
|
|
}
|
|
|
|
if( iono_model->isValid == FALSE )
|
|
{
|
|
// Decode the year and day of year from the file name.
|
|
result = RINEX_DecodeFileName( filepath, station_name, &dayofyear, &file_sequence_nr, &year, &file_type );
|
|
if( result == TRUE )
|
|
{
|
|
result = TIMECONV_GetGPSTimeFromYearAndDayOfYear(
|
|
year,
|
|
dayofyear,
|
|
&(iono_model->week),
|
|
&dtmp
|
|
);
|
|
if( result == FALSE )
|
|
{
|
|
GNSS_ERROR_MSG( "TIMECONV_GetGPSTimeFromYearAndDayOfYear returned FALSE." );
|
|
return FALSE;
|
|
}
|
|
iono_model->tow = (unsigned)dtmp;
|
|
iono_model->isValid = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Get the Iono model time from the first ephemeris record in the file if there is one.
|
|
iono_model->isValid = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|