UNPKG

ootk

Version:

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

222 lines 10 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, Matrix, RelativeState, Vector3D, } from '../main.js'; import { Thrust } from './../force/Thrust.js'; // / Hill Modified Equidistant Cyllindrical _(EQCM)_ coordinates. export class Hill { epoch; position; velocity; semimajorAxis_; meanMotion_; constructor(epoch, position, velocity, semimajorAxis) { this.epoch = epoch; this.position = position; this.velocity = velocity; this.semimajorAxis_ = semimajorAxis; this.meanMotion_ = Earth.smaToMeanMotion(this.semimajorAxis_); } static fromState(origin, radialPosition, intrackPosition, nodeVelocity, nodeOffsetTime) { const a = origin.semimajorAxis; const n = Earth.smaToMeanMotion(a); const yDot = -3.0 * radialPosition * n * 0.5; const z = (nodeVelocity / n) * Math.sin(n * -nodeOffsetTime); const zDot = nodeVelocity * Math.cos(n * -nodeOffsetTime); const r = new Vector3D(radialPosition, intrackPosition, z); const v = new Vector3D(0.0, yDot, zDot); return new Hill(origin.epoch, r, v, a); } static fromNmc(origin, majorAxisRange, nodeVelocity, nodeOffsetTime, translation = 0.0) { const a = origin.semimajorAxis; const n = Earth.smaToMeanMotion(a); const xDot = majorAxisRange * n * 0.5; const z = (nodeVelocity / n) * Math.sin(n * -nodeOffsetTime); const zDot = nodeVelocity * Math.cos(n * -nodeOffsetTime); const r = new Vector3D(0.0, majorAxisRange + translation, z); const v = new Vector3D(xDot, 0.0, zDot); return new Hill(origin.epoch, r, v, a); } static fromPerch(origin, perchRange, nodeVelocity, nodeOffsetTime) { const a = origin.semimajorAxis; const n = Earth.smaToMeanMotion(a); const z = (nodeVelocity / n) * Math.sin(n * -nodeOffsetTime); const zDot = nodeVelocity * Math.cos(n * -nodeOffsetTime); const r = new Vector3D(0.0, perchRange, z); const v = new Vector3D(0.0, 0.0, zDot); return new Hill(origin.epoch, r, v, a); } get semimajorAxis() { return this.semimajorAxis_; } set semimajorAxis(sma) { this.semimajorAxis_ = sma; this.meanMotion_ = Earth.smaToMeanMotion(this.semimajorAxis_); } get meanMotion() { return this.meanMotion_; } toJ2000Matrix(origin, transform) { const magrtgt = origin.position.magnitude(); const magrint = magrtgt + this.position.x; const vtgtrsw = transform.multiplyVector3D(origin.velocity); const lambdadottgt = vtgtrsw.y / magrtgt; const lambdaint = this.position.y / magrtgt; const phiint = this.position.z / magrtgt; const sinphiint = Math.sin(phiint); const cosphiint = Math.cos(phiint); const sinlambdaint = Math.sin(lambdaint); const coslambdaint = Math.cos(lambdaint); const rotRswSez = new Matrix([ [sinphiint * coslambdaint, sinphiint * sinlambdaint, -cosphiint], [-sinlambdaint, coslambdaint, 0], [cosphiint * coslambdaint, cosphiint * sinlambdaint, sinphiint], ]); const rdotint = this.velocity.x + vtgtrsw.x; const lambdadotint = this.velocity.y / magrtgt + lambdadottgt; const phidotint = this.velocity.z / magrtgt; const vintsez = new Vector3D(-magrint * phidotint, magrint * lambdadotint * cosphiint, rdotint); const vintrsw = rotRswSez.transpose().multiplyVector3D(vintsez); const vinteci = transform.transpose().multiplyVector3D(vintrsw); const rintrsw = new Vector3D(cosphiint * magrint * coslambdaint, cosphiint * magrint * sinlambdaint, sinphiint * magrint); const rinteci = transform.transpose().multiplyVector3D(rintrsw); return new J2000(origin.epoch, rinteci, vinteci); } toJ2000(origin) { return this.toJ2000Matrix(origin, RelativeState.createMatrix(origin.position, origin.velocity)); } static transitionMatrix(t, meanMotion) { const n = meanMotion; const sn = Math.sin(n * t); const cs = Math.cos(n * t); return new Matrix([ [4.0 - 3.0 * cs, 0.0, 0.0, sn / n, (2.0 * (1.0 - cs)) / n, 0.0], [6.0 * (sn - n * t), 1.0, 0.0, (-2.0 * (1.0 - cs)) / n, (4.0 * sn - 3.0 * n * t) / n, 0.0], [0.0, 0.0, cs, 0.0, 0.0, sn / n], [3.0 * n * sn, 0.0, 0.0, cs, 2.0 * sn, 0.0], [-6.0 * n * (1.0 - cs), 0.0, 0.0, -2.0 * sn, 4.0 * cs - 3.0, 0.0], [0.0, 0.0, -n * sn, 0.0, 0.0, cs], ]); } transition(t) { const sysMat = Hill.transitionMatrix(t, this.meanMotion_); const res = sysMat.multiplyVector(this.position.join(this.velocity)).elements; return new Hill(this.epoch.roll(t), new Vector3D(res[0], res[1], res[2]), new Vector3D(res[3], res[4], res[5]), this.semimajorAxis_); } transitionWithMatrix(stm, t) { const res = stm.multiplyVector(this.position.join(this.velocity)).elements; return new Hill(this.epoch.roll(t), new Vector3D(res[0], res[1], res[2]), new Vector3D(res[3], res[4], res[5]), this.semimajorAxis_); } propagate(newEpoch) { return this.transition(newEpoch.difference(this.epoch)); } propagateWithMatrix(stm, newEpoch) { return this.transitionWithMatrix(stm, newEpoch.difference(this.epoch)); } maneuver(maneuver) { const state = this.propagate(maneuver.center); return new Hill(state.epoch, state.position, state.velocity.add(maneuver.deltaV), state.semimajorAxis_); } ephemeris(start, stop, step = 60.0) { const output = []; let current = start; while (stop >= current) { output.push(this.propagate(current)); current = current.roll(step); } return output; } get period() { return (2 * Math.PI) / this.meanMotion_; } nextRadialTangent() { const x = this.position.x; const xDot = this.velocity.x; const yDot = this.velocity.y; let t = Math.atan(-xDot / (3.0 * this.meanMotion_ * x + 2.0 * yDot)) / this.meanMotion_; if (t <= 0) { t = t + 0.5 * this.period; } else if (isNaN(t)) { t = 0.5 * this.period; } return this.propagate(this.epoch.roll(t)); } solveManeuver(waypoint, ignoreCrosstrack = false) { const t = waypoint.epoch.difference(this.epoch); const w = waypoint.relativePosition; const sysMat = Hill.transitionMatrix(t, this.meanMotion_); const posEquationMat = new Matrix([ [sysMat.elements[0][0], sysMat.elements[0][1], sysMat.elements[0][2]], [sysMat.elements[1][0], sysMat.elements[1][1], sysMat.elements[1][2]], [sysMat.elements[2][0], sysMat.elements[2][1], sysMat.elements[2][2]], ]); const solnVector = w .subtract(posEquationMat.multiplyVector3D(this.position)); const velEquationMat = new Matrix([ [sysMat.elements[0][3], sysMat.elements[0][4], sysMat.elements[0][5]], [sysMat.elements[1][3], sysMat.elements[1][4], sysMat.elements[1][5]], [sysMat.elements[2][3], sysMat.elements[2][4], sysMat.elements[2][5]], ]); let result = velEquationMat .inverse() .multiplyVector3D(solnVector) .subtract(this.velocity); if (ignoreCrosstrack) { result = new Vector3D(result.x, result.y, 0); } return new Thrust(this.epoch, result.x * 1000, result.y * 1000, result.z * 1000); } maneuverSequence(pivot, waypoints, preManeuvers = [], postManeuvers = []) { let state = new Hill(this.epoch, this.position, this.velocity, this.semimajorAxis_); preManeuvers = preManeuvers.slice(); postManeuvers = postManeuvers.slice(); let output = preManeuvers; // Note difference was once compareTo output.sort((a, b) => a.center.difference(b.center)); output = output.filter((mvr) => mvr.center >= this.epoch && mvr.center >= pivot); for (const mvr of output) { state = state.maneuver(mvr); } state = state.propagate(pivot); for (const wpt of waypoints) { const mvr = state.solveManeuver(wpt); state = state.maneuver(mvr); output.push(mvr); } output.push(...postManeuvers); return output; } maneuverOrigin(maneuver) { const state = this.propagate(maneuver.center); const vInit = Math.sqrt(Earth.mu / this.semimajorAxis_); const vFinal = vInit - maneuver.intrack * 1e-3; const aFinal = Earth.mu / (vFinal * vFinal); return new Hill(state.epoch, state.position, state.velocity.subtract(maneuver.deltaV), aFinal); } get name() { return 'Hill'; } toString() { return [ `[${this.name}]`, ` Epoch: ${this.epoch}`, ` Position: ${this.position.toString(6)} km`, ` Velocity: ${this.velocity.toString(9)} km/s`, ].join('\n'); } } //# sourceMappingURL=Hill.js.map