diff --git a/src/body/Earth.ts b/src/body/Earth.ts index 27acfe7..ff1ed43 100644 --- a/src/body/Earth.ts +++ b/src/body/Earth.ts @@ -33,6 +33,7 @@ import { Kilometers, RAD2DEG, Radians, + RadiansPerSecond, secondsPerDay, secondsPerSiderealDay, TAU, @@ -82,7 +83,11 @@ export class Earth { static readonly j6: number = 5.40681239107085e-7; // / Earth rotation vector _(rad/s)_. - static readonly rotation: Vector3D = new Vector3D(0, 0, 7.292115146706979e-5); + static readonly rotation = new Vector3D( + 0 as RadiansPerSecond, + 0 as RadiansPerSecond, + 7.292115146706979e-5 as RadiansPerSecond, + ); // / Calculate mean motion _(rad/s)_ from a given [semimajorAxis] _(km)_. static smaToMeanMotion(semimajorAxis: number): number { diff --git a/src/coordinate/ClassicalElements.ts b/src/coordinate/ClassicalElements.ts index 563052d..fce03a1 100644 --- a/src/coordinate/ClassicalElements.ts +++ b/src/coordinate/ClassicalElements.ts @@ -21,7 +21,7 @@ * SOFTWARE. */ -import { Minutes, PositionVelocity, Degrees, Kilometers, Radians, Seconds } from '../main'; +import { Minutes, PositionVelocity, Degrees, Kilometers, Radians, Seconds, KilometersPerSecond } from '../main'; import { Vector3D } from '../operations/Vector3D'; import { EpochUTC } from '../time/EpochUTC'; import { earthGravityParam, MINUTES_PER_DAY, RAD2DEG, sec2min, TAU } from '../utils/constants'; @@ -92,37 +92,37 @@ export class ClassicalElements { const pos = state.position; const vel = state.velocity; const a = state.semimajorAxis; - const eVecA = pos.scale(vel.magnitude() ** 2 - mu / pos.magnitude()); + const eVecA = pos.scale(vel.magnitude() ** 2 - mu / pos.magnitude() as KilometersPerSecond); const eVecB = vel.scale(pos.dot(vel)); const eVec = eVecA.subtract(eVecB).scale(1 / mu); const e = eVec.magnitude(); const h = pos.cross(vel); - const i = Math.acos(clamp(h.z / h.magnitude(), -1.0, 1.0)); + const i = Math.acos(clamp(h.z / h.magnitude(), -1.0, 1.0)) as Radians; const n = Vector3D.zAxis.cross(h); - let o = Math.acos(clamp(n.x / n.magnitude(), -1.0, 1.0)); + let o = Math.acos(clamp(n.x / n.magnitude(), -1.0, 1.0)) as Radians; if (n.y < 0) { - o = TAU - o; + o = TAU - o as Radians; } let w = n.angle(eVec); if (eVec.z < 0) { - w = TAU - w; + w = TAU - w as Radians; } let v = eVec.angle(pos); if (pos.dot(vel) < 0) { - v = TAU - v; + v = TAU - v as Radians; } return new ClassicalElements({ epoch: state.epoch, semimajorAxis: a, eccentricity: e, - inclination: i as Radians, - rightAscension: o as Radians, - argPerigee: w as Radians, - trueAnomaly: v as Radians, + inclination: i, + rightAscension: o, + argPerigee: w, + trueAnomaly: v, mu, }); } @@ -253,13 +253,13 @@ export class ClassicalElements { const position = rPQW .rotZ(-this.argPerigee as Radians) .rotX(-this.inclination) - .rotZ(-this.rightAscension as Radians); + .rotZ(-this.rightAscension as Radians) as Vector3D; const velocity = vPQW .rotZ(-this.argPerigee as Radians) .rotX(-this.inclination) - .rotZ(-this.rightAscension as Radians); + .rotZ(-this.rightAscension as Radians) as Vector3D; - return { position: position as Vector3D, velocity: velocity as Vector3D }; + return { position, velocity }; } /** diff --git a/src/coordinate/Geodetic.ts b/src/coordinate/Geodetic.ts index 10cdf22..6e4d5bd 100644 --- a/src/coordinate/Geodetic.ts +++ b/src/coordinate/Geodetic.ts @@ -22,7 +22,7 @@ */ import { Earth } from '../body/Earth'; -import { AngularDistanceMethod, Degrees, GroundObject, Kilometers, Radians } from '../main'; +import { AngularDistanceMethod, Degrees, GroundObject, Kilometers, KilometersPerSecond, Radians } from '../main'; import { Vector3D } from '../operations/Vector3D'; import { EpochUTC } from '../time/EpochUTC'; import { DEG2RAD, RAD2DEG } from '../utils/constants'; @@ -131,7 +131,7 @@ export class Geodetic { ((nVal * (1 - Earth.eccentricitySquared) + this.alt) * sLat) as Kilometers, ); - return new ITRF(epoch, r, Vector3D.origin as Vector3D); + return new ITRF(epoch, r, Vector3D.origin as Vector3D); } /** diff --git a/src/coordinate/ITRF.ts b/src/coordinate/ITRF.ts index 259debc..9fae829 100644 --- a/src/coordinate/ITRF.ts +++ b/src/coordinate/ITRF.ts @@ -22,7 +22,7 @@ */ /* eslint-disable class-methods-use-this */ -import { Kilometers, Radians, Vector3D } from 'src/main'; +import { Kilometers, KilometersPerSecond, Radians, Vector3D } from 'src/main'; import { Earth } from '../body/Earth'; import { Geodetic } from './Geodetic'; import { J2000 } from './J2000'; @@ -93,7 +93,10 @@ export class ITRF extends StateVector { const n = Earth.nutation(this.epoch); const ast = this.epoch.gmstAngle() + n.eqEq; const rTOD = this.position.rotZ(-ast as Radians); - const vTOD = this.velocity.add(Earth.rotation.cross(this.position) as Vector3D).rotZ(-ast as Radians); + const vTOD = this.velocity + // TODO: #13 Intermediate unit type is incorrect. + .add(Earth.rotation.cross(this.position) as unknown as Vector3D) + .rotZ(-ast as Radians); const rMOD = rTOD.rotX(n.eps).rotZ(n.dPsi).rotX(-n.mEps); const vMOD = vTOD.rotX(n.eps).rotZ(n.dPsi).rotX(-n.mEps); const rJ2000 = rMOD @@ -103,7 +106,7 @@ export class ITRF extends StateVector { const vJ2000 = vMOD .rotZ(p.zed) .rotY(-p.theta as Radians) - .rotZ(p.zeta) as Vector3D; + .rotZ(p.zeta) as Vector3D; return new J2000(this.epoch, rJ2000, vJ2000); } diff --git a/src/coordinate/J2000.ts b/src/coordinate/J2000.ts index 4bb011f..0f983e5 100644 --- a/src/coordinate/J2000.ts +++ b/src/coordinate/J2000.ts @@ -21,7 +21,7 @@ * SOFTWARE. */ -import { Kilometers, Radians, Vector3D } from '../main'; +import { Kilometers, KilometersPerSecond, Radians, Vector3D } from '../main'; import { Earth } from '../body/Earth'; import { ClassicalElements } from './ClassicalElements'; import { ITRF } from './ITRF'; @@ -92,7 +92,7 @@ export class J2000 extends StateVector { .rotZ(-n.dPsi as Radians) .rotX(-n.eps); const rPEF = rTOD.rotZ(ast) as Vector3D; - const vPEF = vTOD.rotZ(ast).add(Earth.rotation.negate().cross(rPEF)) as Vector3D; + const vPEF = vTOD.rotZ(ast).add(Earth.rotation.negate().cross(rPEF)) as Vector3D; return new ITRF(this.epoch, rPEF, vPEF); } @@ -123,7 +123,7 @@ export class J2000 extends StateVector { .rotX(n.mEps) .rotZ(-n.dPsi as Radians) .rotX(-eps) - .rotZ(dPsiCosEps) as Vector3D; + .rotZ(dPsiCosEps) as Vector3D; return new TEME(this.epoch, rTEME, vTEME); } diff --git a/src/coordinate/RelativeState.ts b/src/coordinate/RelativeState.ts index d34b984..2d592b8 100644 --- a/src/coordinate/RelativeState.ts +++ b/src/coordinate/RelativeState.ts @@ -21,7 +21,7 @@ * SOFTWARE. */ -import { Kilometers } from 'src/main'; +import { Kilometers, KilometersPerSecond } from '../main'; import { Matrix } from '../operations/Matrix'; import { Vector3D } from '../operations/Vector3D'; import { J2000 } from './J2000'; @@ -31,8 +31,8 @@ import { J2000 } from './J2000'; */ export abstract class RelativeState { position: Vector3D; - velocity: Vector3D; - constructor(position: Vector3D, velocity: Vector3D) { + velocity: Vector3D; + constructor(position: Vector3D, velocity: Vector3D) { this.position = position; this.velocity = velocity; } diff --git a/src/coordinate/StateVector.ts b/src/coordinate/StateVector.ts index 15b7a13..4d0bd09 100644 --- a/src/coordinate/StateVector.ts +++ b/src/coordinate/StateVector.ts @@ -21,7 +21,7 @@ * SOFTWARE. */ -import { Kilometers, Minutes } from 'src/main'; +import { Kilometers, KilometersPerSecond, Minutes } from 'src/main'; import { Earth } from '../body/Earth'; import type { Vector3D } from '../operations/Vector3D'; import type { EpochUTC } from '../time/EpochUTC'; @@ -35,8 +35,8 @@ import { ClassicalElements } from './ClassicalElements'; export abstract class StateVector { epoch: EpochUTC; position: Vector3D; - velocity: Vector3D; - constructor(epoch: EpochUTC, position: Vector3D, velocity: Vector3D) { + velocity: Vector3D; + constructor(epoch: EpochUTC, position: Vector3D, velocity: Vector3D) { this.epoch = epoch; this.position = position; this.velocity = velocity; diff --git a/src/coordinate/TEME.ts b/src/coordinate/TEME.ts index ed5dc2b..47c478b 100644 --- a/src/coordinate/TEME.ts +++ b/src/coordinate/TEME.ts @@ -21,7 +21,7 @@ * SOFTWARE. */ -import { Kilometers, Radians, Vector3D } from '../main'; +import { Kilometers, KilometersPerSecond, Radians, Vector3D } from '../main'; import { Earth } from '../body/Earth'; import type { ClassicalElements } from './ClassicalElements'; import { J2000 } from './J2000'; @@ -90,7 +90,7 @@ export class TEME extends StateVector { const vJ2K = vMOD .rotZ(p.zed) .rotY(-p.theta as Radians) - .rotZ(p.zeta) as Vector3D; + .rotZ(p.zeta) as Vector3D; return new J2000(this.epoch, rJ2K, vJ2K); } diff --git a/src/coordinate/Tle.ts b/src/coordinate/Tle.ts index ac45a7a..24698ea 100644 --- a/src/coordinate/Tle.ts +++ b/src/coordinate/Tle.ts @@ -30,6 +30,7 @@ import { Degrees, EciVec3, Kilometers, + KilometersPerSecond, Line1Data, Line2Data, Minutes, @@ -246,8 +247,8 @@ export class Tle { return new TEME( epoch, - new Vector3D(r[0], r[1], r[2]), - new Vector3D(v[0], v[1], v[2]), + new Vector3D(r[0], r[1], r[2]), + new Vector3D(v[0], v[1], v[2]), ); } @@ -283,8 +284,8 @@ export class Tle { return new TEME( this.epoch, - new Vector3D(r[0], r[1], r[2]), - new Vector3D(v[0], v[1], v[2]), + new Vector3D(r[0], r[1], r[2]), + new Vector3D(v[0], v[1], v[2]), ); } diff --git a/src/objects/Satellite.ts b/src/objects/Satellite.ts index e14643d..40e080e 100644 --- a/src/objects/Satellite.ts +++ b/src/objects/Satellite.ts @@ -39,6 +39,7 @@ import { EcfVec3, EciVec3, Kilometers, + KilometersPerSecond, LlaVec3, Minutes, PosVel, @@ -254,7 +255,7 @@ export class Satellite extends BaseObject { throw new Error('Propagation failed!'); } else { const p = pv.position as EciVec3; - const v = pv.velocity as EciVec3; + const v = pv.velocity as EciVec3; const epoch = new EpochUTC(date.getTime()); const pos = new Vector3D(p.x, p.y, p.z); diff --git a/src/observation/RAE.ts b/src/observation/RAE.ts index dc0f489..b03552a 100644 --- a/src/observation/RAE.ts +++ b/src/observation/RAE.ts @@ -25,7 +25,7 @@ import { ITRF } from '../coordinate/ITRF'; import { J2000 } from '../coordinate/J2000'; import { AngularDistanceMethod } from '../enums/AngularDistanceMethod'; -import { Degrees, Kilometers, Radians } from '../main'; +import { Degrees, Kilometers, KilometersPerSecond, Radians } from '../main'; import { Vector3D } from '../operations/Vector3D'; import { EpochUTC } from '../time/EpochUTC'; import { DEG2RAD, halfPi, RAD2DEG, TAU } from '../utils/constants'; @@ -190,7 +190,7 @@ export class RAE { .rotZ(-geo.lon as Radians) .add(ecef.position); - return new ITRF(this.epoch, rEcef, Vector3D.origin as Vector3D).toJ2000().position; + return new ITRF(this.epoch, rEcef, Vector3D.origin as Vector3D).toJ2000().position; } /** @@ -229,7 +229,10 @@ export class RAE { (this.rngRate * sEl + this.rng * cEl * this.elRateRad) as Kilometers, ); const pEcef = pSez.rotY(-(po2 - geo.lat) as Radians).rotZ(-geo.lon as Radians); - const pDotEcef = pDotSez.rotY(-(po2 - geo.lat) as Radians).rotZ(-geo.lon as Radians); + const pDotEcef = pDotSez + .rotY(-(po2 - geo.lat) as Radians) + // TODO: #13 Intermediate unit type is incorrect. + .rotZ(-geo.lon as Radians) as unknown as Vector3D; const rEcef = pEcef.add(ecef.position); return new ITRF(this.epoch, rEcef, pDotEcef).toJ2000(); diff --git a/src/operations/Vector3D.ts b/src/operations/Vector3D.ts index 16fbe61..84fe3d3 100644 --- a/src/operations/Vector3D.ts +++ b/src/operations/Vector3D.ts @@ -21,7 +21,7 @@ * SOFTWARE. */ -import { Radians, linearDistance } from '../main'; +import { Kilometers, KilometersPerSecond, Radians, linearDistance } from '../main'; import { Matrix } from './Matrix'; import { Vector } from './Vector'; @@ -125,13 +125,13 @@ export class Vector3D { } // / Return a copy of this [Vector3D] scaled by [n]; - scale(n: number): Vector3D { - return new Vector3D(this.x * n as T, this.y * n as T, this.z * n as T); + scale(n: U): Vector3D { + return new Vector3D(this.x * n as U, this.y * n as U, this.z * n as U); } // / Return a copy of this [Vector3D] with the elements negated. negate(): Vector3D { - return this.scale(-1); + return this.scale(-1 as T); } /** @@ -158,8 +158,8 @@ export class Vector3D { } // Calculate the dot product of this and another [Vector3D]. - dot(v: Vector3D): number { - return this.x * v.x + this.y * v.y + this.z * v.z; + dot(v: Vector3D): T { + return this.x * v.x + this.y * v.y + this.z * v.z as T; } // Calculate the outer product between this and another [Vector3D]. @@ -172,11 +172,11 @@ export class Vector3D { } // Calculate the cross product of this and another [Vector3D]. - cross(v: Vector3D): Vector3D { - return new Vector3D( - (this.y * v.z - this.z * v.y) as T, - (this.z * v.x - this.x * v.z) as T, - (this.x * v.y - this.y * v.x) as T, + cross(v: Vector3D): Vector3D { + return new Vector3D( + (this.y * v.z - this.z * v.y) as U, + (this.z * v.x - this.x * v.z) as U, + (this.x * v.y - this.y * v.x) as U, ); } @@ -223,10 +223,10 @@ export class Vector3D { } // Calculate the angle _(rad)_ between this and another [Vector3D]. - angle(v: Vector3D): number { - const theta = Math.atan2(this.cross(v).magnitude(), this.dot(v)); + angle(v: Vector3D): Radians { + const theta = Math.atan2(this.cross(v).magnitude(), this.dot(v)) as Radians; - return isNaN(theta) ? 0 : theta; + return isNaN(theta) ? 0 as Radians : theta; } // Calculate the angle _(°)_ between this and another [Vector3D]. @@ -238,7 +238,7 @@ export class Vector3D { * Return `true` if line-of-sight exists between this and another [Vector3D] * with a central body of the given [radius]. */ - sight(v: Vector3D, radius: number): boolean { + sight(v: Vector3D, radius: Kilometers): boolean { const r1Mag2 = this.magnitude() ** 2; const r2Mag2 = v.magnitude() ** 2; const rDot = this.dot(v); diff --git a/src/types/types.ts b/src/types/types.ts index b42afed..070786a 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -23,7 +23,7 @@ import { Satellite } from 'src/objects'; import { PassType } from '../enums/PassType'; -import { Vector3D } from 'src/main'; +import { Vector3D } from '../main'; /** * Represents a distinct type. @@ -42,9 +42,6 @@ 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. */ export type Days = Distinct; @@ -58,60 +55,59 @@ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ export type Meters = Distinct; +/** + * Represents the type for seconds per meter per second. + */ +export type SecondsPerMeterPerSecond = Distinct; + +/** + * Represents a value in kilometers per second. + */ +export type KilometersPerSecond = Distinct; + +/** + * Represents a value in Radians per second. + */ +export type RadiansPerSecond = Distinct; + +/** + * Represents a value in meters per second. + */ +export type MetersPerSecond = Distinct; + /** * Represents a three-dimensional vector. * @@ -717,5 +713,5 @@ export type TleParams = { export type PositionVelocity = { position: Vector3D; - velocity: Vector3D; + velocity: Vector3D; };