From 39673ba891a8bbe1a5d52be0e13e7d549f3c39f2 Mon Sep 17 00:00:00 2001 From: Theodore Kruczek Date: Sun, 14 Jan 2024 20:17:24 -0500 Subject: [PATCH] test: :white_check_mark: add tests for Tle --- src/coordinate/ClassicalElements.ts | 15 +- src/coordinate/EquinoctialElements.ts | 14 +- src/coordinate/TEME.ts | 34 +++- src/coordinate/Tle.ts | 7 +- test/coordinate/TEME.test.ts | 48 +++++ test/coordinate/Tle.test.ts | 84 ++++++++ .../ClassicalElements.test.ts.snap | 2 +- .../EquinoctialElements.test.ts.snap | 4 +- .../__snapshots__/TEME.test.ts.snap | 41 ++++ .../coordinate/__snapshots__/Tle.test.ts.snap | 186 ++++++++++++++++++ 10 files changed, 409 insertions(+), 26 deletions(-) create mode 100644 test/coordinate/TEME.test.ts create mode 100644 test/coordinate/Tle.test.ts create mode 100644 test/coordinate/__snapshots__/TEME.test.ts.snap create mode 100644 test/coordinate/__snapshots__/Tle.test.ts.snap diff --git a/src/coordinate/ClassicalElements.ts b/src/coordinate/ClassicalElements.ts index 17a8934..bd7787a 100644 --- a/src/coordinate/ClassicalElements.ts +++ b/src/coordinate/ClassicalElements.ts @@ -1,12 +1,11 @@ -import { Degrees, Kilometers, Radians, Seconds } from '../main'; +import { Minutes, PositionVelocity, Degrees, Kilometers, Radians, Seconds } from '../main'; import { Vector3D } from '../operations/Vector3D'; import { EpochUTC } from '../time/EpochUTC'; -import { earthGravityParam, RAD2DEG, sec2min, secondsPerDay, TAU } from '../utils/constants'; +import { earthGravityParam, MINUTES_PER_DAY, RAD2DEG, sec2min, TAU } from '../utils/constants'; import { clamp, matchHalfPlane, newtonNu } from '../utils/functions'; import { EquinoctialElements } from './EquinoctialElements'; import { OrbitRegime } from '../enums/OrbitRegime'; import { StateVector } from './StateVector'; -import { PositionVelocity } from 'src/types/types'; import { ClassicalElementsParams } from '../interfaces/ClassicalElementsParams'; /** @@ -97,7 +96,7 @@ export class ClassicalElements { return new ClassicalElements({ epoch: state.epoch, - semimajorAxis: a as Kilometers, + semimajorAxis: a, eccentricity: e, inclination: i as Radians, rightAscension: o as Radians, @@ -181,8 +180,10 @@ export class ClassicalElements { * Calculates the period of the orbit. * @returns The period in seconds. */ - get period(): Seconds { - return (TAU * Math.sqrt(this.semimajorAxis ** 3 / this.mu)) as Seconds; + get period(): Minutes { + const periodSec = (TAU * Math.sqrt(this.semimajorAxis ** 3 / this.mu)) as Seconds; + + return (periodSec / 60) as Minutes; } /** @@ -190,7 +191,7 @@ export class ClassicalElements { * @returns The number of revolutions per day. */ get revsPerDay(): number { - return secondsPerDay / this.period; + return MINUTES_PER_DAY / this.period; } /** diff --git a/src/coordinate/EquinoctialElements.ts b/src/coordinate/EquinoctialElements.ts index 039bf81..ed8526b 100644 --- a/src/coordinate/EquinoctialElements.ts +++ b/src/coordinate/EquinoctialElements.ts @@ -1,9 +1,9 @@ import { Kilometers, Radians, Seconds } from '../main'; import { EpochUTC } from '../time/EpochUTC'; -import { earthGravityParam, secondsPerDay, TAU } from '../utils/constants'; +import { earthGravityParam, MINUTES_PER_DAY, TAU } from '../utils/constants'; import { newtonM } from '../utils/functions'; import { ClassicalElements } from './ClassicalElements'; -import { PositionVelocity } from 'src/types/types'; +import { Minutes, PositionVelocity } from 'src/types/types'; import { EquinoctialElementsParams } from '../interfaces/EquinoctialElementsParams'; /** @@ -135,10 +135,12 @@ export class EquinoctialElements { /** * Gets the period of the orbit. - * @returns The period in seconds. + * @returns The period in minutes. */ - get period(): Seconds { - return (TAU * Math.sqrt(this.semimajorAxis ** 3 / this.mu)) as Seconds; + get period(): Minutes { + const periodSec = (TAU * Math.sqrt(this.semimajorAxis ** 3 / this.mu)) as Seconds; + + return (periodSec / 60) as Minutes; } /** @@ -147,7 +149,7 @@ export class EquinoctialElements { * @returns The number of revolutions per day. */ get revsPerDay(): number { - return secondsPerDay / this.period; + return MINUTES_PER_DAY / this.period; } /** diff --git a/src/coordinate/TEME.ts b/src/coordinate/TEME.ts index 7eb2178..fa2bb77 100644 --- a/src/coordinate/TEME.ts +++ b/src/coordinate/TEME.ts @@ -18,22 +18,40 @@ import { StateVector } from './StateVector'; * communication, navigation, and remote sensing. */ export class TEME extends StateVector { - // / Create a new [TEME] object from a [ClassicalElements] object. - static fromClassicalElements(elements: ClassicalElements): TEME { - const rv = elements.toPositionVelocity(); - - return new TEME(elements.epoch, rv.position, rv.velocity); - } - + /** + * Gets the name of the coordinate system. + * @returns The name of the coordinate system. + */ get name(): string { return 'TEME'; } + /** + * Gets a value indicating whether the coordinate is inertial. + * @returns A boolean value indicating whether the coordinate is inertial. + */ get inertial(): boolean { return true; } - // / Convert this to a [J2000] state vector object. + /** + * Creates a TEME (True Equator Mean Equinox) object from classical orbital + * elements. + * + * @param elements - The classical orbital elements. + * @returns A new TEME object. + */ + static fromClassicalElements(elements: ClassicalElements): TEME { + const rv = elements.toPositionVelocity(); + + return new TEME(elements.epoch, rv.position, rv.velocity); + } + + /** + * Converts the TEME (True Equator Mean Equinox) coordinates to J2000 + * coordinates. + * @returns The J2000 coordinates. + */ toJ2000(): J2000 { const p = Earth.precession(this.epoch); const n = Earth.nutation(this.epoch); diff --git a/src/coordinate/Tle.ts b/src/coordinate/Tle.ts index 325e1b2..1ee86d6 100644 --- a/src/coordinate/Tle.ts +++ b/src/coordinate/Tle.ts @@ -41,6 +41,7 @@ import { Line2Data, Minutes, SatelliteRecord, + Seconds, StateVectorSgp4, TleData, TleDataFull, @@ -175,8 +176,10 @@ export class Tle { return this.semimajorAxis * (1 - this.eccentricity); } - get period(): number { - return TAU * Math.sqrt(this.semimajorAxis ** 3 / earthGravityParam); + get period(): Minutes { + const periodSec = (TAU * Math.sqrt(this.semimajorAxis ** 3 / earthGravityParam)) as Seconds; + + return (periodSec / 60) as Minutes; } private static parseEpoch_(epochStr: string): EpochUTC { diff --git a/test/coordinate/TEME.test.ts b/test/coordinate/TEME.test.ts new file mode 100644 index 0000000..12a53dd --- /dev/null +++ b/test/coordinate/TEME.test.ts @@ -0,0 +1,48 @@ +import { exampleDate } from '../lib/mockData'; +import { ClassicalElements, EpochUTC, J2000, Kilometers, Radians, TEME, Vector3D } from './../../src/main'; +describe('TEME', () => { + let stateVector: TEME; + + beforeEach(() => { + stateVector = new J2000( + EpochUTC.fromDateTime(new Date(1705109326817)), + new Vector3D(1538.223335842895 as Kilometers, 5102.261204021967 as Kilometers, 4432.634965003577 as Kilometers), + new Vector3D( + -7.341518909379302 as Kilometers, + 0.6516718453998644 as Kilometers, + 1.7933882499861993 as Kilometers, + ), + ).toTEME(); + }); + + // name + it('should return the name of the coordinate system', () => { + expect(stateVector.name).toMatchSnapshot(); + }); + + // inertial + it('should return whether the coordinate system is inertial', () => { + expect(stateVector.inertial).toMatchSnapshot(); + }); + + // fromClassicalElements + it('should create a TEME coordinate from classical orbital elements', () => { + const epoch = EpochUTC.fromDateTime(exampleDate); + const elements = new ClassicalElements({ + epoch, + semimajorAxis: 6943.547853722985 as Kilometers, + eccentricity: 0.0011235968124658146, + inclination: 0.7509087232045765 as Radians, + rightAscension: 0.028239555738616327 as Radians, + argPerigee: 2.5386411901807353 as Radians, + trueAnomaly: 0.5931399364974058 as Radians, + }); + + expect(TEME.fromClassicalElements(elements)).toMatchSnapshot(); + }); + + // toJ2000 + it('should convert the TEME coordinates to J2000 coordinates', () => { + expect(stateVector.toJ2000()).toMatchSnapshot(); + }); +}); diff --git a/test/coordinate/Tle.test.ts b/test/coordinate/Tle.test.ts new file mode 100644 index 0000000..40b16ca --- /dev/null +++ b/test/coordinate/Tle.test.ts @@ -0,0 +1,84 @@ +import { exampleDate } from '../lib/mockData'; +import { ClassicalElements, EpochUTC, Kilometers, Radians, Tle, TleLine1, TleLine2 } from '../../src/main'; + +describe('Tle', () => { + let tle: Tle; + + beforeEach(() => { + // Sample TLE + const tle1 = '1 56006U 23042W 24012.45049317 .00000296 00000-0 36967-4 0 9992' as TleLine1; + const tle2 = '2 56006 143.0043 13.3620 0001137 267.5965 92.4747 15.02542972 44491' as TleLine2; + + tle = new Tle(tle1, tle2); + }); + + // toString + it('should return the TLE as a string', () => { + expect(tle.toString()).toMatchSnapshot(); + }); + + // semiMajorAxis + it('should return the semi-major axis', () => { + expect(tle.semimajorAxis).toMatchSnapshot(); + }); + + // eccentricity + it('should return the eccentricity', () => { + expect(tle.eccentricity).toMatchSnapshot(); + }); + + // inclination + it('should return the inclination', () => { + expect(tle.inclination).toMatchSnapshot(); + }); + + // inclinationDeg + it('should return the inclination in degrees', () => { + expect(tle.inclinationDegrees).toMatchSnapshot(); + }); + + // apogee + it('should return the apogee', () => { + expect(tle.apogee).toMatchSnapshot(); + }); + + // perigee + it('should return the perigee', () => { + expect(tle.perigee).toMatchSnapshot(); + }); + + // period + it('should return the period', () => { + expect(tle.period).toMatchSnapshot(); + }); + + // propagate + it('should propagate the TLE', () => { + const epoch = EpochUTC.fromDateTime(exampleDate); + const propagated = tle.propagate(epoch); + + expect(propagated).toMatchSnapshot(); + }); + + // state + it('should return the state vector', () => { + expect(tle.state).toMatchSnapshot(); + }); + + // fromClassicalElements + it('should create a TLE from classical orbital elements', () => { + const epoch = EpochUTC.fromDateTime(exampleDate); + const elements = new ClassicalElements({ + epoch, + semimajorAxis: 6943.547853722985 as Kilometers, + eccentricity: 0.0011235968124658146, + inclination: 0.7509087232045765 as Radians, + rightAscension: 0.028239555738616327 as Radians, + argPerigee: 2.5386411901807353 as Radians, + trueAnomaly: 0.5931399364974058 as Radians, + }); + const tle = Tle.fromClassicalElements(elements); + + expect(tle).toMatchSnapshot(); + }); +}); diff --git a/test/coordinate/__snapshots__/ClassicalElements.test.ts.snap b/test/coordinate/__snapshots__/ClassicalElements.test.ts.snap index fe0a181..a088bfb 100644 --- a/test/coordinate/__snapshots__/ClassicalElements.test.ts.snap +++ b/test/coordinate/__snapshots__/ClassicalElements.test.ts.snap @@ -14,7 +14,7 @@ exports[`ClassicalElements should calculate the orbit regime of ClassicalElement exports[`ClassicalElements should calculate the perigee of ClassicalElements 1`] = `6935.746105487338`; -exports[`ClassicalElements should calculate the period of ClassicalElements 1`] = `5758.152068066809`; +exports[`ClassicalElements should calculate the period of ClassicalElements 1`] = `95.96920113444682`; exports[`ClassicalElements should calculate true anomaly in degrees 1`] = `33.984415021959016`; diff --git a/test/coordinate/__snapshots__/EquinoctialElements.test.ts.snap b/test/coordinate/__snapshots__/EquinoctialElements.test.ts.snap index 75c1d43..6015699 100644 --- a/test/coordinate/__snapshots__/EquinoctialElements.test.ts.snap +++ b/test/coordinate/__snapshots__/EquinoctialElements.test.ts.snap @@ -68,9 +68,9 @@ exports[`ClassicalElements should return the mean motion 1`] = `0.00109302064133 exports[`ClassicalElements should return the mean motion 2`] = `0.0010926340790517568`; -exports[`ClassicalElements should return the period 1`] = `5748.459882237341`; +exports[`ClassicalElements should return the period 1`] = `95.80766470395568`; -exports[`ClassicalElements should return the period 2`] = `5750.493626038512`; +exports[`ClassicalElements should return the period 2`] = `95.8415604339752`; exports[`ClassicalElements should return the retrograde factor 1`] = `1`; diff --git a/test/coordinate/__snapshots__/TEME.test.ts.snap b/test/coordinate/__snapshots__/TEME.test.ts.snap new file mode 100644 index 0000000..3dd2a06 --- /dev/null +++ b/test/coordinate/__snapshots__/TEME.test.ts.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TEME should convert the TEME coordinates to J2000 coordinates 1`] = ` +J2000 { + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "position": Vector3D { + "x": 1538.2233358428946, + "y": 5102.2612040219665, + "z": 4432.634965003576, + }, + "velocity": Vector3D { + "x": -7.341518909379303, + "y": 0.6516718453998649, + "z": 1.793388249986199, + }, +} +`; + +exports[`TEME should create a TEME coordinate from classical orbital elements 1`] = ` +TEME { + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "position": Vector3D { + "x": -6935.3813042612455, + "y": -146.1261328371035, + "z": 46.43907991639276, + }, + "velocity": Vector3D { + "x": 0.07740377357485445, + "y": -5.543955093986134, + "z": -5.174123893746047, + }, +} +`; + +exports[`TEME should return the name of the coordinate system 1`] = `"TEME"`; + +exports[`TEME should return whether the coordinate system is inertial 1`] = `true`; diff --git a/test/coordinate/__snapshots__/Tle.test.ts.snap b/test/coordinate/__snapshots__/Tle.test.ts.snap new file mode 100644 index 0000000..90e0500 --- /dev/null +++ b/test/coordinate/__snapshots__/Tle.test.ts.snap @@ -0,0 +1,186 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tle should create a TLE from classical orbital elements 1`] = ` +Tle { + "epoch": EpochUTC { + "posix": 1705109279.999904, + }, + "line1": "1 00001U 58001A 24013.06111111 +.00000000 +00000+0 +00000-0 0 09990", + "line2": "2 00001 043.0239 001.6180 0011236 145.4534 033.9125 15.00481387 00010", + "satnum": 1, + "satrec_": Object { + "Om": null, + "PInco": 0, + "a": 1.088949028290718, + "alta": 0.09017257141890522, + "altp": 0.08772548516253043, + "am": null, + "argpdot": 0.00007500841038277947, + "argpo": 2.53864073822032, + "atime": 0, + "aycof": 0.0008000244899288449, + "bstar": 0, + "cc1": 0, + "cc4": 4.3305287931362373e-7, + "cc5": 0.00011839745575895689, + "con41": 0.6033863216244524, + "d2": 0, + "d2201": 0, + "d2211": 0, + "d3": 0, + "d3210": 0, + "d3222": 0, + "d4": 0, + "d4410": 0, + "d4422": 0, + "d5220": 0, + "d5232": 0, + "d5421": 0, + "d5433": 0, + "dedt": 0, + "del1": 0, + "del2": 0, + "del3": 0, + "delmo": 1.0402336592605395, + "didt": 0, + "dmdt": 0, + "dnodt": 0, + "domdt": 0, + "e3": 0, + "ecco": 0.0011236, + "ee2": 0, + "em": null, + "epochdays": 13.06111111, + "epochyr": 24, + "error": 0, + "eta": 0.01594821618248224, + "gsto": 2.3394503579736323, + "im": null, + "inclo": 0.7509087120487883, + "init": "n", + "irez": 0, + "isimp": 0, + "j2": 0.001082616, + "j3": -0.00000253881, + "j3oj2": -0.002345069720011528, + "j4": -0.00000165597, + "jdsatepoch": 2460322.56111111, + "mdot": 0.06547085575553616, + "method": "n", + "mm": null, + "mo": 0.591884782582577, + "mus": 398600.8, + "nddot": 0, + "ndot": 0, + "nm": null, + "no": 0.06544379037496881, + "nodecf": -0, + "nodedot": -0.00006565190786601781, + "nodeo": 0.028239427297268253, + "om": null, + "omgcof": -0, + "operationmode": "a", + "peo": 0, + "pgho": 0, + "pho": 0, + "plo": 0, + "radiusearthkm": 6378.135, + "satnum": "00001", + "se2": 0, + "se3": 0, + "sgh2": 0, + "sgh3": 0, + "sgh4": 0, + "sh2": 0, + "sh3": 0, + "si2": 0, + "si3": 0, + "sinmao": 0.5579261762949905, + "sl2": 0, + "sl3": 0, + "sl4": 0, + "t": 0, + "t2cof": 0, + "t3cof": 0, + "t4cof": 0, + "t5cof": 0, + "tumin": 13.446839696959309, + "x1mth2": 0.4655378927918492, + "x7thm1": 2.7412347504570556, + "xfact": 0, + "xgh2": 0, + "xgh3": 0, + "xgh4": 0, + "xh2": 0, + "xh3": 0, + "xi2": 0, + "xi3": 0, + "xke": 0.07436691613317342, + "xl2": 0, + "xl3": 0, + "xl4": 0, + "xlamo": 0, + "xlcof": 0.0015379049393216336, + "xli": 0, + "xmcof": -0, + "xni": 0, + "zmol": 0, + "zmos": 0, + }, +} +`; + +exports[`Tle should propagate the TLE 1`] = ` +TEME { + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "position": Vector3D { + "x": 3699.9683427262826, + "y": -4340.514516979853, + "z": 3939.5098576859164, + }, + "velocity": Vector3D { + "x": -6.276352177964951, + "y": -3.990490995081803, + "z": 1.4943962253306078, + }, +} +`; + +exports[`Tle should return the TLE as a string 1`] = ` +"1 56006U 23042W 24012.45049317 .00000296 00000-0 36967-4 0 9992 +2 56006 143.0043 13.3620 0001137 267.5965 92.4747 15.02542972 44491" +`; + +exports[`Tle should return the apogee 1`] = `6937.9838309710485`; + +exports[`Tle should return the eccentricity 1`] = `0.0001137`; + +exports[`Tle should return the inclination 1`] = `2.495895879509727`; + +exports[`Tle should return the inclination in degrees 1`] = `143.0043`; + +exports[`Tle should return the perigee 1`] = `6936.406312811701`; + +exports[`Tle should return the period 1`] = `95.83752523784717`; + +exports[`Tle should return the semi-major axis 1`] = `6937.195071891375`; + +exports[`Tle should return the state vector 1`] = ` +TEME { + "epoch": EpochUTC { + "posix": 1705056522.609888, + }, + "position": Vector3D { + "x": 6748.804592785081, + "y": 1602.0774592717344, + "z": 0.7217817565636419, + }, + "velocity": Vector3D { + "x": 1.3943615794564204, + "y": -5.89340298337042, + "z": 4.566020263208419, + }, +} +`;