skycraft: slightly better orbit calculation

This commit is contained in:
Paul Mathieu 2022-10-18 14:43:22 -07:00
parent 4e2d522602
commit 3e9bad8929

View File

@ -362,62 +362,77 @@ function normalizeAngle(theta) {
} }
/** Let's be honest I should clean this up. /** Let's be honest I should clean this up.
* Right now it mostly only works with elliptical orbits (e < 1),
* which is fine for planets & stuff, but not for my spacecraft (or comets)
* *
* This is the part that solves Kepler's equation using Newton's method. * This is the part that solves Kepler's equation using Newton's method.
* For circular-ish orbits, one or two iterations are usually enough. * For circular-ish orbits, one or two iterations are usually enough.
* More excentric orbits can take more (6 or 7?). * More excentric orbits can take more (6 or 7?).
*
* For near-parabolic orbits (and some others?) it often fails to converge...
*/ */
function getCartesianPosition(orbit, mu, time) { function getCartesianState(orbit, mu, time) {
const { const {
excentricity: e, excentricity: e,
semimajorAxis: a, semimajorAxis: a,
inclination: i, inclination: i,
ascendingNodeLongitude: Om, ascendingNodeLongitude: Om,
periapsisArgument: w, periapsisArgument: w,
t0,
} = orbit; } = orbit;
const n = Math.sqrt(mu/(a**3)); // mean motion let n = Math.sqrt(mu/(a**3));
const M = n * time; // mean anomaly if (a < 0) {
// const nu = ( n = Math.sqrt(mu/-(a**3)); // mean motion
// M }
// + (2 * e - e**3 / 4) * Math.sin(M) const M = n * (time - t0); // mean anomaly
// + 5/4 * e**2 * Math.sin(2*M)
// + 13/12 * e**3 * Math.sin(3*M)
// ); // true anomaly, doesn't work :(
// Newton's method // Newton's method
var E2; var E2 = 0;
var E = M; var E = orbit.lastE || M;
for (var j = 1; j < 20; ++j) { let iterations = 0;
// a clever guess? https://link.springer.com/article/10.1023/A:1008200607490
// doesn't work at all.
while (Math.abs(E - E2) > 1e-10) {
if (e < 0.001) { if (e < 0.001) {
break; break;
} }
E = E2;
if (e < 1) { if (e < 1) {
E2 = E - (E - e * Math.sin(E) - M) / (1 - e * Math.cos(E)); E2 = E - (E - e * Math.sin(E) - M) / (1 - e * Math.cos(E));
} else if (e > 1) { } else if (e > 1) {
E2 = E - (-E + e * sinh(E) - M) / (e * cosh(E) - 1); E2 = E - (-E + e * Math.sinh(E) - M) / (e * Math.cosh(E) - 1);
} else { } else {
E2 = E - (E + E*E*E/3 - M) / (1 + E*E); E2 = E - (E + E*E*E/3 - M) / (1 + E*E);
} }
if (Math.abs(E - E2) < 1e-10) {
break; iterations++;
if (iterations > 100) {
console.log('numerical instability');
return {};
} }
E = E2;
} }
const nu = 2 * Math.atan(Math.sqrt((1+e) / (1-e)) * Math.tan(E/2)); orbit.lastE = E;
const r = a * (1 - e**2) / (1 + e * Math.cos(nu)); let nu;
if (e > 1) {
nu = 2 * Math.atan(Math.sqrt((e+1) / (e-1)) * Math.tanh(E/2));
} else {
nu = 2 * Math.atan(Math.sqrt((1+e) / (1-e)) * Math.tan(E/2));
}
const p = a * (1 - e**2);
const r = p / (1 + e * Math.cos(nu));// * ((a < 0) ? -1 : 1);
const rd = e * Math.sqrt(mu / p) * Math.sin(nu);
const cOm = Math.cos(Om); if (orbit.tf === undefined) {
const sOm = Math.sin(Om); orbit.tf = [se3.rotz(Om), se3.rotx(i), se3.rotz(w)].reduce(se3.product);
const cwnu = Math.cos(w + nu); }
const swnu = Math.sin(w + nu);
const x = r * Math.cos(i) * (cOm * cwnu - sOm * swnu); const tf = se3.product(orbit.tf, se3.rotz(nu));
const y = r * (sOm * cwnu + cOm * swnu); const pos = se3.apply(tf, [r, 0, 0, 1]);
const z = r * Math.sin(i) * cwnu; const vel = se3.apply(tf, [rd, Math.sqrt(p * mu) / r, 0, 1]);
return [x, y, z]; return {
position: pos.slice(0, 3),
velocity: vel.slice(0, 3),
};
} }
function getOrientation(body, time) { function getOrientation(body, time) {