From 549f5fce610f343ee34a69e4283dead4941dff23 Mon Sep 17 00:00:00 2001 From: Theodore Kruczek Date: Tue, 16 Jan 2024 19:14:18 -0500 Subject: [PATCH] refactor: :label: improve typing and documentation of radec observations --- src/observation/RadecGeocentric.ts | 129 ++++++++++-------- src/observation/RadecTopocentric.ts | 113 ++++++++------- src/utils/functions.ts | 10 +- .../RadecGeocentric.test.ts.snap | 8 +- .../RadecTopocentric.test.ts.snap | 4 +- 5 files changed, 146 insertions(+), 118 deletions(-) diff --git a/src/observation/RadecGeocentric.ts b/src/observation/RadecGeocentric.ts index 12e6cfd..d0dff9b 100644 --- a/src/observation/RadecGeocentric.ts +++ b/src/observation/RadecGeocentric.ts @@ -21,8 +21,7 @@ * SOFTWARE. */ -/* eslint-disable no-undefined */ -import { Degrees, Kilometers, KilometersPerSecond, Radians, RadiansPerSecond } from 'src/main'; +import { Degrees, DegreesPerSecond, Kilometers, KilometersPerSecond, Radians, RadiansPerSecond } from 'src/main'; import { J2000 } from '../coordinate/J2000'; import { AngularDistanceMethod } from '../enums/AngularDistanceMethod'; import { Vector3D } from '../operations/Vector3D'; @@ -31,32 +30,36 @@ import { DEG2RAD, RAD2DEG, TAU } from '../utils/constants'; import { angularDistance } from '../utils/functions'; import { radecToPosition, radecToVelocity } from './ObservationUtils'; -// / Geocentric right-ascension and declination. +/** + * Represents a geocentric right ascension and declination observation. + * + * In geocentric coordinates, observations are considered from the Earth's center. This approach simplifies calculations + * for distant celestial objects, as it assumes a uniform observation point that ignores the observer's specific + * location on Earth. + */ export class RadecGeocentric { - // / Create a new [RadecGeocentric] object. constructor( public epoch: EpochUTC, public rightAscension: Radians, public declination: Radians, public range?: Kilometers, - public rightAscensionRate?: RadiansPerSecond, - public declinationRate?: RadiansPerSecond, - public rangeRate?: KilometersPerSecond, + public rightAscensionRate?: RadiansPerSecond | null, + public declinationRate?: RadiansPerSecond | null, + public rangeRate?: KilometersPerSecond | null, ) { // Nothing to do here. } /** - * 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 - * @param rightAscensionRateDegrees Right-ascension rate (°/s). - * @param declinationRateDegrees Declination rate (°/s). - * @param rangeRate Range rate (km/s). - * @returns A new [RadecGeocentric] object. + * Creates a RadecGeocentric object from the given parameters in degrees. + * @param epoch - The epoch in UTC. + * @param rightAscensionDegrees - The right ascension in degrees. + * @param declinationDegrees - The declination in degrees. + * @param range - The range in kilometers (optional). + * @param rightAscensionRateDegrees - The right ascension rate in degrees per second (optional). + * @param declinationRateDegrees - The declination rate in degrees per second (optional). + * @param rangeRate - The range rate in kilometers per second (optional). + * @returns A new RadecGeocentric object. */ static fromDegrees( epoch: EpochUTC, @@ -69,8 +72,8 @@ export class RadecGeocentric { ): RadecGeocentric { const rightAscensionRate = rightAscensionRateDegrees ? rightAscensionRateDegrees * DEG2RAD as RadiansPerSecond - : undefined; - const declinationRate = declinationRateDegrees ? declinationRateDegrees * DEG2RAD as RadiansPerSecond : undefined; + : null; + const declinationRate = declinationRateDegrees ? declinationRateDegrees * DEG2RAD as RadiansPerSecond : null; return new RadecGeocentric( epoch, @@ -83,7 +86,11 @@ export class RadecGeocentric { ); } - // / Create a [RadecGeocentric] object from an inertial [state] vector. + /** + * Creates a RadecGeocentric object from a state vector in J2000 coordinates. + * @param state - The J2000 state vector. + * @returns A new RadecGeocentric object. + */ static fromStateVector(state: J2000): RadecGeocentric { const rI = state.position.x; const rJ = state.position.y; @@ -116,33 +123,42 @@ export class RadecGeocentric { ); } - // / Right-ascension _(°)_. - get rightAscensionDegrees(): number { - return this.rightAscension * RAD2DEG; + /** + * Gets the right ascension in degrees. + * @returns The right ascension in degrees. + */ + get rightAscensionDegrees(): Degrees { + return this.rightAscension * RAD2DEG as Degrees; } - // / Declination _(°)_. - get declinationDegrees(): number { - return this.declination * RAD2DEG; + /** + * Gets the declination in degrees. + * @returns The declination in degrees. + */ + get declinationDegrees(): Degrees { + return this.declination * RAD2DEG as Degrees; } - // / Right-ascension rate _(°/s)_. - get rightAscensionRateDegrees(): number | undefined { - return this.rightAscensionRate ? this.rightAscensionRate * RAD2DEG : undefined; + /** + * Gets the right ascension rate in degrees per second. + * @returns The right ascension rate in degrees per second, or null if it is not available. + */ + get rightAscensionRateDegrees(): DegreesPerSecond | null { + return this.rightAscensionRate ? this.rightAscensionRate * RAD2DEG as DegreesPerSecond : null; } - // / Declination rate _(°/s)_. - get declinationRateDegrees(): number | undefined { - return this.declinationRate ? this.declinationRate * RAD2DEG : undefined; + /** + * Gets the rate of change of declination in degrees per second. + * @returns The rate of change of declination in degrees per second, or null if not available. + */ + get declinationRateDegrees(): DegreesPerSecond | null { + return this.declinationRate ? this.declinationRate * RAD2DEG as DegreesPerSecond : null; } /** - * Return the position relative to the center of the Earth. - * - * An optional [range] _(km)_ value can be passed to override the value - * contained in this observation. - * @param range Range _(km)_. - * @returns A [Vector3D] object. + * Calculates the position vector in geocentric coordinates. + * @param range - The range in kilometers (optional). If not provided, it uses the default range or 1.0 kilometer. + * @returns The position vector in geocentric coordinates. */ position(range?: Kilometers): Vector3D { const r = range ?? this.range ?? 1.0 as Kilometers; @@ -151,13 +167,12 @@ export class RadecGeocentric { } /** - * Return the velocity relative to the centar of the Earth. - * - * An optional [range] _(km)_ and [rangeRate] _(km/s)_ value can be passed - * to override the value contained in this observation. - * @param range Range _(km)_. - * @param rangeRate Range rate _(km/s)_. - * @returns A [Vector3D] object. + * Calculates the velocity vector of the celestial object. + * @param range - The range of the celestial object in kilometers. If not provided, it uses the stored range value. + * @param rangeRate - The range rate of the celestial object in kilometers per second. + * If not provided, it uses the stored range rate value. + * @returns The velocity vector of the celestial object in kilometers per second. + * @throws Error if the right ascension rate or declination rate is missing. */ velocity(range?: Kilometers, rangeRate?: KilometersPerSecond): Vector3D { if (!this.rightAscensionRate || !this.declinationRate) { @@ -170,24 +185,22 @@ export class RadecGeocentric { } /** - * Calculate the angular distance _(rad)_ between this and another - * [RadecGeocentric] object. - * @param radec - The other [RadecGeocentric] object. - * @param method - The angular distance method to use. - * @returns The angular distance _(rad)_. + * Calculates the angular distance between two celestial coordinates (RA and Dec). + * @param radec - The celestial coordinates to compare with. + * @param method - The method to use for calculating the angular distance. Default is `AngularDistanceMethod.Cosine`. + * @returns The angular distance between the two celestial coordinates in radians. */ - angle(radec: RadecGeocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): number { + angle(radec: RadecGeocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): Radians { return angularDistance(this.rightAscension, this.declination, radec.rightAscension, radec.declination, method); } /** - * Calculate the angular distance _(°)_ between this and another - * [RadecGeocentric] object. - * @param radec - The other [RadecGeocentric] object. - * @param method - The angular distance method to use. - * @returns The angular distance _(°)_. + * Calculates the angle in degrees between two RadecGeocentric objects. + * @param radec - The RadecGeocentric object to calculate the angle with. + * @param method - The method to use for calculating the angular distance. Default is AngularDistanceMethod.Cosine. + * @returns The angle in degrees. */ - angleDegrees(radec: RadecGeocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): number { - return this.angle(radec, method) * RAD2DEG; + angleDegrees(radec: RadecGeocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): Degrees { + return this.angle(radec, method) * RAD2DEG as Degrees; } } diff --git a/src/observation/RadecTopocentric.ts b/src/observation/RadecTopocentric.ts index 50d66f5..c766eab 100644 --- a/src/observation/RadecTopocentric.ts +++ b/src/observation/RadecTopocentric.ts @@ -21,7 +21,6 @@ * SOFTWARE. */ -/* eslint-disable no-undefined */ import { Degrees, DegreesPerSecond, Kilometers, KilometersPerSecond, Radians, RadiansPerSecond } from 'src/main'; import { J2000 } from '../coordinate/J2000'; import { AngularDistanceMethod } from '../enums/AngularDistanceMethod'; @@ -31,32 +30,37 @@ import { DEG2RAD, RAD2DEG, TAU } from '../utils/constants'; import { angularDistance } from '../utils/functions'; import { radecToPosition, radecToVelocity } from './ObservationUtils'; -// / Topocentric right-ascension and declination. +/** + * Represents a topocentric right ascension and declination observation. + * + * Topocentric coordinates take into account the observer's exact location on the Earth's surface. This model is crucial + * for precise measurements of local astronomical events and nearby celestial objects, where the observer's latitude, + * longitude, and altitude can significantly affect the observed position due to parallax. Topocentric coordinates are + * particularly important for observations of the Moon, planets, and artificial satellites. + */ export class RadecTopocentric { - // / Create a new [RadecTopocentric] object. constructor( public epoch: EpochUTC, public rightAscension: Radians, public declination: Radians, public range?: Kilometers, - public rightAscensionRate?: RadiansPerSecond, - public declinationRate?: RadiansPerSecond, - public rangeRate?: KilometersPerSecond, + public rightAscensionRate?: RadiansPerSecond | null, + public declinationRate?: RadiansPerSecond | null, + public rangeRate?: KilometersPerSecond | null, ) { // Nothing to do here. } /** - * Create a new [RadecTopocentric] object, using degrees for the - * angular values. + * Create a new RadecTopocentric 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)_. - * @returns A new [RadecTopocentric] object. + * @param rightAscensionDegrees Right-ascension in degrees. + * @param declinationDegrees Declination in degrees. + * @param range Range in km. + * @param rightAscensionRateDegrees Right-ascension rate in degrees per second. + * @param declinationRateDegrees Declination rate in degrees per second. + * @param rangeRate Range rate in km/s. + * @returns A new RadecTopocentric object. */ static fromDegrees( epoch: EpochUTC, @@ -69,10 +73,10 @@ export class RadecTopocentric { ): RadecTopocentric { const rightAscensionRate = rightAscensionRateDegrees ? rightAscensionRateDegrees * DEG2RAD as RadiansPerSecond - : undefined; + : null; const declinationRate = declinationRateDegrees ? declinationRateDegrees * DEG2RAD as RadiansPerSecond - : undefined; + : null; return new RadecTopocentric( epoch, @@ -86,11 +90,10 @@ export class RadecTopocentric { } /** - * Create a [RadecTopocentric] object from an inertial [state] and - * [site] vector. + * Create a new RadecTopocentric object from a J2000 state vector. * @param state Inertial state vector. * @param site Site vector. - * @returns A new [RadecTopocentric] object. + * @returns A new RadecTopocentric object. */ static fromStateVector(state: J2000, site: J2000): RadecTopocentric { const p = state.position.subtract(site.position); @@ -126,24 +129,36 @@ export class RadecTopocentric { ); } - // / Right-ascension _(°)_. + /** + * Gets the right ascension in degrees. + * @returns The right ascension in degrees. + */ get rightAscensionDegrees(): Degrees { return this.rightAscension * RAD2DEG as Degrees; } - // / Declination _(°)_. + /** + * Gets the declination in degrees. + * @returns The declination in degrees. + */ get declinationDegrees(): Degrees { return this.declination * RAD2DEG as Degrees; } - // / Right-ascension rate _(°/s)_. + /** + * Gets the right ascension rate in degrees per second. + * @returns The right ascension rate in degrees per second, or null if it is not available. + */ get rightAscensionRateDegrees(): DegreesPerSecond | null { return this.rightAscensionRate ? this.rightAscensionRate * RAD2DEG as DegreesPerSecond : null; } - // / Declination rate _(°/s)_. + /** + * Gets the rate of change of declination in degrees per second. + * @returns The rate of change of declination in degrees per second, or null if the declination rate is not defined. + */ get declinationRateDegrees(): DegreesPerSecond | null { return this.declinationRate ? this.declinationRate * RAD2DEG as DegreesPerSecond @@ -151,31 +166,29 @@ export class RadecTopocentric { } /** - * Return the position relative to the observer [site]. + * Return the position relative to the observer site. * - * An optional [range] _(km)_ value can be passed to override the value - * contained in this observation. + * An optional range value can be passed to override the value contained in this observation. * @param site Observer site. - * @param range Range _(km)_. - * @returns A [Vector3D] object. + * @param range Range in km. + * @returns A Vector3D object. */ - position(site: J2000, range?: Kilometers): Vector3D { + position(site: J2000, range?: Kilometers): Vector3D { const r = range ?? this.range ?? 1.0 as Kilometers; return radecToPosition(this.rightAscension, this.declination, r).add(site.position); } /** - * Return the velocity relative to the observer [site]. + * Return the velocity relative to the observer site. * - * An optional [range] _(km)_ and [rangeRate] _(km/s)_ value can be passed - * to override the values contained in this observation. + * An optional range and rangeRate value can be passed to override the values contained in this observation. * @param site Observer site. - * @param range Range _(km)_. - * @param rangeRate Range rate _(km/s)_. - * @returns A [Vector3D] object. + * @param range Range in km. + * @param rangeRate Range rate in km/s. + * @returns A Vector3D object. */ - velocity(site: J2000, range?: Kilometers, rangeRate?: KilometersPerSecond): Vector3D { + velocity(site: J2000, range?: Kilometers, rangeRate?: KilometersPerSecond): Vector3D { if (!this.rightAscensionRate || !this.declinationRate) { throw new Error('Velocity unsolvable, missing ra/dec rates.'); } @@ -192,7 +205,11 @@ export class RadecTopocentric { ).add(site.velocity); } - // / Convert this observation into a line-of-sight vector. + /** + * Calculates the line of sight vector in the topocentric coordinate system. + * The line of sight vector points from the observer's location towards the celestial object. + * @returns The line of sight vector as a Vector3D object. + */ lineOfSight(): Vector3D { const ca = Math.cos(this.rightAscension); const cd = Math.cos(this.declination); @@ -203,24 +220,22 @@ export class RadecTopocentric { } /** - * Calculate the angular distance _(rad)_ between this and another - * [RadecTopocentric] object. - * @param radec - The other [RadecTopocentric] object. + * Calculate the angular distance between this and another RadecTopocentric object. + * @param radec - The other RadecTopocentric object. * @param method - The angular distance method to use. - * @returns The angular distance _(rad)_. + * @returns The angular distance. */ - angle(radec: RadecTopocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): number { + angle(radec: RadecTopocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): Radians { return angularDistance(this.rightAscension, this.declination, radec.rightAscension, radec.declination, method); } /** - * Calculate the angular distance _(°)_ between this and another - * [RadecTopocentric] object. - * @param radec - The other [RadecTopocentric] object. + * Calculate the angular distance between this and another RadecTopocentric object. + * @param radec - The other RadecTopocentric object. * @param method - The angular distance method to use. - * @returns The angular distance _(°)_. + * @returns The angular distance */ - angleDegrees(radec: RadecTopocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): number { - return this.angle(radec, method) * RAD2DEG; + angleDegrees(radec: RadecTopocentric, method: AngularDistanceMethod = AngularDistanceMethod.Cosine): Degrees { + return this.angle(radec, method) * RAD2DEG as Degrees; } } diff --git a/src/utils/functions.ts b/src/utils/functions.ts index 101a100..2f22ba0 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -153,11 +153,11 @@ export function wrapAngle(theta: Radians): Radians { * @param phi2 - The latitude of the second point in radians. * @returns The angular distance between the two points in radians. */ -function angularDistanceCosine_(lam1: number, phi1: number, lam2: number, phi2: number): number { +function angularDistanceCosine_(lam1: number, phi1: number, lam2: number, phi2: number): Radians { const a = Math.sin(phi1) * Math.sin(phi2); const b = Math.cos(phi1) * Math.cos(phi2) * Math.cos(lam2 - lam1); - return Math.acos(a + b); + return Math.acos(a + b) as Radians; } /** @@ -168,14 +168,14 @@ function angularDistanceCosine_(lam1: number, phi1: number, lam2: number, phi2: * @param phi2 - The latitude of the second point in radians. * @returns The angular distance between the two points in radians. */ -function angularDistanceHaversine_(lam1: number, phi1: number, lam2: number, phi2: number): number { +function angularDistanceHaversine_(lam1: number, phi1: number, lam2: number, phi2: number): Radians { const dlam = lam2 - lam1; const dphi = phi2 - phi1; const sdlam = Math.sin(0.5 * dlam); const sdphi = Math.sin(0.5 * dphi); const a = sdphi * sdphi + Math.cos(phi1) * Math.cos(phi2) * sdlam * sdlam; - return 2.0 * Math.asin(Math.min(1.0, Math.sqrt(a))); + return 2.0 * Math.asin(Math.min(1.0, Math.sqrt(a))) as Radians; } /** @@ -194,7 +194,7 @@ export function angularDistance( lam2: number, phi2: number, method: AngularDistanceMethod = AngularDistanceMethod.Cosine, -): number { +): Radians { switch (method) { case AngularDistanceMethod.Cosine: return angularDistanceCosine_(lam1, phi1, lam2, phi2); diff --git a/test/observation/__snapshots__/RadecGeocentric.test.ts.snap b/test/observation/__snapshots__/RadecGeocentric.test.ts.snap index 66bc4f2..a6727b6 100644 --- a/test/observation/__snapshots__/RadecGeocentric.test.ts.snap +++ b/test/observation/__snapshots__/RadecGeocentric.test.ts.snap @@ -31,14 +31,14 @@ RadecGeocentric { exports[`RadecGeocentric should be constructable from degrees 1`] = ` RadecGeocentric { "declination": 0, - "declinationRate": undefined, + "declinationRate": null, "epoch": EpochUTC { "posix": 1705109326.817, }, "range": undefined, "rangeRate": undefined, "rightAscension": 0.4363323129985824, - "rightAscensionRate": undefined, + "rightAscensionRate": null, } `; @@ -48,7 +48,7 @@ exports[`RadecGeocentric should return the angle in degrees 1`] = `0.99999999999 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 declination rate in degrees 1`] = `null`; exports[`RadecGeocentric should return the position 1`] = ` Vector3D { @@ -60,7 +60,7 @@ Vector3D { 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 right ascension rate in degrees 1`] = `null`; exports[`RadecGeocentric should return the velocity 1`] = ` Vector3D { diff --git a/test/observation/__snapshots__/RadecTopocentric.test.ts.snap b/test/observation/__snapshots__/RadecTopocentric.test.ts.snap index 2d647f7..b2fb687 100644 --- a/test/observation/__snapshots__/RadecTopocentric.test.ts.snap +++ b/test/observation/__snapshots__/RadecTopocentric.test.ts.snap @@ -31,14 +31,14 @@ RadecTopocentric { exports[`RadecTopocentric should be constructable from degrees 1`] = ` RadecTopocentric { "declination": 0, - "declinationRate": undefined, + "declinationRate": null, "epoch": EpochUTC { "posix": 1705109326.817, }, "range": undefined, "rangeRate": undefined, "rightAscension": 0.4363323129985824, - "rightAscensionRate": undefined, + "rightAscensionRate": null, } `;