Skip to content

File J2MeanVop.cpp

File List > astrea > astro > astro > propagation > equations_of_motion > J2MeanVop.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/propagation/equations_of_motion/J2MeanVop.hpp>

#include <algorithm>
#include <cmath>
#include <exception>

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

#include <astro/propagation/equations_of_motion/EquationsOfMotion.hpp>
#include <astro/propagation/force_models/ForceModel.hpp>
#include <astro/state/State.hpp>
#include <astro/state/orbital_elements/instances/Cartesian.hpp>
#include <astro/state/orbital_elements/instances/Keplerian.hpp>
#include <astro/systems/AstrodynamicsSystem.hpp>
#include <astro/systems/CelestialBody.hpp>


namespace astrea {
namespace astro {

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


OrbitalElementPartials J2MeanVop::operator()(const State& state, const Vehicle& vehicle) const
{
    // Extract
    const auto mu          = state.get_system().get_mu();
    const auto J2          = state.get_system().get_central_body()->get_j2();
    const auto equitorialR = state.get_system().get_central_body()->get_equitorial_radius();

    const Keplerian elements = state.in_element_set<Keplerian>();
    const Distance& a        = elements.get_semimajor();
    // const Angle& raan = elements.get_right_ascension();
    const Angle& w     = elements.get_argument_of_perigee();
    const Angle& theta = elements.get_true_anomaly();

    // Prevents singularities from occuring in the propagation. Will cause inaccuracies.
    const Unitless& ecc = (elements.get_eccentricity() < eccTol) ? eccTol : elements.get_eccentricity();
    const Angle& inc    = (elements.get_inclination() < incTol) ? incTol : elements.get_inclination();

    // conversions Keplerian elements to r and v
    const RadiusVector<frames::earth::icrf> r   = state.get_position();
    const VelocityVector<frames::earth::icrf> v = state.get_velocity();

    const Distance& x = r.get_x();
    const Distance& y = r.get_y();
    const Distance& z = r.get_z();
    const Distance R  = r.norm();

    // Variables to reduce calculations
    const auto termA = -1.5 * J2 * mu * pow<2>(equitorialR) / pow<5>(R);
    const auto termB = pow<2>(z / R);

    // accel due to oblateness
    AccelerationVector<frames::earth::icrf> accelOblateness = { termA * (1.0 - 5.0 * termB) * x,
                                                                termA * (1.0 - 5.0 * termB) * y,
                                                                termA * (3.0 - 5.0 * termB) * z };

    // Only normal pert required
    const UnitVector Nhat         = r.cross(v).unit();
    const Acceleration normalPert = accelOblateness.dot(Nhat);

    // Precompute
    const Angle u                   = w + theta;
    const SpecificAngularMomentum h = sqrt(mu * a * (1 - ecc * ecc));

    // Calculate the derivatives of the Keplerian elements - only raan and w considered
    static const Velocity dadt          = 0.0 * km / s;
    static const UnitlessPerTime deccdt = 0.0 * one / s;
    const AngularRate _dincdt           = R / h * cos(u) * normalPert * rad;
    const AngularRate dthetadt          = h / (R * R) * rad;
    const AngularRate draandt           = R * sin(u) / (h * sin(inc)) * normalPert * rad;
    const AngularRate dwdt              = -draandt * cos(inc);

    // Loop to prevent crashes due to circular and zero inclination orbits.
    // Will cause an error
    AngularRate dincdt = _dincdt;
    if (inc == incTol && dincdt <= incTol * one / s) { dincdt = 0.0 * rad / s; }

    return KeplerianPartial(dadt, deccdt, dincdt, draandt, dwdt, dthetadt);
}


StateTransitionMatrix J2MeanVop::compute_stm(const State& state, const Vehicle& vehicle) const
{
    return StateTransitionMatrix(*this, state, vehicle);
}

} // namespace astro
} // namespace astrea