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