OOTK User Guide
Orbital Object Toolkit (ootk) - Comprehensive Documentation
Version: 5.1.1
Table of Contents
- Introduction
- Core Concepts
- Satellite Operations
- Sensor Operations
- Coordinate Systems
- Orbit Propagation
- Force Models
- Initial Orbit Determination
- Observations
- Time Systems
- Interpolation
- Mathematical Operations
- Optimization
- Covariance
- Celestial Bodies
- Maneuvers
- Advanced Topics
Introduction
OOTK is a comprehensive TypeScript/JavaScript library for orbital mechanics calculations. Originally developed for the KeepTrack satellite tracking application, it provides a complete toolkit for working with satellites, orbital objects, sensors, and celestial mechanics.
What Can OOTK Do?
- Satellite Tracking: Parse TLE data and propagate satellite positions using SGP4 or numerical integrators
- Sensor Operations: Calculate visibility, field-of-view, and predict satellite passes
- Orbit Determination: Determine orbits from observations using multiple IOD methods
- Coordinate Transformations: Convert between ECI, ECF, geodetic, and other coordinate systems
- High-Precision Propagation: Model perturbations including gravity harmonics, third-body effects, solar radiation pressure, and atmospheric drag
- Mission Planning: Calculate orbital maneuvers, transfers, and delta-v requirements
- Time Systems: Handle multiple time systems (UTC, TAI, TT, GPS, TDB) with conversions
Key Features
- Type-Safe: Written in TypeScript with comprehensive type definitions
- Unit Types: Distinct types for Degrees, Radians, Kilometers, Meters, etc.
- Multiple Propagators: SGP4, Kepler, Runge-Kutta (4th, 8/9th order), Dormand-Prince
- Coordinate Systems: J2000, TEME, ITRF, Geodetic, RIC, Hill, and more
- Browser & Node.js: Works in both environments
Core Concepts
Type Safety with Units
OOTK uses TypeScript's type system to enforce unit safety:
import { Degrees, Radians, Kilometers, Meters } from 'ootk';
// Type-safe units prevent mixing incompatible values
const latitude = 41.754785 as Degrees;
const altitude = 0.060966 as Kilometers;
// Compilation error if you try to mix units incorrectly
// const wrong = latitude + altitude; // Error!Available Unit Types:
- Angular:
Degrees,Radians - Distance:
Kilometers,Meters - Time:
Seconds,Minutes,Hours,Days - Velocity:
KilometersPerSecond,MetersPerSecond - Angular Velocity:
RadiansPerSecond
Vector Types
OOTK provides specialized vector types for different coordinate systems:
import { TemeVec3, EcfVec3, LlaVec3, RaeVec3 } from 'ootk';
// ECI (Earth-Centered Inertial) vector
const eciPos: TemeVec3 = { x: 6778.137, y: 0, z: 0 } as TemeVec3<Kilometers>;
// Geodetic coordinates
const lla: LlaVec3 = {
lat: 41.754785 as Degrees,
lon: -70.539151 as Degrees,
alt: 0.060966 as Kilometers
};
// Range, Azimuth, Elevation
const rae: RaeVec3 = {
rng: 1000 as Kilometers,
az: 180 as Degrees,
el: 45 as Degrees
};Enumerations
Type-safe enums for common values:
import { OrbitRegime, SpaceObjectType, Sgp4OpsMode } from 'ootk';
const regime = OrbitRegime.LEO; // LEO, MEO, GEO, VHEO, DEEP
const type = SpaceObjectType.PAYLOAD;
const opsMode = Sgp4OpsMode.AFSPC; // AFSPC or IMPROVEDSatellite Operations
Creating Satellites
From TLE (Two-Line Elements)
import { Satellite, TleLine1, TleLine2 } from 'ootk';
const satellite = new Satellite({
tle1: '1 25544U 98067A 24028.54545847 .00031576 00000-0 57240-3 0 9991' as TleLine1,
tle2: '2 25544 51.6418 292.2590 0002595 167.5319 252.0460 15.49326324436741' as TleLine2
});From Orbital Elements
import { Satellite, ClassicalElements, Degrees, Kilometers } 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
});
const tle = elements.toTLE({
intlDes: '98067A',
epochYear: 24,
epochDay: 28.54545847
});
const satellite = new Satellite({ tle1: tle.line1, tle2: tle.line2 });DetailedSatellite
For additional metadata:
import { DetailedSatellite } from 'ootk';
const satellite = new DetailedSatellite({
tle1: line1,
tle2: line2,
id: 25544,
name: 'ISS (ZARYA)',
country: 'US',
launchDate: new Date('1998-11-20'),
mass: 419700, // kg
shape: 'Composite',
period: 92.91, // minutes
apogee: 422.7, // km
perigee: 418.9 // km
});Getting Satellite Position
ECI (Earth-Centered Inertial)
// Current time
const eci = satellite.eci();
console.log(eci.position); // { x, y, z } in km
console.log(eci.velocity); // { x, y, z } in km/s
// Specific time
const date = new Date('2024-01-28T12:00:00Z');
const eciAtTime = satellite.eci(date);Geodetic (Latitude/Longitude/Altitude)
const lla = satellite.lla();
console.log(lla.lat); // Latitude in degrees
console.log(lla.lon); // Longitude in degrees
console.log(lla.alt); // Altitude in kilometersECF (Earth-Centered Fixed)
const ecf = satellite.ecf();
console.log(ecf.position); // { x, y, z } in Earth-fixed frameOrbital Parameters
Access Keplerian orbital elements:
console.log(satellite.inclination); // Degrees
console.log(satellite.eccentricity); // 0-1
console.log(satellite.period); // Minutes
console.log(satellite.apogee); // Kilometers
console.log(satellite.perigee); // Kilometers
console.log(satellite.semiMajorAxis); // Kilometers
console.log(satellite.semiMinorAxis); // Kilometers
console.log(satellite.rightAscension); // Degrees
console.log(satellite.argOfPerigee); // Degrees
console.log(satellite.meanMotion); // Revolutions per dayCoordinate Conversions
// Convert to J2000 coordinate system
const j2000 = satellite.toJ2000(date);
console.log(j2000.position);
console.log(j2000.velocity);
// Convert to ITRF (Earth-fixed)
const itrf = satellite.toITRF(date);
// Get classical orbital elements
const elements = satellite.getClassicalElements();Satellite Visibility
// Check if satellite is in sunlight
const inSunlight = satellite.isInSunlight(date);
// Get illumination (0 = shadowed, 1 = fully illuminated)
const illumination = satellite.getIllumination(date);Sensor Operations
Creating Sensors
Basic Sensor
import { Sensor, Degrees, Kilometers } from 'ootk';
const sensor = new Sensor({
lat: 41.754785 as Degrees,
lon: -70.539151 as Degrees,
alt: 0.060966 as Kilometers,
minEl: 3 as Degrees, // Minimum elevation angle
maxEl: 85 as Degrees, // Maximum elevation angle
minRng: 0 as Kilometers, // Minimum range
maxRng: 5556 as Kilometers // Maximum range
});DetailedSensor
For ground stations with additional metadata:
import { DetailedSensor, SpaceObjectType } from 'ootk';
const sensor = new DetailedSensor({
lat: 41.754785 as Degrees,
lon: -70.539151 as Degrees,
alt: 0.060966 as Kilometers,
minAz: 347 as Degrees,
maxAz: 227 as Degrees,
minEl: 3 as Degrees,
maxEl: 85 as Degrees,
minRng: 0 as Kilometers,
maxRng: 5556 as Kilometers,
name: 'Cape Cod Space Force Station',
type: SpaceObjectType.PHASED_ARRAY_RADAR,
country: 'United States',
freqBand: 'UHF',
operator: 'USSF'
});RF Sensor
For RF sensors with phased array capabilities:
import { RfSensor } from 'ootk';
const rfSensor = new RfSensor({
lat: 41.754785 as Degrees,
lon: -70.539151 as Degrees,
alt: 0.060966 as Kilometers,
minEl: 3 as Degrees,
maxEl: 85 as Degrees,
beamwidth: 2 as Degrees, // Antenna beamwidth
frequency: 420 // MHz
});Range, Azimuth, Elevation (RAE)
Calculate look angles from sensor to satellite:
const rae = sensor.rae(satellite);
console.log(rae.rng); // Range in kilometers
console.log(rae.az); // Azimuth in degrees (0-360)
console.log(rae.el); // Elevation in degrees (-90 to 90)
console.log(rae.rngRate); // Range rate (km/s)
// At a specific time
const raeAtTime = sensor.rae(satellite, date);Field of View
Check if satellite is visible:
// Check if satellite is in sensor's field of view
const isVisible = sensor.isSatInFov(satellite);
console.log(isVisible); // true or false
// At a specific time
const isVisibleAtTime = sensor.isSatInFov(satellite, date);Pass Predictions
Calculate when satellite will be visible:
// Calculate passes over next 7 days in 30-second intervals
const passes = sensor.calculatePasses(30, satellite, {
startDate: new Date(),
lengthDays: 7
});
passes.forEach(pass => {
console.log('Rise time:', pass.rise);
console.log('Culmination time:', pass.culmination);
console.log('Set time:', pass.set);
console.log('Max elevation:', pass.maxEl);
console.log('Duration:', pass.duration); // seconds
});Sensor Position in ECI
// Get sensor position in J2000 ECI coordinates
const j2000 = sensor.toJ2000();
console.log(j2000.position);
// At a specific time
const j2000AtTime = sensor.toJ2000(date);Coordinate Systems
OOTK supports multiple coordinate systems with easy conversions between them.
Earth-Centered Inertial (ECI)
J2000
Standard inertial frame at epoch J2000.0:
import { J2000, Kilometers, KilometersPerSecond } from 'ootk';
const j2000 = new J2000(
epoch,
{ x: 6778.137, y: 0, z: 0 } as TemeVec3<Kilometers>,
{ x: 0, y: 7.67, z: 0 } as TemeVec3<KilometersPerSecond>
);
// Convert to other frames
const teme = j2000.toTEME();
const itrf = j2000.toITRF();TEME (True Equator Mean Equinox)
Used by SGP4 propagator:
import { TEME } from 'ootk';
const teme = new TEME(epoch, position, velocity);
// Convert to J2000
const j2000 = teme.toJ2000();Earth-Centered Fixed (ECF)
ITRF (International Terrestrial Reference Frame)
Earth-fixed frame that rotates with Earth:
import { ITRF } from 'ootk';
const itrf = new ITRF(epoch, position, velocity);
// Convert to inertial
const j2000 = itrf.toJ2000();Geodetic Coordinates
Latitude, Longitude, Altitude:
import { Geodetic } from 'ootk';
const geodetic = new Geodetic(
41.754785 as Degrees,
-70.539151 as Degrees,
0.060966 as Kilometers
);
// Convert to ECF
const itrf = geodetic.toITRF();
// Convert to ECI
const j2000 = geodetic.toJ2000(epoch);Classical Orbital Elements
import { ClassicalElements } from 'ootk';
const elements = new ClassicalElements({
epoch: epoch,
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
});
// Convert to position/velocity
const pv = elements.toPositionVelocity();
// Generate TLE
const tle = elements.toTLE({
intlDes: '98067A',
epochYear: 24,
epochDay: 28.54545847
});Equinoctial Elements
Non-singular orbital elements:
import { EquinoctialElements } from 'ootk';
const equinoctial = new EquinoctialElements(
epoch,
a, // semi-major axis
h, // h = e * sin(ω + Ω)
k, // k = e * cos(ω + Ω)
p, // p = tan(i/2) * sin(Ω)
q, // q = tan(i/2) * cos(Ω)
λ // mean longitude
);
// Convert to classical elements
const classical = equinoctial.toClassicalElements();Relative Frames
RIC (Radial-In-track-Cross-track)
import { RIC } from 'ootk';
// Create RIC frame relative to a reference satellite
const ric = RIC.fromJ2000(referenceState, targetState);
console.log(ric.position.x); // Radial component
console.log(ric.position.y); // In-track component
console.log(ric.position.z); // Cross-track componentHill Frame
Similar to RIC but with different conventions:
import { Hill } from 'ootk';
const hill = Hill.fromJ2000(referenceState, targetState);TLE (Two-Line Element)
Parse and create TLE data:
import { Tle } from 'ootk';
// Parse existing TLE
const tle = new Tle(line1, line2);
console.log(tle.inclination);
console.log(tle.eccentricity);
console.log(tle.meanMotion);
// Create from classical elements
const newTle = ClassicalElements.toTLE(elements, metadata);Orbit Propagation
SGP4 Propagator
Standard propagator for TLE data:
import { Satellite, Sgp4Propagator, EpochUTC } from 'ootk';
const satellite = new Satellite({ tle1, tle2 });
const propagator = new Sgp4Propagator(satellite);
// Propagate to a specific time
const futureEpoch = EpochUTC.fromDateTime(new Date('2024-12-31'));
const state = propagator.propagate(futureEpoch);
console.log(state.position); // TEME coordinates
console.log(state.velocity);SGP4 Operation Modes:
import { Sgp4OpsMode } from 'ootk';
// AFSPC mode (original)
const propagatorAFSPC = new Sgp4Propagator(satellite, Sgp4OpsMode.AFSPC);
// Improved mode (recommended)
const propagatorImproved = new Sgp4Propagator(satellite, Sgp4OpsMode.IMPROVED);Kepler Propagator
Simple two-body propagation (fastest, least accurate):
import { KeplerPropagator } from 'ootk';
const initialState = satellite.toJ2000();
const propagator = new KeplerPropagator(initialState);
const futureState = propagator.propagate(futureEpoch);Runge-Kutta 4 Propagator
4th-order Runge-Kutta numerical integration:
import { RungeKutta4Propagator, ForceModel } from 'ootk';
const forceModel = new ForceModel();
forceModel.setEarthGravity(4, 4); // 4x4 gravity field
const propagator = new RungeKutta4Propagator(
initialState,
forceModel,
{ stepSize: 60 } // 60 second steps
);
const futureState = propagator.propagate(futureEpoch);Runge-Kutta 89 Propagator
8/9th-order with adaptive step size (high accuracy):
import { RungeKutta89Propagator } from 'ootk';
const propagator = new RungeKutta89Propagator(
initialState,
forceModel,
{
minStepSize: 0.01, // seconds
maxStepSize: 600, // seconds
tolerance: 1e-9 // error tolerance
}
);
const futureState = propagator.propagate(futureEpoch);Dormand-Prince 5(4) Propagator
Adaptive Runge-Kutta method:
import { DormandPrince54Propagator } from 'ootk';
const propagator = new DormandPrince54Propagator(
initialState,
forceModel,
{
minStepSize: 0.01,
maxStepSize: 600,
tolerance: 1e-9
}
);
const futureState = propagator.propagate(futureEpoch);Propagator Comparison
| Propagator | Accuracy | Speed | Use Case |
|---|---|---|---|
| SGP4 | Good | Very Fast | TLE propagation, Earth orbits |
| Kepler | Low | Fastest | Quick estimates, educational |
| RK4 | High | Moderate | General purpose |
| RK89 | Very High | Slower | High-precision requirements |
| Dormand-Prince | High | Moderate | Balanced accuracy/speed |
Force Models
For high-precision propagation, configure perturbation forces:
Creating a Force Model
import { ForceModel } from 'ootk';
const forceModel = new ForceModel();Earth Gravity
Include gravitational harmonics:
// Point mass gravity (fastest)
forceModel.setEarthGravity(0, 0);
// J2 perturbation only
forceModel.setEarthGravity(2, 0);
// 8x8 gravity field (recommended for LEO)
forceModel.setEarthGravity(8, 8);
// 20x20 gravity field (high precision)
forceModel.setEarthGravity(20, 20);Degree/Order Guide:
(0, 0)- Point mass only(2, 0)- J2 oblateness(4, 4)- Basic harmonics(8, 8)- Good for most applications(20, 20)- High precision(70, 70)- Maximum precision (slow)
Third-Body Gravity
Sun and Moon perturbations:
forceModel.setThirdBodyGravity({
sun: true,
moon: true
});Solar Radiation Pressure
// Basic SRP
forceModel.setSolarRadiationPressure(
1000, // mass in kg
10, // cross-sectional area in m²
1.5 // radiation pressure coefficient (typically 1.0-2.0)
);Atmospheric Drag
forceModel.setAtmosphericDrag(
1000, // mass in kg
10, // cross-sectional area in m²
2.2 // drag coefficient (typically 2.0-2.5)
);Thrust
For maneuvers:
import { Thrust, Vector3D } from 'ootk';
const thrust = new Thrust(
new Vector3D(100, 0, 0), // thrust vector in Newtons
startEpoch,
endEpoch
);
forceModel.addThrust(thrust);Complete Example
const forceModel = new ForceModel();
// Configure all perturbations
forceModel.setEarthGravity(20, 20);
forceModel.setThirdBodyGravity({ sun: true, moon: true });
forceModel.setSolarRadiationPressure(500, 15, 1.5);
forceModel.setAtmosphericDrag(500, 15, 2.2);
// Use with propagator
const propagator = new RungeKutta89Propagator(
initialState,
forceModel,
{ tolerance: 1e-12 }
);Initial Orbit Determination
Determine orbits from observations using various IOD methods.
Gibbsmethod
Determine orbit from 3 position vectors:
import { GibbsIOD, Vector3D, EpochUTC } from 'ootk';
const iod = new GibbsIOD();
const r1 = new Vector3D(6778, 0, 0);
const r2 = new Vector3D(0, 6778, 0);
const r3 = new Vector3D(-6778, 0, 0);
const result = iod.estimate(r1, r2, r3);
console.log(result.velocity); // Determined velocity
console.log(result.orbit); // Orbital elementsHerrick-Gibbs IOD
Improved Gibbs method using observation times:
import { HerrickGibbsIOD } from 'ootk';
const iod = new HerrickGibbsIOD();
const result = iod.estimate(
r1, epoch1,
r2, epoch2,
r3, epoch3
);Gooding IOD
Advanced method with better handling of short arcs:
import { GoodingIOD } from 'ootk';
const iod = new GoodingIOD();
const result = iod.estimate(
r1, epoch1,
r2, epoch2,
r3, epoch3
);Modified Gooding IOD
Enhanced Gooding method:
import { ModifiedGoodingIOD } from 'ootk';
const iod = new ModifiedGoodingIOD();
const result = iod.estimate(
r1, epoch1,
r2, epoch2,
r3, epoch3
);Lambert IOD
Solve Lambert's problem (two positions, time of flight):
import { LambertIOD } from 'ootk';
const iod = new LambertIOD();
const result = iod.estimate(
r1, epoch1,
r2, epoch2
);
console.log(result.v1); // Velocity at first position
console.log(result.v2); // Velocity at second positionBatch Least Squares
Fit orbit to multiple observations:
import { BatchLeastSquaresOD, ObservationOptical } from 'ootk';
const observations = [
new ObservationOptical(epoch1, ra1, dec1, sensor1),
new ObservationOptical(epoch2, ra2, dec2, sensor2),
new ObservationOptical(epoch3, ra3, dec3, sensor3),
// ... more observations
];
const iod = new BatchLeastSquaresOD();
const result = iod.estimate(observations, initialGuess);
console.log(result.state); // Final state estimate
console.log(result.covariance); // Uncertainty
console.log(result.residuals); // Observation residualsFrom Sensor Observations
Practical example using sensor angle measurements:
import { Sensor, RAE } from 'ootk';
const sensor = new Sensor({ lat, lon, alt, minEl, maxEl });
// Get three observations
const obs1 = new RAE(epoch1, range1, azimuth1, elevation1);
const obs2 = new RAE(epoch2, range2, azimuth2, elevation2);
const obs3 = new RAE(epoch3, range3, azimuth3, elevation3);
// Convert to position vectors
const r1 = sensor.raeToECI(obs1);
const r2 = sensor.raeToECI(obs2);
const r3 = sensor.raeToECI(obs3);
// Run IOD
const iod = new GoodingIOD();
const orbit = iod.estimate(r1, epoch1, r2, epoch2, r3, epoch3);Observations
Range, Azimuth, Elevation (RAE)
Radar or optical tracking station measurements:
import { RAE, Degrees, Kilometers } from 'ootk';
const observation = new RAE(
epoch,
1000 as Kilometers, // range
180 as Degrees, // azimuth (0-360)
45 as Degrees, // elevation (-90 to 90)
0.5 as KilometersPerSecond // range rate (optional)
);
// Convert to topocentric coordinates
const topocentric = observation.toTopocentric(sensor);
// Convert to ECI
const eci = observation.toECI(sensor);Right Ascension / Declination
Optical observations in celestial coordinates:
Geocentric
import { RadecGeocentric } from 'ootk';
const observation = new RadecGeocentric(
epoch,
15.5 as Degrees, // right ascension (0-360)
45.2 as Degrees, // declination (-90 to 90)
1000 as Kilometers // range (optional)
);Topocentric
From a ground station:
import { RadecTopocentric } from 'ootk';
const observation = new RadecTopocentric(
epoch,
ra,
dec,
sensor // observer location
);
// Convert to geocentric
const geocentric = observation.toGeocentric();Radar Observations
import { ObservationRadar } from 'ootk';
const observation = new ObservationRadar(
epoch,
range,
azimuth,
elevation,
rangeRate,
sensor
);Optical Observations
import { ObservationOptical } from 'ootk';
const observation = new ObservationOptical(
epoch,
ra,
dec,
sensor
);Time Systems
Epoch Types
OOTK supports multiple time systems:
import {
EpochUTC,
EpochTAI,
EpochTT,
EpochGPS,
EpochTDB
} from 'ootk';
// UTC (Coordinated Universal Time)
const utc = EpochUTC.fromDateTime(new Date('2024-01-28T12:00:00Z'));
// TAI (International Atomic Time)
const tai = EpochTAI.fromDateTime(new Date('2024-01-28T12:00:00Z'));
// TT (Terrestrial Time)
const tt = EpochTT.fromDateTime(new Date('2024-01-28T12:00:00Z'));
// GPS Time
const gps = EpochGPS.fromDateTime(new Date('2024-01-28T12:00:00Z'));
// TDB (Barycentric Dynamical Time)
const tdb = EpochTDB.fromDateTime(new Date('2024-01-28T12:00:00Z'));Creating Epochs
From JavaScript Date
const epoch = EpochUTC.fromDateTime(new Date());From Julian Date
const epoch = EpochUTC.fromJulianDate(2451545.0);From Modified Julian Date
const epoch = EpochUTC.fromMJD(51544.5);From Components
const epoch = EpochUTC.fromDateTimeComponents(
2024, // year
1, // month
28, // day
12, // hour
0, // minute
0 // second
);Time Conversions
const utc = EpochUTC.fromDateTime(new Date());
// Convert between time systems
const tai = utc.toTAI();
const tt = utc.toTT();
const gps = utc.toGPS();
const tdb = utc.toTDB();
// Get Julian dates
console.log(utc.toJulianDate());
console.log(utc.toMJD());
// Get as JavaScript Date
console.log(utc.toDateTime());Time Windows
Define time intervals:
import { EpochWindow } from 'ootk';
const window = new EpochWindow(startEpoch, endEpoch);
// Check if epoch is in window
const isInWindow = window.contains(testEpoch);
// Get duration
const durationSeconds = window.duration();Time Arithmetic
// Add seconds
const future = epoch.addSeconds(3600); // +1 hour
// Add days
const tomorrow = epoch.addDays(1);
// Difference between epochs
const deltaSeconds = epoch2.difference(epoch1);
// Compare epochs
const isAfter = epoch2.isAfter(epoch1);
const isBefore = epoch2.isBefore(epoch1);Greenwich Mean Sidereal Time
import { EpochUTC } from 'ootk';
const epoch = EpochUTC.fromDateTime(new Date());
// Get GMST in radians
const gmst = epoch.gmst();
// Get GMST in degrees
const gmstDeg = epoch.gmstDegrees();Interpolation
Interpolate state vectors for smooth trajectories.
Chebyshev Interpolation
Efficient for smooth functions:
import { ChebyshevInterpolator, EpochUTC } from 'ootk';
// Generate sample states
const states = [];
for (let i = 0; i < 10; i++) {
const epoch = startEpoch.addSeconds(i * 60);
const state = propagator.propagate(epoch);
states.push({ epoch, state });
}
// Create interpolator
const interpolator = new ChebyshevInterpolator(states, 8); // 8th order
// Interpolate at arbitrary time
const interpEpoch = startEpoch.addSeconds(135); // Between samples
const interpState = interpolator.interpolate(interpEpoch);Lagrange Interpolation
Classic polynomial interpolation:
import { LagrangeInterpolator } from 'ootk';
const interpolator = new LagrangeInterpolator(states, 5); // 5th order
const interpState = interpolator.interpolate(interpEpoch);Cubic Spline Interpolation
Smooth curves through points:
import { CubicSplineInterpolator } from 'ootk';
const interpolator = new CubicSplineInterpolator(states);
const interpState = interpolator.interpolate(interpEpoch);Verlet Blend Interpolation
Optimized for orbital mechanics:
import { VerletBlendInterpolator } from 'ootk';
const interpolator = new VerletBlendInterpolator(states);
const interpState = interpolator.interpolate(interpEpoch);State Interpolator
Generic state interpolation:
import { StateInterpolator } from 'ootk';
const interpolator = new StateInterpolator(states, 'chebyshev');
const interpState = interpolator.interpolate(interpEpoch);Compression
Reduce ephemeris data size:
import { ChebyshevCompressor } from 'ootk';
// Generate dense ephemeris
const ephemeris = [];
for (let i = 0; i < 1000; i++) {
ephemeris.push(propagator.propagate(epoch.addSeconds(i * 10)));
}
// Compress to coefficients
const compressor = new ChebyshevCompressor(ephemeris, 12); // 12th order
const coefficients = compressor.compress();
// Reconstruct with interpolation
const reconstructed = compressor.evaluate(testEpoch);Mathematical Operations
Vector3D
3D vector operations:
import { Vector3D } from 'ootk';
const v1 = new Vector3D(1, 2, 3);
const v2 = new Vector3D(4, 5, 6);
// Basic operations
const sum = v1.add(v2);
const diff = v1.subtract(v2);
const scaled = v1.scale(2.5);
// Dot product
const dot = v1.dot(v2);
// Cross product
const cross = v1.cross(v2);
// Magnitude
const mag = v1.magnitude();
// Normalize
const unit = v1.normalize();
// Distance
const dist = v1.distance(v2);
// Angle between vectors
const angle = v1.angle(v2); // radiansVector
N-dimensional vectors:
import { Vector } from 'ootk';
const v = new Vector([1, 2, 3, 4, 5]);
// Operations
const doubled = v.scale(2);
const sum = v.add(new Vector([1, 1, 1, 1, 1]));
// Norms
const norm2 = v.norm(); // L2 norm
const norm1 = v.norm(1); // L1 norm
const normInf = v.norm(Infinity); // L-infinity normMatrix
Matrix operations:
import { Matrix } from 'ootk';
const m = new Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
// Multiply matrices
const product = m.multiply(otherMatrix);
// Multiply by vector
const result = m.multiplyVector(vector);
// Transpose
const transposed = m.transpose();
// Determinant
const det = m.determinant();
// Inverse
const inverse = m.inverse();
// Identity matrix
const identity = Matrix.identity(3);
// Zero matrix
const zeros = Matrix.zeros(3, 3);Quaternion
Rotation representation:
import { Quaternion, Vector3D } from 'ootk';
// Create from axis-angle
const axis = new Vector3D(0, 0, 1);
const angle = Math.PI / 4; // 45 degrees
const q = Quaternion.fromAxisAngle(axis, angle);
// Quaternion operations
const product = q1.multiply(q2);
const conjugate = q.conjugate();
const inverse = q.inverse();
// Rotate vector
const rotated = q.rotateVector(vector);
// Convert to rotation matrix
const rotMatrix = q.toRotationMatrix();
// Interpolation (SLERP)
const interpolated = Quaternion.slerp(q1, q2, 0.5); // 50% betweenEuler Angles
import { EulerAngles } from 'ootk';
const euler = new EulerAngles(
0.1, // roll (radians)
0.2, // pitch
0.3 // yaw
);
// Convert to quaternion
const quat = euler.toQuaternion();
// Convert to rotation matrix
const matrix = euler.toRotationMatrix();Random Number Generation
import { Random } from 'ootk';
// Uniform random in [0, 1)
const r = Random.uniform();
// Uniform in range
const r2 = Random.uniformRange(-1, 1);
// Gaussian (normal) distribution
const gaussian = Random.gaussian(0, 1); // mean=0, stddev=1
// Random unit vector
const unitVec = Random.unitVector3D();Box-Muller Transform
Gaussian random numbers:
import { BoxMuller } from 'ootk';
const generator = new BoxMuller();
// Generate pair of independent Gaussian random numbers
const [z1, z2] = generator.generate();Optimization
Golden Section Search
1D optimization:
import { GoldenSection } from 'ootk';
// Find minimum of function
const optimizer = new GoldenSection(
func, // function to minimize
lowerBound, // search range start
upperBound, // search range end
tolerance // convergence tolerance
);
const result = optimizer.optimize();
console.log(result.x); // Optimal value
console.log(result.fval); // Function value at optimum
console.log(result.iterations);Downhill Simplex (Nelder-Mead)
Multi-dimensional optimization:
import { DownhillSimplex } from 'ootk';
// Minimize function of multiple variables
const optimizer = new DownhillSimplex(
func, // function to minimize
initialGuess, // starting point
{
tolerance: 1e-6,
maxIterations: 1000
}
);
const result = optimizer.optimize();
console.log(result.x); // Optimal parameters
console.log(result.fval); // Final function value
console.log(result.converged);Polynomial Regression
Fit polynomial to data:
import { PolynomialRegression } from 'ootk';
const x = [1, 2, 3, 4, 5];
const y = [2.1, 3.9, 6.2, 8.1, 9.9];
// Fit 2nd degree polynomial
const regression = new PolynomialRegression(x, y, 2);
// Get coefficients
const coeffs = regression.getCoefficients();
// Predict
const yPred = regression.predict(3.5);
// R-squared
const r2 = regression.rSquared();Simple Linear Regression
import { SimpleLinearRegression } from 'ootk';
const regression = new SimpleLinearRegression(x, y);
console.log(regression.slope);
console.log(regression.intercept);
console.log(regression.rSquared);
const predicted = regression.predict(newX);Covariance
Track state uncertainty for orbit determination and propagation.
State Covariance
import { StateCovariance, Matrix } from 'ootk';
// Create 6x6 covariance matrix (position + velocity)
const covMatrix = new Matrix([
[100, 0, 0, 0, 0, 0], // σ²_x
[0, 100, 0, 0, 0, 0], // σ²_y
[0, 0, 100, 0, 0, 0], // σ²_z
[0, 0, 0, 0.01, 0, 0], // σ²_vx
[0, 0, 0, 0, 0.01, 0], // σ²_vy
[0, 0, 0, 0, 0, 0.01] // σ²_vz
]);
const covariance = new StateCovariance(epoch, state, covMatrix);
// Propagate covariance
const futureCov = covariance.propagate(futureEpoch, propagator);
// Get position uncertainty
const posUncertainty = covariance.getPositionUncertainty(); // km
// Get velocity uncertainty
const velUncertainty = covariance.getVelocityUncertainty(); // km/s
// Get standard deviations
const stdDevs = covariance.getStandardDeviations();
console.log('X uncertainty:', stdDevs.position.x, 'km');Covariance from TLE
Estimate uncertainty from TLE:
import { Satellite } from 'ootk';
const satellite = new Satellite({ tle1, tle2 });
const covariance = satellite.getCovariance();Covariance Sampling
Generate samples from distribution:
import { CovarianceSample } from 'ootk';
const samples = CovarianceSample.generate(
state,
covariance,
1000 // number of samples
);
// Use samples for Monte Carlo analysis
samples.forEach(sample => {
const propagated = propagator.propagate(futureEpoch);
// ... analyze
});Celestial Bodies
Earth
Earth properties and models:
import { Earth } from 'ootk';
// Physical constants
console.log(Earth.radiusEquator); // km
console.log(Earth.radiusPolar); // km
console.log(Earth.radiusMean); // km
console.log(Earth.flattening);
console.log(Earth.mu); // Gravitational parameter
console.log(Earth.angularVelocity); // rad/s
// Gravitational harmonics
console.log(Earth.j2);
console.log(Earth.j3);
console.log(Earth.j4);
console.log(Earth.j5);
// Precession and nutation
const precession = Earth.precession(epoch);
const nutation = Earth.nutation(epoch);Moon
Lunar position and properties:
import { Moon } from 'ootk';
// Moon position in ECI
const moonPos = Moon.position(epoch);
console.log(moonPos); // { x, y, z } in km
// Moon velocity
const moonVel = Moon.velocity(epoch);
// Illumination angles
const illum = Moon.illumination(epoch, observerPos);
console.log(illum.phase); // 0-1 (0=new, 0.5=full)
console.log(illum.angle); // Phase angle
console.log(illum.fraction); // Illuminated fraction
// Physical constants
console.log(Moon.radius);
console.log(Moon.mu);Sun
Solar position and calculations:
import { Sun } from 'ootk';
// Sun position in ECI
const sunPos = Sun.position(epoch);
console.log(sunPos); // { x, y, z } in km
// Sun velocity
const sunVel = Sun.velocity(epoch);
// Check if position is in Earth's shadow
const inShadow = Sun.isInEarthShadow(satellitePos, epoch);
// Eclipse calculations
const eclipse = Sun.getEclipse(satellitePos, epoch);
console.log(eclipse.umbra); // In umbra (total shadow)
console.log(eclipse.penumbra); // In penumbra (partial shadow)
console.log(eclipse.sunlit); // In sunlight
// Solar radiation pressure magnitude
const srp = Sun.radiationPressure(
area, // m²
mass, // kg
coeff // radiation coefficient
);Maneuvers
Plan and execute orbital maneuvers.
Hohmann Transfer
Classic two-burn transfer:
import { TwoBurnOrbitTransfer, Kilometers } from 'ootk';
// Transfer from LEO to GEO
const r1 = 6778 as Kilometers; // LEO radius
const r2 = 42164 as Kilometers; // GEO radius
const transfer = TwoBurnOrbitTransfer.hohmannTransfer(r1, r2);
console.log(transfer.deltaV1); // First burn Δv (km/s)
console.log(transfer.deltaV2); // Second burn Δv
console.log(transfer.totalDeltaV); // Total Δv required
console.log(transfer.transferTime); // Transfer duration (seconds)
// Convert to maneuver objects
const maneuvers = transfer.toManeuvers(startEpoch);Bi-elliptic Transfer
Three-burn transfer (sometimes more efficient):
const transfer = TwoBurnOrbitTransfer.biellipticTransfer(
r1, // Initial radius
r2, // Final radius
rb // Apoapsis of first transfer ellipse
);Maneuver Object
Individual maneuver:
import { Maneuver, Vector3D } from 'ootk';
const maneuver = new Maneuver({
epoch: burnEpoch,
deltaV: new Vector3D(0.5, 0.1, 0), // Δv vector in km/s
duration: 60, // Burn duration in seconds
direction: 'prograde' // or 'retrograde', 'normal', 'radial'
});
// Apply to state
const newState = maneuver.apply(currentState);Plane Change
import { planeChange } from 'ootk';
// Calculate Δv for plane change
const deltaV = planeChange(
velocity, // Current velocity (km/s)
angleChange // Angle to change (radians)
);Inclination Change
const deltaV = inclinationChange(
velocity,
currentInclination,
targetInclination
);Advanced Topics
Custom Propagators
Create custom propagator:
import { Propagator, J2000, EpochUTC } from 'ootk';
class MyPropagator extends Propagator {
propagate(epoch: EpochUTC): J2000 {
// Your custom propagation logic
return new J2000(epoch, position, velocity);
}
}Gravity Field Models
Use custom gravity coefficients:
import { EarthGravity } from 'ootk';
const gravity = new EarthGravity(20, 20); // 20x20 field
// Set custom coefficients
gravity.setCoefficient(2, 0, customJ2);
gravity.setCoefficient(3, 0, customJ3);Ephemeris Generation
Generate ephemeris table:
const startEpoch = EpochUTC.fromDateTime(new Date());
const ephemeris = [];
for (let i = 0; i < 1440; i++) { // 24 hours, 1-minute intervals
const epoch = startEpoch.addMinutes(i);
const state = propagator.propagate(epoch);
ephemeris.push({
epoch: epoch.toDateTime(),
position: state.position,
velocity: state.velocity,
lla: state.toGeodetic()
});
}
// Save or use ephemerisSensor Network
Multiple sensor visibility:
const sensors = [sensor1, sensor2, sensor3];
const visibility = sensors.map(sensor => ({
sensor: sensor.name,
visible: sensor.isSatInFov(satellite),
rae: sensor.rae(satellite)
}));
// Find which sensors can see satellite
const visibleFrom = visibility.filter(v => v.visible);Collision Detection
Check for close approaches:
// Propagate both objects
const state1 = satellite1.toJ2000(epoch);
const state2 = satellite2.toJ2000(epoch);
// Calculate distance
const distance = state1.position.distance(state2.position);
if (distance < threshold) {
console.warn('Close approach detected!');
console.log('Distance:', distance, 'km');
console.log('Relative velocity:',
state1.velocity.subtract(state2.velocity).magnitude());
}Catalog Management
Work with satellite catalogs:
import { Satellite } from 'ootk';
const catalog = [];
// Load TLEs
tleData.forEach(tle => {
const sat = new Satellite({
tle1: tle.line1,
tle2: tle.line2
});
catalog.push(sat);
});
// Find satellites by criteria
const leoSats = catalog.filter(sat =>
sat.apogee < 2000 && sat.perigee > 200
);
const highInclination = catalog.filter(sat =>
sat.inclination > 80
);State Vector Manipulation
import { StateVector } from 'ootk';
const state = new StateVector(epoch, position, velocity);
// Transform coordinate systems
const j2000 = state.toJ2000();
const itrf = state.toITRF();
// Get orbital elements
const elements = state.toClassicalElements();
// Add relative motion
const ricDelta = { x: 10, y: 0, z: 0 }; // 10 km radial
const newState = state.addRIC(ricDelta);Performance Optimization
Tips for optimal performance:
// 1. Reuse propagators
const propagator = new Sgp4Propagator(satellite);
for (let i = 0; i < 1000; i++) {
const state = propagator.propagate(epochs[i]); // Fast
}
// 2. Batch operations
const states = epochs.map(epoch => propagator.propagate(epoch));
// 3. Use appropriate propagator
// - SGP4 for TLE data (fastest)
// - Kepler for quick estimates
// - RK4 for balanced precision/speed
// - RK89 only when high precision needed
// 4. Reduce gravity field degree for distant objects
const forceModel = distance > 10000
? new ForceModel().setEarthGravity(4, 4)
: new ForceModel().setEarthGravity(20, 20);
// 5. Cache calculations
const cached = new Map();
function getState(satellite, epoch) {
const key = `${satellite.id}-${epoch.toJulianDate()}`;
if (!cached.has(key)) {
cached.set(key, satellite.eci(epoch.toDateTime()));
}
return cached.get(key);
}Appendix: Quick Reference
Common Conversions
import { DEG2RAD, RAD2DEG, MINUTES_PER_DAY } from 'ootk';
const radians = degrees * DEG2RAD;
const degrees = radians * RAD2DEG;
// Mean motion to period
const period = MINUTES_PER_DAY / meanMotion;
// Period to semi-major axis
const a = Math.cbrt((Earth.mu * (period * 60)**2) / (4 * Math.PI**2));Useful Constants
import { Earth } from 'ootk';
Earth.radiusEquator; // 6378.137 km
Earth.radiusMean; // 6371.0 km
Earth.mu; // 398600.4418 km³/s²
Earth.j2; // 0.00108263
Earth.angularVelocity; // 7.2921159e-5 rad/sType Casting
// Always cast numeric literals to appropriate types
const lat = 41.754785 as Degrees;
const alt = 400 as Kilometers;
const vel = 7.8 as KilometersPerSecond;Support and Resources
- GitHub: https://github.com/thkruz/ootk
- Issues: https://github.com/thkruz/ootk/issues
- NPM: https://www.npmjs.com/package/ootk
- Examples:
/examplesdirectory in repository
For questions or contributions, please open an issue on GitHub.
License: AGPL-3.0
Author: Theodore Kruczek
Version: 5.1.1