Packages
sidereon
2.1.0
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.1.0
2.0.0
1.4.1
1.3.3
1.3.1
1.3.0
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
0.39.1
0.39.0
0.38.0
0.37.0
0.36.5
0.36.4
0.36.3
0.36.2
0.36.1
0.36.0
0.35.0
0.34.0
0.33.1
0.32.0
0.31.2
0.30.0
0.29.2
0.29.1
0.29.0
0.28.1
0.28.0
0.27.1
0.27.0
0.26.1
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.0
0.16.1
0.15.0
0.14.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.0
retired
Satellite toolkit for Elixir with SGP4 propagation, coordinate transforms, GNSS positioning, orbit determination, conjunction assessment, pass prediction, and a Rust NIF backend.
Current section
Files
Jump to
Current section
Files
examples/conjunction_alert.livemd
# Conjunction Risk Assessment
```elixir
Mix.install([
{:sidereon, "~> 2.1"}
])
```
## 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.