Coordinate Systems Guide

December 7, 2025 · View on GitHub

This guide provides a comprehensive overview of the coordinate systems available in OOTK, explaining their differences, relationships, and when to use each one.


Table of Contents

  1. Overview
  2. Quick Reference
  3. Inertial Reference Frames (ECI)
  4. Earth-Fixed Reference Frames (ECEF)
  5. Relative Motion Frames
  6. Orbital Element Representations
  7. Coordinate Transformations
  8. When to Use Each System
  9. Common Workflows

Overview

OOTK provides multiple coordinate systems to represent positions and velocities of objects in space. Choosing the right coordinate system is essential for efficient and accurate calculations.

Key Concepts

Inertial Frames: Fixed relative to distant stars; Newton's laws apply directly. Use for orbit propagation and mechanics calculations.

Non-Inertial Frames: Rotate or accelerate relative to inertial space; require accounting for fictitious forces. Use for Earth-fixed applications.

Relative Frames: Defined relative to another orbiting object. Use for proximity operations and formation flying.


Quick Reference

SystemTypeInertialPrimary Use
J2000ECIYesGeneral orbit propagation, high-precision calculations
TEMEECIYesSGP4 propagation, TLE-based tracking
ITRFECEFNoGround station calculations, Earth-fixed applications
GeodeticGeographicNoLocation data (lat/lon/alt)
RICRelativeNoConjunction analysis, relative motion
HillRelativeNoProximity operations, rendezvous planning
ClassicalElements-Orbit definition, mission design
EquinoctialElements-Numerical propagation, avoiding singularities

Inertial Reference Frames (ECI)

Earth-Centered Inertial (ECI) frames have their origin at Earth's center and do not rotate with Earth. These are essential for orbit propagation because Newton's laws of motion apply directly.

J2000

The J2000 coordinate system is the standard inertial reference frame defined at epoch J2000.0 (January 1, 2000, 12:00 TT).

Definition

  • Origin: Center of Earth
  • X-axis: Aligned with the mean vernal equinox at J2000.0
  • Z-axis: Aligned with Earth's mean rotation axis (celestial North Pole) at J2000.0
  • Y-axis: Completes the right-handed system (90° East along the celestial equator)

When to Use

  • General orbit propagation with numerical integrators
  • High-precision calculations requiring a stable reference
  • Interoperability with other systems (J2000 is widely used)
  • Long-duration orbit predictions
  • Force modeling (gravity, drag, SRP)

Example

import { J2000, EpochUTC, Kilometers, KilometersPerSecond, Vector3D } from 'ootk';

const epoch = EpochUTC.fromDateTime(new Date());
const position = new Vector3D(6778.137 as Kilometers, 0 as Kilometers, 0 as Kilometers);
const velocity = new Vector3D(0 as KilometersPerSecond, 7.67 as KilometersPerSecond, 0 as KilometersPerSecond);

const state = new J2000(epoch, position, velocity);

// Convert to other frames
const teme = state.toTEME();
const itrf = state.toITRF();
const elements = state.toClassicalElements();

TEME

True Equator Mean Equinox (TEME) is an inertial frame specifically used by the SGP4 propagator.

Definition

  • Origin: Center of Earth
  • Equator: Earth's true equator of date
  • Equinox: Mean equinox of date
  • Purpose: Native output frame for SGP4/SDP4 propagation

When to Use

  • SGP4 propagation from TLE data
  • TLE-based satellite tracking
  • Compatibility with NORAD catalog data
  • When working with Two-Line Elements

Key Difference from J2000

TEME accounts for precession and nutation differently than J2000. While both are inertial, TEME is defined relative to the true equator and mean equinox of date, whereas J2000 is fixed to the epoch J2000.0. This makes TEME the natural output of SGP4 but requires conversion for use with most other tools.

Example

import { Satellite } from 'ootk';

// Create satellite from TLE
const sat = new Satellite({
  tle1: '1 25544U 98067A   24028.54545847  .00031576  00000-0  57240-3 0  9991',
  tle2: '2 25544  51.6418 292.2590 0002595 167.5319 252.0460 15.49326324436741'
});

// SGP4 outputs TEME natively
const teme = sat.propagate(new Date());  // Returns TEME state

// Convert to J2000 for further calculations
const j2000 = teme.toJ2000();

Earth-Fixed Reference Frames (ECEF)

Earth-Centered Earth-Fixed (ECEF) frames rotate with Earth. These are essential for ground-based applications.

ITRF

The International Terrestrial Reference Frame (ITRF) is the standard Earth-fixed Cartesian coordinate system.

Definition

  • Origin: Center of Earth
  • X-axis: Passes through 0° latitude, 0° longitude (intersection of equator and prime meridian)
  • Y-axis: Passes through 0° latitude, 90° East longitude
  • Z-axis: Passes through the North Pole
  • Rotation: Rotates with Earth (~15°/hour)

When to Use

  • Ground station position calculations
  • Satellite sub-point determination
  • Surface-relative measurements
  • Converting between geographic and Cartesian coordinates
  • Look angle calculations from ground sites

Example

import { J2000, ITRF, Geodetic, Degrees, Kilometers } from 'ootk';

// Convert J2000 to Earth-fixed
const j2000State = /* ... */;
const itrf = j2000State.toITRF();

// Get latitude/longitude/altitude
const geodetic = itrf.toGeodetic();
console.log(`Lat: ${geodetic.latDeg}°, Lon: ${geodetic.lonDeg}°, Alt: ${geodetic.alt} km`);

Geodetic

Geodetic coordinates represent positions on or near Earth's surface using latitude, longitude, and altitude.

Definition

  • Latitude: Angle from equatorial plane (-90° to +90°)
  • Longitude: Angle from prime meridian (-180° to +180°)
  • Altitude: Height above the WGS84 reference ellipsoid

When to Use

  • Ground station locations
  • Sensor and observer positions
  • Geographic data input/output
  • Human-readable location representation
  • Visibility calculations from ground

Example

import { Geodetic, Degrees, Kilometers } from 'ootk';

// Create from degrees (more intuitive)
const groundStation = Geodetic.fromDegrees(
  41.754785 as Degrees,   // latitude
  -70.539151 as Degrees,  // longitude
  0.060966 as Kilometers  // altitude
);

// Convert to ITRF for calculations
const itrf = groundStation.toITRF(epoch);

// Calculate distance to another point
const otherStation = Geodetic.fromDegrees(40.0 as Degrees, -75.0 as Degrees, 0 as Kilometers);
const distance = groundStation.distance(otherStation);

Relative Motion Frames

Relative frames describe the position and velocity of one object relative to another orbiting object. These are essential for proximity operations.

RIC (Radial-Intrack-Crosstrack)

RIC coordinates describe relative motion using a frame attached to a reference satellite.

Definition

  • R (Radial): Along the radius vector from Earth center through the reference satellite (positive outward)
  • I (Intrack): Along the velocity direction of the reference satellite (positive in direction of motion)
  • C (Crosstrack): Perpendicular to both R and I, completing the right-handed system (positive toward angular momentum)

When to Use

  • Conjunction assessments and collision avoidance
  • Relative motion analysis
  • Close approach characterization
  • Covariance representation for conjunction screening
  • Miss distance calculations

Example

import { J2000, RIC } from 'ootk';

// Two satellites at the same epoch
const reference = new J2000(epoch, refPosition, refVelocity);
const target = new J2000(epoch, targetPosition, targetVelocity);

// Get relative state in RIC
const ric = RIC.fromJ2000(target, reference);

console.log(`Radial separation: ${ric.position.x} km`);
console.log(`Intrack separation: ${ric.position.y} km`);
console.log(`Crosstrack separation: ${ric.position.z} km`);
console.log(`Total range: ${ric.range} km`);
console.log(`Range rate: ${ric.rangeRate} km/s`);

// Convert back to J2000
const targetRecovered = ric.toJ2000(reference);

Hill Frame

The Hill frame (also called Clohessy-Wiltshire or LVLH frame) is optimized for proximity operations and uses analytical equations of relative motion.

Definition

Similar to RIC but specifically designed for proximity operations:

  • X (Radial): Radial direction (positive outward from Earth)
  • Y (Intrack): Along-track direction (positive in velocity direction)
  • Z (Crosstrack): Cross-track direction (completes right-handed system)

Key Features

  • Analytical propagation: Uses Hill/Clohessy-Wiltshire equations (no numerical integration needed)
  • Maneuver planning: Built-in methods for computing delta-v requirements
  • Natural Motion Coordinates: Support for NMC-based relative orbits
  • State transition matrix: Closed-form solution for relative motion

When to Use

  • Rendezvous and proximity operations (RPO)
  • Formation flying design and maintenance
  • Relative orbit planning
  • Maneuver optimization for proximity operations
  • Quick relative motion predictions (analytical, no integration)

Example

import { J2000, Hill, Kilometers, KilometersPerSecond, Seconds } from 'ootk';

// Create Hill state from reference orbit
const origin = new J2000(epoch, position, velocity);

// Method 1: From relative state components
const hill = Hill.fromState(
  origin,
  0.1 as Kilometers,           // radial offset
  1.0 as Kilometers,           // intrack offset
  0.001 as KilometersPerSecond, // cross-track velocity
  0 as Seconds                 // node offset time
);

// Method 2: From Natural Motion Coordinates
const hillNmc = Hill.fromNmc(
  origin,
  2.0 as Kilometers,           // major axis range
  0.001 as KilometersPerSecond, // node velocity
  0 as Seconds                 // node offset time
);

// Propagate using analytical equations (fast!)
const later = hill.propagate(newEpoch);

// Plan a maneuver to a waypoint
const waypoint = new Waypoint(/* ... */);
const maneuver = hill.solveManeuver(waypoint);

// Convert to J2000 for comparison with other data
const j2000State = hill.toJ2000(origin);

Orbital Element Representations

Orbital elements describe the shape and orientation of an orbit rather than instantaneous position/velocity.

Classical Elements

Classical (Keplerian) elements are the traditional six parameters describing an orbit.

Elements

ElementSymbolDescriptionUnits
Semi-major axisaSize of orbitkm
EccentricityeShape (0=circular, 1=parabolic)-
InclinationiTilt relative to equatorrad/deg
RAANΩLongitude of ascending noderad/deg
Argument of perigeeωOrientation within orbital planerad/deg
True anomalyνPosition along orbitrad/deg

When to Use

  • Orbit definition and description
  • Mission design and planning
  • TLE generation
  • Orbit visualization and understanding
  • Quick analytical propagation (Kepler's equation)

Limitations

Classical elements have singularities for:

  • Circular orbits (e = 0): Argument of perigee undefined
  • Equatorial orbits (i = 0): RAAN undefined

Example

import { ClassicalElements, Degrees, Kilometers, EpochUTC } from 'ootk';

const elements = new ClassicalElements({
  epoch: EpochUTC.fromDateTime(new Date()),
  semimajorAxis: 6778.137 as Kilometers,
  eccentricity: 0.001,
  inclination: 51.6418 as Degrees,
  rightAscension: 292.259 as Degrees,
  argOfPerigee: 167.5319 as Degrees,
  trueAnomaly: 252.046 as Degrees
});

// Get orbital characteristics
console.log(`Period: ${elements.period} minutes`);
console.log(`Apogee: ${elements.apogee} km`);
console.log(`Perigee: ${elements.perigee} km`);
console.log(`Regime: ${elements.getOrbitRegime()}`);

// Convert to position/velocity
const pv = elements.toPositionVelocity();

// Propagate analytically
const propagated = elements.propagate(newEpoch);

// Generate TLE
const tle = elements.toTLE({ intlDes: '98067A', epochYear: 24, epochDay: 28.5 });

Equinoctial Elements

Equinoctial elements are an alternative representation that avoids the singularities of classical elements.

Elements

ElementSymbolDescription
Semi-major axisaSize of orbit
hh = e·sin(ω + Ω)Eccentricity vector component
kk = e·cos(ω + Ω)Eccentricity vector component
pp = tan(i/2)·sin(Ω)Ascending node component
qq = tan(i/2)·cos(Ω)Ascending node component
Mean longitudeλCombined mean anomaly + ω + Ω
Retrograde factorI+1 prograde, -1 retrograde

When to Use

  • Numerical propagation requiring smooth derivatives
  • Circular orbits (avoids e=0 singularity)
  • Equatorial orbits (avoids i=0 singularity)
  • Orbit determination algorithms
  • Optimization problems in astrodynamics

Example

import { EquinoctialElements, ClassicalElements } from 'ootk';

// Create from classical elements
const classical = new ClassicalElements({ /* ... */ });
const equinoctial = classical.toEquinoctialElements();

// Or convert back
const classicalRecovered = equinoctial.toClassicalElements();

// Get position/velocity
const pv = equinoctial.toPositionVelocity();

Coordinate Transformations

Transformation Diagram

┌─────────────────────────────────────────────────────────────────────┐
│                    INERTIAL FRAMES (ECI)                            │
│  ┌─────────┐                              ┌─────────┐               │
│  │  J2000  │◄────────────────────────────►│  TEME   │               │
│  └────┬────┘   toTEME() / toJ2000()       └────┬────┘               │
│       │                                        │                     │
│       │ toITRF()                               │                     │
│       │ toClassicalElements()                  │                     │
│       ▼                                        │                     │
└───────┼────────────────────────────────────────┼─────────────────────┘
        │                                        │
        │                                        │
┌───────┼────────────────────────────────────────┼─────────────────────┐
│       ▼                  EARTH-FIXED FRAMES                         │
│  ┌─────────┐                              ┌──────────┐              │
│  │  ITRF   │◄────────────────────────────►│ Geodetic │              │
│  └─────────┘   toGeodetic() / toITRF()    └──────────┘              │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────┐
│                      RELATIVE FRAMES                                 │
│  ┌─────────┐                              ┌─────────┐               │
│  │   RIC   │                              │  Hill   │               │
│  └────┬────┘                              └────┬────┘               │
│       │                                        │                     │
│       │ fromJ2000(target, reference)           │ toJ2000(origin)    │
│       │ toJ2000(reference)                     │                     │
│       ▼                                        ▼                     │
│           Both convert to/from J2000 frame                          │
└──────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────┐
│                    ORBITAL ELEMENTS                                  │
│  ┌─────────────────┐              ┌─────────────────────┐           │
│  │    Classical    │◄────────────►│    Equinoctial      │           │
│  └────────┬────────┘              └─────────────────────┘           │
│           │                                                          │
│           │ toPositionVelocity()                                    │
│           │ fromStateVector()                                       │
│           ▼                                                          │
│       StateVector (J2000/TEME)                                      │
└──────────────────────────────────────────────────────────────────────┘

Common Conversions

// ECI to ECEF
const itrf = j2000.toITRF();

// ECEF to ECI
const j2000 = itrf.toJ2000();

// ECI to Geographic
const geodetic = j2000.toITRF().toGeodetic();

// Geographic to ECI
const j2000 = geodetic.toITRF(epoch).toJ2000();

// State vector to elements
const elements = j2000.toClassicalElements();

// Elements to state vector
const j2000 = J2000.fromClassicalElements(elements);

// TEME to J2000
const j2000 = teme.toJ2000();

// Absolute to relative
const ric = RIC.fromJ2000(target, reference);

// Relative to absolute
const target = ric.toJ2000(reference);

When to Use Each System

Decision Guide

What are you trying to do?
│
├─► Propagate an orbit?
│   ├─► From TLE data ──────────────► TEME (SGP4 native)
│   └─► Numerical integration ──────► J2000
│
├─► Work with ground stations?
│   ├─► Location input ─────────────► Geodetic
│   └─► Position calculations ──────► ITRF
│
├─► Analyze close approaches?
│   └─► Conjunction/miss distance ──► RIC
│
├─► Plan proximity operations?
│   ├─► Rendezvous maneuvers ───────► Hill
│   └─► Formation flying ───────────► Hill
│
├─► Define or describe an orbit?
│   ├─► Human-readable ─────────────► Classical Elements
│   └─► Numerical algorithms ───────► Equinoctial Elements
│
└─► Store/exchange data?
    ├─► With other systems ─────────► J2000 (most common standard)
    └─► TLE format ─────────────────► Classical Elements

Summary Table

TaskRecommended SystemReason
SGP4/TLE propagationTEMENative SGP4 output
Numerical propagationJ2000Stable inertial frame
Ground station workGeodetic → ITRFHuman-readable input, Cartesian math
Satellite sub-pointITRF → GeodeticEarth-fixed for ground track
Collision avoidanceRICStandard for conjunction screening
Rendezvous planningHillAnalytical relative motion
Formation flyingHillClosed-form propagation
Orbit definitionClassical ElementsIntuitive orbital parameters
Numerical algorithmsEquinoctial ElementsNo singularities
Data exchangeJ2000Widely used standard

Common Workflows

Satellite Tracking from TLE

import { Satellite, Sensor, Degrees, Kilometers } from 'ootk';

// 1. Parse TLE (internally stored as TEME-compatible)
const sat = new Satellite({ tle1: '...', tle2: '...' });

// 2. Propagate to current time (SGP4 → TEME)
const teme = sat.propagate(new Date());

// 3. Convert to J2000 for standard processing
const j2000 = teme.toJ2000();

// 4. Convert to ITRF for ground-relative calculations
const itrf = j2000.toITRF();

// 5. Get lat/lon/alt
const geodetic = itrf.toGeodetic();
console.log(`Satellite at: ${geodetic.latDeg}°, ${geodetic.lonDeg}°`);

Conjunction Assessment

import { J2000, RIC } from 'ootk';

// Two satellites at time of closest approach
const primary = new J2000(epoch, pos1, vel1);
const secondary = new J2000(epoch, pos2, vel2);

// Calculate relative state
const ric = RIC.fromJ2000(secondary, primary);

// Analyze miss distance
console.log(`Miss distance: ${ric.range} km`);
console.log(`Radial: ${ric.position.x} km`);
console.log(`Intrack: ${ric.position.y} km`);
console.log(`Crosstrack: ${ric.position.z} km`);
console.log(`Closing velocity: ${ric.rangeRate} km/s`);

Proximity Operations Planning

import { J2000, Hill, Kilometers, KilometersPerSecond, Seconds } from 'ootk';

// Reference (target) spacecraft
const target = new J2000(epoch, targetPos, targetVel);

// Create chaser relative state
const hill = Hill.fromNmc(
  target,
  1.0 as Kilometers,            // 1 km relative orbit
  0.0 as KilometersPerSecond,   // no cross-track velocity
  0 as Seconds                  // at node
);

// Propagate relative motion (analytical - very fast)
const hillLater = hill.propagate(laterEpoch);

// Plan maneuver to approach
const waypoint = /* define approach waypoint */;
const maneuver = hill.solveManeuver(waypoint);
console.log(`Delta-V required: ${maneuver.deltaV} m/s`);

Converting Between All Systems

import {
  J2000, TEME, ITRF, Geodetic, RIC, Hill,
  ClassicalElements, EquinoctialElements
} from 'ootk';

// Start with J2000 state
const j2000 = new J2000(epoch, position, velocity);

// To other inertial frame
const teme = j2000.toTEME();

// To Earth-fixed
const itrf = j2000.toITRF();

// To geographic
const geodetic = itrf.toGeodetic();

// To orbital elements
const classical = j2000.toClassicalElements();
const equinoctial = classical.toEquinoctialElements();

// To relative frame (need reference)
const reference = new J2000(epoch, refPos, refVel);
const ric = RIC.fromJ2000(j2000, reference);

// Back to J2000
const j2000Recovered = ric.toJ2000(reference);

API Method Reference: Which Frame Does Each Method Return?

Understanding which coordinate frame each method returns is critical for correct calculations. This section provides a quick reference.

SpaceObject / Satellite Methods

MethodReturnsFrameNotes
eci()PosVelTEMENative SGP4 output. Use for visualization, quick calculations
ecef()EcefVec3pseudo-ECEFSimplified GMST rotation only. Use for quick Earth-fixed
lla()LlaVec3GeodeticVia simplified ECEF. Use for ground track display
toJ2000()J2000J2000Full transformation. Use for precise calculations
toITRF()ITRFITRFFull transformation. Use for precise Earth-fixed
toGeodetic()GeodeticGeodeticVia J2000→ITRF. More precise than lla()
toClassicalElements()ClassicalElementsJ2000-basedKeplerian elements

Important: eci() Returns TEME, Not Generic ECI

The eci() method returns coordinates in the TEME (True Equator Mean Equinox) frame, which is the native output of SGP4/SDP4 propagation. This is an Earth-Centered Inertial frame, but it differs from J2000:

// eci() returns TEME frame
const teme = satellite.eci(date);  // TemeVec3 position/velocity

// toJ2000() returns J2000 frame
const j2000 = satellite.toJ2000(date);  // J2000 state vector

// The difference matters for precise calculations
// TEME and J2000 can differ by hundreds of meters

Simplified vs Precise Earth-Fixed Transformations

OOTK provides two levels of Earth-fixed coordinate transformations:

Simplified (GMST-only)

// Quick transformation using GMST rotation only
const ecef = satellite.ecef(date);    // pseudo-ECEF
const lla = satellite.lla(date);       // via pseudo-ECEF
  • Uses simple rotation by Greenwich Mean Sidereal Time
  • Does NOT account for: precession, nutation, polar motion
  • Accuracy: ~1 km for typical applications
  • Speed: Faster computation

Precise (Full IAU transformation)

// Full transformation via J2000 → ITRF
const itrf = satellite.toITRF(date);           // precise ITRF
const geodetic = satellite.toGeodetic(date);   // via precise ITRF
  • Full transformation chain: TEME → J2000 → ITRF
  • Accounts for: precession, nutation, Earth rotation (GMST + equation of equinoxes)
  • Accuracy: Sub-meter for most applications
  • Speed: Slower (more matrix operations)

Low-Level Transform Functions

The transforms.ts module provides low-level functions that work with TEME coordinates:

FunctionInput FrameOutput FrameNotes
eci2ecef()TEMEpseudo-ECEFGMST rotation only
ecef2eci()pseudo-ECEFTEMEGMST rotation only
eci2lla()TEMEGeodeticVia GMST rotation
lla2eci()GeodeticTEMEVia GMST rotation
ecef2rae()pseudo-ECEFRAETopocentric
rae2ecef()RAEpseudo-ECEFTopocentric

For precise transformations, use the class methods (J2000.toITRF(), ITRF.toJ2000(), etc.) instead of these low-level functions.

Conversion Chain Summary

┌────────────────────────────────────────────────────────────────┐
│ SGP4 Propagation Output                                        │
│                                                                 │
│  satellite.eci() ──► TEME (position/velocity)                  │
│        │                                                        │
│        │ .toJ2000() (precession + nutation)                    │
│        ▼                                                        │
│      J2000 ◄──────────────────────────────────────────────────►│
│        │                                                        │
│        │ .toITRF() (precession + nutation + Earth rotation)    │
│        ▼                                                        │
│      ITRF                                                       │
│        │                                                        │
│        │ .toGeodetic() (ellipsoidal conversion)                │
│        ▼                                                        │
│    Geodetic (lat/lon/alt)                                      │
└────────────────────────────────────────────────────────────────┘

SIMPLIFIED PATH (less accurate but faster):
  satellite.eci() ──► TEME ──► ecef() ──► pseudo-ECEF ──► lla() ──► Geodetic
                           (GMST only)              (GMST only)

Further Reading