ootk
Version:
Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.
118 lines • 4.66 kB
JavaScript
/**
* @author @thkruz Theodore Kruczek
* @license AGPL-3.0-or-later
* @copyright (c) 2025 Kruczek Labs LLC
*
* Orbital Object ToolKit is free software: you can redistribute it and/or modify it under the
* terms of the GNU Affero General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* Orbital Object ToolKit is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with
* Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
*/
import { copySign, Earth, EpochWindow, J2000, } from '../main.js';
import { CubicSplineInterpolator } from './CubicSplineInterpolator.js';
import { LagrangeInterpolator } from './LagrangeInterpolator.js';
import { StateInterpolator } from './StateInterpolator.js';
/**
* Two-body Velocity Verlet Blend interpolator.
*
* The [VerletBlendInterpolator] retains the original ephemerides, so the
* original _"truth"_ states can be retrieved if needed without imparting any
* additional error, so this can be used to build other interpolator types.
* The implementation is simple and very tolerant when working with sparse
* ephemerides.
*/
export class VerletBlendInterpolator extends StateInterpolator {
ephemeris;
constructor(ephemeris) {
super();
this.ephemeris = ephemeris;
}
get sizeBytes() {
return (64 * 7 * this.ephemeris.length) / 8;
}
window() {
return new EpochWindow(this.ephemeris[0].epoch, this.ephemeris[this.ephemeris.length - 1].epoch);
}
static getClosest_(target, s1, s2) {
return target - s1.epoch.posix >= s2.epoch.posix - target ? s2 : s1;
}
matchState_(epoch) {
const target = epoch.posix;
if (target <= this.ephemeris[0].epoch.posix) {
return this.ephemeris[0];
}
if (target >= this.ephemeris[this.ephemeris.length - 1].epoch.posix) {
return this.ephemeris[this.ephemeris.length - 1];
}
let i = 0;
let j = this.ephemeris.length;
let mid = 0;
while (i < j) {
mid = (i + j) >> 1;
if (this.ephemeris[mid].epoch.posix === target) {
return this.ephemeris[mid];
}
if (target < this.ephemeris[mid].epoch.posix) {
if (mid > 0 && target > this.ephemeris[mid - 1].epoch.posix) {
return VerletBlendInterpolator.getClosest_(target, this.ephemeris[mid - 1], this.ephemeris[mid]);
}
j = mid;
}
else {
if (mid < this.ephemeris.length - 1 && target < this.ephemeris[mid + 1].epoch.posix) {
return VerletBlendInterpolator.getClosest_(target, this.ephemeris[mid], this.ephemeris[mid + 1]);
}
i = mid + 1;
}
}
return this.ephemeris[mid];
}
static _gravity(position) {
const r = position.magnitude();
return position.scale(-Earth.mu / (r * r * r));
}
static integrate_(state, step) {
const x0 = state.position;
const a0 = VerletBlendInterpolator._gravity(x0);
const v0 = state.velocity;
const x1 = x0
.add(v0.scale(step))
.add(a0.scale(0.5 * step * step));
const a1 = VerletBlendInterpolator._gravity(x1);
const v1 = v0
.add(a0.add(a1).scale(0.5 * step));
return new J2000(state.epoch.roll(step), x1, v1);
}
interpolate(epoch) {
if (!this.inWindow(epoch)) {
return null;
}
let state = this.matchState_(epoch);
while (state.epoch.posix !== epoch.posix) {
const delta = epoch.posix - state.epoch.posix;
const stepMag = Math.min(5.0, Math.abs(delta));
const stepSize = copySign(stepMag, delta);
state = VerletBlendInterpolator.integrate_(state, stepSize);
}
return state;
}
getCachedState(epoch) {
if (!this.inWindow(epoch)) {
return null;
}
return this.matchState_(epoch);
}
toCubicSpline() {
return CubicSplineInterpolator.fromEphemeris(this.ephemeris);
}
toLagrange(order = 10) {
return LagrangeInterpolator.fromEphemeris(this.ephemeris, order);
}
}
//# sourceMappingURL=VerletBlendInterpolator.js.map