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