UNPKG

ootk

Version:

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

238 lines 9.5 kB
/** * @author @thkruz Theodore Kruczek * @description Orbital Object ToolKit (ootk) is a collection of tools for working * with satellites and other orbital objects. * @license AGPL-3.0-or-later * @copyright (c) 2025 Kruczek Labs LLC * * Many of the classes are based off of the work of @david-rc-dayton and his * Pious Squid library (https://github.com/david-rc-dayton/pious_squid) which * is licensed under the MIT license. * * 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 { PassType } from '../enums/PassType.js'; import { SpaceObjectType } from '../types/types.js'; import { GroundObject } from './GroundObject.js'; import { calcGmst, lla2eci, J2000, Vector3D, EpochUTC } from '../main.js'; export class Sensor extends GroundObject { minRng; minAz; minEl; maxRng; maxAz; maxEl; minRng2; minAz2; minEl2; maxRng2; maxAz2; maxEl2; constructor(info) { // If there is a sensor type verify it is valid if (info.type) { switch (info.type) { case SpaceObjectType.OPTICAL: case SpaceObjectType.MECHANICAL: case SpaceObjectType.PHASED_ARRAY_RADAR: case SpaceObjectType.OBSERVER: case SpaceObjectType.BISTATIC_RADIO_TELESCOPE: case SpaceObjectType.SHORT_TERM_FENCE: break; default: throw new Error('Invalid sensor type'); } } super(info); this.validateSensorInputData_(info); this.minRng = info.minRng; this.minAz = info.minAz; this.minEl = info.minEl; this.maxRng = info.maxRng; this.maxAz = info.maxAz; this.maxEl = info.maxEl; this.minRng2 = info.minRng2; this.minAz2 = info.minAz2; this.minEl2 = info.minEl2; this.maxRng2 = info.maxRng2; this.maxAz2 = info.maxAz2; this.maxEl2 = info.maxEl2; } /** * Checks if the object is a sensor. * @returns True if the object is a sensor, false otherwise. */ isSensor() { return true; } calculatePasses(planningInterval, sat, date = new Date()) { let isInViewLast = false; let maxElThisPass = 0; const msnPlanPasses = []; const startTime = date.getTime(); for (let timeOffset = 0; timeOffset < planningInterval; timeOffset++) { const curTime = new Date(startTime + timeOffset * 1000); const rae = this.rae(sat, curTime); if (!rae) { continue; } const isInView = this.isRaeInFov(rae); if (timeOffset === 0) { // Propagate Backwards to get the previous pass const oldRae = this.rae(sat, new Date(date.getTime() - 1 * 1000)); if (!oldRae) { continue; } isInViewLast = this.isRaeInFov(oldRae); } const type = Sensor.getPassType_(isInView, isInViewLast); maxElThisPass = Math.max(maxElThisPass, rae.el); if (type === PassType.ENTER || type === PassType.EXIT) { const pass = { type, time: curTime, az: rae.az, el: rae.el, rng: rae.rng, }; // Only set maxEl for EXIT passes if (type === PassType.EXIT) { pass.maxElPass = maxElThisPass; } msnPlanPasses.push(pass); maxElThisPass = 0; } isInViewLast = isInView; } return msnPlanPasses; } /** * Checks if the given RAE vector is within the field of view of the sensor. * @param rae - The RAE vector to check. * @returns True if the RAE vector is within the field of view, false otherwise. */ isRaeInFov(rae) { if (rae.el < this.minEl || rae.el > this.maxEl) { return false; } if (rae.rng < this.minRng || rae.rng > this.maxRng) { return false; } if (this.minAz > this.maxAz) { // North Facing Sensors if (rae.az < this.minAz && rae.az > this.maxAz) { return false; } // Normal Facing Sensors } else if (rae.az < this.minAz || rae.az > this.maxAz) { return false; } return true; } /** * Checks if a satellite is in the field of view (FOV) of the sensor. * @param sat - The satellite to check. * @param date - The date to use for the calculation. Defaults to the current date. * @returns A boolean indicating whether the satellite is in the FOV. */ isSatInFov(sat, date = new Date()) { const rae = this.rae(sat, date); if (!rae) { return false; } return this.isRaeInFov(rae); } /** * Checks if the sensor is in deep space. * @returns True if the sensor is in deep space, false otherwise. */ isDeepSpace() { return this.maxRng > 6000; } /** * Checks if the sensor is near Earth. * @returns True if the sensor is near Earth, false otherwise. */ isNearEarth() { return this.maxRng <= 6000; } toJ2000(date = new Date()) { const gmst = calcGmst(date).gmst; const position = lla2eci(this.llaRad(), gmst); return new J2000(EpochUTC.fromDateTime(date), new Vector3D(position.x, position.y, position.z), new Vector3D(0, 0, 0)); } /** * Returns the pass type based on the current and previous visibility states. * @param isInView - Indicates if the object is currently in view. * @param isInViewLast - Indicates if the object was in view in the previous state. * @returns The pass type. */ static getPassType_(isInView, isInViewLast) { let type = PassType.OUT_OF_VIEW; if (isInView && !isInViewLast) { type = PassType.ENTER; } else if (!isInView && isInViewLast) { type = PassType.EXIT; } else if (isInView && isInViewLast) { type = PassType.IN_VIEW; } return type; } /** * Validates the field of view (FOV) parameters of the sensor. * @param info - The sensor parameters. */ validateFov_(info) { this.validateParameter(info.maxAz, 0, 360, 'Invalid maximum azimuth - must be between 0 and 360'); this.validateParameter(info.minAz, 0, 360, 'Invalid maximum azimuth - must be between 0 and 360'); this.validateParameter(info.maxEl, -15, 180, 'Invalid maximum elevation - must be between 0 and 180'); this.validateParameter(info.minEl, -15, 90, 'Invalid minimum elevation - must be between 0 and 90'); this.validateParameter(info.maxRng, 0, null, 'Invalid maximum range - must be greater than 0'); this.validateParameter(info.minRng, 0, null, 'Invalid minimum range - must be greater than 0'); } /** * Validates the field of view parameters for the sensor. * @param info - The sensor parameters. */ validateFov2_(info) { this.validateParameter(info.maxAz2, 0, 360, 'Invalid maximum azimuth2 - must be between 0 and 360'); this.validateParameter(info.minAz2, 0, 360, 'Invalid maximum azimuth2 - must be between 0 and 360'); this.validateParameter(info.maxEl2, -15, 180, 'Invalid maximum elevation2 - must be between 0 and 180'); this.validateParameter(info.minEl2, -15, 90, 'Invalid minimum elevation2 - must be between 0 and 90'); this.validateParameter(info.maxRng2, 0, null, 'Invalid maximum range2 - must be greater than 0'); this.validateParameter(info.minRng2, 0, null, 'Invalid minimum range2 - must be greater than 0'); } /** * Validates the input data for the sensor. * @param info - The sensor parameters. */ validateSensorInputData_(info) { this.validateLla_(info); this.validateFov_(info); if (info.minAz2 || info.maxAz2 || info.minEl2 || info.maxEl2 || info.minRng2 || info.maxRng2) { this.validateFov2_(info); } } /** * Validates the latitude, longitude, and altitude of a sensor. * @param info - The sensor parameters containing the latitude, longitude, and altitude. */ validateLla_(info) { this.validateParameter(info.lat, -90, 90, 'Invalid latitude - must be between -90 and 90'); this.validateParameter(info.lon, -180, 180, 'Invalid longitude - must be between -180 and 180'); this.validateParameter(info.alt, 0, null, 'Invalid altitude - must be greater than 0'); } } //# sourceMappingURL=Sensor.js.map