UNPKG

ootk

Version:

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

467 lines 17.1 kB
import { DEG2RAD, Earth, MILLISECONDS_TO_DAYS, PI, RAD2DEG, Sgp4, TAU, } from '../main.js'; /** * Converts ECF to ECI coordinates. * * [X] [C -S 0][X] * [Y] = [S C 0][Y] * [Z]eci [0 0 1][Z]ecf * @param ecf takes xyz coordinates * @param gmst takes a number in gmst time * @returns array containing eci coordinates */ export function ecf2eci(ecf, gmst) { const X = (ecf.x * Math.cos(gmst) - ecf.y * Math.sin(gmst)); const Y = (ecf.x * Math.sin(gmst) + ecf.y * Math.cos(gmst)); const Z = ecf.z; return { x: X, y: Y, z: Z }; } /** * Converts ECEF coordinates to ENU coordinates. * @param ecf - The ECEF coordinates. * @param lla - The LLA coordinates. * @returns The ENU coordinates. */ export function ecf2enu(ecf, lla) { const { lat, lon } = lla; const { x, y, z } = ecf; const e = (-Math.sin(lon) * x + Math.cos(lon) * y); const n = (-Math.sin(lat) * Math.cos(lon) * x - Math.sin(lat) * Math.sin(lon) * y + Math.cos(lat) * z); const u = (Math.cos(lat) * Math.cos(lon) * x + Math.cos(lat) * Math.sin(lon) * y + Math.sin(lat) * z); return { x: e, y: n, z: u }; } /** * Converts ECI to ECF coordinates. * * [X] [C -S 0][X] * [Y] = [S C 0][Y] * [Z]eci [0 0 1][Z]ecf * * Inverse: * [X] [C S 0][X] * [Y] = [-S C 0][Y] * [Z]ecf [0 0 1][Z]eci * @param eci takes xyz coordinates * @param gmst takes a number in gmst time * @returns array containing ecf coordinates */ export function eci2ecf(eci, gmst) { const x = (eci.x * Math.cos(gmst) + eci.y * Math.sin(gmst)); const y = (eci.x * -Math.sin(gmst) + eci.y * Math.cos(gmst)); const z = eci.z; return { x, y, z, }; } /** * EciToGeodetic converts eci coordinates to lla coordinates * @variation cached - results are cached * @param eci takes xyz coordinates * @param gmst takes a number in gmst time * @returns array containing lla coordinates */ export function eci2lla(eci, gmst) { // http://www.celestrak.com/columns/v02n03/ const a = 6378.137; const b = 6356.7523142; const R = Math.sqrt(eci.x * eci.x + eci.y * eci.y); const f = (a - b) / a; const e2 = 2 * f - f * f; let lon = Math.atan2(eci.y, eci.x) - gmst; while (lon < -PI) { lon += TAU; } while (lon > PI) { lon -= TAU; } const kmax = 20; let k = 0; let lat = Math.atan2(eci.z, Math.sqrt(eci.x * eci.x + eci.y * eci.y)); let C = 0; while (k < kmax) { C = 1 / Math.sqrt(1 - e2 * (Math.sin(lat) * Math.sin(lat))); lat = Math.atan2(eci.z + a * C * e2 * Math.sin(lat), R); k += 1; } const alt = R / Math.cos(lat) - a * C; lon = (lon * RAD2DEG); lat = (lat * RAD2DEG); return { lon: lon, lat: lat, alt: alt }; } /** * Converts geodetic coordinates (longitude, latitude, altitude) to Earth-Centered Earth-Fixed (ECF) coordinates. * @param lla The geodetic coordinates in radians and meters. * @returns The ECF coordinates in meters. */ export function llaRad2ecf(lla) { const { lon, lat, alt } = lla; const a = 6378.137; const b = 6356.7523142; const f = (a - b) / a; const e2 = 2 * f - f * f; const normal = a / Math.sqrt(1 - e2 * Math.sin(lat) ** 2); const x = (normal + alt) * Math.cos(lat) * Math.cos(lon); const y = (normal + alt) * Math.cos(lat) * Math.sin(lon); const z = (normal * (1 - e2) + alt) * Math.sin(lat); return { x: x, y: y, z: z, }; } /** * Converts geodetic coordinates (longitude, latitude, altitude) to Earth-Centered Earth-Fixed (ECF) coordinates. * @param lla The geodetic coordinates in degrees and meters. * @returns The ECF coordinates in meters. */ export function lla2ecf(lla) { const { lon, lat, alt } = lla; const lonRad = lon * DEG2RAD; const latRad = lat * DEG2RAD; return llaRad2ecf({ lon: lonRad, lat: latRad, alt, }); } /** * Converts geodetic coordinates (latitude, longitude, altitude) to Earth-centered inertial (ECI) coordinates. * @variation cached - results are cached * @param lla The geodetic coordinates in radians and meters. * @param gmst The Greenwich Mean Sidereal Time in seconds. * @returns The ECI coordinates in meters. */ export function lla2eci(lla, gmst) { const { lat, lon, alt } = lla; const cosLat = Math.cos(lat); const sinLat = Math.sin(lat); const cosLon = Math.cos(lon + gmst); const sinLon = Math.sin(lon + gmst); const x = (Earth.radiusMean + alt) * cosLat * cosLon; const y = (Earth.radiusMean + alt) * cosLat * sinLon; const z = (Earth.radiusMean + alt) * sinLat; return { x, y, z }; } /** * Calculates Geodetic Lat Lon Alt to ECEF coordinates. * @deprecated This needs to be validated. * @param lla The geodetic coordinates in degrees and meters. * @returns The ECEF coordinates in meters. */ export function lla2ecef(lla) { const { lat, lon, alt } = lla; const a = 6378.137; // semi-major axis length in meters according to the WGS84 const b = 6356.752314245; // semi-minor axis length in meters according to the WGS84 const e = Math.sqrt(1 - b ** 2 / a ** 2); // eccentricity const N = a / Math.sqrt(1 - e ** 2 * Math.sin(lat) ** 2); // radius of curvature in the prime vertical const x = ((N + alt) * Math.cos(lat) * Math.cos(lon)); const y = ((N + alt) * Math.cos(lat) * Math.sin(lon)); const z = ((N * (1 - e ** 2) + alt) * Math.sin(lat)); return { x, y, z }; } /** * Converts LLA to SEZ coordinates. * @see http://www.celestrak.com/columns/v02n02/ * @param lla The LLA coordinates. * @param ecf The ECF coordinates. * @returns The SEZ coordinates. */ export function lla2sez(lla, ecf) { const lon = lla.lon; const lat = lla.lat; const observerEcf = llaRad2ecf({ lat, lon, alt: 0, }); const rx = ecf.x - observerEcf.x; const ry = ecf.y - observerEcf.y; const rz = ecf.z - observerEcf.z; // Top is short for topocentric const south = Math.sin(lat) * Math.cos(lon) * rx + Math.sin(lat) * Math.sin(lon) * ry - Math.cos(lat) * rz; const east = -Math.sin(lon) * rx + Math.cos(lon) * ry; const zenith = Math.cos(lat) * Math.cos(lon) * rx + Math.cos(lat) * Math.sin(lon) * ry + Math.sin(lat) * rz; return { s: south, e: east, z: zenith }; } /** * Converts a vector in Right Ascension, Elevation, and Range (RAE) coordinate system * to a vector in South, East, and Zenith (SEZ) coordinate system. * @param rae The vector in RAE coordinate system. * @returns The vector in SEZ coordinate system. */ export function rae2sez(rae) { const south = -rae.rng * Math.cos(rae.el) * Math.cos(rae.az); const east = rae.rng * Math.cos(rae.el) * Math.sin(rae.az); const zenith = rae.rng * Math.sin(rae.el); return { s: south, e: east, z: zenith, }; } /** * Converts a vector in Right Ascension, Elevation, and Range (RAE) coordinate system * to Earth-Centered Fixed (ECF) coordinate system. * @template D - The dimension of the RAE vector. * @template A - The dimension of the LLA vector. * @param rae - The vector in RAE coordinate system. * @param lla - The vector in LLA coordinate system. * @returns The vector in ECF coordinate system. */ export function rae2ecf(rae, lla) { const llaRad = { lat: (lla.lat * DEG2RAD), lon: (lla.lon * DEG2RAD), alt: lla.alt, }; const raeRad = { az: (rae.az * DEG2RAD), el: (rae.el * DEG2RAD), rng: rae.rng, }; const obsEcf = llaRad2ecf(llaRad); const sez = rae2sez(raeRad); // Some needed calculations const slat = Math.sin(llaRad.lat); const slon = Math.sin(llaRad.lon); const clat = Math.cos(llaRad.lat); const clon = Math.cos(llaRad.lon); const x = slat * clon * sez.s + -slon * sez.e + clat * clon * sez.z + obsEcf.x; const y = slat * slon * sez.s + clon * sez.e + clat * slon * sez.z + obsEcf.y; const z = -clat * sez.s + slat * sez.z + obsEcf.z; return { x, y, z }; } /** * Converts a vector from RAE (Range, Azimuth, Elevation) coordinates to ECI (Earth-Centered Inertial) coordinates. * @variation cached - results are cached * @param rae The vector in RAE coordinates. * @param lla The vector in LLA (Latitude, Longitude, Altitude) coordinates. * @param gmst The Greenwich Mean Sidereal Time. * @returns The vector in ECI coordinates. */ export function rae2eci(rae, lla, gmst) { const ecf = rae2ecf(rae, lla); const eci = ecf2eci(ecf, gmst); return eci; } /** * Converts a vector in RAE (Range, Azimuth, Elevation) coordinates to ENU (East, North, Up) coordinates. * @param rae - The vector in RAE coordinates. * @returns The vector in ENU coordinates. */ export function rae2enu(rae) { const e = (rae.rng * Math.cos(rae.el) * Math.sin(rae.az)); const n = (rae.rng * Math.cos(rae.el) * Math.cos(rae.az)); const u = (rae.rng * Math.sin(rae.el)); return { x: e, y: n, z: u }; } /** * Converts South, East, and Zenith (SEZ) coordinates to Right Ascension, Elevation, and Range (RAE) coordinates. * @param sez The SEZ coordinates. * @returns Rng, Az, El array */ export function sez2rae(sez) { const rng = Math.sqrt(sez.s * sez.s + sez.e * sez.e + sez.z * sez.z); const el = Math.asin(sez.z / rng); const az = (Math.atan2(-sez.e, sez.s) + PI); return { rng, az, el }; } /** * Converts Earth-Centered Fixed (ECF) coordinates to Right Ascension (RA), * Elevation (E), and Azimuth (A) coordinates. * @param lla The Latitude, Longitude, and Altitude (LLA) coordinates. * @param ecf The Earth-Centered Fixed (ECF) coordinates. * @returns The Right Ascension (RA), Elevation (E), and Azimuth (A) coordinates. */ export function ecfRad2rae(lla, ecf) { const sezCoords = lla2sez(lla, ecf); const rae = sez2rae(sezCoords); return { rng: rae.rng, az: (rae.az * RAD2DEG), el: (rae.el * RAD2DEG) }; } /** * Converts Earth-Centered Fixed (ECF) coordinates to Right Ascension (RA), * Elevation (E), and Azimuth (A) coordinates. * @variation cached - results are cached * @param lla The Latitude, Longitude, and Altitude (LLA) coordinates. * @param ecf The Earth-Centered Fixed (ECF) coordinates. * @returns The Right Ascension (RA), Elevation (E), and Azimuth (A) coordinates. */ export function ecf2rae(lla, ecf) { const { lat, lon } = lla; const latRad = (lat * DEG2RAD); const lonRad = (lon * DEG2RAD); const rae = ecfRad2rae({ lat: latRad, lon: lonRad, alt: lla.alt }, ecf); return rae; } export const jday = (year, mon, day, hr, minute, sec) => { if (typeof year === 'undefined') { const now = new Date(); const jDayStart = new Date(now.getUTCFullYear(), 0, 0); const jDayDiff = now.getDate() - jDayStart.getDate(); return Math.floor(jDayDiff / MILLISECONDS_TO_DAYS); } if (typeof mon === 'undefined' || typeof day === 'undefined' || typeof hr === 'undefined' || typeof minute === 'undefined' || typeof sec === 'undefined') { throw new Error('Invalid date'); } return (367.0 * year - Math.floor(7 * (year + Math.floor((mon + 9) / 12.0)) * 0.25) + Math.floor((275 * mon) / 9.0) + day + 1721013.5 + ((sec / 60.0 + minute) / 60.0 + hr) / 24.0); }; /** * Calculates the Greenwich Mean Sidereal Time (GMST) for a given date. * @param date - The date for which to calculate the GMST. * @returns An object containing the GMST value and the Julian date. */ export function calcGmst(date) { const j = jday(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()) + date.getUTCMilliseconds() * MILLISECONDS_TO_DAYS; const gmst = Sgp4.gstime(j); return { gmst, j }; } /** * Converts ECI coordinates to RAE (Right Ascension, Azimuth, Elevation) coordinates. * @variation cached - results are cached * @param now - Current date and time. * @param eci - ECI coordinates of the satellite. * @param sensor - Sensor object containing observer's geodetic coordinates. * @returns Object containing azimuth, elevation and range in degrees and kilometers respectively. */ export function eci2rae(now, eci, sensor) { now = new Date(now); const { gmst } = calcGmst(now); const positionEcf = eci2ecf(eci, gmst); const lla = { lat: (sensor.lat * DEG2RAD), lon: (sensor.lon * DEG2RAD), alt: sensor.alt, }; const rae = ecfRad2rae(lla, positionEcf); return rae; } /** * Calculates the inertial azimuth of a satellite given its latitude and inclination. * @param lat - The latitude of the satellite in degrees. * @param inc - The inclination of the satellite in degrees. * @returns The inertial azimuth of the satellite in degrees. */ export function calcInertAz(lat, inc) { const phi = lat * DEG2RAD; const i = inc * DEG2RAD; const az = Math.asin(Math.cos(i) / Math.cos(phi)); return (az * RAD2DEG); } /** * Calculates the inclination angle of a satellite from its launch azimuth and latitude. * @param lat - The latitude of the observer in degrees. * @param az - The launch azimuth angle of the satellite in degrees clockwise from north. * @returns The inclination angle of the satellite in degrees. */ export function calcIncFromAz(lat, az) { const phi = lat * DEG2RAD; const beta = az * DEG2RAD; const inc = Math.acos(Math.sin(beta) * Math.cos(phi)); return (inc * RAD2DEG); } /** * Converts Azimuth and Elevation to U and V. * Azimuth is the angle off of boresight in the horizontal plane. * Elevation is the angle off of boresight in the vertical plane. * Cone half angle is the angle of the cone of the radar max field of view. * @param az - Azimuth in radians * @param el - Elevation in radians * @param coneHalfAngle - Cone half angle in radians * @returns U and V in radians */ export function azel2uv(az, el, coneHalfAngle) { if (az > coneHalfAngle && az < coneHalfAngle) { throw new RangeError(`Azimuth is out of bounds: ${az}`); } if (el > coneHalfAngle && el < coneHalfAngle) { throw new RangeError(`Elevation is out of bounds: ${el}`); } const alpha = (az / (coneHalfAngle * RAD2DEG)) * 90; const beta = (el / (coneHalfAngle * RAD2DEG)) * 90; const u = Math.sin(alpha); let v = -Math.sin(beta); v = Object.is(v, -0) ? 0 : v; return { u, v }; } /** * Determine azimuth and elevation off of boresight based on sensor orientation and RAE. * @param rae Range, Azimuth, Elevation * @param sensor Radar sensor object * @param face Face number of the sensor * @param maxSensorAz Maximum sensor azimuth * @returns Azimuth and Elevation off of boresight */ export function rae2raeOffBoresight(rae, sensor, face, maxSensorAz) { let az = (rae.az * DEG2RAD); let el = (rae.el * DEG2RAD); // Correct azimuth for sensor orientation. az = az > maxSensorAz * DEG2RAD ? (az - TAU) : az; az = (az - sensor.boresightAz[face]); el = (el - sensor.boresightEl[face]); return { az, el }; } /** * Converts Range Az El to Range U V. * @param rae Range, Azimuth, Elevation * @param sensor Radar sensor object * @param face Face number of the sensor * @param maxSensorAz Maximum sensor azimuth * @returns Range, U, V */ export function rae2ruv(rae, sensor, face, maxSensorAz) { const { az, el } = rae2raeOffBoresight(rae, sensor, face, maxSensorAz); const { u, v } = azel2uv(az, el, sensor.beamwidthRad); return { rng: rae.rng, u, v }; } /** * Converts U and V to Azimuth and Elevation off of boresight. * @param u The U coordinate. * @param v The V coordinate. * @param coneHalfAngle The cone half angle of the radar. * @returns Azimuth and Elevation off of boresight. */ export function uv2azel(u, v, coneHalfAngle) { if (u > 1 || u < -1) { throw new RangeError(`u is out of bounds: ${u}`); } if (v > 1 || v < -1) { throw new RangeError(`v is out of bounds: ${v}`); } const alpha = Math.asin(u); const beta = Math.asin(v); const az = ((alpha / 90) * (coneHalfAngle * RAD2DEG)); const el = ((beta / 90) * (coneHalfAngle * RAD2DEG)); return { az, el }; } /** * Converts coordinates from East-North-Up (ENU) to Right-Front-Up (RF) coordinate system. * @param enu - The ENU coordinates to be converted. * @param enu.x - The east coordinate. * @param enu.y - The north coordinate. * @param enu.z - The up coordinate. * @param az - The azimuth angle in radians. * @param el - The elevation angle in radians. * @returns The converted RF coordinates. */ export function enu2rf({ x, y, z }, az, el) { const xrf = Math.cos(el) * Math.cos(az) * x - Math.sin(az) * y + Math.sin(el) * Math.cos(az) * z; const yrf = Math.cos(el) * Math.sin(az) * x + Math.cos(az) * y + Math.sin(el) * Math.sin(az) * z; const zrf = -Math.sin(el) * x + Math.cos(el) * z; return { x: xrf, y: yrf, z: zrf, }; } //# sourceMappingURL=transforms.js.map