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 EngEphemeris.cpp
41  * Ephemeris data encapsulated in engineering terms
42  */
43 #include <iomanip>
44 #include <cmath>
45 
46 #include "StringUtils.hpp"
47 #include "GNSSconstants.hpp"
48 #include "MathBase.hpp"
49 #include "GPSEllipsoid.hpp"
50 #include "EngEphemeris.hpp"
51 #include "GPSWeekSecond.hpp"
52 #include "YDSTime.hpp"
53 #include "CivilTime.hpp"
54 #include "TimeSystem.hpp"
55 #include "GPS_URA.hpp"
56 #include "TimeString.hpp"
57 
58 namespace gpstk
59 {
60    using namespace std;
61 
EngEphemeris()62    EngEphemeris::EngEphemeris()
63       throw()
64    {
65       haveSubframe[0] = haveSubframe[1] = haveSubframe[2] = false;
66 
67       tlm_message[0] = tlm_message[1] = tlm_message[2] = 0;
68 
69       satSys = "";
70 
71       PRNID = tracker = ASalert[0] = ASalert[1] = ASalert[2] = weeknum =
72          codeflags = health = L2Pdata = 0;
73 
74       HOWtime[0] = HOWtime[1] = HOWtime[2] = 0;
75 
76       IODC = IODE = 0;
77       Tgd = 0.0;
78 
79       isFIC = true;
80 
81       fitint = 0;
82 
83       /*
84        *  AODO is initialized to 27900 to indicate that the offset
85        *  data should not be relied upon.
86        *  The satellite transmits a 5-bit unsigned integer that is
87        *  then multiplied by 900 to get the ago of the offset data
88        *  in seconds
89        *  31 * 900 = 27900, hence setting AODO to 27900 is setting
90        *  the age to the maximum value
91        */
92       AODO = 27900;
93 
94       for (int j = 0; j<3; j++)
95       {
96          for (int i = 0; i<10; i++) subframeStore[j][i] = 0L;
97       }
98    }
99 
100 
operator ==(const gpstk::EngEphemeris & right) const101    bool EngEphemeris::operator==(const gpstk::EngEphemeris& right) const throw()
102    {
103          // ignored as not important for eng eph comparison
104          //haveSubframe
105          //subframeStore
106          //isFIC;
107          // EngNav has no data to compare
108       return ((tlm_message[0] == right.tlm_message[0]) &&
109               (tlm_message[1] == right.tlm_message[1]) &&
110               (tlm_message[2] == right.tlm_message[2]) &&
111               (satSys == right.satSys) &&
112               (PRNID == right.PRNID) &&
113               (tracker == right.tracker) &&
114               (HOWtime[0] == right.HOWtime[0]) &&
115               (HOWtime[1] == right.HOWtime[1]) &&
116               (HOWtime[2] == right.HOWtime[2]) &&
117               (ASalert[0] == right.ASalert[0]) &&
118               (ASalert[1] == right.ASalert[1]) &&
119               (ASalert[2] == right.ASalert[2]) &&
120               (weeknum == right.weeknum) &&
121               (codeflags == right.codeflags) &&
122               (health == right.health) &&
123               (L2Pdata == right.L2Pdata) &&
124               (IODC == right.IODC) &&
125               (IODE == right.IODE) &&
126               (AODO == right.AODO) &&
127               (fitint == right.fitint) &&
128               (Tgd == right.Tgd) &&
129               (bcClock == right.bcClock) &&
130               (orbit == right.orbit));
131    }
132 
133 
134       /**
135        *  Historically, EngEphemeris allowed calling programs to add data
136        *  one subframe at a time.  This functionality does not exist in
137        *  KeplerOrbit and BroadcastClockCorrection.  Therefore, EngEphemeris
138        *  must handle the subframe collection overhead before calling
139        *  BrcKeplerOrbit.loadData() and BrcClockCorrection.loadData().
140        *
141        *  Note: Historically, the gpsWeek in the calling parameters to
142        *  addSubframe is the full week number associated with the TRANSMIT
143        *  time (not the epoch times).
144        */
145 
addSubframe(const uint32_t subframe[10],const int gpsWeek,const short PRN,const short track)146    bool EngEphemeris::addSubframe(const uint32_t subframe[10],
147                                   const int gpsWeek, const short PRN,
148                                   const short track)
149    {
150          // Determine the subframe number
151       uint32_t SFword2 = (uint32_t) subframe[1];
152       SFword2 &= 0x00000700;      // Strip all but three bit subframe ID
153       SFword2 >>= 8;              // Right shift to move to lsbs;
154       short sfID = static_cast<short>( SFword2 );
155 
156       if (sfID<1 || sfID>3)
157       {
158          InvalidParameter exc("Invalid SF ID: "+StringUtils::asString(sfID));
159          GPSTK_THROW(exc);
160       }
161 
162          // Store the subframe in the appropriate location
163          // and set the flag
164       int sfNdx = sfID - 1;
165       for (int i=0;i<10;++i) subframeStore[sfNdx][i] = subframe[i];
166       haveSubframe[sfNdx] = true;
167 
168          // Determine if all subframes are available.  If so,
169          // load the data. Otherwise return and try again later.
170          // default return OK in cases where no cracking occurs
171       bool result = true;
172       if (haveSubframe[0] &&
173           haveSubframe[1] &&
174           haveSubframe[2])
175       {
176          result = unifiedConvert( gpsWeek, PRN, track );
177       }
178       return(result);
179    }
180 
addSubframeNoParity(const uint32_t subframe[10],const int gpsWeek,const short PRN,const short track)181    bool EngEphemeris::addSubframeNoParity(const uint32_t subframe[10],
182                                           const int  gpsWeek,
183                                           const short PRN,
184                                           const short track)
185    {
186       uint32_t paddedSF[10];
187       short PRNArg;
188       short trackArg;
189 
190       for (int i=0;i<10;++i)
191       {
192          paddedSF[i] = subframe[i];
193          paddedSF[i] <<= 6;
194          paddedSF[i] &= 0x3FFFFFC0;    // Guarantee 2 msb and 6 lsb are zeroes
195       }
196       PRNArg = PRN;
197       trackArg = track;
198       return( addSubframe( paddedSF, gpsWeek, PRNArg, trackArg ));
199    }
200 
addIncompleteSF1Thru3(const uint32_t sf1[8],const uint32_t sf2[8],const uint32_t sf3[8],const long sf1TransmitSOW,const int gpsWeek,const short PRN,const short track)201    bool EngEphemeris::addIncompleteSF1Thru3(
202       const uint32_t sf1[8], const uint32_t sf2[8], const uint32_t sf3[8],
203       const long sf1TransmitSOW, const int gpsWeek,
204       const short PRN, const short track)
205    {
206          // Need to provide a valid subframe number in the handover word.
207          // While we're at it, we'll fake the A-S bit such that it
208          // appears A-S is ON, even though we warn the user NOT to trust
209          // returns from the getASAlert() method.
210       const uint32_t sf1Lead[2] = { 0x00000000, 0x00000900 };
211       const uint32_t sf2Lead[2] = { 0x00000000, 0x00000A00 };
212       const uint32_t sf3Lead[2] = { 0x00000000, 0x00000B00 };
213 
214          // Handover word times represent the time of the leading edge of the
215          // NEXvt subframe.  Therefore, HOW should always correspond to
216          //   :06/:36 for SF 1
217          //   :12/:42 for SF 2
218          //   :18/:48 for SF 3
219          // This method hasn't a clue about the accuracy of the SOW
220          // input by the user, but it WILL enforce this relationship.
221          //long frameCount = sf1TransmitSOW / 30;
222          //long SF1HOWTime = (frameCount * 30) + 6;
223 
224          // Convert subframe 1 parameters
225       subframeStore[0][0] = sf1Lead[0];
226       subframeStore[0][1] = sf1Lead[1];
227       int i;
228       for (i=0; i<8; ++i) subframeStore[0][i+2] = sf1[i];
229       haveSubframe[0] = true;
230 
231          // Convert subframe 2 parameters
232       subframeStore[1][0] = sf2Lead[0];
233       subframeStore[1][1] = sf2Lead[1];
234       for (i=0; i<8; ++i) subframeStore[1][i+2] = sf2[i];
235       haveSubframe[1] = true;
236 
237          // Convert subframe 3 parameters
238       subframeStore[2][0] = sf3Lead[0];
239       subframeStore[2][1] = sf3Lead[1];
240       for (i=0; i<8; ++i) subframeStore[2][i+2] = sf3[i];
241       haveSubframe[2] = true;
242 
243          // Call method to crack and load the data.
244       bool result = unifiedConvert( gpsWeek, PRN, track );
245 
246       return(result);
247    }
248 
249       /**
250        * Each of the addSubframe( ) methods eventually calls unifiedConvert( )
251        * in order to crack the raw subframe data into engineering units and
252        * load the orbit and clock objects.
253        */
unifiedConvert(const int gpsWeek,const short PRN,const short track)254    bool EngEphemeris::unifiedConvert( const int gpsWeek,
255                                       const short PRN,
256                                       const short track)
257    {
258       double ficked[60];
259 
260       if (!subframeConvert(subframeStore[0], gpsWeek, ficked))
261          return false;
262       tlm_message[0] = (subframeStore[0][0] >> 8) & 0x3fff;
263       HOWtime[0] = static_cast<long>( ficked[2] );
264       ASalert[0] = static_cast<short>( ficked[3] );
265       weeknum    = static_cast<short>( ficked[5] );
266       codeflags  = static_cast<short>( ficked[6] );
267       short accFlag = static_cast<short>( ficked[7] );
268       health     = static_cast<short>( ficked[8] );
269       IODC       = static_cast<short>( ldexp( ficked[9], -11 ) );
270       L2Pdata    = static_cast<short>( ficked[10] );
271       Tgd        = ficked[11];
272       double Toc = ficked[12];
273       double af2 = ficked[13];
274       double af1 = ficked[14];
275       double af0 = ficked[15];
276       tracker    = track;
277 
278       if (!subframeConvert(subframeStore[1], gpsWeek, ficked))
279          return false;
280 
281       tlm_message[1] = (subframeStore[1][0] >> 8) & 0x3fff;
282       HOWtime[1]     = static_cast<long>( ficked[2] );
283       ASalert[1]     = static_cast<short>( ficked[3] );
284       IODE           = static_cast<short>( ldexp( ficked[5], -11 ) );
285       double Crs     = ficked[6];
286       double dn      = ficked[7];
287       double M0      = ficked[8];
288       double Cuc     = ficked[9];
289       double ecc     = ficked[10];
290       double Cus     = ficked[11];
291       double Ahalf   = ficked[12];
292       double Toe     = ficked[13];
293       fitint         = static_cast<short>( ficked[14] );
294       AODO           = static_cast<long>( ficked[15] );
295 
296 
297       if (!subframeConvert(subframeStore[2], gpsWeek, ficked))
298          return false;
299 
300       tlm_message[2]   = (subframeStore[2][0] >> 8) & 0x3fff;
301       HOWtime[2]       = static_cast<long>( ficked[2] );
302       ASalert[2]       = static_cast<short>( ficked[3] );
303       double Cic       = ficked[5];
304       double OMEGA0    = ficked[6];
305       double Cis       = ficked[7];
306       double i0        = ficked[8];
307       double Crc       = ficked[9];
308       double w         = ficked[10];
309       double OMEGAdot  = ficked[11];
310       double idot      = ficked[13];
311 
312          // The system is assumed (legacy navigation message is from GPS)
313       satSys = "G";
314       PRNID = PRN;
315 
316          // The observation ID has a type of navigation, but the
317          // carrier and code types are undefined.  They could be
318          // L1/L2 C/A, P, Y,.....
319       ObsID obsID(ObservationType::NavMsg, CarrierBand::Undefined, TrackingCode::Undefined);
320 
321       bool healthy = false;
322       if (health==0)
323          healthy = true;
324       double Adot = 0.0;
325       double dnDot = 0.0;
326       double A = Ahalf * Ahalf;
327 
328       double timeDiff = Toe - HOWtime[1];
329       short toeWeek = weeknum, tocWeek = weeknum;
330       if (timeDiff < -HALFWEEK)
331          toeWeek++;
332       else if (timeDiff > HALFWEEK)
333          toeWeek--;
334       timeDiff = Toc - HOWtime[1];
335       if (timeDiff < -HALFWEEK)
336          tocWeek++;
337       else if (timeDiff > HALFWEEK)
338          tocWeek--;
339 
340          // Toe is now in CommonTime, and needs to be passed to
341          // BrcKeplerOrbit as a CommonTime variable.
342       CommonTime ToeCT = GPSWeekSecond(toeWeek, Toe, TimeSystem::GPS);
343       CommonTime TocCT = GPSWeekSecond(tocWeek, Toc, TimeSystem::GPS);
344 
345          //short fiti = static_cast<short>(ficked[14]);
346       short fitHours = getLegacyFitInterval(IODC, fitint);
347       long beginFitSOW = Toe - (fitHours/2)*3600;
348       long endFitSOW = Toe + (fitHours/2)*3600;
349       short beginFitWk = toeWeek;
350       short endFitWk = toeWeek;
351       if (beginFitSOW < 0)
352       {
353          beginFitSOW += FULLWEEK;
354          beginFitWk--;
355       }
356       CommonTime beginFit = GPSWeekSecond(beginFitWk, beginFitSOW,
357                                           TimeSystem::GPS);
358 
359       if (endFitSOW >= FULLWEEK)
360       {
361          endFitSOW -= FULLWEEK;
362          endFitWk++;
363       }
364       CommonTime endFit = GPSWeekSecond(endFitWk, endFitSOW, TimeSystem::GPS);
365 
366       orbit.loadData(satSys, obsID, PRN, beginFit, endFit, ToeCT,
367                      accFlag, healthy, Cuc, Cus, Crc, Crs, Cic, Cis, M0,
368                      dn, dnDot, ecc, A, Ahalf, Adot, OMEGA0, i0, w, OMEGAdot,
369                      idot);
370 
371       bcClock.loadData( satSys, obsID, PRNID, TocCT,
372                         accFlag, healthy, af0, af1, af2);
373 
374       return true;
375    }
376 
isValid() const377    bool EngEphemeris :: isValid() const
378       throw()
379    {
380       try
381       {
382          return ((PRNID >= 1) && (PRNID <= 37) &&
383                  (HOWtime[0] >= 0) && (HOWtime[0] <= 604784) &&
384                  (HOWtime[1] >= 0) && (HOWtime[1] <= 604784) &&
385                  (HOWtime[2] >= 0) && (HOWtime[2] <= 604784) &&
386                  (getToc() >= 0) && (getToc() <= 604784) &&
387                  (getToe() >= 0) && (getToe() <= 604784) &&
388                  (getEcc() >= 0) && (getEcc() <= 0.03));
389       }
390       catch( ... )
391       {
392          return false;
393       }
394    }
395 
isData(short subframe) const396    bool EngEphemeris::isData(short subframe) const
397    {
398       if ((subframe < 1) || (subframe > 3))
399       {
400          InvalidRequest exc("Subframe "+StringUtils::asString(subframe)+
401                             " is not a valid ephemeris subframe.");
402          GPSTK_THROW(exc);
403       }
404 
405       return haveSubframe[subframe-1];
406    }
407 
isDataSet() const408    bool EngEphemeris :: isDataSet() const
409    {
410       if (haveSubframe[0] && haveSubframe[1] && haveSubframe[2])
411       {
412          return ((IODC & 0xff) == (IODE & 0xff));
413       }
414       return false;
415    }
416 
setAccuracy(double acc)417    void EngEphemeris::setAccuracy(double acc)
418    {
419       if( acc < 0 )
420       {
421          InvalidParameter exc("SV Accuracy of " + StringUtils::asString(acc) +
422                               " meters is invalid.");
423          GPSTK_THROW(exc);
424       }
425       orbit.setAccuracy(acc);
426    }
427 
getFitInterval() const428    short EngEphemeris :: getFitInterval() const
429    {
430       return getFitInterval(getIODC(), getFitInt());
431    }
432 
433       /**
434        * This is for Block II/IIA
435        * Need update for Block IIR and IIF
436        */
getFitInterval(short iodc,short fiti)437    short EngEphemeris :: getFitInterval(short iodc, short fiti)
438    {
439          /* check the IODC */
440       if (iodc < 0 || iodc > 1023)
441       {
442             /* error in iodc, return minimum fit */
443          return 4;
444       }
445 
446       if ((((fiti == 0) &&
447             (iodc & 0xFF) < 240) || (iodc & 0xFF) > 255 ))
448       {
449             /* fit interval of 4 hours */
450          return 4;
451       }
452       else if (fiti == 1)
453       {
454          if( ((iodc & 0xFF) < 240 || (iodc & 0xFF) > 255))
455          {
456                /* fit interval of 6 hours */
457             return 6;
458          }
459          else if(iodc >=240 && iodc <=247)
460          {
461                /* fit interval of 8 hours */
462             return 8;
463          }
464          else if((iodc >= 248 && iodc <= 255) || iodc == 496)
465          {
466                /* fit interval of 14 hours */
467             return 14;
468          }
469             // Revised in IS-GPS-200 Revision D for Block IIR/IIR-M/IIF
470          else if((iodc >= 497 && iodc <=503) || (iodc >= 1021 && iodc <= 1023))
471          {
472                /* fit interval of 26 hours */
473             return 26;
474          }
475          else if(iodc >= 504 && iodc <=510)
476          {
477                /* fit interval of 50 hours */
478             return 50;
479          }
480          else if(iodc == 511 || (iodc >= 752 && iodc <= 756))
481          {
482                /* fit interval of 74 hours */
483             return 74;
484          }
485             /* NOTE:
486              * The following represents old fit intervals for Block II
487              * (not IIA) and is present only in old versions of the
488              * ICD-GPS-200 Rev. C.  Please do not remove them as there
489              * are still people that may want to process old Block II
490              * data and none of the IODC intervals overlap (so far) so
491              * there is no need to remove them.
492              */
493          else if(iodc >= 757 && iodc <= 763)
494          {
495                /* fit interval of 98 hours */
496             return 98;
497          }
498          else if((iodc >= 764 && iodc <=767) || (iodc >=1008 && iodc <=1010))
499          {
500                /* fit interval of 122 hours */
501             return 122;
502          }
503          else if(iodc >= 1011 && iodc <=1020)
504          {
505                /* fit interval of 146 hours */
506             return 146;
507          }
508          else
509          {
510                /* error in the iodc or ephemeris, return minimum
511                   fit */
512             return 4;
513          }
514       }
515       else
516       {
517             /* error in ephemeris/iodc, return minimum fit */
518          return 4;
519       }
520 
521       return 0; // never reached
522    }
523 
524 
svXvt(const CommonTime & t) const525    Xvt EngEphemeris::svXvt(const CommonTime& t) const
526    {
527       Xvt sv;
528 
529       Xvt xv = orbit.svXvt(t);
530 
531       sv.x = xv.x;
532       sv.v = xv.v;
533 
534       sv.clkbias = bcClock.svClockBias(t);
535       sv.relcorr = orbit.svRelativity(t);
536 
537       sv.clkdrift = bcClock.svClockDrift(t);
538 
539       return sv;
540    }
541 
svRelativity(const CommonTime & t) const542    double EngEphemeris::svRelativity(const CommonTime& t) const
543    {
544       return orbit.svRelativity(t);
545    }
546 
svClockBias(const CommonTime & t) const547    double EngEphemeris::svClockBias(const CommonTime& t) const
548    {
549       return bcClock.svClockBias(t);
550    }
551 
svClockDrift(const CommonTime & t) const552    double EngEphemeris::svClockDrift(const CommonTime& t) const
553    {
554       return bcClock.svClockDrift(t);
555    }
556 
getTLMMessage(short subframe) const557    unsigned EngEphemeris::getTLMMessage(short subframe) const
558    {
559       if (!haveSubframe[subframe-1])
560       {
561          InvalidRequest exc("Subframe "+StringUtils::asString(subframe)+
562                             " not stored.");
563          GPSTK_THROW(exc);
564       }
565       return tlm_message[subframe-1];
566    }
567 
getTransmitTime() const568    CommonTime EngEphemeris::getTransmitTime() const
569    {
570       CommonTime toReturn;
571       try
572       {
573          toReturn = GPSWeekSecond(getFullWeek(), static_cast<double>(getTot()),
574                                   TimeSystem::GPS);
575       }
576       catch (InvalidRequest& ire)
577       {
578          GPSTK_RETHROW(ire);
579       }
580       catch (Exception& exc)
581       {
582          InvalidRequest ire(exc);
583          GPSTK_THROW(ire);
584       }
585       return toReturn;
586    }
587 
getEpochTime() const588    CommonTime EngEphemeris::getEpochTime() const
589    {
590       return bcClock.getEpochTime();
591    }
592 
getEphemerisEpoch() const593    CommonTime EngEphemeris::getEphemerisEpoch() const
594    {
595       return orbit.getOrbitEpoch();
596    }
597 
getOrbit() const598    BrcKeplerOrbit EngEphemeris::getOrbit() const
599    {
600       if(!orbit.hasData())
601       {
602          InvalidRequest exc("getOrbit(): Required Orbit data not stored.");
603          GPSTK_THROW(exc);
604       }
605       return (orbit);
606    }
607 
getClock() const608    BrcClockCorrection EngEphemeris::getClock() const
609    {
610       if(!bcClock.hasData())
611       {
612          InvalidRequest exc("getClock(): Required Clock Correction data not"
613                             " stored.");
614          GPSTK_THROW(exc);
615       }
616       return (bcClock);
617    }
618 
getPRNID() const619    short EngEphemeris::getPRNID() const
620    {
621       if(!haveSubframe[0])
622       {
623          InvalidRequest exc("getPRNID(): Required subframe 1 not stored.");
624          GPSTK_THROW(exc);
625       }
626       return PRNID;
627    }
628 
getTracker() const629    short EngEphemeris::getTracker() const
630    {
631       if(!haveSubframe[0])
632       {
633          InvalidRequest exc("getTracker(): Required subframe 1 not stored.");
634          GPSTK_THROW(exc);
635       }
636       return tracker;
637    }
638 
getHOWTime(short subframe) const639    double EngEphemeris::getHOWTime(short subframe) const
640    {
641       if (!haveSubframe[subframe-1])
642       {
643          InvalidRequest exc("getHOWTime(): Subframe "
644                             +StringUtils::asString(subframe)+" not stored.");
645          GPSTK_THROW(exc);
646       }
647          // This return as a double is necessary for sets into CommonTime
648          // to not get confused.  Ints are Zcounts whereas doubles are seconds.
649          // This should still return a double after CommonTime->CommonTime
650          // conversion, for backwards compatibility. [DR]
651       return static_cast<double>(HOWtime[subframe-1]);
652    }
653 
getASAlert(short subframe) const654    short EngEphemeris::getASAlert(short subframe)  const
655    {
656       if (!haveSubframe[subframe-1])
657       {
658          InvalidRequest exc("getASAlert(): Subframe "
659                             +StringUtils::asString(subframe)+" not stored.");
660          GPSTK_THROW(exc);
661       }
662       return ASalert[subframe-1];
663    }
664 
getFullWeek() const665    short EngEphemeris::getFullWeek()  const
666    {
667       if (!haveSubframe[0])
668       {
669          InvalidRequest exc("getFullWeek(): Required subframe 1 not stored.");
670          GPSTK_THROW(exc);
671       }
672       return weeknum;
673    }
674 
getCodeFlags() const675    short EngEphemeris::getCodeFlags()  const
676    {
677       if (!haveSubframe[0])
678       {
679          InvalidRequest exc("getCodeFlags(): Required subframe 1 not stored.");
680          GPSTK_THROW(exc);
681       }
682       return codeflags;
683    }
684 
getAccuracy() const685    double EngEphemeris::getAccuracy()  const
686    {
687       if (!haveSubframe[0])
688       {
689          InvalidRequest exc("getAccuracy(): Required subframe 1 not stored.");
690          GPSTK_THROW(exc);
691       }
692       return orbit.getAccuracy();
693    }
694 
getAccFlag() const695    short EngEphemeris::getAccFlag()  const
696    {
697       if (!haveSubframe[0])
698       {
699          InvalidRequest exc("getAccFlag(): Required subframe 1 not stored.");
700          GPSTK_THROW(exc);
701       }
702       return orbit.getURAoe();
703    }
704 
getHealth() const705    short EngEphemeris::getHealth() const
706    {
707       if (!haveSubframe[0])
708       {
709          InvalidRequest exc("getHealth(): Required subframe 1 not stored.");
710          GPSTK_THROW(exc);
711       }
712       return health;
713    }
714 
getL2Pdata() const715    short EngEphemeris::getL2Pdata() const
716    {
717       if (!haveSubframe[0])
718       {
719          InvalidRequest exc("getL2Pdata(): Required subframe 1 not stored.");
720          GPSTK_THROW(exc);
721       }
722       return L2Pdata;
723    }
724 
getIODC() const725    short EngEphemeris::getIODC() const
726    {
727       if (!haveSubframe[0])
728       {
729          InvalidRequest exc("getIODC(): Required subframe 1 not stored.");
730          GPSTK_THROW(exc);
731       }
732       return static_cast<short>(IODC);
733    }
734 
getIODE() const735    short EngEphemeris::getIODE() const
736    {
737       if (!haveSubframe[1])
738       {
739          InvalidRequest exc("getIODE(): Required subframe 2 not stored.");
740          GPSTK_THROW(exc);
741       }
742       return static_cast<short>(IODE);
743    }
744 
getAODO() const745    long EngEphemeris::getAODO() const
746    {
747       if (!haveSubframe[1])
748       {
749          InvalidRequest exc("getAODO(): Required subframe 2 not stored.");
750          GPSTK_THROW(exc);
751       }
752       return AODO;
753    }
754 
getToc() const755    double EngEphemeris::getToc() const
756    {
757       if (!haveSubframe[0])
758       {
759          InvalidRequest exc("getToc(): Required subframe 1 not stored.");
760          GPSTK_THROW(exc);
761       }
762       return bcClock.getToc();
763    }
764 
getAf0() const765    double EngEphemeris::getAf0() const
766    {
767       if (!haveSubframe[0])
768       {
769          InvalidRequest exc("getAf0(): Required subframe 1 not stored.");
770          GPSTK_THROW(exc);
771       }
772       return bcClock.getAf0();
773    }
774 
getAf1() const775    double EngEphemeris::getAf1() const
776    {
777       if (!haveSubframe[0])
778       {
779          InvalidRequest exc("getAf1(): Required subframe 1 not stored.");
780          GPSTK_THROW(exc);
781       }
782       return bcClock.getAf1();
783    }
784 
getAf2() const785    double EngEphemeris::getAf2() const
786    {
787       if (!haveSubframe[0])
788       {
789          InvalidRequest exc("getAf1(): Required subframe 1 not stored.");
790          GPSTK_THROW(exc);
791       }
792       return bcClock.getAf2();
793    }
794 
getTgd() const795    double EngEphemeris::getTgd() const
796    {
797       if (!haveSubframe[0])
798       {
799          InvalidRequest exc("getTgd(): Required subframe 1 not stored.");
800          GPSTK_THROW(exc);
801       }
802       return Tgd;
803    }
804 
getCus() const805    double EngEphemeris::getCus() const
806    {
807       if (!haveSubframe[1])
808       {
809          InvalidRequest exc("getCus(): Required subframe 2 not stored.");
810          GPSTK_THROW(exc);
811       }
812       return orbit.getCus();
813    }
814 
getCrs() const815    double EngEphemeris::getCrs() const
816    {
817       if (!haveSubframe[1])
818       {
819          InvalidRequest exc("getCrs(): Required subframe 2 not stored.");
820          GPSTK_THROW(exc);
821       }
822       return orbit.getCrs();
823    }
824 
getCis() const825    double EngEphemeris::getCis() const
826    {
827       if (!haveSubframe[2])
828       {
829          InvalidRequest exc("getCis(): Required subframe 3 not stored.");
830          GPSTK_THROW(exc);
831       }
832       return orbit.getCis();
833    }
834 
getCrc() const835    double EngEphemeris::getCrc() const
836    {
837       if (!haveSubframe[2])
838       {
839          InvalidRequest exc("getCrc(): Required subframe 3 not stored.");
840          GPSTK_THROW(exc);
841       }
842       return orbit.getCrc();
843    }
844 
getCuc() const845    double EngEphemeris::getCuc() const
846    {
847       if (!haveSubframe[1])
848       {
849          InvalidRequest exc("getCuc(): Required subframe 2 not stored.");
850          GPSTK_THROW(exc);
851       }
852       return orbit.getCuc();
853    }
854 
getCic() const855    double EngEphemeris::getCic() const
856    {
857       if (!haveSubframe[2])
858       {
859          InvalidRequest exc("getCic(): Required subframe 3 not stored.");
860          GPSTK_THROW(exc);
861       }
862       return orbit.getCic();
863    }
864 
getToe() const865    double EngEphemeris::getToe() const
866    {
867       if (!haveSubframe[1])
868       {
869          InvalidRequest exc("getToe(): Required subframe 2 not stored.");
870          GPSTK_THROW(exc);
871       }
872       return orbit.getToe();
873    }
874 
getM0() const875    double EngEphemeris::getM0() const
876    {
877       if (!haveSubframe[1])
878       {
879          InvalidRequest exc("getM0(): Required subframe 2 not stored.");
880          GPSTK_THROW(exc);
881       }
882       return orbit.getM0();
883    }
884 
getDn() const885    double EngEphemeris::getDn() const
886    {
887       if (!haveSubframe[1])
888       {
889          InvalidRequest exc("getDn(): Required subframe 2 not stored.");
890          GPSTK_THROW(exc);
891       }
892       return orbit.getDn();
893    }
894 
getEcc() const895    double EngEphemeris::getEcc() const
896    {
897       if (!haveSubframe[1])
898       {
899          InvalidRequest exc("getEcc(): Required subframe 2 not stored.");
900          GPSTK_THROW(exc);
901       }
902       return orbit.getEcc();
903    }
904 
getAhalf() const905    double EngEphemeris::getAhalf() const
906    {
907       if (!haveSubframe[1])
908       {
909          InvalidRequest exc("getAhalf(): Required subframe 2 not stored.");
910          GPSTK_THROW(exc);
911       }
912       return orbit.getAhalf();
913    }
914 
getA() const915    double EngEphemeris::getA() const
916    {
917       if (!haveSubframe[1])
918       {
919          InvalidRequest exc("getA(): Required subframe 2 not stored.");
920          GPSTK_THROW(exc);
921       }
922       return orbit.getA();
923    }
924 
getOmega0() const925    double EngEphemeris::getOmega0() const
926    {
927       if (!haveSubframe[2])
928       {
929          InvalidRequest exc("getOmega0(): Required subframe 3 not stored.");
930          GPSTK_THROW(exc);
931       }
932       return orbit.getOmega0();
933    }
934 
getI0() const935    double EngEphemeris::getI0() const
936    {
937       if (!haveSubframe[2])
938       {
939          InvalidRequest exc("getI0(): Required subframe 3 not stored.");
940          GPSTK_THROW(exc);
941       }
942       return orbit.getI0();
943    }
944 
getW() const945    double EngEphemeris::getW() const
946    {
947       if (!haveSubframe[2])
948       {
949          InvalidRequest exc("getW(): Required subframe 3 not stored.");
950          GPSTK_THROW(exc);
951       }
952       return orbit.getW();
953    }
954 
getOmegaDot() const955    double EngEphemeris::getOmegaDot() const
956    {
957       if (!haveSubframe[2])
958       {
959          InvalidRequest exc("getOmegaDot(): Required subframe 3 not stored.");
960          GPSTK_THROW(exc);
961       }
962       return orbit.getOmegaDot();
963    }
964 
getIDot() const965    double EngEphemeris::getIDot() const
966    {
967       if (!haveSubframe[2])
968       {
969          InvalidRequest exc("getIDot(): Required subframe 3 not stored.");
970          GPSTK_THROW(exc);
971       }
972       return orbit.getIDot();
973    }
974 
getFitInt() const975    short EngEphemeris::getFitInt() const
976    {
977       if (!haveSubframe[1])
978       {
979          InvalidRequest exc("getFitInt(): Required subframe 2 not stored.");
980          GPSTK_THROW(exc);
981       }
982       return fitint;
983    }
984 
getTot() const985    long EngEphemeris::getTot() const
986    {
987       if(!haveSubframe[0])
988       {
989          InvalidRequest exc("getTot(): Required subframe 1 not stored.");
990          GPSTK_THROW(exc);
991       }
992       if(!haveSubframe[1])
993       {
994          InvalidRequest exc("getTot(): Required subframe 2 not stored.");
995          GPSTK_THROW(exc);
996       }
997       if(!haveSubframe[2])
998       {
999          InvalidRequest exc("getTot(): Required subframe 3 not stored.");
1000          GPSTK_THROW(exc);
1001       }
1002 
1003          // MSVC
1004 #ifdef _MSC_VER
1005       long foo = static_cast<long>( getHOWTime(1) < getHOWTime(2) )
1006          ? getHOWTime(1) : getHOWTime(2);
1007       foo = ( foo < getHOWTime(3) ) ? foo : getHOWTime(3) ;
1008 #else
1009       long foo =
1010          static_cast<long>( std::min( getHOWTime(1),
1011                                       std::min(getHOWTime(2), getHOWTime(3))));
1012 #endif
1013          // The ephemeris comes on 30 second boundaries, so...
1014       foo/=30;
1015       foo*=30;
1016       return foo;
1017    }
1018 
loadData(const std::string satSysArg,const unsigned short tlm[3],const long how[3],const short asalert[3],const short Tracker,const short prn,const short fullweek,const short cflags,const short acc,const short svhealth,const short iodc,const short l2pdata,const long aodo,const double tgd,const double toc,const double Af2,const double Af1,const double Af0,const short iode,const double crs,const double Dn,const double m0,const double cuc,const double Ecc,const double cus,const double ahalf,const double toe,const short fitInt,const double cic,const double Omega0,const double cis,const double I0,const double crc,const double W,const double OmegaDot,const double IDot)1019    EngEphemeris& EngEphemeris::loadData(
1020       const std::string satSysArg, const unsigned short tlm[3],
1021       const long how[3], const short asalert[3],
1022       const short Tracker, const short prn,
1023       const short fullweek, const short cflags, const short acc,
1024       const short svhealth, const short iodc, const short l2pdata,
1025       const long aodo, const double tgd, const double toc,
1026       const double Af2, const double Af1, const double Af0,
1027       const short iode, const double crs, const double Dn,
1028       const double m0, const double cuc, const double Ecc,
1029       const double cus, const double ahalf, const double toe,
1030       const short fitInt, const double cic, const double Omega0,
1031       const double cis, const double I0, const double crc,
1032       const double W, const double OmegaDot, const double IDot )
1033    {
1034       PRNID = prn;
1035       tracker = Tracker;
1036       for (int i=0; i<3; i++)
1037       {
1038          tlm_message[i] = tlm[i];
1039          HOWtime[i] = how[i];
1040          ASalert[i] = asalert[i];
1041       }
1042       weeknum   = fullweek;
1043       codeflags = cflags;
1044       short accFlag = acc;
1045          //double accuracy = gpstk::ura2accuracy(accFlag);
1046       health    = svhealth;
1047       L2Pdata   = l2pdata;
1048       IODC      = iodc;
1049       IODE      = iode;
1050       AODO      = aodo;
1051       fitint    = fitInt;
1052       Tgd       = tgd;
1053       satSys = satSysArg;
1054 
1055       satSys = "G";
1056 
1057          // The observation ID has a type of navigation, but the
1058          // carrier and code types are undefined.  They could be
1059          // L1/L2 C/A, P, Y,.....
1060       ObsID obsID(ObservationType::NavMsg, CarrierBand::Undefined, TrackingCode::Undefined);
1061       try
1062       {
1063          CommonTime toeCT = GPSWeekSecond(weeknum, toe, TimeSystem::GPS);
1064          CommonTime tocCT = GPSWeekSecond(weeknum, toc, TimeSystem::GPS);
1065 
1066          double A = ahalf*ahalf;
1067          double dndot = 0.0;
1068          double Adot = 0.0;
1069          short fitHours = getLegacyFitInterval(IODC, fitint);
1070          long beginFitSOW = toe - (fitHours/2)*3600;
1071          long endFitSOW = toe + (fitHours/2)*3600;
1072          short beginFitWk = weeknum;
1073          short endFitWk = weeknum;
1074          if (beginFitSOW < 0)
1075          {
1076             beginFitSOW += FULLWEEK;
1077             beginFitWk--;
1078          }
1079          CommonTime beginFit = GPSWeekSecond(beginFitWk, beginFitSOW,
1080                                              TimeSystem::GPS);
1081          if (endFitSOW >= FULLWEEK)
1082          {
1083             endFitSOW += FULLWEEK;
1084             endFitWk++;
1085          }
1086          CommonTime endFit = GPSWeekSecond(endFitWk, endFitSOW,
1087                                            TimeSystem::GPS);
1088 
1089          orbit.loadData(satSys, obsID, PRNID, beginFit, endFit, toeCT,
1090                         accFlag, (health == 0), cuc, cus, crc, crs, cic, cis,
1091                         m0, Dn, dndot, Ecc, A, ahalf, Adot, Omega0, I0, W,
1092                         OmegaDot, IDot);
1093 
1094          bcClock.loadData( satSys, obsID, PRNID, tocCT,
1095                            accFlag, (health == 0), Af0, Af1, Af2);
1096          haveSubframe[0] = true;
1097          haveSubframe[1] = true;
1098          haveSubframe[2] = true;
1099       }
1100       catch (Exception& exc)
1101       {
1102          InvalidRequest ire(exc);
1103          GPSTK_THROW(ire);
1104       }
1105       return *this;
1106    }
1107 
setSF1(unsigned tlm,double how,short asalert,short fullweek,short cflags,short acc,short svhealth,short iodc,short l2pdata,double tgd,double toc,double Af2,double Af1,double Af0,short Tracker,short prn)1108    EngEphemeris& EngEphemeris::setSF1( unsigned tlm, double how, short asalert,
1109                                        short fullweek, short cflags, short acc,
1110                                        short svhealth, short iodc,
1111                                        short l2pdata, double tgd, double toc,
1112                                        double Af2, double Af1, double Af0,
1113                                        short Tracker, short prn )
1114    {
1115       tlm_message[0] = tlm;
1116       HOWtime[0] = static_cast<long>( how );
1117       ASalert[0] = asalert;
1118       weeknum    = fullweek;
1119       codeflags  = cflags;
1120       accFlagTmp = acc;
1121       health     = svhealth;
1122       IODC       = iodc;
1123       L2Pdata    = l2pdata;
1124       Tgd        = tgd;
1125       tracker    = Tracker;
1126       PRNID      = prn;
1127       bool healthy = false;
1128       if (health == 0) healthy = true;
1129 
1130       double timeDiff = toc - HOWtime[0];
1131       short tocWeek = fullweek;
1132       if (timeDiff < -HALFWEEK)
1133          tocWeek++;
1134       else if (timeDiff > HALFWEEK)
1135          tocWeek--;
1136       try
1137       {
1138          CommonTime tocCT = GPSWeekSecond(tocWeek, toc, TimeSystem::GPS);
1139 
1140             // The system is assumed (legacy navigation message is from GPS)
1141          satSys = "G";
1142 
1143             // The observation ID has a type of navigation, but the
1144             // carrier and code types are undefined.  They could be
1145             // L1/L2 C/A, P, Y,.....
1146          ObsID obsID(ObservationType::NavMsg, CarrierBand::Undefined, TrackingCode::Undefined);
1147 
1148          bcClock.loadData( satSys, obsID, PRNID, tocCT,
1149                            accFlagTmp, healthy, Af0, Af1, Af2);
1150          haveSubframe[0] = true;
1151       }
1152       catch (Exception& exc)
1153       {
1154          haveSubframe[0] = false;
1155          InvalidRequest ire(exc);
1156          GPSTK_THROW(ire);
1157       }
1158       return *this;
1159    }
1160 
setSF2(unsigned tlm,double how,short asalert,short iode,double crs,double Dn,double m0,double cuc,double Ecc,double cus,double ahalf,double toe,short fitInt)1161    EngEphemeris& EngEphemeris::setSF2( unsigned tlm, double how, short asalert,
1162                                        short iode, double crs, double Dn,
1163                                        double m0, double cuc, double Ecc,
1164                                        double cus, double ahalf, double toe,
1165                                        short fitInt )
1166    {
1167       tlm_message[1] = tlm;
1168       HOWtime[1] = static_cast<long>( how );
1169       ASalert[1] = asalert;
1170       IODE       = iode;
1171       fitint     = fitInt;
1172 
1173       if (!haveSubframe[0])
1174       {
1175          InvalidRequest exc("Need to load subframe 1 before subframe 2");
1176          GPSTK_THROW(exc);
1177       }
1178       bool healthy = false;
1179       if (health == 0)
1180          healthy = true;
1181 
1182       double timeDiff = toe - HOWtime[1];
1183       short toeWeek = weeknum;
1184       if (timeDiff < -HALFWEEK)
1185          toeWeek++;
1186       else if (timeDiff > HALFWEEK)
1187          toeWeek--;
1188 
1189          // The observation ID has a type of navigation, but the
1190          // carrier and code types are undefined.  They could be
1191          // L1/L2 C/A, P, Y,.....
1192       ObsID obsID(ObservationType::NavMsg, CarrierBand::Undefined, TrackingCode::Undefined);
1193 
1194       short accFlag = accFlagTmp;   // accFlagTmp set in setSF1( )
1195          //local variables in SF3 that are needed to load SF2
1196       double crc = 0.0;
1197       double cis = 0.0;
1198       double cic = 0.0;
1199       double Omega0 = 0.0;
1200       double I0 = 0.0;
1201       double W = 0.0;
1202       double OmegaDot = 0.0;
1203       double IDot = 0.0;
1204          //also need locals for modernized nav quantaties not in SF2 or SF3
1205       double A = ahalf*ahalf;     // TEMP fix BWT
1206       double dndot = 0.0;
1207       double Adot = 0.0;
1208       try
1209       {
1210          short fitHours = getLegacyFitInterval(IODC, fitint);
1211          long beginFitSOW = toe - (fitHours/2)*3600.0;
1212          long endFitSOW = toe + (fitHours/2)*3600.0;
1213          short beginFitWk = weeknum;
1214          short endFitWk = weeknum;
1215          if (beginFitSOW < 0)
1216          {
1217             beginFitSOW += FULLWEEK;
1218             beginFitWk--;
1219          }
1220          CommonTime beginFit = GPSWeekSecond(beginFitWk, beginFitSOW,
1221                                              TimeSystem::GPS);
1222          if (endFitSOW >= FULLWEEK)
1223          {
1224             endFitSOW += FULLWEEK;
1225             endFitWk++;
1226          }
1227          CommonTime endFit = GPSWeekSecond(endFitWk, endFitSOW,
1228                                            TimeSystem::GPS);
1229 
1230          CommonTime toeCT = GPSWeekSecond(toeWeek, toe, TimeSystem::GPS);
1231 
1232          orbit.loadData(satSys, obsID, PRNID, beginFit, endFit, toeCT,
1233                         accFlag, healthy, cuc, cus, crc, crs, cic, cis, m0, Dn,
1234                         dndot, Ecc, A, ahalf, Adot, Omega0, I0, W, OmegaDot,
1235                         IDot);
1236          haveSubframe[1] = true;
1237       }
1238       catch (Exception& exc)
1239       {
1240          haveSubframe[1] = false;
1241          InvalidRequest ire(exc);
1242          GPSTK_THROW(ire);
1243       }
1244       return *this;
1245    }
1246 
setSF3(unsigned tlm,double how,short asalert,double cic,double Omega0,double cis,double I0,double crc,double W,double OmegaDot,double IDot)1247    EngEphemeris& EngEphemeris::setSF3( unsigned tlm, double how, short asalert,
1248                                        double cic, double Omega0, double cis,
1249                                        double I0, double crc, double W,
1250                                        double OmegaDot, double IDot )
1251    {
1252       tlm_message[2] = tlm;
1253       HOWtime[2] = static_cast<long>( how );
1254       ASalert[2] = asalert;
1255 
1256       if (!haveSubframe[1])
1257       {
1258          InvalidRequest exc("Need to load subframe 2 before subframe 3");
1259          GPSTK_THROW(exc);
1260       }
1261       bool healthy = false;
1262       if (health == 0)
1263          healthy = true;
1264 
1265       double timeDiff = orbit.getToe() - HOWtime[2];
1266       short toeWeek = weeknum;
1267       if (timeDiff < -HALFWEEK)
1268          toeWeek++;
1269       else if (timeDiff > HALFWEEK)
1270          toeWeek--;
1271 
1272          // The observation ID has a type of navigation, but the
1273          // carrier and code types are undefined.  They could be
1274          // L1/L2 C/A, P, Y,.....
1275       ObsID obsID(ObservationType::NavMsg, CarrierBand::Undefined, TrackingCode::Undefined);
1276 
1277       short accFlag = 0;
1278       double toe = 0.0;
1279       double cuc = 0.0;
1280       double cus = 0.0;
1281       double crs = 0.0;
1282       double m0 = 0.0;
1283       double Dn = 0.0;
1284       double dndot = 0.0;
1285       double Ecc = 0.0;
1286       double A = 0.0;
1287       double ahalf = 0.0;
1288       double Adot = 0.0;
1289       CommonTime beginFit;
1290       CommonTime endFit;
1291       try
1292       {
1293          accFlag = orbit.getURAoe();
1294          toe = orbit.getToe();
1295          cuc = orbit.getCuc();
1296          cus = orbit.getCus();
1297          dndot = orbit.getDnDot();
1298          A = orbit.getA();
1299          Adot = orbit.getAdot();
1300          crs = orbit.getCrs();
1301          m0 = orbit.getM0();
1302          Dn = orbit.getDn();
1303          Ecc = orbit.getEcc();
1304          ahalf = orbit.getAhalf();
1305          beginFit = orbit.getBeginningOfFitInterval();
1306          endFit = orbit.getEndOfFitInterval();
1307       }
1308       catch (Exception)
1309       {
1310             //Should not get to this point because of the
1311             //if(!haveSubFrame[1]) check above.
1312          haveSubframe[1] = false;
1313          haveSubframe[2] = false;
1314          return *this;
1315       }
1316       try
1317       {
1318          CommonTime toeCT = GPSWeekSecond(toeWeek, toe, TimeSystem::GPS);
1319 
1320          orbit.loadData(satSys, obsID, PRNID, beginFit, endFit, toeCT,
1321                         accFlag, healthy, cuc, cus, crc, crs, cic, cis, m0, Dn,
1322                         dndot, Ecc, A, ahalf, Adot, Omega0, I0, W, OmegaDot,
1323                         IDot);
1324 
1325          haveSubframe[2] = true;
1326       }
1327       catch (Exception& exc)
1328       {
1329          haveSubframe[2] = false;
1330       }
1331       return *this;
1332    }
1333 
setFIC(const bool arg)1334    void EngEphemeris::setFIC(const bool arg)
1335    {
1336       isFIC = arg;
1337    }
1338 
timeDisplay(ostream & os,const CommonTime & t)1339    static void timeDisplay( ostream & os, const CommonTime& t )
1340    {
1341          // Convert to CommonTime struct from GPS wk,SOW to M/D/Y, H:M:S.
1342       GPSWeekSecond dummyTime;
1343       dummyTime = GPSWeekSecond(t);
1344       os << setw(4) << dummyTime.week << "(";
1345       os << setw(4) << (dummyTime.week & 0x03FF) << ")  ";
1346       os << setw(6) << setfill(' ') << dummyTime.sow << "   ";
1347 
1348       switch (dummyTime.getDayOfWeek())
1349       {
1350          case 0: os << "Sun-0"; break;
1351          case 1: os << "Mon-1"; break;
1352          case 2: os << "Tue-2"; break;
1353          case 3: os << "Wed-3"; break;
1354          case 4: os << "Thu-4"; break;
1355          case 5: os << "Fri-5"; break;
1356          case 6: os << "Sat-6"; break;
1357          default: break;
1358       }
1359       os << "   " << (static_cast<YDSTime>(t)).printf("%3j   %5.0s   ")
1360          << (static_cast<CivilTime>(t)).printf("%02m/%02d/%04Y   %02H:%02M:%02S");
1361    }
1362 
shortcut(ostream & os,const long HOW)1363    static void shortcut(ostream & os, const long HOW )
1364    {
1365       short DOW, hour, min, sec;
1366       long SOD, SOW;
1367       short SOH;
1368 
1369       SOW = static_cast<long>( HOW );
1370       DOW = static_cast<short>( SOW / SEC_PER_DAY );
1371       SOD = SOW - static_cast<long>( DOW * SEC_PER_DAY );
1372       hour = static_cast<short>( SOD/3600 );
1373 
1374       SOH = static_cast<short>( SOD - (hour*3600) );
1375       min = SOH/60;
1376 
1377       sec = SOH - min * 60;
1378       switch (DOW)
1379       {
1380          case 0: os << "Sun-0"; break;
1381          case 1: os << "Mon-1"; break;
1382          case 2: os << "Tue-2"; break;
1383          case 3: os << "Wed-3"; break;
1384          case 4: os << "Thu-4"; break;
1385          case 5: os << "Fri-5"; break;
1386          case 6: os << "Sat-6"; break;
1387          default: break;
1388       }
1389 
1390       os << ":" << setfill('0')
1391          << setw(2) << hour
1392          << ":" << setw(2) << min
1393          << ":" << setw(2) << sec
1394          << setfill(' ');
1395    }
1396 
1397 
dumpTerse(ostream & s) const1398    void EngEphemeris :: dumpTerse(ostream& s) const
1399    {
1400 
1401          // Check if the subframes have been loaded before attempting
1402          // to dump them.
1403       if (!haveSubframe[0] || !haveSubframe[1] || !haveSubframe[2])
1404       {
1405          InvalidRequest exc("Need to load subframes 1,2 and 3");
1406          GPSTK_THROW(exc);
1407       }
1408 
1409       ios::fmtflags oldFlags = s.flags();
1410 
1411       s.setf(ios::fixed, ios::floatfield);
1412       s.setf(ios::right, ios::adjustfield);
1413       s.setf(ios::uppercase);
1414       s.precision(0);
1415       s.fill(' ');
1416 
1417       s << setw(2) << PRNID << " ! ";
1418 
1419       string tform = "%3j %02H:%02M:%02S";
1420 
1421       s << printTime(getTransmitTime(), tform) << " ! ";
1422       s << printTime(bcClock.getEpochTime(), tform) << " ! ";
1423       s << printTime(orbit.getEndOfFitInterval(), tform) << " !  ";
1424 
1425       s << setw(4) << setprecision(1) << getAccuracy() << "  ! ";
1426       s << "0x" << setfill('0') << hex << setw(3) << IODC << " ! ";
1427       s << "0x" << setfill('0')  << setw(2) << health;
1428       s << setfill(' ') << dec;
1429       s << "   " << setw(2) << health << " ! ";
1430 
1431       s << endl;
1432       s.flags(oldFlags);
1433 
1434    } // end of SF123::dumpTerse()
1435 
1436 
1437 
dump(ostream & s) const1438    void EngEphemeris :: dump(ostream& s) const
1439    {
1440 
1441 
1442          // Check if the subframes have been loaded before attempting
1443          // to dump them.
1444       if (!haveSubframe[0] || !haveSubframe[1] || !haveSubframe[2])
1445       {
1446          InvalidRequest exc("Need to load subframes 1,2 and 3");
1447          GPSTK_THROW(exc);
1448       }
1449 
1450       ios::fmtflags oldFlags = s.flags();
1451 
1452       s.setf(ios::fixed, ios::floatfield);
1453       s.setf(ios::right, ios::adjustfield);
1454       s.setf(ios::uppercase);
1455       s.precision(0);
1456       s.fill(' ');
1457 
1458       s << "****************************************************************"
1459         << "************" << endl
1460         << "Broadcast Ephemeris (Engineering Units)";
1461       if(isFIC)
1462       {
1463          s << " -FIC" << endl;
1464       }
1465       else
1466       {
1467          s << " -RINEX" << endl;
1468       }
1469       s << endl;
1470       s << "PRN : " << setw(2) << PRNID << endl;
1471       s << endl;
1472 
1473 
1474       s << "              Week(10bt)     SOW     DOW   UTD     SOD"
1475         << "   MM/DD/YYYY   HH:MM:SS\n";
1476       s << "Clock Epoch:  ";
1477 
1478       timeDisplay(s, bcClock.getEpochTime());
1479       s << endl;
1480       s << "Eph Epoch:    ";
1481       timeDisplay(s, orbit.getOrbitEpoch());
1482       s << endl;
1483 /*
1484   #if 0
1485      // FIX when moved from sf123, the tot got zapped.. because in
1486         // order for EngEphemeris to be able to use such a thing, it
1487            // needs to be pulled out of as-broadcast bits somehow.
1488            s << "Transmit time:" << setw(4) << weeknum << ", sow=" << Tot.GPSsecond() << endl
1489            << "Fit interval flag :  " << setw(2) << fitint
1490            << " (" << fitintlen << " hours)" << endl;
1491            #elsif 0
1492            s << "Transmit time:" << setw(4) << weeknum << endl
1493            << "Fit interval flag :  " << setw(2) << fitint
1494            << " (" << getFitInt() << " hours)" << endl;
1495            #endif
1496               // nuts to the above, let's just make it look like navdump output
1497               */
1498       s << "Transmit Time:";
1499       timeDisplay(s, getTransmitTime());
1500       s << endl;
1501       s << "Fit interval flag :  " << fitint << endl;
1502       if(isFIC)
1503       {
1504      	 s << endl
1505            << "          SUBFRAME OVERHEAD"
1506            << endl
1507            << endl
1508            << "               SOW    DOW:HH:MM:SS     IOD    ALERT   A-S\n";
1509      	 for (int i=0;i<3;i++)
1510      	 {
1511             s << "SF" << setw(1) << (i+1)
1512               << " HOW:   " << setw(7) << HOWtime[i]
1513               << "  ";
1514 
1515             shortcut( s, HOWtime[i]);
1516             if (i==0)
1517                s << "   ";
1518             else
1519                s << "    ";
1520 
1521             s << "0x" << setfill('0') << hex;
1522 
1523             if (i==0)
1524                s << setw(3) << IODC;
1525             else
1526                s << setw(2) << IODE;
1527 
1528             s << dec << "      " << setfill(' ');
1529 
1530             if (ASalert[i] & 0x0002)    // "Alert" bit handling
1531                s << "1     ";
1532             else
1533                s << "0     ";
1534 
1535             if (ASalert[i] & 0x0001)     // A-S flag handling
1536                s << " on";
1537             else
1538                s << "off";
1539             s << endl;
1540          }
1541       }
1542       else
1543       {
1544          s << endl;
1545          s << "IODC: 0x"
1546            << setfill('0') << hex;
1547          s << setw(3) << IODC << endl;
1548          s << "IODE:  0x"
1549            << setfill('0') << hex;
1550          s << setw(2) << IODE << endl;
1551       }
1552       s.setf(ios::scientific, ios::floatfield);
1553       s.precision(8);
1554       s.fill(' ');
1555 
1556       s << endl
1557         << "           CLOCK"
1558         << endl
1559         << endl
1560         << "Bias T0:     " << setw(16) << bcClock.getAf0() << " sec" << endl
1561         << "Drift:       " << setw(16) << bcClock.getAf1() << " sec/sec" << endl
1562         << "Drift rate:  " << setw(16) << bcClock.getAf2() << " sec/(sec**2)" << endl
1563         << "Group delay: " << setw(16) << Tgd << " sec" << endl;
1564 
1565       s << endl
1566         << "           ORBIT PARAMETERS"
1567         << endl
1568         << endl
1569         << "Semi-major axis:       " << setw(16) << orbit.getAhalf()  << " m**.5" << endl
1570         << "Motion correction:     " << setw(16) << orbit.getDn()     << " rad/sec"
1571         << endl
1572         << "Eccentricity:          " << setw(16) << orbit.getEcc()    << endl
1573         << "Arg of perigee:        " << setw(16) << orbit.getW()      << " rad" << endl
1574         << "Mean anomaly at epoch: " << setw(16) << orbit.getM0()     << " rad" << endl
1575         << "Right ascension:       " << setw(16) << orbit.getOmega0() << " rad    "
1576         << setw(16) << orbit.getOmegaDot() << " rad/sec" << endl
1577         << "Inclination:           " << setw(16) << orbit.getI0()     << " rad    "
1578         << setw(16) << orbit.getIDot()     << " rad/sec" << endl;
1579 
1580       s << endl
1581         << "           HARMONIC CORRECTIONS"
1582         << endl
1583         << endl
1584         << "Radial        Sine: " << setw(16) << orbit.getCrs() << " m    Cosine: "
1585         << setw(16) << orbit.getCrc() << " m" << endl
1586         << "Inclination   Sine: " << setw(16) << orbit.getCis() << " rad  Cosine: "
1587         << setw(16) << orbit.getCic() << " rad" << endl
1588         << "In-track      Sine: " << setw(16) << orbit.getCus() << " rad  Cosine: "
1589         << setw(16) << orbit.getCuc() << " rad" << endl;
1590 
1591       s << endl
1592         << "           SV STATUS"
1593         << endl
1594         << endl
1595         << "Health bits:   0x" << setfill('0')  << setw(2) << hex << health << dec
1596         << "      URA index: " << setfill(' ') << setw(4) << orbit.getURAoe() << endl
1597         << "Code on L2:   ";
1598 
1599       switch (codeflags)
1600       {
1601          case 0:
1602             s << "reserved ";
1603             break;
1604 
1605          case 1:
1606             s << " P only  ";
1607             break;
1608 
1609          case 2:
1610             s << " C/A only";
1611             break;
1612 
1613          case 3:
1614             s << " P & C/A ";
1615             break;
1616 
1617          default:
1618             break;
1619 
1620       }
1621       if(isFIC)
1622       {
1623          s << "  L2 P Nav data:          ";
1624          if (L2Pdata!=0)
1625             s << "off";
1626          else
1627             s << "on";
1628       }
1629 
1630       s << endl;
1631       s.flags(oldFlags);
1632 
1633    } // end of SF123::dump()
1634 
operator <<(ostream & s,const EngEphemeris & eph)1635    ostream& operator<<(ostream& s, const EngEphemeris& eph)
1636    {
1637       try
1638       {
1639          eph.dump(s);
1640       }
1641       catch(gpstk::Exception& ex)
1642       {
1643          ex.addLocation(FILE_LOCATION);
1644          GPSTK_RETHROW(ex);
1645       }
1646       return s;
1647 
1648    } // end of operator<<
1649 
1650 } // namespace
1651