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 GPSEphemeris.cpp Encapsulates the GPS legacy broadcast ephemeris and clock.
40 /// Inherits OrbitEph, which does most of the work; this class adds health and
41 /// accuracy information, fit interval, ionospheric correction terms and data
42 /// flags.
43 
44 #include <string>
45 #include "Exception.hpp"
46 #include "GPSWeekSecond.hpp"
47 #include "CivilTime.hpp"
48 #include "TimeString.hpp"
49 
50 #include "GPSEphemeris.hpp"
51 
52 using namespace std;
53 
54 namespace gpstk
55 {
56    // Returns true if the time, ct, is within the period of validity of
57    // this OrbitEph object.
58    // @throw Invalid Request if the required data has not been stored.
isValid(const CommonTime & ct) const59    bool GPSEphemeris::isValid(const CommonTime& ct) const
60    {
61       try {
62          if(ct >= beginValid && ct <= endValid)
63          {
64             return true;
65          }
66          return false;
67       }
68       catch(Exception& e) { GPSTK_RETHROW(e); }
69    }
70 
71    // This function returns the health status of the SV.
isHealthy(void) const72    bool GPSEphemeris::isHealthy(void) const
73    {
74       try {
75          OrbitEph::isHealthy();     // ignore the return value; for dataLoaded check
76          if(health == 0) return true;
77          return false;
78       }
79       catch(Exception& e) { GPSTK_RETHROW(e); }
80    }
81 
82    // adjustBeginningValidity determines the beginValid and endValid times.
83    // @throw Invalid Request if the required data has not been stored.
adjustValidity(void)84    void GPSEphemeris::adjustValidity(void)
85    {
86       try {
87          OrbitEph::adjustValidity();   // for dataLoaded check
88 
89 	      // Beginning of Validity
90                // New concept.  Admit the following.
91 	      //  (a.) The collection system may not capture the data at earliest transmit.
92 	      //  (b.) The collection system may not capture the three SFs consecutively.
93 	      // Consider a couple of IS-GPS-200 promises,
94 	      //  (c.) By definition, beginning of validity == beginning of transmission.
95 	      //  (d.) Except for uploads, cutovers will only happen on hour boundaries
96 	      //  (e.) Cutovers can be detected by non-even Toc.
97 	      //  (f.) Even uploads will cutover on a frame (30s) boundary.
98                // Therefore,
99 	      //   1.) If Toc is NOT even two hour interval, pick lowest HOW time,
100 	      //   round back to even 30s.  That's the earliest Xmit time we can prove.
101 	      //   NOTE: For the case where this is the SECOND SF 1/2/3 after an upload,
102 	      //   this may yield a later time as such a SF 1/2/3 will be on a even
103 	      //   hour boundary.  Unfortunately, we have no way of knowing whether
104 	      //   this item is first or second after upload without additional information
105 	      //   2.) If Toc IS even two hour interval, then derive beginValid from promis
106          //   in IS-GPS-200 that beginning of transmission will be Toc- fitInterval/2.
107          long longToc = static_cast<GPSWeekSecond>(ctToc).getSOW();
108          if ( (longToc % 7200) != 0)     // NOT an even two hour change
109          {
110             long XmitWeek = static_cast<GPSWeekSecond>(transmitTime).getWeek();
111             double XmitSOW = 0.0;
112             long Xmit = HOWtime - (HOWtime % 30);
113 	         XmitSOW = (double) Xmit;
114             beginValid = GPSWeekSecond( XmitWeek, XmitSOW, TimeSystem::GPS );
115          }
116          else
117          {
118             beginValid = ctToc - ((fitDuration/2) * 3600);
119          }
120 
121 	      // End of Validity.
122 	      // The end of validity is calculated from the fit interval
123 	      // and the Toe.  The fit interval is either trivial
124 	      // (if fit interval flag==0, fit interval is 4 hours)
125 	      // or a look-up table based on the IODC.
126 	      // Round the Toe value to the hour to elminate confusion
127 	      // due to possible "small offsets" indicating uploads
128          long epochWeek = static_cast<GPSWeekSecond>(ctToe).getWeek();
129          double Toe = static_cast<GPSWeekSecond>(ctToe).getSOW();
130          long ToeOffset = (long) Toe % 3600;
131          double adjToe = Toe;                  // Default case
132          if (ToeOffset)
133          {
134             adjToe += 3600.0 - (double)ToeOffset;  // If offset, then adjust to remove
135          }
136          long endFitSOW = adjToe + (fitDuration/2)*3600;
137          short endFitWk = epochWeek;
138          if (endFitSOW >= FULLWEEK)
139          {
140             endFitSOW -= FULLWEEK;
141             endFitWk++;
142          }
143          endValid = GPSWeekSecond(endFitWk, endFitSOW, TimeSystem::GPS);
144       }
145       catch(Exception& e) { GPSTK_RETHROW(e); }
146    }
147 
148    // Dump the overhead information as a string containing a single line.
149    // @throw Invalid Request if the required data has not been stored.
asString(void) const150    string GPSEphemeris::asString(void) const
151    {
152       if(!dataLoadedFlag)
153          GPSTK_THROW(InvalidRequest("Data not loaded"));
154       try {
155          ostringstream os;
156          CivilTime ct;
157          os << "EPH G" << setfill('0') << setw(2) << satID.id << setfill(' ');
158          ct = CivilTime(beginValid);
159          os << printTime(ct," | %4Y %3j %02H:%02M:%02S |");
160          ct = CivilTime(ctToe);
161          os << printTime(ct," %3j %02H:%02M:%02S |");
162          ct = CivilTime(ctToc);
163          os << printTime(ct," %3j %02H:%02M:%02S |");
164          ct = CivilTime(endValid);
165          os << printTime(ct," %3j %02H:%02M:%02S |");
166          ct = CivilTime(transmitTime);
167          os << printTime(ct," %3j %02H:%02M:%02S | ");
168          os << setw(3) << IODE << " | " << setw(3) << IODC << " | " << health;
169          return os.str();
170       }
171       catch(Exception& e) { GPSTK_RETHROW(e);
172       }
173    }
174 
175    // Dump the overhead information to the given output stream.
176    // @throw Invalid Request if the required data has not been stored.
dumpHeader(std::ostream & os) const177    void GPSEphemeris::dumpHeader(std::ostream& os) const
178    {
179       try {
180          // copy from OrbitEph::dumpHeader() ...
181          if(!dataLoadedFlag)
182             GPSTK_THROW(InvalidRequest("Data not loaded"));
183 
184          os << "****************************************************************"
185             << "************" << endl
186             << "Broadcast Orbit Ephemeris of class " << getName() << endl;
187          os << "Satellite: " << convertSatelliteSystemToString(satID.system)
188             << " " << setfill('0') << setw(2) << satID.id << setfill(' ');
189 
190       }
191       catch(Exception& e) { GPSTK_RETHROW(e); }
192    }
193 
194    // Dump the orbit, etc information to the given output stream.
195    // @throw Invalid Request if the required data has not been stored.
dumpBody(std::ostream & os) const196    void GPSEphemeris::dumpBody(std::ostream& os) const
197    {
198       try {
199          OrbitEph::dumpBody(os);
200 
201          os << "           GPS-SPECIFIC PARAMETERS\n"
202             << scientific << setprecision(8)
203             << "Tgd (L1/L2) : " << setw(16) << Tgd << " meters" << endl
204             << "HOW time    : " << setw(6) << HOWtime << " (sec of GPS week "
205                << setw(4) << static_cast<GPSWeekSecond>(ctToe).getWeek() << ")"
206             << "   fitDuration: " << setw(2) << fitDuration << " hours" << endl
207             << "TransmitTime: " << OrbitEph::timeDisplay(transmitTime) << endl
208             << "Accuracy    : flag(URA): " << accuracyFlag << " => "
209             << fixed << setprecision(2) << getAccuracy() << " meters" << endl
210             << "IODC: " << IODC << "   IODE: " << IODE << "   health: " << health
211             << " (0=good)   codeflags: " << codeflags << "   L2Pdata: " << L2Pdata
212             << endl;
213       }
214       catch(Exception& e) { GPSTK_RETHROW(e); }
215    }
216 
217    // Get the fit interval in hours from the fit interval flag and the IODC
getFitInterval(const short IODC,const short fitIntFlag)218    short GPSEphemeris::getFitInterval(const short IODC, const short fitIntFlag)
219    {
220       // TD This is for Block II/IIA, need to update for Block IIR and IIF
221 
222       if(IODC < 0 || IODC > 1023) // error in IODC, return minimum fit
223          return 4;
224 
225       if((fitIntFlag == 0 && (IODC & 0xFF) < 240) || (IODC & 0xFF) > 255)
226          return 4;
227 
228       else if(fitIntFlag == 1) {
229 
230          if(((IODC & 0xFF) < 240 || (IODC & 0xFF) > 255))
231             return 6;
232          else if(IODC >=240 && IODC <=247)
233             return 8;
234          else if((IODC >= 248 && IODC <= 255) || IODC == 496)
235             return 14;
236 
237          // Revised in IS-GPS-200 Revision D for Block IIR/IIR-M/IIF
238          else if((IODC >= 497 && IODC <=503) || (IODC >= 1021 && IODC <= 1023))
239             return 26;
240          else if(IODC >= 504 && IODC <=510)
241             return 50;
242          else if(IODC == 511 || (IODC >= 752 && IODC <= 756))
243             return 74;
244 
245          // NOTE:
246          // The following represents old fit intervals for Block II (not IIA)
247          // and is present only in old versions of the ICD-GPS-200 Rev. C.
248          // Please do not remove them as there are still people that may
249          // want to process old Block II data and none of the IODC intervals
250          // overlap (so far) so there is no need to remove them.
251          else if(IODC >= 757 && IODC <= 763)
252             return 98;
253          else if((IODC >= 764 && IODC <=767) || (IODC >=1008 && IODC <=1010))
254             return 122;
255          else if(IODC >= 1011 && IODC <=1020)
256             return 146;
257          else              // error in the IODC or ephemeris, return minimum fit
258             return 4;
259       }
260       else                 // error in ephemeris/IODC, return minimum fit
261          return 4;
262 
263       return 0; // never reached
264    }
265 
266 } // end namespace
267