GNSS_RxData.cpp

Go to the documentation of this file.
00001 /**
00002 \file    GNSS_RxData.cpp
00003 \brief   The implementation file for the GNSS_RxData class.
00004 
00005 \author  Glenn D. MacGougan (GDM)
00006 \date    2007-11-29
00007 \since   2006-11-13
00008 
00009 \b "LICENSE INFORMATION" \n
00010 Copyright (c) 2007, refer to 'author' doxygen tags \n
00011 All rights reserved. \n
00012 
00013 Redistribution and use in source and binary forms, with or without
00014 modification, are permitted provided the following conditions are met: \n
00015 
00016 - Redistributions of source code must retain the above copyright
00017   notice, this list of conditions and the following disclaimer. \n
00018 - Redistributions in binary form must reproduce the above copyright
00019   notice, this list of conditions and the following disclaimer in the
00020   documentation and/or other materials provided with the distribution. \n
00021 - The name(s) of the contributor(s) may not be used to endorse or promote 
00022   products derived from this software without specific prior written 
00023   permission. \n
00024 
00025 THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 
00026 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
00027 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00028 DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00029 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00030 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
00031 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
00032 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00033 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
00034 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
00035 SUCH DAMAGE.
00036 */
00037 
00038 #include <memory.h>
00039 #include <math.h>
00040 
00041 #include "GNSS_RxData.h"
00042 #include "novatel.h" 
00043 #include "constants.h"
00044 #include "geodesy.h"
00045 #include "Matrix.h"
00046 
00047 using namespace Zenautics; // for Matrix
00048 
00049 
00050 #define GPS_NUMBER_VALID_PRNS (64)
00051 
00052 #ifndef SECONDS_IN_DAY
00053 #define SECONDS_IN_DAY (86400.0)
00054 #endif
00055 
00056 #ifndef SECONDS_IN_WEEK
00057 #define SECONDS_IN_WEEK (604800.0)
00058 #endif
00059 
00060 
00061 #ifndef WIN32
00062 #define _CRT_SECURE_NO_DEPRECATE
00063 #endif
00064 
00065 
00066 namespace GNSS
00067 {
00068 
00069   GPS_BroadcastEphemerisAndAlmanacArray::GPS_BroadcastEphemerisAndAlmanacArray()
00070   : m_array(NULL),
00071     m_arrayLength(0)
00072   { 
00073   }
00074 
00075 
00076   GPS_BroadcastEphemerisAndAlmanacArray::~GPS_BroadcastEphemerisAndAlmanacArray()
00077   {
00078     if( m_array != NULL )
00079     {
00080       delete[] m_array;
00081       m_arrayLength = 0;
00082     }
00083   }
00084 
00085   bool GPS_BroadcastEphemerisAndAlmanacArray::AllocateArray()
00086   {
00087     unsigned i = 0;
00088     if( m_array != NULL )
00089     {
00090       if( m_arrayLength == GPS_NUMBER_VALID_PRNS )
00091         return true; // already allocated
00092       else
00093         return false; // error
00094     }
00095     m_array = new GPS_structOrbitParameters[GPS_NUMBER_VALID_PRNS];
00096     if( m_array == NULL )
00097       return false;
00098     m_arrayLength = GPS_NUMBER_VALID_PRNS;
00099 
00100     // Initialize all to zero.
00101     for( i = 0; i < GPS_NUMBER_VALID_PRNS; i++ )
00102     {
00103       memset( &m_array[i], 0, sizeof(GPS_structOrbitParameters) );
00104     }
00105 
00106     return true;
00107   }
00108 
00109 
00110   bool GPS_BroadcastEphemerisAndAlmanacArray::AddEphemeris( const unsigned short prn, const GPS_structEphemeris &eph )
00111   {
00112     unsigned short index = 0;
00113 
00114     if( m_arrayLength == 0 )
00115     {
00116       if( !AllocateArray() )
00117         return false;
00118     }
00119 
00120     if( !GetIndexGivenPRN( prn, index ) )
00121       return false;
00122 
00123     // The previous ephemeris is set based on what was the current prior this update.
00124     m_array[index].previousEph = m_array[index].currentEph;
00125     m_array[index].currentEph  = eph;
00126 
00127     return true;
00128   }
00129 
00130   bool GPS_BroadcastEphemerisAndAlmanacArray::AddAlmanac( const unsigned short prn, const GPS_structAlmanac &alm )
00131   {
00132     unsigned short index = 0;
00133 
00134     if( m_arrayLength == 0 )
00135     {
00136       if( !AllocateArray() )
00137         return false;
00138     }
00139 
00140     if( !GetIndexGivenPRN( prn, index ) )
00141       return false;
00142     
00143     m_array[index].almanac = alm;
00144 
00145     return true;
00146   }
00147 
00148   bool GPS_BroadcastEphemerisAndAlmanacArray::GetEphemeris( 
00149     const unsigned short prn, //!< The desired GPS PRN. (1-32 GPS, 120-138 SBAS).
00150     GPS_structEphemeris &eph, //!< A reference to an ephemeris struct in which to store the data.
00151     bool &isAvailable,        //!< This boolean indicates if ephemeris data is available or not.
00152     char iode                 //!< The issue of data for the ephemeris, -1 means get the most current.
00153     )
00154   {
00155     unsigned short index = 0;
00156 
00157     if( m_arrayLength == 0 )
00158     {
00159       isAvailable = false;
00160       return true;
00161     }
00162     
00163     if( !GetIndexGivenPRN( prn, index ) )
00164       return false;
00165 
00166     // check the prn to see if any ephemeris information is available.
00167     if( m_array[index].currentEph.prn == 0 )
00168     {
00169       isAvailable = false;
00170       return true;
00171     }
00172 
00173     isAvailable = true;
00174     if( iode == -1 )
00175     {
00176       eph = m_array[index].currentEph;      
00177     }
00178     else
00179     {
00180       if( m_array[index].currentEph.iode == iode )
00181       {
00182         eph = m_array[index].currentEph;
00183       }
00184       else if( m_array[index].previousEph.iode == iode )
00185       {
00186         eph = m_array[index].previousEph;
00187       }
00188       else
00189       {
00190         isAvailable = false;
00191       }
00192     }
00193     return true;
00194   }
00195 
00196   bool GPS_BroadcastEphemerisAndAlmanacArray::GetEphemerisTOW( 
00197     const unsigned short prn, //!< The desired GPS PRN. (1-32 GPS, 120-138 SBAS).
00198     bool &isAvailable,        //!< This boolean indicates if ephemeris data is available or not.
00199     unsigned short &week,     //!< The correct week corresponding to the time of week based on the Z-count in the Hand Over Word.
00200     unsigned &tow             //!< The time of week based on the Z-count in the Hand Over Word.
00201     )
00202   {
00203     unsigned short index = 0;
00204     unsigned short eph_week = 0;
00205     int eph_toe = 0;
00206     int eph_tow = 0;
00207 
00208     if( m_arrayLength == 0 )
00209     {
00210       isAvailable = false;
00211       return true;
00212     }
00213     
00214     if( !GetIndexGivenPRN( prn, index ) )
00215       return false;
00216 
00217     // check the prn to see if any ephemeris information is available.
00218     if( m_array[index].currentEph.prn == 0 )
00219     {
00220       isAvailable = false;
00221       return true;
00222     }
00223 
00224     isAvailable = true;
00225     if( isAvailable )
00226     {
00227       eph_toe  = (int)m_array[index].currentEph.toe;
00228       eph_week = m_array[index].currentEph.week;
00229       eph_tow  = (int)m_array[index].currentEph.tow;
00230 
00231       // check for week rolloever condition, 
00232       // if the tow of week is different by more than four days 
00233       // compared to the time of ephemeris, then the tow is in the next week
00234       if( (eph_tow - eph_toe) < (-4*86400) )
00235       {
00236         eph_week++;
00237       }
00238 
00239       week = eph_week;
00240       tow  = eph_tow;
00241     }
00242     else
00243     {
00244       week = 0;
00245       tow = 0;
00246     }
00247     return true;
00248 
00249   }
00250 
00251   bool GPS_BroadcastEphemerisAndAlmanacArray::IsEphemerisAvailable( 
00252     const unsigned short prn, //!< The desired GPS PRN. (1-32 GPS, 120-138 SBAS).
00253     bool &isAvailable,        //!< This boolean indicates if ephemeris data is available or not.
00254     char iode                 //!< The issue of data for the ephemeris, -1 means get the most current.
00255     )
00256   {
00257     unsigned short index = 0;
00258 
00259     // Check if there is any data in the array.
00260     if( m_arrayLength == 0 )
00261     { 
00262       isAvailable = false;
00263       return true;
00264     }
00265     
00266     if( !GetIndexGivenPRN( prn, index ) )
00267       return false;
00268 
00269     // check the prn to see if any ephemeris information is available.
00270     if( m_array[index].currentEph.prn == 0 )
00271     {
00272       isAvailable = false;
00273       return true;
00274     }
00275 
00276     if( iode == -1 )
00277     {
00278       isAvailable = true;
00279     }
00280     else
00281     {
00282       if( m_array[index].currentEph.iode == iode )
00283       {
00284         isAvailable = true;
00285       }
00286       else if( m_array[index].previousEph.iode == iode )
00287       {
00288         isAvailable = true;
00289       }
00290       else
00291       {
00292         isAvailable = false;
00293       }
00294     }
00295     return true;
00296   }
00297 
00298 
00299 
00300   bool GPS_BroadcastEphemerisAndAlmanacArray::GetIndexGivenPRN( const unsigned short prn, unsigned short &index )
00301   {
00302     // GPS 1-32
00303     // Pseudolites are 33-37
00304     // SBAS is 120-138
00305     // WAAS, EGNOS, MSAS
00306     //
00307     // WAAS:
00308     // AOR-W       122
00309     // Anik        138
00310     // POR         134
00311     // PanAm       135
00312     //
00313     // EGNOS:
00314     // AOR-E       120
00315     // Artemis     124
00316     // IOR-W       126
00317     // IOR-E       131
00318     //
00319     // MSAS:
00320     // MTSAT-1     129
00321     // MTSAT-2     137
00322     //
00323     // The index mapping is as follows:
00324     // PRN 1-37    maps to indidex 0-36
00325     // PRN 38-40   maps to indices 37-39 (reserved mappings)
00326     // PRN 120-138 maps to indicex 40-58
00327     
00328 
00329     // check unsupported prn values
00330     if( prn == 0 )
00331     {
00332       return false;
00333     }
00334     if( prn > 38 && prn < 120 )
00335     {
00336       return false;
00337     }
00338     if( prn > 138 )
00339     {
00340       return false;
00341     }
00342     
00343     if( prn < 38 )
00344     {
00345       index = prn - 1;
00346     }
00347     if( prn > 119 && prn < 139 )
00348     {
00349       index = prn - 80;
00350     }
00351     return true;
00352   }
00353 
00354 
00355 
00356 
00357 
00358   GNSS_RxData::GNSS_RxData()
00359   : m_nrValidObs(0), 
00360     m_prev_nrValidObs(0),
00361     m_elevationMask(5.0*DEG2RAD),
00362     m_cnoMask(28.0),
00363     m_locktimeMask(0.0),
00364     m_maxAgeEphemeris(14400), // 4 hours
00365     m_DisableTropoCorrection(false),
00366     m_DisableIonoCorrection(false),
00367     m_fid(NULL),
00368     m_messageLength(0),
00369     m_rxDataType(GNSS_RXDATA_UNKNOWN),
00370     m_CheckRinexObservationHeader(false),
00371     m_RINEX_use_eph(false),
00372     m_RINEX_eph_index(0)
00373   { 
00374     m_message[0] = '\0';
00375     ZeroAllMeasurements();
00376     ZeroPVT();
00377 
00378     memset( &m_klobuchar, 0, sizeof(GNSS_structKlobuchar) );
00379     memset( &m_RINEX_obs_header, 0, sizeof(RINEX_structDecodedHeader) );
00380     
00381     m_RINEX_eph.eph_array = NULL;
00382     m_RINEX_eph.array_length = 0;
00383     m_RINEX_eph.max_array_length = 0;
00384   }
00385 
00386 
00387   GNSS_RxData::~GNSS_RxData()
00388   {
00389     if( m_fid != NULL )
00390     {
00391       fclose( m_fid );
00392     }
00393     if( m_RINEX_eph.eph_array != NULL )
00394     {
00395       delete[] m_RINEX_eph.eph_array;
00396     }
00397   }
00398 
00399 
00400   bool GNSS_RxData::ZeroAllMeasurements()
00401   {
00402     unsigned i = 0;
00403 
00404     m_nrValidObs = 0;
00405 
00406     for( i = 0; i < GNSS_RXDATA_NR_CHANNELS; i++ )
00407     {
00408       memset( &(m_ObsArray[i]), 0, sizeof(GNSS_structMeasurement) );
00409       memset( &(m_prev_ObsArray[i]), 0, sizeof(GNSS_structMeasurement) );
00410     }
00411     return true;
00412   }
00413 
00414 
00415   bool GNSS_RxData::ZeroPVT()
00416   {
00417     memset( &m_pvt, 0, sizeof(GNSS_structPVT) );
00418     memset( &m_prev_pvt, 0, sizeof(GNSS_structPVT) );
00419     return true;
00420   }
00421 
00422 
00423   bool GNSS_RxData::Initialize( 
00424     const char* path,                  //!< The path to the Observation data file. 
00425     bool &isValidPath,                 //!< A boolean to indicate if the path is valid.
00426     const GNSS_enumRxDataType rxType,  //!< The receiver data type.
00427     const char* RINEX_ephemeris_path   //!< The path to a RINEX ephemeris file, NULL if not available.
00428     )
00429   {
00430     bool isRinexValid = false;
00431     isValidPath = false;
00432     
00433     if( path == NULL )
00434     {
00435       return false;
00436     }
00437     if( rxType == GNSS_RXDATA_UNKNOWN )
00438     {
00439       return false;
00440     }
00441 
00442     m_rxDataType = rxType;
00443     
00444     if( rxType == GNSS_RXDATA_RINEX21 || rxType == GNSS_RXDATA_RINEX211 )
00445     {
00446       if( !CheckRINEXObservationHeader( path, isRinexValid ) )
00447         return false;
00448       if( !isRinexValid )
00449         return false;
00450     }
00451 
00452     if( RINEX_ephemeris_path != NULL )
00453     {
00454       m_RINEX_eph.filepath = RINEX_ephemeris_path;
00455       
00456       if( !LoadRINEXNavigationData() )
00457         return false;
00458 
00459       // Indicate that the RINEX ephemeris data can be used.
00460       m_RINEX_use_eph = true;
00461     }
00462 
00463 #ifndef _CRT_SECURE_NO_DEPRECATE
00464     if( fopen_s( &m_fid, path, "rb" ) != 0 )
00465       return false;
00466 #else
00467     m_fid = fopen( path, "rb" );
00468 #endif
00469     if( m_fid == NULL )    
00470       return false;
00471 
00472 
00473     if( rxType == GNSS_RXDATA_RINEX21 || rxType == GNSS_RXDATA_RINEX211 )
00474     {
00475       // advance over the header lines
00476       while( !feof(m_fid) && ferror(m_fid)==0 )
00477       {
00478         if( fgets( (char*)m_message, GNSS_RXDATA_MSG_LENGTH, m_fid ) == NULL )
00479           break;
00480         if( strstr( (char*)m_message, "END OF HEADER" ) != NULL )
00481           break;
00482       }
00483     }
00484 
00485     if( feof(m_fid) || ferror(m_fid) != 0 )
00486     {
00487       if( m_fid != NULL )
00488         fclose(m_fid);
00489       return false;
00490     }
00491     
00492     isValidPath = true;
00493 
00494     return true;
00495   }
00496 
00497   bool GNSS_RxData::LoadNext( bool &endOfStream )
00498   {
00499     bool result = false;
00500     switch( m_rxDataType )
00501     {
00502     case GNSS_RXDATA_NOVATELOEM4:
00503       {
00504         result = LoadNext_NOVATELOEM4( endOfStream );
00505         break;
00506       }
00507     case GNSS_RXDATA_RINEX21:
00508       {
00509         result = LoadNext_RINEX21( endOfStream );
00510         break;
00511       }
00512     case GNSS_RXDATA_RINEX211:
00513       {
00514         result = LoadNext_RINEX211( endOfStream );
00515         break;
00516       }
00517     default:
00518       {
00519         return false;
00520         break;
00521       }
00522     }
00523 
00524     if( result )
00525     {
00526       if( m_RINEX_use_eph )
00527       {
00528         if( !UpdateTheEphemerisArrayWithUsingRINEX() )
00529           return false;
00530       }
00531     }
00532     return result;
00533   }
00534 
00535   bool GNSS_RxData::CheckRINEXObservationHeader( const char *filepath, bool &isValid )
00536   {
00537     BOOL result;
00538     char RINEX_buffer[16384];
00539     unsigned RINEX_buffer_size = 0;
00540     double version = 0.0;
00541     RINEX_enumFileType file_type = RINEX_FILE_TYPE_UNKNOWN;
00542 
00543     isValid = false;
00544 
00545     if( filepath == NULL )
00546       return false;    
00547     
00548     result = RINEX_GetHeader( 
00549       filepath,
00550       RINEX_buffer,
00551       16384,
00552       &RINEX_buffer_size,
00553       &version,
00554       &file_type
00555     );
00556     if( result == FALSE )
00557       return false;
00558 
00559     result = RINEX_DecodeHeader_ObservationFile(
00560       RINEX_buffer,
00561       RINEX_buffer_size,
00562       &m_RINEX_obs_header
00563       );
00564     if( result == FALSE )
00565       return false;
00566 
00567     if( file_type != RINEX_FILE_TYPE_OBS )
00568     {
00569       isValid = false;
00570       return true;
00571     }
00572 
00573     if( m_rxDataType == GNSS_RXDATA_RINEX21 )
00574     {
00575       if( fabs( version - 2.1 ) < 1e-02 )
00576         isValid = true;
00577     }
00578     else if( m_rxDataType == GNSS_RXDATA_RINEX211 )
00579     {
00580       if( fabs( version - 2.11 ) < 1e-03 )
00581         isValid = true;
00582     }
00583     else
00584     {
00585       isValid = false;
00586     }
00587     return true;
00588   }
00589 
00590   bool GNSS_RxData::LoadNext_RINEX21( bool &endOfStream )
00591   {
00592     /*
00593     BOOL result;
00594     unsigned i = 0;
00595 
00596     FILE* fid = NULL;
00597     BOOL wasEndOfFileReached;  // Has the end of the file been reached (output).
00598     BOOL wasObservationFound;  // Was a valid observation found (output).
00599     unsigned filePosition=0;   // The file position for the start of the 
00600     unsigned nrObs = 0;
00601     
00602     */
00603     return true;
00604   }
00605 
00606   bool GNSS_RxData::LoadNext_RINEX211( bool &endOfStream )
00607   {
00608     BOOL result=0;
00609     unsigned i = 0;
00610     unsigned j = 0;
00611     unsigned short rx_gps_week = 0;
00612     double rx_gps_tow = 0.0;     
00613 
00614     BOOL wasEndOfFileReached=0;  // Has the end of the file been reached (output).
00615     BOOL wasObservationFound=0;  // Was a valid observation found (output).
00616     unsigned filePosition=0;   // The file position for the start of the 
00617     unsigned nrObs = 0;
00618     bool isAvailable = false;
00619     
00620     endOfStream = false;
00621 
00622     if( m_fid == NULL )
00623       return false;
00624 
00625     // Copy the current observations into the previous storage.
00626     m_prev_nrValidObs = m_nrValidObs;
00627     for( i = 0; i < m_nrValidObs; i++ )
00628     {
00629       m_prev_ObsArray[i] = m_ObsArray[i];
00630       memset( &(m_ObsArray[i]), 0, sizeof(GNSS_structMeasurement) ); // Initialize to zero.
00631     }
00632 
00633     // Get the next observation set.
00634     result = RINEX_GetNextObservationSet(
00635       m_fid,
00636       &m_RINEX_obs_header,
00637       &wasEndOfFileReached,
00638       &wasObservationFound,
00639       &filePosition,
00640       m_ObsArray,
00641       GNSS_RXDATA_NR_CHANNELS,
00642       &nrObs,
00643       &rx_gps_week,
00644       &rx_gps_tow
00645       );
00646     if( result == FALSE )
00647       return false;
00648 
00649     if( wasEndOfFileReached )
00650     {
00651       endOfStream = true;
00652       return true;
00653     }
00654 
00655     m_nrGPSL1Obs = 0;
00656     m_nrValidObs = 0;
00657     if( nrObs > 0 )
00658     {
00659       // Set the receiver time of the observation set.
00660       m_pvt.time.gps_week = rx_gps_week;
00661       m_pvt.time.gps_tow  = rx_gps_tow;
00662 
00663       for(i = 0; i < nrObs; i++ )
00664       {
00665         m_ObsArray[i].stdev_psr     = 1.4f;  // [m]
00666         m_ObsArray[i].stdev_adr     = 0.05f; // these are in cycles!.
00667         m_ObsArray[i].stdev_doppler = 0.5f;  // Hz
00668 
00669         // Check if ephemeris information is available
00670         if( !m_EphAlmArray.IsEphemerisAvailable( m_ObsArray[i].id, isAvailable ) )
00671           return false;
00672         m_ObsArray[i].flags.isEphemerisValid      = isAvailable;
00673 
00674         if( m_DisableTropoCorrection )
00675           m_ObsArray[i].flags.useTropoCorrection          = 0;
00676         else
00677           m_ObsArray[i].flags.useTropoCorrection          = 1; // defaults to yes
00678 
00679         if( m_DisableIonoCorrection )
00680           m_ObsArray[i].flags.useBroadcastIonoCorrection  = 0;
00681         else
00682           m_ObsArray[i].flags.useBroadcastIonoCorrection  = 1; // default to yes
00683 
00684         if( m_ObsArray[i].system == GNSS_GPS &&
00685           m_ObsArray[i].freqType == GNSS_GPSL1 )
00686         {
00687           m_nrGPSL1Obs++;
00688         }
00689         m_nrValidObs++;
00690       }
00691     }
00692 
00693 
00694     // Search for matching observations in the previous set of data to pass on static information
00695     // like ambiguities.
00696     for( i = 0; i < m_nrValidObs; i++ )
00697     {
00698       for( j = 0; j < m_prev_nrValidObs; j++ )
00699       {
00700         if( m_ObsArray[i].codeType == m_prev_ObsArray[j].codeType &&
00701           m_ObsArray[i].freqType == m_prev_ObsArray[j].freqType &&
00702           m_ObsArray[i].system == m_prev_ObsArray[j].system &&
00703           m_ObsArray[i].id == m_prev_ObsArray[j].id ) // Note should also check that channels are the same if real time!
00704         {
00705           m_ObsArray[i].ambiguity = m_prev_ObsArray[j].ambiguity;
00706           break;
00707         }
00708       }
00709     }
00710 
00711     return true;
00712   }  
00713 
00714   bool GNSS_RxData::LoadNext_NOVATELOEM4( bool &endOfStream )
00715   {
00716     BOOL result = FALSE;
00717     BOOL wasEndOfFileReached = FALSE;
00718     BOOL wasMessageFound = FALSE;
00719     unsigned filePosition = 0;
00720     unsigned short messageID = 0;
00721     NOVATELOEM4_enumMessageType messageType;
00722     unsigned numberBadCRC = 0;
00723 
00724     //  A NovAtel OEM4 header information struct.
00725     NOVATELOEM4_structBinaryHeader header; 
00726 
00727     // An array of struct_NOVATELOEM4_RANGE.
00728     NOVATELOEM4_structObservation obsArray[GNSS_RXDATA_NR_CHANNELS];
00729 
00730     // A gps ephemeris struct.
00731     GPS_structEphemeris eph;
00732     unsigned prn;            // The PRN.
00733     unsigned reference_week; // The ephemeris reference week.
00734     unsigned reference_tow;  // The ephemeris reference time of week.
00735     unsigned tow;            // The tow associated with the start of Subframe1.
00736 
00737     unsigned nrValidObs;
00738     unsigned i = 0;
00739     unsigned j = 0;
00740     bool isAvailable;  // A boolean used in checking if ephemeris is available.
00741 
00742 
00743     endOfStream = false;
00744 
00745     if( m_fid == NULL )
00746       return false;
00747 
00748     // Copy the current observations into the previous storage.
00749     m_prev_nrValidObs = m_nrValidObs;
00750     for( i = 0; i < m_nrValidObs; i++ )
00751     {
00752       m_prev_ObsArray[i] = m_ObsArray[i];
00753       memset( &(m_ObsArray[i]), 0, sizeof(GNSS_structMeasurement) ); // Initialize to zero.
00754     }
00755 
00756     switch( m_rxDataType )
00757     {
00758     case GNSS_RXDATA_NOVATELOEM4:
00759       {  
00760         while( !wasEndOfFileReached && !wasMessageFound )
00761         {
00762           result = NOVATELOEM4_FindNextMessageInFile( 
00763             m_fid,
00764             m_message,
00765             GNSS_RXDATA_MSG_LENGTH,
00766             &wasEndOfFileReached,
00767             &wasMessageFound,
00768             &filePosition,
00769             &m_messageLength,
00770             &messageID,
00771             &numberBadCRC 
00772             );
00773           if( result == FALSE )
00774             return false;
00775           
00776           messageType = (NOVATELOEM4_enumMessageType)messageID;
00777           if( wasMessageFound && messageType != NOVATELOEM4_RANGEB )
00778           { 
00779             if( messageType == NOVATELOEM4_RAWEPHEMB )
00780             {
00781               memset( &eph, 0, sizeof(GPS_structEphemeris) );
00782              
00783               result = NOVATELOEM4_DecodeRAWEPHEMB(
00784                 m_message,
00785                 m_messageLength,
00786                 &header,
00787                 &prn,
00788                 &reference_week,
00789                 &reference_tow,
00790                 &tow,
00791                 &eph.iodc,
00792                 &eph.iode,
00793                 &eph.toe,
00794                 &eph.toc,
00795                 &eph.week,
00796                 &eph.health,
00797                 &eph.alert_flag,
00798                 &eph.anti_spoof,
00799                 &eph.code_on_L2,
00800                 &eph.ura,
00801                 &eph.L2_P_data_flag,
00802                 &eph.fit_interval_flag,
00803                 &eph.age_of_data_offset,
00804                 &eph.tgd,
00805                 &eph.af2,
00806                 &eph.af1,
00807                 &eph.af0,
00808                 &eph.m0,
00809                 &eph.delta_n,
00810                 &eph.ecc,
00811                 &eph.sqrta,
00812                 &eph.omega0,
00813                 &eph.i0,
00814                 &eph.w,
00815                 &eph.omegadot,
00816                 &eph.idot,
00817                 &eph.cuc,
00818                 &eph.cus,
00819                 &eph.crc,
00820                 &eph.crs,
00821                 &eph.cic,
00822                 &eph.cis );
00823               if( result )
00824               {
00825                 eph.prn = prn;
00826                 
00827                 result = m_EphAlmArray.AddEphemeris( eph.prn, eph );
00828                 if( !result )
00829                 {
00830                   // An error occurred.
00831                   return false;
00832                 }
00833               }
00834             }
00835             wasMessageFound = false;
00836           }
00837         }
00838         if( wasMessageFound && messageType == NOVATELOEM4_RANGEB )
00839         {
00840           m_nrValidObs = 0;
00841           m_nrGPSL1Obs = 0;
00842           
00843           result = NOVATELOEM4_DecodeRANGEB( 
00844             m_message,
00845             m_messageLength,
00846             &header,
00847             obsArray,
00848             GNSS_RXDATA_NR_CHANNELS,
00849             &nrValidObs 
00850             );
00851           if( result == FALSE )
00852             return false;
00853 
00854           // Set the receiver time of the observation set.
00855           m_pvt.time.gps_week = header.gpsWeek;
00856           m_pvt.time.gps_tow  = header.gpsMilliSeconds / 1000.0;
00857 
00858           for( i = 0; i < nrValidObs && i < GNSS_RXDATA_NR_CHANNELS; i++ )
00859           {
00860             m_ObsArray[i].channel   = obsArray[i].trackingStatus.channelNumber;
00861             m_ObsArray[i].id        = obsArray[i].prn;
00862 
00863             m_ObsArray[i].system    = (GNSS_enumSystem)obsArray[i].trackingStatus.eSatelliteSystem;
00864             m_ObsArray[i].codeType  = (GNSS_enumCodeType)obsArray[i].trackingStatus.eCodeType;
00865             m_ObsArray[i].freqType  = (GNSS_enumFrequency)obsArray[i].trackingStatus.eFrequency;
00866 
00867             if( m_ObsArray[i].system == GNSS_GPS &&
00868               m_ObsArray[i].freqType == GNSS_GPSL1 )
00869             {
00870               m_nrGPSL1Obs++;
00871             }
00872 
00873             m_ObsArray[i].psr       = obsArray[i].psr;
00874             m_ObsArray[i].adr       = -1.0*obsArray[i].adr;
00875             m_ObsArray[i].doppler   = obsArray[i].doppler;
00876             m_ObsArray[i].cno       = obsArray[i].cno;
00877             m_ObsArray[i].locktime  = obsArray[i].locktime;
00878     
00879             if( obsArray[i].psrstd < 0.2 )
00880               m_ObsArray[i].stdev_psr     = 0.2f;
00881             else
00882               m_ObsArray[i].stdev_psr     = obsArray[i].psrstd; 
00883 
00884             m_ObsArray[i].stdev_psr     = 1.4f;
00885             
00886             if( obsArray[i].adrstd < 0.0025 )
00887               m_ObsArray[i].stdev_adr     = 0.0025f; // these are in cycles!.              
00888             else
00889               m_ObsArray[i].stdev_adr     = obsArray[i].adrstd; // these are in cycles!.
00890 
00891             if( obsArray[i].adrstd < 0.0001 )
00892               int gaa = 99;
00893 
00894             m_ObsArray[i].stdev_adr     = 0.05f; // these are in cycles!.
00895             
00896             m_ObsArray[i].stdev_doppler = 0.5f; // Hz
00897 
00898             m_ObsArray[i].psr_smoothed      = 0.0;
00899             m_ObsArray[i].psr_predicted     = 0.0;
00900             m_ObsArray[i].doppler_predicted = 0.0;
00901             m_ObsArray[i].azimuthRads       = 0.0;
00902             m_ObsArray[i].elevationRads     = 0.0;
00903 
00904             m_ObsArray[i].flags.isActive = 1;
00905             m_ObsArray[i].flags.isCodeLocked   = obsArray[i].trackingStatus.isCodeLocked;
00906             m_ObsArray[i].flags.isPhaseLocked  = obsArray[i].trackingStatus.isPhaseLocked;
00907             m_ObsArray[i].flags.isParityValid  = obsArray[i].trackingStatus.isParityKnown;
00908             m_ObsArray[i].flags.isPsrValid     = obsArray[i].trackingStatus.isCodeLocked;
00909             m_ObsArray[i].flags.isAdrValid     = obsArray[i].trackingStatus.isPhaseLocked & obsArray[i].trackingStatus.isParityKnown;
00910             m_ObsArray[i].flags.isDopplerValid = obsArray[i].trackingStatus.isCodeLocked;            
00911             m_ObsArray[i].flags.isGrouped      = obsArray[i].trackingStatus.isGrouped;
00912             m_ObsArray[i].flags.isAutoAssigned = !obsArray[i].trackingStatus.isForcedAssignment;
00913             m_ObsArray[i].flags.isCarrierSmoothed     = 0; // not yet known
00914 
00915             // Check if ephemeris information is available
00916             if( !m_EphAlmArray.IsEphemerisAvailable( m_ObsArray[i].id, isAvailable ) )
00917               return false;
00918 
00919             m_ObsArray[i].flags.isEphemerisValid      = isAvailable;
00920 
00921             m_ObsArray[i].flags.isAlmanacValid          = 0; // not yet known
00922             m_ObsArray[i].flags.isAboveElevationMask    = 0; // not yet known
00923             m_ObsArray[i].flags.isAboveCNoMask          = 0; // not yet known
00924             m_ObsArray[i].flags.isAboveLockTimeMask     = 0; // not yet known
00925             m_ObsArray[i].flags.isNotUserRejected       = 1; // assume not rejected
00926             m_ObsArray[i].flags.isNotPsrRejected        = 1; // assume not rejected
00927             m_ObsArray[i].flags.isNotAdrRejected        = 1; // assume not rejected
00928             m_ObsArray[i].flags.isNotDopplerRejected    = 1; // assume not rejected
00929             m_ObsArray[i].flags.isNoCycleSlipDetected   = 1; // assume no slip
00930             m_ObsArray[i].flags.isPsrUsedInSolution     = 0; // not yet known
00931             m_ObsArray[i].flags.isDopplerUsedInSolution = 0; // not yet known
00932             m_ObsArray[i].flags.isAdrUsedInSolution     = 0; // not yet known
00933 
00934             if( m_DisableTropoCorrection )
00935               m_ObsArray[i].flags.useTropoCorrection          = 0;
00936             else
00937               m_ObsArray[i].flags.useTropoCorrection          = 1; // defaults to yes
00938 
00939             if( m_DisableIonoCorrection )
00940               m_ObsArray[i].flags.useBroadcastIonoCorrection  = 0;
00941             else
00942               m_ObsArray[i].flags.useBroadcastIonoCorrection  = 1; // default to yes
00943             
00944             m_ObsArray[i].corrections.prcTropoDry = 0;
00945             m_ObsArray[i].corrections.prcTropoWet = 0;
00946             m_ObsArray[i].corrections.prcIono = 0;
00947             m_ObsArray[i].corrections.prcSatClk = 0;
00948             m_ObsArray[i].corrections.prcReserved1 = 0;
00949             m_ObsArray[i].corrections.prcReserved2 = 0;
00950             m_ObsArray[i].corrections.rrcSatClkDrift = 0;
00951             m_ObsArray[i].corrections.rrcReserved1 = 0;
00952             m_ObsArray[i].corrections.rrcReserved2 = 0;
00953             m_ObsArray[i].corrections.dX = 0;
00954             m_ObsArray[i].corrections.dY = 0;
00955             m_ObsArray[i].corrections.dZ = 0;
00956             
00957             m_ObsArray[i].residuals.psrResidual = 0;
00958             m_ObsArray[i].residuals.adrResidual = 0;
00959             m_ObsArray[i].residuals.dopplerResidual = 0;
00960             m_ObsArray[i].residuals.reserved = 0;
00961 
00962             m_nrValidObs++;
00963           }
00964           
00965         }
00966         if( wasEndOfFileReached )
00967         {
00968           endOfStream = true;
00969         }
00970         break;
00971       }
00972     default:
00973       {
00974         return false;
00975       }
00976     }
00977 
00978 
00979     // Search for matching observations in the previous set of data to pass on static information
00980     // like ambiguities.
00981     for( i = 0; i < m_nrValidObs; i++ )
00982     {
00983       for( j = 0; j < m_prev_nrValidObs; j++ )
00984       {
00985         if( m_ObsArray[i].codeType == m_prev_ObsArray[j].codeType &&
00986           m_ObsArray[i].freqType == m_prev_ObsArray[j].freqType &&
00987           m_ObsArray[i].system == m_prev_ObsArray[j].system &&
00988           m_ObsArray[i].id == m_prev_ObsArray[j].id ) // Note should also check that channels are the same if real time!
00989         {
00990           m_ObsArray[i].ambiguity = m_prev_ObsArray[j].ambiguity;
00991           break;
00992         }
00993       }
00994     }
00995 
00996 
00997     return true;
00998   }
00999 
01000   bool GNSS_RxData::SetInitialPVT( 
01001     const double latitudeRads,   //!< The latitude [rad].
01002     const double longitudeRads,  //!< The longitude [rad].
01003     const double height,         //!< The orthometric height [m].
01004     const double vn,             //!< The northing velocity [m/s].
01005     const double ve,             //!< The easting velocity [m/s].
01006     const double vup,            //!< The up velocity [m/s].
01007     const double clk,            //!< The clock offset [m].
01008     const double clkdrift,       //!< The clock drift [m/s].
01009     const double std_lat,        //!< The standard deviation uncertainty in the latitude [m].
01010     const double std_lon,        //!< The standard deviation uncertainty in the longitude [m].
01011     const double std_hgt,        //!< The standard deviation uncertainty in the height [m].
01012     const double std_vn,         //!< The standard deviation uncertainty in the northing velocity [m/s].
01013     const double std_ve,         //!< The standard deviation uncertainty in the easting velocity [m/s].
01014     const double std_vup,        //!< The standard deviation uncertainty in the up velocity [m/s].
01015     const double std_clk,        //!< The standard deviation uncertainty in the clock offset [m].
01016     const double std_clkdrift,   //!< The standard deviation uncertainty in the clock drift [m/s].
01017     const double undulation      //!< The undulation if known [m].
01018     )
01019   {
01020     bool result;
01021 
01022     result = UpdatePositionAndRxClock(
01023       latitudeRads,
01024       longitudeRads,
01025       height,
01026       clk,
01027       std_lat,
01028       std_lon,
01029       std_hgt,
01030       std_clk );
01031     if( !result )
01032       return false;
01033 
01034     result = UpdateVelocityAndClockDrift(
01035       vn,
01036       ve,
01037       vup,
01038       clkdrift,
01039       std_vn,
01040       std_ve,
01041       std_vup,
01042       std_clkdrift );
01043     if( !result )
01044       return false;
01045 
01046     //m_pvt.dop;     //!< All the associated DOP information for this solution.
01047 
01048     if( std_lat == 0.0 && std_lon == 0.0 && std_hgt == 0.0 )
01049     {
01050       m_pvt.std_lat = 1e-03;
01051       m_pvt.std_lon = 1e-03;
01052       m_pvt.std_hgt = 1e-03;      
01053       m_pvt.isPositionConstrained = 1;
01054     }
01055 
01056     m_pvt.isHeightConstrained = 0;
01057     m_pvt.isClockConstrained = 0;
01058     m_pvt.isSolutionBasedOnEphemeris = 0;
01059 
01060     m_pvt.undulation = undulation;
01061 
01062     // Set the previous pvt as well.
01063     m_prev_pvt = m_pvt;
01064     
01065     return true;
01066   }
01067 
01068 
01069   bool GNSS_RxData::UpdatePositionAndRxClock( 
01070     const double latitudeRads,   //!< The latitude [rad].
01071     const double longitudeRads,  //!< The longitude [rad].
01072     const double height,         //!< The orthometric height [m].
01073     const double clk,            //!< The clock offset [m].
01074     const double std_lat,        //!< The standard deviation uncertainty in the latitude [m].
01075     const double std_lon,        //!< The standard deviation uncertainty in the longitude [m].
01076     const double std_hgt,        //!< The standard deviation uncertainty in the height [m].
01077     const double std_clk         //!< The standard deviation uncertainty in the clock offset [m].
01078     )
01079   {
01080     bool result;
01081     m_pvt.latitude   = latitudeRads;
01082     m_pvt.longitude  = longitudeRads;
01083     m_pvt.height     = height;
01084     
01085     m_pvt.latitudeDegs  = latitudeRads*RAD2DEG;
01086     m_pvt.longitudeDegs = longitudeRads*RAD2DEG;
01087 
01088     result = GetDMS( 
01089       m_pvt.latitudeDegs, 
01090       m_pvt.lat_dms.degrees, 
01091       m_pvt.lat_dms.minutes, 
01092       m_pvt.lat_dms.seconds, 
01093       (char*)m_pvt.lat_dms.dms_str, 24 );
01094     if( result == false )
01095       return false;
01096 
01097     result = GetDMS( 
01098       m_pvt.longitudeDegs, 
01099       m_pvt.lon_dms.degrees, 
01100       m_pvt.lon_dms.minutes, 
01101       m_pvt.lon_dms.seconds, 
01102       (char*)m_pvt.lon_dms.dms_str, 24 );
01103     if( result == false )
01104       return false;
01105 
01106     if( GEODESY_ConvertGeodeticCurvilinearToEarthFixedCartesianCoordinates(
01107       GEODESY_REFERENCE_ELLIPSE_WGS84,
01108       m_pvt.latitude,
01109       m_pvt.longitude,
01110       m_pvt.height,
01111       &m_pvt.x,
01112       &m_pvt.y,
01113       &m_pvt.z ) == FALSE )
01114     {
01115       return false;
01116     }
01117     m_pvt.clockOffset = clk;
01118 
01119     m_pvt.std_lat = std_lat;
01120     m_pvt.std_lon = std_lon;
01121     m_pvt.std_hgt = std_hgt;
01122     m_pvt.std_clk = std_clk;
01123     
01124     return true;
01125   }
01126 
01127   bool GNSS_RxData::UpdateVelocityAndClockDrift( 
01128     const double vn,             //!< The northing velocity [m/s].
01129     const double ve,             //!< The easting velocity [m/s].
01130     const double vup,            //!< The up velocity [m/s].
01131     const double clkdrift,       //!< The clock drift [m/s].
01132     const double std_vn,         //!< The standard deviation uncertainty in the northing velocity [m/s].
01133     const double std_ve,         //!< The standard deviation uncertainty in the easting velocity [m/s].
01134     const double std_vup,        //!< The standard deviation uncertainty in the up velocity [m/s].
01135     const double std_clkdrift    //!< The standard deviation uncertainty in the clock drift [m/s].
01136     )
01137   {
01138     m_pvt.vn  = vn;
01139     m_pvt.ve  = ve;
01140     m_pvt.vup = vup;
01141 
01142     GEODESY_RotateVectorFromLocalGeodeticFrameToEarthFixedFrame(
01143       m_pvt.latitude,
01144       m_pvt.longitude,
01145       m_pvt.vn,
01146       m_pvt.ve,
01147       m_pvt.vup,
01148       &m_pvt.vx,
01149       &m_pvt.vy,
01150       &m_pvt.vz );
01151     
01152     m_pvt.clockDrift = clkdrift;
01153 
01154     m_pvt.std_vn  = std_vn;
01155     m_pvt.std_ve  = std_ve;
01156     m_pvt.std_vup = std_vup;
01157     m_pvt.std_clkdrift  = std_clkdrift;
01158 
01159     return true;
01160   }
01161 
01162 
01163 
01164   //static 
01165   bool GNSS_RxData::GetDMS( 
01166     const double angleDegs,  //!< The angle [degrees].
01167     short &degrees,          //!< The degrees part.
01168     short &minutes,          //!< The minutes part.
01169     float &seconds,          //!< The seconds part.
01170     char *dms_str,           //!< A DMS string e.g. dms_str = "-180'59'59.9999\""
01171     const unsigned maxLength_dms_str //!< The maximum length of the dms_str string.
01172     )
01173   {
01174     bool isNegative = false;
01175     double angle = angleDegs;
01176     int result = 0;
01177 
01178     if( angleDegs < 0 )
01179     {
01180       isNegative = true;
01181       angle = -angleDegs;
01182     }
01183     else
01184     {
01185       angle = angleDegs;
01186     }
01187 
01188     degrees = static_cast<short>(floor(angle));
01189     minutes = static_cast<short>(floor((angle - degrees)*60.0));
01190     seconds = static_cast<float>((angle - degrees - minutes/60.0)*3600.0);
01191 
01192     if( isNegative )
01193       degrees = -degrees;
01194 
01195 #ifndef _CRT_SECURE_NO_DEPRECATE
01196     result = sprintf_s( 
01197       dms_str, 
01198       maxLength_dms_str, 
01199       "%+4d%c%02d\'%02.4f\"", 
01200       degrees, 
01201       176, // the degrees symbol
01202       minutes,
01203       seconds );
01204 #else
01205     result = sprintf( 
01206       dms_str,     
01207       "%+4d%c%02d\'%02.4f\"", 
01208       degrees, 
01209       176, // the degrees symbol
01210       minutes,
01211       seconds );
01212 #endif
01213 
01214     if( result == -1 )
01215       return false;
01216 
01217     return true;
01218   }
01219 
01220 
01221   bool GNSS_RxData::CheckForCycleSlips_UsingPhaseRatePrediction( 
01222     const double nrThresholdCycles //!< The maximum number of cycles to use as the threshold to detect a slip [cycles].
01223     )
01224   {
01225     unsigned i = 0;
01226     unsigned j = 0;
01227     double t_prev = 0.0;
01228     double t = 0.0;
01229     double dt = 0.0;
01230     double phase_diff = 0.0; // The difference between the predicted phase and the measured phase.
01231     double predicted_phase = 0.0;
01232     double mean_doppler = 0.0;
01233 
01234     static double max_dif = 0.0;
01235 
01236     t_prev = m_prev_pvt.time.gps_week*SECONDS_IN_WEEK + m_prev_pvt.time.gps_tow;
01237     t = m_pvt.time.gps_week*SECONDS_IN_WEEK + m_pvt.time.gps_tow;
01238     dt = t - t_prev;
01239     if( dt <= 0.0 )
01240     {
01241       return false;
01242     }
01243     if( dt > 60.0 ) // The phase rate prediction method is not going to work well.
01244     {
01245       return true;
01246     }
01247             
01248     for( i = 0; i < m_nrValidObs; i++ )
01249     {
01250       for( j = 0; j < m_prev_nrValidObs; j++ )
01251       {
01252         if( m_ObsArray[i].system == m_prev_ObsArray[j].system &&
01253           m_ObsArray[i].id == m_prev_ObsArray[j].id &&
01254           m_ObsArray[i].freqType == m_prev_ObsArray[j].freqType )
01255         {
01256           // channel matching is not perforned.
01257           // This should be done if this a real time receiver data.
01258           // However, since the data source is generally not known for
01259           // post processing software, it is not performed here.
01260           if( m_ObsArray[i].flags.isPhaseLocked && m_prev_ObsArray[j].flags.isPhaseLocked )
01261           {
01262             mean_doppler = (m_ObsArray[i].doppler + m_prev_ObsArray[j].doppler)/2.0;
01263 
01264             predicted_phase = m_prev_ObsArray[j].adr - mean_doppler * dt; // GDM_BEWARE Doppler sign convention
01265 
01266             phase_diff = predicted_phase - m_ObsArray[i].adr;
01267 
01268             if( fabs(phase_diff) > max_dif )
01269               max_dif = fabs(phase_diff);
01270             if( fabs(phase_diff) > nrThresholdCycles )
01271             {
01272               m_ObsArray[i].flags.isNoCycleSlipDetected = 0; // Indicate a cycle slip has occured.
01273             }
01274             else
01275             {
01276               m_ObsArray[i].flags.isNoCycleSlipDetected = 1; // No cycle slip detected.
01277             }
01278             break;
01279           }
01280         }
01281       }
01282     }
01283 
01284     return true;
01285   }
01286 
01287 
01288   bool GNSS_RxData::DebugPrintObservationArray( const char *filepath )
01289   {
01290     unsigned i = 0;
01291     unsigned j = 0;
01292     Matrix X(100,m_nrValidObs);
01293     
01294     if( filepath == NULL )
01295       return false;
01296 
01297 
01298     for( i = 0; i < m_nrValidObs; i++ )
01299     {
01300       j = 0;
01301       X[j][i] = m_ObsArray[i].channel; j++;
01302       X[j][i] = m_ObsArray[i].id; j++;       //!< The unique id for this channel (eg PRN for GPS).    
01303       X[j][i] = m_ObsArray[i].system; j++;   //!< The satellite system associated with this channel.
01304       X[j][i] = m_ObsArray[i].codeType; j++; //!< The code type for this channel.
01305       X[j][i] = m_ObsArray[i].freqType; j++; //!< The frequency type for this channel.
01306 
01307       X[j][i] = m_ObsArray[i].flags.isActive; j++;              //!< This flag indicates that the channel is active for use. If this is not set, no other flags are valid for use.
01308       X[j][i] = m_ObsArray[i].flags.isCodeLocked; j++;          //!< Indicates if the code tracking is locked.
01309       X[j][i] = m_ObsArray[i].flags.isPhaseLocked; j++;         //!< Indicates if the phase tracking is locked.
01310       X[j][i] = m_ObsArray[i].flags.isParityValid; j++;         //!< Indicates if the phase parity if valid.      
01311       X[j][i] = m_ObsArray[i].flags.isPsrValid; j++;            //!< Indicates if the pseudorange valid for use.
01312       X[j][i] = m_ObsArray[i].flags.isAdrValid; j++;            //!< Indicates if the ADR is valid for use.
01313       X[j][i] = m_ObsArray[i].flags.isDopplerValid; j++;        //!< Indicates if the Doppler if valid for use.
01314       X[j][i] = m_ObsArray[i].flags.isGrouped; j++;             //!< Indicates if this channel has another associated channel. eg. L1 and L2 measurements.
01315       X[j][i] = m_ObsArray[i].flags.isAutoAssigned; j++;        //!< Indicates if the channel was receiver assigned (otherwise, the user forced this channel assignment).
01316       X[j][i] = m_ObsArray[i].flags.isCarrierSmoothed; j++;     //!< Indicates if the pseudorange has carrier smoothing enabled.
01317       X[j][i] = m_ObsArray[i].flags.isEphemerisValid; j++;      //!< Indicates if this channel has valid associated ephemeris information. 
01318       X[j][i] = m_ObsArray[i].flags.isAlmanacValid; j++;        //!< Indicates if this channel has valid associated almanac information.
01319       X[j][i] = m_ObsArray[i].flags.isAboveElevationMask; j++;  //!< Indicates if the satellite tracked is above the elevation mask.    
01320       X[j][i] = m_ObsArray[i].flags.isAboveCNoMask; j++;        //!< Indciates if the channel's C/No is above a threshold value.
01321       X[j][i] = m_ObsArray[i].flags.isAboveLockTimeMask; j++;   //!< Indicates if the channel's locktime is above a treshold value.
01322       X[j][i] = m_ObsArray[i].flags.isNotUserRejected; j++;     //!< Indicates if the user has not forced the rejection of this channel or PRN.
01323       X[j][i] = m_ObsArray[i].flags.isNotPsrRejected; j++;      //!< Indicates if the pseudorange was not rejetced (ie Fault Detection and Exclusion).
01324       X[j][i] = m_ObsArray[i].flags.isNotAdrRejected; j++;      //!< Indicates if the ADR was not rejetced (ie Fault Detection and Exclusion).
01325       X[j][i] = m_ObsArray[i].flags.isNotDopplerRejected; j++;  //!< Indicates if the Doppler was not rejected (ie Fault Detection and Exclusion).
01326       X[j][i] = m_ObsArray[i].flags.isNoCycleSlipDetected; j++; //!< Indicates that no cycle slip has occurred at this epoch.
01327       X[j][i] = m_ObsArray[i].flags.isPsrUsedInSolution; j++;   //!< Indicates if some part (pseudorange) of this channel's measurement was used in the position solution.
01328       X[j][i] = m_ObsArray[i].flags.isDopplerUsedInSolution; j++;   //!< Indicates if some part (Doppler) of this channel's measurement was used in the velocity solution.
01329       X[j][i] = m_ObsArray[i].flags.isAdrUsedInSolution; j++;   //!< Indicates if the the ADR is used in the solution.
01330       X[j][i] = m_ObsArray[i].flags.isDifferentialPsrAvailable; j++;     //!< Indicates if a matching pseudrange observation is available from another receiver.
01331       X[j][i] = m_ObsArray[i].flags.isDifferentialDopplerAvailable; j++; //!< Indicates if a matching Doppler observation is available from another receiver.
01332       X[j][i] = m_ObsArray[i].flags.isDifferentialAdrAvailable; j++;     //!< Indicates if a matching ADR observation is available from another receiver.
01333       X[j][i] = m_ObsArray[i].flags.useTropoCorrection; j++;         //!< Indicates that the tropospheric correction should be applied.
01334       X[j][i] = m_ObsArray[i].flags.useBroadcastIonoCorrection; j++; //!< Indicates that the broadcast ionospheric correction should be applied.
01335       X[j][i] = m_ObsArray[i].flags.isTimeDifferntialPsrAvailable; j++;
01336       X[j][i] = m_ObsArray[i].flags.isTimeDifferntialDopplerAvailable; j++;
01337 
01338       X[j][i] = m_ObsArray[i].week; j++;  //!< The measurement gps week (at 'transmit' time) [weeks].
01339       X[j][i] = m_ObsArray[i].tow; j++;   //!< The measurement gps time of week (at 'transmit' time) [s].
01340 
01341       // The actual measurements.
01342       X[j][i] = m_ObsArray[i].psr; j++;               //!< The pseudorange measurement [m].
01343       X[j][i] = m_ObsArray[i].adr; j++;               //!< The carrier phase or accumulated Doppler range measurement [cycles].
01344       X[j][i] = m_ObsArray[i].doppler; j++;           //!< The Doppler measurement for this channel [Hz].
01345       X[j][i] = m_ObsArray[i].cno; j++;               //!< The carrier to noise density ratio for this channel [dB-Hz]
01346       X[j][i] = m_ObsArray[i].locktime; j++;          //!< The number of seconds of continous phase tracking (no known cycle slips) [s].
01347 
01348       // The variance information associated with the actual measurements.
01349       X[j][i] = m_ObsArray[i].stdev_psr; j++;         //!< The estimated pseudorange measurement standard deviation [m].
01350       X[j][i] = m_ObsArray[i].stdev_adr; j++;         //!< The estimated accumulated Doppler range measurement standard deviation [cycles].
01351       X[j][i] = m_ObsArray[i].stdev_doppler; j++;     //!< The estimated Doppler measurement standard deviation [Hz].
01352 
01353       // Derived information.
01354       X[j][i] = m_ObsArray[i].psr_misclosure; j++;    //!< The measured psr minus the computed psr estimate [m].
01355       X[j][i] = m_ObsArray[i].doppler_misclosure; j++;//!< The measured Doppler minus the computed Doppler estimate [m].
01356       X[j][i] = m_ObsArray[i].range; j++;             //!< The best estimate of the geometric range between the antenna and the satellite [m].
01357       X[j][i] = m_ObsArray[i].rangerate; j++;         //!< The best estimate of the geometric range rate between the antenna and the satellite [m/s].
01358       X[j][i] = m_ObsArray[i].psr_smoothed; j++;      //!< The carrier smoothed pseudorange if available [m].
01359       X[j][i] = m_ObsArray[i].psr_predicted; j++;     //!< The predicted pseudorange based on the satellite position, user position, and current clock offset [m].
01360       X[j][i] = m_ObsArray[i].ambiguity; j++;         //!< The estimated integer component of the adr. This may be the single or double differenced ambiguity [].
01361       X[j][i] = m_ObsArray[i].doppler_predicted; j++; //!< The predicted Doppler based on user position, velocity, satellite position, velocity and clock rate [Hz].
01362       X[j][i] = m_ObsArray[i].azimuthRads; j++;       //!< The associated satellite azimuth for this channel [rad].
01363       X[j][i] = m_ObsArray[i].elevationRads; j++;     //!< The associated satellite elevation for this channel  [rad].
01364 
01365 
01366       X[j][i] = m_ObsArray[i].index_differential; j++;      //!< The channel index of a matching differential observation. -1 means there is no matching channel.
01367       X[j][i] = m_ObsArray[i].index_time_differential; j++; //!< The channel index of a matching time differential observation. -1 means there is no matching channel.
01368 
01369       X[j][i] = m_ObsArray[i].index_ambiguity_state; j++;   //!< The index into the state vector for this ambiguity state. -1 not estimated.
01370       X[j][i] = m_ObsArray[i].adr_misclosure; j++;          //!< The measured ADR minus the computed ADR estimate [m]. This is likely a differential quantity.
01371 
01372       X[j][i] = m_ObsArray[i].H_p[0]; j++; //!< The design matrix row relating the pseudorange measurements to the position solution. dP/d(lat), dP/d(lon), dP/d(hgt).
01373       X[j][i] = m_ObsArray[i].H_p[1]; j++;
01374       X[j][i] = m_ObsArray[i].H_p[2]; j++;
01375       X[j][i] = m_ObsArray[i].H_v[0]; j++; //!< The design matrix row relating the Doppler measurements to the velocity solution. dD/d(lat), dD/d(lon), dD/d(hgt).
01376       X[j][i] = m_ObsArray[i].H_v[1]; j++;
01377       X[j][i] = m_ObsArray[i].H_v[2]; j++;
01378 
01379       X[j][i] = m_ObsArray[i].corrections.dX; j++; //!< The corrections associated with this channel.
01380       X[j][i] = m_ObsArray[i].corrections.dY; j++;
01381       X[j][i] = m_ObsArray[i].corrections.dZ; j++;
01382       X[j][i] = m_ObsArray[i].corrections.prcIono; j++;
01383       X[j][i] = m_ObsArray[i].corrections.prcReserved1; j++;
01384       X[j][i] = m_ObsArray[i].corrections.prcReserved2; j++;
01385       X[j][i] = m_ObsArray[i].corrections.prcSatClk; j++;
01386       X[j][i] = m_ObsArray[i].corrections.prcTropoDry; j++;
01387       X[j][i] = m_ObsArray[i].corrections.prcTropoWet; j++;
01388       X[j][i] = m_ObsArray[i].corrections.rrcReserved1; j++;
01389 
01390       X[j][i] = m_ObsArray[i].residuals.psrResidual; j++;   //!< The post-adjustment (filtering) measurement residual associated with this channel.
01391       X[j][i] = m_ObsArray[i].residuals.dopplerResidual; j++;
01392       X[j][i] = m_ObsArray[i].residuals.adrResidual; j++;
01393       X[j][i] = m_ObsArray[i].residuals.reserved; j++;      
01394     }
01395     X.Redim(j,m_nrValidObs);
01396 
01397     X.Print( filepath, 12 ); // print to the file in append mode.
01398 
01399     return true;
01400   }
01401 
01402 
01403 
01404   bool GNSS_RxData::Debug_WriteSuperMsg80CharsWide(
01405     char* buffer,                    //!< A large character buffer (8KB min).
01406     const unsigned maxBufferLength,  //!< The maximum buffer length [bytes].
01407     const double referenceLatitude,  //!< Reference position latitude [rad].
01408     const double referenceLongitude, //!< Reference position longitude [rad].
01409     const double referenceHeight,    //!< Reference position height [m].
01410     unsigned& nrBytesInBuffer )      //!< The number of bytes set in the buffer.
01411   {
01412     unsigned i = 0;
01413     unsigned j = 0;
01414     int week = -1;
01415     int tmpi = 0;
01416 
01417     unsigned scount = 0;
01418 
01419     double x = 0;
01420     double y = 0;
01421     double z = 0;
01422 
01423     double n_err  = 0.0;
01424     double e_err  = 0.0;
01425     double up_err = 0.0;
01426     double dtmp = 0.0;
01427     double v_azimuth = 0.0;
01428 
01429     if( maxBufferLength < 1024*8 )
01430     {
01431       nrBytesInBuffer = 0;
01432       return false;
01433     }
01434     
01435     scount += sprintf( buffer, "\n");
01436     scount += sprintf( buffer+scount, "GPS_Week   GPS_TOW(s)   DayOfYear   UTC_Time\n" );
01437     scount += sprintf( buffer+scount, "    %4d", m_pvt.time.gps_week );
01438     scount += sprintf( buffer+scount, "%13.3lf", m_pvt.time.gps_tow );
01439     scount += sprintf( buffer+scount, "         %03d", m_pvt.time.day_of_year );
01440     scount += sprintf( buffer+scount, "   %04d, %02d, %02d, %02d:%02d:%05.2lf\n", 
01441       m_pvt.time.utc_year, 
01442       m_pvt.time.utc_month,
01443       m_pvt.time.utc_day,
01444       m_pvt.time.utc_hour,
01445       m_pvt.time.utc_minute,
01446       m_pvt.time.utc_seconds );
01447     scount += sprintf( buffer+scount, "\n" );
01448 
01449     scount += sprintf( buffer+scount, "            Lat(deg)  Long(deg)  Height(m)         X(m)        Y(m)        Z(m)\n");
01450     scount += sprintf( buffer+scount, "Estimated:%10.5f %10.5f %10.3f ", m_pvt.latitudeDegs, m_pvt.longitudeDegs, m_pvt.height );    
01451     scount += sprintf( buffer+scount, "%12.1lf %11.1lf %11.1lf\n", m_pvt.x, m_pvt.y, m_pvt.z );
01452 
01453     if( referenceLatitude == 0 && referenceLongitude == 0 && referenceHeight == 0 )
01454     {
01455       x = y = z = 0;
01456     }
01457     else
01458     {
01459       GEODESY_ConvertGeodeticCurvilinearToEarthFixedCartesianCoordinates(
01460         GEODESY_REFERENCE_ELLIPSE_WGS84,
01461         referenceLatitude,
01462         referenceLongitude,
01463         referenceHeight,
01464         &x, 
01465         &y,
01466         &z );
01467     }
01468 
01469     scount += sprintf( buffer+scount, "Reference:%10.5f %10.5f %10.3f ", referenceLatitude*RAD2DEG, referenceLongitude*RAD2DEG, referenceHeight );      
01470     scount += sprintf( buffer+scount, "%12.1lf %11.1lf %11.1lf\n", x, y, z );
01471 
01472     if( x == 0 && y == 0 && z == 0 )
01473     {
01474       n_err = e_err = up_err = 0.0;
01475     }
01476     else
01477     {
01478       GEODESY_ComputePositionDifference(
01479         GEODESY_REFERENCE_ELLIPSE_WGS84,
01480         referenceLatitude,  
01481         referenceLongitude, 
01482         referenceHeight,    
01483         m_pvt.latitude,
01484         m_pvt.longitude,
01485         m_pvt.height,  
01486         &n_err,
01487         &e_err,
01488         &up_err
01489         );
01490     }
01491  
01492     scount += sprintf( buffer+scount, "Diff(m)  :");
01493     if (fabs(n_err) < 1000)  scount += sprintf( buffer+scount, " %9.5f", n_err);
01494     else                     scount += sprintf( buffer+scount, "          ");
01495     if (fabs(e_err) < 1000)  scount += sprintf( buffer+scount, "  %9.5f", e_err);
01496     else                     scount += sprintf( buffer+scount, "           ");
01497     if (fabs(up_err) < 1000) scount += sprintf( buffer+scount, "  %9.3f\n\n", up_err );
01498     else                     scount += sprintf( buffer+scount, "           \n\n");
01499 
01500     scount += sprintf( buffer+scount, "East(m/s)      North(m/s)       Up(m/s)      Ground_Speed(km/h)    Azimuth(deg)\n" );
01501     dtmp = sqrt( m_pvt.ve*m_pvt.ve + m_pvt.vn*m_pvt.vn )*3.6;
01502     v_azimuth = atan2(m_pvt.ve, m_pvt.vn);
01503     scount += sprintf( buffer+scount, "%9.2lf %15.2lf %13.2lf %23.2lf %15.3lf\n", 
01504       m_pvt.vn,
01505       m_pvt.ve,
01506       m_pvt.vup,
01507       dtmp,
01508       v_azimuth*RAD2DEG );      
01509     scount += sprintf( buffer+scount, "\n" );  
01510 
01511 
01512     scount += sprintf( buffer+scount, "Nr: PSR  U / R  Doppler  U / R  ADR  U / R    HDOP   VDOP   PDOP   TDOP   GDOP\n" );
01513     scount += sprintf( buffer+scount, "    %3d%3d%4d", m_pvt.nrPsrObsAvailable, m_pvt.nrPsrObsUsed, m_pvt.nrPsrObsRejected ); 
01514     scount += sprintf( buffer+scount, "%9d%3d%4d", m_pvt.nrDopplerObsAvailable, m_pvt.nrDopplerObsUsed, m_pvt.nrDopplerObsRejected ); 
01515     scount += sprintf( buffer+scount, "%5d%3d%4d", m_pvt.nrAdrObsAvailable, m_pvt.nrAdrObsUsed, m_pvt.nrAdrObsRejected ); 
01516     scount += sprintf( buffer+scount, "%8.1lf %6.1lf %6.1lf %6.1lf %6.1lf\n", 
01517       m_pvt.dop.hdop, 
01518       m_pvt.dop.vdop, 
01519       m_pvt.dop.pdop, 
01520       m_pvt.dop.tdop, 
01521       m_pvt.dop.gdop );
01522     scount += sprintf( buffer+scount, "\n" );  
01523 
01524     scount += sprintf( buffer+scount, "%2s", "CH" );
01525     scount += sprintf( buffer+scount, "%3s", "ID" );
01526     scount += sprintf( buffer+scount, "%3s", "S" );
01527     scount += sprintf( buffer+scount, "%13s", "PSR" );
01528     scount += sprintf( buffer+scount, "%2s", "U" );
01529     scount += sprintf( buffer+scount, "%9s", "DOPPLER" );
01530     scount += sprintf( buffer+scount, "%2s", "U" );
01531     scount += sprintf( buffer+scount, "%13s", "ADR" );
01532     scount += sprintf( buffer+scount, "%2s", "U" );
01533     scount += sprintf( buffer+scount, "%6s", "E" );
01534     scount += sprintf( buffer+scount, "%7s", "A" );
01535     scount += sprintf( buffer+scount, "%5s", "C/No" );
01536     scount += sprintf( buffer+scount, "%8s", "LOCK" );
01537     scount += sprintf( buffer+scount, "%3s", "SI" );
01538     scount += sprintf( buffer+scount, "\n" );
01539 
01540     for( i = 0; i < m_nrValidObs; i++ )
01541     {
01542       if( m_ObsArray[i].flags.isActive && m_ObsArray[i].freqType == GNSS_GPSL1)
01543       {
01544         scount += sprintf( buffer+scount, "%-2d", m_ObsArray[i].channel );
01545         scount += sprintf( buffer+scount, "%3d", m_ObsArray[i].id );
01546         if( m_ObsArray[i].system == GNSS_GPS && m_ObsArray[i].freqType == GNSS_GPSL1 )
01547         {
01548           scount += sprintf( buffer+scount, "%3s", "L1" );
01549         }
01550         else if( m_ObsArray[i].system == GNSS_GPS && m_ObsArray[i].freqType == GNSS_GPSL2 )
01551         {
01552           scount += sprintf( buffer+scount, "%3s", "L2" );
01553         }
01554         else
01555         {
01556           scount += sprintf( buffer+scount, "%3s", "--" );
01557         }
01558         scount += sprintf( buffer+scount, "%13.1lf", m_ObsArray[i].psr );
01559         scount += sprintf( buffer+scount, "%2d", m_ObsArray[i].flags.isPsrUsedInSolution );
01560 
01561         scount += sprintf( buffer+scount, "%9.1lf", m_ObsArray[i].doppler );
01562         scount += sprintf( buffer+scount, "%2d", m_ObsArray[i].flags.isDopplerUsedInSolution );
01563         
01564         scount += sprintf( buffer+scount, "%13.1lf", m_ObsArray[i].adr );    
01565         scount += sprintf( buffer+scount, "%2d", m_ObsArray[i].flags.isAdrUsedInSolution );
01566 
01567         scount += sprintf( buffer+scount, "%6.1lf", m_ObsArray[i].satellite.elevation*RAD2DEG );
01568         scount += sprintf( buffer+scount, "%7.1lf", m_ObsArray[i].satellite.azimuth*RAD2DEG );
01569         scount += sprintf( buffer+scount, "%5.1lf", m_ObsArray[i].cno );
01570         scount += sprintf( buffer+scount, "%8.1lf", m_ObsArray[i].locktime );
01571         scount += sprintf( buffer+scount, "%3d", m_ObsArray[i].index_ambiguity_state );
01572         scount += sprintf( buffer+scount, "\n" );
01573       }
01574     }  
01575     scount += sprintf( buffer+scount, "\n" );
01576 
01577     nrBytesInBuffer = scount;
01578     return true;
01579   }
01580 
01581 
01582   bool GNSS_RxData::LoadRINEXNavigationData(void)
01583   {
01584     // First estimate the size of the ephemeris array needed based on the number of lines
01585     // in the data file after the header.
01586     unsigned line_count = 0;
01587     unsigned estimated_nr_eph = 0;
01588     BOOL result = 0;
01589 
01590     FILE* fid = NULL;
01591     fid = fopen( m_RINEX_eph.filepath.c_str(), "r" );
01592     if( fid == NULL )
01593       return false;
01594 
01595     // Advance over the header.
01596     while( !feof(fid) && ferror(fid)==0 )
01597     {
01598       if( fgets( (char*)m_message, GNSS_RXDATA_MSG_LENGTH, fid ) == NULL )
01599         break;
01600       if( strstr( (char*)m_message, "END OF HEADER" ) != NULL )
01601         break;
01602     }
01603 
01604     // Count the number of lines in the remaining data.
01605     while( !feof(fid) && ferror(fid)==0 )
01606     {
01607       if( fgets( (char*)m_message, GNSS_RXDATA_MSG_LENGTH, fid ) == NULL )
01608         break;
01609       line_count++;
01610     }
01611 
01612     fclose(fid);
01613 
01614     // There are 8 lines per ephemeris record generally.
01615     estimated_nr_eph = line_count/8;
01616 
01617     estimated_nr_eph += 32; // for good measure.
01618 
01619     // Allocate enough memory for the ephemeris array.
01620     m_RINEX_eph.eph_array = NULL;
01621     m_RINEX_eph.eph_array = new GPS_structEphemeris[estimated_nr_eph];
01622     if( m_RINEX_eph.eph_array == NULL )
01623       return FALSE;
01624     
01625     m_RINEX_eph.max_array_length = estimated_nr_eph;
01626     
01627     result = RINEX_DecodeGPSNavigationFile(
01628       m_RINEX_eph.filepath.c_str(),
01629       &m_RINEX_eph.iono_model,
01630       m_RINEX_eph.eph_array,
01631       m_RINEX_eph.max_array_length,
01632       &m_RINEX_eph.array_length
01633       );
01634     if( result == FALSE )
01635       return false;
01636 
01637     return true;
01638   }
01639 
01640   bool GNSS_RxData::UpdateTheEphemerisArrayWithUsingRINEX()
01641   {
01642     int RINEX_eph_index = -1;
01643     unsigned i = 0;
01644     unsigned j = 0;
01645     unsigned k = 0;
01646     unsigned short eph_week = 0;
01647     unsigned eph_tow = 0;
01648     bool result = false;
01649     double eph_time = 0;
01650     double current_eph_time = 0;
01651     const double rx_time = m_pvt.time.gps_week*SECONDS_IN_WEEK + m_pvt.time.gps_tow;
01652     bool isEphUpToDate;
01653     bool isAvailable;
01654 
01655     // GDM CONTINUE HERE
01656 
01657     /*
01658     - Only update thte active satellites
01659     - Update the ephemeris object as if the data is real time.
01660     - i.e. Not the best ephemeris but the most up to date.
01661     */
01662     for( j = 0; j < m_nrValidObs; j++ )
01663     {
01664       RINEX_eph_index = -1; // Not available.
01665       eph_time = 0;
01666       current_eph_time = 0;
01667       isEphUpToDate = false;
01668       isAvailable = false;
01669       if( m_ObsArray[j].flags.isActive )
01670       {
01671         if( m_ObsArray[j].system == GNSS_GPS )
01672         {
01673           // Check if new ephemeris information is available. 
01674           // Process as if the data is real-time.
01675     
01676           result = m_EphAlmArray.GetEphemerisTOW(
01677             m_ObsArray[j].id, 
01678             isAvailable,
01679             eph_week,
01680             eph_tow
01681             );
01682           if( !result )
01683             return false;
01684 
01685           if( isAvailable )
01686           {
01687             current_eph_time = eph_week*SECONDS_IN_WEEK + eph_tow;
01688           }
01689 
01690           // Go through the entire ephemeris array. This algorihtms assumes
01691           // the ephemeris records are increasing in time.
01692           for( k = 0; k < m_RINEX_eph.array_length; k++ )
01693           {
01694             if( m_RINEX_eph.eph_array[k].prn == m_ObsArray[j].id )
01695             {
01696               eph_time = m_RINEX_eph.eph_array[k].tow_week*SECONDS_IN_WEEK + m_RINEX_eph.eph_array[k].tow;
01697 
01698               if( !isAvailable )
01699               {
01700                 if( rx_time > eph_time )
01701                 {
01702                   RINEX_eph_index = k;
01703                 }
01704                 else
01705                 {
01706                   if( RINEX_eph_index >= 0 )
01707                   {
01708                     result = m_EphAlmArray.AddEphemeris( 
01709                       m_RINEX_eph.eph_array[RINEX_eph_index].prn, 
01710                       m_RINEX_eph.eph_array[RINEX_eph_index]
01711                     );
01712                     if( !result )
01713                       return false;
01714                     isEphUpToDate = true;
01715                     break;
01716                   }
01717                   else
01718                   {
01719                     // There are no ephemeris records for this rx time yet.                    
01720                     break;
01721                   }                  
01722                 }
01723               }
01724               else
01725               {
01726                 if( rx_time > eph_time )
01727                 {
01728                   if( current_eph_time < eph_time )
01729                   {
01730                     RINEX_eph_index = k;
01731                   }
01732                 }
01733                 else
01734                 {
01735                   if( RINEX_eph_index >= 0 )
01736                   {
01737                     result = m_EphAlmArray.AddEphemeris( 
01738                       m_RINEX_eph.eph_array[RINEX_eph_index].prn, 
01739                       m_RINEX_eph.eph_array[RINEX_eph_index]
01740                     );
01741                     if( !result )
01742                       return false;
01743                     isEphUpToDate = true;                    
01744                   }
01745                   break;
01746                 }
01747               }
01748             }
01749           }
01750 
01751           // Deal with end of file ephemeris records, no records are greater in time.
01752           if( !isEphUpToDate )
01753           {
01754             if( RINEX_eph_index >= 0 )
01755             {
01756               result = m_EphAlmArray.AddEphemeris( 
01757                 m_RINEX_eph.eph_array[RINEX_eph_index].prn, 
01758                 m_RINEX_eph.eph_array[RINEX_eph_index]
01759               );
01760               if( !result )
01761                 return false;
01762               isEphUpToDate = true;
01763             }
01764           }
01765         }
01766       }
01767     }
01768 
01769     // Update the observation flags to indicate that ephemeris is available.
01770     for( j = 0; j < m_nrValidObs; j++ )
01771     {
01772       if( m_ObsArray[j].flags.isActive )
01773       {
01774         if( m_ObsArray[j].system == GNSS_GPS )
01775         {
01776           result = m_EphAlmArray.IsEphemerisAvailable( m_ObsArray[j].id, isAvailable );
01777           if( !result )
01778             return false;
01779 
01780           m_ObsArray[j].flags.isEphemerisValid = isAvailable;
01781         }
01782       }
01783     }
01784       
01785     
01786 
01787     return true;
01788   }
01789 
01790 
01791 } // end namespace GNSS
01792 
01793 
01794