Beispiel #1
0
      // Get the SourceID of the rinex observation file
   SourceID NetworkObsStreams::sourceIDOfRinexObsFile(std::string obsFile)
   {
      try
      {
         RinexObsStream rin;
         rin.exceptions(std::ios::failbit);
         rin.open(obsFile, std::ios::in);
 
         gnssRinex gRin;
         rin >> gRin;

         rin.close();
         
         return gRin.header.source;
      }
      catch(...)
      {
         // Problem opening the file
         // Maybe it doesn't exist or you don't have proper read permissions

         Exception e("Problem opening the file "
            + obsFile 
            + "Maybe it doesn't exist or you don't have proper read permissions");

         GPSTK_THROW(e);
      }

   }  // End of method 'NetworkObsStreams::sourceIDOfRinexObsFile'
Beispiel #2
0
//------------------------------------------------------------------------------------
int OpenFiles(void) throw(Exception)
{
try {
   string filename;
   if(InputDirectory.empty())
      filename = NovatelFile;
   else
      filename = InputDirectory + string("/") + NovatelFile;
   instr.open(filename.c_str(),ios::in | ios::binary);
   if(!instr.is_open()) {
      cerr << "Failed to open input file " << NovatelFile << endl;
      return -1;
   }
   if(verbose) cout << "Opened input file " << NovatelFile << endl;
   instr.exceptions(fstream::failbit);

   TempFileName = GetTempFileName();
   rostr.open(TempFileName.c_str(),ios::out);
   if(!rostr.is_open()) {
      cerr << "Failed to open temporary output file " << TempFileName << endl;
      return -2;
   }
   rostr.exceptions(fstream::failbit);

   rnstr.open(RinexNavFile.c_str(),ios::out);
   if(!rnstr.is_open()) {
      cerr << "Failed to open output nav file " << RinexNavFile << endl;
      return -3;
   }
   if(verbose) cout << "Opened output nav file " << RinexNavFile << endl;
   rnstr.exceptions(fstream::failbit);

   return 0;
}
catch(Exception& e) { GPSTK_RETHROW(e); }
catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
}
Beispiel #3
0
   // Method that will really process information
void example9::process()
{

      // We will read each section name, which is equivalent to station name
      // Station names will be read in alphabetical order
   string station;
   while ( (station = confReader.getEachSection()) != "" )
   {

         // We will skip 'DEFAULT' section because we are waiting for
         // a specific section for each receiver. However, if data is
         // missing we will look for it in 'DEFAULT' (see how we use method
         // 'setFallback2Default()' of 'ConfDataReader' object in 'spinUp()'
      if( station == "DEFAULT" )
      {
         continue;
      }


         // Show a message indicating that we are starting with this station
      cout << "Starting processing for station: '" << station << "'." << endl;


         // Create input observation file stream
      RinexObsStream rin;

         // Enable exceptions
      rin.exceptions(ios::failbit);

         // Try to open Rinex observations file
      try
      {

            // Open Rinex observations file in read-only mode
         rin.open( confReader("rinexObsFile", station), std::ios::in );

      }
      catch(...)
      {

         cerr << "Problem opening file '"
              << confReader.getValue("rinexObsFile", station)
              << "'." << endl;

         cerr << "Maybe it doesn't exist or you don't have "
              << "proper read permissions."
              << endl;

         cerr << "Skipping receiver '" << station << "'."
              << endl;

            // Close current Rinex observation stream
         rin.close();

         continue;

      }  // End of 'try-catch' block


         // Declare a "SP3EphemerisStore" object to handle precise ephemeris
      SP3EphemerisStore SP3EphList;

         // Set flags to reject satellites with bad or absent positional
         // values or clocks
      SP3EphList.rejectBadPositions(true);
      SP3EphList.rejectBadClocks(true);

         // Read if we should check for data gaps.
      if ( confReader.getValueAsBoolean( "checkGaps", station ) )
      {
         SP3EphList.enableDataGapCheck();
         SP3EphList.setGapInterval(
                     confReader.getValueAsDouble("SP3GapInterval",station) );
      }

         // Read if we should check for too wide interpolation intervals
      if ( confReader.getValueAsBoolean( "checkInterval", station ) )
      {
         SP3EphList.enableIntervalCheck();
         SP3EphList.setMaxInterval(
                     confReader.getValueAsDouble("maxSP3Interval",station) );
      }


         // Load all the SP3 ephemerides files from variable list
      string sp3File;
      while ( (sp3File = confReader.fetchListValue("SP3List",station) ) != "" )
      {

            // Try to load each ephemeris file
         try
         {

            SP3EphList.loadFile( sp3File );

         }
         catch (FileMissingException& e)
         {
               // If file doesn't exist, issue a warning
            cerr << "SP3 file '" << sp3File << "' doesn't exist or you don't "
                 << "have permission to read it. Skipping it." << endl;

            continue;

         }

      }  // End of 'while ( (sp3File = confReader.fetchListValue( ... "


         // Load station nominal position
      double xn(confReader.fetchListValueAsDouble("nominalPosition",station));
      double yn(confReader.fetchListValueAsDouble("nominalPosition",station));
      double zn(confReader.fetchListValueAsDouble("nominalPosition",station));
         // The former peculiar code is possible because each time we
         // call a 'fetchListValue' method, it takes out the first element
         // and deletes it from the given variable list.

      Position nominalPos( xn, yn, zn );


         // Create a 'ProcessingList' object where we'll store
         // the processing objects in order
      ProcessingList pList;


         // This object will check that all required observables are present
      RequireObservables requireObs;
      requireObs.addRequiredType(TypeID::P2);
      requireObs.addRequiredType(TypeID::L1);
      requireObs.addRequiredType(TypeID::L2);

         // This object will check that code observations are within
         // reasonable limits
      SimpleFilter pObsFilter;
      pObsFilter.setFilteredType(TypeID::P2);

         // Read if we should use C1 instead of P1
      bool usingC1( confReader.getValueAsBoolean( "useC1", station ) );
      if ( usingC1 )
      {
         requireObs.addRequiredType(TypeID::C1);
         pObsFilter.addFilteredType(TypeID::C1);
      }
      else
      {
         requireObs.addRequiredType(TypeID::P1);
         pObsFilter.addFilteredType(TypeID::P1);
      }

         // Add 'requireObs' to processing list (it is the first)
      pList.push_back(requireObs);

         // IMPORTANT NOTE:
         // It turns out that some receivers don't correct their clocks
         // from drift.
         // When this happens, their code observations may drift well beyond
         // what it is usually expected from a pseudorange. In turn, this
         // effect causes that "SimpleFilter" objects start to reject a lot of
         // satellites.
         // Thence, the "filterCode" option allows you to deactivate the
         // "SimpleFilter" object that filters out C1, P1 and P2, in case you
         // need to.
      bool filterCode( confReader.getValueAsBoolean( "filterCode", station ) );

         // Check if we are going to use this "SimpleFilter" object or not
      if( filterCode )
      {
         pList.push_back(pObsFilter);       // Add to processing list
      }


         // This object defines several handy linear combinations
      LinearCombinations comb;


         // Object to compute linear combinations for cycle slip detection
      ComputeLinear linear1;

         // Read if we should use C1 instead of P1
      if ( usingC1 )
      {
         linear1.addLinear(comb.pdeltaCombWithC1);
         linear1.addLinear(comb.mwubbenaCombWithC1);
      }
      else
      {
         linear1.addLinear(comb.pdeltaCombination);
         linear1.addLinear(comb.mwubbenaCombination);
      }
      linear1.addLinear(comb.ldeltaCombination);
      linear1.addLinear(comb.liCombination);
      pList.push_back(linear1);       // Add to processing list


         // Objects to mark cycle slips
      LICSDetector2 markCSLI2;         // Checks LI cycle slips
      pList.push_back(markCSLI2);      // Add to processing list
      MWCSDetector  markCSMW;          // Checks Merbourne-Wubbena cycle slips
      pList.push_back(markCSMW);       // Add to processing list


         // Object to keep track of satellite arcs
      SatArcMarker markArc;
      markArc.setDeleteUnstableSats(true);
      markArc.setUnstablePeriod(151.0);
      pList.push_back(markArc);       // Add to processing list


         // Object to decimate data
      Decimate decimateData(
               confReader.getValueAsDouble( "decimationInterval", station ),
               confReader.getValueAsDouble( "decimationTolerance", station ),
               SP3EphList.getInitialTime() );
      pList.push_back(decimateData);       // Add to processing list


         // Declare a basic modeler
      BasicModel basic(nominalPos, SP3EphList);

         // Set the minimum elevation
      basic.setMinElev(confReader.getValueAsDouble("cutOffElevation",station));

         // If we are going to use P1 instead of C1, we must reconfigure 'basic'
      if ( !usingC1 )
      {
         basic.setDefaultObservable(TypeID::P1);
      }


         // Add to processing list
      pList.push_back(basic);


         // Object to remove eclipsed satellites
      EclipsedSatFilter eclipsedSV;
      pList.push_back(eclipsedSV);       // Add to processing list


         // Object to compute gravitational delay effects
      GravitationalDelay grDelay(nominalPos);
      pList.push_back(grDelay);       // Add to processing list


         // Vector from monument to antenna ARP [UEN], in meters
      double uARP(confReader.fetchListValueAsDouble( "offsetARP", station ) );
      double eARP(confReader.fetchListValueAsDouble( "offsetARP", station ) );
      double nARP(confReader.fetchListValueAsDouble( "offsetARP", station ) );
      Triple offsetARP( uARP, eARP, nARP );


         // Declare some antenna-related variables
      Triple offsetL1( 0.0, 0.0, 0.0 ), offsetL2( 0.0, 0.0, 0.0 );
      AntexReader antexReader;
      Antenna receiverAntenna;

         // Check if we want to use Antex information
      bool useantex( confReader.getValueAsBoolean( "useAntex", station ) );
      if( useantex )
      {
            // Feed Antex reader object with Antex file
         antexReader.open( confReader.getValue( "antexFile", station ) );

            // Get receiver antenna parameters
         receiverAntenna =
            antexReader.getAntenna( confReader.getValue( "antennaModel",
                                                         station ) );

      }


         // Object to compute satellite antenna phase center effect
      ComputeSatPCenter svPcenter(nominalPos);
      if( useantex )
      {
            // Feed 'ComputeSatPCenter' object with 'AntexReader' object
         svPcenter.setAntexReader( antexReader );
      }

      pList.push_back(svPcenter);       // Add to processing list


         // Declare an object to correct observables to monument
      CorrectObservables corr(SP3EphList);
      corr.setNominalPosition(nominalPos);
      corr.setMonument( offsetARP );

         // Check if we want to use Antex patterns
      bool usepatterns(confReader.getValueAsBoolean("usePCPatterns", station ));
      if( useantex && usepatterns )
      {
         corr.setAntenna( receiverAntenna );

            // Should we use elevation/azimuth patterns or just elevation?
         corr.setUseAzimuth(confReader.getValueAsBoolean("useAzim", station));
      }
      else
      {
            // Fill vector from antenna ARP to L1 phase center [UEN], in meters
         offsetL1[0] = confReader.fetchListValueAsDouble("offsetL1", station);
         offsetL1[1] = confReader.fetchListValueAsDouble("offsetL1", station);
         offsetL1[2] = confReader.fetchListValueAsDouble("offsetL1", station);

            // Vector from antenna ARP to L2 phase center [UEN], in meters
         offsetL2[0] = confReader.fetchListValueAsDouble("offsetL2", station);
         offsetL2[1] = confReader.fetchListValueAsDouble("offsetL2", station);
         offsetL2[2] = confReader.fetchListValueAsDouble("offsetL2", station);

         corr.setL1pc( offsetL1 );
         corr.setL2pc( offsetL2 );

      }

      pList.push_back(corr);       // Add to processing list


         // Object to compute wind-up effect
      ComputeWindUp windup( SP3EphList,
                            nominalPos,
                            confReader.getValue( "satDataFile", station ) );
      pList.push_back(windup);       // Add to processing list


         // Declare a NeillTropModel object, setting its parameters
      NeillTropModel neillTM( nominalPos.getAltitude(),
                              nominalPos.getGeodeticLatitude(),
                              confReader.getValueAsInt("dayOfYear", station) );

         // We will need this value later for printing
      double drytropo( neillTM.dry_zenith_delay() );


         // Object to compute the tropospheric data
      ComputeTropModel computeTropo(neillTM);
      pList.push_back(computeTropo);       // Add to processing list


         // Object to compute ionosphere-free combinations to be used
         // as observables in the PPP processing
      ComputeLinear linear2;

         // Read if we should use C1 instead of P1
      if ( usingC1 )
      {
            // WARNING: When using C1 instead of P1 to compute PC combination,
            //          be aware that instrumental errors will NOT cancel,
            //          introducing a bias that must be taken into account by
            //          other means. This won't be taken into account in this
            //          example.
         linear2.addLinear(comb.pcCombWithC1);
      }
      else
      {
         linear2.addLinear(comb.pcCombination);
      }
      linear2.addLinear(comb.lcCombination);
      pList.push_back(linear2);       // Add to processing list


         // Declare a simple filter object to screen PC
      SimpleFilter pcFilter;
      pcFilter.setFilteredType(TypeID::PC);

         // IMPORTANT NOTE:
         // Like in the "filterCode" case, the "filterPC" option allows you to
         // deactivate the "SimpleFilter" object that filters out PC, in case
         // you need to.
      bool filterPC( confReader.getValueAsBoolean( "filterPC", station ) );

         // Check if we are going to use this "SimpleFilter" object or not
      if( filterPC )
      {
         pList.push_back(pcFilter);       // Add to processing list
      }


         // Object to align phase with code measurements
      PhaseCodeAlignment phaseAlign;
      pList.push_back(phaseAlign);       // Add to processing list


         // Object to compute prefit-residuals
      ComputeLinear linear3(comb.pcPrefit);
      linear3.addLinear(comb.lcPrefit);
      pList.push_back(linear3);       // Add to processing list


         // Declare a base-changing object: From ECEF to North-East-Up (NEU)
      XYZ2NEU baseChange(nominalPos);
         // We always need both ECEF and NEU data for 'ComputeDOP', so add this
      pList.push_back(baseChange);


         // Object to compute DOP values
      ComputeDOP cDOP;
      pList.push_back(cDOP);       // Add to processing list


         // Get if we want results in ECEF or NEU reference system
      bool isNEU( confReader.getValueAsBoolean( "USENEU", station ) );


         // Declare solver objects
      SolverPPP   pppSolver(isNEU);
      SolverPPPFB fbpppSolver(isNEU);


         // Get if we want 'forwards-backwards' or 'forwards' processing only
      int cycles( confReader.getValueAsInt("forwardBackwardCycles", station) );


         // Get if we want to process coordinates as white noise
      bool isWN( confReader.getValueAsBoolean( "coordinatesAsWhiteNoise",
                                               station ) );


         // White noise stochastic model
      WhiteNoiseModel wnM(100.0);      // 100 m of sigma


         // Decide what type of solver we will use for this station
      if ( cycles > 0 )
      {

            // In this case, we will use the 'forwards-backwards' solver

            // Check about coordinates as white noise
         if ( isWN )
         {
               // Reconfigure solver
            fbpppSolver.setCoordinatesModel(&wnM);
         }

            // Add solver to processing list
         pList.push_back(fbpppSolver);

      }
      else
      {

            // In this case, we will use the 'forwards-only' solver

            // Check about coordinates as white noise
         if ( isWN )
         {
               // Reconfigure solver
            pppSolver.setCoordinatesModel(&wnM);
         }

            // Add solver to processing list
         pList.push_back(pppSolver);

      }  // End of 'if ( cycles > 0 )'


         // Object to compute tidal effects
      SolidTides solid;


         // Configure ocean loading model
      OceanLoading ocean;
      ocean.setFilename( confReader.getValue( "oceanLoadingFile", station ) );


         // Numerical values (xp, yp) are pole displacements (arcsec).
      double xp( confReader.fetchListValueAsDouble( "poleDisplacements",
                                                    station ) );
      double yp( confReader.fetchListValueAsDouble( "poleDisplacements",
                                                    station ) );
         // Object to model pole tides
      PoleTides pole;
      pole.setXY( xp, yp );


         // This is the GNSS data structure that will hold all the
         // GNSS-related information
      gnssRinex gRin;


         // Prepare for printing
      int precision( confReader.getValueAsInt( "precision", station ) );


         // Let's open the output file
      string outName(confReader.getValue( "outputFile", station ) );

      ofstream outfile;
      outfile.open( outName.c_str(), ios::out );

         // Let's check if we are going to print the model
      bool printmodel( confReader.getValueAsBoolean( "printModel", station ) );

      string modelName;
      ofstream modelfile;

         // Prepare for model printing
      if( printmodel )
      {
         modelName = confReader.getValue( "modelFile", station );
         modelfile.open( modelName.c_str(), ios::out );
      }


         //// *** Now comes the REAL forwards processing part *** ////


         // Loop over all data epochs
      while(rin >> gRin)
      {

            // Store current epoch
         DayTime time(gRin.header.epoch);

            // Compute solid, oceanic and pole tides effects at this epoch
         Triple tides( solid.getSolidTide( time, nominalPos )  +
                       ocean.getOceanLoading( station, time )  +
                       pole.getPoleTide( time, nominalPos )    );


            // Update observable correction object with tides information
         corr.setExtraBiases(tides);

         try
         {

               // Let's process data. Thanks to 'ProcessingList' this is
               // very simple and compact: Just one line of code!!!.
            gRin >> pList;

         }
         catch(DecimateEpoch& d)
         {
               // If we catch a DecimateEpoch exception, just continue.
            continue;
         }
         catch(Exception& e)
         {
            cerr << "Exception for receiver '" << station <<
                    "' at epoch: " << time << "; " << e << endl;
            continue;
         }
         catch(...)
         {
            cerr << "Unknown exception for receiver '" << station <<
                    " at epoch: " << time << endl;
            continue;
         }


            // Ask if we are going to print the model
         if ( printmodel )
         {
            printModel( modelfile,
                        gRin );

         }

            // Check what type of solver we are using
         if ( cycles < 1 )
         {

               // This is a 'forwards-only' filter. Let's print to output
               // file the results of this epoch
            printSolution( outfile,
                           pppSolver,
                           time,
                           cDOP,
                           isNEU,
                           gRin.numSats(),
                           drytropo,
                           precision );

         }  // End of 'if ( cycles < 1 )'



         // The given epoch hass been processed. Let's get the next one

      }  // End of 'while(rin >> gRin)'


         // Close current Rinex observation stream
      rin.close();


         // If we printed the model, we must close the file
      if ( printmodel )
      {
            // Close model file for this station
         modelfile.close();
      }


         // Clear content of SP3 ephemerides object
      SP3EphList.clear();



         //// *** Forwards processing part is over *** ////



         // Now decide what to do: If solver was a 'forwards-only' version,
         // then we are done and should continue with next station.
      if ( cycles < 1 )
      {

            // Close output file for this station
         outfile.close();

            // We are done with this station. Let's show a message
         cout << "Processing finished for station: '" << station
              << "'. Results in file: '" << outName << "'." << endl;

            // Go process next station
         continue;

      }


         //// *** If we got here, it is a 'forwards-backwards' solver *** ////


         // Now, let's do 'forwards-backwards' cycles
      try
      {

         fbpppSolver.ReProcess(cycles);

      }
      catch(Exception& e)
      {

            // If problems arose, issue an message and skip receiver
         cerr << "Exception at reprocessing phase: " << e << endl;
         cerr << "Skipping receiver '" << station << "'." << endl;

            // Close output file for this station
         outfile.close();

            // Go process next station
         continue;

      }  // End of 'try-catch' block



         // Reprocess is over. Let's finish with the last processing

         // Loop over all data epochs, again, and print results
      while( fbpppSolver.LastProcess(gRin) )
      {

         DayTime time(gRin.header.epoch);

         printSolution( outfile,
                        fbpppSolver,
                        time,
                        cDOP,
                        isNEU,
                        gRin.numSats(),
                        drytropo,
                        precision );

      }  // End of 'while( fbpppSolver.LastProcess(gRin) )'


         // We are done. Close and go for next station

         // Close output file for this station
      outfile.close();


         // We are done with this station. Let's show a message
      cout << "Processing finished for station: '" << station
           << "'. Results in file: '" << outName << "'." << endl;


   }  // end of 'while ( (station = confReader.getEachSection()) != "" )'

   return;

}  // End of 'example9::process()'
Beispiel #4
0
//------------------------------------------------------------------------------------
int AfterReadingFiles(int reading) throw(Exception)
{
try {
   int i,j,iret=0;
   double dt;

   if(reading == 1) {

         // compute data interval for this file
      for(j=0,i=1; i<9; i++) { if(PIC.ndt[i]>PIC.ndt[j]) j=i; }
      PIC.DT = PIC.estdt[j];
      PIC.oflog << endl;
      PIC.oflog << "Estimated data interval is " << PIC.DT << " seconds.\n";
      PIC.oflog << "Interpolate to " << PIC.irate << " times the input data rate\n";
      PIC.oflog << "Last data epoch is "
         << PIC.LastEpoch.printf("%04Y/%02m/%02d %02H:%02M:%06.3f = %4F %.3g") << endl;

      if(TimePositionMap.size() == 0) {
         cout << "No position information was found in the input file! Abort.\n";
         PIC.oflog << "No position information was found in the input file! Abort.\n";
         return -1;
      }
      PIC.oflog << endl;

         // dump the map of positions
      if(PIC.DumpMap) {
         PIC.oflog << "Here is all the Time/Position information:\n";
         map<DayTime,PosInfo>::const_iterator itr;
         itr = TimePositionMap.begin();
         i = 0;
         while(itr != TimePositionMap.end()) {
            PIC.oflog << setw(4) << i << " "
               << itr->first.printf("%04Y/%02m/%02d %02H:%02M:%6.3f %4F %10.3g")
               << fixed << setprecision(3)
               << " " << setw(2) << itr->second.N
               << " " << setw(13) << itr->second.X
               << " " << setw(13) << itr->second.Y
               << " " << setw(13) << itr->second.Z
               << " " << setw(13) << itr->second.T
               << " " << setw(7) << itr->second.rms
               << endl;
            itr++;
            i++;
         }
         PIC.oflog << "End of the Time/Position information.\n\n";
      }

         // open output file
      if(!PIC.OutRinexObs.empty()) {
         ofstr.open(PIC.OutRinexObs.c_str(), ios::out);
         if(ofstr.fail()) {
            PIC.oflog << "Failed to open output file " << PIC.OutRinexObs
               << ". Abort.\n";
            return 1;
         }
         else PIC.oflog << "Opened output file " << PIC.OutRinexObs << endl;
         ofstr.exceptions(ios::failbit);
      }
   }
   else if(reading==2) {
      PIC.oflog << "Close the output file\n";
      ofstr.close();
   }

   return iret;
}
catch(Exception& e) { GPSTK_RETHROW(e); }
catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
}
Beispiel #5
0
//------------------------------------------------------------------------------------
// open the file, read header and check for data; then loop over the epochs
// Return 0 ok, <0 fatal error, >0 non-fatal error (ie skip this file)
// 0 ok, 1 couldn't open file, 2 file doesn't have required data
int ReadFile(int nfile, int reading) throw(Exception)
{
try {
   int i,iret;
   RinexObsData rodata;
   RinexObsStream ifstr;

      // open input file
   ifstr.open(PIC.InputObsName[nfile].c_str(),ios::in);
   if(ifstr.fail()) {
      PIC.oflog << "(" << reading << ") Failed to open input file "
         << PIC.InputObsName[nfile] << ". Abort.\n";
      return 1;
   }
   else PIC.oflog << "(" << reading << ") Opened input file "
      << PIC.InputObsName[nfile] << endl;
   ifstr.exceptions(ios::failbit);

      // read header and (on 2nd reading) output
   iret = ProcessHeader(ifstr, nfile, reading);
   if(iret) return iret;

      // loop over epochs in the file
   if(reading == 2)
      LastInterpolated = DayTime::BEGINNING_OF_TIME;

   while(1) {
      try {
         ifstr >> rodata;
      }
      catch(Exception& e) {
         GPSTK_RETHROW(e);
      }
      catch(...) {
         Exception e("Unknown exception in ReadFile() from operator>>");
         GPSTK_THROW(e);
         break;
      }
      if(ifstr.eof()) break;
      if(ifstr.bad()) {
         Exception e("Bad read in ReadFile() from operator>>");
         GPSTK_THROW(e);
      }
      iret = ProcessOneEntireEpoch(rodata,reading);
      if(iret < -1) break;
      if(iret == -1) { iret=0; break; }         // end of file
      if(iret == 1) continue;                   // ignore this epoch
   }

   ifstr.clear();
   ifstr.close();
   
   PIC.oflog << endl << "Finished reading (" << reading
      << ") file " << PIC.InputObsName[nfile] << endl;

   return iret;
}
catch(Exception& e) { GPSTK_RETHROW(e); }
catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
}