Skip to content

File Cartesian.cpp

File List > astrea > astro > astro > state > orbital_elements > instances > Cartesian.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_elements/instances/Cartesian.hpp>

#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 <astro/state/orbital_elements/OrbitalElements.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 <math/interpolation.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 {

Cartesian Cartesian::LEO(const GravParam& mu) { return Cartesian(Keplerian::LEO(), mu); }
Cartesian Cartesian::LMEO(const GravParam& mu) { return Cartesian(Keplerian::LMEO(), mu); }
Cartesian Cartesian::GPS(const GravParam& mu) { return Cartesian(Keplerian::GPS(), mu); }
Cartesian Cartesian::HMEO(const GravParam& mu) { return Cartesian(Keplerian::HMEO(), mu); }
Cartesian Cartesian::GEO(const GravParam& mu) { return Cartesian(Keplerian::GEO(), mu); }

Cartesian::Cartesian(const Keplerian& elements, const GravParam& mu)
{
    // Extract elements
    const auto& a     = elements.get_semimajor();
    const auto& ecc   = elements.get_eccentricity();
    const auto& inc   = elements.get_inclination();
    const auto& raan  = elements.get_right_ascension();
    const auto& w     = elements.get_argument_of_perigee();
    const auto& theta = elements.get_true_anomaly();

    if (a == 0.0 * km) {
        _r = { 0.0 * km, 0.0 * km, 0.0 * km };
        _v = { 0.0 * km / s, 0.0 * km / s, 0.0 * km / s };
        return;
    }

    // Precalculate
    const quantity cosTheta = cos(theta);
    const quantity sinTheta = sin(theta);

    const quantity cosW = cos(w);
    const quantity sinW = sin(w);

    const quantity cosRaan = cos(raan);
    const quantity sinRaan = sin(raan);

    const quantity cosInc = cos(inc);
    const quantity sinInc = sin(inc);

    const quantity h = sqrt(mu * a * (1.0 - ecc * ecc));
    const quantity A = h * h / mu / (1.0 + ecc * cosTheta);
    const quantity B = mu / h;

    // Perifocal Coordinates
    const quantity xPeri = A * cosTheta;
    const quantity yPeri = A * sinTheta;

    const quantity vxPeri = -B * sinTheta;
    const quantity vyPeri = B * (ecc + cosTheta);

    // Preallocate Dcm values for speed
    const quantity DcmPeri2Eci11 = (cosW * cosRaan - sinW * cosInc * sinRaan);
    const quantity DcmPeri2Eci12 = (-sinW * cosRaan - cosW * cosInc * sinRaan);

    const quantity DcmPeri2Eci21 = (cosW * sinRaan + sinW * cosInc * cosRaan);
    const quantity DcmPeri2Eci22 = (-sinW * sinRaan + cosW * cosInc * cosRaan);

    const quantity DcmPeri2Eci31 = sinInc * sinW;
    const quantity DcmPeri2Eci32 = sinInc * cosW;

    // Inertial position and _velocity
    _r[0] = DcmPeri2Eci11 * xPeri + DcmPeri2Eci12 * yPeri;
    _r[1] = DcmPeri2Eci21 * xPeri + DcmPeri2Eci22 * yPeri;
    _r[2] = DcmPeri2Eci31 * xPeri + DcmPeri2Eci32 * yPeri;

    _v[0] = DcmPeri2Eci11 * vxPeri + DcmPeri2Eci12 * vyPeri;
    _v[1] = DcmPeri2Eci21 * vxPeri + DcmPeri2Eci22 * vyPeri;
    _v[2] = DcmPeri2Eci31 * vxPeri + DcmPeri2Eci32 * vyPeri;
}


Cartesian::Cartesian(const Equinoctial& elements, const GravParam& mu)
{
    // Extract
    const auto& semilatus     = elements.get_semilatus();
    const auto& f             = elements.get_f();
    const auto& g             = elements.get_g();
    const auto& h             = elements.get_h();
    const auto& k             = elements.get_k();
    const auto& trueLongitude = elements.get_true_longitude();

    // Catch default/nonsense case
    if (semilatus == 0.0 * km) {
        _r[0] = 0.0 * km;
        _r[1] = 0.0 * km;
        _r[2] = 0.0 * km;
        _v[0] = 0.0 * km / s;
        _v[1] = 0.0 * km / s;
        _v[2] = 0.0 * km / s;
        return;
    }

    // Precalculate
    const auto cosL = cos(trueLongitude);
    const auto sinL = sin(trueLongitude);

    const auto alphaSq = h * h - k * k;
    const auto sSq     = 1.0 + h * h + k * k;
    const auto w       = 1.0 + f * cosL + g * sinL;
    const auto r       = semilatus / w;

    const auto rOverSSq = r / sSq;
    const auto twoHK    = 2.0 * h * k;

    const auto gamma = 1.0 / sSq * sqrt(mu / semilatus);

    // Radius
    _r[0] = rOverSSq * (cosL * (1.0 + alphaSq) + twoHK * sinL);
    _r[1] = rOverSSq * (sinL * (1.0 - alphaSq) + twoHK * cosL);
    _r[2] = 2.0 * rOverSSq * (h * sinL - k * cosL);

    // Velocity
    _v[0] = -gamma * (sinL * (1.0 + alphaSq) - twoHK * (cosL + f) + g * (1.0 + alphaSq));
    _v[1] = -gamma * (cosL * (-1.0 + alphaSq) + twoHK * (sinL + g) + f * (-1.0 + alphaSq));
    _v[2] = 2.0 * gamma * (h * cosL + k * sinL + f * h + g * k);
}

Cartesian::Cartesian(const OrbitalElements& elements, const GravParam& mu)
{
    *this = elements.in_element_set<Cartesian>(mu);
}

// Copy constructor
Cartesian::Cartesian(const Cartesian& other) :
    _r(other._r),
    _v(other._v)
{
}

// Move constructor
Cartesian::Cartesian(Cartesian&& other) noexcept :
    _r(std::move(other._r)),
    _v(std::move(other._v))
{
}

// Move assignment operator
Cartesian& Cartesian::operator=(Cartesian&& other) noexcept
{
    if (this != &other) {
        _r = std::move(other._r);
        _v = std::move(other._v);
    }
    return *this;
}

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

// Comparitors operators
bool Cartesian::operator==(const Cartesian& other) const { return (_r == other._r && _v == other._v); }

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


// Mathmatical operators
Cartesian Cartesian::operator+(const Cartesian& other) const { return Cartesian(_r + other._r, _v + other._v); }
Cartesian& Cartesian::operator+=(const Cartesian& other)
{
    _r += other._r;
    _v += other._v;
    return *this;
}

Cartesian Cartesian::operator+(const RadiusVector<frames::earth::icrf>& r) const { return Cartesian(_r + r, _v); }
Cartesian& Cartesian::operator+=(const RadiusVector<frames::earth::icrf>& r)
{
    _r += r;
    return *this;
}

Cartesian Cartesian::operator+(const VelocityVector<frames::earth::icrf>& v) const { return Cartesian(_r, _v + v); }
Cartesian& Cartesian::operator+=(const VelocityVector<frames::earth::icrf>& v)
{
    _v += v;
    return *this;
}

Cartesian Cartesian::operator-(const Cartesian& other) const { return Cartesian(_r - other._r, _v - other._v); }
Cartesian& Cartesian::operator-=(const Cartesian& other)
{
    _r -= other._r;
    _v -= other._v;
    return *this;
}

Cartesian Cartesian::operator-(const RadiusVector<frames::earth::icrf>& r) const { return Cartesian(_r - r, _v); }
Cartesian& Cartesian::operator-=(const RadiusVector<frames::earth::icrf>& r)
{
    _r -= r;
    return *this;
}

Cartesian Cartesian::operator-(const VelocityVector<frames::earth::icrf>& v) const { return Cartesian(_r, _v - v); }
Cartesian& Cartesian::operator-=(const VelocityVector<frames::earth::icrf>& v)
{
    _v -= v;
    return *this;
}

Cartesian Cartesian::operator*(const Unitless& multiplier) const { return Cartesian(_r * multiplier, _v * multiplier); }
Cartesian& Cartesian::operator*=(const Unitless& multiplier)
{
    _r *= multiplier;
    _v *= multiplier;
    return *this;
}

CartesianPartial Cartesian::operator/(const Time& time) const { return CartesianPartial(_r / time, _v / time); }

Cartesian Cartesian::operator/(const Unitless& divisor) const { return Cartesian(_r / divisor, _v / divisor); }
Cartesian& Cartesian::operator/=(const Unitless& divisor)
{
    _r /= divisor;
    _v /= divisor;
    return *this;
}

Cartesian Cartesian::interpolate(const Time& thisTime, const Time& otherTime, const Cartesian& other, const GravParam& mu, const Time& targetTime) const
{
    const std::array<Time, 2> times = { thisTime, otherTime };
    const Distance interpX          = math::fast_interpolate<Time, Distance>(times, { _r[0], other._r[0] }, targetTime);
    const Distance interpY          = math::fast_interpolate<Time, Distance>(times, { _r[1], other._r[1] }, targetTime);
    const Distance interpZ          = math::fast_interpolate<Time, Distance>(times, { _r[2], other._r[2] }, targetTime);
    const Velocity interpVx         = math::fast_interpolate<Time, Velocity>(times, { _v[0], other._v[0] }, targetTime);
    const Velocity interpVy         = math::fast_interpolate<Time, Velocity>(times, { _v[1], other._v[1] }, targetTime);
    const Velocity interpVz         = math::fast_interpolate<Time, Velocity>(times, { _v[2], other._v[2] }, targetTime);

    return Cartesian(interpX, interpY, interpZ, interpVx, interpVy, interpVz);
}

std::vector<Unitless> Cartesian::force_to_vector() const
{
    return { _r[0] / _r[0].unit, _r[1] / _r[1].unit, _r[2] / _r[2].unit,
             _v[0] / _v[0].unit, _v[1] / _v[1].unit, _v[2] / _v[2].unit };
}

Cartesian Cartesian::from_vector(const std::vector<Unitless>& vec)
{
    if (vec.size() != 6) {
        throw std::runtime_error("Input vector must have exactly 6 elements to convert to Cartesian.");
    }

    return Cartesian(
        vec[0] * astrea::detail::distance_unit,
        vec[1] * astrea::detail::distance_unit,
        vec[2] * astrea::detail::distance_unit,
        vec[3] * astrea::detail::distance_unit / astrea::detail::time_unit,
        vec[4] * astrea::detail::distance_unit / astrea::detail::time_unit,
        vec[5] * astrea::detail::distance_unit / astrea::detail::time_unit
    );
}

Cartesian CartesianPartial::operator*(const Time& time) const { return Cartesian(_v * time, _a * time); }


std::vector<Unitless> CartesianPartial::force_to_vector() const
{
    return { _v[0] / _v[0].unit, _v[1] / _v[1].unit, _v[2] / _v[2].unit,
             _a[0] / _a[0].unit, _a[1] / _a[1].unit, _a[2] / _a[2].unit };
}

std::ostream& operator<<(std::ostream& os, Cartesian const& elements)
{
    os << "[";
    os << elements.get_x() << ", ";
    os << elements.get_y() << ", ";
    os << elements.get_z() << ", ";
    os << elements.get_vx() << ", ";
    os << elements.get_vy() << ", ";
    os << elements.get_vz();
    os << "] (Cartesian)";
    return os;
}

std::ostream& operator<<(std::ostream& os, CartesianPartial const& elements)
{
    os << "[";
    os << elements._v[0] << ", ";
    os << elements._v[1] << ", ";
    os << elements._v[2] << ", ";
    os << elements._a[0] << ", ";
    os << elements._a[1] << ", ";
    os << elements._a[2];
    os << "] (CartesianPartial)";
    return os;
}

} // namespace astro
} // namespace astrea