No description
Find a file
2026-08-18 22:03:52 +08:00
odm Initial commit 2026-08-18 22:03:52 +08:00
tests Initial commit 2026-08-18 22:03:52 +08:00
.gitignore Initial commit 2026-08-18 22:03:52 +08:00
pyproject.toml Initial commit 2026-08-18 22:03:52 +08:00
README.md Initial commit 2026-08-18 22:03:52 +08:00

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

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