Program Listing for File tle.cc

Return to documentation for file (states/tle.cc)

#include "lupnt/states/tle.h"

#include <boost/algorithm/string.hpp>
#include <fstream>
#include <memory>
#include <string>

#include "lupnt/agents/agent.h"
#include "lupnt/agents/satellite.h"
#include "lupnt/conversions/time_conversions.h"
#include "lupnt/core/constants.h"
#include "lupnt/core/definitions.h"
#include "lupnt/core/error.h"
#include "lupnt/core/logger.h"
#include "lupnt/core/string_utils.h"
#include "lupnt/states/state.h"

namespace lupnt {

  TLE TLE::FromLines(const std::string& line1, const std::string& line2, const std::string& line3) {
    auto parse_bstar = [](const std::string& s) {
      std::string mant = s.substr(0, 5);
      std::string exp = s.substr(5, 3);
      return std::stod(mant + "E" + exp);
    };
    auto parse_year = [](double yy) { return (yy < 57 ? 2000 : 1900) + static_cast<int>(yy); };

    TLE tle;
    tle.name = boost::trim_right_copy(line1);
    tle.epoch_year = parse_year(stod(line2.substr(18, 2)));
    tle.epoch_day = stod(line2.substr(20, 12));
    tle.bstar = parse_bstar(line2.substr(53, 8));
    tle.inclination = stod(line3.substr(8, 8));
    tle.raan = stod(line3.substr(17, 8));
    tle.eccentricity = stod("0." + line3.substr(26, 7));
    tle.arg_perigee = stod(line3.substr(34, 8));
    tle.mean_anomaly = stod(line3.substr(43, 8));
    tle.mean_motion = stod(line3.substr(52, 11));

    Real epoch_year_start_utc = GregorianToTime(tle.epoch_year, 1, 1, 0, 0, 0);
    Real epoch_year_start_tai = UtcToTai(epoch_year_start_utc);

    double epoch_tai = epoch_year_start_tai.val() + tle.epoch_day * SECS_DAY;
    tle.epoch_tai = epoch_tai;
    tle.prn = GetPrn(tle.name);
    return tle;
  };

  std::vector<TLE> TLE::FromFile(const std::string_view filename) {
    std::filesystem::path path = GetFilePath(filename);
    std::ifstream input_file(path);
    LUPNT_CHECK(input_file.is_open(), "Could not open file " + std::string(filename), "TLE");
    std::vector<TLE> tles;
    std::string line1, line2, line3;
    while (getline(input_file, line1) && getline(input_file, line2) && getline(input_file, line3)) {
      TLE tle = TLE::FromLines(line1, line2, line3);
      if (tle.prn != -1) tles.push_back(tle);
    };
    return tles;
  };

  std::vector<Ptr<Agent>> LoadTleFile(const std::string& filename) {
    std::vector<Ptr<Agent>> satellites;

    for (auto tle : TLE::FromFile(filename)) {
      // Classical orbital elements
      Real T = SECS_DAY / tle.mean_motion;
      Real a = pow((T * T * GM_EARTH) / (4.0 * PI * PI), 1.0 / 3.0);
      Real e = tle.eccentricity;
      Real i = tle.inclination * RAD;
      Real Omega = tle.raan * RAD;
      Real w = tle.arg_perigee * RAD;
      Real rad_per_sec = tle.mean_motion * 2.0 * PI / SECS_DAY;
      Real dt = GetLupntEpoch() - tle.epoch_tai;
      Real M = WrapToPi(tle.mean_anomaly * RAD + rad_per_sec * dt);

      // Convert to Cartesian
      State coe = ClassicalOE({a, e, i, Omega, w, M}, Frame::GCRF);

      // Create the spacecraft
      auto sat = MakePtr<Satellite>();
      sat->SetTime(0.0);
      sat->SetState(coe);
      sat->SetName(tle.name);
      satellites.push_back(std::move(sat));
    }

    Logger::Debug(
        fmt::format("Loaded {} satellites from TLE file: {}", satellites.size(), filename), "TLE",
        0.0);
    return satellites;
  }

  int GetPrn(const std::string& name) {
    // PRN
    if (name.size() < 3) return -1;

    std::string prefix = name.substr(0, 3);
    std::vector<std::string> split;

    if (prefix == "GPS") {
      // GPS BIIR-2  (PRN 13)  -> 13
      return std::stoi(SplitString(name, '(')[1].substr(4, 2));
    }
    if (prefix == "BEI") {
      // BEIDOU-2 M3 (C11)     -> 11
      split = SplitString(name, '(');
      return (split[1][0] == 'C') ? std::stoi(split[1].substr(1, 2)) : 0;
    }
    if (prefix == "GSA") {
      // GSAT0202 (GALILEO 6)  -> 6
      if (name.substr(4, 2) == "01") return -1;
      split = SplitString(SplitString(SplitString(name, '(')[1], ' ')[1], ')');
      return split[0].size() <= 2 ? std::stoi(split[0]) : -1;
    }
    if (prefix == "COS") {
      // COSMOS 2501 (702K)    -> 702
      return std::stoi(SplitString(name, '(')[1].substr(0, 3));
    }
    if (prefix == "QZS") {
      // QZS-1R (QZSS/PRN 196) -> 196
      return std::stoi(name.substr(4, 1));
    }

    std::cout << prefix << std::endl;
    std::cout << name.substr(4, 2) << std::endl;
    std::cout << (prefix == "GSA") << std::endl;
    std::cout << (name.substr(4, 2) == "01") << std::endl;
    LUPNT_CHECK(false, "Unknown GNSS PRN format for name: " + name, "TLE");
    return -1;
  }

}  // namespace lupnt