odm/README.md
2026-08-18 22:03:52 +08:00

74 lines
3.5 KiB
Markdown

# odm
Orbit Data Messages: read, write and convert orbital element sets.
Everything ODMS knows about orbital *formats*, with nothing it knows about
Django, HTTP or a database. Dependencies are `sgp4` and the standard library.
## Why it is a library
The formats used to live in the service that happened to serve them first —
TLE rendering in a Django app's `formats.py`, the element-set catalogue in
another app, the OPM/OEM builders in a third, and the canonical record itself
implicit in a model row. That worked while one service read the data. It stops
working the moment a second one does: a format two services implement separately
is a format they will eventually disagree about, and the disagreement surfaces as
a satellite whose OMM and TLE describe slightly different orbits.
So: the OMM is the record. TLE text, OMM XML, OMM KVN and Celestrak GP CSV are
*renderings* of it, and the CCSDS SANA element sets are conversions of the state
it propagates to. One definition each, here.
## Use
```python
from odm import OmmRecord, parse_tle, registry
parsed = parse_tle(name, line1, line2)
record = OmmRecord(omm=parsed.omm, object_name=parsed.name, line1=line1, line2=line2)
xml = registry.get("omm_xml").write([record])
csv = registry.get("csv").write([record])
kep = registry.get("keplerian").write([record], backend=backend, at=when)
```
Every writer takes the same arguments — `write(records, *, backend=None,
at=None)` — so a caller never has to know which kind it got. `backend` and `at`
matter only to the element-set formats, which propagate before converting.
## Modules
| Module | What |
|---|---|
| `records` | `OmmRecord`, `ParsedTLE`, `StateVector` — what readers produce and writers consume. |
| `tle` | TLE text in and out. Lossy by construction; see the module docstring. |
| `omm` | CCSDS 502.0-B-3 OMM, XML and KVN. The authoritative rendering. |
| `gp` | Celestrak GP CSV, in their exact column order. |
| `norad` | Catalog-ID normalisation, ALPHA-5, and the temp-ID fallback. |
| `orbits` | Mean-element derivations: period, apogee, perigee, altitudes. |
| `conversions` | State vector → one SANA element set. |
| `element_sets` | The SANA catalogue, and computing a set for an OMM. |
| `messages` | CCSDS OPM/OEM: build and parse. |
| `registry` | Every output format in one table. |
| `propagator` | The seam a propagation backend implements. |
## The propagator seam
`odm` does no orbital mechanics beyond mean-element algebra. Anything needing a
propagated state takes a `PropagatorBackend``element_sets.compute_element_set`,
`messages.build_opm`, `messages.build_oem`, the element-set writers.
The backend is always **passed in**, never looked up. A library that reached for
a service's configured default would only work inside that service, which is the
thing this package exists not to be. Implementations live in the `yksa_orbital`
Django app (pure-Python SGP4, and an HTTP client to the Orekit sidecar).
## What is deliberately not here
- **Storage.** ODMS holds elements in Django, `track` holds them elsewhere, the
sidecar holds none. The format code works on records; the service adapts.
- **Serving.** Content types and filenames are in `registry` because they are
properties of the format, but nothing here builds a response.
- **The simulation model.** Force models, atmospheres and propagator settings
belong to the Orekit sidecar (`services/orekit/config.py`), which is their one
definition for the same reason this package is the formats' one definition.