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.
 
 
 
 
 

381 lines
9.7 KiB

#include "orbits.h"
const static uint ELLIPSE_VERT_COUNT = 256;
void
systemInit(TwoBodySystem& system, GravBody gb, OrbitalElements el)
{
system.body = gb;
system.elements = el;
system.ep = ellipseInitAE(el.a, el.e);
system.epsilon = orbitGetSpecificEnergy(system.ep.a, gb.mu);
system.h = orbitGetAngularMomentum(system.ep.p, gb.mu);
system.orbital_period = orbitGetPeriod(system.ep.a, gb.mu);
system.r_periapsis = system.ep.a - system.ep.c;
system.r_apoapsis = 2 * system.ep.a - system.r_periapsis;
system.rotation = orbitGetXForm(el);
system.sat.theta = el.nu;
system.sat.r = orbitGetRadialDistance(system.ep.e, system.ep.p, el.nu);
system.sat.v = orbitGetVelocity(system.epsilon, gb.mu, system.sat.r);
system.sat.position =
system.rotation * orbitGetPositionVector(system.sat.r, el.nu);
}
GravBody
gravBodyInit(double mu, double r)
{
GravBody gb = {0};
gb.mu = mu;
gb.radius = r;
return gb;
}
EllipseParameters
ellipseInitAB(double a, double b)
{
assert(a > 0 && b > 0 && a >= b);
EllipseParameters ep = { a, b };
ep.c = sqrt(a * a - b * b);
ep.e = ep.c / ep.a;
ep.p = ep.a * (1 - pow(ep.e, 2));
ep.f1.x = ep.c;
ep.f2.x = -1 * ep.c;
return ep;
}
// FIXME: we should avoid calling ellipseInitAB, and recalculate the properties
// to avoid floating point errors in the known quantity 'e'
EllipseParameters
ellipseInitAE(double a, double e)
{
assert(e >= 0 && e < 1);
double b = a * sqrt(1 - pow(e, 2.0));
return ellipseInitAB(a, b);
}
OrbitalElements
orbitInit(double a, double e, double iota, double ohm, double omega, double nu)
{
OrbitalElements o = {0};
o.a = a;
o.e = e;
o.iota = iota;
o.ohm = ohm;
o.omega = omega;
o.nu = nu;
return o;
}
OrbitalElements
orbitGetElementsFromStateVectors(glm::dvec3 r, glm::dvec3 v, double mu)
{
OrbitalElements el = {0};
const glm::dvec3 I = glm::dvec3(1, 0, 0);
const glm::dvec3 J = glm::dvec3(0, 1, 0);
const glm::dvec3 K = glm::dvec3(0, 0, 1);
double r_mag = orbitGetVectorMagnitude(r);
double v_mag = orbitGetVectorMagnitude(v);
double epsilon = orbitGetSpecificEnergyFromStateVectors(r_mag, v_mag, mu);
// TODO: orbits other than ellipses
assert(epsilon < 0);
glm::dvec3 ecc_v = orbitGetEccentricityVector(r, v, mu);
el.a = orbitGetSemiMajorAxis(epsilon, mu);
el.e = fabs(orbitGetVectorMagnitude(ecc_v));
glm::dvec3 h = glm::cross(r, v);
double cosi = glm::dot(K, h) / orbitGetVectorMagnitude(h);
el.iota = acos(cosi);
if (el.iota == 0) { // prograde equatorial orbit
el.ohm = 0;
double i_dot_e = glm::dot(I, ecc_v);
el.omega = acos(i_dot_e / el.e);
if (ecc_v.y < 0) el.omega *= -1; // quadrant check
} else if (el.iota == M_PI) { // retrograde equatorial orbit
// FIXME: retrograde equatorial orbit case
assert(0);
} else {
glm::dvec3 n = glm::cross(K, h); // ascending node vector
double n_mag = orbitGetVectorMagnitude(n);
double cos_ohm = glm::dot(I, n) / n_mag;
double sin_ohm = glm::dot(J, n) / n_mag;
el.ohm = atan2(sin_ohm, cos_ohm);
double cos_omega = glm::dot(n, ecc_v) / (n_mag * el.e);
el.omega = acos(cos_omega);
if (ecc_v.z < 0) el.omega = 2 * M_PI - el.omega; // quadrant check
}
if (el.e == 0) {
el.nu = 0;
} else {
double cos_theta = glm::dot(r, ecc_v) / (el.e * r_mag);
// NOTE: clamp to vaild range for acos function
// we were getting slight floating point errors out of the glm::dot()
// function above
if (cos_theta > 1.0)
cos_theta = 1.0;
else if (cos_theta < - 1.0)
cos_theta = -1.0;
el.nu= acos(cos_theta);
}
// FIXME: breaks test case
//if (glm::dot(r, v) < 0) el.nu *= -1; //quadrant check
if (glm::dot(r, v) < 0) el.nu = 2 * M_PI - el.nu; //quadrant check
return el;
}
StateVectors
orbitGetStateVectorsFromElements(const OrbitalElements& el, double mu)
{
double p = 0;
// FIXME: need a helper here
if (el.e < 1) { // ellipse or circle
p = el.a * (1 - pow(el.e, 2));
} else if (el.e == 1) { // parabola
assert(0);
} else { // hyperbola
assert(0);
}
// FIXME: we need to ensure that we update el.nu instead of sat.theta
double r = orbitGetRadialDistance(el.e, p, el.nu);
double h = orbitGetAngularMomentum(p, mu);
StateVectors sv;
// TODO: getPos/getVel don't need to be interface functions
sv.position = orbitGetPositionVector(r, el.nu);
sv.velocity = orbitGetVelocityVector(mu, h, el.e, el.nu);
glm::dmat3 M = orbitGetXForm(el);
sv.position = M * sv.position;
sv.velocity = M * sv.velocity;
return sv;
}
glm::dvec3
orbitGetEccentricityVector(glm::dvec3 r, glm::dvec3 v, double mu)
{
double v_mag = orbitGetVectorMagnitude(v);
double r_mag = orbitGetVectorMagnitude(r);
return 1 / mu * ((pow(v_mag, 2) - mu / r_mag) * r - (glm::dot(r, v) * v));
}
glm::dvec3
orbitGetPositionVector(double r, double theta)
{
return glm::dvec3(r * cos(theta), r * sin(theta), 0);
}
glm::dvec3
orbitGetVelocityVector(double mu, double h, double e, double theta)
{
return glm::dvec3(-1 * (mu / h) * sin(theta), mu / h * (e + cos(theta)), 0);
}
glm::dmat3
orbitGetXForm(OrbitalElements elements)
{
const OrbitalElements& el = elements;
glm::mat3 M(1.0);
M[0][0] = cos(el.ohm) * cos(el.omega) - sin(el.ohm) * sin(el.omega) * cos(el.iota);
M[1][0] = -cos(el.ohm) * sin(el.omega) - sin(el.ohm) * cos(el.omega) * cos(el.iota);
M[2][0] = sin(el.ohm) * sin(el.iota);
M[0][1] = sin(el.ohm) * cos(el.omega) + cos(el.ohm) * sin(el.omega) * cos(el.iota);
M[1][1] = -sin(el.ohm) * sin(el.omega) + cos(el.ohm) * cos(el.omega) * cos(el.iota);
M[2][1] = -cos(el.ohm) * sin(el.iota);
M[0][2] = sin(el.omega) * sin(el.iota);
M[1][2] = cos(el.omega) * sin(el.iota);
M[2][2] = cos(el.iota);
return M;
}
double
orbitGetVectorMagnitude(glm::dvec3 v)
{
return(sqrt(pow(v.x, 2) + pow(v.y, 2) + pow(v.z, 2)));
}
//
// NOTE: propagate anomaly functions:
//
// FIXME: variable names 'ecc' should be just 'e'
inline double
getEccAnomFromTrueAnom(double ecc, double true_anom)
{
return 2 * atan(sqrt((1 - ecc) / (1 + ecc)) * tan(true_anom / 2));
}
inline double
getTrueAnomFromEccAnom(double ecc, double ecc_anom)
{
return 2 * atan(sqrt((1 + ecc) / (1 - ecc)) * tan(ecc_anom / 2));
}
inline double
getMeanAnomFromEccAnom(double ecc_anom, double ecc)
{
return ecc_anom - ecc * sin(ecc_anom);
}
inline double
getMeanMotion(double mu, double a)
{
return sqrt(mu / pow(a, 3));
}
inline double
getPropagatedMeanAnom(double mean_anom, double mean_motion, double time_step)
{
return mean_anom + mean_motion * (time_step);
}
inline double
getInitialTrialValue(double mean_anom, double ecc)
{
return mean_anom + ecc * sin(mean_anom)
+ ((pow(ecc, 2) / 2) * sin(2 * mean_anom));
}
inline double
getTrialError(double ecc, double test_anom, double mean_anom)
{
return test_anom - ecc * sin(test_anom) - mean_anom;
}
inline double
getNextTrialValue(double err, double ecc, double test_anom, double mean_anom)
{
// compute derivative of the error function
double derr = 1 - ecc * cos(test_anom);
// use Newton's method to compute next trial value of E2
return test_anom - (err / derr);
}
double
getPropagatedEccAnomaly(TwoBodySystem sys,
double initial_anom,
double time_step)
{
double e = sys.ep.e;
double E1 = getEccAnomFromTrueAnom(e, initial_anom);
double M1 = getMeanAnomFromEccAnom(E1, e);
double n = getMeanMotion(sys.body.mu, sys.elements.a);
double M2 = getPropagatedMeanAnom(M1, n, time_step);
double E2_1 = getInitialTrialValue(M2, e);
// test if guess is a solution to kepler's equation
const double ACCEPTABLE_ERROR = 0.00000001;
double E2_test = E2_1;
for (uint i = 0; i < 10; i++) {
double err = getTrialError(e, E2_test, M2);
if (fabs(err) < ACCEPTABLE_ERROR)
break;
E2_test = getNextTrialValue(err, e, E2_test, M2);
}
return E2_test;
}
double
orbitGetPropagatedTrueAnomaly(TwoBodySystem sys,
double initial_anom,
double time_step)
{
// FIXME: I don't think we need this now that we have gs->running?
// NOTE: 'pause' simulation when time_step is set close to 0
if (time_step < 1e-8 && time_step > -1e-8)
return initial_anom;
double ecc_anom = getPropagatedEccAnomaly(sys, initial_anom, time_step);
return getTrueAnomFromEccAnom(sys.ep.e, ecc_anom);
}
// FIXME: organize into interface/internal functions
double
orbitTimeSincePeriapsis(TwoBodySystem sys, double theta);
double
orbitGetTimeOfFlight(TwoBodySystem sys, double theta_begin, double theta_end)
{
double e = sys.ep.e;
double n = getMeanMotion(sys.body.mu, sys.ep.a);
double ecc_begin = getEccAnomFromTrueAnom(sys.ep.e, theta_begin);
double ecc_end = getEccAnomFromTrueAnom(sys.ep.e, theta_end);
// NOTE: test if flight passes through perisapsis
if (ecc_begin > ecc_end)
ecc_end += 2 * M_PI;
double M1 = getMeanAnomFromEccAnom(ecc_begin, e);
double M2 = getMeanAnomFromEccAnom(ecc_end, e);
// NOTE: Kepler's equation for time of flight
double tof_begin = 1 / n * M1;
double tof_end = 1 / n * M2;
return tof_end - tof_begin;
}
double
orbitGetTransferVelocity(const TwoBodySystem& sys,
const OrbitalElements& target)
{
const OrbitalElements& el1 = sys.elements;
assert(el1.iota == target.iota);
assert(el1.e == 0 && target.e == 0);
const double mu = sys.body.mu;
double a_t = (el1.a + target.a) / 2;
double transfer_energy = orbitGetSpecificEnergy(a_t, mu);
double r_periapse = el1.a; // NOTE: circular orbit
double v1 = orbitGetVelocity(orbitGetSpecificEnergy(el1.a, mu), mu, el1.a);
double vt = orbitGetVelocity(transfer_energy, mu, r_periapse);
return vt - v1;
}
double
orbitGetCircVelocity(const TwoBodySystem& sys, bool raise_apoapse)
{
double r = (raise_apoapse) ? sys.r_apoapsis : sys.r_periapsis;
double mu = sys.body.mu;
double epsilon_target = orbitGetSpecificEnergy(r, mu);
double v = orbitGetVelocity(epsilon_target, mu, r);
return v - sys.sat.v;
}
// internal
double
getApoapsis(double a, double e)
{
return a * (1 + e);
}
double
getPeriapsis(double a, double e)
{
return a * (1 - e);
}