ootk
Version:
Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.
247 lines • 9.26 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, 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