ootk
Version:
Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.
162 lines • 6.4 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 { EpochUTC } from '../main.js';
import { VerletBlendInterpolator } from '../interpolator/VerletBlendInterpolator.js';
import { GoldenSection } from './../optimize/GoldenSection.js';
// Propagator base class.
export class Propagator {
/*
* Generate a [VerletBlendInterpolator] containing ephemeris over the
* [start] and [stop] propagation period, with an optional
* ephemeris [interval].
*/
ephemeris(start, stop, interval = 60.0) {
const output = [this.propagate(start)];
let tempEpoch = start;
while (tempEpoch <= stop) {
tempEpoch = tempEpoch.roll(interval);
output.push(this.propagate(tempEpoch));
}
return new VerletBlendInterpolator(output);
}
/*
* Generate a list of [J2000] states integrating over a maneuver.
*
* If the maneuver is impulsive, the list will only contain a single state.
*/
maneuver(maneuver, interval = 60.0) {
const output = [];
const tempEpoch = maneuver.start;
const stop = maneuver.stop;
output.push(this.propagate(tempEpoch));
let current = tempEpoch;
while (current <= stop) {
current = current.roll(interval);
output.push(this.propagate(current));
}
return output;
}
/*
* Generate a [VerletBlendInterpolator] containing maneuver ephemeris over
* the [start] and [finish] interval, with an optional ephemeris [interval].
*/
ephemerisManeuver(start, finish, maneuvers, interval = 60.0) {
const output = [];
const tempEpoch = start;
output.push(this.propagate(tempEpoch));
const stop = finish;
let current = tempEpoch;
while (current <= stop) {
current = current.roll(interval);
output.push(this.propagate(current));
}
return new VerletBlendInterpolator(output);
}
// Return the epoch of the ascending node after the [start] epoch.
ascendingNodeEpoch(start) {
const period = this.state.period / 60;
const step = period / 8;
let current = start;
const stop = current.roll(period);
this.propagate(current);
let previous = this.state.position.z;
while (current <= stop) {
current = current.roll(step);
this.propagate(current);
if (Math.sign(this.state.position.z) === Math.sign(-previous) && this.state.velocity.z > 0) {
break;
}
previous = this.state.position.z;
}
const t = GoldenSection.search((x) => {
this.propagate(new EpochUTC(x));
return Math.abs(this.state.position.z);
}, current.posix - step, current.posix, { tolerance: 1e-3 });
return new EpochUTC(t);
}
// Return the epoch of the descending node after the [start] epoch.
descendingNodeEpoch(start) {
const period = this.state.period / 60;
const step = period / 8;
let current = start;
const stop = current.roll(period);
this.propagate(current);
let previous = this.state.position.z;
while (current <= stop) {
current = current.roll(step);
this.propagate(current);
if (Math.sign(this.state.position.z) === Math.sign(-previous) && this.state.velocity.z < 0) {
break;
}
previous = this.state.position.z;
}
const t = GoldenSection.search((x) => {
this.propagate(new EpochUTC(x));
return Math.abs(this.state.position.z);
}, current.posix - step, current.posix, { tolerance: 1e-3 });
return new EpochUTC(t);
}
// Return the epoch of apogee after the [start] epoch.
apogeeEpoch(start) {
const slice = 8;
const period = this.state.period / 60;
const step = period / slice;
let current = start;
this.propagate(current);
let tCache = current;
let rCache = this.state.position.magnitude();
for (let i = 0; i < slice; i++) {
current = current.roll(step);
const t = new EpochUTC(GoldenSection.search((x) => {
this.propagate(new EpochUTC(x));
return this.state.position.magnitude();
}, current.posix - step, current.posix, { tolerance: 1e-3, solveMax: true }));
this.propagate(t);
const r = this.state.position.magnitude();
if (r > rCache) {
tCache = t;
rCache = r;
}
}
return tCache;
}
// Return the epoch of perigee after the [start] epoch.
perigeeEpoch(start) {
const slice = 8;
const period = this.state.period / 60;
const step = period / slice;
let current = start;
this.propagate(current);
let tCache = current;
let rCache = this.state.position.magnitude();
for (let i = 0; i < slice; i++) {
current = current.roll(step);
const t = new EpochUTC(GoldenSection.search((x) => {
this.propagate(new EpochUTC(x));
return this.state.position.magnitude();
}, current.posix - step, current.posix, { tolerance: 1e-3, solveMax: false }));
this.propagate(t);
const r = this.state.position.magnitude();
if (r < rCache) {
tCache = t;
rCache = r;
}
}
return tCache;
}
}
//# sourceMappingURL=Propagator.js.map