RinexObsData.cpp
Go to the documentation of this file.
1 //==============================================================================
2 //
3 // This file is part of GNSSTk, the ARL:UT GNSS Toolkit.
4 //
5 // The GNSSTk is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published
7 // by the Free Software Foundation; either version 3.0 of the License, or
8 // any later version.
9 //
10 // The GNSSTk is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with GNSSTk; if not, write to the Free Software Foundation,
17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 // This software was developed by Applied Research Laboratories at the
20 // University of Texas at Austin.
21 // Copyright 2004-2022, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 // This software was developed by Applied Research Laboratories at the
28 // University of Texas at Austin, under contract to an agency or agencies
29 // within the U.S. Department of Defense. The U.S. Government retains all
30 // rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 // Pursuant to DoD Directive 523024
33 //
34 // DISTRIBUTION STATEMENT A: This software has been approved for public
35 // release, distribution is unlimited.
36 //
37 //==============================================================================
38 
44 #include "StringUtils.hpp"
45 #include "RinexObsData.hpp"
46 #include "RinexObsStream.hpp"
47 #include "CivilTime.hpp"
48 
49 using namespace gnsstk::StringUtils;
50 using namespace std;
51 
52 namespace gnsstk
53 {
54 
55  // Definition of static variable to be used across RinexObsData objects
57 
59  {
60  // is there anything to write?
61  if( (epochFlag==0 || epochFlag==1 || epochFlag==6)
62  && (numSvs==0 || obs.empty()) ) return;
63  if((epochFlag>=2 && epochFlag<=5) &&
64  auxHeader.NumberHeaderRecordsToBeWritten()==0) return;
65 
66  RinexObsStream& strm = dynamic_cast<RinexObsStream&>(ffs);
67  string line;
68 
69  // first the epoch line to 'line'
70  line = writeTime(time);
71  line += string(2, ' ');
72  line += rightJustify(asString<short>(epochFlag), 1);
73  line += rightJustify(asString<short>(numSvs), 3);
74 
75  // write satellite ids to 'line'
76  const int maxPrnsPerLine = 12;
77  int satsWritten = 0;
78  RinexSatMap::const_iterator obsItr = obs.begin();
79  if(epochFlag==0 || epochFlag==1 || epochFlag==6)
80  {
81  while ((obsItr != obs.end()) && (satsWritten < maxPrnsPerLine))
82  {
83  RinexSatID prn((*obsItr).first);
84  line += prn.toString();
85  satsWritten++;
86  obsItr++;
87  }
88 
89  // add clock offset
90  if(clockOffset != 0.0)
91  {
92  line += string(68 - line.size(), ' ');
93  line += rightJustify(asString(clockOffset, 9), 12);
94  }
95 
96  // continuation lines
97  while (satsWritten != (int)obs.size())
98  {
99  if ((satsWritten % maxPrnsPerLine) == 0)
100  {
101  strm << line << endl;
102  strm.lineNumber++;
103  line = string(32, ' ');
104  }
105  RinexSatID prn(obsItr->first);
106  line += prn.toString();
107  satsWritten++;
108  obsItr++;
109  }
110  }
111 
112  // write the epoch line
113  strm << line << endl;
114  strm.lineNumber++;
115 
116  // write the auxiliary header records, if any
117  if(epochFlag >= 2 && epochFlag <= 5)
118  {
119  try {
120  auxHeader.WriteHeaderRecords(strm);
121  }
122  catch(FFStreamError& e)
123  {
124  GNSSTK_RETHROW(e);
125  }
126  catch(StringException& e)
127  {
128  GNSSTK_RETHROW(e);
129  }
130  }
131 
132  // write the obs data
133  else if (!obs.empty())
134  {
135  // write out the observations themselves
136  obsItr = obs.begin();
137 
138  const int maxObsPerLine = 5;
139 
140  while(obsItr != obs.end())
141  {
142  vector<RinexObsType>::iterator obsTypeItr =
143  strm.header.obsTypeList.begin();
144 
145  line.erase();
146  int obsWritten = 0;
147 
148  while (obsTypeItr != strm.header.obsTypeList.end())
149  {
150  if ( ((obsWritten % maxObsPerLine) == 0) &&
151  (obsWritten != 0))
152  {
153  strm << line << endl;
154  strm.lineNumber++;
155  line.erase();
156  }
157 
158  RinexObsTypeMap::const_iterator rotmi(obsItr->second.find(*obsTypeItr));
159  RinexDatum thisData;
160  if (rotmi != obsItr->second.end())
161  thisData = rotmi->second;
162  line += rightJustify(asString(thisData.data,3),14);
163  if (thisData.lli == 0)
164  line += string(1, ' ');
165  else
166  line += rightJustify(asString<short>(thisData.lli),1);
167  if (thisData.ssi == 0)
168  line += string(1, ' ');
169  else
170  line += rightJustify(asString<short>(thisData.ssi),1);
171  obsWritten++;
172  obsTypeItr++;
173  }
174  strm << line << endl;
175  strm.lineNumber++;
176  obsItr++;
177  }
178  }
179  } // end RinexObsData::reallyPutRecord
180 
181 
183  {
184  RinexObsStream& strm = dynamic_cast<RinexObsStream&>(ffs);
185 
186  // If the header hasn't been read, read it...
187  if(!strm.headerRead) strm >> strm.header;
188 
189  // Clear out this object
190  RinexObsHeader& hdr = strm.header;
191 
192  RinexObsData rod;
193  *this=rod;
194 
195  string line;
196 
197  // The following block handles Rinex2 observation files that have
198  // empty (but otherwise valid) epoch lines and comments in the middle
199  // of the observation section. This is frequent in Rinex2 files that
200  // are 'spliced' every hour.
201  bool isValidEpochLine(false);
202  while( !isValidEpochLine && !strm.eof())
203  {
204  // Get line
205  strm.formattedGetLine(line, true);
206 
207  isValidEpochLine = true;
208 
209  try
210  {
211  // Rinex2 observation lines have a 80 characters length limit
212  if( line.size()>80 ) isValidEpochLine = false;
213 
214  // Try to read the epoch
215  CommonTime tempEpoch = parseTime(line, hdr);
216 
217  // We also have to check if the epoch is valid
218  if( tempEpoch == CommonTime::BEGINNING_OF_TIME )
219  {
220  isValidEpochLine = false;
221  }
222 
223  // Check if it is a number; if not, an exception will be thrown
224  (void)asInt(line.substr(29,3));
225  }
226  catch(...)
227  {
228  // Any problem will cause the loop to be repeated
229  isValidEpochLine = false;
230  }
231 
232  } // End of 'while( !isValidEpochLine )'
233 
234  // process the epoch line, including SV list and clock bias
235  epochFlag = asInt(line.substr(28,1));
236  if ((epochFlag < 0) || (epochFlag > 6))
237  {
238  FFStreamError e("Invalid epoch flag: " + asString(epochFlag));
239  GNSSTK_THROW(e);
240  }
241 
242  // Not all epoch flags are required to have a time.
243  // Specifically, 0, 1, 5, 6 must have an epoch time and
244  // it is optional for 2, 3, 4.
245  // If there is and epoch time, parse it and load it in
246  // the member "time".
247  // If epoch flag = 0, 1, 5, or 6 and there is NO epoch time,
248  // then throw an error.
249  // If epoch flag = 2, 3, or 4 and there is no epoch time,
250  // use the time of the previous record.
251  bool noEpochTime = (line.substr(0,26) == string(26, ' '));
252  if (noEpochTime &&
253  (epochFlag==0 || epochFlag==1 || epochFlag==5 || epochFlag==6 ))
254  {
255  FFStreamError e("Required epoch time missing: " + line);
256  GNSSTK_THROW(e);
257  }
258  else if (noEpochTime)
259  {
260  time = previousTime;
261  }
262  else
263  {
264  time = parseTime(line, hdr);
265  previousTime = time;
266  }
267 
268  numSvs = asInt(line.substr(29,3));
269 
270  if( line.size() > 68 )
271  clockOffset = asDouble(line.substr(68, 12));
272  else
273  clockOffset = 0.0;
274 
275  // Now read the observations ...
276  if(epochFlag==0 || epochFlag==1 || epochFlag==6)
277  {
278  int isv, ndx, line_ndx;
279  vector<SatID> satIndex(numSvs);
280  int col=30;
281  for (isv=1, ndx=0; ndx<numSvs; isv++, ndx++)
282  {
283  if(! (isv % 13))
284  {
285  strm.formattedGetLine(line);
286  isv = 1;
287  if(line.size() > 80)
288  {
289  FFStreamError err("Invalid line size:" + asString(line.size()));
290  GNSSTK_THROW(err);
291  }
292  }
293  try
294  {
295  satIndex[ndx] = RinexSatID(line.substr(col+isv*3-1, 3));
296  }
297  catch (Exception& e)
298  {
299  FFStreamError ffse(e);
300  GNSSTK_THROW(ffse);
301  }
302  }
303 
304  for (isv=0; isv < numSvs; isv++)
305  {
306  short numObs = hdr.obsTypeList.size();
307  for (ndx=0, line_ndx=0; ndx < numObs; ndx++, line_ndx++)
308  {
309  SatID sat = satIndex[isv];
310  RinexObsType obs_type = hdr.obsTypeList[ndx];
311  if (! (line_ndx % 5))
312  {
313  strm.formattedGetLine(line);
314  line_ndx = 0;
315  if (line.size() > 80)
316  {
317  FFStreamError err("Invalid line size:" + asString(line.size()));
318  GNSSTK_THROW(err);
319  }
320  }
321 
322  line.resize(80, ' ');
323 
324  obs[sat][obs_type].data = asDouble(line.substr(line_ndx*16, 14));
325  obs[sat][obs_type].lli = asInt( line.substr(line_ndx*16+14, 1));
326  obs[sat][obs_type].ssi = asInt( line.substr(line_ndx*16+15, 1));
327  }
328  }
329  }
330  // ... or the auxiliary header information
331  else if(numSvs > 0)
332  {
333  auxHeader.clear();
334  for(int i=0; i<numSvs; i++)
335  {
336  strm.formattedGetLine(line);
338  try
339  {
340  auxHeader.ParseHeaderRecord(line);
341  }
342  catch(FFStreamError& e)
343  {
344  GNSSTK_RETHROW(e);
345  }
346  catch(StringException& e)
347  {
348  GNSSTK_RETHROW(e);
349  }
350  }
351  }
352 
353  return;
354 
355  } // end of reallyGetRecord()
356 
357 
359  const RinexObsHeader& hdr) const
360  {
361  try
362  {
363  // check if the spaces are in the right place - an easy
364  // way to check if there's corruption in the file
365  if ( (line[0] != ' ') ||
366  (line[3] != ' ') ||
367  (line[6] != ' ') ||
368  (line[9] != ' ') ||
369  (line[12] != ' ') ||
370  (line[15] != ' '))
371  {
372  FFStreamError e("Invalid time format");
373  GNSSTK_THROW(e);
374  }
375 
376  // if there's no time, just return a bad time
377  if (line.substr(0,26) == string(26, ' '))
378  {
380  }
381 
382  int year, month, day, hour, min;
383  double sec;
384  int yy = (static_cast<CivilTime>(hdr.firstObs)).year/100;
385  yy *= 100;
386 
387  year = asInt( line.substr(1, 2 ));
388  month = asInt( line.substr(4, 2 ));
389  day = asInt( line.substr(7, 2 ));
390  hour = asInt( line.substr(10, 2 ));
391  min = asInt( line.substr(13, 2 ));
392  sec = asDouble(line.substr(15, 11));
393 
394  // Real Rinex has epochs 'yy mm dd hr 59 60.0' surprisingly often....
395  double ds=0;
396  if(sec >= 60.) { ds=sec; sec=0.0; }
397  CivilTime rv(yy+year, month, day, hour, min, sec, TimeSystem::GPS);
398  if(ds != 0) rv.second += ds;
399 
400  return rv.convertToCommonTime();
401  }
402  // string exceptions for substr are caught here
403  catch (std::exception &e)
404  {
405  FFStreamError err("std::exception: " + string(e.what()));
406  GNSSTK_THROW(err);
407  }
408  catch (gnsstk::Exception& e)
409  {
410  std::string text;
411  for(size_t i=0; i<e.getTextCount(); i++) text += e.getText(i);
412  FFStreamError err("gnsstk::Exception in parseTime(): " + text);
413  GNSSTK_THROW(err);
414  }
415 
416  }
417 
418  string RinexObsData::writeTime(const CommonTime& dt) const
419  {
421  {
422  return string(26, ' ');
423  }
424 
425  string line;
426  CivilTime civTime(dt);
427  line = string(1, ' ');
428  line += rightJustify(asString<short>(civTime.year),2);
429  line += string(1, ' ');
430  line += rightJustify(asString<short>(civTime.month),2);
431  line += string(1, ' ');
432  line += rightJustify(asString<short>(civTime.day),2);
433  line += string(1, ' ');
434  line += rightJustify(asString<short>(civTime.hour),2);
435  line += string(1, ' ');
436  line += rightJustify(asString<short>(civTime.minute),2);
437  line += rightJustify(asString(civTime.second, 7),11);
438 
439  return line;
440  }
441 
442 
443  void RinexObsData::dump(ostream& s) const
444  {
445  if (obs.empty())
446  return;
447 
448  s << "Dump of RinexObsData - time: ";
449  s << writeTime(time) << " epochFlag: "
450  << " " << epochFlag << " numSvs: " << numSvs
451  << fixed << setprecision(6)
452  << " clk offset: " << clockOffset << endl;
453  if(epochFlag == 0 || epochFlag == 1)
454  {
455  RinexSatMap::const_iterator it;
456  for(it=obs.begin(); it!=obs.end(); it++)
457  {
458  s << "Sat " << setw(2) << RinexSatID(it->first);
459  RinexObsTypeMap::const_iterator jt;
460  for(jt=it->second.begin(); jt!=it->second.end(); jt++)
461  {
462  s << " " << jt->first.type << ":" << fixed << setprecision(3)
463  << " " << setw(12) << jt->second.data
464  << "/" << jt->second.lli << "/" << jt->second.ssi;
465  }
466  s << endl;
467  }
468  }
469  else
470  {
471  s << "aux. header info:\n";
472  auxHeader.dump(s);
473  }
474  }
475 
476 } // namespace
gnsstk::RinexObsStream
Definition: RinexObsStream.hpp:67
gnsstk::RinexObsData::previousTime
static gnsstk::CommonTime previousTime
Definition: RinexObsData.hpp:148
gnsstk::StringUtils::asInt
long asInt(const std::string &s)
Definition: StringUtils.hpp:713
gnsstk::RinexDatum
Storage for single RINEX OBS data measurements.
Definition: RinexDatum.hpp:55
example6.day
day
Definition: example6.py:66
gnsstk::CivilTime::year
int year
Definition: CivilTime.hpp:198
gnsstk::FFStream
Definition: FFStream.hpp:119
StringUtils.hpp
example6.year
year
Definition: example6.py:64
gnsstk::RinexObsHeader::obsTypeList
std::vector< RinexObsType > obsTypeList
NUMBER & TYPES OF OBSERV.
Definition: RinexObsHeader.hpp:272
gnsstk::RinexObsData::parseTime
CommonTime parseTime(const std::string &line, const RinexObsHeader &hdr) const
Definition: RinexObsData.cpp:358
gnsstk::FFTextStream::formattedGetLine
void formattedGetLine(std::string &line, const bool expectEOF=false)
Definition: FFTextStream.cpp:149
gnsstk::FFTextStream::lineNumber
unsigned int lineNumber
Definition: FFTextStream.hpp:98
gnsstk::Exception::getTextCount
size_t getTextCount() const
Returns the number of text strings in the exception text stack.
Definition: Exception.cpp:152
example6.hour
hour
Definition: example6.py:67
gnsstk::SatID
Definition: SatID.hpp:89
RinexObsStream.hpp
gnsstk::StringUtils::asString
std::string asString(IonexStoreStrategy e)
Convert a IonexStoreStrategy to a whitespace-free string name.
Definition: IonexStoreStrategy.cpp:46
gnsstk::CivilTime::day
int day
Definition: CivilTime.hpp:200
gnsstk::CommonTime::BEGINNING_OF_TIME
static const GNSSTK_EXPORT CommonTime BEGINNING_OF_TIME
earliest representable CommonTime
Definition: CommonTime.hpp:102
gnsstk::RinexDatum::data
double data
The actual data point.
Definition: RinexDatum.hpp:76
gnsstk
For Sinex::InputHistory.
Definition: BasicFramework.cpp:50
gnsstk::Exception
Definition: Exception.hpp:151
gnsstk::StringUtils::stripTrailing
std::string & stripTrailing(std::string &s, const std::string &aString, std::string::size_type num=std::string::npos)
Definition: StringUtils.hpp:1453
gnsstk::RinexDatum::lli
short lli
See the RINEX Spec. for an explanation.
Definition: RinexDatum.hpp:78
gnsstk::RinexDatum::ssi
short ssi
See the RINEX Spec. for an explanation.
Definition: RinexDatum.hpp:80
gnsstk::Exception::getText
std::string getText(const size_t &index=0) const
Definition: Exception.cpp:139
gnsstk::RinexObsData::reallyGetRecord
virtual void reallyGetRecord(FFStream &s)
Definition: RinexObsData.cpp:182
gnsstk::CivilTime::convertToCommonTime
virtual CommonTime convertToCommonTime() const
Definition: CivilTime.cpp:75
gnsstk::RinexObsData::writeTime
std::string writeTime(const CommonTime &dt) const
Definition: RinexObsData.cpp:418
example4.time
time
Definition: example4.py:103
example4.err
err
Definition: example4.py:126
gnsstk::RinexObsType
RINEX Observation Types.
Definition: RinexObsHeader.hpp:67
gnsstk::CommonTime
Definition: CommonTime.hpp:84
example6.clockOffset
clockOffset
Definition: example6.py:110
gnsstk::min
T min(const SparseMatrix< T > &SM)
Maximum element - return 0 if empty.
Definition: SparseMatrix.hpp:858
CivilTime.hpp
gnsstk::StringUtils::asDouble
double asDouble(const std::string &s)
Definition: StringUtils.hpp:705
GNSSTK_RETHROW
#define GNSSTK_RETHROW(exc)
Definition: Exception.hpp:369
RinexObsData.hpp
gnsstk::CivilTime::minute
int minute
Definition: CivilTime.hpp:202
gnsstk::RinexObsData::dump
virtual void dump(std::ostream &s) const
Definition: RinexObsData.cpp:443
gnsstk::CivilTime
Definition: CivilTime.hpp:55
gnsstk::StringUtils
Definition: IonexStoreStrategy.cpp:44
gnsstk::StringUtils::rightJustify
std::string & rightJustify(std::string &s, const std::string::size_type length, const char pad=' ')
Definition: StringUtils.hpp:1557
gnsstk::RinexObsHeader::firstObs
CommonTime firstObs
TIME OF FIRST OBS.
Definition: RinexObsHeader.hpp:274
gnsstk::RinexSatID::toString
std::string toString() const noexcept
Definition: RinexSatID.cpp:204
gnsstk::RinexSatID
Definition: RinexSatID.hpp:63
gnsstk::TimeSystem::GPS
@ GPS
GPS system time.
std
Definition: Angle.hpp:142
gnsstk::CivilTime::month
int month
Definition: CivilTime.hpp:199
gnsstk::CivilTime::second
double second
Definition: CivilTime.hpp:203
example6.auxHeader
auxHeader
Definition: example6.py:109
GNSSTK_THROW
#define GNSSTK_THROW(exc)
Definition: Exception.hpp:366
gnsstk::RinexObsData::reallyPutRecord
virtual void reallyPutRecord(FFStream &s) const
Definition: RinexObsData.cpp:58
example6.month
month
Definition: example6.py:65
gnsstk::RinexObsHeader
Definition: RinexObsHeader.hpp:107
gnsstk::RinexObsStream::header
RinexObsHeader header
The header for this file.
Definition: RinexObsStream.hpp:112
gnsstk::RinexObsStream::headerRead
bool headerRead
Whether or not the RinexObsHeader has been read.
Definition: RinexObsStream.hpp:109
gnsstk::RinexObsData
Definition: RinexObsData.hpp:68
example6.epochFlag
epochFlag
Definition: example6.py:111
gnsstk::CivilTime::hour
int hour
Definition: CivilTime.hpp:201


gnsstk
Author(s):
autogenerated on Wed Oct 25 2023 02:40:41