SP3Data.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 
41 
42 #include "SP3Stream.hpp"
43 #include "SP3Header.hpp"
44 #include "SP3Data.hpp"
45 #include "StringUtils.hpp"
46 #include "CivilTime.hpp"
47 #include "GPSWeekSecond.hpp"
48 
49 using namespace gnsstk::StringUtils;
50 using namespace std;
51 
52 namespace gnsstk
53 {
55  {
56  // cast the stream to be an SP3Stream
57  SP3Stream& strm = dynamic_cast<SP3Stream&>(ffs);
58 
59  // status says which records have been written already this call
60  // status = 1 if an epoch record was read
61  // status = 2 if an P or V record was read
62  // status = 3 if an EP or EV record was read
63  int status = 0;
64 
65  // version to be written out is determined by written header (stored in strm)
66  bool isVerA = (strm.header.getVersion() == SP3Header::SP3a);
67  bool isVerC = (strm.header.getVersion() == SP3Header::SP3c) || (strm.header.getVersion() == SP3Header::SP3d);
68 
69  // correlation flag will be set if there is an EP/EV record
70  correlationFlag = false;
71 
72  // default for the 'c' arrays is all zeros
73  sdev[0] = sdev[1] = sdev[2] = sdev[3] = 0;
74  sig[0] = sig[1] = sig[2] = sig[3] = 0;
75  correlation[0] = correlation[1] = correlation[2]
76  = correlation[3] = correlation[4] = correlation[5] = 0;
77 
78  // TimeSystem for this stream
79  TimeSystem timeSystem;
80  timeSystem = strm.header.timeSystem;
81 
82  // loop until an error occurs, or until the entire record (which may consist
83  // of two lines) is read.
84  bool unexpectedEOF=false;
85  while(1) {
86 
87  // set the time to be the last epoch read by the stream
88  time = strm.currentEpoch;
89 
90  // process the lastLine string stored in strm - contains the last line read
91  // empty record
92  if(strm.lastLine.size() < 3) { // 3 b/c it may contain "EOF"
93  // nothing in lastLine - do nothing here, get another line
94  ;
95  }
96 
97  // EOF has been read
98  else if(strm.lastLine.substr(0,3) == string("EOF")) {
99  // if a data record has already been processed during this call,
100  // then return here, and let the next call process this EOF.
101  // This gives the caller a chance to process the data before hitting EOF.
102 
103  // if an epoch record was processed this call, that's an error
104  if(status == 1) {
105  FFStreamError ffse("EOF was found immediately after epoch line");
106  GNSSTK_THROW(ffse);
107  }
108 
109  // some other record was processed this call, so quit
110  if(status > 1) break;
111 
112  // this next read had better fail - if it does not, then there is
113  // an "EOF" that is followed by something other than the file EOF.
114  try {
115  strm.formattedGetLine(strm.lastLine, true); // true is 'expect EOF'
116  }
117  catch(EndOfFile& err) {
118  break; // normal exit; the stream will now be at eof()
119  }
120 
121  // the GetLine succeeded, so this is an error
122  FFStreamError err("EOF text found, followed by: " + strm.lastLine);
123  GNSSTK_THROW(err);
124  }
125 
126  // Epoch line read
127  else if(strm.lastLine[0] == '*') {
128 
129  // if an epoch record was already processed this call, that's an error
130  // TD consider simply removing this if(status==1) entirely. Some SP3 files
131  // particularly those generated from a realtime stream, have consecutive
132  // epoch lines. Why would we not just ignore the epoch with no data?
133  if(status == 1) {
134  FFStreamError ffse("Consecutive epoch records found");
135  GNSSTK_THROW(ffse);
136  }
137 
138  // if another record has been process during this call, quit now
139  if(status > 1) break;
140 
141  // process an epoch record
142  status = 1;
143 
144  // mark this record as non-data, in case another P|V record is not read
145  sat = SatID();
146 
147  // warn if the line is short but not too short
148  if(strm.lastLine.size() <= 30 && strm.lastLine.size() > 26)
149  strm.warnings.push_back(string("Warning (SP3 std): short epoch line: ")
150  + strm.lastLine);
151 
152  // throw if the line is short
153  if(strm.lastLine.size() <= 26) { // some igs files cut seconds short 30)
154  FFStreamError err("Invalid line length "
155  + asString(strm.lastLine.size()));
156  GNSSTK_THROW(err);
157  }
158 
159  // parse the epoch line
160  RecType = strm.lastLine[0];
161  int year = asInt(strm.lastLine.substr(3,4));
162  int month = asInt(strm.lastLine.substr(8,2));
163  int dom = asInt(strm.lastLine.substr(11,2));
164  int hour = asInt(strm.lastLine.substr(14,2));
165  int minute = asInt(strm.lastLine.substr(17,2));
166  double second = asInt(strm.lastLine.substr(20,10));
167  CivilTime t;
168  try {
169  t = CivilTime(year, month, dom, hour, minute, second, timeSystem);
170  }
171  catch (gnsstk::Exception& e) {
172  FFStreamError fe("Invalid time in:" + strm.lastLine);
173  GNSSTK_THROW(fe);
174  }
175  time = strm.currentEpoch = static_cast<CommonTime>(t);
176  }
177 
178  // P or V record read
179  else if(strm.lastLine[0] == 'P' || strm.lastLine[0] == 'V') {
180 
181  // if another record has been process during this call, quit now
182  if(status > 0) break;
183 
184  // process this P|V
185  status = 2;
186  RecType = strm.lastLine[0]; // P or V
187 
188  // if its version c and the line is short (<80) but valid(>59),
189  // then add blanks at the end to make it 80 character.
190  if (isVerC && strm.lastLine.size() < 80 && strm.lastLine.size() > 59)
191  leftJustify(strm.lastLine,80);
192 
193  // throw if the line is short
194  if ((!isVerC && strm.lastLine.size() < 60) ||
195  (isVerC && strm.lastLine.size() < 80) ) {
196  FFStreamError err("Invalid line length ("
197  + asString(strm.lastLine.size())
198  + ") for line:\n" + strm.lastLine);
199  GNSSTK_THROW(err);
200  }
201 
202  // parse the line
203  sat = static_cast<SatID>(SP3SatID(strm.lastLine.substr(1,3)));
204 
205  x[0] = asDouble(strm.lastLine.substr(4,14)); // XYZ
206  x[1] = asDouble(strm.lastLine.substr(18,14));
207  x[2] = asDouble(strm.lastLine.substr(32,14));
208  clk = asDouble(strm.lastLine.substr(46,14)); // Clock
209 
210  // handle NGA extension to SP3a - the event flag
211  eventFlag = false;
212  if (isVerA && (RecType == 'P')
213  && (strm.lastLine.size() >= 75)
214  && (strm.lastLine[74] == 'E'))
215  {
216  eventFlag = true;
217  strm.header.allowSP3aEvents = true;
218  }
219 
220  // the rest is version c only
221  if(isVerC) {
222  sig[0] = asInt(strm.lastLine.substr(61,2)); // sigma XYZ
223  sig[1] = asInt(strm.lastLine.substr(64,2));
224  sig[2] = asInt(strm.lastLine.substr(67,2));
225  sig[3] = asInt(strm.lastLine.substr(70,3)); // sigma clock
226 
227  if(RecType == 'P') { // P flags
228  clockEventFlag = clockPredFlag
229  = orbitManeuverFlag = orbitPredFlag = false;
230  if(strm.lastLine[74] == 'E') clockEventFlag = true;
231  if(strm.lastLine[75] == 'P') clockPredFlag = true;
232  if(strm.lastLine[78] == 'M') orbitManeuverFlag = true;
233  if(strm.lastLine[79] == 'P') orbitPredFlag = true;
234  }
235  }
236  }
237 
238  // EP or EV correlation record read
239  else if(strm.lastLine[0] == 'E' &&
240  (strm.lastLine[1] == 'P' || strm.lastLine[1] == 'V'))
241  {
242  // throw if correlation record did not follow corresponding P|V record
243  if(status != 2 || strm.lastLine[1] != RecType) {
244  Exception e("correlation EP|V record mismatched with previous record");
245  GNSSTK_THROW(e);
246  }
247 
248  // process EP|V record
249  status = 3;
250 
251  // throw if line is short
252  if(strm.lastLine.size()<80) {
253  FFStreamError err("Invalid SP3c correlation line length ("
254  + asString(strm.lastLine.size())
255  + ") for line:\n" + strm.lastLine);
256  GNSSTK_THROW(err);
257  }
258 
259  // parse the line
260  sdev[0] = abs(asInt(strm.lastLine.substr(4,4)));
261  sdev[1] = abs(asInt(strm.lastLine.substr(9,4)));
262  sdev[2] = abs(asInt(strm.lastLine.substr(14,4)));
263  sdev[3] = abs(asInt(strm.lastLine.substr(19,7)));
264  correlation[0] = asInt(strm.lastLine.substr(27,8));
265  correlation[1] = asInt(strm.lastLine.substr(36,8));
266  correlation[2] = asInt(strm.lastLine.substr(45,8));
267  correlation[3] = asInt(strm.lastLine.substr(54,8));
268  correlation[4] = asInt(strm.lastLine.substr(63,8));
269  correlation[5] = asInt(strm.lastLine.substr(72,8));
270 
271  // tell the caller that correlation data is now present
272  correlationFlag = true;
273  }
274 
275  else { // Unknown record
276  FFStreamError err("Unknown record label " + strm.lastLine.substr(0,2));
277  GNSSTK_THROW(err);
278  }
279 
280  // be tolerant of files without EOF -- IGS!
281  // if previous iteration of the loop found unexpected EOF, then quit here.
282  if(unexpectedEOF) {
283  // add a warning
284  strm.warnings.push_back(string("Warning (SP3 std): Unexpected EOF"));
285 
286  // clear the buffer so it won't be (re)processed next call
287  strm.lastLine = string("EOF");
288 
289  // clear the eof bit and return, so the user will process this SP3Data
290  strm.clear(ios::eofbit);
291  status = 3;
292  }
293  else { // normal flow
294  // read next line into the lastLine
295  try {
296  strm.formattedGetLine(strm.lastLine);
297  }
298  catch(FFStreamError& err) {
299  string what = err.what().substr(0,21);
300  //cout << "Found unexpected EOF" << endl;
301  //cout << "what is " << what << endl;
302  //cout << "Here is the buffer:\n" << strm.lastLine << endl;
303  if(what == string("text 0:Unexpected EOF")) {
304  unexpectedEOF = true;
305 
306  // there could still be unprocessed data in the buffer:
307  if(strm.lastLine.size() < 3) status = 3; // nothing there, quit
308  else status = 0; // go back and process it
309  }
310  else
312  }
313  }
314 
315  if(status == 3) break; // quit if found EOF or EP|EV was processed, but
316  // go back if lastLine was empty (0)
317  // or if epoch was processed (1)
318  // or if P|V was processed (2)
319 
320  } // end while loop processing records
321 
322  } // end reallyGetRecord()
323 
325  {
326  string line;
327 
328  // cast the stream to be an SP3Stream
329  SP3Stream& strm = dynamic_cast<SP3Stream&>(ffs);
330 
331  // version to be written out is determined by written header (stored in strm)
332  bool isVerA = (strm.header.getVersion() == SP3Header::SP3a);
333  bool isVerC = (strm.header.getVersion() == SP3Header::SP3c) || (strm.header.getVersion() == SP3Header::SP3d);
334 
335  // output Epoch Header Record
336  if(RecType == '*') {
337  CivilTime civTime(time);
338  line = "* ";
339  line += civTime.printf(" %4Y %2m %2d %2H %2M");
340  line += " " + rightJustify(civTime.printf("%.8f"),11);
341  }
342 
343  // output Position and Clock OR Velocity and Clock Rate Record
344  else {
345  line = RecType; // P or V
346  if (isVerA) {
347  if(sat.system != SatelliteSystem::GPS) {
348  FFStreamError fse("Cannot output non-GPS to SP3a");
349  GNSSTK_THROW(fse);
350  }
351  line += rightJustify(asString(sat.id),3);
352  }
353  else
354  line += static_cast<SP3SatID>(sat).toString(); // sat ID
355 
356  line += rightJustify(asString(x[0],6),14); // XYZ
357  line += rightJustify(asString(x[1],6),14);
358  line += rightJustify(asString(x[2],6),14);
359  line += rightJustify(asString(clk,6),14); // Clock
360 
361  // handle NGA extension to SP3a
362  if(isVerA && strm.header.allowSP3aEvents
363  && (RecType == 'P') && eventFlag)
364  {
365  line += string(14,' ');
366  line += 'E';
367  }
368 
369  if(isVerC) {
370  line += rightJustify(asString(sig[0]),3); // sigma XYZ
371  line += rightJustify(asString(sig[1]),3);
372  line += rightJustify(asString(sig[2]),3);
373  line += rightJustify(asString(sig[3]),4); // sigma Clock
374 
375  if(RecType == 'P') { // flags or blanks
376  line += string(" ");
377  line += (clockEventFlag ? string("E") : string(" "));
378  line += (clockPredFlag ? string("P") : string(" "));
379  line += string(" ");
380  line += (orbitManeuverFlag ? string("M") : string(" "));
381  line += (orbitPredFlag ? string("P") : string(" "));
382  }
383  }
384 
385  // if version is 'c' and correlation flag is set,
386  // then output the P|V Correlation Record
387  if(isVerC && correlationFlag) {
388 
389  // first output the P|V record you just built
390  strm << line << endl;
391  strm.lineNumber++;
392 
393  // now build and output the correlation record
394  if(RecType == 'P') // P or V
395  line = "EP ";
396  else
397  line = "EV ";
398  line += rightJustify(asString(sdev[0]),5); // stddev X
399  line += rightJustify(asString(sdev[1]),5); // stddev Y
400  line += rightJustify(asString(sdev[2]),5); // stddev Z
401  line += rightJustify(asString(sdev[3]),8); // stddev Clk
402  for(int i=0; i<6; i++) // correlations
403  line += rightJustify(asString(correlation[i]),9);
404  }
405  }
406 
407  // write the line just built
408  strm << line << endl;
409  strm.lineNumber++;
410 
411  } // end reallyPutRecord()
412 
413  void SP3Data::dump(ostream& s, bool includeC) const noexcept
414  {
415  // dump record type (PV*), sat id, and current epoch
416  s << RecType << " " << static_cast<SP3SatID>(sat).toString() << " "
417  << (static_cast<CivilTime>(time)).printf("%Y/%02m/%02d %2H:%02M:%06.3f")
418  << " = "
419  << (static_cast<GPSWeekSecond>(time)).printf("%F/%10.3g");
420 
421  if(RecType != '*') { // not epoch line
422  s << fixed << setprecision(6)
423  << " X=" << setw(14) << x[0] // XYZ
424  << " Y=" << setw(14) << x[1]
425  << " Z=" << setw(14) << x[2]
426  << " C=" << setw(14) << clk; // clk
427 
428  if(includeC) {
429  s << " sX=" << setw(2) << sig[0] // sigma XYZ
430  << " sY=" << setw(2) << sig[1]
431  << " sZ=" << setw(2) << sig[2]
432  << " sC=" << setw(3) << sig[3]; // sigma clock
433 
434  if(RecType == 'P') // flags
435  s << " " << (clockEventFlag ? "clockEvent" : "-")
436  << " " << (clockPredFlag ? "clockPrediction" : "-")
437  << " " << (orbitManeuverFlag ? "orbitManeuver" : "-")
438  << " " << (orbitPredFlag ? "orbitPrediction" : "-");
439 
440  if(correlationFlag) // stddevs and correlations
441  s << endl
442  << " and E" << RecType
443  << " cXX=" << setw(4) << sdev[0]
444  << " cYY=" << setw(4) << sdev[1]
445  << " cZZ=" << setw(4) << sdev[2]
446  << " cCC=" << setw(7) << sdev[3]
447  << " cXY=" << setw(8) << correlation[0]
448  << " cXZ=" << setw(8) << correlation[1]
449  << " cXC=" << setw(8) << correlation[2]
450  << " cYZ=" << setw(8) << correlation[3]
451  << " cYC=" << setw(8) << correlation[4]
452  << " cZC=" << setw(8) << correlation[5];
453  }
454  }
455 
456  s << endl;
457 
458  } // end dump()
459 
460 } // namespace
gnsstk::StringUtils::asInt
long asInt(const std::string &s)
Definition: StringUtils.hpp:713
SP3Header.hpp
SP3Data.hpp
gnsstk::FFStream
Definition: FFStream.hpp:119
StringUtils.hpp
gnsstk::SP3Header::SP3d
@ SP3d
SP3 version d (Expanded Satellites and Comments)
Definition: SP3Header.hpp:80
example6.year
year
Definition: example6.py:64
gnsstk::SP3Header::allowSP3aEvents
bool allowSP3aEvents
If true, file may contain NGA SP3a events.
Definition: SP3Header.hpp:179
gnsstk::FFTextStream::formattedGetLine
void formattedGetLine(std::string &line, const bool expectEOF=false)
Definition: FFTextStream.cpp:149
gnsstk::SP3Stream::header
SP3Header header
SP3Header for this file.
Definition: SP3Stream.hpp:88
gnsstk::FFTextStream::lineNumber
unsigned int lineNumber
Definition: FFTextStream.hpp:98
example6.hour
hour
Definition: example6.py:67
gnsstk::SatID
Definition: SatID.hpp:89
gnsstk::StringUtils::asString
std::string asString(IonexStoreStrategy e)
Convert a IonexStoreStrategy to a whitespace-free string name.
Definition: IonexStoreStrategy.cpp:46
example6.minute
minute
Definition: example6.py:68
gnsstk
For Sinex::InputHistory.
Definition: BasicFramework.cpp:50
gnsstk::GPSWeekSecond
Definition: GPSWeekSecond.hpp:56
gnsstk::Exception
Definition: Exception.hpp:151
gnsstk::SatelliteSystem::GPS
@ GPS
example6.second
second
Definition: example6.py:69
gnsstk::SP3Stream::currentEpoch
CommonTime currentEpoch
Time from last epoch record read.
Definition: SP3Stream.hpp:91
gnsstk::SP3Header::getVersion
Version getVersion(void) const noexcept
Definition: SP3Header.hpp:94
gnsstk::SP3Data::reallyGetRecord
virtual void reallyGetRecord(FFStream &s)
Definition: SP3Data.cpp:54
example4.time
time
Definition: example4.py:103
gnsstk::CivilTime::printf
virtual std::string printf(const std::string &fmt) const
Definition: CivilTime.cpp:111
example4.err
err
Definition: example4.py:126
gnsstk::SP3Stream
Definition: SP3Stream.hpp:61
gnsstk::CommonTime
Definition: CommonTime.hpp:84
gnsstk::TimeSystem
TimeSystem
Definition of various time systems.
Definition: TimeSystem.hpp:51
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
gnsstk::SP3Stream::warnings
std::vector< std::string > warnings
warnings produced by reallyGetRecord()s
Definition: SP3Stream.hpp:93
gnsstk::CivilTime
Definition: CivilTime.hpp:55
gnsstk::StringUtils
Definition: IonexStoreStrategy.cpp:44
gnsstk::SP3Header::SP3a
@ SP3a
SP3 version a.
Definition: SP3Header.hpp:77
gnsstk::StringUtils::rightJustify
std::string & rightJustify(std::string &s, const std::string::size_type length, const char pad=' ')
Definition: StringUtils.hpp:1557
GPSWeekSecond.hpp
std
Definition: Angle.hpp:142
gnsstk::SP3Data::reallyPutRecord
virtual void reallyPutRecord(FFStream &s) const
Definition: SP3Data.cpp:324
SP3Stream.hpp
gnsstk::SP3Data::dump
virtual void dump(std::ostream &s=std::cout, bool includeC=true) const noexcept
Debug output function.
Definition: SP3Data.cpp:413
gnsstk::StringUtils::leftJustify
std::string & leftJustify(std::string &s, const std::string::size_type length, const char pad=' ')
Definition: StringUtils.hpp:1582
GNSSTK_THROW
#define GNSSTK_THROW(exc)
Definition: Exception.hpp:366
gnsstk::SP3SatID
Definition: SP3SatID.hpp:63
example6.month
month
Definition: example6.py:65
gnsstk::SP3Stream::lastLine
std::string lastLine
Last line read, perhaps not yet processed.
Definition: SP3Stream.hpp:92
gnsstk::SP3Header::timeSystem
TimeSystem timeSystem
Time system used.
Definition: SP3Header.hpp:190
gnsstk::SP3Header::SP3c
@ SP3c
SP3 version c (contains a/b as a subset)
Definition: SP3Header.hpp:79


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