Program Listing for File state_conversions.cc¶

↰ Return to documentation for file (conversions/state_conversions.cc)

#include "lupnt/conversions/state_conversions.h"

#include <cmath>

#include "lupnt/conversions/anomaly_conversions.h"
#include "lupnt/core/constants.h"
#include "lupnt/numerics/math_utils.h"
#include "lupnt/states/state.h"

namespace lupnt {

  State CartToClassical(const State& rv, Real GM) {
    Vec3 r = rv.head(3);  // Position
    Vec3 v = rv.tail(3);  // Velocity
    Vec3 h = r.cross(v);  // Areal velocity
    Real H = h.norm();    // Angular momentum

    Real Omega = atan2(h(0), -h(1));                        // Long. ascend. node
    Real i = atan2(sqrt(h(0) * h(0) + h(1) * h(1)), h(2));  // Inclination
    Real u = atan2(r(2) * H, -r(0) * h(1) + r(1) * h(0));   // Arg. of latitude
    Real R = r.norm();                                      // Distance
    Real a = 1.0 / (2.0 / R - v.squaredNorm() / GM);        // Semi-major axis

    Real eCosE = 1.0 - R / a;              // e*cos(E)
    Real eSinE = r.dot(v) / sqrt(GM * a);  // e*sin(E)
    Real e2 = eCosE * eCosE + eSinE * eSinE;
    Real e = sqrt(e2);             // Eccentricity
    Real E = atan2(eSinE, eCosE);  // Eccentric anomaly

    Real M = WrapToPi(E - eSinE);                         // Mean anomaly
    Real nu = atan2(sqrt(1.0 - e2) * eSinE, eCosE - e2);  // True anomaly
    Real omega = WrapToPi(u - nu);                        // Arg. of perihelion
    return ClassicalOE({a, e, i, Omega, omega, M}, rv.GetFrame());
  }

  State CartToClassical(Real dt, const State& r1, const State& r2, Real GM) {
    // Calculate vector r0 (fraction of r2 perpendicular to r1)
    // and the magnitudes of r1, r2 and r0
    Real s_a = r1.norm();
    Vec3 e1 = r1 / s_a;
    Real s_b = r2.norm();
    Real fac = r2.dot(e1);
    Vec3 r0 = r2 - fac * e1;
    Real s0 = r0.norm();
    Vec3 e0 = r0 / s0;

    // Inclination and ascending node
    Vec3 W = e1.cross(e0);
    Real Omega = (atan2(W(0), -W(1)));                      // Long. ascend. node
    Real i = atan2(sqrt(W(0) * W(0) + W(1) * W(1)), W(2));  // Inclination
    Real u;
    if (i == 0.0)
      u = atan2(r1(1), r1(0));
    else
      u = atan2(+e1(2), -e1(0) * W(1) + e1(1) * W(0));

    // Semilatus rectum
    Real tau = sqrt(GM) * dt;
    Real eta = RatioOfSectorToTriangleArea(r1, r2, tau);
    Real p = pow(s_a * s0 * eta / tau, 2);

    // Eccentricity, true anomaly and argument of perihelion
    Real cos_dnu = fac / s_b;
    Real sin_dnu = s0 / s_b;

    Real ecos_nu = p / s_a - 1.0;
    Real esin_nu = (ecos_nu * cos_dnu - (p / s_b - 1.0)) / sin_dnu;

    Real e = sqrt(ecos_nu * ecos_nu + esin_nu * esin_nu);
    Real nu = atan2(esin_nu, ecos_nu);
    Real omega = WrapToPi(u - nu);

    // Perihelion distance, semimajor axis and mean motion
    Real a = p / (1.0 - e * e);

    // Mean anomaly and time of perihelion passage
    Real M;
    if (e < 1.0) {
      Real E = atan2(sqrt((1.0 - e) * (1.0 + e)) * esin_nu, ecos_nu + e * e);
      M = WrapToPi(E - e * sin(E));
    } else {
      Real sinhH = sqrt((e - 1.0) * (e + 1.0)) * esin_nu / (e + e * ecos_nu);
      M = e * sinhH - log(sinhH + sqrt(1.0 + sinhH * sinhH));
    }

    // Keplerian elements vector
    return ClassicalOE({a, e, i, Omega, omega, M}, r1.GetFrame());
  }

  State InertialToSynodic(const State& rv_c, const State& rv_d) {
    Vec3 r_d = rv_d.head(3);
    Vec3 v_d = rv_d.tail(3);
    Vec3 r_c = rv_c.head(3);
    Vec3 v_c = rv_c.tail(3);

    Vec3 x = r_c.normalized();
    Vec3 y = (r_c.cross(v_c)).normalized();
    Vec3 z = y.cross(x);

    Mat3 R_inert2syn;
    R_inert2syn << x.transpose(), z.transpose(), y.transpose();

    Vec3 w = r_c.cross(v_c) / r_c.norm();
    Vec3 r_syn_d = R_inert2syn * (r_d - r_c);
    Vec3 v_syn_d = R_inert2syn * (v_d - v_c - w.cross(r_d - r_c));

    return Cart6(r_syn_d, v_syn_d, rv_c.GetFrame());
  }

  State SynodicToInertial(const State& rv_c, const State& rv_syn_d) {
    Vec3 r_c = rv_c.head(3);
    Vec3 v_c = rv_c.tail(3);

    // RTN basis vectors
    Vec3 x = r_c.normalized();
    Vec3 y = (r_c.cross(v_c)).normalized();
    Vec3 z = y.cross(x);

    Mat3 R_syn2inert;  // Rotation matrix from RTN to inertial
    R_syn2inert << x, z, y;

    Vec3 w = r_c.cross(v_c) / r_c.norm();
    Vec3 r_d = r_c + R_syn2inert * rv_syn_d.head(3);
    Vec3 v_d = v_c + R_syn2inert * rv_syn_d.tail(3) + w.cross(r_d - r_c);

    return Cart6(r_d, v_d, rv_c.GetFrame());
  }

  State ClassicalToCart(const State& coe, Real GM) {
    auto [a, e, i, Omega, omega, M] = Unpack(Vec6(coe));

    Real E = MeanToEccAnomaly(M, e);  // Eccentric anomaly
    Real cosE = cos(E);
    Real sinE = sin(E);

    Real fac = sqrt((1.0 - e) * (1.0 + e));
    Real R = a * (1.0 - e * cosE);  // Distance
    Real V = sqrt(GM * a) / R;      // Velocity
    Vec3 r(a * (cosE - e), a * fac * sinE, 0.0);
    Vec3 v(-V * sinE, +V * fac * cosE, 0.0);

    Mat3 PQW = RotZ(-Omega) * RotX(-i) * RotZ(-omega);
    r = PQW * r;
    v = PQW * v;

    return Cart6(r, v, coe.GetFrame());
  }

  State ClassicalToQuasiNonsing(const State& coe, Real GM) {
    (void)GM;
    auto [a, e, i, Omega, w, M] = Unpack(Vec6(coe));

    Real u = w + M;
    Real ex = e * cos(w);
    Real ey = e * sin(w);

    return QuasiNonsingularOE({a, u, ex, ey, i, Omega}, coe.GetFrame());
  }

  State ClassicalToEquinoctial(const State& coe, Real GM) {
    (void)GM;
    auto [a, e, i, Omega, w, M] = Unpack(Vec6(coe));

    Real f = MeanToTrueAnomaly(M, e);
    Real w_tilde = Omega + w;
    Real Psi = w_tilde + f;
    Real tq1 = e * cos(w_tilde);
    Real tq2 = e * sin(w_tilde);
    Real p1 = tan(i / 2) * cos(Omega);
    Real p2 = tan(i / 2) * sin(Omega);

    Psi = fmod(Psi.val(), 2 * PI);
    if (Psi > PI) {
      Psi = Psi - 2 * PI;
    }

    return EquinoctialOE({a, Psi, tq1, tq2, p1, p2}, coe.GetFrame());
  }

  State ClassicalToDelaunay(const State& coe, Real GM) {
    auto [a, e, i, O, w, M] = Unpack(Vec6(coe));

    Real n = sqrt(GM / pow(a, 3));
    Real t = M / n;

    Real l = M;
    Real g = w;
    Real h = O - n * t;
    Real L = sqrt(GM * a);
    Real G = L * sqrt(1 - e * e);
    Real H = G * cos(i);

    return DelaunayOE({l, g, h, L, G, H}, coe.GetFrame());
  }

  State QuasiNonsingToClassical(const State& qnsoeVec, Real GM) {
    (void)GM;
    auto [a, u, ex, ey, i, Omega] = Unpack(Vec6(qnsoeVec));

    Real e = sqrt(ex * ex + ey * ey);
    Real w = atan2(ey, ex);
    Real M = u - w;

    Vec6 coe(a, e, i, Omega, w, M);
    return ClassicalOE(coe, qnsoeVec.GetFrame());
  }

  State EquinoctialToClassical(const State& equioe, Real GM) {
    (void)GM;
    auto [a, Psi, tq1, tq2, p1, p2] = Unpack(Vec6(equioe));

    Real Omega = atan2(p2, p1);
    Real i = 2 * atan2(p1, cos(Omega));

    Real wtilde = atan2(tq2, tq1);
    Real e = sqrt(tq1 * tq1 + tq2 * tq2);

    Real w = wtilde - Omega;
    Real f = Psi - wtilde;

    Real E = atan2(sin(f) * sqrt(1 - e * e), cos(f) + e);
    Real M = E - e * sin(E);

    w = std::fmod(w.val(), 2 * PI);
    M = std::fmod(M.val(), 2 * PI);
    if (std::abs(M.val() - 2 * PI) < std::numeric_limits<double>::epsilon()) {
      M = 0;
    }
    return ClassicalOE({a, e, i, Omega, w, M}, equioe.GetFrame());
  }

  State DelaunayToClassical(const State& delaunay, Real GM) {
    auto [l, g, h, L, G, H] = Unpack(Vec6(delaunay));

    Real a = L * L / GM;
    Real M = l;

    Real n = sqrt(GM / pow(a, 3));
    Real t = M / n;

    Real e = sqrt(1 - pow(G / L, 2));
    Real i = safe_acos(H / G);
    Real O = h + n * t;
    Real w = g;
    return ClassicalOE({a, e, i, O, w, M}, delaunay.GetFrame());
  }

  State RelQuasiNonsingToClassical(const State& coe_c, const State& rqnsoe) {
    auto [ac, ec, ic, Oc, wc, Mc] = Unpack(Vec6(coe_c));
    auto [ada, adl, adex, adey, adix, adiy] = Unpack(Vec6(rqnsoe));

    Real uc = WrapToPi(wc + Mc);
    Real exc = ec * cos(wc);
    Real eyc = ec * sin(wc);

    Real dO = (adiy / ac) / sin(ic);
    Real du = (adl / ac) - (dO * cos(ic));

    Real ad = ac + ada;
    Real exd = exc + (adex / ac);
    Real eyd = eyc + (adey / ac);
    Real id = ic + (wc / ac);
    Real Od = Oc + dO;
    Real ud = uc + du;

    Real wd = atan2(eyd, exd);
    Real ed = sqrt(exd * exd + eyd * eyd);
    Real Md = WrapToPi(ud - wd);

    return ClassicalOE({ad, ed, id, Od, wd, Md}, coe_c.GetFrame());
  }

  State TleToClassical(const TLE& tle, Real GM) {
    (void)GM;
    Real period = SECS_DAY / tle.mean_motion;
    Real sma = pow((period * period * GM_EARTH) / (4.0 * PI * PI), 1.0 / 3.0);
    Real ecc = tle.eccentricity;
    Real inc = tle.inclination * RAD;
    Real raan = tle.raan * RAD;
    Real aop = tle.arg_perigee * RAD;
    Real ma = WrapToPi(tle.mean_anomaly * RAD);
    return ClassicalOE({sma, ecc, inc, raan, aop, ma});
  }

  VEC_IMP_VECTOR_REAL(ClassicalToCart, 6);
  VEC_IMP_VECTOR_VECTOR(InertialToSynodic, 6);
  VEC_IMP_VECTOR_VECTOR(SynodicToInertial, 6);
  VEC_IMP_VECTOR_REAL(CartToClassical, 6);
  VEC_IMP_VECTOR_REAL(ClassicalToQuasiNonsing, 6);
  VEC_IMP_VECTOR_REAL(ClassicalToEquinoctial, 6);
  VEC_IMP_VECTOR_REAL(ClassicalToDelaunay, 6);
  VEC_IMP_VECTOR_REAL(QuasiNonsingToClassical, 6);
  VEC_IMP_VECTOR_REAL(EquinoctialToClassical, 6);
  VEC_IMP_VECTOR_REAL(DelaunayToClassical, 6);
  VEC_IMP_VECTOR_VECTOR(RelQuasiNonsingToClassical, 6);

}  // namespace lupnt