Current section
Files
Jump to
Current section
Files
examples/conjunction_alert.livemd
# Conjunction Risk Assessment
```elixir
Mix.install([
{:sidereon, "~> 0.9"}
])
```
## Introduction
This notebook shows how to assess the risk of a satellite conjunction using the
CCSDS Conjunction Data Message (CDM) standard: parse a CDM, convert it to
collision parameters, and compute the probability of collision (Pc).
## Load a CDM fixture
We use a sample CDM in KVN (Keyword = Value Notation) format.
```elixir
cdm_kvn = """
CCSDS_CDM_VERS = 1.0
CREATION_DATE = 2010-03-12T22:31:12.000
ORIGINATOR = JSPOC
MESSAGE_ID = 201113719185
TCA = 2010-03-13T22:37:52.618
MISS_DISTANCE = 715 [m]
RELATIVE_SPEED = 14762 [m/s]
COLLISION_PROBABILITY = 4.835E-05
COLLISION_PROBABILITY_METHOD = FOSTER-1992
OBJECT = OBJECT1
OBJECT_DESIGNATOR = 12345
OBJECT_NAME = SATELLITE A
REF_FRAME = EME2000
X = 2570.097065 [km]
Y = 2244.654904 [km]
Z = 6281.497978 [km]
X_DOT = 4.418769571 [km/s]
Y_DOT = 4.833547743 [km/s]
Z_DOT = -3.526774282 [km/s]
CR_R = 4.142E+01 [m**2]
CT_R = -8.579E+00 [m**2]
CT_T = 2.533E+03 [m**2]
CN_R = -2.313E+01 [m**2]
CN_T = 1.336E+01 [m**2]
CN_N = 7.098E+01 [m**2]
OBJECT = OBJECT2
OBJECT_DESIGNATOR = 30337
OBJECT_NAME = FENGYUN 1C DEB
REF_FRAME = EME2000
X = 2569.540800 [km]
Y = 2245.093614 [km]
Z = 6281.599946 [km]
X_DOT = -2.888612500 [km/s]
Y_DOT = -6.007247516 [km/s]
Z_DOT = 3.328770172 [km/s]
CR_R = 1.337E+03 [m**2]
CT_R = -4.806E+04 [m**2]
CT_T = 2.492E+06 [m**2]
CN_R = -3.298E+01 [m**2]
CN_T = -7.5888E+02 [m**2]
CN_N = 7.105E+01 [m**2]
"""
{:ok, cdm} = Sidereon.CCSDS.CDM.parse(cdm_kvn)
```
## Assess the Risk
Convert the CDM to collision parameters and compute Pc with both Foster methods.
```elixir
# Assume a combined hard-body radius of 20 meters.
cdm_with_hbr = %{cdm | hard_body_radius_m: 20.0}
params = Sidereon.CCSDS.CDM.to_collision_params(cdm_with_hbr)
# Compute Pc using both methods.
{:ok, res_ea} = Sidereon.Collision.probability(params, method: :equal_area)
{:ok, res_num} = Sidereon.Collision.probability(params, method: :numerical)
IO.puts("Miss distance: #{Float.round(res_ea.miss_km, 4)} km")
IO.puts("Equal-area Pc: #{res_ea.pc}")
IO.puts("Numerical Pc: #{res_num.pc}")
```
## Screen two orbits over a window
`Sidereon.Conjunction.find_tca_conjunctions/8` propagates two TLEs over a time
window, finds the time of closest approach (TCA), and computes the miss distance,
relative speed, and Pc at that point. The window is given as Julian dates split
into a whole part and a fractional part. We build them from a UTC instant.
```elixir
# Two real ISS-era element sets, screened over a one-day window.
primary = {
"1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9009",
"2 25544 51.6400 208.8657 0002644 250.3037 109.7782 15.49560812999990"
}
secondary = {
"1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9009",
"2 25544 51.6400 208.8657 0002644 250.3037 109.7782 15.49560820000000"
}
# Julian date for the UTC window bounds, as {whole, fraction} tuples.
to_jd = fn dt ->
jd = DateTime.to_unix(dt) / 86_400.0 + 2_440_587.5
whole = Float.floor(jd)
{whole, jd - whole}
end
window_start = to_jd.(~U[2024-01-01 00:00:00Z])
window_end = to_jd.(~U[2024-01-02 00:00:00Z])
{p1, p2} = primary
{s1, s2} = secondary
# Combined hard-body radius of 20 meters (0.02 km).
{:ok, conjunctions} =
Sidereon.Conjunction.find_tca_conjunctions(p1, p2, s1, s2, window_start, window_end, 0.02)
for c <- conjunctions do
%{
seconds_into_window: Float.round(c.candidate.tca_seconds_since_window_start, 1),
miss_km: Float.round(c.miss_km, 6),
relative_speed_km_s: Float.round(c.relative_speed_km_s, 6),
pc: c.pc
}
end
```
`Sidereon.Conjunction.screen_tca_conjunctions/7` extends this to screen one
primary against a whole catalog of secondaries by a miss-distance threshold.