From b804dedc68170d9ee5e000f7e9f0fe1f5e1067a0 Mon Sep 17 00:00:00 2001 From: Theodore Kruczek Date: Mon, 15 Jan 2024 12:14:38 -0500 Subject: [PATCH] test: :white_check_mark: add more tests --- .eslintrc.json | 2 +- src/observation/RAE.ts | 11 +- src/observation/RadecGeocentric.ts | 49 +++---- src/observation/RadecTopocentric.ts | 2 +- src/time/Comparable.ts | 5 - src/time/Epoch.ts | 12 +- src/time/EpochGPS.ts | 3 +- src/time/EpochUTC.ts | 5 +- src/time/TimeStamped.ts | 1 + test/observation/RAE.test.ts | 134 ++++++++++++++++++ test/observation/RadecGeocentric.test.ts | 104 ++++++++++++++ test/observation/RadecTopocentric.test.ts | 118 +++++++++++++++ .../__snapshots__/RAE.test.ts.snap | 91 ++++++++++++ .../RadecGeocentric.test.ts.snap | 71 ++++++++++ .../RadecTopocentric.test.ts.snap | 71 ++++++++++ test/time/Epoch.test.ts | 118 +++++++++++++++ test/time/EpochGPS.test.ts | 38 +++++ test/time/EpochUTC.test.ts | 103 ++++++++++++++ test/time/EpochWindow.test.ts | 12 ++ test/time/TimeStamped.test.ts | 10 ++ test/time/__snapshots__/Epoch.test.ts.snap | 18 +++ test/time/__snapshots__/EpochGPS.test.ts.snap | 13 ++ test/time/__snapshots__/EpochUTC.test.ts.snap | 70 +++++++++ .../__snapshots__/TimeStamped.test.ts.snap | 10 ++ 24 files changed, 1024 insertions(+), 47 deletions(-) delete mode 100644 src/time/Comparable.ts create mode 100644 test/observation/RAE.test.ts create mode 100644 test/observation/RadecGeocentric.test.ts create mode 100644 test/observation/RadecTopocentric.test.ts create mode 100644 test/observation/__snapshots__/RAE.test.ts.snap create mode 100644 test/observation/__snapshots__/RadecGeocentric.test.ts.snap create mode 100644 test/observation/__snapshots__/RadecTopocentric.test.ts.snap create mode 100644 test/time/Epoch.test.ts create mode 100644 test/time/EpochGPS.test.ts create mode 100644 test/time/EpochUTC.test.ts create mode 100644 test/time/EpochWindow.test.ts create mode 100644 test/time/TimeStamped.test.ts create mode 100644 test/time/__snapshots__/Epoch.test.ts.snap create mode 100644 test/time/__snapshots__/EpochGPS.test.ts.snap create mode 100644 test/time/__snapshots__/EpochUTC.test.ts.snap create mode 100644 test/time/__snapshots__/TimeStamped.test.ts.snap diff --git a/.eslintrc.json b/.eslintrc.json index 2413405..a9b8e44 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -108,7 +108,7 @@ "no-extra-label": "error", "no-extra-parens": "off", "no-floating-decimal": "error", - "no-implicit-coercion": "error", + "no-implicit-coercion": "off", "no-implicit-globals": "error", "no-implied-eval": "error", "no-inline-comments": "off", diff --git a/src/observation/RAE.ts b/src/observation/RAE.ts index 0398bcc..c5da017 100644 --- a/src/observation/RAE.ts +++ b/src/observation/RAE.ts @@ -33,7 +33,6 @@ import { angularDistance } from '../utils/functions'; // / Range, azimuth, and elevation. export class RAE { - // / Create a new [Razel] object. constructor( public epoch: EpochUTC, public range: Kilometers, @@ -88,7 +87,7 @@ export class RAE { * @param site The observer [site] vector. * @returns A new [Razel] object. */ - static fromStateVectors(state: J2000, site: J2000): RAE { + static fromStateVector(state: J2000, site: J2000): RAE { const stateEcef = state.toITRF(); const siteEcef = site.toITRF(); const po2 = halfPi; @@ -199,9 +198,11 @@ export class RAE { * @returns A [J2000] state vector. */ toStateVector(site: J2000): J2000 { - if (!this.rangeRate || !this.elevationRate || !this.azimuthRate) { - throw new Error('Cannot create state, required values are undefined.'); - } + // If the rates are not defined then assume stationary + this.rangeRate ??= 0; + this.elevationRate ??= 0; + this.azimuthRate ??= 0; + const ecef = site.toITRF(); const geo = ecef.toGeodetic(); const po2 = halfPi; diff --git a/src/observation/RadecGeocentric.ts b/src/observation/RadecGeocentric.ts index a71f250..5724e66 100644 --- a/src/observation/RadecGeocentric.ts +++ b/src/observation/RadecGeocentric.ts @@ -22,6 +22,7 @@ */ /* eslint-disable no-undefined */ +import { Degrees, Kilometers, Radians } from 'src/main'; import { J2000 } from '../coordinate/J2000'; import { AngularDistanceMethod } from '../enums/AngularDistanceMethod'; import { Vector3D } from '../operations/Vector3D'; @@ -35,11 +36,11 @@ export class RadecGeocentric { // / Create a new [RadecGeocentric] object. constructor( public epoch: EpochUTC, - public rightAscension: number, - public declination: number, - public range?: number, - public rightAscensionRate?: number, - public declinationRate?: number, + public rightAscension: Radians, + public declination: Radians, + public range?: Kilometers, + public rightAscensionRate?: Radians, + public declinationRate?: Radians, public rangeRate?: number, ) { // Nothing to do here. @@ -49,21 +50,21 @@ export class RadecGeocentric { * Create a new [RadecGeocentric] object, using degrees for the * angular values. * @param epoch UTC epoch. - * @param rightAscensionDegrees Right-ascension _(°)_. - * @param declinationDegrees Declination _(°)_. - * @param range Range _(km)_. - * @param rightAscensionRateDegrees Right-ascension rate _(°/s)_. - * @param declinationRateDegrees Declination rate _(°/s)_. - * @param rangeRate Range rate _(km/s)_. + * @param rightAscensionDegrees Right-ascension + * @param declinationDegrees Declination + * @param range Range + * @param rightAscensionRateDegrees Right-ascension rate (°/s). + * @param declinationRateDegrees Declination rate (°/s). + * @param rangeRate Range rate (km/s). * @returns A new [RadecGeocentric] object. */ static fromDegrees( epoch: EpochUTC, - rightAscensionDegrees: number, - declinationDegrees: number, - range?: number, - rightAscensionRateDegrees?: number, - declinationRateDegrees?: number, + rightAscensionDegrees: Degrees, + declinationDegrees: Degrees, + range?: Kilometers, + rightAscensionRateDegrees?: Degrees, + declinationRateDegrees?: Degrees, rangeRate?: number, ): RadecGeocentric { const rightAscensionRate = rightAscensionRateDegrees && rightAscensionRateDegrees * DEG2RAD; @@ -71,11 +72,11 @@ export class RadecGeocentric { return new RadecGeocentric( epoch, - rightAscensionDegrees * DEG2RAD, - declinationDegrees * DEG2RAD, + rightAscensionDegrees * DEG2RAD as Radians, + declinationDegrees * DEG2RAD as Radians, range, - rightAscensionRate, - declinationRate, + rightAscensionRate as Radians, + declinationRate as Radians, rangeRate, ); } @@ -104,11 +105,11 @@ export class RadecGeocentric { return new RadecGeocentric( state.epoch, - rightAscension % TAU, - declination, + rightAscension % TAU as Radians, + declination as Radians, rMag, - rightAscensionRate, - declinationRate, + rightAscensionRate as Radians, + declinationRate as Radians, rangeRate, ); } diff --git a/src/observation/RadecTopocentric.ts b/src/observation/RadecTopocentric.ts index c5cddea..53b1804 100644 --- a/src/observation/RadecTopocentric.ts +++ b/src/observation/RadecTopocentric.ts @@ -87,7 +87,7 @@ export class RadecTopocentric { * @param site Site vector. * @returns A new [RadecTopocentric] object. */ - static fromStateVectors(state: J2000, site: J2000): RadecTopocentric { + static fromStateVector(state: J2000, site: J2000): RadecTopocentric { const p = state.position.subtract(site.position); const pI = p.x; const pJ = p.y; diff --git a/src/time/Comparable.ts b/src/time/Comparable.ts deleted file mode 100644 index eaa5dc8..0000000 --- a/src/time/Comparable.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Epoch } from './Epoch'; - -export interface Comparable { - compareTo(other: Epoch): number; -} diff --git a/src/time/Epoch.ts b/src/time/Epoch.ts index 883760e..13aa05b 100644 --- a/src/time/Epoch.ts +++ b/src/time/Epoch.ts @@ -1,8 +1,8 @@ +import { Seconds } from 'src/main'; import { secondsPerDay } from '../utils/constants'; -import { Comparable } from './Comparable'; // / Base class for [Epoch] data. -export class Epoch implements Comparable { +export class Epoch { /* * Create a new [Epoch] object given the number of seconds elapsed since the * [posix] epoch _(`1970-01-01T00:00:00.000`)_ in the [Epoch] time scale. @@ -23,8 +23,8 @@ export class Epoch implements Comparable { } // / Return the difference _(s)_ between this and another [epoch]/ - difference(epoch: Epoch): number { - return this.posix - epoch.posix; + difference(epoch: Epoch): Seconds { + return this.posix - epoch.posix as Seconds; } // / Check if this has the same timestamp as the provided [epoch]. @@ -83,10 +83,6 @@ export class Epoch implements Comparable { return (this.toJulianDate() - 2451545) / 36525; } - compareTo(other: Epoch): number { - return this.posix - other.posix; - } - // / Check if this is later than the [other] epoch. operatorGreaterThan(other: Epoch): boolean { return this.posix > other.posix; diff --git a/src/time/EpochGPS.ts b/src/time/EpochGPS.ts index cd6a333..a833ec4 100644 --- a/src/time/EpochGPS.ts +++ b/src/time/EpochGPS.ts @@ -8,7 +8,7 @@ export class EpochGPS { * of [seconds] into the [week]. * @param week Number of weeks since the GPS reference epoch. * @param seconds Number of seconds into the week. - * @param reference Reference epoch. + * @param reference Reference should always be EpochUTC.fromDateTimeString('1980-01-06T00:00:00.000Z'). */ constructor(public week: number, public seconds: number, reference: EpochUTC) { if (week < 0) { @@ -18,6 +18,7 @@ export class EpochGPS { throw new Error('GPS seconds must be within a week.'); } + // TODO: #9 Set EpochGPS.reference statically without circular dependency. EpochGPS.reference = reference; } diff --git a/src/time/EpochUTC.ts b/src/time/EpochUTC.ts index 7d7cb89..0982a92 100644 --- a/src/time/EpochUTC.ts +++ b/src/time/EpochUTC.ts @@ -96,13 +96,14 @@ export class EpochUTC extends Epoch { } toGPS(): EpochGPS { + const referenceTime = EpochUTC.fromDateTimeString('1980-01-06T00:00:00.000Z'); const ls = DataHandler.getInstance().getLeapSeconds(this.toJulianDate()); - const delta = this.roll(ls - EpochGPS.offset).difference(EpochGPS.reference); + const delta = this.roll(ls - EpochGPS.offset).difference(referenceTime); const week = delta / secondsPerWeek; const weekFloor = Math.floor(week); const seconds = (week - weekFloor) * secondsPerWeek; - return new EpochGPS(weekFloor, seconds, EpochUTC.fromDateTimeString('1980-01-06T00:00:00.000Z')); + return new EpochGPS(weekFloor, seconds, referenceTime); } gmstAngle(): number { diff --git a/src/time/TimeStamped.ts b/src/time/TimeStamped.ts index ad4a9da..76441cf 100644 --- a/src/time/TimeStamped.ts +++ b/src/time/TimeStamped.ts @@ -2,6 +2,7 @@ import type { EpochUTC } from './EpochUTC'; /** * Time stamped value container. + * TODO: #10 Is TimeStamped class needed? */ export class TimeStamped { /** diff --git a/test/observation/RAE.test.ts b/test/observation/RAE.test.ts new file mode 100644 index 0000000..bda4bc2 --- /dev/null +++ b/test/observation/RAE.test.ts @@ -0,0 +1,134 @@ +import { + AngularDistanceMethod, + DEG2RAD, + Degrees, + EpochUTC, + J2000, + Kilometers, + RAE, + Radians, + Vector3D, +} from '../../src/main'; + +describe('RAE', () => { + let exampleDate: Date; + let rae: RAE; + + beforeEach(() => { + exampleDate = new Date(1705109326817); + rae = new RAE( + EpochUTC.fromDateTime(exampleDate), + 1000 as Kilometers, + 10 * DEG2RAD as Radians, + 20 * DEG2RAD as Radians, + ); + }); + + it('should be constructable', () => { + expect(rae).toMatchSnapshot(); + }); + + it('should be constructable from degrees', () => { + const raeDeg = RAE.fromDegrees( + EpochUTC.fromDateTime(exampleDate), + 1000 as Kilometers, + 10 as Degrees, + 20 as Degrees, + ); + + expect(raeDeg).toMatchSnapshot(); + expect(raeDeg).toMatchObject(rae); + }); + + // azimuthDegrees + it('should return the azimuth in degrees', () => { + expect(rae.azimuthDegrees).toMatchSnapshot(); + }); + + // elevationDegrees + it('should return the elevation in degrees', () => { + expect(rae.elevationDegrees).toMatchSnapshot(); + }); + + // azimuthRateDegrees + it('should return the azimuth rate in degrees', () => { + expect(rae.azimuthRateDegrees).toMatchSnapshot(); + }); + + // elevationRateDegrees + it('should return the elevation rate in degrees', () => { + expect(rae.elevationRateDegrees).toMatchSnapshot(); + }); + + // toString + it('should return a string representation', () => { + expect(rae.toString()).toMatchSnapshot(); + }); + + // fromStateVector + it('should return a RAE object from a state vector', () => { + const state = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(7000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + const site = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(6000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + + const rae = RAE.fromStateVector(state, site); + + expect(rae).toMatchSnapshot(); + }); + + // toStateVector + it('should return a state vector', () => { + const site = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(6000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + + expect(rae.toStateVector(site)).toMatchSnapshot(); + }); + + // position + it('should return the position vector', () => { + const site = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(6000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + + expect(rae.position(site)).toMatchSnapshot(); + }); + + // angle + it('should return the angle', () => { + const rae2 = new RAE( + EpochUTC.fromDateTime(exampleDate), + 1000 as Kilometers, + 15 * DEG2RAD as Radians, + 25 * DEG2RAD as Radians, + ); + + expect(rae.angle(rae2)).toMatchSnapshot(); + expect(rae.angle(rae2, AngularDistanceMethod.Haversine)).toMatchSnapshot(); + expect(rae.angle(rae2, AngularDistanceMethod.Haversine)) + .not.toEqual(rae.angle(rae2, AngularDistanceMethod.Cosine)); + }); + + // angleDegrees + it('should return the angle in degrees', () => { + const rae2 = new RAE( + EpochUTC.fromDateTime(exampleDate), + 1000 as Kilometers, + 15 * DEG2RAD as Radians, + 25 * DEG2RAD as Radians, + ); + + expect(rae.angleDegrees(rae2)).toMatchSnapshot(); + }); +}); diff --git a/test/observation/RadecGeocentric.test.ts b/test/observation/RadecGeocentric.test.ts new file mode 100644 index 0000000..b04198a --- /dev/null +++ b/test/observation/RadecGeocentric.test.ts @@ -0,0 +1,104 @@ +import { DEG2RAD, Degrees, EpochUTC, J2000, Kilometers, RadecGeocentric, Radians, Vector3D } from './../../src/main'; +describe('RadecGeocentric', () => { + let radec: RadecGeocentric; + let exampleDate: Date; + + beforeEach(() => { + exampleDate = new Date(1705109326817); + radec = new RadecGeocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 0 * DEG2RAD as Radians, + ); + }); + + it('should be constructable', () => { + expect(radec).toMatchSnapshot(); + }); + + // fromDegrees + it('should be constructable from degrees', () => { + const radecDeg = RadecGeocentric.fromDegrees( + EpochUTC.fromDateTime(exampleDate), + 25 as Degrees, + 0 as Degrees, + ); + + expect(radecDeg).toMatchSnapshot(); + }); + + // fromStateVector + it('should be constructable from a state vector', () => { + const state = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(7000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + const radecSv = RadecGeocentric.fromStateVector(state); + + expect(radecSv).toMatchSnapshot(); + }); + + // rightAscensionDegrees + it('should return the right ascension in degrees', () => { + expect(radec.rightAscensionDegrees).toMatchSnapshot(); + }); + + // declinationDegrees + it('should return the declination in degrees', () => { + expect(radec.declinationDegrees).toMatchSnapshot(); + }); + + // rightAscensionRateDegrees + it('should return the right ascension rate in degrees', () => { + expect(radec.rightAscensionRateDegrees).toMatchSnapshot(); + }); + + // declinationRateDegrees + it('should return the declination rate in degrees', () => { + expect(radec.declinationRateDegrees).toMatchSnapshot(); + }); + + // position + it('should return the position', () => { + expect(radec.position()).toMatchSnapshot(); + }); + + // velocity + it('should return the velocity', () => { + expect(() => radec.velocity()).toThrow(); + + const radec2 = new RadecGeocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 1 * DEG2RAD as Radians, + 1000 as Kilometers, + 1.5 * DEG2RAD as Radians, + 2.5 * DEG2RAD as Radians, + ); + + expect(radec2.velocity()).toMatchSnapshot(); + }); + + // angle + it('should return the angle', () => { + const radec2 = new RadecGeocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 1 * DEG2RAD as Radians, + ); + + expect(radec.angle(radec2)).toMatchSnapshot(); + }); + + // angleDegrees + it('should return the angle in degrees', () => { + const radec2 = new RadecGeocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 1 * DEG2RAD as Radians, + ); + + expect(radec.angleDegrees(radec2)).toMatchSnapshot(); + }); +}); diff --git a/test/observation/RadecTopocentric.test.ts b/test/observation/RadecTopocentric.test.ts new file mode 100644 index 0000000..2c15d21 --- /dev/null +++ b/test/observation/RadecTopocentric.test.ts @@ -0,0 +1,118 @@ +import { DEG2RAD, Degrees, EpochUTC, J2000, Kilometers, RadecTopocentric, Radians, Vector3D } from '../../src/main'; +describe('RadecTopocentric', () => { + let radec: RadecTopocentric; + let exampleDate: Date; + + beforeEach(() => { + exampleDate = new Date(1705109326817); + radec = new RadecTopocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 0 * DEG2RAD as Radians, + ); + }); + + it('should be constructable', () => { + expect(radec).toMatchSnapshot(); + }); + + // fromDegrees + it('should be constructable from degrees', () => { + const radecDeg = RadecTopocentric.fromDegrees( + EpochUTC.fromDateTime(exampleDate), + 25 as Degrees, + 0 as Degrees, + ); + + expect(radecDeg).toMatchSnapshot(); + }); + + // fromStateVector + it('should be constructable from a state vector', () => { + const site = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(8000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + const state = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(7000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + const radecSv = RadecTopocentric.fromStateVector(state, site); + + expect(radecSv).toMatchSnapshot(); + }); + + // rightAscensionDegrees + it('should return the right ascension in degrees', () => { + expect(radec.rightAscensionDegrees).toMatchSnapshot(); + }); + + // declinationDegrees + it('should return the declination in degrees', () => { + expect(radec.declinationDegrees).toMatchSnapshot(); + }); + + // rightAscensionRateDegrees + it('should return the right ascension rate in degrees', () => { + expect(radec.rightAscensionRateDegrees).toMatchSnapshot(); + }); + + // declinationRateDegrees + it('should return the declination rate in degrees', () => { + expect(radec.declinationRateDegrees).toMatchSnapshot(); + }); + + // position + it('should return the position', () => { + const site = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(8000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + + expect(radec.position(site)).toMatchSnapshot(); + }); + + // velocity + it('should return the velocity', () => { + const radec2 = new RadecTopocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 1 * DEG2RAD as Radians, + 1000 as Kilometers, + 1.5 * DEG2RAD as Radians, + 2.5 * DEG2RAD as Radians, + ); + const site = new J2000( + EpochUTC.fromDateTime(exampleDate), + new Vector3D(8000 as Kilometers, 7000 as Kilometers, 8000 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); + + expect(radec2.velocity(site)).toMatchSnapshot(); + }); + + // angle + it('should return the angle', () => { + const radec2 = new RadecTopocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 1 * DEG2RAD as Radians, + ); + + expect(radec.angle(radec2)).toMatchSnapshot(); + }); + + // angleDegrees + it('should return the angle in degrees', () => { + const radec2 = new RadecTopocentric( + EpochUTC.fromDateTime(exampleDate), + 25 * DEG2RAD as Radians, + 1 * DEG2RAD as Radians, + ); + + expect(radec.angleDegrees(radec2)).toMatchSnapshot(); + }); +}); diff --git a/test/observation/__snapshots__/RAE.test.ts.snap b/test/observation/__snapshots__/RAE.test.ts.snap new file mode 100644 index 0000000..8354c1d --- /dev/null +++ b/test/observation/__snapshots__/RAE.test.ts.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RAE should be constructable 1`] = ` +RAE { + "azimuth": 0.17453292519943295, + "azimuthRate": undefined, + "elevation": 0.3490658503988659, + "elevationRate": undefined, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": 1000, + "rangeRate": undefined, +} +`; + +exports[`RAE should be constructable from degrees 1`] = ` +RAE { + "azimuth": 0.17453292519943295, + "azimuthRate": undefined, + "elevation": 0.3490658503988659, + "elevationRate": undefined, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": 1000, + "rangeRate": undefined, +} +`; + +exports[`RAE should return a RAE object from a state vector 1`] = ` +RAE { + "azimuth": 4.202021284209623, + "azimuthRate": 0.00043952100964508644, + "elevation": 0.5130090849662382, + "elevationRate": -0.0003354532635408476, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": 999.9999999999991, + "rangeRate": 0.5104272486229879, +} +`; + +exports[`RAE should return a state vector 1`] = ` +J2000 { + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "position": Vector3D { + "x": 5649.940123598527, + "y": 6839.039773929244, + "z": 8922.794608001712, + }, + "velocity": Vector3D { + "x": -0.4986876341007287, + "y": 0.41048541483137624, + "z": 0.0011464115053515227, + }, +} +`; + +exports[`RAE should return a string representation 1`] = ` +"[RazEl] + Epoch: 2024-01-13T01:28:46.817Z + Azimuth: 10.0000° + Elevation: 20.0000° + Range: 1000.000 km" +`; + +exports[`RAE should return the angle 1`] = `0.11878030341845575`; + +exports[`RAE should return the angle 2`] = `0.11878030341845577`; + +exports[`RAE should return the angle in degrees 1`] = `6.805610075160859`; + +exports[`RAE should return the azimuth in degrees 1`] = `10`; + +exports[`RAE should return the azimuth rate in degrees 1`] = `undefined`; + +exports[`RAE should return the elevation in degrees 1`] = `20`; + +exports[`RAE should return the elevation rate in degrees 1`] = `undefined`; + +exports[`RAE should return the position vector 1`] = ` +Vector3D { + "x": 5649.940123598527, + "y": 6839.039773929244, + "z": 8922.794608001712, +} +`; diff --git a/test/observation/__snapshots__/RadecGeocentric.test.ts.snap b/test/observation/__snapshots__/RadecGeocentric.test.ts.snap new file mode 100644 index 0000000..66bc4f2 --- /dev/null +++ b/test/observation/__snapshots__/RadecGeocentric.test.ts.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RadecGeocentric should be constructable 1`] = ` +RadecGeocentric { + "declination": 0, + "declinationRate": undefined, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": undefined, + "rangeRate": undefined, + "rightAscension": 0.4363323129985824, + "rightAscensionRate": undefined, +} +`; + +exports[`RadecGeocentric should be constructable from a state vector 1`] = ` +RadecGeocentric { + "declination": 0.679673818908244, + "declinationRate": 0, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": 12727.922061357855, + "rangeRate": 0, + "rightAscension": 0.7853981633974483, + "rightAscensionRate": -0, +} +`; + +exports[`RadecGeocentric should be constructable from degrees 1`] = ` +RadecGeocentric { + "declination": 0, + "declinationRate": undefined, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": undefined, + "rangeRate": undefined, + "rightAscension": 0.4363323129985824, + "rightAscensionRate": undefined, +} +`; + +exports[`RadecGeocentric should return the angle 1`] = `0.017453292519941554`; + +exports[`RadecGeocentric should return the angle in degrees 1`] = `0.9999999999999002`; + +exports[`RadecGeocentric should return the declination in degrees 1`] = `0`; + +exports[`RadecGeocentric should return the declination rate in degrees 1`] = `undefined`; + +exports[`RadecGeocentric should return the position 1`] = ` +Vector3D { + "x": 0.9063077870366499, + "y": 0.42261826174069944, + "z": 0, +} +`; + +exports[`RadecGeocentric should return the right ascension in degrees 1`] = `25`; + +exports[`RadecGeocentric should return the right ascension rate in degrees 1`] = `undefined`; + +exports[`RadecGeocentric should return the velocity 1`] = ` +Vector3D { + "x": -11.752592909334684, + "y": 23.401642759234754, + "z": 43.62658574738897, +} +`; diff --git a/test/observation/__snapshots__/RadecTopocentric.test.ts.snap b/test/observation/__snapshots__/RadecTopocentric.test.ts.snap new file mode 100644 index 0000000..e48b58f --- /dev/null +++ b/test/observation/__snapshots__/RadecTopocentric.test.ts.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RadecTopocentric should be constructable 1`] = ` +RadecTopocentric { + "declination": 0, + "declinationRate": undefined, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": undefined, + "rangeRate": undefined, + "rightAscension": 0.4363323129985824, + "rightAscensionRate": undefined, +} +`; + +exports[`RadecTopocentric should be constructable from a state vector 1`] = ` +RadecTopocentric { + "declination": 0, + "declinationRate": 0, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": 1000, + "rangeRate": 0, + "rightAscension": 3.141592653589793, + "rightAscensionRate": -0, +} +`; + +exports[`RadecTopocentric should be constructable from degrees 1`] = ` +RadecTopocentric { + "declination": 0, + "declinationRate": undefined, + "epoch": EpochUTC { + "posix": 1705109326.817, + }, + "range": undefined, + "rangeRate": undefined, + "rightAscension": 0.4363323129985824, + "rightAscensionRate": undefined, +} +`; + +exports[`RadecTopocentric should return the angle 1`] = `0.017453292519941554`; + +exports[`RadecTopocentric should return the angle in degrees 1`] = `0.9999999999999002`; + +exports[`RadecTopocentric should return the declination in degrees 1`] = `0`; + +exports[`RadecTopocentric should return the declination rate in degrees 1`] = `undefined`; + +exports[`RadecTopocentric should return the position 1`] = ` +Vector3D { + "x": 8000.906307787037, + "y": 7000.42261826174, + "z": 8000, +} +`; + +exports[`RadecTopocentric should return the right ascension in degrees 1`] = `25`; + +exports[`RadecTopocentric should return the right ascension rate in degrees 1`] = `undefined`; + +exports[`RadecTopocentric should return the velocity 1`] = ` +Vector3D { + "x": -11.752592909334684, + "y": 23.401642759234754, + "z": 43.62658574738897, +} +`; diff --git a/test/time/Epoch.test.ts b/test/time/Epoch.test.ts new file mode 100644 index 0000000..a730dc0 --- /dev/null +++ b/test/time/Epoch.test.ts @@ -0,0 +1,118 @@ +import { Epoch } from '../../src/main'; + +describe('Epoch', () => { + it('should be constructed from a POSIX timestamp', () => { + const posix = 1614556800; + const epoch = new Epoch(posix); + + expect(epoch.posix).toEqual(posix); + }); + + // toString + it('should convert to a string', () => { + const posix = 1614556800; + const epoch = new Epoch(posix); + + expect(epoch.toString()).toMatchSnapshot(); + }); + + // toExcelString + it('should convert to an Excel string', () => { + const posix = 1614556800; + const epoch = new Epoch(posix); + + expect(epoch.toExcelString()).toMatchSnapshot(); + }); + + // difference + it('should calculate the difference between two epochs', () => { + const posix1 = 1614556800; + const posix2 = 1614556800 + 60; + const epoch1 = new Epoch(posix1); + const epoch2 = new Epoch(posix2); + + expect(epoch1.difference(epoch2)).toEqual(-60); + }); + + // equals + it('should check if two epochs are equal', () => { + const posix1 = 1614556800; + const posix2 = 1614556800 + 60; + const epoch1 = new Epoch(posix1); + const epoch2 = new Epoch(posix2); + + expect(epoch1.equals(epoch2)).toEqual(false); + }); + + // toDateTime + it('should convert to a DateTime object', () => { + const posix = 1614556800; + const epoch = new Epoch(posix); + + expect(epoch.toDateTime()).toMatchSnapshot(); + }); + + // toEpochYearAndDay + it('should convert to an epoch year and day', () => { + const posix = 1614556800; + const epoch = new Epoch(posix); + + expect(epoch.toEpochYearAndDay()).toMatchSnapshot(); + }); + + // toJulianDate + it('should convert to a Julian date', () => { + const posix = 1614556800; + const epoch = new Epoch(posix); + + expect(epoch.toJulianDate()).toMatchSnapshot(); + }); + + // toJulianCenturies + it('should convert to Julian centuries', () => { + const posix = 1614556800; + const epoch = new Epoch(posix); + + expect(epoch.toJulianCenturies()).toMatchSnapshot(); + }); + + // operatorGreaterThan + it('should check if one epoch is greater than another', () => { + const posix1 = 1614556800; + const posix2 = 1614556800 + 60; + const epoch1 = new Epoch(posix1); + const epoch2 = new Epoch(posix2); + + expect(epoch1.operatorGreaterThan(epoch2)).toEqual(false); + }); + + // operatorGreaterThanOrEqual + it('should check if one epoch is greater than or equal to another', () => { + const posix1 = 1614556800; + const posix2 = 1614556800 + 60; + const epoch1 = new Epoch(posix1); + const epoch2 = new Epoch(posix2); + + expect(epoch1.operatorGreaterThanOrEqual(epoch2)).toEqual(false); + }); + + // operatorLessThan + it('should check if one epoch is less than another', () => { + const posix1 = 1614556800; + const posix2 = 1614556800 + 60; + const epoch1 = new Epoch(posix1); + const epoch2 = new Epoch(posix2); + + expect(epoch1.operatorLessThan(epoch2)).toEqual(true); + }); + + // operatorLessThanOrEqual + it('should check if one epoch is less than or equal to another', () => { + const posix1 = 1614556800; + const posix2 = 1614556800 + 60; + const epoch1 = new Epoch(posix1); + const epoch2 = new Epoch(posix2); + + expect(epoch1.operatorLessThanOrEqual(epoch2)).toEqual(true); + }); +}); diff --git a/test/time/EpochGPS.test.ts b/test/time/EpochGPS.test.ts new file mode 100644 index 0000000..5aafdbd --- /dev/null +++ b/test/time/EpochGPS.test.ts @@ -0,0 +1,38 @@ +import { EpochGPS, EpochUTC } from '../../src/main'; + +describe('EpochGPS', () => { + let reference: EpochUTC; + + beforeAll(() => { + reference = EpochUTC.fromDateTimeString('1980-01-06T00:00:00.000Z'); + }); + + it('should be constructed from a week and seconds', () => { + const week = 1; + const seconds = 5; + const epoch = new EpochGPS(week, seconds, reference); + + expect(epoch.week).toEqual(week); + expect(epoch.seconds).toEqual(seconds); + expect(epoch.week10Bit).toMatchSnapshot(); + expect(epoch.week13Bit).toMatchSnapshot(); + }); + + // toString + it('should convert to a string', () => { + const week = 1; + const seconds = 5; + const epoch = new EpochGPS(week, seconds, reference); + + expect(epoch.toString()).toMatchSnapshot(); + }); + + // toUTC + it('should convert to a UTC epoch', () => { + const week = 1; + const seconds = 5; + const epoch = new EpochGPS(week, seconds, reference); + + expect(epoch.toUTC()).toMatchSnapshot(); + }); +}); diff --git a/test/time/EpochUTC.test.ts b/test/time/EpochUTC.test.ts new file mode 100644 index 0000000..5c7f0b9 --- /dev/null +++ b/test/time/EpochUTC.test.ts @@ -0,0 +1,103 @@ +import { EpochUTC } from '../../src/main'; + +describe('EpochGPS', () => { + it('now', () => { + const epoch = EpochUTC.now(); + const now = new Date(); + + expect(epoch.posix).toBeCloseTo(now.getTime() / 1000, 2); + }); + + it('fromDate', () => { + const epoch = EpochUTC.fromDate({ year: 2021, month: 3, day: 1 }); + + expect(epoch).toMatchSnapshot(); + }); + + it('fromDateTime', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch).toMatchSnapshot(); + }); + + it('fromDateTimeString', () => { + const epoch = EpochUTC.fromDateTimeString('2021-03-01'); + + expect(epoch).toMatchSnapshot(); + }); + + it('fromJ2000TTSeconds', () => { + const epoch = EpochUTC.fromJ2000TTSeconds(0); + + expect(epoch).toMatchSnapshot(); + }); + + it('fromDefinitiveString', () => { + const epoch = EpochUTC.fromDefinitiveString('1/2021 00:00:00'); + + expect(epoch).toMatchSnapshot(); + }); + + // roll + it('should roll the epoch forward', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.roll(60)).toMatchSnapshot(); + }); + + // toMjd + it('should convert to an MJD', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.toMjd()).toMatchSnapshot(); + }); + + // toMjdGsfc + it('should convert to an MJD GSFC', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.toMjdGsfc()).toMatchSnapshot(); + }); + + // toTAI + it('should convert to a TAI epoch', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.toTAI()).toMatchSnapshot(); + }); + + // toTT + it('should convert to a TT epoch', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.toTT()).toMatchSnapshot(); + }); + + // toTDB + it('should convert to a TDB epoch', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.toTDB()).toMatchSnapshot(); + }); + + // toGPS + it('should convert to a GPS epoch', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.toGPS()).toMatchSnapshot(); + }); + + // gmstAngle + it('should calculate the GMST angle', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.gmstAngle()).toMatchSnapshot(); + }); + + // gmstAngleDegrees + it('should calculate the GMST angle in degrees', () => { + const epoch = EpochUTC.fromDateTime(new Date(2021, 2, 1)); + + expect(epoch.gmstAngleDegrees()).toMatchSnapshot(); + }); +}); diff --git a/test/time/EpochWindow.test.ts b/test/time/EpochWindow.test.ts new file mode 100644 index 0000000..b20479a --- /dev/null +++ b/test/time/EpochWindow.test.ts @@ -0,0 +1,12 @@ +import { EpochUTC, EpochWindow } from '../../src/main'; + +describe('EpochWindow', () => { + it('should be constructed from a start and end epoch', () => { + const start = EpochUTC.fromDateTimeString('1980-01-06T00:00:00.000Z'); + const end = EpochUTC.fromDateTimeString('1980-01-07T00:00:00.000Z'); + const epoch = new EpochWindow(start, end); + + expect(epoch.start).toEqual(start); + expect(epoch.end).toEqual(end); + }); +}); diff --git a/test/time/TimeStamped.test.ts b/test/time/TimeStamped.test.ts new file mode 100644 index 0000000..4117441 --- /dev/null +++ b/test/time/TimeStamped.test.ts @@ -0,0 +1,10 @@ +import { EpochUTC, TimeStamped } from '../../src/main'; + +describe('TimeStamped', () => { + it('should be constructed from an epoch', () => { + const epoch = EpochUTC.fromDateTimeString('1980-01-06T00:00:00.000Z'); + const timeStamped = new TimeStamped(epoch, 'test'); + + expect(timeStamped).toMatchSnapshot(); + }); +}); diff --git a/test/time/__snapshots__/Epoch.test.ts.snap b/test/time/__snapshots__/Epoch.test.ts.snap new file mode 100644 index 0000000..7f4097b --- /dev/null +++ b/test/time/__snapshots__/Epoch.test.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Epoch should convert to Julian centuries 1`] = `0.21162217659137578`; + +exports[`Epoch should convert to a DateTime object 1`] = `2021-03-01T00:00:00.000Z`; + +exports[`Epoch should convert to a Julian date 1`] = `2459274.5`; + +exports[`Epoch should convert to a string 1`] = `"2021-03-01T00:00:00.000Z"`; + +exports[`Epoch should convert to an Excel string 1`] = `"2021-03-01T00:00:00"`; + +exports[`Epoch should convert to an epoch year and day 1`] = ` +Object { + "epochDay": "060.00000000", + "epochYr": "21", +} +`; diff --git a/test/time/__snapshots__/EpochGPS.test.ts.snap b/test/time/__snapshots__/EpochGPS.test.ts.snap new file mode 100644 index 0000000..76e3abe --- /dev/null +++ b/test/time/__snapshots__/EpochGPS.test.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EpochGPS should be constructed from a week and seconds 1`] = `1`; + +exports[`EpochGPS should be constructed from a week and seconds 2`] = `1`; + +exports[`EpochGPS should convert to a UTC epoch 1`] = ` +EpochUTC { + "posix": 316569605, +} +`; + +exports[`EpochGPS should convert to a string 1`] = `"1:5.000"`; diff --git a/test/time/__snapshots__/EpochUTC.test.ts.snap b/test/time/__snapshots__/EpochUTC.test.ts.snap new file mode 100644 index 0000000..c3d7a35 --- /dev/null +++ b/test/time/__snapshots__/EpochUTC.test.ts.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EpochGPS fromDate 1`] = ` +EpochUTC { + "posix": 1614556800, +} +`; + +exports[`EpochGPS fromDateTime 1`] = ` +EpochUTC { + "posix": 1614574800, +} +`; + +exports[`EpochGPS fromDateTimeString 1`] = ` +EpochUTC { + "posix": 1614556800, +} +`; + +exports[`EpochGPS fromDefinitiveString 1`] = ` +EpochUTC { + "posix": 1609459200, +} +`; + +exports[`EpochGPS fromJ2000TTSeconds 1`] = ` +EpochUTC { + "posix": 946727935.816, +} +`; + +exports[`EpochGPS should calculate the GMST angle 1`] = `4.088037525758033`; + +exports[`EpochGPS should calculate the GMST angle in degrees 1`] = `234.22729671703883`; + +exports[`EpochGPS should convert to a GPS epoch 1`] = ` +EpochGPS { + "seconds": 104417.99999993818, + "week": 2147, +} +`; + +exports[`EpochGPS should convert to a TAI epoch 1`] = ` +EpochTAI { + "posix": 1614574837, +} +`; + +exports[`EpochGPS should convert to a TDB epoch 1`] = ` +EpochTDB { + "posix": 1614574869.1853862, +} +`; + +exports[`EpochGPS should convert to a TT epoch 1`] = ` +EpochTT { + "posix": 1614574869.184, +} +`; + +exports[`EpochGPS should convert to an MJD 1`] = `59274.20833333349`; + +exports[`EpochGPS should convert to an MJD GSFC 1`] = `29274.70833333349`; + +exports[`EpochGPS should roll the epoch forward 1`] = ` +EpochUTC { + "posix": 1614574860, +} +`; diff --git a/test/time/__snapshots__/TimeStamped.test.ts.snap b/test/time/__snapshots__/TimeStamped.test.ts.snap new file mode 100644 index 0000000..c9c12ec --- /dev/null +++ b/test/time/__snapshots__/TimeStamped.test.ts.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TimeStamped should be constructed from an epoch 1`] = ` +TimeStamped { + "epoch": EpochUTC { + "posix": 315964800, + }, + "value": "test", +} +`;