You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

264 lines
6.3 KiB

#pragma once
#include <cmath>
#define GLM_FORCE_XYZW_ONLY
#include <glm/glm.hpp>
#include "util.h"
#define DEG2RAD(x) x * M_PI / 180
struct EllipseParameters
{
double a; // semi-major axis
double b; // semi-minor axis
double e; // eccentricity
double c; // linear eccentricity
double p; // semilatus rectum
glm::vec2 f1; // 'primary' focus
glm::vec2 f2; // 'vacant' focus
};
struct OrbitalElements
{
// NOTE: classical orbital elements
double a; // semimajor axis
double e; // eccentricity
double iota; // (ι) inclination
double ohm; // (Ω) longitude of the ascending node
double omega; // (ω) argument of periapsis
double nu; // (ν) true anomaly at T0
};
struct GravBody
{
double mu; // (μ) gravitational parameter
double radius; // radius of ideal sphere representing the body
// double r_atmos; // TODO: bodies w/ atmosphere
};
struct Satellite
{
glm::dvec3 position;
glm::dvec3 velocity;
double theta; // true anomaly
double r; // radius magnitude at theta
double gamma; // (γ) flight path angle
double v; // velocity magnitute
};
struct StateVectors
{
glm::dvec3 position;
glm::dvec3 velocity;
};
// NOTE: top level composite for 2 body system
struct TwoBodySystem
{
GravBody body;
Satellite sat;
EllipseParameters ep;
OrbitalElements elements;
glm::dmat3 rotation;
double epsilon; // (ε) specific orbital energy, MJ/kg
double h; // angular momentum
double r_apoapsis; // apoapse distance from body center
double r_periapsis; // periapsis distance from body center
double orbital_period; // in seconds
};
void
systemInit(TwoBodySystem& system, GravBody gb, OrbitalElements el);
GravBody
gravBodyInit(double mu, double r);
EllipseParameters
ellipseInitAB(double a, double b);
EllipseParameters
ellipseInitAE(double a, double e);
inline double
ellipseGetEccentricity(double a, double p)
{
return sqrt(1 - p / a);
}
inline bool
ellipseValidate(const EllipseParameters& ep)
{
// TODO: find out why satellite position gets wonky with orbit
// eccentricity > 0.995 while passing through true anom = 0.
// maybe divide by 0, or some floating point error?
return (ep.a > 0 &&
ep.b > 0 &&
ep.a >= ep.b &&
ep.e >= 0 &&
ep.e < 0.995);
}
inline bool
ellipsesEqual(EllipseParameters& e1, EllipseParameters& e2)
{
return (e1.a == e2.a &&
e1.b == e2.b &&
e1.e == e2.e);
}
OrbitalElements
orbitInit(double a, double e, double iota, double ohm, double omega, double nu);
OrbitalElements
orbitGetElementsFromStateVectors(glm::dvec3 r, glm::dvec3 v, double mu);
StateVectors
orbitGetStateVectorsFromElements(const OrbitalElements& el, double mu);
glm::dvec3 orbitGetEccentricityVector(glm::dvec3 r, glm::dvec3 v, double mu);
// NOTE: returns position vector in perifocal plane
glm::dvec3 orbitGetPositionVector(double r, double theta);
// NOTE: returns velocity vector in perifocal plane
glm::dvec3 orbitGetVelocityVector(double mu, double h, double e, double theta);
// NOTE: return transform from perifocal to grav body (IJK) coordinate system
// in column major format
glm::dmat3 orbitGetXForm(OrbitalElements elements);
double orbitGetVectorMagnitude(glm::dvec3 v);
inline double
orbitGetAngularMomentum(double p, double mu)
{
return sqrt(mu * p);
}
inline double
orbitGetSemiMajorAxis(double epsilon, double mu)
{
return -1 * mu / (2 * epsilon);
}
inline double
orbitGetSemiLatusRectum(double h, double mu)
{
return pow(h, 2) / mu;
}
inline double
orbitGetAngularMomentumFromStateVectors(double r, double v, double gamma)
{
return r * v * cos(gamma);
}
inline double
orbitGetSpecificEnergy(double a, double mu)
{
return -1 * mu / (2 * a);
}
inline double
orbitGetSpecificEnergyFromStateVectors(double r, double v, double mu)
{
return pow(v, 2) / 2 - mu / r;
}
inline double
orbitGetVelocity(double epsilon, double mu, double r)
{
return sqrt(2 * (epsilon + mu / r));
}
inline double
orbitGetFlightPathAngle(double e, double true_anom)
{
return atan(e * sin(true_anom) / (1 + e * cos(true_anom)));
}
inline double
orbitGetPeriod(double a, double mu)
{
return 2 * M_PI / sqrt(mu) * sqrt(pow(a, 3));
}
// NOTE: aka) the trajectory equation (eq. 2.45)
inline double
orbitGetRadialDistance(double e, double p, double true_anom)
{
return p / (1 + e * cos(true_anom));
}
// NOTE: return an equivalent angle between 0 and 2 pi radians
inline double
orbitClampAngle(double theta)
{
double revs = floor(theta / (2 * M_PI));
return theta - revs * 2 * M_PI;
}
inline glm::vec2
polarToRect(double angle, double r)
{
return glm::vec2(r * cos(angle), r * sin(angle));
}
void
orbitUpdate(OrbitalElements& o, double a, double e);
/* NOTE: how-to propagate orbit position given initial true anomaly, semimajor
* axis, mean motion, and eccentricity: ref) section 4.4, Kepler's Problem,
* "Space Flight Dynamics" by Craig A. Kluever
*
* obtain initial eccentric anomaly (E1) from true anomaly (ϴ1) and e:
* tan(E1/2) = sqrt((1-e)/(1+e)) * tan(ϴ1/2)
*
* obtain inital mean anomaly:
* M1 = E1 - e*sin(E1)
*
* obtain propagated mean anomaly, mean motion (n) is sqrt(μ/a^3):
* M2 = M1 + n(t2 - t1)
*
* express Kepler's equation in terms of propagated mean anomly:
* M2 = E2 - e*sin(E2)
*
* Use Newton's method to search for an E2 that satisfies Kepler's equation:
* guess a starting value for E2_k (k indicates iteration index):
* E2_1 = M2 + e*sin(M2) + ((e^2 / 2) * sin(2 * M2))
*
* test if guess is a solution:
* F(E2_1) = E2_1 - e*sin(E2) - M2
*
* if the result of the error function is not below some small value
* (1 * 10^-8), compute the derivative, and and iterate again:
* f'(E2_1) = 1 - e*cos(E2_1)
*
* use Newton's method to compute the next trial value of E2:
* E2_2 = E2_1 - (f(E2_1) / f'(E2_1))
*
* after we converge on a solution, convert eccentric anomaly back to true
* anomoly:
* tan(ϴ2/2) = sqrt((1+e) / (1-e)) * tan(E2/2)
*/
double
orbitGetPropagatedTrueAnomaly(TwoBodySystem sys,
double initial_anom,
double time_step); // in seconds
double
orbitGetTimeOfFlight(TwoBodySystem sys, double theta_begin, double theta_end);
// NOTE return impulse magnitute for Hohmann transfer orbit
double orbitGetTransferVelocity(const TwoBodySystem& sys,
const OrbitalElements& target);
// NOTE: return impulse magnitude for orbit circularization
double
orbitGetCircVelocity(const TwoBodySystem& sys, bool raise_apoapse = true);