UNPKG

ootk

Version:

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

213 lines 7.75 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 } from '../main.js'; // / Lambert two-position and time initial orbit determination. export class LambertIOD { mu; constructor(mu = Earth.mu) { this.mu = mu; // Nothing to do here. } /** * Try to guess the short path argument given an [interceptor] and * [target] state. * @param interceptor Interceptor * @param target Target * @returns True if the short path should be used, false otherwise. */ static useShortPath(interceptor, target) { const transN = interceptor.position.cross(target.position); const h = interceptor.position.cross(interceptor.velocity); return h.dot(transN) >= 0; } static timeOfFlight_(x, longway, mrev, minSma, speri, chord) { const a = minSma / (1.0 - x * x); let tof; if (Math.abs(x) < 1) { const beta = longway * 2.0 * Math.asin(Math.sqrt((speri - chord) / (2.0 * a))); const alpha = 2.0 * Math.acos(x); tof = a * Math.sqrt(a) * (alpha - Math.sin(alpha) - (beta - Math.sin(beta)) + 2.0 * Math.PI * mrev); } else { const alpha = 2.0 * Math.acosh(x); const beta = longway * 2.0 * Math.asinh(Math.sqrt((speri - chord) / (-2.0 * a))); tof = -a * Math.sqrt(-a) * (Math.sinh(alpha) - alpha - (Math.sinh(beta) - beta)); } return tof; } /** * Attempt to solve output velocity [v1] _(km/s)_ given radii [r1] and * [r2] _(canonical)_, sweep angle [dth] _(rad)_, time of flight [tau] * _(canonical)_, and number of revolutions _(mRev)_. * @param r1 Radius 1 * @param r2 Radius 2 * @param dth Sweep angle * @param tau Time of flight * @param mRev Number of revolutions * @param v1 Output velocity * @returns True if successful, false otherwise. */ static solve(r1, r2, dth, tau, mRev, v1) { const leftBranch = dth < Math.PI; let longway = 1; if (dth > Math.PI) { longway = -1; } const m = Math.abs(mRev); const rtof = Math.abs(tau); const theta = dth; const chord = Math.sqrt(r1 * r1 + r2 * r2 - 2.0 * r1 * r2 * Math.cos(theta)); const speri = 0.5 * (r1 + r2 + chord); const minSma = 0.5 * speri; const lambda = longway * Math.sqrt(1.0 - chord / speri); const logt = Math.log(rtof); let in1; let in2; let x1; let x2; if (m === 0) { in1 = -0.6523333; in2 = 0.6523333; x1 = Math.log(1.0 + in1); x2 = Math.log(1.0 + in2); } else { if (!leftBranch) { in1 = -0.523334; in2 = -0.223334; } else { in1 = 0.723334; in2 = 0.523334; } x1 = Math.tan((in1 * Math.PI) / 2); x2 = Math.tan((in2 * Math.PI) / 2); } const tof1 = LambertIOD.timeOfFlight_(in1, longway, m, minSma, speri, chord); const tof2 = LambertIOD.timeOfFlight_(in2, longway, m, minSma, speri, chord); let y1; let y2; if (m === 0) { y1 = Math.log(tof1) - logt; y2 = Math.log(tof2) - logt; } else { y1 = tof1 - rtof; y2 = tof2 - rtof; } let err = 1e20; let iterations = 0; const tol = 1e-13; const maxiter = 50; let xnew = 0.0; while (err > tol && iterations < maxiter) { xnew = (x1 * y2 - y1 * x2) / (y2 - y1); let xt; if (m === 0) { xt = Math.exp(xnew) - 1.0; } else { xt = (Math.atan(xnew) * 2.0) / Math.PI; } const tof = LambertIOD.timeOfFlight_(xt, longway, m, minSma, speri, chord); let ynew; if (m === 0) { ynew = Math.log(tof) - logt; } else { ynew = tof - rtof; } x1 = x2; x2 = xnew; y1 = y2; y2 = ynew; err = Math.abs(x1 - xnew); ++iterations; } if (err > tol) { return false; } let x; if (m === 0) { x = Math.exp(xnew) - 1.0; } else { x = (Math.atan(xnew) * 2.0) / Math.PI; } const sma = minSma / (1.0 - x * x); let eta; if (x < 1) { const alfa = 2.0 * Math.acos(x); const beta = longway * 2.0 * Math.asin(Math.sqrt((speri - chord) / (2.0 * sma))); const psi = (alfa - beta) / 2.0; const sinPsi = Math.sin(psi); const etaSq = (2.0 * sma * sinPsi * sinPsi) / speri; eta = Math.sqrt(etaSq); } else { const gamma = 2.0 * Math.acosh(x); const delta = longway * 2.0 * Math.asinh(Math.sqrt((chord - speri) / (2.0 * sma))); const psi = (gamma - delta) / 2.0; const sinhPsi = Math.sinh(psi); const etaSq = (-2.0 * sma * sinhPsi * sinhPsi) / speri; eta = Math.sqrt(etaSq); } const vr1 = (1.0 / eta) * Math.sqrt(1.0 / minSma) * ((2.0 * lambda * minSma) / r1 - (lambda + x * eta)); const vt1 = (1.0 / eta) * Math.sqrt(1.0 / minSma) * Math.sqrt(r2 / r1) * Math.sin(dth / 2.0); v1[0] = vr1; v1[1] = vt1; return true; } /** * Estimate a state vector for inertial position [p1] _(km)_ given the * two epoch and positions. * @param p1 Position vector 1 * @param p2 Position vector 2 * @param t1 Epoch 1 * @param t2 Epoch 2 * @param root0 Optional parameters * @param root0.posigrade If true, use the positive root (default: true) * @param root0.nRev Number of revolutions (default: 0) * @returns A [J2000] object with the estimated state vector. */ estimate(p1, p2, t1, t2, { posigrade = true, nRev = 0 } = {}) { const r1 = p1.magnitude(); const r2 = p2.magnitude(); const tof = t2.difference(t1); const r = Math.max(r1, r2); const v = Math.sqrt(this.mu / r); const t = r / v; let dth = p1.angle(p2); if (!posigrade) { dth = 2 * Math.PI - dth; } const vDep = new Float64Array(2); const exitFlag = LambertIOD.solve(r1 / r, r2 / r, dth, tof / t, nRev, vDep); if (exitFlag) { const pn = p1.cross(p2); const pt = pn.cross(p1); let rt = pt.magnitude(); if (!posigrade) { rt = -rt; } const vel1 = p1.scale((v * vDep[0]) / r1).add(pt.scale((v * vDep[1]) / rt)); return new J2000(t1, p1, vel1); } return null; } } //# sourceMappingURL=LambertIOD.js.map