Skip to content

File TwoLineElements.cpp

File List > astrea > astro > astro > state > orbital_data_formats > instances > TwoLineElements.cpp

Go to the documentation of this file

/*
 * The GNU Lesser General Public License (LGPL)
 *
 * Copyright (c) 2025 Jay Iuliano
 *
 * This file is part of Astrea.
 * Astrea is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * Astrea is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should
 * have received a copy of the GNU General Public License along with Astrea. If not, see <https://www.gnu.org/licenses/>.
 */

#include <astro/state/orbital_data_formats/instances/TwoLineElements.hpp>

#include <cmath>
#include <iomanip>
#include <iostream>

// mp-units
#include <mp-units/math.h>
#include <mp-units/systems/angular/math.h>
#include <mp-units/systems/si.h>
#include <mp-units/systems/si/math.h>

#include <math/interpolation.hpp>
#include <utilities/string_util.hpp>

#include <astro/state/orbital_elements/instances/Equinoctial.hpp>
#include <astro/state/orbital_elements/instances/Keplerian.hpp>
#include <astro/systems/AstrodynamicsSystem.hpp>
#include <astro/types/typedefs.hpp>
#include <astro/utilities/conversions.hpp>


using namespace mp_units;
using namespace mp_units::non_si;
using namespace mp_units::angular;
using angular::unit_symbols::deg;
using angular::unit_symbols::rad;
using si::unit_symbols::km;
using si::unit_symbols::s;

namespace astrea {
namespace astro {

TwoLineElements::TwoLineElements(const std::array<std::string, 3> rawTle, const AstrodynamicsSystem& sys) :
    _rawTLE({ rawTle[1], rawTle[2] })
{
    ctor_impl({ rawTle[1], rawTle[2] }, sys);
    _name = utilities::trim(rawTle[0]);
}

TwoLineElements::TwoLineElements(const std::array<std::string, 2> rawTle, const AstrodynamicsSystem& sys) :
    _rawTLE(rawTle)
{
    ctor_impl(rawTle, sys);
    _name = "Unnamed";
}

void TwoLineElements::ctor_impl(const std::array<std::string, 2> rawTle, const AstrodynamicsSystem& sys)
{
    // Parse the TLE lines
    if (rawTle[0].size() != 69) {
        throw std::invalid_argument(
            "TLE lines must be exactly 69 characters long. First line was " + std::to_string(rawTle[0].size()) + " characters."
        );
    }
    else if (rawTle[1].size() != 69) {
        throw std::invalid_argument(
            "TLE lines must be exactly 69 characters long. Second line was " + std::to_string(rawTle[1].size()) + " characters."
        );
    }

    // Line 1
    _catalogueNumber = std::stoi(rawTle[0].substr(2, 5));
    _classification  = utilities::trim(rawTle[0].substr(7, 1));
    _launchYear      = utilities::trim(rawTle[0].substr(9, 2));
    _launchNumber    = utilities::trim(rawTle[0].substr(11, 3));
    _launchPiece     = utilities::trim(rawTle[0].substr(14, 3));

    const unsigned yearInt      = std::stoi(rawTle[0].substr(18, 2));
    const std::string epochYear = yearInt < 50 ? "20" + rawTle[0].substr(18, 2) : "19" + rawTle[0].substr(18, 2);
    const double epochDay       = std::stod(rawTle[0].substr(20, 12));
    const double epochHour      = (epochDay - std::floor(epochDay)) * 24.0;
    const double epochMinute    = (epochHour - std::floor(epochHour)) * 60.0;
    const double epochSecond    = (epochMinute - std::floor(epochMinute)) * 60.0;
    const std::string epochStr  = epochYear + "-" + std::to_string(static_cast<int>(std::floor(epochDay))) + " " +
                                 std::to_string(static_cast<int>(std::floor(epochHour))) + ":" +
                                 std::to_string(static_cast<int>(std::floor(epochMinute))) + ":" +
                                 std::to_string(static_cast<int>(std::floor(epochSecond)));
    _epoch = Date(epochStr, "%Y-%j %H:%M:%S");

    _meanMotion1st = std::stod(rawTle[0].substr(33, 10)) * one / pow<2>(si::day);

    std::string meanMotion2ndStr = rawTle[0].substr(44, 8);
    int nDecimalPlaces           = std::stoi(meanMotion2ndStr.substr(6)) - 5;
    _meanMotion2nd               = std::stod(meanMotion2ndStr) * std::pow(10.0, nDecimalPlaces) * one / pow<3>(si::day);

    const std::string ballisticCoefficientStr = rawTle[0].substr(53, 8);
    nDecimalPlaces                            = std::stoi(ballisticCoefficientStr.substr(6)) - 5;
    _ballisticCoefficient = std::stod(ballisticCoefficientStr.substr(0, 6)) * std::pow(10.0, nDecimalPlaces) * one / EarthRadii;

    _ephemerisType = std::stoi(rawTle[0].substr(62, 1));
    _elementSetNo  = std::stoi(rawTle[0].substr(64, 4));
    _checkSum1     = std::stoi(rawTle[0].substr(68, 1));

    // Line 2
    _meanMotion = std::stod(rawTle[1].substr(52, 11)) * one / si::day;
    _revNumber  = std::stoi(rawTle[1].substr(63, 5));
    _checkSum2  = std::stoi(rawTle[1].substr(68, 1));

    const Distance semimajor      = pow<1, 3>(sys.get_mu() / (_meanMotion.in(one / s) * _meanMotion.in(one / s)));
    const Angle inclination       = std::stod(rawTle[1].substr(8, 8)) * deg;
    const Angle rightAscension    = std::stod(rawTle[1].substr(17, 8)) * deg;
    const Unitless eccentricity   = std::stod("." + rawTle[1].substr(26, 7)) * one;
    const Angle argumentOfPerigee = std::stod(rawTle[1].substr(34, 8)) * deg;
    const Angle meanAnomaly       = std::stod(rawTle[1].substr(43, 8)) * deg;

    const Angle trueAnomaly = convert_mean_anomaly_to_true_anomaly(meanAnomaly, eccentricity);

    _elements = Keplerian(semimajor, eccentricity, inclination, rightAscension, argumentOfPerigee, trueAnomaly);
}

// Copy constructor
TwoLineElements::TwoLineElements(const TwoLineElements& other) :
    _rawTLE(other._rawTLE),
    _catalogueNumber(other._catalogueNumber),
    _classification(other._classification),
    _launchYear(other._launchYear),
    _launchNumber(other._launchNumber),
    _launchPiece(other._launchPiece),
    _epoch(other._epoch),
    _meanMotion(other._meanMotion),
    _meanMotion1st(other._meanMotion1st),
    _meanMotion2nd(other._meanMotion2nd),
    _ballisticCoefficient(other._ballisticCoefficient),
    _ephemerisType(other._ephemerisType),
    _checkSum1(other._checkSum1),
    _revNumber(other._revNumber),
    _checkSum2(other._checkSum2),
    _elements(other._elements)
{
}

// Move constructor
TwoLineElements::TwoLineElements(TwoLineElements&& other) noexcept :
    _rawTLE(std::move(other._rawTLE)),
    _catalogueNumber(std::move(other._catalogueNumber)),
    _classification(std::move(other._classification)),
    _launchYear(std::move(other._launchYear)),
    _launchNumber(std::move(other._launchNumber)),
    _launchPiece(std::move(other._launchPiece)),
    _epoch(std::move(other._epoch)),
    _meanMotion(std::move(other._meanMotion)),
    _meanMotion1st(std::move(other._meanMotion1st)),
    _meanMotion2nd(std::move(other._meanMotion2nd)),
    _ballisticCoefficient(std::move(other._ballisticCoefficient)),
    _ephemerisType(std::move(other._ephemerisType)),
    _checkSum1(std::move(other._checkSum1)),
    _revNumber(std::move(other._revNumber)),
    _checkSum2(std::move(other._checkSum2)),
    _elements(std::move(other._elements))
{
}

// Move assignment operator
TwoLineElements& TwoLineElements::operator=(TwoLineElements&& other) noexcept
{
    if (this != &other) {
        _rawTLE               = std::move(other._rawTLE);
        _catalogueNumber      = std::move(other._catalogueNumber);
        _classification       = std::move(other._classification);
        _launchYear           = std::move(other._launchYear);
        _launchNumber         = std::move(other._launchNumber);
        _launchPiece          = std::move(other._launchPiece);
        _epoch                = std::move(other._epoch);
        _meanMotion           = std::move(other._meanMotion);
        _meanMotion1st        = std::move(other._meanMotion1st);
        _meanMotion2nd        = std::move(other._meanMotion2nd);
        _ballisticCoefficient = std::move(other._ballisticCoefficient);
        _ephemerisType        = std::move(other._ephemerisType);
        _checkSum1            = std::move(other._checkSum1);
        _revNumber            = std::move(other._revNumber);
        _checkSum2            = std::move(other._checkSum2);
        _elements             = std::move(other._elements);
    }
    return *this;
}

// Copy assignment operator
TwoLineElements& TwoLineElements::operator=(const TwoLineElements& other) { return *this = TwoLineElements(other); }

// Comparitors operators
bool TwoLineElements::operator==(const TwoLineElements& other) const
{
    return _catalogueNumber == other._catalogueNumber && _classification == other._classification &&
           _launchYear == other._launchYear && _launchNumber == other._launchNumber && _launchPiece == other._launchPiece &&
           _epoch == other._epoch && _meanMotion == other._meanMotion && _meanMotion1st == other._meanMotion1st &&
           _meanMotion2nd == other._meanMotion2nd && _ballisticCoefficient == other._ballisticCoefficient &&
           _ephemerisType == other._ephemerisType && _checkSum1 == other._checkSum1 && _revNumber == other._revNumber &&
           _checkSum2 == other._checkSum2 && _elements == other._elements;
}

bool TwoLineElements::operator!=(const TwoLineElements& other) const { return !(*this == other); }


std::ostream& operator<<(std::ostream& os, TwoLineElements const& elements)
{
    os << "[";
    os << elements.get_1st_line() << std::endl;
    os << " " << elements.get_2nd_line();
    os << "] (Tle)";
    return os;
}

} // namespace astro
} // namespace astrea