UNPKG

ootk

Version:

Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.

83 lines 4.21 kB
/** * @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