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