# 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.
