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 GloEphemerisStore.cpp 41 * Get GLONASS broadcast ephemeris data information 42 */ 43 44 #include "GloEphemerisStore.hpp" 45 #include "TimeString.hpp" 46 47 using namespace std; 48 49 namespace gpstk 50 { 51 52 // Add ephemeris information from a Rinex3NavData object. addEphemeris(const Rinex3NavData & data)53 bool GloEphemerisStore::addEphemeris(const Rinex3NavData& data) 54 { 55 56 // If enabled, check SV health before entering here (health = 0 -> OK) 57 if( (data.health == 0) || (!onlyHealthy) ) 58 { 59 // Get a GloEphemeris object from Rinex3NavData object 60 GloEphemeris gloEphem(data); 61 62 CommonTime t( data.time); 63 t.setTimeSystem(TimeSystem::GLO); // must be GLONASS time 64 65 SatID sat( data.sat ); 66 pe[sat][t] = gloEphem; // find or add entry 67 68 if (t < initialTime) 69 initialTime = t; 70 if (t > finalTime) 71 finalTime = t; 72 73 return true; 74 75 } // End of 'if( (data.health == 0) || (!onlyHealthy) )' 76 77 return false; 78 79 } // End of method 'GloEphemerisStore::addEphemeris()' 80 81 82 /* Returns the position, velocity and clock offset of the indicated 83 * satellite in ECEF coordinates (meters) at the indicated time, 84 * in the PZ-90 ellipsoid. 85 * 86 * @param[in] sat Satellite's identifier 87 * @param[in] epoch Time to look up 88 * 89 * @return the Xvt of the object at the indicated time 90 * 91 * @throw InvalidRequest If the request can not be completed for any 92 * reason, this is thrown. The text may have additional information 93 * as to why the request failed. 94 */ getXvt(const SatID & sat,const CommonTime & epoch) const95 Xvt GloEphemerisStore::getXvt( const SatID& sat, 96 const CommonTime& epoch ) const 97 { 98 // TD is this too strict? 99 if(epoch.getTimeSystem() != initialTime.getTimeSystem()) 100 { 101 InvalidRequest e(string("Requested time system is not GLONASS time")); 102 GPSTK_THROW(e); 103 } 104 105 // Check that the given epoch is within the available time limits. 106 // We have to add a margin of 15 minutes (900 seconds). 107 if ( epoch < (initialTime - 900.0) || 108 epoch > (finalTime + 900.0) ) 109 { 110 InvalidRequest e( "Requested time is out of boundaries for satellite " 111 + StringUtils::asString(sat) ); 112 GPSTK_THROW(e); 113 } 114 115 // Look for the satellite in the 'pe' (EphMap) data structure. 116 GloEphMap::const_iterator svmap = pe.find(sat); 117 118 // If satellite was not found, issue an exception 119 if (svmap == pe.end()) 120 { 121 InvalidRequest e( "Ephemeris for satellite " 122 + StringUtils::asString(sat) + " not found." ); 123 GPSTK_THROW(e); 124 } 125 126 // Let's take the second part of the EphMap 127 const TimeGloMap& sem = svmap->second; 128 129 // Look for 'i': the first element whose key >= epoch. 130 TimeGloMap::const_iterator i = sem.lower_bound(epoch);; 131 132 // Values to be returned will be stored here 133 Xvt sv; 134 135 // If we reached the end, the requested time is beyond the last 136 // ephemeris record, but it may still be within the allowable time 137 // span, so we can use the last record. 138 if ( i == sem.end() ) 139 { 140 i = --i; 141 } 142 143 // If key > (epoch+900), we must use the previous record if possible. 144 if ( ( i->first > (epoch+900.0) ) && ( i != sem.begin() ) ) 145 { 146 i = --i; 147 } 148 149 // Check that the given epoch is within the available time limits for 150 // this specific satellite, with a margin of 15 minutes (900 seconds). 151 if ( epoch < (i->first - 900.0) || 152 epoch >= (i->first + 900.0) ) 153 { 154 InvalidRequest e( "Requested time is out of boundaries for satellite " 155 + StringUtils::asString(sat) ); 156 GPSTK_THROW(e); 157 } 158 159 // We now have the proper reference data record. Let's use it 160 GloEphemeris data( i->second ); 161 162 // Compute the satellite position, velocity and clock offset 163 sv = data.svXvt( epoch ); 164 165 // We are done, let's return 166 return sv; 167 168 }; // End of method 'GloEphemerisStore::getXvt()' 169 170 computeXvt(const SatID & sat,const CommonTime & epoch) const171 Xvt GloEphemerisStore::computeXvt(const SatID& sat, 172 const CommonTime& epoch) const throw() 173 { 174 Xvt rv; 175 rv.health = Xvt::HealthStatus::Unavailable; 176 try 177 { 178 // TD is this too strict? 179 if (epoch.getTimeSystem() != initialTime.getTimeSystem()) 180 { 181 // Not valid, but we don't have a specific "health" state 182 // of "wrong time system" 183 return rv; 184 } 185 186 // Check that the given epoch is within the available time limits. 187 // We have to add a margin of 15 minutes (900 seconds). 188 if ((epoch < (initialTime - 900.0)) || 189 (epoch > (finalTime + 900.0))) 190 { 191 return rv; 192 } 193 194 // Look for the satellite in the 'pe' (EphMap) data structure. 195 GloEphMap::const_iterator svmap = pe.find(sat); 196 197 // If satellite was not found, say so 198 if (svmap == pe.end()) 199 { 200 return rv; 201 } 202 203 // Let's take the second part of the EphMap 204 const TimeGloMap& sem = svmap->second; 205 206 // Look for 'i': the first element whose key >= epoch. 207 TimeGloMap::const_iterator i = sem.lower_bound(epoch);; 208 209 // If we reached the end, the requested time is beyond the last 210 // ephemeris record, but it may still be within the allowable time 211 // span, so we can use the last record. 212 if (i == sem.end()) 213 { 214 i = --i; 215 } 216 217 // If key > (epoch+900), we must use the previous record 218 // if possible. 219 if ((i->first > (epoch+900.0)) && (i != sem.begin())) 220 { 221 i = --i; 222 } 223 224 // Check that the given epoch is within the available time 225 // limits for this specific satellite, with a margin of 15 226 // minutes (900 seconds). 227 if ((epoch < (i->first - 900.0)) || 228 (epoch >= (i->first + 900.0))) 229 { 230 return rv; 231 } 232 233 // We now have the proper reference data record. Let's use it 234 GloEphemeris data(i->second); 235 236 // Compute the satellite position, velocity and clock offset 237 rv = data.svXvt(epoch); 238 rv.health = (data.getHealth() == 0 ? Xvt::HealthStatus::Healthy 239 : Xvt::HealthStatus::Unhealthy); 240 } 241 catch (...) 242 { 243 } 244 245 // We are done, let's return 246 return rv; 247 } 248 249 getSVHealth(const SatID & sat,const CommonTime & epoch) const250 Xvt::HealthStatus GloEphemerisStore::getSVHealth(const SatID& sat, 251 const CommonTime& epoch) 252 const throw() 253 { 254 Xvt::HealthStatus rv = Xvt::HealthStatus::Unavailable; 255 try 256 { 257 // TD is this too strict? 258 if (epoch.getTimeSystem() != initialTime.getTimeSystem()) 259 { 260 // Not valid, but we don't have a specific "health" state 261 // of "wrong time system" 262 return rv; 263 } 264 265 // Check that the given epoch is within the available time limits. 266 // We have to add a margin of 15 minutes (900 seconds). 267 if ((epoch < (initialTime - 900.0)) || 268 (epoch > (finalTime + 900.0))) 269 { 270 return rv; 271 } 272 273 // Look for the satellite in the 'pe' (EphMap) data structure. 274 GloEphMap::const_iterator svmap = pe.find(sat); 275 276 // If satellite was not found, say so 277 if (svmap == pe.end()) 278 { 279 return rv; 280 } 281 282 // Let's take the second part of the EphMap 283 const TimeGloMap& sem = svmap->second; 284 285 // Look for 'i': the first element whose key >= epoch. 286 TimeGloMap::const_iterator i = sem.lower_bound(epoch);; 287 288 // If we reached the end, the requested time is beyond the last 289 // ephemeris record, but it may still be within the allowable time 290 // span, so we can use the last record. 291 if (i == sem.end()) 292 { 293 i = --i; 294 } 295 296 // If key > (epoch+900), we must use the previous record 297 // if possible. 298 if ((i->first > (epoch+900.0)) && (i != sem.begin())) 299 { 300 i = --i; 301 } 302 303 // Check that the given epoch is within the available time 304 // limits for this specific satellite, with a margin of 15 305 // minutes (900 seconds). 306 if ((epoch < (i->first - 900.0)) || 307 (epoch >= (i->first + 900.0))) 308 { 309 return rv; 310 } 311 312 // We now have the proper reference data record. Let's use it 313 GloEphemeris data(i->second); 314 rv = (data.getHealth() == 0 ? Xvt::HealthStatus::Healthy 315 : Xvt::HealthStatus::Unhealthy); 316 } 317 catch (...) 318 { 319 } 320 321 // We are done, let's return 322 return rv; 323 324 } 325 326 327 /* A debugging function that outputs in human readable form, 328 * all data stored in this object. 329 * 330 * @param[in] s The stream to receive the output; defaults to cout 331 * @param[in] detail The level of detail to provide 332 * 333 * @warning GLONASS position, velocity and acceleration information are 334 * given in km, km/s and km/(s*s), respectively. 335 */ dump(std::ostream & s,short detail) const336 void GloEphemerisStore::dump( std::ostream& s, short detail ) const 337 { 338 static const string fmt("%04Y/%02m/%02d %02H:%02M:%02S %P"); 339 s << "Dump of GloEphemerisStore:\n"; 340 341 //if(detail == 0 ) { 342 s << " Span is " << (initialTime == CommonTime::END_OF_TIME 343 ? "End_time" : printTime(initialTime, fmt)) 344 << " to " << (finalTime == CommonTime::BEGINNING_OF_TIME 345 ? "Begin_time" : printTime(finalTime, fmt)) 346 << " with " << pe.size() << " entries; checkHealthFlag is " 347 << (onlyHealthy ? "T":"F") << std::endl; 348 349 //} 350 //else 351 if(detail > 0) { 352 if(pe.size()) 353 s << "Dump every record:\nweek sow = year/mn/dy hr:mi:sc Sys Sat " 354 << "X Y Z " 355 << "VX VY VZ " 356 << "AX AY AZ " 357 << "TauN GammaN MFtime Hlth fNo AgeInfo\n"; 358 359 // Iterate through all items in the 'pe' GloEphMap 360 for( GloEphMap::const_iterator it = pe.begin(); it != pe.end(); ++it ) { 361 362 // Then, iterate through corresponding 'TimeGloMap' 363 for( TimeGloMap::const_iterator tgmIter = (*it).second.begin(); 364 tgmIter != (*it).second.end(); 365 ++tgmIter ) 366 { 367 368 // First, print year, Day-Of-Year and Seconds of Day 369 s << printTime(tgmIter->first,fmt) << " "; 370 371 // Second, print SatID information 372 s << RinexSatID((*it).first) << " "; 373 374 // Third, print satellite ephemeris data 375 GloEphemeris data( (*tgmIter).second ); 376 // Get the satellite's acceleration 377 Triple a( data.getAcc() ); 378 379 s << scientific << setprecision(12); 380 s << setw(19) << data.x[0] << " " 381 << setw(19) << data.x[1] << " " 382 << setw(19) << data.x[2] << " " 383 << setw(19) << data.v[0] << " " 384 << setw(19) << data.v[1] << " " 385 << setw(19) << data.v[2] << " " 386 << setw(19) << a[0] << " " 387 << setw(19) << a[1] << " " 388 << setw(19) << a[2] << " " 389 << setw(19) << data.getTauN() << " " 390 << setw(19) << data.getGammaN() << " " 391 << setw(6) << data.getMFtime() << " " 392 << setw(3) << data.getHealth() << " " 393 << setw(3) << data.getfreqNum() << " " 394 << setprecision(2) << setw(5) << data.getAgeOfInfo(); 395 396 // Add end-of-line 397 s << endl; 398 399 } // End of 'for( TimeGloMap::const_iterator tgmIter = ...' 400 401 } // End of 'for( GloEphMap::const_iterator it = pe.begin(); ...' 402 403 } // End of 'else', i.e., detail != 0 404 405 s << " End of GloEphemerisStore data." << std::endl; 406 407 408 }; // End of method 'GloEphemerisStore::dump()' 409 410 411 /* Edit the dataset, removing data outside the indicated time interval 412 * 413 * @param[in] tmin Defines the beginning of the time interval 414 * @param[in] tmax Defines the end of the time interval 415 */ edit(const CommonTime & tmin,const CommonTime & tmax)416 void GloEphemerisStore::edit( const CommonTime& tmin, 417 const CommonTime& tmax ) 418 { 419 420 // Create a working copy 421 GloEphMap bak; 422 423 // Reset the initial and final times 424 initialTime = CommonTime::END_OF_TIME; 425 finalTime = CommonTime::BEGINNING_OF_TIME; 426 427 // Iterate through all items in the 'bak' GloEphMap 428 for( GloEphMap::const_iterator it = pe.begin(); 429 it != pe.end(); 430 ++it ) 431 { 432 433 // Then, iterate through corresponding 'TimeGloMap' 434 for( TimeGloMap::const_iterator tgmIter = (*it).second.begin(); 435 tgmIter != (*it).second.end(); 436 ++tgmIter ) 437 { 438 439 CommonTime t( (*tgmIter).first ); 440 441 // Check if the current record is within the given time interval 442 if( ( tmin <= t ) && ( t <= tmax ) ) 443 { 444 445 // If we are within the proper boundaries, let's add the data 446 GloEphemeris data( (*tgmIter).second ); 447 448 SatID sat( (*it).first ); 449 bak[sat][t] = data; // Add entry 450 451 // Update 'initialTime' and 'finalTime', if necessary 452 if (t < initialTime) 453 initialTime = t; 454 else if (t > finalTime) 455 finalTime = t; 456 457 } // End of 'if ( ( (*tgmIter).first >= tmin ) && ...' 458 459 } // End of 'for( TimeGloMap::const_iterator tgmIter = ...' 460 461 } // End of 'for( GloEphMap::const_iterator it = pe.begin(); ...' 462 463 // Update the data map before returning 464 pe = bak; 465 466 return; 467 468 }; // End of method 'GloEphemerisStore::edit()' 469 470 471 // Determine the earliest time for which this object can successfully 472 // determine the Xvt for any object. 473 // @return The initial time 474 // @throw InvalidRequest This is thrown if the object has no data. getInitialTime() const475 CommonTime GloEphemerisStore::getInitialTime() const 476 { 477 478 // Check if the data map is empty 479 if( pe.empty() ) 480 { 481 InvalidRequest e( "GloEphemerisStore object has no data." ); 482 GPSTK_THROW(e); 483 } 484 485 return initialTime; 486 487 }; // End of method 'GloEphemerisStore::getInitialTime()' 488 489 490 // Determine the latest time for which this object can successfully 491 // determine the Xvt for any object. 492 // @return The final time 493 // @throw InvalidRequest This is thrown if the object has no data. getFinalTime() const494 CommonTime GloEphemerisStore::getFinalTime() const 495 { 496 497 // Check if the data map is empty 498 if( pe.empty() ) 499 { 500 InvalidRequest e( "GloEphemerisStore object has no data." ); 501 GPSTK_THROW(e); 502 } 503 504 return finalTime; 505 506 }; // End of method 'GloEphemerisStore::getFinalTime()' 507 508 // Compute initial time for the given satellite getInitialTime(const SatID & sat) const509 CommonTime GloEphemerisStore::getInitialTime(const SatID& sat) const 510 { 511 CommonTime ret(CommonTime::END_OF_TIME); 512 GloEphMap::const_iterator svmap = pe.find(sat); 513 if(svmap != pe.end()) { 514 const TimeGloMap& tgm = svmap->second; 515 TimeGloMap::const_iterator it = tgm.begin(); 516 ret = it->first; 517 } 518 return ret; 519 } 520 521 // Compute final time for the given satellite getFinalTime(const SatID & sat) const522 CommonTime GloEphemerisStore::getFinalTime(const SatID& sat) const 523 { 524 CommonTime ret(CommonTime::BEGINNING_OF_TIME); 525 GloEphMap::const_iterator svmap = pe.find(sat); 526 if(svmap != pe.end()) { 527 const TimeGloMap& tgm = svmap->second; 528 TimeGloMap::const_reverse_iterator rit = tgm.rbegin(); 529 ret = rit->first; 530 } 531 return ret; 532 } 533 getIndexSet() const534 set<SatID> GloEphemerisStore::getIndexSet() const 535 { 536 set<SatID> retSet; 537 GloEphMap::const_iterator cit; 538 for (cit=pe.begin();cit!=pe.end();cit++) 539 { 540 const SatID& sidr = cit->first; 541 retSet.insert(sidr); 542 } 543 return retSet; 544 } 545 546 /* Find the corresponding GLONASS ephemeris for the given epoch. 547 * 548 * @param sat SatID of satellite of interest. 549 * @param t time with which to search for ephemeris. 550 * 551 * @return a reference to the desired ephemeris. 552 * @throw InvalidRequest object thrown when no ephemeris is found. 553 */ 554 const GloEphemeris& GloEphemerisStore :: findEphemeris(const SatID & sat,const CommonTime & epoch) const555 findEphemeris( const SatID& sat, const CommonTime& epoch ) const 556 { 557 // Check that the given epoch is within the available time limits. 558 // We have to add a margin of 15 minutes (900 seconds). 559 if ( epoch < (initialTime - 900.0) || 560 epoch > (finalTime + 900.0) ) 561 { 562 InvalidRequest e( "Requested time is out of boundaries for satellite " 563 + StringUtils::asString(sat) ); 564 GPSTK_THROW(e); 565 } 566 567 // Look for the satellite in the 'pe' (EphMap) data structure. 568 GloEphMap::const_iterator svmap = pe.find(sat); 569 570 // If satellite was not found, issue an exception 571 if (svmap == pe.end()) 572 { 573 InvalidRequest e( "Ephemeris for satellite " 574 + StringUtils::asString(sat) + " not found." ); 575 GPSTK_THROW(e); 576 } 577 578 // Let's take the second part of the EphMap 579 const TimeGloMap& sem = svmap->second; 580 581 // 'i' will be the first element whose key >= epoch. 582 TimeGloMap::const_iterator i = sem.lower_bound(epoch); 583 584 // If we reached the end, the requested time is beyond the last 585 // ephemeris record, but it may still be within the allowable time 586 // span, so we can use the last record. 587 if ( i == sem.end() ) 588 { 589 i = --i; 590 } 591 592 // If key > (epoch+900), we must use the previous record if possible. 593 if ( ( i->first > (epoch+900.0) ) && ( i != sem.begin() ) ) 594 { 595 i = --i; 596 } 597 598 // Check that the given epoch is within the available time limits for 599 // this specific satellite, with a margin of 15 minutes (900 seconds). 600 if ( epoch < (i->first - 900.0) || 601 epoch > (i->first + 900.0) ) 602 { 603 InvalidRequest e( "Requested time is out of boundaries for satellite " 604 + StringUtils::asString(sat) ); 605 GPSTK_THROW(e); 606 } 607 608 // We now have the proper reference data record. Let's return it 609 return ( i->second ); 610 611 }; // End of method 'GloEphemerisStore::findEphemeris()' 612 613 614 // Return true if the given SatID is present in the store isPresent(const SatID & id) const615 bool GloEphemerisStore::isPresent(const SatID& id) const 616 { 617 618 // Look for the satellite in the 'pe' (GloEphMap) data structure. 619 GloEphMap::const_iterator svmap = pe.find(id); 620 621 // If satellite was not found return false, else return true 622 if (svmap == pe.end()) 623 { 624 return false; 625 } 626 else 627 { 628 return true; 629 } 630 631 }; // End of method 'GloEphemerisStore::isPresent(const SatID& id)' 632 633 addToList(std::list<GloEphemeris> & v) const634 int GloEphemerisStore::addToList(std::list<GloEphemeris>& v) const 635 { 636 int n = 0; 637 for(GloEphMap::const_iterator it = pe.begin(); it != pe.end(); ++it ) 638 { 639 for(TimeGloMap::const_iterator tgmIter = (*it).second.begin(); 640 tgmIter != (*it).second.end(); ++tgmIter ) 641 { 642 v.push_back(tgmIter->second); 643 n++; 644 } 645 } 646 return n; 647 } 648 649 650 } // End of namespace gpstk 651