astro/orbit.tstypescript
import * as math from "../math";
import { Mat3 } from "../math/Mat3";
type Func = (x: number) => number;
export function getEccentricAnomaly(M: number, ecc: number): number {
if (ecc === 0.0) {
// Circular orbit
return M;
} else if (ecc < 0.2) {
// Low eccentricity, so use the standard iteration technique
return math.solve_iteration_fixed(solveKeplerFunc1(ecc, M), M, 5);
} else if (ecc < 0.9) {
// Higher eccentricity elliptical orbit; use a more complex but
// much faster converging iteration.
return math.solve_iteration_fixed(solveKeplerFunc2(ecc, M), M, 6);
} else if (ecc < 1.0) {
// Extremely stable Laguerre-Conway method for solving Kepler's
// equation. Only use this for high-eccentricity orbits, as it
// requires more calculation.
let E = M + 0.85 * ecc * Math.sign(Math.sin(M));
return math.solve_iteration_fixed(solveKeplerLaguerreConway(ecc, M), E, 8);
} else if (ecc === 1.0) {
// TODO: Parabolic orbit
return M;
} else {
// Laguerre-Conway method for hyperbolic (ecc > 1) orbits.
let E = Math.log((2 * M) / ecc + 1.85);
return math.solve_iteration_fixed(solveKeplerLaguerreConwayHyp(ecc, M), E, 30);
}
}
// Standard iteration for solving Kepler's Equation
function solveKeplerFunc1(ecc: number, M: number): Func {
return function (x: number) {
return M + ecc * Math.sin(x);
};
}
// Faster converging iteration for Kepler's Equation; more efficient
// than above for orbits with eccentricities greater than 0.3. This
// is from Jean Meeus's _Astronomical Algorithms_ (2nd ed), p. 199
function solveKeplerFunc2(ecc: number, M: number): Func {
return function (x: number) {
return x + (M + ecc * Math.sin(x) - x) / (1 - ecc * Math.cos(x));
};
}
function solveKeplerLaguerreConway(ecc: number, M: number): Func {
return function (x: number) {
const s = ecc * Math.sin(x);
const c = ecc * Math.cos(x);
const f = x - s - M;
const f1 = 1 - c;
x += (-5 * f) / (f1 + Math.sign(f1) * Math.sqrt(Math.abs(16 * f1 * f1 - 20 * f * s)));
return x;
};
}
function solveKeplerLaguerreConwayHyp(ecc: number, M: number): Func {
return function (x: number) {
const s = ecc * Math.sinh(x);
const c = ecc * Math.cosh(x);
const f = s - x - M;
const f1 = c - 1;
x += (-5 * f) / (f1 + Math.sign(f1) * Math.sqrt(Math.abs(16 * f1 * f1 - 20 * f * s)));
return x;
};
}
export function getEllipticalEccentricAnomaly(meanAnomaly: number, eccentricity: number): number {
const tol = 0.00000001745;
let iterations = 20;
let e = meanAnomaly - 2.0 * Math.PI * ((meanAnomaly / (2.0 * Math.PI)) | 0);
let err = 1;
while (Math.abs(err) > tol && iterations > 0) {
err = e - eccentricity * Math.sin(e) - meanAnomaly;
let delta = err / (1 - eccentricity * Math.cos(e));
e -= delta;
iterations--;
}
return e;
}
export function getTrueAnomaly(eccentricAnomaly: number, eccentricity: number): number {
const revs = Math.floor(eccentricAnomaly / math.TWO_PI);
eccentricAnomaly -= revs * math.TWO_PI;
let trueAnomaly = Math.atan2(
Math.sin(eccentricAnomaly) * Math.sqrt(1 - eccentricity * eccentricity),
Math.cos(eccentricAnomaly) - eccentricity
);
trueAnomaly = math.zeroTwoPI(trueAnomaly);
if (eccentricAnomaly < 0) {
trueAnomaly -= math.TWO_PI;
}
return trueAnomaly + revs * math.TWO_PI;
}
export function getPerifocalToCartesianMatrix(
argumentOfPeriapsis: number,
inclination: number,
rightAscension: number
): Mat3 {
let res = new Mat3();
let cosap = Math.cos(argumentOfPeriapsis),
sinap = Math.sin(argumentOfPeriapsis),
cosi = Math.cos(inclination),
sini = Math.sin(inclination),
cosraan = Math.cos(rightAscension),
sinraan = Math.sin(rightAscension);
res._m[0] = cosraan * cosap - sinraan * sinap * cosi;
res._m[1] = sinraan * cosap + cosraan * sinap * cosi;
res._m[2] = sinap * sini;
res._m[3] = -cosraan * sinap - sinraan * cosap * cosi;
res._m[4] = -sinraan * sinap + cosraan * cosap * cosi;
res._m[5] = cosap * sini;
res._m[6] = sinraan * sini;
res._m[7] = -cosraan * sini;
res._m[8] = cosi;
return res;
}