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 OrbElemStore.cpp 41 * Store GNSS broadcast OrbElemBase information, and access by 42 * satellite and time. Several of the "least common denominator" 43 * methods are defined in this base class, several are over-ridden 44 * by descendent classes. 45 */ 46 47 #include <iostream> 48 #include <fstream> 49 #include <iomanip> 50 51 #include "StringUtils.hpp" 52 #include "OrbElemStore.hpp" 53 #include "MathBase.hpp" 54 #include "OrbElem.hpp" 55 #include "CivilTime.hpp" 56 #include "TimeString.hpp" 57 58 using namespace std; 59 using gpstk::StringUtils::asString; 60 61 62 namespace gpstk 63 { 64 65 //-------------------------------------------------------------------------- 66 67 OrbElemStore :: ~OrbElemStore()68 ~OrbElemStore() 69 { 70 for (auto& ubei : ube) 71 { 72 for (auto& oemi : ubei.second) 73 { 74 delete oemi.second; 75 } 76 } 77 } 78 79 getXvt(const SatID & sat,const CommonTime & t) const80 Xvt OrbElemStore::getXvt(const SatID& sat, const CommonTime& t) const 81 { 82 try 83 { 84 // Find appropriate orbit elements (if available) 85 const OrbElemBase* eph = findOrbElem(sat,t); 86 87 // If the orbital elements are unhealthy, refuse to 88 // calculate an SV position and throw. 89 if (!eph->isHealthy() && onlyHealthy) 90 { 91 InvalidRequest exc( std::string("SV is transmitting unhealthy navigation ") 92 + std::string("message at time of interest.") ); 93 GPSTK_THROW( exc ); 94 } 95 Xvt sv = eph->svXvt(t); 96 return sv; 97 } 98 catch(InvalidRequest& ir) 99 { 100 GPSTK_RETHROW(ir); 101 } 102 } 103 104 computeXvt(const SatID & sat,const CommonTime & t) const105 Xvt OrbElemStore::computeXvt(const SatID& sat, const CommonTime& t) const 106 throw() 107 { 108 Xvt rv; 109 rv.health = Xvt::HealthStatus::Unavailable; 110 try 111 { 112 // Find appropriate orbit elements (if available) 113 const OrbElemBase* eph = findOrbElem(sat,t); 114 if (eph != nullptr) 115 { 116 // compute the position, velocity and time 117 rv = eph->svXvt(t); 118 rv.health = (eph->isHealthy() ? Xvt::HealthStatus::Healthy 119 : Xvt::HealthStatus::Unhealthy); 120 } 121 } 122 catch (...) 123 { 124 } 125 return rv; 126 } 127 128 129 Xvt::HealthStatus OrbElemStore :: getSVHealth(const SatID & sat,const CommonTime & t) const130 getSVHealth(const SatID& sat, const CommonTime& t) const throw() 131 { 132 Xvt::HealthStatus rv = Xvt::HealthStatus::Unavailable; 133 try 134 { 135 // Find appropriate orbit elements (if available) 136 const OrbElemBase* eph = findOrbElem(sat,t); 137 if (eph != nullptr) 138 { 139 rv = (eph->isHealthy() ? Xvt::HealthStatus::Healthy 140 : Xvt::HealthStatus::Unhealthy); 141 } 142 } 143 catch (...) 144 { 145 } 146 return rv; 147 } 148 149 //------------------------------------------------------------------------------ 150 validSatSystem(const SatID & sat) const151 void OrbElemStore::validSatSystem(const SatID& sat) const 152 { 153 if (!isSatSysPresent(sat.system)) 154 { 155 stringstream ess; 156 ess << "Store does not contain orbit/clock elements for system "; 157 ess << static_cast<int>(sat.system); 158 ess << ". " << endl; 159 ess << " Valid systems are :" << endl; 160 161 list<SatelliteSystem>::const_iterator cit; 162 for (cit=sysList.begin();cit!=sysList.end();cit++) 163 { 164 SatelliteSystem ssTest = *cit; 165 ess << convertSatelliteSystemToString(ssTest) << endl; 166 } 167 168 InvalidRequest ir(ess.str()); 169 GPSTK_THROW(ir); 170 } 171 } 172 173 //-------------------------------------------------------------------------- 174 // Typically overridden by descendents to obtain system-specific 175 // listing behavior. 176 dump(std::ostream & s,short detail) const177 void OrbElemStore::dump(std::ostream& s, short detail) const 178 throw() 179 { 180 UBEMap::const_iterator it; 181 static const string fmt("%04Y/%02m/%02d %02H:%02M:%02S %P"); 182 183 s << "Dump of OrbElemStore:\n"; 184 if (detail==0) 185 { 186 s << " Span is " << (initialTime == CommonTime::END_OF_TIME 187 ? "End_time" : printTime(initialTime,fmt)) 188 << " to " << (finalTime == CommonTime::BEGINNING_OF_TIME 189 ? "Begin_time" : printTime(finalTime,fmt)) 190 << " with " << size() << " entries." 191 << std::endl; 192 } // end if-block 193 else if (detail==1) 194 { 195 for (it = ube.begin(); it != ube.end(); it++) 196 { 197 const OrbElemMap& em = it->second; 198 s << " Orbit/clock list for satellite " << it->first 199 << " has " << em.size() << " entries." << std::endl; 200 OrbElemMap::const_iterator ei; 201 202 for (ei = em.begin(); ei != em.end(); ei++) 203 { 204 const OrbElemBase* oe = ei->second; 205 s << "PRN " << setw(2) << it->first 206 << " TOE " << printTime(oe->ctToe,fmt) 207 << " KEY " << printTime(ei->first,fmt); 208 s << " begVal: " << printTime(oe->beginValid,fmt) 209 << " endVal: " << printTime(oe->endValid,fmt); 210 211 s << std::endl; 212 } //end inner for-loop */ 213 214 } // end outer for-loop 215 216 //s << " End of Orbit/Clock data." << std::endl << std::endl; 217 218 } //end else-block 219 } // end OrbElemStore::dump 220 221 //------------------------------------------------------------------------------------ 222 // Keeps only one OrbElemBase for a given SVN and Toe. 223 // It should keep the one with the earliest transmit time. 224 //------------------------------------------------------------------------------------ addOrbElem(const OrbElemBase * eph)225 bool OrbElemStore::addOrbElem(const OrbElemBase* eph) 226 { 227 bool dbg = false; 228 229 try 230 { 231 SatID sid = eph->satID; 232 233 // If this is the first set of elements and the valid sat system has not 234 // been set, then assume the intent is to store this system in the store. 235 if (sysList.size()==0 && size()==0) 236 { 237 addSatSys(sid.system); 238 } 239 240 // Find reference to map for this SV 241 OrbElemMap& oem = ube[sid]; 242 string ts = "%02m/%02d/%02y %02H:%02M:%02S"; 243 244 if (dbg) 245 { 246 cout << "Entering OrbElemStore::addOrbElem." << endl; 247 cout << " Toe = " << printTime(eph->ctToe,ts) << endl; 248 } 249 250 // If satellite system is wrong type for this store, throw an error 251 if (!isSatSysPresent(sid.system)) 252 { 253 stringstream ess; 254 ess << "Attempted to add orbit elements for satellite"; 255 ess << sid; 256 ess << " and that satellite system is not contained in this store."; 257 InvalidParameter ip(ess.str()); 258 GPSTK_THROW(ip); 259 } 260 261 // if map is empty, load object and return 262 if (dbg) cout << "oes::addOE. Checking oem.size()" << endl; 263 if (oem.size()==0) 264 { 265 oem[eph->beginValid] = eph->clone(); 266 updateInitialFinal(eph); 267 return (true); 268 } 269 // Search for beginValid in current keys. 270 // If found candidate, should be same data 271 // as already in table. Test this by using the 272 // isSameData() method. 273 if (dbg) cout << "oes::addOE. Searching for beginValid()" << endl; 274 OrbElemMap::iterator it = oem.find(eph->beginValid); 275 if(it!=oem.end()) 276 { 277 const OrbElemBase* oe = it->second; 278 // Found duplicate already in table 279 //if(oe->ctToe==eph->ctToe) 280 if(oe->isSameData(eph)) 281 { 282 if (dbg) cout << "oes::addOE. isSameData() returned true." << endl; 283 return (false); 284 } 285 // Found matching beginValid but different Toe - This shouldn't happen 286 else 287 { 288 string str = "Unexpectedly found matching beginValid times"; 289 stringstream os; 290 os << eph->satID; 291 str += " but different Toe. SV = " + os.str(); 292 str += ", beginValid(map)= " + printTime(eph->beginValid,ts); 293 str += ", Toe(map)= " + printTime(eph->ctToe,ts); 294 str += ", beginValid(candidate)" + printTime(oe->beginValid,ts); 295 str += ", Toe(candidate)= "+ printTime(oe->ctToe,ts); 296 str += ". "; 297 if (dbg) 298 cout << str << endl; 299 InvalidParameter exc( str ); 300 GPSTK_THROW(exc); 301 } 302 } 303 // Did not already find match to 304 // beginValid in map 305 // N.B:: lower_bound will return element beyond key since there is no match 306 if (dbg) cout << "oes::addOE. Did not find beginValid in map." << endl; 307 it = oem.lower_bound(eph->beginValid); 308 // Case where candidate is before beginning of map 309 if(it==oem.begin()) 310 { 311 if (dbg) cout << "oes::addOE. Case of beginning of map" << endl; 312 const OrbElemBase* oe = it->second; 313 //if(oe->ctToe==eph->ctToe) 314 if(oe->isSameData(eph)) 315 { 316 if (dbg) cout << "oes::addOE. isSameData() returned true." << endl; 317 oem.erase(it); 318 oem[eph->beginValid] = eph->clone(); 319 updateInitialFinal(eph); 320 return (true); 321 } 322 if (dbg) cout << "oes::addOE. Added to beginning of map." << endl; 323 oem[eph->beginValid] = eph->clone(); 324 updateInitialFinal(eph); 325 return (true); 326 } 327 // Case where candidate is after end of current map 328 if(it==oem.end()) 329 { 330 if (dbg) cout << "oes::addOE. Candidate after end of map." << endl; 331 // Get last item in map and check Toe 332 OrbElemMap::reverse_iterator rit = oem.rbegin(); 333 const OrbElemBase* oe = rit->second; 334 //if(oe->ctToe!=eph->ctToe) 335 if(!oe->isSameData(eph)) 336 { 337 if (dbg) cout << "oes::addOE. isSameData() returned false. Adding to end." << endl; 338 oem[eph->beginValid] = eph->clone(); 339 updateInitialFinal(eph); 340 return (true); 341 } 342 if (dbg) cout << "oes::addOE. isSameData() returned true. No need to add." << endl; 343 return (false); 344 } 345 // case where candidate is "In the middle" 346 // Check if iterator points to late transmission of 347 // same OrbElem as candidate 348 const OrbElemBase* oe = it->second; 349 //if(oe->ctToe==eph->ctToe) 350 if(oe->isSameData(eph)) 351 { 352 oem.erase(it); 353 oem[eph->beginValid] = eph->clone(); 354 updateInitialFinal(eph); 355 return (true); 356 } 357 // Two cases: 358 // (a.) Candidate is late transmit copy of 359 // previous OrbElemBase in table - discard (do nothing) 360 // (b.) Candidate OrbElemBase is not in table 361 362 // Already checked for it==oem.beginValid() earlier 363 it--; 364 const OrbElemBase* oe2 = it->second; 365 //if(oe2->ctToe!=eph->ctToe) 366 if(!oe2->isSameData(eph)) 367 { 368 oem[eph->beginValid] = eph->clone(); 369 updateInitialFinal(eph); 370 return (true); 371 } 372 return (false); 373 374 } 375 catch(Exception& e) 376 { 377 GPSTK_RETHROW(e) 378 } 379 } 380 381 //----------------------------------------------------------------------------- 382 edit(const CommonTime & tmin,const CommonTime & tmax)383 void OrbElemStore::edit(const CommonTime& tmin, const CommonTime& tmax) 384 throw() 385 { 386 for(UBEMap::iterator i = ube.begin(); i != ube.end(); i++) 387 { 388 OrbElemMap& eMap = i->second; 389 390 OrbElemMap::iterator lower = eMap.lower_bound(tmin); 391 if (lower != eMap.begin()) 392 { 393 for (OrbElemMap::iterator emi = eMap.begin(); emi != lower; emi++) 394 delete emi->second; 395 eMap.erase(eMap.begin(), lower); 396 } 397 398 OrbElemMap::iterator upper = eMap.upper_bound(tmax); 399 if (upper != eMap.end()) 400 { 401 for (OrbElemMap::iterator emi = upper; emi != eMap.end(); emi++) 402 delete emi->second; 403 eMap.erase(upper, eMap.end()); 404 } 405 } 406 407 initialTime = tmin; 408 finalTime = tmax; 409 } 410 411 //----------------------------------------------------------------------------- 412 size() const413 unsigned OrbElemStore::size() const 414 throw() 415 { 416 unsigned counter = 0; 417 for(UBEMap::const_iterator i = ube.begin(); i != ube.end(); i++) 418 counter += i->second.size(); 419 return counter; 420 } 421 422 423 //----------------------------------------------------------------------------- isPresent(const SatID & id) const424 bool OrbElemStore::isPresent(const SatID& id) const 425 throw() 426 { 427 UBEMap::const_iterator ci = ube.find(id); 428 if (ci==ube.end()) return false; 429 return true; 430 } 431 432 433 //----------------------------------------------------------------------------- 434 435 //----------------------------------------------------------------------------- 436 // Goal is to find the set of orbital elements that would have been 437 // used by a receiver in real-time. That is to say, the most recently 438 // broadcast elements (assuming the receiver has visibility to the SV 439 // in question). 440 //----------------------------------------------------------------------------- 441 442 const OrbElemBase* findOrbElem(const SatID & sat,const CommonTime & t) const443 OrbElemStore::findOrbElem(const SatID& sat, const CommonTime& t) const 444 { 445 // Check to see that there exists a map of orbital elements 446 // relevant to this SV. 447 UBEMap::const_iterator prn_i = ube.find(sat); 448 if (prn_i == ube.end()) 449 { 450 InvalidRequest e("No orbital elements for satellite " + asString(sat)); 451 GPSTK_THROW(e); 452 } 453 454 // Define reference to the relevant map of orbital elements 455 const OrbElemMap& em = prn_i->second; 456 if (em.empty()) 457 { 458 InvalidRequest e("No orbital elements for satellite " + asString(sat)); 459 GPSTK_THROW(e); 460 } 461 462 // The map is ordered by beginning times of validity, which 463 // is another way of saying "earliest transmit time". A call 464 // to em.lower_bound( t ) will return the element of the map 465 // with a key "one beyond the key" assuming the t is NOT a direct 466 // match for any key. 467 468 // First, check for the "direct match" case. If this is 469 // found, return the result. 470 OrbElemMap::const_iterator it = em.find(t); 471 if (it!=em.end()) 472 return it->second; 473 474 it = em.lower_bound(t); 475 476 // Tricky case here. If the key is beyond the last key in the table, 477 // lower_bound( ) will return em.end( ). However, this doesn't entirely 478 // settle the matter. It is theoretically possible that the final 479 // item in the table may have an effectivity that "stretches" far enough 480 // to cover time t. Therefore, if it==em.end( ) we need to check 481 // the period of validity of the final element in the table against 482 // time t. 483 // 484 if (it==em.end()) 485 { 486 OrbElemMap::const_reverse_iterator rit = em.rbegin(); 487 if (rit->second->isValid(t)) return(rit->second); // Last element in map works 488 489 // We reached the end of the map, checked the end of the map, 490 // and we still have nothing. 491 string mess = "All orbital elements found for satellite " + asString(sat) + " are too early for time " 492 + (static_cast<CivilTime>(t)).printf("%02m/%02d/%04Y %02H:%02M:%02S %P"); 493 InvalidRequest e(mess); 494 GPSTK_THROW(e); 495 } 496 497 // Since lower_bound( ) was called, "it" points to the element 498 // after the time of the key. 499 // That is to say "it" points ONE BEYOND the element we want. 500 // 501 // The exception is if it is pointing to em.begin( ). If that is the case, 502 // then all of the elements in the map are too late. 503 if (it==em.begin()) 504 { 505 string mess = "All orbital elements found for satellite " + asString(sat) + " are too late for time " 506 + (static_cast<CivilTime>(t)).printf("%02m/%02d/%04Y %02H:%02M:%02S %P"); 507 InvalidRequest e(mess); 508 GPSTK_THROW(e); 509 } 510 511 // The iterator should be a valid iterator and set one beyond 512 // the item of interest. However, there may be gaps in the 513 // middle of the map and cases where periods of effectivity do 514 // not overlap. That's OK, the key represents the EARLIEST 515 // time the elements should be used. Therefore, we can 516 // decrement the counter and test to see if the element is 517 // valid. 518 it--; 519 if (!(it->second->isValid(t))) 520 { 521 // If we reach this throw, the cause is a "hole" in the middle of a map. 522 string mess = "No orbital elements found for satellite " + asString(sat) + " at " 523 + (static_cast<CivilTime>(t)).printf("%02m/%02d/%04Y %02H:%02M:%02S %P"); 524 InvalidRequest e(mess); 525 GPSTK_THROW(e); 526 } 527 return(it->second); 528 } 529 530 531 //----------------------------------------------------------------------------- 532 533 const OrbElemBase* findNearOrbElem(const SatID & sat,const CommonTime & t) const534 OrbElemStore::findNearOrbElem(const SatID& sat, const CommonTime& t) const 535 { 536 // Check for any OrbElem for this SV 537 UBEMap::const_iterator prn_i = ube.find(sat); 538 if (prn_i == ube.end()) 539 { 540 InvalidRequest e("No OrbElem for satellite " + asString(sat)); 541 GPSTK_THROW(e); 542 } 543 544 // Define reference to the relevant map of orbital elements 545 const OrbElemMap& em = prn_i->second; 546 if (em.empty()) 547 { 548 InvalidRequest e("No orbital elements for satellite " + asString(sat)); 549 GPSTK_THROW(e); 550 } 551 552 // FIRST, try to find the elements that were 553 // actually being broadcast at the time of 554 // interest. That will ALWAYS be the most 555 // correct response. IF YOU REALLY THINK 556 // OTHERWISE CALL ME AND LET'S TALK ABOUT 557 // IT - Brent Renfro 558 try 559 { 560 const OrbElemBase* oep = findOrbElem(sat, t); 561 return(oep); 562 } 563 // No OrbElemBase in store for requested sat time 564 catch(InvalidRequest) 565 { 566 // Create references to map for this satellite 567 const OrbElemMap& em = prn_i->second; 568 /* 569 Three Cases: 570 1. t is within a gap within the store 571 2. t is before all OrbElemBase in the store 572 3. t is after all OrbElemBase in the store 573 */ 574 575 // Attempt to find next in store after t 576 OrbElemMap::const_iterator itNext = em.lower_bound(t); 577 // Test for case 2 578 if(itNext==em.begin()) 579 { 580 return(itNext->second); 581 } 582 // Test for case 3 583 if(itNext==em.end()) 584 { 585 OrbElemMap::const_reverse_iterator rit = em.rbegin(); 586 return(rit->second); 587 } 588 // Handle case 1 589 // Know that itNext is not the beginning, so safe to decrement 590 CommonTime nextBeginValid = itNext->first; 591 OrbElemMap::const_iterator itPrior = itNext; 592 itPrior--; 593 CommonTime lastEndValid = itPrior->second->endValid; 594 double diffToNext = nextBeginValid-t; 595 double diffFromLast = t - lastEndValid; 596 if(diffToNext>diffFromLast) 597 { 598 return(itPrior->second); 599 } 600 return(itNext->second); 601 } 602 } 603 604 //----------------------------------------------------------------------------- 605 const OrbElemBase* OrbElemStore:: findToe(const SatID & sat,const CommonTime & t) const606 findToe(const SatID& sat, const CommonTime& t) 607 const 608 { 609 // If the TimeSystem of the requested t doesn't match 610 // the TimeSystem stored in this store, throw an error. 611 if (timeSysForStore!=t.getTimeSystem()) 612 { 613 std::stringstream ss; 614 ss << "Mismatched TimeSystems. Time system of store: "; 615 ss << timeSysForStore << ", Time system of argument: "; 616 ss << t.getTimeSystem(); 617 InvalidRequest e(ss.str()); 618 GPSTK_THROW(e); 619 } 620 621 // Check for any OrbElem for this SV 622 UBEMap::const_iterator prn_i = ube.find(sat); 623 if (prn_i == ube.end()) 624 { 625 InvalidRequest e("No OrbElem for satellite " + asString(sat)); 626 GPSTK_THROW(e); 627 } 628 629 // Create a reference to map for this satellite 630 const OrbElemMap& em = prn_i->second; 631 632 // We are looking for an exact match for a Toe. 633 // The map is keyed with the beginValid time, so the 634 // only way to determine if there is a match is to iterate 635 // over the map and check. 636 OrbElemMap::const_iterator cit; 637 for (cit=em.begin();cit!=em.end();cit++) 638 { 639 const OrbElemBase* candidate = cit->second; 640 if (candidate->ctToe==t) return(candidate); 641 } 642 643 // If we reached this point, we didn't find a match. 644 std::stringstream ss; 645 ss << "No match found for SV " << sat; 646 ss << " with Toe " << printTime(t,"%02m/%02d/%04Y %02H:%02M:%02S"); 647 InvalidRequest e(ss.str()); 648 GPSTK_THROW(e); 649 650 // Keep the compiler happy. 651 OrbElemBase* dummy = 0; 652 return(dummy); 653 } 654 655 656 //----------------------------------------------------------------------------- 657 addToList(std::list<OrbElemBase * > & v) const658 int OrbElemStore::addToList(std::list<OrbElemBase*>& v) const 659 throw() 660 { 661 int n = 0; 662 UBEMap::const_iterator prn_i; 663 for (prn_i = ube.begin(); prn_i != ube.end(); prn_i++) 664 { 665 const OrbElemMap& em = prn_i->second; 666 OrbElemMap::const_iterator ei; 667 for (ei = em.begin(); ei != em.end(); ei++) 668 { 669 v.push_back(ei->second->clone()); 670 n++; 671 } 672 } 673 return n; 674 } 675 676 //----------------------------------------------------------------------------- 677 678 /// Remove all data from this collection. clear()679 void OrbElemStore::clear() 680 throw() 681 { 682 for( UBEMap::iterator ui = ube.begin(); ui != ube.end(); ui++) 683 { 684 OrbElemMap& oem = ui->second; 685 for (OrbElemMap::iterator oi = oem.begin(); oi != oem.end(); oi++) 686 { 687 delete oi->second; 688 } 689 } 690 ube.clear(); 691 initialTime = gpstk::CommonTime::END_OF_TIME; 692 finalTime = gpstk::CommonTime::BEGINNING_OF_TIME; 693 initialTime.setTimeSystem(timeSysForStore); 694 finalTime.setTimeSystem(timeSysForStore); 695 } 696 697 //----------------------------------------------------------------------------- 698 699 const OrbElemStore::OrbElemMap& getOrbElemMap(const SatID & sat) const700 OrbElemStore::getOrbElemMap( const SatID& sat ) const 701 { 702 validSatSystem(sat); 703 704 UBEMap::const_iterator prn_i = ube.find(sat); 705 if (prn_i == ube.end()) 706 { 707 InvalidRequest e("No OrbElemBase for satellite " + asString(sat)); 708 GPSTK_THROW(e); 709 } 710 return(prn_i->second); 711 } 712 713 //----------------------------------------------------------------------------- 714 getSatIDList() const715 list<gpstk::SatID> OrbElemStore::getSatIDList() const 716 { 717 list<gpstk::SatID> retList; 718 for( UBEMap::const_iterator ui = ube.begin(); ui != ube.end(); ui++) 719 { 720 SatID sid = ui->first; 721 retList.push_back(sid); 722 } 723 return retList; 724 } 725 726 //----------------------------------------------------------------------------- getIndexSet() const727 set<SatID> OrbElemStore::getIndexSet() const 728 { 729 set<SatID> retSet; 730 for( UBEMap::const_iterator ui = ube.begin(); ui != ube.end(); ui++) 731 { 732 SatID sid = ui->first; 733 retSet.insert(sid); 734 } 735 return retSet; 736 } 737 738 //----------------------------------------------------------------------------- 739 isSatSysPresent(const SatelliteSystem ss) const740 bool OrbElemStore::isSatSysPresent(const SatelliteSystem ss) const 741 { 742 list<SatelliteSystem>::const_iterator cit; 743 for (cit=sysList.begin();cit!=sysList.end();cit++) 744 { 745 SatelliteSystem ssTest = *cit; 746 if (ssTest==ss) return true; 747 } 748 return false; 749 } 750 addSatSys(const SatelliteSystem ss)751 void OrbElemStore::addSatSys(const SatelliteSystem ss) 752 { 753 sysList.push_back(ss); 754 } 755 getValidSystemList() const756 std::list<SatelliteSystem> OrbElemStore::getValidSystemList() const 757 { 758 list<SatelliteSystem> retList; 759 list<SatelliteSystem>::const_iterator cit; 760 for (cit=sysList.begin();cit!=sysList.end();cit++) 761 { 762 SatelliteSystem ssTest = *cit; 763 retList.push_back(ssTest); 764 } 765 return retList; 766 } 767 dumpValidSystemList(std::ostream & out) const768 void OrbElemStore::dumpValidSystemList(std::ostream& out) const 769 { 770 out << "List of systems in OrbElemStore: "; 771 bool first = true; 772 list<SatelliteSystem>::const_iterator cit; 773 for (cit=sysList.begin();cit!=sysList.end();cit++) 774 { 775 if (!first) 776 out << ", "; 777 SatelliteSystem ssTest = *cit; 778 out << static_cast<int>(ssTest); 779 first = false; 780 } 781 out << endl; 782 } 783 784 785 //------------------------------------------------------------------------------------ 786 // See notes in the .hpp. This function is designed to be called 787 // AFTER all elements are loaded. It can then make adjustments to 788 // time relationships based on inter-comparisons between sets of 789 // elements that cannot be performed until the ordering has been 790 // determined. 791 //----------------------------------------------------------------------------- rationalize()792 void OrbElemStore::rationalize( ) 793 { 794 // check to verify that the system type is SatelliteSystem::GPS 795 bool sat_sys_gps = OrbElemStore::isSatSysPresent(SatelliteSystem::GPS); 796 797 if (sat_sys_gps) 798 { 799 cout << "GPS system used, will use OrbElemStore::rationalize" << endl; 800 } 801 802 if (!sat_sys_gps) 803 { 804 cout << "GPS system not used, exiting OrbElemStore::rationalize" << endl; 805 InvalidRequest e("GPS system not used, exiting OrbElemStore::rationalize"); 806 GPSTK_THROW(e); 807 } 808 809 UBEMap::iterator it; 810 for (it = ube.begin(); it != ube.end(); it++) 811 { 812 OrbElemMap& em = it->second; 813 OrbElemMap::iterator ei; 814 OrbElemMap::iterator eiPrev; 815 bool begin = true; 816 double previousOffset = 0.0; 817 long previousToe = 0.0; 818 bool previousIsOffset = false; 819 bool currentIsOffset = false; 820 bool previousBeginAdjusted = false; 821 bool adjustedBegin = false; 822 CommonTime prevOrigBeginValid; 823 824 string tForm = "%03j.%02H:%02M:%02S"; 825 //SatID sid = it->first; 826 //cout << " Scannning PRN ID: " << sid.id << endl; 827 828 // Scan the map for this SV looking for 829 // uploads. Uploads are identifed by 830 // Toe values that are offset from 831 // an even hour. 832 OrbElemBase* oePrev = 0; 833 for (ei = em.begin(); ei != em.end(); ei++) 834 { 835 currentIsOffset = false; // start with this assumption 836 // Since this is OrbElemStore, then the type in the 837 // store must AT LEAST be OrbElem. 838 OrbElemBase* oeb = ei->second; 839 OrbElem* oe = dynamic_cast<OrbElem*>(oeb); 840 long Toe = (long) (static_cast<GPSWeekSecond> (oe->ctToe)).sow; 841 double currentOffset = Toe % 3600; 842 843 CommonTime currOrigBeginValid = oe->beginValid; 844 845 //cout << "Top of For Loop. oe->beginValid = " << printTime(oe->beginValid,tForm); 846 //cout << ", currentOffset =" << currentOffset << endl; 847 848 if ( (currentOffset)!=0) 849 { 850 currentIsOffset = true; 851 852 //cout << "*** Found an offset" << endl; 853 //cout << " currentIsOffset: " << currentIsOffset; 854 //cout << " previousIsOffset: " << previousIsOffset; 855 //cout << " current, previous Offset = " << currentOffset << ", " << previousOffset << endl; 856 857 // If this set is offset AND the previous set is offset AND 858 // the two offsets are the same AND the difference 859 // in time between the two Toe == two hours, 860 // then this is the SECOND 861 // set of elements in an upload. In that case the OrbElem 862 // load routines have conservatively set the beginning time 863 // of validity to the transmit time because there was no 864 // way to prove this was the second data set. Since we can 865 // now prove its second by observing the ordering, we can 866 // adjust the beginning of validity as needed. 867 // Since the algorithm is dependent on the message 868 // format, this must be done in OrbElem. 869 // 870 // IMPORTANT NOTE: We also need to adjust the 871 // key in the map, which is based on the beginning 872 // of validity. However, we can't do it in this 873 // loop without destroying the integrity of the 874 // iterator. This is handled later in a second 875 // loop. See notes on the second loop, below. 876 long diffToe = Toe - previousToe; 877 if (previousIsOffset && 878 currentIsOffset && 879 currentOffset==previousOffset && 880 diffToe==7200) 881 { 882 //cout << "*** Adjusting beginValid. Initial value: " 883 // << printTime(oe->beginValid,tForm); 884 oe->adjustBeginningValidity(); 885 adjustedBegin = true; 886 //cout << ", Adjusted value: " 887 // << printTime(oe->beginValid,tForm) << endl; 888 } 889 890 // If the previous set is not offset, then 891 // we've found an upload. 892 // For that matter, if previous IS offset, but 893 // the current offset is different than the 894 // previous, then it is an upload. 895 if (!previousIsOffset || 896 (previousIsOffset && (currentOffset!=previousOffset) ) ) 897 { 898 // Record the offset for later reference 899 previousOffset = currentOffset; 900 901 // Adjust the ending time of validity of any elements 902 // broadcast BEFORE the new upload such that they 903 // end at the beginning validity of the upload. 904 // That beginning validity value should already be 905 // set to the transmit time (or earliest transmit 906 // time found) by OrbElem and OrbElemStore.addOrbElem( ) 907 // This adjustment is consistent with the IS-GPS-XXX 908 // rules that a new upload invalidates previous elements. 909 // Note that this may be necessary for more than one 910 // preceding set of elements. 911 if (!begin) 912 { 913 //cout << "*** Adjusting endValid Times" << endl; 914 OrbElemMap::iterator ri; 915 ri = em.find(oePrev->beginValid); // We KNOW it exists in the map 916 // Actuall, there is a really, odd rare case where it 917 // DOES NOT exist in the map. That case is the case 918 // in which we modified the begin valid time of oePrev 919 // in the previous iteration of the loop. 920 if (ri==em.end() && previousBeginAdjusted) 921 { 922 //cout << "*** Didn't find oePrev->beginValid(), but prev beginValid changed."; 923 //cout << " Testing the prevOrigBeginValid value." << endl; 924 ri = em.find(prevOrigBeginValid); 925 if (ri==em.end()) 926 { 927 //cout << "Still didn't find a valid key. Abort rationalization for this SV." << endl; 928 continue; // Abort rationalization for this SV with no further changes. 929 } 930 //cout << " Successfully found the previous object." << endl; 931 } 932 933 934 bool done = false; 935 while (!done) 936 { 937 OrbElemBase* oeRev = ri->second; 938 //cout << "Testing Toe of " << printTime(oeRev->ctToe,"%02H:%02M:%02S"); 939 //cout << " with endValid of " << printTime(oeRev->endValid,"%02H:%02M:%02S") << endl; 940 941 // If the current set of elements has an ending 942 // validity prior to the upload, then do NOT 943 // adjust the ending and set done to true. 944 if (oeRev->endValid <= oe->beginValid) done = true; 945 946 // Otherwise, adjust the ending validity to 947 // match the begin of the upload. 948 else oeRev->endValid = oe->beginValid; 949 950 // If we've reached the beginning of the map, stop. 951 // Otherwise, decrement and test again. 952 if (ri!=em.begin()) ri--; 953 else done = true; 954 } 955 } 956 } 957 } 958 959 // Update condition flags for next loop 960 previousIsOffset = currentIsOffset; 961 previousToe = Toe; 962 963 // If beginValid was adjusted for THIS oe, set 964 // the flag previousBeginAdjusted so we have that 965 // information to inform the next iteration. However, 966 // do not let the flag persist beyond one iteration 967 // unless adjustedBegin is set again. 968 previousBeginAdjusted = false; 969 if (adjustedBegin) previousBeginAdjusted = true; 970 adjustedBegin = false; 971 prevOrigBeginValid = currOrigBeginValid; 972 973 oePrev = oe; // May need this for next loop. 974 begin = false; 975 //cout << "Bottom of For loop. currentIsOffset: " << currentIsOffset << 976 // ", previousIsOffset: " << previousIsOffset << endl; 977 } //end inner for-loop 978 979 // The preceding process has left some elements in a condition 980 // when the beginValid value != the key in the map. This 981 // must be addressed, but the map key is a const (by definition). 982 // We have to search the map for these disagreements. When found, 983 // the offending item need to be copied out of the map, the 984 // existing entry deleted, the item re-entered with the new key. 985 // Since it is unsafe to modify a map while traversing the map, 986 // each time this happens, we have to reset the loop process. 987 // 988 // NOTE: Simplisitically, we could restart the loop at the 989 // beginning each time. HOWEVER, we sometimes load a long 990 // period in a map (e.g. a year). At one upload/day, that 991 // would mean ~365 times, each time one day deeper into the map. 992 // As an alternate, we use the variable loopStart to note how 993 // far we scanned prior to finding a problem and manipulating 994 // the map. Then we can restart at that point. 995 bool done = false; 996 CommonTime loopStart = CommonTime::BEGINNING_OF_TIME; 997 while (!done) 998 { 999 ei = em.lower_bound(loopStart); 1000 while (ei!=em.end()) 1001 { 1002 OrbElemBase* oe = ei->second; 1003 if (ei->first!=oe->beginValid) 1004 { 1005 //cout << "Removing an element....."; 1006 OrbElemBase* oeAdj= oe->clone(); // Adjustment was done in 1007 // first loop above. 1008 delete ei->second; // oe becomes invalid. 1009 em.erase(ei); // Remove the map entry. 1010 em[oeAdj->beginValid] = oeAdj->clone(); // Add back to map 1011 //cout << "...restored the element." << endl; 1012 break; // exit this while loop without finishing 1013 } 1014 loopStart = ei->first; // Scanned this far successfully, so 1015 // save this as a potential restart point. 1016 ei++; 1017 if (ei==em.end()) done = true; // Successfully completed 1018 // the loop w/o finding any 1019 // mismatches. We're done. 1020 } 1021 } 1022 1023 // Well, not quite done. We need to update the initial/final 1024 // times of the map. 1025 const OrbElemMap::iterator Cei = em.begin( ); 1026 initialTime = Cei->second->beginValid; 1027 const OrbElemMap::reverse_iterator rCei = em.rbegin(); 1028 finalTime = rCei->second->endValid; 1029 1030 } // end outer for-loop 1031 //cout << "Exiting OrbElem.rationalize()" << endl; 1032 } 1033 1034 } // namespace 1035