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