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 /**
40  * @file RinexNavData.cpp
41  * Encapsulates RINEX Navigation data
42  */
43 
44 #include "StringUtils.hpp"
45 
46 #include "CommonTime.hpp"
47 #include "CivilTime.hpp"
48 #include "GPSWeekSecond.hpp"
49 
50 #include "RinexNavData.hpp"
51 #include "RinexNavStream.hpp"
52 #include "GNSSconstants.hpp"
53 #include "TimeString.hpp"
54 
55 namespace gpstk
56 {
57    using namespace gpstk::StringUtils;
58    using namespace std;
59 
RinexNavData()60    RinexNavData::RinexNavData()
61          : time(gpstk::CommonTime::BEGINNING_OF_TIME), PRNID(-1),
62            sf1XmitTime(0), toeWeek(0), codeflgs(0), accuracy(0),
63            health(0), L2Pdata(0), IODC(0), IODE(0), af0(0), af1(0), af2(0),
64            Tgd(0), Cuc(0), Cus(0), Crc(0), Crs(0), Cic(0), Cis(0), Toe(0),
65            M0(0), dn(0), ecc(0), Ahalf(0), OMEGA0(0), i0(0), w(0), OMEGAdot(0),
66            idot(0), fitint(4)
67    {
68       time.setTimeSystem(gpstk::TimeSystem::GPS);
69    }
70 
RinexNavData(const EngEphemeris & ee)71    RinexNavData::RinexNavData(const EngEphemeris& ee)
72          : time(ee.getEpochTime()), PRNID(ee.getPRNID()), sf1XmitTime(0),
73            toeWeek(0), codeflgs(ee.getCodeFlags()), accuracy(ee.getAccuracy()),
74            health(ee.getHealth()), L2Pdata(ee.getL2Pdata()), IODC(ee.getIODC()),
75            IODE(ee.getIODE()), af0(ee.getAf0()), af1(ee.getAf1()),
76            af2(ee.getAf2()), Tgd(ee.getTgd()), Cuc(ee.getCuc()),
77            Cus(ee.getCus()), Crc(ee.getCrc()), Crs(ee.getCrs()),
78            Cic(ee.getCic()), Cis(ee.getCis()), Toe(ee.getToe()), M0(ee.getM0()),
79            dn(ee.getDn()), ecc(ee.getEcc()), Ahalf(ee.getAhalf()),
80            OMEGA0(ee.getOmega0()), i0(ee.getI0()), w(ee.getW()),
81            OMEGAdot(ee.getOmegaDot()), idot(ee.getIDot()),
82            fitint(ee.getFitInterval())
83    {
84       setXmitTime(ee.getFullWeek(), ee.getHOWTime(1));
85    }
86 
reallyPutRecord(FFStream & ffs) const87    void RinexNavData::reallyPutRecord(FFStream& ffs) const
88    {
89       RinexNavStream& strm = dynamic_cast<RinexNavStream&>(ffs);
90 
91       strm << setw(2) << right << PRNID
92            << printTime(time, " %02y %2m %2d %2H %2M%5.1f")
93            << af0 << af1 << af2 << endlpp
94            << "   " << IODE << Crs << dn << M0 << endlpp
95            << "   " << Cuc << ecc << Cus << Ahalf << endlpp
96            << "   " << Toe << Cic << OMEGA0 << Cis << endlpp
97            << "   " << i0 << Crc << w << OMEGAdot << endlpp
98            << "   " << idot << RNDouble(codeflgs) << RNDouble(toeWeek)
99            << RNDouble(L2Pdata) << endlpp
100            << "   " << accuracy << RNDouble(health) << Tgd << IODC << endlpp
101            << "   " << RNDouble(sf1XmitTime);
102       if (strm.header.version >= 2.1)
103          strm << fitint;
104       strm << endlpp;
105    }
106 
reallyGetRecord(FFStream & ffs)107    void RinexNavData::reallyGetRecord(FFStream& ffs)
108    {
109       RinexNavStream& strm = dynamic_cast<RinexNavStream&>(ffs);
110 
111          // If the header hasn't been read, read it...
112       if(!strm.headerRead)
113          strm >> strm.header;
114 
115       string line;
116 
117       strm.formattedGetLine(line, true);
118       getPRNEpoch(line);
119 
120       strm.formattedGetLine(line);
121       getBroadcastOrbit1(line);
122 
123       strm.formattedGetLine(line);
124       getBroadcastOrbit2(line);
125 
126       strm.formattedGetLine(line);
127       getBroadcastOrbit3(line);
128 
129       strm.formattedGetLine(line);
130       getBroadcastOrbit4(line);
131 
132       strm.formattedGetLine(line);
133       getBroadcastOrbit5(line);
134 
135       strm.formattedGetLine(line);
136       getBroadcastOrbit6(line);
137 
138       strm.formattedGetLine(line);
139       getBroadcastOrbit7(line);
140    }
141 
stableText() const142    std::string RinexNavData::stableText() const
143    {
144       ostringstream s;
145       s << "PRN: " << setw(2) << PRNID
146         << " TOE: " << printTime(getToeTime(), "%02m/%02d/%04Y %02H:%02M:%02S")
147         << " TOC: " << printTime(time, "%4F %10.3g")
148         << " IODE: " << setw(4) << int(IODE)            // IODE should be int
149         << " HOWtime: " << setw(6) << getHOWWS().sow;   // HOW should be double
150       return s.str();
151    }
152 
dump(ostream & s) const153    void RinexNavData::dump(ostream& s) const
154    {
155       s << "PRN: " << setw(2) << PRNID
156         << " TOE: " << printTime(getToeTime(), "%02m/%02d/%04Y %02H:%02M:%02S")
157         << " TOC: " << printTime(time, "%02m/%02d/%04Y %02H:%02M:%02S")
158         << " IODE: " << setw(4) << int(IODE)            // IODE should be int
159         << " HOWtime: " << setw(6) << getHOWWS().sow    // HOW should be double
160         << endl;
161    }
162 
operator EngEphemeris() const163    RinexNavData::operator EngEphemeris() const throw()
164    {
165       EngEphemeris ee;
166 
167          // there's no TLM word in RinexNavData, so it's set to 0.
168          // likewise, there's no AS alert or tracker.
169          // Also, in Rinex, the accuracy is in meters, and setSF1 expects
170          // the accuracy flag.  We'll give it zero and pass the accuracy
171          // separately via the setAccuracy() method.
172       CommonTime how1(getHOWTime()), how2(how1+6), how3(how2+6);
173       GPSWeekSecond ws1(how1), ws2(how2), ws3(how3);
174       ee.setSF1(0, ws1.sow, 0, ws1.week, codeflgs, 0, health,
175                 short(IODC), L2Pdata, Tgd, getTocWS().sow, af2, af1, af0, 0,
176                 PRNID);
177       ee.setSF2(0, ws2.sow, 0, short(IODE), Crs, dn, M0, Cuc, ecc, Cus, Ahalf,
178                 Toe, (fitint > 4) ? 1 : 0);
179       ee.setSF3(0, ws3.sow, 0, Cic, OMEGA0, Cis, i0, Crc, w, OMEGAdot,
180                 idot);
181       ee.setFIC(false);
182       ee.setAccuracy(accuracy);
183 
184       return ee;
185    }
186 
187       // Convert this RinexNavData to a GPSEphemeris object.
188       // for backward compatibility only - use Rinex3NavData
operator GPSEphemeris() const189    RinexNavData::operator GPSEphemeris() const
190    {
191       GPSEphemeris gpse;
192       try
193       {
194             // Overhead
195          gpse.satID = SatID(PRNID, SatelliteSystem::GPS);
196          gpse.ctToe = time;
197 
198             // clock model
199          gpse.af0 = af0;
200          gpse.af1 = af1;
201          gpse.af2 = af2;
202 
203             // Major orbit parameters
204          gpse.M0 = M0;
205          gpse.dn = dn;
206          gpse.ecc = ecc;
207          gpse.A = Ahalf * Ahalf;
208          gpse.OMEGA0 = OMEGA0;
209          gpse.i0 = i0;
210          gpse.w = w;
211          gpse.OMEGAdot = OMEGAdot;
212          gpse.idot = idot;
213             // modern nav msg
214          gpse.dndot = 0.;
215          gpse.Adot = 0.;
216 
217             // Harmonic perturbations
218          gpse.Cuc = Cuc;
219          gpse.Cus = Cus;
220          gpse.Crc = Crc;
221          gpse.Crs = Crs;
222          gpse.Cic = Cic;
223          gpse.Cis = Cis;
224 
225          gpse.dataLoadedFlag = true;
226 
227          gpse.ctToc = time;
228          gpse.ctToc.setTimeSystem(TimeSystem::GPS);
229 
230             // now load the GPS-specific parts
231          gpse.IODC = IODC;
232          gpse.IODE = IODE;
233          gpse.health = health;
234          gpse.accuracyFlag = accuracy;
235          gpse.Tgd = Tgd;
236 
237          gpse.HOWtime = getHOWWS().sow;
238          gpse.transmitTime = getXmitTime();
239          gpse.transmitTime.setTimeSystem(TimeSystem::GPS);
240 
241          gpse.codeflags = codeflgs;
242          gpse.L2Pdata = L2Pdata;
243 
244             /// @note IODC must be set first...
245          gpse.fitint = fitint;
246          gpse.setFitIntervalFlag(int(fitint));
247          gpse.adjustValidity();
248       }
249       catch(Exception& e)
250       {
251          GPSTK_RETHROW(e);
252       }
253       return gpse;
254    }
255 
toList() const256    list<double> RinexNavData::toList() const
257    {
258       list<double> l;
259       GPSWeekSecond howws(getHOWWS());
260 
261       l.push_back(PRNID);
262       l.push_back(howws.sow);
263       l.push_back(howws.week);
264       l.push_back(codeflgs);
265       l.push_back(accuracy.val);
266       l.push_back(health);
267       l.push_back(L2Pdata);
268       l.push_back(IODC.val);
269       l.push_back(IODE.val);
270       l.push_back(getTocWS().sow);
271       l.push_back(af0.val);
272       l.push_back(af1.val);
273       l.push_back(af2.val);
274       l.push_back(Tgd.val);
275       l.push_back(Cuc.val);
276       l.push_back(Cus.val);
277       l.push_back(Crc.val);
278       l.push_back(Crs.val);
279       l.push_back(Cic.val);
280       l.push_back(Cis.val);
281       l.push_back(Toe.val);
282       l.push_back(M0.val);
283       l.push_back(dn.val);
284       l.push_back(ecc.val);
285       l.push_back(Ahalf.val);
286       l.push_back(OMEGA0.val);
287       l.push_back(i0.val);
288       l.push_back(w.val);
289       l.push_back(OMEGAdot.val);
290       l.push_back(idot.val);
291       l.push_back(fitint.val);
292 
293       return l;
294    }
295 
getPRNEpoch(const string & currentLine)296    void RinexNavData::getPRNEpoch(const string& currentLine)
297    {
298       try
299       {
300             // check for spaces in the right spots...
301          for (int i = 2; i <= 17; i += 3)
302             if (currentLine[i] != ' ')
303                throw(FFStreamError("Badly formatted line"));
304 
305          PRNID = asInt(currentLine.substr(0,2));
306 
307          short yr = asInt(currentLine.substr(2,3));
308          short mo = asInt(currentLine.substr(5,3));
309          short day = asInt(currentLine.substr(8,3));
310          short hr = asInt(currentLine.substr(11,3));
311          short min = asInt(currentLine.substr(14,3));
312          double sec = asDouble(currentLine.substr(17,5));
313 
314             // years 80-99 represent 1980-1999
315          const int rolloverYear = 80;
316          if (yr < rolloverYear)
317             yr += 100;
318          yr += 1900;
319 
320             // Real Rinex has epochs 'yy mm dd hr 59 60.0'
321             // surprisingly often....
322          double ds=0;
323          if(sec >= 60.)
324          {
325             ds=sec;
326             sec=0.0;
327          }
328          time = CivilTime(yr,mo,day,hr,min,sec,gpstk::TimeSystem::GPS).convertToCommonTime();
329          if(ds != 0) time += ds;
330 
331          af0 = currentLine.substr(22,19);
332          af1 = currentLine.substr(41,19);
333          af2 = currentLine.substr(60,19);
334       }
335       catch (std::exception &e)
336       {
337          FFStreamError err("std::exception: " +
338                            string(e.what()));
339          GPSTK_THROW(err);
340       }
341    }
342 
getBroadcastOrbit1(const string & currentLine)343    void RinexNavData::getBroadcastOrbit1(const string& currentLine)
344    {
345       try
346       {
347          IODE = currentLine.substr(3,19);
348          Crs = currentLine.substr(22,19);
349          dn = currentLine.substr(41,19);
350          M0 = currentLine.substr(60,19);
351       }
352       catch (std::exception &e)
353       {
354          FFStreamError err("std::exception: " +
355                            string(e.what()));
356          GPSTK_THROW(err);
357       }
358    }
359 
getBroadcastOrbit2(const string & currentLine)360    void RinexNavData::getBroadcastOrbit2(const string& currentLine)
361    {
362       try
363       {
364          Cuc = currentLine.substr(3,19);
365          ecc = currentLine.substr(22,19);
366          Cus = currentLine.substr(41,19);
367          Ahalf = currentLine.substr(60,19);
368       }
369       catch (std::exception &e)
370       {
371          FFStreamError err("std::exception: " +
372                            string(e.what()));
373          GPSTK_THROW(err);
374       }
375    }
376 
getBroadcastOrbit3(const string & currentLine)377    void RinexNavData::getBroadcastOrbit3(const string& currentLine)
378    {
379       try
380       {
381          Toe = currentLine.substr(3,19);
382          Cic = currentLine.substr(22,19);
383          OMEGA0 = currentLine.substr(41,19);
384          Cis = currentLine.substr(60,19);
385       }
386       catch (std::exception &e)
387       {
388          FFStreamError err("std::exception: " +
389                            string(e.what()));
390          GPSTK_THROW(err);
391       }
392    }
393 
getBroadcastOrbit4(const string & currentLine)394    void RinexNavData::getBroadcastOrbit4(const string& currentLine)
395    {
396       try
397       {
398          i0 = currentLine.substr(3,19);
399          Crc = currentLine.substr(22,19);
400          w = currentLine.substr(41,19);
401          OMEGAdot = currentLine.substr(60,19);
402       }
403       catch (std::exception &e)
404       {
405          FFStreamError err("std::exception: " +
406                            string(e.what()));
407          GPSTK_THROW(err);
408       }
409    }
410 
getBroadcastOrbit5(const string & currentLine)411    void RinexNavData::getBroadcastOrbit5(const string& currentLine)
412    {
413       try
414       {
415          RNDouble codeL2(0), L2P(0), toe_wn(0);
416 
417          idot = currentLine.substr(3,19);
418          codeL2 = currentLine.substr(22,19);
419          toe_wn = currentLine.substr(41,19);
420          L2P = currentLine.substr(60,19);
421 
422          codeflgs = (short) codeL2;
423          L2Pdata = (short) L2P;
424          toeWeek = (short) toe_wn;
425       }
426       catch (std::exception &e)
427       {
428          FFStreamError err("std::exception: " +
429                            string(e.what()));
430          GPSTK_THROW(err);
431       }
432    }
433 
getBroadcastOrbit6(const string & currentLine)434    void RinexNavData::getBroadcastOrbit6(const string& currentLine)
435    {
436       try
437       {
438          RNDouble SV_health(0);
439 
440          accuracy = currentLine.substr(3,19);
441          SV_health = currentLine.substr(22,19);
442          Tgd = currentLine.substr(41,19);
443          IODC = currentLine.substr(60,19);
444 
445 
446          health = (short) SV_health;
447       }
448       catch (std::exception &e)
449       {
450          FFStreamError err("std::exception: " +
451                            string(e.what()));
452          GPSTK_THROW(err);
453       }
454    }
455 
getBroadcastOrbit7(const string & currentLine)456    void RinexNavData::getBroadcastOrbit7(const string& currentLine)
457    {
458       try
459       {
460          RNDouble HOW_sec(0);
461 
462          HOW_sec = currentLine.substr(3,19);
463             // leave it alone so round-trips are possible
464             // (even though we're storing a double as a long, which
465             //could lead to failures in round-trip testing, though if
466             //that happens your transmit time is messed).
467             //setXmitTime(HOW_sec);
468          sf1XmitTime = HOW_sec;
469          fitint = currentLine.substr(22,19);
470       }
471       catch (std::exception &e)
472       {
473          FFStreamError err("std::exception: " +
474                            string(e.what()));
475          GPSTK_THROW(err);
476       }
477    }
478 
479 
getXmitWS() const480    GPSWeekSecond RinexNavData::getXmitWS() const
481    {
482       GPSWeekSecond rv;
483          // sf1XmitTime may not actually be a proper subframe 1
484          // transmit time.  It may be a HOW time or something like
485          // that.
486       if (sf1XmitTime < 0)
487       {
488             // If the transmit time is negative, assume that it
489             // corresponds to the Toe week, according to the footnote
490             // attached to Table A4 in the 2.11 standard.
491          long properXmit = fixSF1xmitSOW(sf1XmitTime+FULLWEEK);
492          rv = GPSWeekSecond(toeWeek-1, properXmit, TimeSystem::GPS);
493       }
494       else
495       {
496             // If the transmit time is >= 0, make sure that we have
497             // the right week using a trusty old half-week test.
498          long properXmit = fixSF1xmitSOW(sf1XmitTime);
499          double diff = Toe - properXmit;
500          if (diff < -HALFWEEK)
501             rv = GPSWeekSecond(toeWeek-1, properXmit, TimeSystem::GPS);
502          else if (diff > HALFWEEK)
503             rv = GPSWeekSecond(toeWeek+1, properXmit, TimeSystem::GPS);
504          else
505             rv = GPSWeekSecond(toeWeek, properXmit, TimeSystem::GPS);
506       }
507       return rv;
508    }
509 
510 
setXmitWeek(unsigned short fullweek)511    RinexNavData& RinexNavData::setXmitWeek(unsigned short fullweek)
512    {
513       if (sf1XmitTime < 0)
514       {
515             // If the transmit time is negative, assume that the
516             // transmit week corresponds to the Toe week, according to
517             // the footnote attached to Table A4 in the 2.11 standard.
518          toeWeek = fullweek;
519       }
520       else
521       {
522             // If the transmit time is >= 0, make sure that we have
523             // the right week using a trusty old half-week test.
524          double diff = Toe - sf1XmitTime;
525          if (diff < -HALFWEEK)
526          {
527             toeWeek = fullweek+1;
528                // adjust  transmit time to be relative to the week.
529                // week is in broadcast orbit 5
530                // transmission time is in broadcast orbit 7
531                //   see footnote in RINEX 2.11 document
532             sf1XmitTime -= FULLWEEK;
533          }
534          else if (diff > HALFWEEK)
535          {
536             toeWeek = fullweek-1;
537                // see comments above
538             sf1XmitTime += FULLWEEK;
539          }
540          else
541             toeWeek = fullweek;
542       }
543       return *this;
544    }
545 
546 
setXmitTime(unsigned short fullweek,unsigned long sow)547    RinexNavData& RinexNavData::setXmitTime(unsigned short fullweek,
548                                            unsigned long sow)
549    {
550       setXmitTime(sow);
551       setXmitWeek(fullweek);
552       return *this;
553    }
554 
555 
556 }  // end of namespace
557