Skip to content

Commit

Permalink
test: ✅ add testing for ITRF class
Browse files Browse the repository at this point in the history
  • Loading branch information
thkruz committed Jan 14, 2024
1 parent bb94780 commit 49501a3
Show file tree
Hide file tree
Showing 5 changed files with 573 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/coordinate/EquinoctialElements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { EquinoctialElementsParams } from '../interfaces/EquinoctialElementsPara
* (equatorial orbits). This makes them more reliable for numerical simulations
* and analytical studies, especially in these edge cases.
*
* Reference: https://faculty.nps.edu/dad/orbital/th0.pdf
* @see https://faculty.nps.edu/dad/orbital/th0.pdf
*/
export class EquinoctialElements {
epoch: EpochUTC;
Expand Down
2 changes: 1 addition & 1 deletion src/coordinate/Geodetic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class Geodetic {
}

if (altitude < -Earth.radiusMean) {
throw new RangeError('Altitude cannot be less than -6378.137 km.');
throw new RangeError(`Altitude must be greater than ${-Earth.radiusMean} km. Got ${altitude} km.`);
}

this.lat = latitude;
Expand Down
76 changes: 49 additions & 27 deletions src/coordinate/ITRF.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,64 @@ import { StateVector } from './StateVector';
* 1980 to 2014.
* @see https://en.wikipedia.org/wiki/International_Terrestrial_Reference_Frame
*
* This is a geocentric coordinate system, also referenced as ECEF (Earth
* This is a geocentric coordinate system, also referenced as ECF/ECEF (Earth
* Centered Earth Fixed). It is a Cartesian coordinate system with the origin at
* the center of the Earth. The x-axis intersects the sphere of the Earth at 0°
* latitude (the equator) and 0° longitude (the Prime Meridian). The z-axis goes
* through the North Pole. The y-axis goes through 90° East longitude. @see
* through the North Pole. The y-axis goes through 90° East longitude.
*
* @see
* https://en.wikipedia.org/wiki/Earth-centered,_Earth-fixed_coordinate_system
*/
export class ITRF extends StateVector {
/**
* Gets the name of the ITRF coordinate system.
* @returns The name of the coordinate system.
*/
get name(): string {
return 'ITRF';
}

/**
* Gets a value indicating whether the coordinate system is inertial.
* @returns A boolean value indicating whether the coordinate system is
* inertial.
*/
get inertial(): boolean {
return false;
}

getHeight(): number {
/**
* Gets the height of the ITRF coordinate above the surface of the Earth in
* kilometers.
*
* @returns The height in kilometers.
*/
get height(): Kilometers {
const a = Earth.radiusEquator;
const e2 = Earth.eccentricitySquared;
const r = this.position.magnitude();
const sl = this.position.z / r;
const cl2 = 1 - sl * sl;
const coeff = Math.sqrt((1 - e2) / (1 - e2 * cl2));

return r - a * coeff;
return (r - a * coeff) as Kilometers;
}

/**
* Converts the current coordinate to the J2000 coordinate system.
* Gets the altitude in kilometers.
* @returns The altitude in kilometers.
*/
get alt(): Kilometers {
return this.height;
}

/**
* Converts the current coordinate to the J2000 coordinate system. This is an
* Earth-Centered Inertial (ECI) coordinate system with the origin at the
* center of the Earth.
*
* @see https://en.wikipedia.org/wiki/Epoch_(astronomy)#Julian_years_and_J2000
* @returns The coordinate in the J2000 coordinate system.
*/
toJ2000(): J2000 {
Expand All @@ -61,15 +90,9 @@ export class ITRF extends StateVector {
}

/**
* Converts the coordinate from ITRF (ECEF) to J2000 (ECI) reference frame.
* @returns The coordinate in J2000 (ECI) reference frame.
*/
toEci(): J2000 {
return this.toJ2000();
}

/**
* Converts the current ITRF coordinate to Geodetic coordinate.
* Converts the current ITRF coordinate to Geodetic coordinate. This is a
* coordinate system for latitude, longitude, and altitude.
*
* @returns {Geodetic} The converted Geodetic coordinate.
*/
toGeodetic(): Geodetic {
Expand All @@ -82,23 +105,22 @@ export class ITRF extends StateVector {
const r = Math.sqrt(x * x + y * y);
const phi = Math.atan(z / r);
let lat = phi;
let alt: Kilometers;
let c = 0.0;

for (let i = 0; i < 12; i++) {
const slat = Math.sin(lat);
if (x === 0 && y === 0) {
lat = phi;
alt = z > 0 ? ((z - Earth.radiusPolar) as Kilometers) : ((z + Earth.radiusPolar) as Kilometers);
} else {
for (let i = 0; i < 20; i++) {
const slat = Math.sin(lat);

c = 1 / Math.sqrt(1 - esq * slat * slat);
lat = Math.atan((z + sma * c * esq * slat) / r);
c = 1 / Math.sqrt(1 - esq * slat * slat);
lat = Math.atan((z + sma * c * esq * slat) / r);
}
alt = (r / Math.cos(lat) - sma * c) as Kilometers;
}
const alt = r / Math.cos(lat) - sma * c;

return new Geodetic(lat as Radians, lon as Radians, alt as Kilometers);
}

/**
* Converts the current ECI coordinate to latitude, longitude, and altitude.
*/
toLla(): Geodetic {
return this.toGeodetic();
return new Geodetic(lat as Radians, lon as Radians, alt);
}
}
201 changes: 201 additions & 0 deletions test/coordinate/ITRF.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import { exampleDate } from '../lib/mockData';
import { Earth, EpochUTC, ITRF, Kilometers, Vector3D } from './../../src/main';
// Generated by CodiumAI

describe('ITRF', () => {
let epochUtc: EpochUTC;
let itrf: ITRF;

beforeEach(() => {
epochUtc = EpochUTC.fromDateTime(exampleDate);
itrf = new ITRF(epochUtc, new Vector3D(1000, 2000, 3000), new Vector3D(10, 20, 30));
});

// can get the name of the ITRF coordinate system
it('should return the name of the coordinate system', () => {
expect(itrf.name).toMatchSnapshot();
});

// can determine if the coordinate system is inertial
it('should return whether the coordinate system is inertial', () => {
expect(itrf.inertial).toMatchSnapshot();
});

/*
* can get the height of the ITRF coordinate above the surface of the Earth in
* kilometers
*/
it('should return the height above the surface of the Earth in kilometers', () => {
expect(itrf.height).toMatchSnapshot();
});

// can get the altitude in kilometers
it('should return the altitude in kilometers', () => {
expect(itrf.alt).toMatchSnapshot();
});

// can convert the current coordinate to the J2000 coordinate system
it('should convert the current coordinate to the J2000 coordinate system', () => {
expect(itrf.toJ2000()).toMatchSnapshot();
});

// can convert the current ITRF coordinate to Geodetic coordinate
it('should convert the current ITRF coordinate to Geodetic coordinate', () => {
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// 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));
expect(itrf.toJ2000()).toMatchSnapshot();
});

// can handle position at the North Pole
it('should handle position at the North Pole', () => {
const itrf = new ITRF(
epochUtc,
new Vector3D<Kilometers>(0 as Kilometers, 0 as Kilometers, Earth.radiusPolar),
new Vector3D(0, 0, 0),
);

expect(itrf.toJ2000()).toMatchSnapshot();
// Test the toGeodetic method
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// 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));

itrf.position = new Vector3D(0, 0, -Earth.radiusPolar);
expect(itrf.toJ2000()).toMatchSnapshot();
// Test the toGeodetic method
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// can handle position at the International Date Line
it('should handle position at the International Date Line', () => {
const itrf = new ITRF(
epochUtc,
new Vector3D<Kilometers>(Earth.radiusEquator, 0 as Kilometers, 0 as Kilometers),
new Vector3D(0, 0, 0),
);

// Test height
expect(itrf.height).toMatchSnapshot();

// Test alt
expect(itrf.alt).toMatchSnapshot();

// Test toJ2000
expect(itrf.toJ2000()).toMatchSnapshot();

// Test toGeodetic
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// can handle position at the Prime Meridian
it('should handle position at the Prime Meridian', () => {
const itrf = new ITRF(
epochUtc,
new Vector3D<Kilometers>(0 as Kilometers, Earth.radiusEquator, 0 as Kilometers),
new Vector3D(0, 0, 0),
);

expect(itrf.name).toMatchSnapshot();
expect(itrf.inertial).toMatchSnapshot();
expect(itrf.height).toMatchSnapshot();
expect(itrf.alt).toMatchSnapshot();
expect(itrf.toJ2000()).toMatchSnapshot();
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// can handle position at high altitude
it('should handle position at high altitude', () => {
const itrf = new ITRF(
epochUtc,
new Vector3D<Kilometers>(0 as Kilometers, (Earth.radiusEquator + 1000) as Kilometers, 0 as Kilometers),
new Vector3D(0, 0, 0),
);

expect(itrf.height).toMatchSnapshot();
expect(itrf.alt).toMatchSnapshot();
expect(itrf.toJ2000()).toMatchSnapshot();
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// can handle position at low altitude
it('should handle position at low altitude', () => {
const itrf = new ITRF(
epochUtc,
new Vector3D<Kilometers>(0 as Kilometers, (Earth.radiusEquator - 10) as Kilometers, 0 as Kilometers),
new Vector3D(0, 0, 0),
);

expect(itrf.height).toMatchSnapshot();
expect(itrf.alt).toMatchSnapshot();
expect(itrf.toJ2000()).toMatchSnapshot();
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// can handle position at the antimeridian
it('should handle position at the antimeridian', () => {
const itrf = new ITRF(
epochUtc,
new Vector3D(0 as Kilometers, 0 as Kilometers, Earth.radiusEquator),
new Vector3D(0, 0, 0),
);

// Test the height property
expect(itrf.height).toMatchSnapshot();

// Test the alt property
expect(itrf.alt).toMatchSnapshot();

// Test the toJ2000 method
expect(itrf.toJ2000()).toMatchSnapshot();

// Test the toGeodetic method
expect(() => itrf.toGeodetic()).toMatchSnapshot();
});

// 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));

expect(itrf.name).toMatchSnapshot();
expect(itrf.inertial).toMatchSnapshot();
expect(itrf.height).toMatchSnapshot();
expect(itrf.alt).toMatchSnapshot();
expect(itrf.toJ2000()).toMatchSnapshot();
expect(itrf.toGeodetic()).toMatchSnapshot();
});

// 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 epoch1 = EpochUTC.fromDateTime(exampleDate);
const epoch2 = EpochUTC.fromDateTime(new Date(exampleDate.getTime() + 1000));
const itrf1 = new ITRF(epoch1, position, velocity);
const itrf2 = new ITRF(epoch2, position, velocity);

// Test height property
expect(itrf1.height).toMatchSnapshot();
expect(itrf2.height).toMatchSnapshot();

// Test alt property
expect(itrf1.alt).toMatchSnapshot();
expect(itrf2.alt).toMatchSnapshot();

// Test toJ2000 method
const j2000 = itrf1.toJ2000();

expect(j2000).toMatchSnapshot();

// Test toGeodetic method
const geodetic = itrf1.toGeodetic();

expect(geodetic).toMatchSnapshot();
});
});
Loading

0 comments on commit 49501a3

Please sign in to comment.