ootk
Version:
Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.
100 lines (89 loc) • 3.17 kB
text/typescript
/**
* @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 {
DataHandler,
DEG2RAD,
Earth,
ITRF,
J2000,
Kilometers,
KilometersPerSecond,
Radians,
Sun,
Vector3D,
} from '../main.js';
import { Force } from './Force.js';
/**
* Harris-Priester atmospheric drag force model.
* Atmospheric density model assumes mean solar flux.
*/
export class AtmosphericDrag implements Force {
mass: number;
area: number;
dragCoeff: number;
cosine: number;
constructor(mass: number, area: number, dragCoeff: number, cosine: number) {
this.mass = mass;
this.area = area;
this.dragCoeff = dragCoeff;
this.cosine = cosine;
}
private static _getHPDensity(state: ITRF, n: number): number {
const hpa = DataHandler.getInstance().getHpAtmosphere(state.height);
if (hpa === null) {
return 0.0;
}
const sunPos = Sun.positionApparent(state.epoch);
const sunVec = new J2000(
state.epoch,
sunPos,
Vector3D.origin as Vector3D<KilometersPerSecond>,
).toITRF().position.normalize();
const bulVec = sunVec.rotZ(-30.0 * DEG2RAD as Radians);
const cosPsi = bulVec.normalize().dot(state.position.normalize());
const c2Psi2 = 0.5 * (1.0 + cosPsi);
const cPsi2 = Math.sqrt(c2Psi2);
const cosPow = cPsi2 > 1e-12 ? c2Psi2 * cPsi2 ** (n - 2) : 0.0;
const altitude = hpa.height;
const [h0, min0, max0] = hpa.hp0;
const [h1, min1, max1] = hpa.hp1;
const dH = (h0 - altitude) / (h0 - h1);
const rhoMin = min0 * (min1 / min0) ** dH;
if (cosPow === 0) {
return rhoMin;
}
const rhoMax = max0 * (max1 / max0) ** dH;
return rhoMin + (rhoMax - rhoMin) * cosPow;
}
acceleration(state: J2000): Vector3D {
const itrfState = state.toITRF();
const density = AtmosphericDrag._getHPDensity(itrfState, this.cosine);
if (density === 0) {
return Vector3D.origin;
}
const rotation = new ITRF(
state.epoch,
Earth.rotation as unknown as Vector3D<Kilometers>,
Vector3D.origin as Vector3D<KilometersPerSecond>,
).toJ2000().position;
const vRel = state.velocity.subtract(rotation.cross(state.position) as unknown as Vector3D<KilometersPerSecond>)
.scale(1000.0 as KilometersPerSecond);
const vm = vRel.magnitude();
const fScale = -0.5 * density * ((this.dragCoeff * this.area) / this.mass) * vm;
return vRel.scale(fScale / 1000.0);
}
}