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 #include <fstream>
40 #include "SatMetaDataStore.hpp"
41 #include "StringUtils.hpp"
42 #include "YDSTime.hpp"
43 
44 using namespace std;
45 
46 namespace gpstk
47 {
48    SatMetaDataStore::SVNID ::
SVNID()49    SVNID()
50          : system(SatelliteSystem::Unknown)
51    {
52    }
53 
54 
55    SatMetaDataStore::SVNID ::
SVNID(SatelliteSystem sys,const std::string & svn)56    SVNID(SatelliteSystem sys, const std::string& svn)
57          : system(sys), id(svn)
58    {
59    }
60 
61 
62    bool SatMetaDataStore::SVNID ::
operator <(const SVNID & right) const63    operator<(const SVNID& right) const
64    {
65       if (static_cast<int>(system) < static_cast<int>(right.system))
66       {
67          return true;
68       }
69       if (static_cast<int>(system) > static_cast<int>(right.system))
70       {
71          return false;
72       }
73       return id < right.id;
74    }
75 
76 
77    bool SatMetaDataStore ::
loadData(const std::string & sourceName)78    loadData(const std::string& sourceName)
79    {
80       bool rv = true;
81       std::ifstream ins(sourceName);
82       unsigned long lineNo = 0;
83       if (!ins)
84       {
85             // std::cerr << "Couldn't open " << sourceName << std::endl;
86          return false;
87       }
88       while (ins)
89       {
90          std::string txt;
91          std::getline(ins, txt);
92          lineNo++;
93             // skip comments and blank lines
94          if ((txt[0] == '#') || txt.empty())
95             continue;
96          std::vector<std::string> vals = StringUtils::split(txt, ',');
97          try
98          {
99             for (unsigned i = 0; i < vals.size(); i++)
100             {
101                gpstk::StringUtils::strip(vals[i]);
102             }
103             if (vals.size() == 0)
104             {
105                   // this could still happen if there are lines with no commas
106                continue;
107             }
108             string key = gpstk::StringUtils::upperCase(vals[0]);
109             if (key == "SAT")
110             {
111                if (!addSat(vals, lineNo))
112                {
113                   rv = false;
114                   continue;
115                }
116             }
117             else if (key == "SIG")
118             {
119                if (!addSignal(vals, lineNo))
120                {
121                   rv = false;
122                   continue;
123                }
124             }
125             else if (key == "CLOCK")
126             {
127                if (!addClock(vals, lineNo))
128                {
129                   rv = false;
130                   continue;
131                }
132             }
133             else if (key == "LAUNCH")
134             {
135                if (!addLaunch(vals, lineNo))
136                {
137                   rv = false;
138                   continue;
139                }
140             }
141             else if (key == "NORAD")
142             {
143                if (!addNORAD(vals, lineNo))
144                {
145                   rv = false;
146                   continue;
147                }
148             }
149             else
150             {
151                cerr << "Invalid record type: " << vals[0] << " on line "
152                     << lineNo << endl;
153                rv = false;
154                continue;
155             }
156          }
157          catch (gpstk::Exception& exc)
158          {
159             cerr << "Exception while processing line " << lineNo << ":" << endl
160                  << exc << endl;
161             rv = false;
162          }
163          catch (std::exception& exc)
164          {
165             cerr << "Exception while processing line " << lineNo << ": "
166                  << exc.what() << endl;
167             rv = false;
168          }
169          catch (...)
170          {
171             cerr << "Unknown exception processing line " << lineNo << endl;
172             rv = false;
173          }
174       }
175       return rv;
176    }
177 
178 
179    bool SatMetaDataStore ::
addSat(const std::vector<std::string> & vals,unsigned long lineNo)180    addSat(const std::vector<std::string>& vals, unsigned long lineNo)
181    {
182          // simple way to index the columns without having to change
183          // all the numbers with every little change.
184       unsigned i = 1;
185       if (vals.size() != 17)
186       {
187          cerr << "Invalid SAT record on line " << lineNo << " size!=17" << endl;
188          return false;
189       }
190       SatMetaData sat;
191       sat.sys = convertStringToSatelliteSystem(vals[i++]);
192       sat.svn = vals[i++];
193       if (StringUtils::isDigitString(vals[i]))
194       {
195          sat.prn = StringUtils::asUnsigned(vals[i]);
196       }
197       else
198       {
199          cerr << "Invalid PRN on line " << lineNo << endl;
200          return false;
201       }
202       i++;
203       if (StringUtils::isDigitString(vals[i]))
204       {
205          sat.chl = StringUtils::asInt(vals[i]);
206       }
207       else
208       {
209          cerr << "Invalid FDMA channel on line " << lineNo << endl;
210          return false;
211       }
212       i++;
213       if (StringUtils::isDigitString(vals[i]))
214       {
215          sat.slotID = StringUtils::asUnsigned(vals[i]);
216       }
217       else
218       {
219          cerr << "Invalid FDMA slot on line " << lineNo << endl;
220          return false;
221       }
222       i++;
223       unsigned long y,doy;
224       double sod;
225          // Set all time systems to any for now, the dozen or so
226          // seconds offset between time systems really isn't
227          // likely to amount to anything in this context.
228       y = StringUtils::asUnsigned(vals[i++]);
229       doy = StringUtils::asUnsigned(vals[i++]);
230       sod = StringUtils::asDouble(vals[i++]);
231       try
232       {
233          sat.startTime = YDSTime(y,doy,sod,gpstk::TimeSystem::Any);
234       }
235       catch (gpstk::Exception& exc)
236       {
237          exc.addText("Processing startTime");
238          GPSTK_RETHROW(exc);
239       }
240       y = StringUtils::asUnsigned(vals[i++]);
241       doy = StringUtils::asUnsigned(vals[i++]);
242       sod = StringUtils::asDouble(vals[i++]);
243       try
244       {
245          sat.endTime = YDSTime(y,doy,sod,gpstk::TimeSystem::Any);
246       }
247       catch (gpstk::Exception& exc)
248       {
249          exc.addText("Processing endTime");
250          GPSTK_RETHROW(exc);
251       }
252       sat.plane = vals[i++];
253       sat.slot = vals[i++];
254       sat.signals = vals[i++];
255       sat.status = SatMetaData::asStatus(vals[i++]);
256       sat.activeClock = StringUtils::asUnsigned(vals[i]);
257          // cross-reference check and fill
258       SVNID svn(sat.sys, sat.svn);
259       if (noradMap.find(svn) == noradMap.end())
260       {
261          cerr << "Missing NORAD mapping for SVN " << svn << " on line "
262               << lineNo << endl;
263          return false;
264       }
265       sat.norad = noradMap[svn];
266       if (launchMap.find(svn) == launchMap.end())
267       {
268          cerr << "Missing LAUNCH record for SVN " << svn << " on line "
269               << lineNo << endl;
270          return false;
271       }
272       sat.launchTime = launchMap[svn].launchTime;
273       sat.type = launchMap[svn].type;
274       sat.mission = launchMap[svn].mission;
275       SystemBlock sysBlock;
276       sysBlock.sys = sat.sys;
277       sysBlock.blk = launchMap[svn].type;
278       if (clkMap.find(sysBlock) == clkMap.end())
279       {
280          cerr << "Missing CLOCK record for " << sysBlock << " on line "
281               << lineNo << endl;
282          return false;
283       }
284          // note: no checks for clock vector size!
285       const ClockVec& cv(clkMap[sysBlock]);
286       for (unsigned cn = 0; cn < SatMetaData::NUMCLOCKS; cn++)
287       {
288          sat.clocks[cn] = cv[cn];
289       }
290          // add the complete record
291       satMap[sat.sys].insert(sat);
292       return true;
293    }
294 
295 
296    bool SatMetaDataStore ::
addSignal(const std::vector<std::string> & vals,unsigned long lineNo)297    addSignal(const std::vector<std::string>& vals, unsigned long lineNo)
298    {
299          // simple way to index the columns without having to change
300          // all the numbers with every little change.
301       unsigned i = 1;
302       if (vals.size() != 5)
303       {
304          cerr << "Invalid SIG record on line " << lineNo << " size!=5" << endl;
305          return false;
306       }
307       Signal sig;
308       std::string name = vals[i++];
309       std::string carrier = vals[i++];
310       std::string code = vals[i++];
311       std::string nav = vals[i++];
312          /** @todo implement the rest of this when we have some
313           * from/to string translation methods for the enumerations
314           * used in Signal. */
315       return true;
316    }
317 
318 
319    bool SatMetaDataStore ::
addClock(const std::vector<std::string> & vals,unsigned long lineNo)320    addClock(const std::vector<std::string>& vals, unsigned long lineNo)
321    {
322          // simple way to index the columns without having to change
323          // all the numbers with every little change.
324       unsigned i = 1;
325       if (vals.size() != 7)
326       {
327          cerr << "Invalid CLOCK record on line " << lineNo << " size!=7"
328               << endl;
329          return false;
330       }
331       SystemBlock key;
332       key.sys = convertStringToSatelliteSystem(vals[i++]);
333       key.blk = vals[i++];
334       if (clkMap.find(key) != clkMap.end())
335       {
336             // enforce no duplicates
337          cerr << "Duplicate CLOCK " << StringUtils::asString(key.sys) << " "
338               << key.blk << " on line " << lineNo << endl;
339          return false;
340       }
341          // currently support up to four clocks.
342       clkMap[key].resize(SatMetaData::NUMCLOCKS);
343       for (unsigned j = 0; j < SatMetaData::NUMCLOCKS; j++)
344       {
345          clkMap[key][j] = SatMetaData::asClockType(vals[i++]);
346       }
347       return true;
348    }
349 
350 
351    bool SatMetaDataStore ::
addLaunch(const std::vector<std::string> & vals,unsigned long lineNo)352    addLaunch(const std::vector<std::string>& vals, unsigned long lineNo)
353    {
354          // simple way to index the columns without having to change
355          // all the numbers with every little change.
356       unsigned i = 1;
357       if (vals.size() != 8)
358       {
359          cerr << "Invalid LAUNCH record on line " << lineNo << " size!=8"
360               << endl;
361          return false;
362       }
363       SVNID svn;
364       svn.system = convertStringToSatelliteSystem(vals[i++]);
365       svn.id = vals[i++];
366       if (launchMap.find(svn) != launchMap.end())
367       {
368             // enforce no duplicates
369          cerr << "Duplicate LAUNCH " << svn << " on line " << lineNo << endl;
370          return false;
371       }
372       launchMap[svn].svn = svn;
373       unsigned y = StringUtils::asUnsigned(vals[i++]);
374       unsigned doy = StringUtils::asUnsigned(vals[i++]);
375       double sod = StringUtils::asDouble(vals[i++]);
376       try
377       {
378          launchMap[svn].launchTime = YDSTime(y,doy,sod,gpstk::TimeSystem::Any);
379       }
380       catch (gpstk::Exception& exc)
381       {
382          exc.addText("Processing launchTime");
383          GPSTK_RETHROW(exc);
384       }
385       launchMap[svn].type = vals[i++];
386       launchMap[svn].mission = vals[i++];
387       return true;
388    }
389 
390 
391    bool SatMetaDataStore ::
addNORAD(const std::vector<std::string> & vals,unsigned long lineNo)392    addNORAD(const std::vector<std::string>& vals, unsigned long lineNo)
393    {
394          // simple way to index the columns without having to change
395          // all the numbers with every little change.
396       unsigned i = 1;
397       if (vals.size() != 4)
398       {
399          cerr << "Invalid NORAD record on line " << lineNo << " size!=4"
400               << endl;
401          return false;
402       }
403       SVNID svn;
404       svn.system = convertStringToSatelliteSystem(vals[i++]);
405       svn.id = vals[i++];
406       if (noradMap.find(svn) != noradMap.end())
407       {
408             // enforce no duplicates
409          cerr << "Duplicate NORAD " << svn << " on line " << lineNo << endl;
410          return false;
411       }
412       unsigned long noradID = StringUtils::asUnsigned(vals[i++]);
413       noradMap[svn] = noradID;
414       return true;
415    }
416 
417 
418    bool SatMetaDataStore ::
findSat(SatelliteSystem sys,uint32_t prn,const gpstk::CommonTime & when,SatMetaData & sat) const419    findSat(SatelliteSystem sys, uint32_t prn,
420            const gpstk::CommonTime& when,
421            SatMetaData& sat)
422       const
423    {
424       SatMetaMap::const_iterator sysIt = satMap.find(sys);
425       if (sysIt == satMap.end())
426       {
427          // std::cerr << "no system" << std::endl;
428          return false;
429       }
430          // Unfortunately we have to do a linear search because
431          // different systems have different methods of
432          // identification.
433       for (SatSet::const_iterator rv = sysIt->second.begin();
434            rv != sysIt->second.end();
435            rv++)
436       {
437          if (rv->prn < prn)
438          {
439             // cerr << "< prn" << endl;
440             continue;
441          }
442          if (rv->prn > prn)
443          {
444             // cerr << "> prn" << endl;
445             return false;
446          }
447          // cerr << "= prn" << endl;
448             // same prn at this point
449          if (when < rv->startTime)
450          {
451             // cerr << "< startTime" << endl;
452             continue;
453          }
454          if (when < rv->endTime)
455          {
456             // std::cerr << "found it" << std::endl;
457             // cerr << *rv << endl;
458             sat = *rv;
459             return true;
460          }
461       } // for (SatSet::const_iterator rv = sysIt->second.begin();
462       // cerr << "giving up" << endl;
463       return false;
464    } // findSat()
465 
466 
467    bool SatMetaDataStore ::
findSatBySVN(SatelliteSystem sys,const std::string & svn,const gpstk::CommonTime & when,SatMetaData & sat) const468    findSatBySVN(SatelliteSystem sys, const std::string& svn,
469                 const gpstk::CommonTime& when,
470                 SatMetaData& sat)
471       const
472    {
473       SatMetaMap::const_iterator sysIt = satMap.find(sys);
474       if (sysIt == satMap.end())
475       {
476          // std::cerr << "no system" << std::endl;
477          return false;
478       }
479          // This is a bit different than the PRN search because the
480          // map is sorted by PRN and not SVN, so we have to search
481          // until we either hit the end of the map or we find a match,
482          // there's no short-cut failures.
483       for (SatSet::const_iterator rv = sysIt->second.begin();
484            rv != sysIt->second.end();
485            rv++)
486       {
487          if ((rv->svn == svn) &&
488              (when >= rv->startTime) &&
489              (when < rv->endTime))
490          {
491             // std::cerr << "found it" << std::endl;
492             // cerr << *rv << endl;
493             sat = *rv;
494             return true;
495          }
496       } // for (SatSet::const_iterator rv = sysIt->second.begin();
497       // cerr << "giving up" << endl;
498       return false;
499    } // findSat()
500 
501 
502    bool SatMetaDataStore::
findSatBySlotFdma(uint32_t slotID,int32_t channel,const gpstk::CommonTime & when,SatMetaData & sat) const503    findSatBySlotFdma(uint32_t slotID,
504                      int32_t channel,
505                      const gpstk::CommonTime& when,
506                      SatMetaData& sat)
507          const
508    {
509       SatelliteSystem sys = SatelliteSystem::Glonass;
510       SatMetaMap::const_iterator sysIt = satMap.find(sys);
511       if (sysIt == satMap.end())
512       {
513          // std::cerr << "no system" << std::endl;
514          return false;
515       }
516          // This is a bit different than the PRN search because the
517          // map is sorted by PRN and not slotID, so we have to search
518          // until we either hit the end of the map or we find a match,
519          // there's no short-cut failures.
520       for (SatSet::const_iterator rv = sysIt->second.begin();
521            rv != sysIt->second.end();
522            rv++)
523       {
524          if ((rv->slotID == slotID)  &&
525              (rv->chl    == channel) &&
526              (when >= rv->startTime) &&
527              (when < rv->endTime))
528          {
529             // std::cerr << "found it" << std::endl;
530             // cerr << *rv << endl;
531             sat = *rv;
532             return true;
533          }
534       } // for (SatSet::const_iterator rv = sysIt->second.begin();
535       // cerr << "giving up" << endl;
536       return false;
537    } // findSatByFdmaSlot()
538 
539 
540    bool SatMetaDataStore ::
getSVN(SatelliteSystem sys,uint32_t prn,const gpstk::CommonTime & when,std::string & svn) const541    getSVN(SatelliteSystem sys, uint32_t prn,
542           const gpstk::CommonTime& when,
543           std::string& svn)
544       const
545    {
546       SatMetaData sat;
547       if (findSat(sys, prn, when, sat))
548       {
549          svn = sat.svn;
550          return true;
551       }
552       return false;
553    }
554 
555 
556    bool SatMetaDataStore ::
getPRN(SatelliteSystem sys,const std::string & svn,const gpstk::CommonTime & when,uint32_t & prn) const557    getPRN(SatelliteSystem sys, const std::string& svn,
558           const gpstk::CommonTime& when,
559           uint32_t& prn)
560       const
561    {
562       SatMetaData sat;
563       if (findSatBySVN(sys, svn, when, sat))
564       {
565          prn = sat.prn;
566          return true;
567       }
568       return false;
569    }
570 }
571