From d923dae7834c13efe404749dea0dea8543bf9737 Mon Sep 17 00:00:00 2001 From: Theodore Kruczek Date: Sun, 14 Jan 2024 19:49:47 -0500 Subject: [PATCH] test: :white_check_mark: add tests for StateVector --- examples/satellite-js-migration.ts | 16 +- src/coordinate/ClassicalElements.ts | 5 +- src/coordinate/EquinoctialElements.ts | 2 +- src/coordinate/StateVector.ts | 81 +++-- src/types/types.ts | 342 ++++++++++-------- test/coordinate/ITRF.test.ts | 53 ++- test/coordinate/J2000.test.ts | 62 ++++ .../__snapshots__/J2000.test.ts.snap | 26 ++ 8 files changed, 392 insertions(+), 195 deletions(-) create mode 100644 test/coordinate/__snapshots__/J2000.test.ts.snap diff --git a/examples/satellite-js-migration.ts b/examples/satellite-js-migration.ts index 7ce5cce..2246d13 100644 --- a/examples/satellite-js-migration.ts +++ b/examples/satellite-js-migration.ts @@ -5,7 +5,7 @@ import { calcGmst, DEG2RAD, Degrees, - GroundPosition, + GroundObject, Kilometers, lla2eci, Radians, @@ -28,9 +28,9 @@ const satellite = new Satellite({ tle2, }); -const elements = satellite.getClassicalElements(new Date(1705109326817)).toEquinoctialElements(); +const period = satellite.toJ2000(new Date(1705109326817)).period; -console.warn(elements); +console.warn(period); /* * You can still propagate a satellite using time since epoch (in minutes), but @@ -62,7 +62,7 @@ const positionEci2 = satellite.eci().position; // This is correctly typed * Set the Observer at 71°W, 41°N, 0.37 km altitude using DEGREES because who * likes using Radians? */ -const observer = new GroundPosition({ +const observer = new GroundObject({ lon: -71.0308 as Degrees, lat: 41.9613422 as Degrees, alt: 0.37 as Kilometers, @@ -78,7 +78,7 @@ const { gmst, j } = calcGmst(new Date()); const positionEcf = satellite.ecf(exampleDate); const observerEcf = observer.ecf(); const positionGd = satellite.lla(exampleDate); -const lookAngles = satellite.rae(observer, exampleDate); +const lookAngles = satellite.toRae(observer, exampleDate); // This never worked in satellite.js, but it does now! const uplinkFreq = 420e6; const dopplerFactor = satellite.dopplerFactor(observer, exampleDate); @@ -110,9 +110,9 @@ const rangeRate = lookAngles.rangeRate; // Kilometers/Second * There is a built in cache to allow fast retrieval of repeated calculations. * This means you can make repeat calls to `.rae()` for minimal performance hit. */ -const rangeCache = satellite.rae(observer, exampleDate).range; -const azimuthCached = satellite.rae(observer, exampleDate).azimuth; -const elevationCached = satellite.rae(observer, exampleDate).elevation; +const rangeCache = satellite.rae(observer, exampleDate).rng; +const azimuthCached = satellite.rae(observer, exampleDate).az; +const elevationCached = satellite.rae(observer, exampleDate).el; const latitudeCached = satellite.lla(exampleDate).lat; const longitudeCached = satellite.lla(exampleDate).lon; const heightCached = satellite.lla(exampleDate).alt; diff --git a/src/coordinate/ClassicalElements.ts b/src/coordinate/ClassicalElements.ts index 7906c46..17a8934 100644 --- a/src/coordinate/ClassicalElements.ts +++ b/src/coordinate/ClassicalElements.ts @@ -5,7 +5,8 @@ import { earthGravityParam, RAD2DEG, sec2min, secondsPerDay, TAU } from '../util import { clamp, matchHalfPlane, newtonNu } from '../utils/functions'; import { EquinoctialElements } from './EquinoctialElements'; import { OrbitRegime } from '../enums/OrbitRegime'; -import { PositionVelocity, StateVector } from './StateVector'; +import { StateVector } from './StateVector'; +import { PositionVelocity } from 'src/types/types'; import { ClassicalElementsParams } from '../interfaces/ClassicalElementsParams'; /** @@ -70,7 +71,7 @@ export class ClassicalElements { } const pos = state.position; const vel = state.velocity; - const a = state.semimajorAxis(); + const a = state.semimajorAxis; const eVecA = pos.scale(vel.magnitude() ** 2 - mu / pos.magnitude()); const eVecB = vel.scale(pos.dot(vel)); const eVec = eVecA.subtract(eVecB).scale(1 / mu); diff --git a/src/coordinate/EquinoctialElements.ts b/src/coordinate/EquinoctialElements.ts index be201c0..039bf81 100644 --- a/src/coordinate/EquinoctialElements.ts +++ b/src/coordinate/EquinoctialElements.ts @@ -3,7 +3,7 @@ import { EpochUTC } from '../time/EpochUTC'; import { earthGravityParam, secondsPerDay, TAU } from '../utils/constants'; import { newtonM } from '../utils/functions'; import { ClassicalElements } from './ClassicalElements'; -import { PositionVelocity } from './StateVector'; +import { PositionVelocity } from 'src/types/types'; import { EquinoctialElementsParams } from '../interfaces/EquinoctialElementsParams'; /** diff --git a/src/coordinate/StateVector.ts b/src/coordinate/StateVector.ts index cc9c42d..6559ee5 100644 --- a/src/coordinate/StateVector.ts +++ b/src/coordinate/StateVector.ts @@ -1,30 +1,39 @@ -import { Kilometers } from 'src/main'; +import { Kilometers, Minutes } from 'src/main'; import { Earth } from '../body/Earth'; import type { Vector3D } from '../operations/Vector3D'; import type { EpochUTC } from '../time/EpochUTC'; import { TAU } from '../utils/constants'; import { ClassicalElements } from './ClassicalElements'; -// / Position and velocity [Vector3D] container. -export type PositionVelocity = { +/** + * A state vector is a set of coordinates used to specify the position and + * velocity of an object in a particular reference frame. + */ +export abstract class StateVector { + epoch: EpochUTC; position: Vector3D; velocity: Vector3D; -}; - -// / Base class for state vectors. -export abstract class StateVector { - constructor( - public epoch: EpochUTC, - public position: Vector3D, - public velocity: Vector3D, - ) { - // Nothing to do here. + constructor(epoch: EpochUTC, position: Vector3D, velocity: Vector3D) { + this.epoch = epoch; + this.position = position; + this.velocity = velocity; } + /** + * The name of the reference frame in which the state vector is defined. + */ abstract get name(): string; + /** + * Whether the state vector is defined in an inertial reference frame. + */ abstract get inertial(): boolean; + /** + * Returns a string representation of the StateVector object. The string + * includes the name, epoch, position, and velocity. + * @returns A string representation of the StateVector object. + */ toString(): string { return [ `[${this.name}]`, @@ -34,31 +43,59 @@ export abstract class StateVector { ].join('\n'); } - mechanicalEnergy(): number { + /** + * Calculates the mechanical energy of the state vector. + * + * @returns The mechanical energy value. + */ + get mechanicalEnergy(): number { const r = this.position.magnitude(); const v = this.velocity.magnitude(); return v * v * 0.5 - Earth.mu / r; } - semimajorAxis(): number { - const energy = this.mechanicalEnergy(); + /** + * Calculates the semimajor axis of the state vector. + * + * @returns The semimajor axis in kilometers. + */ + get semimajorAxis(): Kilometers { + const energy = this.mechanicalEnergy; - return -Earth.mu / (2.0 * energy); + return (-Earth.mu / (2.0 * energy)) as Kilometers; } - period(): number { - const a = this.semimajorAxis(); + /** + * Gets the period of the state vector in minutes. + * + * @returns The period in minutes. + */ + get period(): Minutes { + const a = this.semimajorAxis; + const periodSeconds = TAU * Math.sqrt((a * a * a) / Earth.mu); - return TAU * Math.sqrt((a * a * a) / Earth.mu); + return (periodSeconds / 60.0) as Minutes; } - angularRate(): number { - const a = this.semimajorAxis(); + /** + * Gets the angular rate of the state vector. + * + * @returns The angular rate. + */ + get angularRate(): number { + const a = this.semimajorAxis; return Math.sqrt(Earth.mu / (a * a * a)); } + /** + * Converts the state vector to classical elements. + * @param mu The gravitational parameter of the celestial body. Defaults to + * Earth's gravitational parameter. @returns The classical elements + * corresponding to the state vector. @throws Error if classical elements are + * undefined for fixed frames. + */ toClassicalElements({ mu = Earth.mu }: { mu?: number } = {}): ClassicalElements { if (!this.inertial) { throw new Error('Classical elements are undefined for fixed frames.'); diff --git a/src/types/types.ts b/src/types/types.ts index adc4222..deae82c 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,16 +1,19 @@ import { Satellite } from 'src/objects'; import { PassType } from '../enums/PassType'; +import { Vector3D } from 'src/main'; /** * Represents a distinct type. * - * This type is used to create new types based on existing ones, but with a unique identifier. - * This can be useful for creating types that are semantically different but structurally the same. + * This type is used to create new types based on existing ones, but with a + * unique identifier. This can be useful for creating types that are + * semantically different but structurally the same. * * @template T The base type from which the distinct type is created. * @template DistinctName A unique identifier for the distinct type. * - * @property __TYPE__ A property that holds the unique identifier for the distinct type. + * @property __TYPE__ A property that holds the unique identifier for the + * distinct type. */ type Distinct = T & { __TYPE__: DistinctName }; @@ -19,91 +22,92 @@ type Distinct = T & { __TYPE__: DistinctName }; /** * Represents a quantity of days. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Days = Distinct; /** * Represents a quantity of hours. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Hours = Distinct; /** * Represents a quantity of minutes. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Minutes = Distinct; /** * Represents a quantity of seconds. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Seconds = Distinct; /** * Represents a quantity of milliseconds. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Milliseconds = Distinct; /** * Represents a quantity of degrees. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Degrees = Distinct; /** * Represents a quantity of radians. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Radians = Distinct; /** * Represents a quantity of kilometers. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Kilometers = Distinct; /** * Represents a quantity of meters. * - * This type is based on the number type, but is distinct and cannot be used interchangeably - * with other number-based types. + * This type is based on the number type, but is distinct and cannot be used + * interchangeably with other number-based types. */ export type Meters = Distinct; /** * Represents a three-dimensional vector. * - * This type is used to represent a point in space in terms of x, y, and z coordinates. - * It is a generic type that allows for flexibility in the units of measure used for each dimension. - * The default unit of measure is Kilometers. + * This type is used to represent a point in space in terms of x, y, and z + * coordinates. It is a generic type that allows for flexibility in the units of + * measure used for each dimension. The default unit of measure is Kilometers. * - * @template Units The unit of measure used for the dimensions. - * This is typically a type representing a distance, such as kilometers or meters. The default is Kilometers. + * @template Units The unit of measure used for the dimensions. This is + * typically a type representing a distance, such as kilometers or meters. The + * default is Kilometers. * - * @property x The x dimension of the vector, representing the distance from the origin to - * the point in the x direction. - * @property y The y dimension of the vector, representing the distance from the origin to - * the point in the y direction. - * @property z The z dimension of the vector, representing the distance from the origin to - * the point in the z direction. + * @property x The x dimension of the vector, representing the distance from the + * origin to the point in the x direction. + * @property y The y dimension of the vector, representing the distance from the + * origin to the point in the y direction. @property z The z dimension of the + * vector, representing the distance from the origin to the point in the z + * direction. */ export type Vec3 = { x: Units; @@ -112,89 +116,98 @@ export type Vec3 = { }; /** - * Represents a three-dimensional vector in Earth-Centered Inertial (ECI) coordinates. + * Represents a three-dimensional vector in Earth-Centered Inertial (ECI) + * coordinates. * - * This type is used to represent a point in space in terms of x, y, and z coordinates. - * It is a generic type that allows for flexibility in the units of measure used for each dimension. - * The default unit of measure is Kilometers. + * This type is used to represent a point in space in terms of x, y, and z + * coordinates. It is a generic type that allows for flexibility in the units of + * measure used for each dimension. The default unit of measure is Kilometers. * - * @property x The x dimension of the vector, representing the distance from the origin to - * the point in the x direction. - * @property y The y dimension of the vector, representing the distance from the origin to - * the point in the y direction. - * @property z The z dimension of the vector, representing the distance from the origin to - * the point in the z direction. + * @property x The x dimension of the vector, representing the distance from the + * origin to the point in the x direction. + * @property y The y dimension of the vector, representing the distance from the + * origin to the point in the y direction. @property z The z dimension of the + * vector, representing the distance from the origin to the point in the z + * direction. */ export type EciVec3 = Vec3; /** - * Represents a three-dimensional vector in Earth-Centered Fixed (ECF) coordinates. + * Represents a three-dimensional vector in Earth-Centered Fixed (ECF) + * coordinates. * - * NOTE: ECF (Earth-Centered Fixed) and ECEF (Earth-Centered, Earth-Fixed) are essentially - * the same thing. Both refer to a coordinate system that is fixed with respect to the Earth, - * meaning that the coordinates of a point in this system do not change even as the Earth rotates. + * NOTE: ECF (Earth-Centered Fixed) and ECEF (Earth-Centered, Earth-Fixed) are + * essentially the same thing. Both refer to a coordinate system that is fixed + * with respect to the Earth, meaning that the coordinates of a point in this + * system do not change even as the Earth rotates. * - * The difference between the two is that ECF is a Cartesian coordinate system, while ECEF is a - * spherical coordinate system. The ECF system is used in this library because it is easier to - * work with in the context of the SGP4 algorithm. + * The difference between the two is that ECF is a Cartesian coordinate system, + * while ECEF is a spherical coordinate system. The ECF system is used in this + * library because it is easier to work with in the context of the SGP4 + * algorithm. * - * This type is used to represent a point in space in terms of x, y, and z coordinates. - * It is a generic type that allows for flexibility in the units of measure used for each dimension. - * The default unit of measure is Kilometers. + * This type is used to represent a point in space in terms of x, y, and z + * coordinates. It is a generic type that allows for flexibility in the units of + * measure used for each dimension. The default unit of measure is Kilometers. * - * @property x The x dimension of the vector, representing the distance from the origin to - * the point in the x direction. - * @property y The y dimension of the vector, representing the distance from the origin to - * the point in the y direction. - * @property z The z dimension of the vector, representing the distance from the origin to - * the point in the z direction. + * @property x The x dimension of the vector, representing the distance from the + * origin to the point in the x direction. + * @property y The y dimension of the vector, representing the distance from the + * origin to the point in the y direction. @property z The z dimension of the + * vector, representing the distance from the origin to the point in the z + * direction. */ export type EcfVec3 = Vec3; /** - * Represents a three-dimensional vector in Earth-Centered Earth-Fixed (ECEF) coordinates. + * Represents a three-dimensional vector in Earth-Centered Earth-Fixed (ECEF) + * coordinates. */ export type EcefVec3 = EcfVec3; /** * Represents a three-dimensional vector in East, North, Up (ENU) coordinates. * - * East–west tangent to parallels, North–south tangent to meridians, and - * Up–down in the direction normal to the oblate spheroid used as Earth's ellipsoid, + * East–west tangent to parallels, North–south tangent to meridians, and Up–down + * in the direction normal to the oblate spheroid used as Earth's ellipsoid, * which does not generally pass through the center of Earth. * * In many targeting and tracking applications the local East, North, Up (ENU) * Cartesian coordinate system is far more intuitive and practical than ECEF or - * Geodetic coordinates. The local ENU coordinates are formed from a plane tangent - * to the Earth's surface fixed to a specific location and hence it is sometimes - * known as a "Local Tangent" or "local geodetic" plane. By convention the east axis - * is labeled x, the north y, and the up z. + * Geodetic coordinates. The local ENU coordinates are formed from a plane + * tangent to the Earth's surface fixed to a specific location and hence it is + * sometimes known as a "Local Tangent" or "local geodetic" plane. By convention + * the east axis is labeled x, the north y, and the up z. * * @see https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates * - * @template Units The unit of measure used for the dimensions. - * This is typically a type representing a distance, such as kilometers or meters. The default is Kilometers. + * @template Units The unit of measure used for the dimensions. This is + * typically a type representing a distance, such as kilometers or meters. The + * default is Kilometers. * - * @property e The east dimension of the vector, representing the distance from the origin to - * the point in the east direction. - * @property n The north dimension of the vector, representing the distance from the origin to - * the point in the north direction. - * @property u The up dimension of the vector, representing the distance from the origin to - * the point in the upward direction. + * @property e The east dimension of the vector, representing the distance from + * the origin to the point in the east direction. + * @property n The north dimension of the vector, representing the distance from + * the origin to the point in the north direction. @property u The up dimension + * of the vector, representing the distance from the origin to the point in the + * upward direction. */ export type EnuVec3 = Vec3; /** * Represents a three-dimensional vector in geographical coordinates. * - * This type is used to represent a point in space in terms of latitude, longitude, and altitude. - * It is a generic type that allows for flexibility in the units of measure used for each dimension. + * This type is used to represent a point in space in terms of latitude, + * longitude, and altitude. It is a generic type that allows for flexibility in + * the units of measure used for each dimension. * - * @template A The unit of measure used for the latitude and longitude dimensions. - * This is typically a type representing an angle, such as degrees or radians. The default is Radians. + * @template A The unit of measure used for the latitude and longitude + * dimensions. This is typically a type representing an angle, such as degrees + * or radians. The default is Radians. * - * @template D The unit of measure used for the altitude dimension. - * This is typically a type representing a distance, such as kilometers or meters. The default is Kilometers. + * @template D The unit of measure used for the altitude dimension. This is + * typically a type representing a distance, such as kilometers or meters. The + * default is Kilometers. */ export type LlaVec3 = { lat: A; @@ -203,21 +216,27 @@ export type LlaVec3 = { }; /** - * Represents a three-dimensional vector in Range, Azimuth, and Elevation (RAE) coordinates. + * Represents a three-dimensional vector in Range, Azimuth, and Elevation (RAE) + * coordinates. * - * This type is used to represent a point in space in terms of range, azimuth, and elevation. - * It is a generic type that allows for flexibility in the units of measure used for each dimension. + * This type is used to represent a point in space in terms of range, azimuth, + * and elevation. It is a generic type that allows for flexibility in the units + * of measure used for each dimension. * * @template DistanceUnit The unit of measure used for the altitude dimension. - * This is typically a type representing a distance, such as kilometers or meters. The default is Kilometers. + * This is typically a type representing a distance, such as kilometers or + * meters. The default is Kilometers. * - * @template AngleUnit The unit of measure used for the latitude and longitude dimensions. - * This is typically a type representing an angle, such as degrees or radians. The default is Radians. + * @template AngleUnit The unit of measure used for the latitude and longitude + * dimensions. This is typically a type representing an angle, such as degrees + * or radians. The default is Radians. * - * @property rng The range dimension of the vector, representing the distance from the origin to the point. - * @property az The azimuth dimension of the vector, representing the angle in the horizontal plane from a - * reference direction. - * @property el The elevation dimension of the vector, representing the angle from the horizontal plane to the point. + * @property rng The range dimension of the vector, representing the distance + * from the origin to the point. + * @property az The azimuth dimension of the vector, representing the angle in + * the horizontal plane from a reference direction. @property el The elevation + * dimension of the vector, representing the angle from the horizontal plane to + * the point. */ export type RaeVec3 = { rng: DistanceUnit; @@ -226,14 +245,16 @@ export type RaeVec3 = { }; /** - * Represents a three-dimensional vector in South, East, and Zenith (SEZ) coordinates. + * Represents a three-dimensional vector in South, East, and Zenith (SEZ) + * coordinates. * - * This type is used to represent a point in space in terms of south, east, and zenith. - * It is a generic type that allows for flexibility in the units of measure used for each dimension. + * This type is used to represent a point in space in terms of south, east, and + * zenith. It is a generic type that allows for flexibility in the units of + * measure used for each dimension. * * @property s The south dimension of the vector - * @property e The east dimension of the vector - * @property z The zenith dimension of the vector + * @property e The east dimension of the vector @property z The zenith dimension + * of the vector */ export type SezVec3 = { s: D; @@ -242,8 +263,8 @@ export type SezVec3 = { }; /** - * @interface SatelliteRecord contains all of the orbital parameters necessary for running SGP4. - * It is generated by Sgp4.createSatrec. + * @interface SatelliteRecord contains all of the orbital parameters necessary + * for running SGP4. It is generated by Sgp4.createSatrec. * */ export interface SatelliteRecord { @@ -367,14 +388,17 @@ export interface SatelliteRecord { } /** - * The StateVector is a type that represents the output from the Sgp4.propagate function. - * It consists of two main properties: position and velocity, each of which is a three-dimensional vector. + * The StateVector is a type that represents the output from the Sgp4.propagate + * function. It consists of two main properties: position and velocity, each of + * which is a three-dimensional vector. * - * The position and velocity vectors are represented as objects with x, y, and z properties, - * each of which is a number. Alternatively, they can be a boolean value. + * The position and velocity vectors are represented as objects with x, y, and z + * properties, each of which is a number. Alternatively, they can be a boolean + * value. * - * This type is primarily used in the context of satellite tracking and prediction, - * where it is crucial to know both the current position and velocity of a satellite. + * This type is primarily used in the context of satellite tracking and + * prediction, where it is crucial to know both the current position and + * velocity of a satellite. */ export type StateVectorSgp4 = { position: @@ -399,18 +423,20 @@ export type PosVel = { }; /** - * The RUV coordinate system is a spherical coordinate system with the origin - * at the radar. The RUV coordinate system is defined with respect to the - * radar boresight. The R-axis points outward along the boresight with - * the origin at the radar. The U-axis is in the horizontal plane and - * points to the right of the boresight. The V-axis is in the vertical - * plane and points down from the boresight. + * The RUV coordinate system is a spherical coordinate system with the origin at + * the radar. The RUV coordinate system is defined with respect to the radar + * boresight. The R-axis points outward along the boresight with the origin at + * the radar. The U-axis is in the horizontal plane and points to the right of + * the boresight. The V-axis is in the vertical plane and points down from the + * boresight. * * @template DistanceUnit The unit of measure used for the altitude dimension. - * This is typically a type representing a distance, such as kilometers or meters. The default is Kilometers. + * This is typically a type representing a distance, such as kilometers or + * meters. The default is Kilometers. * - * * @template AngleUnit The unit of measure used for the latitude and longitude dimensions. - * This is typically a type representing an angle, such as degrees or radians. The default is Radians. + * * @template AngleUnit The unit of measure used for the latitude and longitude + * dimensions. This is typically a type representing an angle, such as degrees + * or radians. The default is Radians. */ export type RuvVec3 = { rng: DistanceUnit; @@ -419,14 +445,14 @@ export type RuvVec3 = { }; /** - * Phased Array Radar Face Cartesian Coordinates - * The cartesian coordinates (XRF, YRF ZRF) are defined with respect to the - * phased array radar face. The radar face lies in the XRF-YRF plane, with - * the XRF-axis horizontal and the YRF-axis pointing upward. - * The ZRF-axis points outward along the normal to the array face. + * Phased Array Radar Face Cartesian Coordinates The cartesian coordinates (XRF, + * YRF ZRF) are defined with respect to the phased array radar face. The radar + * face lies in the XRF-YRF plane, with the XRF-axis horizontal and the YRF-axis + * pointing upward. The ZRF-axis points outward along the normal to the array + * face. * - * The orientation of the phased array face is defined by the azimuth and - * the elevation of the phased array boresight (i.e., the phased array Z-axis). + * The orientation of the phased array face is defined by the azimuth and the + * elevation of the phased array boresight (i.e., the phased array Z-axis). */ export type RfVec3 = Vec3; @@ -434,45 +460,52 @@ export type RfVec3 = Vec3; * A type that represents a three-dimensional vector in a flat array format. * This type is used in vector mathematics and physics calculations. * - * It is an array of three numbers, where each number represents a coordinate in 3D space: + * It is an array of three numbers, where each number represents a coordinate in + * 3D space: * - The first number represents the x-coordinate. * - The second number represents the y-coordinate. * - The third number represents the z-coordinate. * - * This format is particularly useful in scenarios where you need to perform operations on vectors, - * such as addition, subtraction, scalar multiplication, dot product, and cross product. + * This format is particularly useful in scenarios where you need to perform + * operations on vectors, such as addition, subtraction, scalar multiplication, + * dot product, and cross product. */ export type Vec3Flat = [T, T, T]; /** - * A type that represents a two-line element set (TLE). - * A TLE is a data format used to convey sets of orbital elements that describe the orbits of Earth-orbiting objects. - * It consists of two lines of text, each of which is 69 characters long. + * A type that represents a two-line element set (TLE). A TLE is a data format + * used to convey sets of orbital elements that describe the orbits of + * Earth-orbiting objects. It consists of two lines of text, each of which is 69 + * characters long. * @see https://en.wikipedia.org/wiki/Two-line_element_set */ export type TleLine1 = Distinct; /** - * A type that represents a two-line element set (TLE). - * A TLE is a data format used to convey sets of orbital elements that describe the orbits of Earth-orbiting objects. - * It consists of two lines of text, each of which is 69 characters long. + * A type that represents a two-line element set (TLE). A TLE is a data format + * used to convey sets of orbital elements that describe the orbits of + * Earth-orbiting objects. It consists of two lines of text, each of which is 69 + * characters long. * @see https://en.wikipedia.org/wiki/Two-line_element_set */ export type TleLine2 = Distinct; /** * The Line1Data type represents the first line of a two-line element set (TLE). - * A TLE is a data format used to convey sets of orbital elements that describe the orbits of Earth-orbiting objects. + * A TLE is a data format used to convey sets of orbital elements that describe + * the orbits of Earth-orbiting objects. * * The properties of this type include: * - lineNumber1: The line number of the TLE (should be 1 for this line). * - satNum: The satellite number. * - satNumRaw: The raw string representation of the satellite number. - * - classification: The classification of the satellite (e.g., "U" for unclassified). + * - classification: The classification of the satellite (e.g., "U" for + * unclassified). * - intlDes: The international designator for the satellite. * - intlDesYear: The year of the international designator. * - intlDesLaunchNum: The launch number of the international designator. - * - intlDesLaunchPiece: The piece of the launch of the international designator. + * - intlDesLaunchPiece: The piece of the launch of the international + * designator. * - epochYear: The last two digits of the year of the epoch. * - epochYearFull: The full four-digit year of the epoch. * - epochDay: The day of the year of the epoch. @@ -506,8 +539,9 @@ export type Line1Data = { }; /** - * The Line2Data type represents the second line of a two-line element set (TLE). - * A TLE is a data format used to convey sets of orbital elements that describe the orbits of Earth-orbiting objects. + * The Line2Data type represents the second line of a two-line element set + * (TLE). A TLE is a data format used to convey sets of orbital elements that + * describe the orbits of Earth-orbiting objects. * * The properties of this type include: * - lineNumber2: The line number of the TLE (should be 2 for this line). @@ -579,22 +613,26 @@ export enum SpaceObjectType { /** * Represents the Greenwich Mean Sidereal Time (GMST). * - * GMST is a time system that is a measure of the angle, on the celestial equator, from the - * Greenwich meridian to the meridian that passes through the vernal equinox. + * GMST is a time system that is a measure of the angle, on the celestial + * equator, from the Greenwich meridian to the meridian that passes through the + * vernal equinox. */ export type GreenwichMeanSiderealTime = Distinct; /** - * Represents the azimuth and elevation of an object in the sky. - * Azimuth and elevation are the two coordinates that define the position of a - * celestial body (sun, moon, planet, star, etc.) in the sky as observed from a specific location - * on the Earth's surface. + * Represents the azimuth and elevation of an object in the sky. Azimuth and + * elevation are the two coordinates that define the position of a celestial + * body (sun, moon, planet, star, etc.) in the sky as observed from a specific + * location on the Earth's surface. * - * @template Units The units in which the azimuth and elevation are expressed. By default, this is radians. + * @template Units The units in which the azimuth and elevation are expressed. + * By default, this is radians. * - * @property az The azimuth of the object. This is the angle between the observer's north vector and - * the perpendicular projection of the object onto the observer's local horizon. - * @property el The elevation of the object. This is the angle between the object and the observer's local horizon. + * @property az The azimuth of the object. This is the angle between the + * observer's north vector and the perpendicular projection of the object onto + * the observer's local horizon. + * @property el The elevation of the object. This is the angle between the + * object and the observer's local horizon. */ export type AzEl = { az: Units; @@ -602,7 +640,8 @@ export type AzEl = { }; /** - * Represents the coordinates of a celestial object in Right Ascension (RA) and Declination (Dec). + * Represents the coordinates of a celestial object in Right Ascension (RA) and + * Declination (Dec). */ export type RaDec = { dec: Radians; @@ -615,8 +654,8 @@ export type RaDec = { * * @property solarNoon The time at which the sun is at its highest point in the * sky (directly above the observer's head). This is the midpoint of the day. - * @property nadir The time at which the sun is at its lowest point, directly below the observer. - * This is the midpoint of the night. + * @property nadir The time at which the sun is at its lowest point, directly + * below the observer. This is the midpoint of the night. */ export type SunTime = { solarNoon: Date; @@ -654,14 +693,13 @@ export type OperationsDetails = { }; /** * Represents a function that calculates the Jacobian matrix. - * @param xs - The input values as a Float64Array. - * @returns The Jacobian matrix as a Float64Array. + * @param xs - The input values as a Float64Array. @returns The Jacobian matrix + * as a Float64Array. */ export type JacobianFunction = (xs: Float64Array) => Float64Array; /** * Represents a differentiable function. - * @param x The input value. - * @returns The output value. + * @param x The input value. @returns The output value. */ export type DifferentiableFunction = (x: number) => number; @@ -714,3 +752,9 @@ export type TleParams = { /** alpha 5 satellite number */ scc: string; }; +// / Position and velocity [Vector3D] container. + +export type PositionVelocity = { + position: Vector3D; + velocity: Vector3D; +}; diff --git a/test/coordinate/ITRF.test.ts b/test/coordinate/ITRF.test.ts index c739206..a44ce4f 100644 --- a/test/coordinate/ITRF.test.ts +++ b/test/coordinate/ITRF.test.ts @@ -8,7 +8,11 @@ describe('ITRF', () => { beforeEach(() => { epochUtc = EpochUTC.fromDateTime(exampleDate); - itrf = new ITRF(epochUtc, new Vector3D(1000, 2000, 3000), new Vector3D(10, 20, 30)); + itrf = new ITRF( + epochUtc, + new Vector3D(1000 as Kilometers, 2000 as Kilometers, 3000 as Kilometers), + new Vector3D(10 as Kilometers, 20 as Kilometers, 30 as Kilometers), + ); }); // can get the name of the ITRF coordinate system @@ -46,7 +50,11 @@ describe('ITRF', () => { // can handle position at the center of the Earth it('should handle position at the center of the Earth', () => { - itrf = new ITRF(epochUtc, new Vector3D(0, 0, 0), new Vector3D(0, 0, 0)); + itrf = new ITRF( + epochUtc, + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); expect(itrf.toJ2000()).toMatchSnapshot(); }); @@ -55,7 +63,7 @@ describe('ITRF', () => { const itrf = new ITRF( epochUtc, new Vector3D(0 as Kilometers, 0 as Kilometers, Earth.radiusPolar), - new Vector3D(0, 0, 0), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), ); expect(itrf.toJ2000()).toMatchSnapshot(); @@ -65,9 +73,13 @@ describe('ITRF', () => { // can handle position at the South Pole it('should handle position at the South Pole', () => { - const itrf = new ITRF(epochUtc, new Vector3D(0, 0, -Earth.radiusPolar), new Vector3D(0, 0, 0)); + const itrf = new ITRF( + epochUtc, + new Vector3D(0 as Kilometers, 0 as Kilometers, -Earth.radiusPolar as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); - itrf.position = new Vector3D(0, 0, -Earth.radiusPolar); + itrf.position = new Vector3D(0 as Kilometers, 0 as Kilometers, -Earth.radiusPolar as Kilometers); expect(itrf.toJ2000()).toMatchSnapshot(); // Test the toGeodetic method expect(itrf.toGeodetic()).toMatchSnapshot(); @@ -78,7 +90,7 @@ describe('ITRF', () => { const itrf = new ITRF( epochUtc, new Vector3D(Earth.radiusEquator, 0 as Kilometers, 0 as Kilometers), - new Vector3D(0, 0, 0), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), ); // Test height @@ -99,7 +111,7 @@ describe('ITRF', () => { const itrf = new ITRF( epochUtc, new Vector3D(0 as Kilometers, Earth.radiusEquator, 0 as Kilometers), - new Vector3D(0, 0, 0), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), ); expect(itrf.name).toMatchSnapshot(); @@ -115,7 +127,7 @@ describe('ITRF', () => { const itrf = new ITRF( epochUtc, new Vector3D(0 as Kilometers, (Earth.radiusEquator + 1000) as Kilometers, 0 as Kilometers), - new Vector3D(0, 0, 0), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), ); expect(itrf.height).toMatchSnapshot(); @@ -129,7 +141,7 @@ describe('ITRF', () => { const itrf = new ITRF( epochUtc, new Vector3D(0 as Kilometers, (Earth.radiusEquator - 10) as Kilometers, 0 as Kilometers), - new Vector3D(0, 0, 0), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), ); expect(itrf.height).toMatchSnapshot(); @@ -143,7 +155,7 @@ describe('ITRF', () => { const itrf = new ITRF( epochUtc, new Vector3D(0 as Kilometers, 0 as Kilometers, Earth.radiusEquator), - new Vector3D(0, 0, 0), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), ); // Test the height property @@ -161,7 +173,11 @@ describe('ITRF', () => { // can handle position at the equator it('should handle position at the equator with negative y-coordinate', () => { - const itrf = new ITRF(epochUtc, new Vector3D(0, -Earth.radiusEquator, 0), new Vector3D(0, 0, 0)); + const itrf = new ITRF( + epochUtc, + new Vector3D(0 as Kilometers, -Earth.radiusEquator as Kilometers, 0 as Kilometers), + new Vector3D(0 as Kilometers, 0 as Kilometers, 0 as Kilometers), + ); expect(itrf.name).toMatchSnapshot(); expect(itrf.inertial).toMatchSnapshot(); @@ -173,8 +189,8 @@ describe('ITRF', () => { // can handle position at different epochs it('should handle position at different epochs', () => { - const position = new Vector3D(1000, 2000, 3000); - const velocity = new Vector3D(10, 20, 30); + const position = new Vector3D(1000 as Kilometers, 2000 as Kilometers, 3000 as Kilometers); + const velocity = new Vector3D(10 as Kilometers, 20 as Kilometers, 30 as Kilometers); const epoch1 = EpochUTC.fromDateTime(exampleDate); const epoch2 = EpochUTC.fromDateTime(new Date(exampleDate.getTime() + 1000)); const itrf1 = new ITRF(epoch1, position, velocity); @@ -198,4 +214,15 @@ describe('ITRF', () => { expect(geodetic).toMatchSnapshot(); }); + + // toClassicalElements + it('should return classical elements', () => { + const itrf = new ITRF( + epochUtc, + new Vector3D(1000 as Kilometers, 2000 as Kilometers, 3000 as Kilometers), + new Vector3D(10 as Kilometers, 20 as Kilometers, 30 as Kilometers), + ); + + expect(() => itrf.toClassicalElements()).toThrow(); + }); }); diff --git a/test/coordinate/J2000.test.ts b/test/coordinate/J2000.test.ts index 560710a..11e21d0 100644 --- a/test/coordinate/J2000.test.ts +++ b/test/coordinate/J2000.test.ts @@ -67,4 +67,66 @@ describe('J2000', () => { expect(j200FromTeme.position.y).toBeCloseTo(j2000.position.y, 8); expect(j200FromTeme.position.z).toBeCloseTo(j2000.position.z, 8); }); + + // toString + it('should return a string', () => { + const j2000 = new J2000( + epoch, + new Vector3D(5000 as Kilometers, 10000 as Kilometers, 2100 as Kilometers), + new Vector3D(7 as Kilometers, 4 as Kilometers, 2 as Kilometers), + ); + + expect(j2000.toString()).toMatchSnapshot(); + }); + + // mechanical energy + it('should calculate the mechanical energy', () => { + const j2000 = new J2000( + epoch, + new Vector3D(5000 as Kilometers, 10000 as Kilometers, 2100 as Kilometers), + new Vector3D(7 as Kilometers, 4 as Kilometers, 2 as Kilometers), + ); + + expect(j2000.mechanicalEnergy).toMatchSnapshot(); + }); + + // period + it('should calculate the period', () => { + const j2000 = new J2000( + epoch, + new Vector3D(5000 as Kilometers, 10000 as Kilometers, 2100 as Kilometers), + new Vector3D(7 as Kilometers, 4 as Kilometers, 2 as Kilometers), + ); + + expect(j2000.period).toMatchSnapshot(); + }); + + // angular rate + it('should calculate the angular rate', () => { + const j2000 = new J2000( + epoch, + new Vector3D(5000 as Kilometers, 10000 as Kilometers, 2100 as Kilometers), + new Vector3D(7 as Kilometers, 4 as Kilometers, 2 as Kilometers), + ); + + expect(j2000.angularRate).toMatchSnapshot(); + }); + + // toClassicalElements + it('should convert to classical elements', () => { + const j2000 = new J2000( + epoch, + new Vector3D(5000 as Kilometers, 10000 as Kilometers, 2100 as Kilometers), + new Vector3D(7 as Kilometers, 4 as Kilometers, 2 as Kilometers), + ); + const elements = j2000.toClassicalElements(); + + expect(elements.epoch).toEqual(epoch); + expect(elements.semimajorAxis).toMatchSnapshot(); + expect(elements.eccentricity).toMatchSnapshot(); + expect(elements.inclination).toMatchSnapshot(); + expect(elements.rightAscension).toMatchSnapshot(); + expect(elements.argPerigee).toMatchSnapshot(); + expect(elements.trueAnomaly).toMatchSnapshot(); + }); }); diff --git a/test/coordinate/__snapshots__/J2000.test.ts.snap b/test/coordinate/__snapshots__/J2000.test.ts.snap new file mode 100644 index 0000000..7f87a02 --- /dev/null +++ b/test/coordinate/__snapshots__/J2000.test.ts.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`J2000 should calculate the angular rate 1`] = `0.000002809314397474213`; + +exports[`J2000 should calculate the mechanical energy 1`] = `-0.539173763730993`; + +exports[`J2000 should calculate the period 1`] = `37275.911593878845`; + +exports[`J2000 should convert to classical elements 1`] = `369640.0569843672`; + +exports[`J2000 should convert to classical elements 2`] = `0.9909435374737835`; + +exports[`J2000 should convert to classical elements 3`] = `2.896313029154172`; + +exports[`J2000 should convert to classical elements 4`] = `1.955753713088999`; + +exports[`J2000 should convert to classical elements 5`] = `5.144903601262446`; + +exports[`J2000 should convert to classical elements 6`] = `2.0019281534313316`; + +exports[`J2000 should return a string 1`] = ` +"[J2000] + Epoch: 2024-01-13T01:28:46.817Z + Position: [5000.000000, 10000.000000, 2100.000000] km + Velocity: [7.000000000, 4.000000000, 2.000000000] km/s" +`;