UNPKG

ootk

Version:

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

183 lines (148 loc) 6.41 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 { DataHandler, Earth, ITRF, J2000, Kilometers, KilometersPerSecond, Vector3D } from '../main.js'; /* eslint-disable class-methods-use-this */ // / Complex Earth gravity model, accounting for EGM-96 zonal, sectoral, and import { Force } from './Force.js'; /** * designed to model the Earth's gravitational field, which is not uniformly distributed due to variations in mass * distribution within the Earth and the Earth's shape (it's not a perfect sphere). To accurately model this complex * field, the gravity model is expanded into a series of spherical harmonics, characterized by their degree and order. * * This `degree` parameter is related to the spatial resolution of the gravity model. A higher degree corresponds to a * finer resolution, capable of representing smaller-scale variations in the gravity field. The degree essentially * denotes how many times the gravitational potential function varies over the surface of the Earth. * * For each degree, there can be multiple orders ranging from 0 up to the degree. The `order` accounts for the * longitudinal variation in the gravity field. Each order within a degree captures different characteristics of the * gravity anomalies. * * `Degree 0` corresponds to the overall, mean gravitational force of the Earth (considered as a point mass). * * `Degree 1` terms are related to the Earth's center of mass but are usually not used because the center of mass is * defined as the origin of the coordinate system. * * `Degree 2` and higher capture the deviations from this spherical symmetry, such as the flattening at the poles and * bulging at the equator (degree 2), and other anomalies at finer scales as the degree increases. */ export class EarthGravity implements Force { degree: number; order: number; _asphericalFlag: boolean; /** * Creates a new instance of the EarthGravity class. * @param degree The degree of the Earth's gravity field. Must be between 0 and 36. * @param order The order of the Earth's gravity field. Must be between 0 and 36. */ constructor(degree: number, order: number) { this.degree = Math.min(Math.max(degree, 0), 36); this.order = Math.min(Math.max(order, 0), 36); this._asphericalFlag = degree >= 2; } _spherical(state: J2000): Vector3D { const rMag = state.position.magnitude(); return state.position.scale(-Earth.mu / (rMag * rMag * rMag)); } // eslint-disable-next-line max-statements _aspherical(state: J2000): Vector3D { const posEcef = state.toITRF().position; const ri = 1.0 / posEcef.magnitude(); const xor = posEcef.x * ri; const yor = posEcef.y * ri; const zor = posEcef.z * ri; const ep = zor; const reor = Earth.radiusEquator * ri; let reorn = reor; const muor2 = Earth.mu * ri * ri; let sumH = 0.0; let sumGm = 0.0; let sumJ = 0.0; let sumK = 0.0; const cTil = new Float64Array(this.order + 4); const sTil = new Float64Array(this.order + 4); const pN = new Float64Array(this.order + 4); const pNm1 = new Float64Array(this.order + 4); const pNm2 = new Float64Array(this.order + 4); pNm2[0] = 1.0; pNm1[0] = ep; pNm1[1] = 1.0; cTil[0] = 1.0; cTil[1] = xor; sTil[1] = yor; const dh = DataHandler.getInstance(); for (let n = 2, nm1 = 1, nm2 = 0, np1 = 3; n <= this.degree; nm2++, nm1++, n++, np1++) { const twonm1 = 2.0 * n - 1.0; reorn *= reor; const cN0 = dh.getEgm96Coeffs(n, 0)[2]; pN[0] = (twonm1 * ep * pNm1[0] - nm1 * pNm2[0]) / n; pN[1] = pNm2[1] + twonm1 * pNm1[0]; pN[2] = pNm2[2] + twonm1 * pNm1[1]; let sumHn = pN[1] * cN0; let sumGmn = pN[0] * cN0 * np1; if (this.order > 0) { let sumJn = 0.0; let sumKn = 0.0; cTil[n] = cTil[1] * cTil[nm1] - sTil[1] * sTil[nm1]; sTil[n] = sTil[1] * cTil[nm1] + cTil[1] * sTil[nm1]; const lim = n < this.order ? n : this.order; for (let m = 1, mm1 = 0, mm2 = -1, mp1 = 2, mp2 = 3; m <= lim; mm2++, mm1++, m++, mp1++, mp2++) { pN[mp1] = pNm2[mp1] + twonm1 * pNm1[m]; const dm = m; const npmp1 = n + mp1; const pNm = pN[m]; const pNmp1 = pN[mp1]; const coefs = dh.getEgm96Coeffs(n, m); const cNm = coefs[2]; const sNm = coefs[3]; const mxPnm = dm * pNm; const bNmtil = cNm * cTil[m] + sNm * sTil[m]; const pNmBnm = pNm * bNmtil; const bNmtm1 = cNm * cTil[mm1] + sNm * sTil[mm1]; const aNmtm1 = cNm * sTil[mm1] - sNm * cTil[mm1]; sumHn += pNmp1 * bNmtil; sumGmn += npmp1 * pNmBnm; sumJn += mxPnm * bNmtm1; sumKn -= mxPnm * aNmtm1; } sumJ += reorn * sumJn; sumK += reorn * sumKn; } sumH += reorn * sumHn; sumGm += reorn * sumGmn; if (n < this.degree) { for (let i = 0; i <= n; i++) { pNm2[i] = pNm1[i]; pNm1[i] = pN[i]; } } } const lambda = sumGm + ep * sumH; const g = new Vector3D<Kilometers>( -muor2 * (lambda * xor - sumJ) as Kilometers, -muor2 * (lambda * yor - sumK) as Kilometers, -muor2 * (lambda * zor - sumH) as Kilometers, ); return new ITRF(state.epoch, g, Vector3D.origin as Vector3D<KilometersPerSecond>).toJ2000().position; } acceleration(state: J2000): Vector3D { let accVec = this._spherical(state); if (this._asphericalFlag) { accVec = accVec.add(this._aspherical(state)); } return accVec; } }