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