ootk
Version:
Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.
83 lines • 4.21 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 { Earth, J2000, RadecTopocentric, Vector3D } from '../main.js';
import { ForceModel } from '../force/ForceModel.js';
import { RungeKutta89Propagator } from '../propagator/RungeKutta89Propagator.js';
import { DownhillSimplex } from './../optimize/DownhillSimplex.js';
import { GoodingIOD } from './GoodingIOD.js';
/**
* Gooding angles-only initial orbit determination.
*
* Used for orbit determination from multiple optical observations.
*/
export class ModifiedGoodingIOD {
observations_;
mu_;
constructor(observations, mu = Earth.mu) {
this.observations_ = observations;
this.mu_ = mu;
}
createInitial_(r0, rN, nRev, direction) {
const iod = new GoodingIOD(this.observations_[0], this.observations_[Math.floor(this.observations_.length / 2)], this.observations_[this.observations_.length - 1], this.mu_);
return iod.solve(r0, rN, nRev, direction);
}
_createErrorFunction(aprioriEpoch) {
const forceModel = new ForceModel().setGravity(this.mu_);
const scoreFn = (x) => {
const position = new Vector3D(x[0], x[1], x[2]);
const velocity = new Vector3D(x[3], x[4], x[5]);
const state = new J2000(aprioriEpoch, position, velocity);
const propagator = new RungeKutta89Propagator(state, forceModel);
let total = 0;
for (const oC of this.observations_) {
const sC = propagator.propagate(oC.epoch);
const pC = oC.site;
const expected = oC.observation.lineOfSight();
const actual = RadecTopocentric.fromStateVector(sC, pC).lineOfSight();
const error = expected.angle(actual);
total += error;
}
return total;
};
return scoreFn;
}
solve(r0, rN, { nRev = 0, direction = true, posSearch = 10.0, velSearch = 0.1, tolerance = 1e-6, printIter = false, }) {
if (this.observations_.length < 3) {
throw new Error('At least 3 observations required for Gooding IOD.');
}
const init = this.createInitial_(r0, rN, nRev, direction);
const guess = Float64Array.from([...init.position.toArray(), ...init.velocity.toArray()]);
const solveFn = this._createErrorFunction(init.epoch);
const simplex = [
Float64Array.from(guess),
Float64Array.from([guess[0] + posSearch, guess[1], guess[2], guess[3], guess[4], guess[5]]),
Float64Array.from([guess[0], guess[1] + posSearch, guess[2], guess[3], guess[4], guess[5]]),
Float64Array.from([guess[0], guess[1], guess[2] + posSearch, guess[3], guess[4], guess[5]]),
Float64Array.from([guess[0], guess[1], guess[2], guess[3] + velSearch, guess[4], guess[5]]),
Float64Array.from([guess[0], guess[1], guess[2], guess[3], guess[4] + velSearch, guess[5]]),
Float64Array.from([guess[0], guess[1], guess[2], guess[3], guess[4], guess[5] + velSearch]),
];
const result = DownhillSimplex.solveSimplex(solveFn, simplex, {
adaptive: true,
xTolerance: tolerance,
fTolerance: tolerance,
printIter,
});
return new J2000(init.epoch, new Vector3D(result[0], result[1], result[2]), new Vector3D(result[3], result[4], result[5]));
}
}
//# sourceMappingURL=ModifiedGoodingIOD.js.map