Skip to content

Commit

Permalink
refactor: 🏷️ add proper velocity types
Browse files Browse the repository at this point in the history
  • Loading branch information
thkruz committed Jan 16, 2024
1 parent 0f1ffbf commit 32ae737
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 80 deletions.
7 changes: 6 additions & 1 deletion src/body/Earth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
Kilometers,
RAD2DEG,
Radians,
RadiansPerSecond,
secondsPerDay,
secondsPerSiderealDay,
TAU,
Expand Down Expand Up @@ -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<RadiansPerSecond>(
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 {
Expand Down
28 changes: 14 additions & 14 deletions src/coordinate/ClassicalElements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
});
}
Expand Down Expand Up @@ -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<Kilometers>;
const velocity = vPQW
.rotZ(-this.argPerigee as Radians)
.rotX(-this.inclination)
.rotZ(-this.rightAscension as Radians);
.rotZ(-this.rightAscension as Radians) as Vector3D<KilometersPerSecond>;

return { position: position as Vector3D<Kilometers>, velocity: velocity as Vector3D<Kilometers> };
return { position, velocity };
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/coordinate/Geodetic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<Kilometers>);
return new ITRF(epoch, r, Vector3D.origin as Vector3D<KilometersPerSecond>);
}

/**
Expand Down
9 changes: 6 additions & 3 deletions src/coordinate/ITRF.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<Kilometers>).rotZ(-ast as Radians);
const vTOD = this.velocity
// TODO: #13 Intermediate unit type is incorrect.

Check warning on line 97 in src/coordinate/ITRF.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: #13 Intermediate unit type is...'
.add(Earth.rotation.cross(this.position) as unknown as Vector3D<KilometersPerSecond>)
.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
Expand All @@ -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<Kilometers>;
.rotZ(p.zeta) as Vector3D<KilometersPerSecond>;

return new J2000(this.epoch, rJ2000, vJ2000);
}
Expand Down
6 changes: 3 additions & 3 deletions src/coordinate/J2000.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -92,7 +92,7 @@ export class J2000 extends StateVector {
.rotZ(-n.dPsi as Radians)
.rotX(-n.eps);
const rPEF = rTOD.rotZ(ast) as Vector3D<Kilometers>;
const vPEF = vTOD.rotZ(ast).add(Earth.rotation.negate().cross(rPEF)) as Vector3D<Kilometers>;
const vPEF = vTOD.rotZ(ast).add(Earth.rotation.negate().cross(rPEF)) as Vector3D<KilometersPerSecond>;

return new ITRF(this.epoch, rPEF, vPEF);
}
Expand Down Expand Up @@ -123,7 +123,7 @@ export class J2000 extends StateVector {
.rotX(n.mEps)
.rotZ(-n.dPsi as Radians)
.rotX(-eps)
.rotZ(dPsiCosEps) as Vector3D<Kilometers>;
.rotZ(dPsiCosEps) as Vector3D<KilometersPerSecond>;

return new TEME(this.epoch, rTEME, vTEME);
}
Expand Down
6 changes: 3 additions & 3 deletions src/coordinate/RelativeState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -31,8 +31,8 @@ import { J2000 } from './J2000';
*/
export abstract class RelativeState {
position: Vector3D<Kilometers>;
velocity: Vector3D<Kilometers>;
constructor(position: Vector3D<Kilometers>, velocity: Vector3D<Kilometers>) {
velocity: Vector3D<KilometersPerSecond>;
constructor(position: Vector3D<Kilometers>, velocity: Vector3D<KilometersPerSecond>) {
this.position = position;
this.velocity = velocity;
}
Expand Down
6 changes: 3 additions & 3 deletions src/coordinate/StateVector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -35,8 +35,8 @@ import { ClassicalElements } from './ClassicalElements';
export abstract class StateVector {
epoch: EpochUTC;
position: Vector3D<Kilometers>;
velocity: Vector3D<Kilometers>;
constructor(epoch: EpochUTC, position: Vector3D<Kilometers>, velocity: Vector3D<Kilometers>) {
velocity: Vector3D<KilometersPerSecond>;
constructor(epoch: EpochUTC, position: Vector3D<Kilometers>, velocity: Vector3D<KilometersPerSecond>) {
this.epoch = epoch;
this.position = position;
this.velocity = velocity;
Expand Down
4 changes: 2 additions & 2 deletions src/coordinate/TEME.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<Kilometers>;
.rotZ(p.zeta) as Vector3D<KilometersPerSecond>;

return new J2000(this.epoch, rJ2K, vJ2K);
}
Expand Down
9 changes: 5 additions & 4 deletions src/coordinate/Tle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
Degrees,
EciVec3,
Kilometers,
KilometersPerSecond,
Line1Data,
Line2Data,
Minutes,
Expand Down Expand Up @@ -246,8 +247,8 @@ export class Tle {

return new TEME(
epoch,
new Vector3D<Kilometers>(<Kilometers>r[0], <Kilometers>r[1], <Kilometers>r[2]),
new Vector3D<Kilometers>(<Kilometers>v[0], <Kilometers>v[1], <Kilometers>v[2]),
new Vector3D(<Kilometers>r[0], <Kilometers>r[1], <Kilometers>r[2]),
new Vector3D(<KilometersPerSecond>v[0], <KilometersPerSecond>v[1], <KilometersPerSecond>v[2]),
);
}

Expand Down Expand Up @@ -283,8 +284,8 @@ export class Tle {

return new TEME(
this.epoch,
new Vector3D<Kilometers>(<Kilometers>r[0], <Kilometers>r[1], <Kilometers>r[2]),
new Vector3D<Kilometers>(<Kilometers>v[0], <Kilometers>v[1], <Kilometers>v[2]),
new Vector3D(<Kilometers>r[0], <Kilometers>r[1], <Kilometers>r[2]),
new Vector3D(<KilometersPerSecond>v[0], <KilometersPerSecond>v[1], <KilometersPerSecond>v[2]),
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/objects/Satellite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
EcfVec3,
EciVec3,
Kilometers,
KilometersPerSecond,
LlaVec3,
Minutes,
PosVel,
Expand Down Expand Up @@ -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<KilometersPerSecond>;

const epoch = new EpochUTC(date.getTime());
const pos = new Vector3D(p.x, p.y, p.z);
Expand Down
9 changes: 6 additions & 3 deletions src/observation/RAE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<Kilometers>).toJ2000().position;
return new ITRF(this.epoch, rEcef, Vector3D.origin as Vector3D<KilometersPerSecond>).toJ2000().position;
}

/**
Expand Down Expand Up @@ -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.

Check warning on line 234 in src/observation/RAE.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: #13 Intermediate unit type is...'
.rotZ(-geo.lon as Radians) as unknown as Vector3D<KilometersPerSecond>;
const rEcef = pEcef.add(ecef.position);

return new ITRF(this.epoch, rEcef, pDotEcef).toJ2000();
Expand Down
30 changes: 15 additions & 15 deletions src/operations/Vector3D.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -125,13 +125,13 @@ export class Vector3D<T extends number = number> {
}

// / Return a copy of this [Vector3D] scaled by [n];
scale(n: number): Vector3D<T> {
return new Vector3D<T>(this.x * n as T, this.y * n as T, this.z * n as T);
scale<U extends number>(n: U): Vector3D<U> {
return new Vector3D<U>(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<T> {
return this.scale(-1);
return this.scale(-1 as T);
}

/**
Expand All @@ -158,8 +158,8 @@ export class Vector3D<T extends number = number> {
}

// 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<T extends number>(v: Vector3D<T>): 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].
Expand All @@ -172,11 +172,11 @@ export class Vector3D<T extends number = number> {
}

// Calculate the cross product of this and another [Vector3D].
cross(v: Vector3D<T>): Vector3D<T> {
return new Vector3D<T>(
(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<U extends number>(v: Vector3D<U>): Vector3D<U> {
return new Vector3D<U>(
(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,
);
}

Expand Down Expand Up @@ -223,10 +223,10 @@ export class Vector3D<T extends number = number> {
}

// Calculate the angle _(rad)_ between this and another [Vector3D].
angle(v: Vector3D<T>): number {
const theta = Math.atan2(this.cross(v).magnitude(), this.dot(v));
angle<U extends number>(v: Vector3D<U>): 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].
Expand All @@ -238,7 +238,7 @@ export class Vector3D<T extends number = number> {
* 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<KilometersPerSecond>, radius: Kilometers): boolean {
const r1Mag2 = this.magnitude() ** 2;
const r2Mag2 = v.magnitude() ** 2;
const rDot = this.dot(v);
Expand Down
Loading

0 comments on commit 32ae737

Please sign in to comment.