Conjunction Assessment
This example runs the ConjunctionAssessment workflow between two space objects: it finds the time of closest approach (TCA) inside a search window, reports the miss distance in RIC components, and computes probability of collision from either TLE-derived or user-supplied covariance. Use it for close-approach screening and risk evaluation.
import {
ConjunctionAssessment,
CovarianceFrame,
EpochUTC,
ForceModel,
Kilometers,
RungeKutta89Propagator,
StateCovariance,
Tle,
} from 'ootk';Run it
npm run build
npx tsx ./examples/conjunction-assessment-example.tsScenario setup
Two fabricated TLEs place the objects in nearly identical LEO orbits so a close approach exists inside the 6-hour search window. The hard body radii are required for any probability-of-collision calculation; without radii on both objects the assessment only reports geometry.
// Sample TLE data for two satellites in close proximity
const primaryTle = new Tle(
'1 25544U 98067A 25019.50000000 .00016717 00000-0 10270-3 0 9005',
'2 25544 51.6400 339.8000 0002571 90.5000 269.6000 15.50000000000000',
);
const secondaryTle = new Tle(
'1 44691U 19074A 25019.50000000 .00016500 00000-0 10200-3 0 9006',
'2 44691 51.6450 339.8050 0002600 90.5050 269.6050 15.50005000000000',
);
// Search window (6 hours, starting half a day after the TLE epoch)
const startTime = EpochUTC.fromDateTimeString('2025-01-19T12:00:00.000Z');
const endTime = EpochUTC.fromDateTimeString('2025-01-19T18:00:00.000Z');
// Hard body radii used for probability of collision
const primaryRadius = 0.05 as Kilometers; // 50 meters
const secondaryRadius = 0.01 as Kilometers; // 10 metersBasic SGP4 assessment
With plain TLE inputs, assess() propagates both objects with SGP4, does a coarse 60-second sweep of the window, then refines the TCA with a golden-section search. The returned ConjunctionEvent carries the TCA, total and per-axis (radial, intrack, crosstrack) miss distances, and relative velocity; isHighRisk() applies miss-distance and Pc thresholds.
console.log('=== Example 1: Basic Conjunction Assessment ===\n');
// With TLE inputs and no extra options, both objects are propagated with SGP4
const basicAssessment = new ConjunctionAssessment(
{ name: 'ISS (Zarya)', tle: primaryTle, radius: primaryRadius },
{ name: 'Secondary Object', tle: secondaryTle, radius: secondaryRadius },
);
const basicEvent = basicAssessment.assess({ startTime, endTime });
console.log(basicEvent.toString());
console.log(`\nHigh Risk: ${basicEvent.isHighRisk(1.0 as Kilometers)}`);High fidelity with covariance
For higher fidelity, seed a RungeKutta89Propagator from the SGP4 state at the window start and pass it via the propagator override on each object input. The ForceModel here uses 8x8 spherical harmonic gravity plus atmospheric drag and solar radiation pressure (both take spacecraft mass in kg and area in m^2; the drag model is still marked work-in-progress in the library). propagateCovariance: true builds sigma-point covariance samples from each TLE, scales them by TLE quality and age, and propagates them to TCA to produce a combined covariance and Pc.
console.log('\n=== Example 2: High-Fidelity Assessment with Covariance ===\n');
// Build a force model with 8x8 spherical harmonic gravity plus drag and
// solar radiation pressure (both need spacecraft mass in kg and area in m^2)
const forceModel = new ForceModel();
forceModel.setEarthGravity(8, 8);
forceModel.setAtmosphericDrag(460000, 1500);
forceModel.setSolarRadiationPressure(460000, 2500);
// Seed each numerical propagator with the SGP4 state at the window start,
// then hand the propagators to the assessment via the propagator override.
const primaryJ2000 = primaryTle.propagate(startTime).toJ2000();
const secondaryJ2000 = secondaryTle.propagate(startTime).toJ2000();
const hifiAssessment = new ConjunctionAssessment(
{
name: 'ISS (Zarya)',
tle: primaryTle,
radius: primaryRadius,
propagator: new RungeKutta89Propagator(primaryJ2000, forceModel),
},
{
name: 'Secondary Object',
tle: secondaryTle,
radius: secondaryRadius,
propagator: new RungeKutta89Propagator(secondaryJ2000, forceModel),
},
);
const hifiEvent = hifiAssessment.assess({
startTime,
endTime,
forceModel,
propagateCovariance: true, // Propagate TLE-based covariances via sigma points
});
console.log(hifiEvent.toString());
if (hifiEvent.probabilityOfCollision !== undefined) {
console.log(`\nProbability of Collision: ${hifiEvent.probabilityOfCollision.toExponential(6)}`);
}
const mahalanobis = hifiEvent.getMahalanobisDistance();
if (mahalanobis !== undefined) {
console.log(`Mahalanobis Distance: ${mahalanobis.toFixed(3)} sigma`);
}Custom covariance
If you already have covariance data (for example from an OD process or a CDM), supply StateCovariance.fromSigmas() matrices on the object inputs instead. RIC-frame covariances are used as-is at TCA; ECI-frame covariances are rotated into RIC automatically.
console.log('\n=== Example 3: Custom Covariance Matrices ===\n');
/*
* Define custom covariances (1-sigma values in RIC frame)
* [radial, intrack, crosstrack, radial_vel, intrack_vel, crosstrack_vel]
*/
const primaryCovariance = StateCovariance.fromSigmas(
[
0.5, // 500 m radial uncertainty
1.5, // 1.5 km intrack uncertainty
0.5, // 500 m crosstrack uncertainty
0.001, // 1 m/s radial velocity uncertainty
0.003, // 3 m/s intrack velocity uncertainty
0.001, // 1 m/s crosstrack velocity uncertainty
],
CovarianceFrame.RIC,
);
const secondaryCovariance = StateCovariance.fromSigmas(
[0.3, 1.0, 0.3, 0.0005, 0.002, 0.0005],
CovarianceFrame.RIC,
);
const customAssessment = new ConjunctionAssessment(
{
name: 'ISS (Zarya)',
tle: primaryTle,
covariance: primaryCovariance,
radius: primaryRadius,
},
{
name: 'Secondary Object',
tle: secondaryTle,
covariance: secondaryCovariance,
radius: secondaryRadius,
},
);
const customEvent = customAssessment.assess({ startTime, endTime });
console.log(customEvent.toString());Multi object screening
Screening loops one primary against a list of candidate TLEs, then filters events by miss distance and flags any with Pc above a reporting threshold. For large candidate sets the library also provides CatalogScreener with orbital-shell prefilters.
console.log('\n=== Example 4: Multi-Object Screening ===\n');
// List of potential conjunction objects
const secondaryTles = [
secondaryTle,
new Tle(
'1 12345U 81001A 25019.50000000 .00016400 00000-0 10100-3 0 9007',
'2 12345 51.6500 339.8100 0002650 90.5100 269.6100 15.50010000000000',
),
];
const screeningThreshold = 5.0 as Kilometers; // 5 km screening threshold
const pcThreshold = 1e-6; // Pc > 1e-6 is concerning
console.log(`Screening ${secondaryTles.length} objects for conjunctions...\n`);
secondaryTles.forEach((tle, index) => {
const screeningAssessment = new ConjunctionAssessment(
{ tle: primaryTle, radius: primaryRadius },
{ tle, radius: secondaryRadius },
);
const event = screeningAssessment.assess({
startTime,
endTime,
propagateCovariance: true,
});
if (event.missDistance < screeningThreshold) {
console.log(`Object ${index + 1}: CLOSE APPROACH DETECTED`);
console.log(` TCA: ${event.tca.toString()}`);
console.log(` Miss Distance: ${event.missDistance.toFixed(3)} km`);
if (event.probabilityOfCollision !== undefined && event.probabilityOfCollision > pcThreshold) {
console.log(` Pc: ${event.probabilityOfCollision.toExponential(3)} [HIGH RISK]`);
} else if (event.probabilityOfCollision !== undefined) {
console.log(` Pc: ${event.probabilityOfCollision.toExponential(3)}`);
}
console.log();
} else {
console.log(`Object ${index + 1}: no close approach inside ${screeningThreshold} km`);
console.log(` Miss Distance: ${event.missDistance.toFixed(3)} km\n`);
}
});
console.log('=== All Examples Complete ===');Output
The covariance sampling scales with TLE age relative to the current system date, so probability-of-collision values will differ per run.
=== Example 1: Basic Conjunction Assessment ===
[Conjunction Event]
TCA: 2025-01-19T12:08:10.081Z
Miss Distance: 1.533883 km
Radial: 0.026031 km
Intrack: 1.531291 km
Crosstrack: 0.085245 km
Relative Velocity: 0.001941 km/s
Combined Hard Body Radius: 0.060 km
High Risk: false
=== Example 2: High-Fidelity Assessment with Covariance ===
[Conjunction Event]
TCA: 2025-01-19T12:08:06.773Z
Miss Distance: 1.533903 km
Radial: 0.025963 km
Intrack: 1.531154 km
Crosstrack: 0.088044 km
Relative Velocity: 0.001941 km/s
Probability of Collision: 1.319935e-3
Combined Hard Body Radius: 0.060 km
Probability of Collision: 1.319935e-3
Mahalanobis Distance: 0.000 sigma
=== Example 3: Custom Covariance Matrices ===
[Conjunction Event]
TCA: 2025-01-19T12:08:10.081Z
Miss Distance: 1.533883 km
Radial: 0.026031 km
Intrack: 1.531291 km
Crosstrack: 0.085245 km
Relative Velocity: 0.001941 km/s
Probability of Collision: 1.578144e-3
Combined Hard Body Radius: 0.060 km
=== Example 4: Multi-Object Screening ===
Screening 2 objects for conjunctions...
Object 1: CLOSE APPROACH DETECTED
TCA: 2025-01-19T12:08:10.081Z
Miss Distance: 1.534 km
Pc: 1.324e-3 [HIGH RISK]
Object 2: CLOSE APPROACH DETECTED
TCA: 2025-01-19T12:07:54.425Z
Miss Distance: 3.043 km
Pc: 3.370e-4 [HIGH RISK]
=== All Examples Complete ===