ootk-core
Version:
Orbital Object Toolkit. A modern typed replacement for satellite.js including SGP4 propagation, TLE parsing, Sun and Moon calculations, and more.
229 lines • 9.38 kB
JavaScript
/**
* @author Theodore Kruczek.
* @license MIT
* @copyright (c) 2022-2025 Theodore Kruczek Permission is
* hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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);
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));
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.
* TODO: #8 This doesn't account for secondary sensor FOV
* @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()) {
return this.isRaeInFov(this.rae(sat, date));
}
/**
* 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