1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk 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 GPSTk 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 GPSTk; 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-2020, 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 
39 /// @file Rinex3NavData.cpp
40 /// Encapsulates RINEX 3 Navigation data.
41 
42 #include "Rinex3NavData.hpp"
43 
44 #include "CivilTime.hpp"
45 #include "GPSWeekSecond.hpp"
46 #include "GALWeekSecond.hpp"
47 #include "BDSWeekSecond.hpp"
48 #include "QZSWeekSecond.hpp"
49 #include "TimeString.hpp"
50 #include "GNSSconstants.hpp"
51 #include "StringUtils.hpp"
52 
53 namespace gpstk
54 {
55    using namespace StringUtils;
56    using namespace std;
57 
58    // Create from a RINEX version 2 RinexNavData (for backward compatibility)
Rinex3NavData(const RinexNavData & rnd)59    Rinex3NavData::Rinex3NavData(const RinexNavData& rnd)
60    {
61       // Epoch
62       time = rnd.time;
63       satSys = "G";
64       PRNID = rnd.PRNID;
65       sat = RinexSatID(PRNID,SatelliteSystem::GPS);
66       xmitTime = rnd.getXmitWS().sow;
67       weeknum = rnd.getXmitWS().week;
68       accuracy = rnd.accuracy;
69       health = rnd.health;
70 
71       // flags
72       codeflgs = rnd.codeflgs;
73       L2Pdata = rnd.L2Pdata;
74       IODC = rnd.IODC;
75       IODE = rnd.IODE;
76 
77       // clock
78       Toc = rnd.getTocWS().sow;
79       af0 = rnd.af0;
80       af1 = rnd.af1;
81       af2 = rnd.af2;
82       Tgd = rnd.Tgd;
83       Tgd2 = 0.0;
84 
85       // perturbations
86       Cus = rnd.Cus;
87       Crc = rnd.Crc;
88       Crs = rnd.Crs;
89       Cic = rnd.Cic;
90       Cis = rnd.Cis;
91 
92       // Orbit parameters
93       Toe = rnd.Toe;
94       M0 = rnd.M0;
95       dn = rnd.dn;
96       ecc = rnd.ecc;
97       Ahalf = rnd.Ahalf;
98       OMEGA0 = rnd.OMEGA0;
99       i0 = rnd.i0;
100       w = rnd.w;
101       OMEGAdot = rnd.OMEGAdot;
102       idot = rnd.idot;
103       fitint = rnd.fitint;
104    }
105 
106    // Private helper routine for constructors from OrbitEph-based Ephemerides
loadFrom(const OrbitEph * oeptr)107    void Rinex3NavData::loadFrom(const OrbitEph *oeptr)
108    {
109       time = oeptr->ctToc;
110       sat = RinexSatID(oeptr->satID);
111       satSys = string(1,sat.systemChar());
112       PRNID = sat.id;
113 
114       //Toc = static_cast<GPSWeekSecond>(oeptr->ctToe).getSOW();
115       af0 = oeptr->af0;
116       af1 = oeptr->af1;
117       af2 = oeptr->af2;
118 
119       //Toe = static_cast<GPSWeekSecond(oeptr->ctToe).getSOW();
120       M0 = oeptr->M0;
121       dn = oeptr->dn;
122       ecc = oeptr->ecc;
123       Ahalf = SQRT(oeptr->A);
124       OMEGA0 = oeptr->OMEGA0;
125       i0 = oeptr->i0;
126       w = oeptr->w;
127       OMEGAdot = oeptr->OMEGAdot;
128       idot = oeptr->idot;
129 
130       Cuc = oeptr->Cuc;
131       Cus = oeptr->Cus;
132       Crc = oeptr->Crc;
133       Crs = oeptr->Crs;
134       Cic = oeptr->Cic;
135       Cis = oeptr->Cis;
136    }
137 
138    // Initializes the nav data with a GPSEphemeris
Rinex3NavData(const GPSEphemeris & gpseph)139    Rinex3NavData::Rinex3NavData(const GPSEphemeris& gpseph)
140    {
141       loadFrom(dynamic_cast<const OrbitEph*>(&gpseph));
142 
143       Toc = static_cast<GPSWeekSecond>(gpseph.ctToc).getSOW();
144       Toe = static_cast<GPSWeekSecond>(gpseph.ctToe).getSOW();
145       xmitTime = static_cast<GPSWeekSecond>(gpseph.transmitTime).getSOW();
146       weeknum = static_cast<GPSWeekSecond>(gpseph.transmitTime).getWeek();
147 
148       accuracy = gpseph.accuracyFlag;
149       health = gpseph.health;
150 
151       codeflgs = gpseph.codeflags;
152       L2Pdata = gpseph.L2Pdata;
153       IODC = gpseph.IODC;
154       IODE = gpseph.IODE;
155 
156       Tgd = gpseph.Tgd;
157       Tgd2 = 0.0;
158 
159       fitint = gpseph.fitint;
160    }
161 
162    // Initializes the nav data with a GalEphemeris
Rinex3NavData(const GalEphemeris & galeph)163    Rinex3NavData::Rinex3NavData(const GalEphemeris& galeph)
164    {
165       loadFrom(dynamic_cast<const OrbitEph*>(&galeph));
166 
167       Toc = static_cast<GALWeekSecond>(galeph.ctToc).getSOW();
168       Toe = static_cast<GALWeekSecond>(galeph.ctToe).getSOW();
169       xmitTime = static_cast<GPSWeekSecond>(galeph.transmitTime).getSOW();
170       weeknum = static_cast<GPSWeekSecond>(galeph.transmitTime).getWeek();
171 
172       IODnav = galeph.IODnav;
173       health = galeph.health;
174       accuracy = galeph.accuracy;
175       Tgd = galeph.Tgda;
176       Tgd2 = galeph.Tgdb;
177       datasources = galeph.datasources;
178    }
179 
180    // Initializes the nav data with a BDSEphemeris
Rinex3NavData(const BDSEphemeris & bdseph)181    Rinex3NavData::Rinex3NavData(const BDSEphemeris& bdseph)
182    {
183       loadFrom(dynamic_cast<const OrbitEph*>(&bdseph));
184 
185       Toc = static_cast<BDSWeekSecond>(bdseph.ctToc).getSOW();
186       Toe = static_cast<BDSWeekSecond>(bdseph.ctToe).getSOW();
187       xmitTime = static_cast<BDSWeekSecond>(bdseph.transmitTime).getSOW();
188       weeknum = static_cast<BDSWeekSecond>(bdseph.transmitTime).getWeek();
189 
190       //Cis = -Cis;       // really? Rinex3.02 A13 misprint?
191       IODC = bdseph.IODC;
192       IODE = bdseph.IODE;
193       health = bdseph.health;
194       accuracy = bdseph.accuracy;
195       Tgd = bdseph.Tgd13;
196       Tgd2 = bdseph.Tgd23;
197    }
198 
199    // Initializes the nav data with a QZSEphemeris
Rinex3NavData(const QZSEphemeris & qzseph)200    Rinex3NavData::Rinex3NavData(const QZSEphemeris& qzseph)
201    {
202       loadFrom(dynamic_cast<const OrbitEph*>(&qzseph));
203 
204       Toc = static_cast<QZSWeekSecond>(qzseph.ctToc).getSOW();
205       Toe = static_cast<QZSWeekSecond>(qzseph.ctToe).getSOW();
206       xmitTime = static_cast<QZSWeekSecond>(qzseph.transmitTime).getSOW();
207       weeknum = static_cast<QZSWeekSecond>(qzseph.transmitTime).getWeek();
208 
209       PRNID -= 192;                    // RINEX stores PRN minus 192
210       sat = RinexSatID(PRNID,SatelliteSystem::QZSS);
211       IODC = qzseph.IODC;
212       IODE = qzseph.IODE;
213       health = qzseph.health;
214       accuracy = qzseph.accuracy;
215       Tgd = qzseph.Tgd;
216 
217       codeflgs = qzseph.codeflags;
218       L2Pdata = qzseph.L2Pdata;
219 
220       fitint = qzseph.fitint;
221    }
222 
223    // Deprecated; used GPSEphemeris.
224    // This routine uses EngEphemeris, so is for GPS data only.
225    // The comments about GPS v. Galileo next to each elements are just notes
226    // from sorting out the ICDs in the RINEX 3 documentation. Please leave
227    // them there until we add a routine for handling GalRecord or similar.
Rinex3NavData(const EngEphemeris & ee)228    Rinex3NavData::Rinex3NavData(const EngEphemeris& ee) // GPS only
229    {
230       // epoch info
231       satSys = ee.getSatSys();
232       PRNID  = ee.getPRNID();
233       sat    = RinexSatID(PRNID,SatelliteSystem::GPS);
234       time   = ee.getEpochTime();
235 
236       Toc     = ee.getToc();
237       xmitTime = fixSF1xmitSOW(ee.getHOWTime(1));
238       weeknum = ee.getFullWeek();
239 
240       accuracy = ee.getAccuracy();
241       health   = ee.getHealth();
242 
243       // GPS or Galileo data
244 
245       af0 = ee.getAf0(); // GPS and Galileo only
246       af1 = ee.getAf1(); // GPS and Galileo only
247       af2 = ee.getAf2(); // GPS and Galileo only
248 
249       Crs = ee.getCrs(); // GPS and Galileo only
250       dn  = ee.getDn();  // GPS and Galileo only
251       M0  = ee.getM0();  // GPS and Galileo only
252 
253       Cuc   = ee.getCuc();   // GPS and Galileo only
254       ecc   = ee.getEcc();   // GPS and Galileo only
255       Cus   = ee.getCus();   // GPS and Galileo only
256       Ahalf = ee.getAhalf(); // GPS and Galileo only
257 
258       Toe    = ee.getToe();    // GPS and Galileo only
259       Cic    = ee.getCic();    // GPS and Galileo only
260       OMEGA0 = ee.getOmega0(); // GPS and Galileo only
261       Cis    = ee.getCis();    // GPS and Galileo only
262 
263       i0       = ee.getI0();       // GPS and Galileo only
264       Crc      = ee.getCrc();      // GPS and Galileo only
265       w        = ee.getW();        // GPS and Galileo only
266       OMEGAdot = ee.getOmegaDot(); // GPS and Galileo only
267 
268       idot = ee.getIDot(); // GPS and Galileo only
269 
270       // GPS-only data
271 
272       IODE = ee.getIODE(); // GPS only
273 
274       codeflgs = ee.getCodeFlags(); // GPS only
275       L2Pdata  = ee.getL2Pdata();   // GPS only
276 
277       Tgd  = ee.getTgd();  // GPS only
278       IODC = ee.getIODC(); // GPS only
279 
280       fitint = ee.getFitInterval(); // GPS only
281    }  // End of 'Rinex3NavData::Rinex3NavData(const EngEphemeris& ee)'
282 
283       // This constructor initializes R3NavData with Glonass data.
Rinex3NavData(const GloEphemeris & gloe)284    Rinex3NavData::Rinex3NavData(const GloEphemeris& gloe)
285    {
286 
287          // Epoch info
288       satSys = gloe.getSatSys();
289       PRNID  = gloe.getPRNID();
290       sat    = RinexSatID(PRNID,SatelliteSystem::Glonass);
291       time   = gloe.getEpochTime();
292 
293          // GLONASS parameters
294       TauN = gloe.getTauN();
295       GammaN = gloe.getGammaN();
296       MFtime = gloe.getMFtime();
297       health = gloe.getHealth();
298       freqNum = gloe.getfreqNum();
299       ageOfInfo = gloe.getAgeOfInfo();
300 
301       Triple x( gloe.x );
302       px = x[0];
303       py = x[1];
304       pz = x[2];
305 
306       Triple v( gloe.v );
307       vx = v[0];
308       vy = v[1];
309       vz = v[2];
310 
311       Triple a( gloe.getAcc() );
312       ax = a[0];
313       ay = a[1];
314       az = a[2];
315 
316    }  // End of 'Rinex3NavData::Rinex3NavData(const GloEphemeris& ge)'
317 
318 
319       /* This function retrieves a RINEX 3 NAV record from the given
320        *  FFStream.
321        *  If an error is encountered in reading from the stream, the stream
322        *  is returned to its original position and its fail-bit is set.
323        *  @throw StringException when a StringUtils function fails.
324        *  @throw FFStreamError when exceptions(failbit) is set and a read
325        *          or formatting error occurs. This also resets the stream
326        *          to its pre-read position.
327        */
reallyGetRecord(FFStream & ffs)328    void Rinex3NavData::reallyGetRecord(FFStream& ffs)
329    {
330 
331       try
332       {
333          Rinex3NavStream& strm = dynamic_cast<Rinex3NavStream&>(ffs);
334 
335          // If the header hasn't been read, read it...
336          if (!strm.headerRead)
337          {
338             try
339             {
340                strm >> strm.header;
341             }
342             catch(std::exception& e)
343             {
344                FFStreamError fse(string("std::exception reading header ") +
345                                  e.what());
346                GPSTK_THROW(fse);
347             }
348             catch(FFStreamError& fse)
349             {
350                GPSTK_RETHROW(fse);
351             }
352          }
353 
354          // get the first line, the epoch line
355          getPRNEpoch(strm);
356 
357          // get 3 data records
358          for(int i=1; i<=3; i++) getRecord(i, strm);
359 
360          // SBAS and GLO only have 3 records
361          if (satSys == "S" || satSys == "R") return;
362 
363          // GPS GAL QZSS BDS have 7 records, get 4-7
364          if (satSys == "G" || satSys == "E" || satSys == "J" || satSys == "C" ||
365              satSys == "I")
366          {
367             for(int i=4; i<=7; i++)
368             {
369                getRecord(i, strm);
370             }
371          }
372       }
373       catch(std::exception& e)
374       {
375          FFStreamError fse(string("std::exception: ") + e.what());
376          GPSTK_THROW(fse);
377       }
378       catch(FFStreamError& fse)
379       {
380          GPSTK_RETHROW(fse);
381       }
382       catch(StringException& se)
383       {
384          GPSTK_RETHROW(se);
385       }
386 
387    }  // End of method 'Rinex3NavData::reallyGetRecord(FFStream& ffs)'
388 
389 
390       // Outputs the record to the FFStream \a s.
reallyPutRecord(FFStream & ffs) const391    void Rinex3NavData::reallyPutRecord(FFStream& ffs) const
392    {
393       try
394       {
395          Rinex3NavStream& strm = dynamic_cast<Rinex3NavStream&>(ffs);
396 
397          putPRNEpoch(strm);
398 
399          // put 3 data records
400          for(int i=1; i<=3; i++)
401          {
402             putRecord(i, strm);
403          }
404 
405          // SBAS and GLO only have 3 records
406          if (satSys == "S" || satSys == "R")
407          {
408             return;
409          }
410 
411          // GPS QZS BDS and GAL have 7 records, put 4-7
412          if (satSys == "G" || satSys == "C" || satSys == "E" || satSys == "J")
413          {
414             for(int i=4; i<=7; i++)
415             {
416                putRecord(i, strm);
417             }
418          }
419       }
420       catch(std::exception& e)
421       {
422          FFStreamError fse(string("std::exception: ") + e.what());
423          GPSTK_THROW(fse);
424       }
425       catch(FFStreamError& fse)
426       {
427          GPSTK_RETHROW(fse);
428       }
429       catch(StringException& se)
430       {
431          GPSTK_RETHROW(se);
432       }
433 
434    }  // End of method 'Rinex3NavData::reallyPutRecord(FFStream& ffs)'
435 
436       // A debug output function.
437       // Prints the PRN id and the IODC for this record.
dump(ostream & s) const438    void Rinex3NavData::dump(ostream& s) const
439    {
440       s << "Rinex3NavData dump: "
441          << satSys << setfill('0') << setw(2) << PRNID << setfill(' ')
442          << printTime(time, " TOC %Y/%02m/%02d %02H:%02M:%02S")
443          << fixed << setprecision(3)
444          << " wk " << weeknum << " xmit " << xmitTime << " Toe " << Toe << endl;
445       s << " Toc " << Toc << scientific << setprecision(12)
446          << " af0 " << af0 << " af1 " << af1 << " af2 " << af2
447          << " Tgd " << Tgd << " Tgd2 " << Tgd2 << endl;
448 
449       s << " M0 " << M0 << " Ecc " << ecc << " sqrtA " << Ahalf << " OM "
450         << OMEGA0 << endl;
451       s << " i0 " << i0 << " om " << w << " dOMdt " << OMEGAdot << " didt "
452         << idot << endl;
453       s << " Cuc " << Cuc << " Cus " << Cus << " Crc " << Crc << " Crs " << Crs
454         << " Cic " << Cic << " Cis " << Cis << endl;
455 
456       if (satSys == "G" || satSys == "J")
457       {
458             // GPS QZSS
459          s << " health " << health << " acc " << accuracy << " fit " << fitint
460            << " IODE " << IODE << " IODC " << IODC
461            << " codeflags " << codeflgs << " L2P " << L2Pdata << endl;
462       }
463       //else if (satSys == "R")     // GLONASS
464       else if (satSys == "E")
465       {
466             // Galileo
467          s << " IODnav " << IODnav << " datasources " << datasources << endl;
468       }
469       //else if (satSys == "C")     // BeiDou
470    }
471 
dumpString(void) const472    string Rinex3NavData::dumpString(void) const
473    {
474       ostringstream s;
475       s << "RND " << satSys
476          << setfill('0') << setw(2) << PRNID << setfill(' ');
477       if (satSys == "G" || satSys == "J")
478       {
479             // GPS or QZSS
480          s << " TOE: " << setw(4) << weeknum
481            << " " << fixed << setw(10) << setprecision(3) << Toe.val
482            << " TOC: " << printTime(time,"%4Y %02m %02d %02H %02M %06.3f %P")
483            << " xmitTime: " << setw(6) << xmitTime
484            << " IODE/C: " << int(IODE) << "/" << int(IODC)
485            << " hlth: " << health
486            << " cflgs: " << codeflgs << " L2P: " << L2Pdata
487            << " fit: " << fitint.val;
488       }
489       else if (satSys == "R")
490       {
491             // GLONASS
492          s << " freq: " << setw(2) << freqNum
493            << " hlth: " << setw(2) << health
494            << " " << printTime(time,"%4Y %02m %02d %02H %02M %06.3f")
495            << " MFtime: " << setw(6) << MFtime
496            << " TauN: " << scientific << setw(19) << setprecision(12)
497            << TauN.val
498            << " GammaN: " << setw(19) << GammaN.val
499            << " AOI: " << fixed << setprecision(2) << setw(4) << ageOfInfo.val;
500       }
501       else if (satSys == "S")
502       {
503             // Geosync (SBAS)
504          s << " URAm: " << setw(2) << freqNum
505            << " hlth: " << setw(2) << health
506            << " " << printTime(time,"%4Y %02m %02d %02H %02M %06.3f")
507            << " MFtime: " << setw(6) << MFtime
508            << " aGf0: " << scientific << setw(19) << setprecision(12)
509            << TauN.val
510            << " aGf1: " << setw(19) << GammaN.val
511            << " IODN " << fixed << setprecision(2) << setw(4) << ageOfInfo.val;
512       }
513       else if (satSys == "E")
514       {
515             // Galileo
516          s << " TOE: " << setw(4) << weeknum
517            << " " << fixed << setw(10) << setprecision(3) << Toe.val
518            << " TOC: " << printTime(time,"%4Y %02m %02d %02H %02M %06.3f %P")
519            << " xmitTime: " << setw(6) << xmitTime
520            << " IODnav: " << int(IODnav) << " hlth: " << health
521            << " datasources " << datasources;
522       }
523       else if (satSys == "C")
524       {
525             // BeiDou
526          s << " TOE: " << setw(4) << weeknum
527            << " " << fixed << setw(10) << setprecision(3) << Toe.val
528            << " TOC: " << printTime(time,"%4Y %02m %02d %02H %02M %06.3f %P")
529            << " xmitTime: " << setw(6) << xmitTime
530            << " IODE/C: " << int(IODE) << "/" << int(IODC);
531       }
532       else
533       {
534          s << " (unknown system: " << satSys << ")";
535       }
536 
537       return s.str();
538    }  // End of method 'Rinex3NavData::asString
539 
540       // Deprecated; use GPSEphemeris.
541       // Converts this Rinex3NavData to an EngEphemeris object.
operator EngEphemeris() const542    Rinex3NavData::operator EngEphemeris() const throw()
543    {
544       EngEphemeris ee;
545 
546       // There's no TLM word in Rinex3NavData, so it's set to 0.
547       // Likewise, there's no AS alert or tracker.
548       // Also, in RINEX, the accuracy is in meters, and setSF1 expects
549       // the accuracy flag.  We'll give it zero and pass the accuracy
550       // separately via the setAccuracy() method.
551 
552       ee.tlm_message[0] = 0;
553       ee.tlm_message[1] = 0;
554       ee.tlm_message[2] = 0;
555       ee.HOWtime[0] = xmitTime + 6;       // GPS standard specifies
556       ee.HOWtime[1] = ee.HOWtime[0] + 6;  // how the transmit time
557       ee.HOWtime[2] = ee.HOWtime[1] + 6;  // relates to HOWtime.
558       ee.ASalert[0] = 1;               //AS and alert flags set to 1 (default)
559       ee.ASalert[1] = 1;
560       ee.ASalert[2] = 1;
561 
562       ee.weeknum    = weeknum;
563       ee.codeflags  = codeflgs;
564       ee.health     = health;
565       ee.IODC       = short(IODC);
566       ee.L2Pdata    = L2Pdata;
567       ee.Tgd        = Tgd;
568       ee.tracker    = 0;
569       ee.PRNID      = PRNID;
570       ee.satSys     = satSys;
571       bool healthy = false;
572       if (health == 0) healthy = true;
573       short accFlag = 0; //will be set later.
574           //BrcClockCorrection takes a flag, while EngEphemeris takes a double.
575       double toc    = Toc;
576 
577       double timeDiff =toc - ee.HOWtime[0];
578       short epochWeek = ee.weeknum;
579       if (timeDiff < -HALFWEEK) epochWeek++;
580       else if (timeDiff > HALFWEEK) epochWeek--;
581 
582       CommonTime tocCT = GPSWeekSecond(epochWeek, Toc, TimeSystem::GPS);
583 
584          // The observation ID has a type of navigation, but the
585          // carrier and code types are undefined.  They could be
586          // L1/L2 C/A, P, Y,.....
587       ObsID obsID(ObservationType::NavMsg, CarrierBand::Undefined, TrackingCode::Undefined);
588       ee.bcClock.loadData( satSys, obsID, PRNID, tocCT,
589                         accFlag, healthy, af0, af1, af2);
590 
591       ee.IODE    = short(IODE);
592       ee.fitint  = (fitint > 4) ? 1 : 0;
593       //double toe = Toe; //?????
594 
595       //Needed for modernized nav quatities
596       double A = Ahalf * Ahalf;
597       double dndot = 0.0;
598       double Adot = 0.0;
599 
600       short fitHours = getLegacyFitInterval(ee.IODC, ee.fitint);
601       long beginFitSOW = Toe - (fitHours/2)*3600.0;
602       long endFitSOW = Toe + (fitHours/2)*3600.0;
603       short beginFitWk = ee.weeknum;
604       short endFitWk = ee.weeknum;
605 
606       if (beginFitSOW < 0)
607       {
608          beginFitSOW += FULLWEEK;
609          beginFitWk--;
610       }
611       CommonTime beginFit = GPSWeekSecond(beginFitWk, beginFitSOW,
612                                           TimeSystem::GPS);
613       if (endFitSOW >= FULLWEEK)
614       {
615          endFitSOW += FULLWEEK;
616          endFitWk++;
617       }
618 
619       CommonTime endFit = GPSWeekSecond(endFitWk, endFitSOW, TimeSystem::GPS);
620       CommonTime toeCT = GPSWeekSecond(epochWeek, Toe, TimeSystem::GPS);
621 
622       ee.orbit.loadData( satSys, obsID, PRNID, beginFit, endFit, toeCT,
623                       accFlag, healthy, Cuc, Cus, Crc, Crs, Cic, Cis,
624                       M0, dn, dndot, ecc, A, Ahalf, Adot, OMEGA0, i0,
625                       w, OMEGAdot, idot);
626 
627       ee.haveSubframe[0] = true;    // need to be true to perform certain EngEphemeris functions
628       ee.haveSubframe[1] = true;    // examples: ee.dump(), ee.setAccuracy()
629       ee.haveSubframe[2] = true;
630 
631       ee.setAccuracy(accuracy);
632 
633      return ee;
634 
635    }  // End of 'Rinex3NavData::operator EngEphemeris()'
636 
637    // Private helper routine for casts from this to OrbitEph-based Ephemerides
castTo(OrbitEph * oeptr) const638    void Rinex3NavData::castTo(OrbitEph *oeptr) const
639    {
640       try
641       {
642          // Glonass and Geosync do not have a orbit-based ephemeris
643          if (satSys == "R" || satSys == "S")
644          {
645             oeptr->dataLoadedFlag = false;
646             return;
647          }
648 
649          // Overhead
650          RinexSatID sat;
651          sat.fromString(satSys + StringUtils::asString(PRNID));
652          oeptr->satID = SatID(sat);
653          //obsID = ?? ObsID obsID; // Defines carrier and tracking code
654          oeptr->ctToe = time;
655 
656          // clock model
657          oeptr->af0 = af0;
658          oeptr->af1 = af1;
659          oeptr->af2 = af2;
660 
661          // Major orbit parameters
662          oeptr->M0 = M0;
663          oeptr->dn = dn;
664          oeptr->ecc = ecc;
665          oeptr->A = Ahalf * Ahalf;
666          oeptr->OMEGA0 = OMEGA0;
667          oeptr->i0 = i0;
668          oeptr->w = w;
669          oeptr->OMEGAdot = OMEGAdot;
670          oeptr->idot = idot;
671          // modern nav msg
672          oeptr->dndot = 0.;
673          oeptr->Adot = 0.;
674 
675          // Harmonic perturbations
676          oeptr->Cuc = Cuc;
677          oeptr->Cus = Cus;
678          oeptr->Crc = Crc;
679          oeptr->Crs = Crs;
680          oeptr->Cic = Cic;
681          oeptr->Cis = Cis;
682 
683          oeptr->dataLoadedFlag = true;
684       }
685       catch(Exception& e)
686       {
687          GPSTK_RETHROW(e);
688       }
689    }
690 
691       // Casts Rinex3NavData to a GPSEphemeris object.
operator GPSEphemeris() const692    Rinex3NavData::operator GPSEphemeris() const throw()
693    {
694       GPSEphemeris gpse;
695 
696       // fill the OrbitEph parts
697       OrbitEph* ptr = dynamic_cast<OrbitEph*>(&gpse);
698       castTo(ptr);                                 //sets dataLoadedFlag
699 
700       // is it right?
701       if (gpse.satID.system != SatelliteSystem::GPS)
702          gpse.dataLoadedFlag = false;
703 
704       if (!gpse.dataLoadedFlag)
705          return gpse;          // throw?
706 
707       // now load the GPS-specific parts
708       gpse.IODC = IODC;
709       gpse.IODE = IODE;
710       gpse.health = health;
711       gpse.accuracyFlag = accuracy;
712       gpse.Tgd = Tgd;
713       gpse.codeflags = codeflgs;
714       gpse.L2Pdata = L2Pdata;
715 
716       // NB IODC must be set first
717       gpse.fitint = fitint;
718       if (fitint==0) gpse.fitint = 4;
719       if (fitint==1) gpse.fitint = 6;
720       gpse.setFitIntervalFlag(int(fitint));
721 
722          // Rinex transmit times are frequently flawed.  For GPS, except for
723          // the first data set in an upload the beginning of transmission is
724          // deterministic based on the Toe/Toc.   Therefore,
725          //  a.) For each item with an EVEN Toe/Toc, set
726          // the transmission time to be equivalent to the nominal beginning
727          // of transmission based on the statements in IS-GPS-200
728          // Section 20.3.4.5 and Table 20-XIII.
729          //  b.) If this is the SECOND data set of an upload,
730          // set the transmission time to be equivalent to the nominal beginning
731          // of transmission based on the statements in IS-GPS-200
732          // Section 20.3.4.5 and Table 20-XIII.
733 
734          // If Toc/Toe is an even-hour interval the initial time of transmission
735          // will be Toc/Toe minus 1/2 of the fit interval.
736       long adjXmitTime = xmitTime;
737       short adjWeeknum = weeknum;
738       long sowToc = static_cast<GPSWeekSecond>(time).sow;
739       if (sowToc%3600==0)
740       {
741          adjXmitTime = sowToc - (gpse.fitint/2 * 3600);
742          if (adjXmitTime<0)
743          {
744             adjXmitTime += FULLWEEK;
745             adjWeeknum--;
746          }
747       }
748 
749       // Get week for clock, to build Toc
750       gpse.ctToc = time;
751       gpse.ctToc.setTimeSystem(TimeSystem::GPS);
752 
753       gpse.transmitTime = GPSWeekSecond(adjWeeknum, static_cast<double>(adjXmitTime),
754          TimeSystem::GPS);
755       gpse.HOWtime = adjXmitTime + 6;
756 
757          // N.B.: The preceding times must be set prior to calling adjustValidity().
758       gpse.adjustValidity();
759       return gpse;
760    }
761 
762       // Casts this Rinex3NavData to a GloEphemeris object.
operator GloEphemeris() const763    Rinex3NavData::operator GloEphemeris() const throw()
764    {
765 
766       GloEphemeris gloe;
767 
768       gloe.setRecord( satSys,
769                       PRNID,
770                       time,
771                       Triple(px, py, pz),
772                       Triple(vx, vy, vz),
773                       Triple(ax, ay, az),
774                       TauN,
775                       GammaN,
776                       MFtime,
777                       health,
778                       freqNum,
779                       ageOfInfo );
780 
781       return gloe;
782 
783    }  // End of 'Rinex3NavData::operator GloEphemeris()'
784 
785       // Casts Rinex3NavData to a GalEphemeris object.
operator GalEphemeris() const786    Rinex3NavData::operator GalEphemeris() const throw()
787    {
788       GalEphemeris gale;
789 
790       // fill the OrbitEph parts
791       OrbitEph *ptr = dynamic_cast<OrbitEph*>(&gale);
792       castTo(ptr);  // sets dataLoadedFlag
793 
794       // is it right?
795       if (gale.satID.system != SatelliteSystem::Galileo)
796          gale.dataLoadedFlag = false;
797 
798       if (!gale.dataLoadedFlag)
799          return gale;          // throw?
800 
801       // get the epochs right
802       CommonTime ct = time;
803       //unsigned int year = static_cast<CivilTime>(ct).year;
804 
805       // Get week for clock, to build Toc
806       double dt = Toc - xmitTime;
807       int week = weeknum;
808       if (dt < -HALFWEEK) week++; else if (dt > HALFWEEK) week--;
809       //MGEX NB MGEX data has GPS week numbers in all systems except BeiDou,
810       //MGEX so must implement temporary fixes: use GPS Toc for GAL and QZSS
811       //MGEX GALWeekSecond galws = GALWeekSecond(week, Toc, TimeSystem::GAL);
812       //MGEX galws.adjustToYear(year);
813       //MGEX gale.ctToc = CommonTime(galws);
814       CommonTime gpstoc = GPSWeekSecond(week, Toc, TimeSystem::GPS);    //MGEX
815       gale.ctToc = gpstoc;                                              //MGEX
816       gale.ctToc.setTimeSystem(TimeSystem::GAL);
817 
818       // now load the Galileo-specific parts
819       // NOTE: The Galileo fit interval is not defined in the message;
820       // However, the SDD states that the data shall not be used bryond
821       // four hours from initial time of transmission.
822       gale.IODnav = IODnav;
823       //gale.health = health;
824       gale.accuracy = accuracy;
825       gale.Tgda = Tgd;
826       gale.Tgdb = Tgd2;
827       gale.datasources = datasources;
828       gale.fitDuration = 4;
829 
830       // In RINEX, the SISA value has already been translated
831       // to accuracy.  A SISA value of 255 is given tha
832       // accuracy value of -1.   For purposes of the deriveHealth()
833       // method, we need a value from 0-255.  deriveHealth() only
834       // cares about 255 or "not 255".
835       unsigned short SISA = 255;
836       if (accuracy!=-1)
837          SISA = 1;
838 
839       // The RINEX "health" field contains a variety
840       // of bit-encoded information, including the DVS
841       // HS values (RINEX 3.04, Table A8).
842       // Based on the data source,
843       // derive DVS and HS bit values for this message.
844       //
845       // Default to the values for F/NAV (E5a)
846       unsigned shiftDVS = 3;
847       unsigned shiftHS  = 4;
848       if ( (datasources & 0x01) !=0 )  // I/NAV (E1B)
849       {
850          shiftDVS = 0;
851          shiftHS  = 1;
852       }
853       else if ( (datasources & 0x04) !=0 ) // I/NAV (E5b)
854       {
855          shiftDVS = 6;
856          shiftHS  = 7;
857       }
858       unsigned short DVS = (health >> shiftDVS) & 0x01;
859       unsigned short HS  = (health >> shiftHS)  & 0x03;
860 
861       gale.health = GalEphemeris::deriveHealth(HS,DVS,SISA);
862 
863       week = static_cast<GALWeekSecond>(gale.ctToe).getWeek();
864       gale.transmitTime = GALWeekSecond(week, static_cast<double>(xmitTime),
865                                        TimeSystem::GAL);
866       gale.adjustValidity();
867 
868       return gale;
869    }
870 
871       // Casts Rinex3NavData to a BDSEphemeris object.
operator BDSEphemeris() const872    Rinex3NavData::operator BDSEphemeris() const throw()
873    {
874       BDSEphemeris bdse;
875 
876       // fill the OrbitEph parts
877       OrbitEph* ptr = dynamic_cast<OrbitEph*>(&bdse);
878       castTo(ptr);                                    // set dataLoadedFlag
879 
880       // is it right?
881       if (bdse.satID.system != SatelliteSystem::BeiDou)
882          bdse.dataLoadedFlag = false;
883 
884       if (!bdse.dataLoadedFlag)
885          return bdse;          // throw?
886 
887       // get the epochs right
888       CommonTime ct = time;
889       unsigned int year = static_cast<CivilTime>(ct).year;
890 
891       // Get week for clock, to build Toc
892       double dt = Toc - xmitTime;
893       int week = weeknum;
894       if (dt < -HALFWEEK) week++; else if (dt > HALFWEEK) week--;
895       BDSWeekSecond bdsws = BDSWeekSecond(week, Toc, TimeSystem::BDT);
896       bdsws.adjustToYear(year);
897       bdse.ctToc = CommonTime(bdsws);
898 
899       // now load the BDS-specific parts
900       //bdse.Cis = -Cis;     // really? RINEX 3.02 misprint?
901       bdse.IODC = IODC;
902       bdse.IODE = IODE;
903       bdse.health = health;
904       bdse.accuracy = accuracy;
905       bdse.Tgd13 = Tgd;
906       bdse.Tgd23 = Tgd2;
907 
908 //      bdse.HOWtime = xmitTime;
909       week = static_cast<BDSWeekSecond>(bdse.ctToe).getWeek();
910       bdse.transmitTime = BDSWeekSecond(week, static_cast<double>(xmitTime),
911                                        TimeSystem::BDT);
912       bdse.adjustValidity();
913 
914       return bdse;
915    }
916 
917       // Casts Rinex3NavData to a QZSEphemeris object.
operator QZSEphemeris() const918    Rinex3NavData::operator QZSEphemeris() const throw()
919    {
920       QZSEphemeris qzse;
921 
922       // fill the OrbitEph parts
923       castTo(dynamic_cast<OrbitEph*>(&qzse));
924 
925       // is it right?
926       if (qzse.satID.system != SatelliteSystem::QZSS)
927          qzse.dataLoadedFlag = false;
928 
929       if (!qzse.dataLoadedFlag)
930          return qzse;          // throw?
931 
932       // get the epochs right
933       CommonTime ct = time;
934       unsigned int year = static_cast<CivilTime>(ct).year;
935 
936       // Get week for clock, to build Toc
937       double dt = Toc - xmitTime;
938       int week = weeknum;
939       if (dt < -HALFWEEK) week++; else if (dt > HALFWEEK) week--;
940       QZSWeekSecond qzsws = QZSWeekSecond(week, Toc, TimeSystem::QZS);
941       qzsws.adjustToYear(year);
942       qzse.ctToc = CommonTime(qzsws);
943 
944       //MGEX NB MGEX data has GPS week numbers in all systems except BeiDou,
945       //MGEX so must implement temporary fixes: use GPS Toc for QZS and QZSS
946       CommonTime gpstoc = GPSWeekSecond(week, Toc, TimeSystem::GPS);    //MGEX
947       qzse.ctToc = gpstoc;                                              //MGEX
948 
949       qzse.ctToc.setTimeSystem(TimeSystem::QZS);
950 
951       // now load the QZSS-specific parts
952       qzse.satID = SatID(qzse.satID.id + 192, SatelliteSystem::QZSS);
953       qzse.IODC = IODC;
954       qzse.IODE = IODE;
955       qzse.health = health;
956       qzse.accuracy = accuracy;
957       qzse.Tgd = Tgd;
958 
959 //      qzse.HOWtime = xmitTime;
960       week = static_cast<QZSWeekSecond>(qzse.ctToe).getWeek();
961       qzse.transmitTime = QZSWeekSecond(week, static_cast<double>(xmitTime),
962                                           TimeSystem::QZS);
963       qzse.beginValid = qzse.transmitTime;
964 
965       qzse.codeflags = codeflgs;
966       qzse.L2Pdata = L2Pdata;
967 
968       // NB IODC must be set first...
969       qzse.fitint = fitint;
970       qzse.setFitIntervalFlag(int(fitint));  // calls adjustValidity();
971 
972       return qzse;
973    }
974 
975 
976       // Converts the (non-CommonTime) data to an easy list
977       // for comparison operators.
toList() const978    list<double> Rinex3NavData::toList() const
979    {
980 
981       list<double> l;
982 
983       l.push_back(PRNID);
984       l.push_back(xmitTime);
985       l.push_back(weeknum);
986       l.push_back(codeflgs);
987       l.push_back(accuracy.val);
988       l.push_back(health);
989       l.push_back(L2Pdata);
990       l.push_back(IODC.val);
991       l.push_back(IODE.val);
992       l.push_back(Toe.val);
993       l.push_back(af0.val);
994       l.push_back(af1.val);
995       l.push_back(af2.val);
996       l.push_back(Tgd.val);
997       l.push_back(Cuc.val);
998       l.push_back(Cus.val);
999       l.push_back(Crc.val);
1000       l.push_back(Crs.val);
1001       l.push_back(Cic.val);
1002       l.push_back(Cis.val);
1003       l.push_back(Toc);
1004       l.push_back(M0.val);
1005       l.push_back(dn.val);
1006       l.push_back(ecc.val);
1007       l.push_back(Ahalf.val);
1008       l.push_back(OMEGA0.val);
1009       l.push_back(i0.val);
1010       l.push_back(w.val);
1011       l.push_back(OMEGAdot.val);
1012       l.push_back(idot.val);
1013       l.push_back(fitint.val);
1014 
1015       return l;
1016 
1017    }  // End of method 'Rinex3NavData::toList()'
1018 
1019 
1020       /* Generates the PRN/epoch line and outputs it to strm
1021        *  @param strm RINEX Nav stream
1022        */
putPRNEpoch(Rinex3NavStream & strm) const1023    void Rinex3NavData::putPRNEpoch(Rinex3NavStream& strm) const
1024    {
1025       string line;
1026       CivilTime civtime(time);
1027 
1028       if (strm.header.version >= 3)
1029       {
1030             // version 3
1031          strm << sat.toString() << ' '
1032               << printTime(civtime, "%4Y %02m %02d %02H %02M %02S");
1033       }
1034       else
1035       {
1036             // version 2
1037          strm << setw(2) << PRNID << ' '
1038               << printTime(civtime, "%02y %2m %2d %2H %2M %4.1f");
1039       }
1040 
1041       if (satSys == "R" || satSys == "S")
1042       {
1043          strm << TauN << GammaN << RNDouble(MFtime);
1044       }
1045       else if (satSys == "G" || satSys == "E" || satSys == "J" || satSys == "C")
1046       {
1047          strm << af0 << af1 << af2;
1048       }
1049 
1050       strm << endl;
1051       strm.lineNumber++;
1052    }  // End of 'Rinex3NavData::putPRNEpoch(Rinex3NavStream& strm)'
1053 
1054 
1055       // Construct and write the nth record after the epoch record
1056       //  @param int n                 Record number (1-7), for nth record
1057       //                               after the epoch line.
1058       //  @param Rinex3NavStream strm  Stream to read from.
putRecord(const int & nline,Rinex3NavStream & strm) const1059    void Rinex3NavData::putRecord(const int& nline, Rinex3NavStream& strm) const
1060    {
1061 
1062       if (nline < 1 || nline > 7)
1063       {
1064          FFStreamError fse(string("Invalid line number ") + asString(nline));
1065          GPSTK_THROW(fse);
1066       }
1067 
1068       try
1069       {
1070          if (strm.header.version < 3)
1071          {
1072             strm << "   ";
1073          }
1074          else
1075          {
1076             strm << "    ";
1077          }
1078 
1079 
1080          // Internally (Rinex3NavData), weeknum=week of HOW
1081          // In RINEX 3 *files*, weeknum is the week of TOE.
1082          RNDouble wk(weeknum);
1083          long xmit = xmitTime;
1084          if (xmit - Toe > HALFWEEK)
1085          {
1086             xmit -= FULLWEEK;
1087             wk++;
1088          }
1089          else if (xmit - Toe < -(HALFWEEK))
1090          {
1091             xmit += FULLWEEK;
1092             wk--;
1093          }
1094 
1095          if (nline == 1)
1096          {
1097             if (satSys == "R" || satSys == "S")
1098             {
1099                   // GLO and GEO
1100                strm << px << vx << ax << RNDouble(health);
1101             }
1102             else if (satSys == "G" || satSys == "C" || satSys == "J")
1103             {
1104                   // GPS,BDS,QZS
1105                strm << IODE << Crs << dn << M0;
1106             }
1107             else if (satSys == "E")
1108             {
1109                   // GAL
1110                strm << IODnav << Crs << dn << M0;
1111             }
1112          }
1113          else if (nline == 2)
1114          {
1115             if (satSys == "R" || satSys == "S")
1116             {
1117                   // GLO and GEO
1118                strm << py << vy << ay;
1119                if (satSys == "R")
1120                   strm << RNDouble(freqNum);
1121                else
1122                   strm << accCode;
1123             }
1124             else
1125             {
1126                   // GPS,GAL,BDS,QZS
1127                strm << Cuc << ecc << Cus << Ahalf;
1128             }
1129          }
1130          else if (nline == 3)
1131          {
1132             if (satSys == "R" || satSys == "S")
1133             {
1134                   // GLO GEO
1135                strm << pz << vz << az;
1136                if (satSys == "R")
1137                   strm << ageOfInfo;
1138                else                             // GEO
1139                   strm << IODN;
1140             }
1141             else
1142             {
1143                   // GPS,GAL,BDS,QZS
1144                strm << Toe << Cic << OMEGA0 << Cis;
1145             }
1146          }
1147 
1148          // SBAS and GLO end here
1149 
1150          else if (nline == 4)
1151          {
1152                // GPS,GAL,BDS,QZS
1153             strm << i0 << Crc << w << OMEGAdot;
1154          }
1155 
1156          else if (nline == 5)
1157          {
1158             if (satSys == "G" || satSys == "J")
1159             {
1160                   // GPS QZS
1161                strm << idot << RNDouble(codeflgs) << wk << RNDouble(L2Pdata);
1162             }
1163             else if (satSys == "E")
1164             {
1165                   // GAL
1166                strm << idot << RNDouble(datasources) << wk << RNDouble(0);
1167             }
1168             else if (satSys == "C")
1169             {
1170                   // BDS
1171                strm << idot << RNDouble(0) << wk << RNDouble(0);
1172             }
1173          }
1174 
1175          else if (nline == 6)
1176          {
1177             strm << accuracy << RNDouble(health);
1178 
1179             if (satSys == "G" || satSys == "J")
1180             {
1181                   // GPS, QZS
1182                strm << Tgd << IODC;
1183             }
1184             else if (satSys == "E" || satSys == "C")
1185             {
1186                   // GAL, BDS
1187                strm << Tgd << Tgd2;
1188             }
1189          }
1190 
1191          else if (nline == 7)
1192          {
1193             strm << RNDouble(xmit);
1194             if (satSys == "G" || satSys == "J")
1195             {
1196                strm << fitint;
1197             }
1198             else if (satSys == "E")
1199             {
1200                ;
1201             }
1202             else if (satSys == "C")
1203             {
1204                strm << IODC;
1205             }
1206          }
1207 
1208          strm << endl;
1209          strm.lineNumber++;
1210       }
1211       catch (std::exception &e)
1212       {
1213          FFStreamError err("std::exception: " + string(e.what()));
1214          GPSTK_THROW(err);
1215       }
1216 
1217    }  // End of method 'Rinex3NavData::putRecord(const int& nline,...'
1218 
1219 
getPRNEpoch(Rinex3NavStream & strm)1220    void Rinex3NavData::getPRNEpoch(Rinex3NavStream& strm)
1221    {
1222       try
1223       {
1224          int i;
1225          short yr,mo,day,hr,min;
1226          double dsec;
1227 
1228          string line;
1229          while(line.empty()) // ignore blank lines in place of epoch lines
1230             strm.formattedGetLine(line, true);
1231 
1232          if (strm.header.version >= 3)
1233          {
1234             // check for spaces in the right spots...
1235             if (line[3] != ' ')
1236                GPSTK_THROW(FFStreamError("Badly formatted epoch line"));
1237             for(i = 8; i <= 20; i += 3)
1238             {
1239                if (line[i] != ' ')
1240                {
1241                   GPSTK_THROW(FFStreamError("Badly formatted epoch line"));
1242                }
1243             }
1244 
1245             satSys = line.substr(0,1);
1246             PRNID = asInt(line.substr(1,2));
1247             sat.fromString(line.substr(0,3));
1248 
1249             yr  = asInt(line.substr( 4,4));
1250             mo  = asInt(line.substr( 9,2));
1251             day = asInt(line.substr(12,2));
1252             hr  = asInt(line.substr(15,2));
1253             min = asInt(line.substr(18,2));
1254             dsec = asDouble(line.substr(21,2));
1255          }
1256          else
1257          {
1258                // RINEX 2
1259             for(i=2; i <= 17; i+=3)
1260             {
1261                if (line[i] != ' ')
1262                {
1263                   GPSTK_THROW(FFStreamError("Badly formatted epoch line"));
1264                }
1265             }
1266 
1267             satSys = string(1,strm.header.fileSys[0]);
1268             PRNID = asInt(line.substr(0,2));
1269             sat.fromString(satSys + line.substr(0,2));
1270 
1271             yr  = asInt(line.substr( 2,3));
1272             if (yr < 80)
1273                yr += 100;     // rollover is at 1980
1274             yr += 1900;
1275             mo  = asInt(line.substr( 5,3));
1276             day = asInt(line.substr( 8,3));
1277             hr  = asInt(line.substr(11,3));
1278             min = asInt(line.substr(14,3));
1279             dsec = asDouble(line.substr(17,5));
1280          }
1281 
1282          // Fix RINEX epochs of the form 'yy mm dd hr 59 60.0'
1283          short ds = 0;
1284          if (dsec >= 60.)
1285          {
1286             ds = dsec;
1287             dsec = 0;
1288          }
1289          time = CivilTime(yr,mo,day,hr,min,dsec).convertToCommonTime();
1290          if (ds != 0)
1291             time += ds;
1292 
1293          // specify the time system based on satellite system
1294          time.setTimeSystem(TimeSystem::Any);
1295          if (satSys == "G") time.setTimeSystem(TimeSystem::GPS);
1296          if (satSys == "R") time.setTimeSystem(TimeSystem::GLO);
1297          if (satSys == "E") time.setTimeSystem(TimeSystem::GAL);
1298          if (satSys == "C") time.setTimeSystem(TimeSystem::BDT);
1299          if (satSys == "J") time.setTimeSystem(TimeSystem::QZS);
1300          if (satSys == "S") time.setTimeSystem(TimeSystem::GPS);
1301 
1302          // TOC is the clock time
1303          GPSWeekSecond gws(time);         // sow is system-independent
1304          Toc = gws.sow;
1305 
1306          if (strm.header.version < 3)
1307          {
1308                // Rinex 2.*
1309             if (satSys == "G")
1310             {
1311                af0 = line.substr(22,19);
1312                af1 = line.substr(41,19);
1313                af2 = line.substr(60,19);
1314             }
1315             else if (satSys == "R" || satSys == "S")
1316             {
1317                TauN   =      line.substr(22,19);
1318                GammaN =      line.substr(41,19);
1319                MFtime =      RNDouble(line.substr(60,19));
1320                if (satSys == "R")
1321                {
1322                      // make MFtime consistent with R3.02
1323                   MFtime += int(Toc/86400) * 86400;
1324                }
1325             }
1326          }
1327          else if (satSys == "G" || satSys == "E" || satSys == "C" ||
1328                   satSys == "J")
1329          {
1330             af0 = line.substr(23,19);
1331             af1 = line.substr(42,19);
1332             af2 = line.substr(61,19);
1333          }
1334          else if (satSys == "R" || satSys == "S")
1335          {
1336             TauN   =      line.substr(23,19);
1337             GammaN =      line.substr(42,19);
1338             MFtime =      RNDouble(line.substr(61,19));
1339          }
1340       }
1341       catch (std::exception &e)
1342       {
1343          FFStreamError err("std::exception: " + string(e.what()));
1344          GPSTK_THROW(err);
1345       }
1346    }
1347 
1348 
getRecord(const int & nline,Rinex3NavStream & strm)1349    void Rinex3NavData::getRecord(const int& nline, Rinex3NavStream& strm)
1350    {
1351       if (nline < 1 || nline > 7)
1352       {
1353          FFStreamError fse(string("Invalid line number ") + asString(nline));
1354          GPSTK_THROW(fse);
1355       }
1356 
1357       try
1358       {
1359          int n(strm.header.version < 3 ? 3 : 4);
1360          string line;
1361          strm.formattedGetLine(line);
1362 
1363          if (nline == 1)
1364          {
1365             if (satSys == "G" || satSys == "J" || satSys == "C")
1366             {
1367                IODE = line.substr(n,19); n+=19;
1368                Crs  = line.substr(n,19); n+=19;
1369                dn   = line.substr(n,19); n+=19;
1370                M0   = line.substr(n,19);
1371             }
1372             else if (satSys == "E")
1373             {
1374                IODnav = line.substr(n,19); n+=19;
1375                Crs    = line.substr(n,19); n+=19;
1376                dn     = line.substr(n,19); n+=19;
1377                M0     = line.substr(n,19);
1378             }
1379             else if (satSys == "R" || satSys == "S")
1380             {
1381                px     =        line.substr(n,19); n+=19;
1382                vx     =        line.substr(n,19); n+=19;
1383                ax     =        line.substr(n,19); n+=19;
1384                health =        RNDouble(line.substr(n,19));
1385             }
1386          }
1387 
1388          else if (nline == 2)
1389          {
1390             if (satSys == "G" || satSys == "E" || satSys == "J" ||
1391                 satSys == "C")
1392             {
1393                Cuc   = line.substr(n,19); n+=19;
1394                ecc   = line.substr(n,19); n+=19;
1395                Cus   = line.substr(n,19); n+=19;
1396                Ahalf = line.substr(n,19);
1397             }
1398             else if (satSys == "R" || satSys == "S")
1399             {
1400                py      =        line.substr(n,19); n+=19;
1401                vy      =        line.substr(n,19); n+=19;
1402                ay      =        line.substr(n,19); n+=19;
1403                if (satSys == "R")
1404                {
1405                   freqNum = RNDouble(line.substr(n,19));
1406                }
1407                else                       // GEO
1408                {
1409                   accCode = line.substr(n,19);
1410                }
1411             }
1412          }
1413 
1414          else if (nline == 3)
1415          {
1416             if (satSys == "G" || satSys == "E" || satSys == "J" ||
1417                 satSys == "C")
1418             {
1419                Toe    = line.substr(n,19); n+=19;
1420                Cic    = line.substr(n,19); n+=19;
1421                OMEGA0 = line.substr(n,19); n+=19;
1422                Cis    = line.substr(n,19);
1423             }
1424             else if (satSys == "R" || satSys == "S")
1425             {
1426                pz        = line.substr(n,19); n+=19;
1427                vz        = line.substr(n,19); n+=19;
1428                az        = line.substr(n,19); n+=19;
1429                if (satSys == "R")
1430                {
1431                   ageOfInfo = line.substr(n,19);
1432                }
1433                else                       // GEO
1434                {
1435                   IODN = line.substr(n,19);
1436                }
1437             }
1438          }
1439 
1440          else if (nline == 4)
1441          {
1442             i0       = line.substr(n,19); n+=19;
1443             Crc      = line.substr(n,19); n+=19;
1444             w        = line.substr(n,19); n+=19;
1445             OMEGAdot = line.substr(n,19);
1446          }
1447 
1448          else if (nline == 5)
1449          {
1450             if (satSys == "G" || satSys == "J" || satSys == "C")
1451             {
1452                idot     =        line.substr(n,19); n+=19;
1453                codeflgs = RNDouble(line.substr(n,19)); n+=19;
1454                weeknum  = RNDouble(line.substr(n,19)); n+=19;
1455                L2Pdata  = RNDouble(line.substr(n,19));
1456             }
1457             else if (satSys == "E")
1458             {
1459                idot        =       line.substr(n,19); n+=19;
1460                datasources =RNDouble(line.substr(n,19)); n+=19;
1461                weeknum     =RNDouble(line.substr(n,19)); n+=19;
1462             }
1463          }
1464 
1465          else if (nline == 6)
1466          {
1467             Tgd2 = 0.0;
1468             if (satSys == "G" || satSys == "J")
1469             {
1470                accuracy =       line.substr(n,19); n+=19;
1471                health   = RNDouble(line.substr(n,19)); n+=19;
1472                Tgd      =       line.substr(n,19); n+=19;
1473                IODC     =       line.substr(n,19);
1474             }
1475             else if (satSys == "E")
1476             {
1477                accuracy =       line.substr(n,19); n+=19;
1478                health   = RNDouble(line.substr(n,19)); n+=19;
1479                Tgd      =       line.substr(n,19); n+=19;
1480                Tgd2     =       line.substr(n,19);
1481             }
1482             else if (satSys == "C")
1483             {
1484                accuracy =       line.substr(n,19); n+=19;
1485                health   = RNDouble(line.substr(n,19)); n+=19;
1486                Tgd      =       line.substr(n,19); n+=19;
1487                Tgd2     =       line.substr(n,19);
1488             }
1489          }
1490 
1491          else if (nline == 7)
1492          {
1493             xmitTime = RNDouble(line.substr(n,19)); n+=19;
1494             if (satSys == "C")
1495             {
1496                IODC    =        line.substr(n,19); n+=19;
1497             }
1498             else
1499             {
1500                fitint  =        line.substr(n,19); n+=19;
1501             }
1502 
1503             // Some RINEX files have xmitTime < 0.
1504             while(xmitTime < 0)
1505             {
1506                xmitTime += (long)FULLWEEK;
1507             }
1508 
1509             // In RINEX *files*, weeknum is the week of TOE.
1510             // Internally (Rinex3NavData), weeknum is week of transmission
1511             if (xmitTime - Toe > HALFWEEK)
1512                weeknum--;
1513             else if (xmitTime - Toe < -HALFWEEK)
1514                weeknum++;
1515          }
1516       }
1517       catch (std::exception &e)
1518       {
1519          FFStreamError err("std::exception: " + string(e.what()));
1520          GPSTK_THROW(err);
1521       }
1522 
1523    }  // end getRecord()
1524 
1525 
1526 }  // End of namespace gpstk
1527