# yksa_orbital Propagation for Django services: a backend registry, and the client to the Orekit sidecar. ```python from yksa_orbital import get_backend state = get_backend().state_at(omm, at) weather = get_backend("orekit").space_weather(start, stop) ``` ## Why it is its own app ODMS and `track.tmtc.yksa.space` both propagate, both against the same sidecar. The alternative to sharing this is two HTTP clients that drift apart on timeouts, on 503 handling, and on what they put in a request — and since the sidecar's whole purpose is that two services fly the *same* model, two clients that send different things quietly defeat it. ## Install Add `"yksa_orbital"` to `INSTALLED_APPS` and set: ``` ORBITAL_PROPAGATOR_BACKEND=sgp4 # or "orekit" OREKIT_SERVICE_URL=http://orekit:5000 ``` It depends on `odm` and on `httpx`. Nothing else — in particular, nothing from the host service. ## Backends | Name | What it can do | |---|---| | `sgp4` | Pure Python. TEME plus a GMST Earth-fixed frame. Analytical theory only: **no drag integration**, so it refuses decay, drag fitting and space weather rather than returning a number nobody should trust. | | `orekit` | HTTP client to the Orekit sidecar (`tle/services/orekit`). Rigorous frames, numerical and semi-analytical propagation, TLE fitting, drag fitting, decay forecasts, solar-activity ensembles, space weather. | ### Name the backend when you need the sidecar `get_backend()` returns the deployment's default, which is `sgp4`. Anything needing physics `sgp4` does not have must ask for `"orekit"` explicitly. This is not hypothetical. A view that forgot to, and swallowed the resulting `PropagationError`, rendered an empty space-weather chart for weeks with nothing logging a complaint. If your call needs the sidecar, say so. ## Capacity The sidecar keeps a worker free for the interactive queries a page load waits on, and turns heavy work away rather than queueing it. A 503 arrives here as `BackendBusy` — a subclass of `PropagationError`, so existing handlers still work, but a Celery task should catch it **first** and retry. Nothing about the request needs to change for it to succeed later; recording it as a failed forecast is wrong. ## The model config `yksa_orbital.model_config` reads the sidecar's `GET /config` — the force model, propagator, decay altitude, drag-fit thresholds and weather sources it is actually flying — with a cache and a vendored fallback. Use it for values a page genuinely needs locally: the ensemble's display floor, the decay altitude in a caption. Do **not** use it to build a request. Omitting a parameter already gets the sidecar's value; echoing it back only adds a way for the two to disagree. ```python from yksa_orbital.model_config import model_config, setting floor_km = setting("ensemble", "display_floor_km", 150.0) ``` `setting()` applies this deployment's `DECAY_*` override when there is one, so a caption describes the run on screen rather than the model in general. ## Layout | File | What | |---|---| | `registry.py` | `get_backend()`, and `register()` for a service with its own propagator. | | `backends/sgp4.py` | The pure-Python backend. | | `backends/orekit.py` | The sidecar HTTP client. | | `wire.py` | Stored OMM dict → the CCSDS message the sidecar consumes. The only place that conversion happens. | | `model_config.py` | Cached access to the sidecar's model definition. |