UNPKG

ootk

Version:

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

247 lines 9.26 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, TAU, Vector3D } from '../main.js'; import { ForceModel } from '../force/ForceModel.js'; import { RungeKutta89Propagator } from '../propagator/RungeKutta89Propagator.js'; import { GibbsIOD } from './GibbsIOD.js'; import { LambertIOD } from './LambertIOD.js'; /** * Gooding angles-only initial orbit determination. * * Used for orbit determination from three optical observations. */ export class GoodingIOD { _mu; o1_; o2_; o3_; vObserverPosition1_ = Vector3D.origin; vObserverPosition2_ = Vector3D.origin; vObserverPosition3_ = Vector3D.origin; r_ = 0.0; v_ = 0.0; t_ = 0.0; r1_ = 0.0; r2_ = 0.0; r3_ = 0.0; rho1_ = 0.0; rho2_ = 0.0; rho3_ = 0.0; d1_ = 0.0; d3_ = 0.0; facFiniteDiff_ = 0.0; _forceModel = new ForceModel().setGravity(1.0); constructor(o1, o2, o3, mu = Earth.mu) { this._mu = mu; this.o1_ = o1; this.o2_ = o2; this.o3_ = o3; } _getPositionOnLoS2({ e1, r01, e3, r03, t13, t12, nRev, posigrade, }) { const p1 = this.vObserverPosition1_.add(e1.scale(r01)); this.r1_ = p1.magnitude(); const p3 = this.vObserverPosition3_.add(e3.scale(r03)); this.r3_ = p3.magnitude(); const p13 = p1.cross(p3); let th = Math.atan2(p13.magnitude(), p1.dot(p3)); if (!posigrade) { th = TAU - th; } const v1 = new Float64Array(2); const exitflag = LambertIOD.solve(this.r1_, this.r3_, th, t13, nRev, v1); if (exitflag) { const pn = p1.cross(p3); const pt = pn.cross(p1); let rt = pt.magnitude(); if (!posigrade) { rt = -rt; } const vel1 = p1.scale(v1[0] / this.r1_).add(pt.scale(v1[1] / rt)); const p2 = new RungeKutta89Propagator(new J2000(this.o1_.epoch, p1, vel1), this._forceModel).propagate(this.o1_.epoch.roll(t12)).position; return p2; } return null; } _modifyIterate(lineOfSight1, lineOfSight3) { const r13 = this.vObserverPosition3_.subtract(this.vObserverPosition1_); this.d1_ = r13.dot(lineOfSight1); this.d3_ = r13.dot(lineOfSight3); const d2 = lineOfSight1.dot(lineOfSight3); const d4 = 1.0 - d2 * d2; this.rho1_ = Math.max((this.d1_ - this.d3_ * d2) / d4, 0.0); this.rho3_ = Math.max((this.d1_ * d2 - this.d3_) / d4, 0.0); } _computeDerivatives({ x, y, lineOfSight1, lineOfSight3, pin, ein, t13, t12, nrev, direction, fd, gd, }) { const p = pin.normalize(); const en = ein.normalize(); const dx = this.facFiniteDiff_ * x; const dy = this.facFiniteDiff_ * y; const cm1 = this._getPositionOnLoS2({ e1: lineOfSight1, r01: x - dx, e3: lineOfSight3, r03: y, t13, t12, nRev: nrev, posigrade: direction, }).subtract(this.vObserverPosition2_); const fm1 = p.dot(cm1); const gm1 = en.dot(cm1); const cp1 = this._getPositionOnLoS2({ e1: lineOfSight1, r01: x + dx, e3: lineOfSight3, r03: y, t13, t12, nRev: nrev, posigrade: direction, }).subtract(this.vObserverPosition2_); const fp1 = p.dot(cp1); const gp1 = en.dot(cp1); const fx = (fp1 - fm1) / (2.0 * dx); const gx = (gp1 - gm1) / (2.0 * dx); const cm3 = this._getPositionOnLoS2({ e1: lineOfSight1, r01: x, e3: lineOfSight3, r03: y - dy, t13, t12, nRev: nrev, posigrade: direction, }).subtract(this.vObserverPosition2_); const fm3 = p.dot(cm3); const gm3 = en.dot(cm3); const cp3 = this._getPositionOnLoS2({ e1: lineOfSight1, r01: x, e3: lineOfSight3, r03: y + dy, t13, t12, nRev: nrev, posigrade: direction, }).subtract(this.vObserverPosition2_); const fp3 = p.dot(cp3); const gp3 = en.dot(cp3); const fy = (fp3 - fm3) / (2.0 * dy); const gy = (gp3 - gm3) / (2.0 * dy); fd[0] = fx; fd[1] = fy; gd[0] = gx; gd[1] = gy; } solve(r1Init, r3Init, nRev = 0, direction = true) { const lineOfSight1 = this.o1_.observation.lineOfSight(); const lineOfSight2 = this.o2_.observation.lineOfSight(); const lineOfSight3 = this.o3_.observation.lineOfSight(); this.r_ = Math.max(r1Init, r3Init); this.v_ = Math.sqrt(this._mu / this.r_); this.t_ = this.r_ / this.v_; this.vObserverPosition1_ = this.o1_.site.position.scale(1.0 / this.r_); this.vObserverPosition2_ = this.o2_.site.position.scale(1.0 / this.r_); this.vObserverPosition3_ = this.o3_.site.position.scale(1.0 / this.r_); const maxiter = 100; this._solveRangeProblem({ rho1init: r1Init / this.r_, rho3init: r3Init / this.r_, t13: this.o3_.epoch.difference(this.o1_.epoch) / this.t_, t12: this.o2_.epoch.difference(this.o1_.epoch) / this.t_, nrev: nRev, direction, lineOfSight1, lineOfSight2, lineOfSight3, maxIterations: maxiter, }); const gibbs = new GibbsIOD(this._mu); const p1 = this.vObserverPosition1_.add(lineOfSight1.scale(this.rho1_)).scale(this.r_); const p2 = this.vObserverPosition2_.add(lineOfSight2.scale(this.rho2_)).scale(this.r_); const p3 = this.vObserverPosition3_.add(lineOfSight3.scale(this.rho3_)).scale(this.r_); return gibbs.solve(p1, p2, p3, this.o2_.epoch, this.o3_.epoch); } _solveRangeProblem({ rho1init, rho3init, t13, t12, nrev, direction, lineOfSight1, lineOfSight2, lineOfSight3, maxIterations, }) { const arbf = 1e-6; const cvtol = 1e-14; this.rho1_ = rho1init; this.rho3_ = rho3init; let iter = 0; let stoppingCriterion = 10.0 * cvtol; while (iter < maxIterations && Math.abs(stoppingCriterion) > cvtol) { this.facFiniteDiff_ = arbf; const p2 = this._getPositionOnLoS2({ e1: lineOfSight1, r01: this.rho1_, e3: lineOfSight3, r03: this.rho3_, t13, t12, nRev: nrev, posigrade: direction, }); if (p2 === null) { this._modifyIterate(lineOfSight1, lineOfSight3); } else { this.r2_ = p2.magnitude(); const c = p2.subtract(this.vObserverPosition2_); this.rho2_ = c.magnitude(); const cr = lineOfSight2.dot(c); const u = lineOfSight2.cross(c); const p = u.cross(lineOfSight2).normalize(); const ent = lineOfSight2.cross(p); const enr = ent.magnitude(); if (enr === 0.0) { return; } const en = ent.normalize(); const fc = p.dot(c); const fd = new Float64Array(2); const gd = new Float64Array(2); this._computeDerivatives({ x: this.rho1_, y: this.rho3_, lineOfSight1, lineOfSight3, pin: p, ein: en, t13, t12, nrev, direction, fd, gd, }); const fr1 = fd[0]; const fr3 = fd[1]; const gr1 = gd[0]; const gr3 = gd[1]; const detj = fr1 * gr3 - fr3 * gr1; this.d3_ = (-gr3 * fc) / detj; this.d1_ = (gr1 * fc) / detj; this.rho1_ = this.rho1_ + this.d3_; this.rho3_ = this.rho3_ + this.d1_; const den = Math.max(cr, this.r2_); stoppingCriterion = fc / den; } ++iter; } } } //# sourceMappingURL=GoodingIOD.js.map