108 lines
4.2 KiB
Python
108 lines
4.2 KiB
Python
"""What simulation model the sidecar is flying, asked rather than assumed.
|
|
|
|
The model -- force model, propagator, decay altitude, drag-fit thresholds,
|
|
space-weather sources -- is defined once, in the sidecar's ``config.py``, because
|
|
more than one service flies it and two forecasts are only comparable if the model
|
|
behind them is the same. This is how a Django service reads that definition
|
|
instead of keeping a second copy that drifts.
|
|
|
|
Use it for the values a page or a plot genuinely needs locally: the ensemble's
|
|
display floor, the drag-fit window count, the decay altitude in a caption. Do
|
|
*not* use it to build a request -- omitting a parameter already gets the
|
|
sidecar's value, and echoing it back would only add a way for the two to
|
|
disagree.
|
|
|
|
Cached, because it changes on deploy and not otherwise, and because a page that
|
|
wants one number out of it must not pay a round trip. Falls back to a vendored
|
|
copy of the defaults when the sidecar is unreachable, so a caption never takes a
|
|
page down; the fallback is flagged so a caller can tell.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from django.conf import settings
|
|
from django.core.cache import cache
|
|
|
|
from odm import PropagationError
|
|
|
|
from .registry import get_backend
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
CACHE_KEY = "yksa_orbital:model_config"
|
|
CACHE_TTL_S = 3600
|
|
#: A failure is cached too, briefly. Without it a sidecar that is down costs a
|
|
#: fresh connection attempt on every page render -- and against an unresolvable
|
|
#: hostname that is a DNS timeout each time, which is how a caption took a page
|
|
#: down rather than the other way round.
|
|
FAILURE_TTL_S = 60
|
|
|
|
#: The sidecar's defaults as of this release. Only ever used when the sidecar
|
|
#: cannot be reached, and only for values that are read for display -- so a stale
|
|
#: entry here shows a slightly wrong caption rather than flying a wrong model.
|
|
#: Keep it in step with ``services/orekit/config.py`` when a default changes.
|
|
FALLBACK = {
|
|
"force_model": {
|
|
"gravity_degree": 4, "gravity_order": 4,
|
|
"srp": True, "third_bodies": True, "tesseral": False,
|
|
"atmosphere": "NRLMSISE00",
|
|
},
|
|
"decay": {
|
|
"method": "auto", "strengths": ["AVERAGE"], "altitude_km": 105.0,
|
|
"max_years": 25.0, "dsst_handover_altitude_km": 180.0,
|
|
},
|
|
"drag_fit": {"window_days": 90, "min_elements": 30, "windows": 5},
|
|
"ensemble": {
|
|
"realizations": 30, "percentiles": [10, 25, 50, 75, 90],
|
|
"display_floor_km": 150.0,
|
|
},
|
|
"unavailable": True,
|
|
}
|
|
|
|
|
|
def model_config(*, refresh: bool = False) -> dict:
|
|
"""The sidecar's resolved model. Never raises."""
|
|
if not refresh:
|
|
cached = cache.get(CACHE_KEY)
|
|
if cached is not None:
|
|
return cached
|
|
try:
|
|
config = get_backend("orekit").model_config()
|
|
except (PropagationError, ValueError) as exc:
|
|
logger.warning("could not read the sidecar's model config: %s", exc)
|
|
cache.set(CACHE_KEY, FALLBACK, FAILURE_TTL_S)
|
|
return FALLBACK
|
|
cache.set(CACHE_KEY, config, CACHE_TTL_S)
|
|
return config
|
|
|
|
|
|
def setting(section: str, key: str, default=None):
|
|
"""One value out of the model, with the local override applied if there is one.
|
|
|
|
Django's ``DECAY_*`` settings are overrides: when one is set, this deployment
|
|
really is flying something other than the sidecar's model, and a caption
|
|
reading the sidecar's value would be describing a different run than the one
|
|
on screen.
|
|
"""
|
|
override = getattr(settings, _OVERRIDES.get((section, key), ""), None)
|
|
if override not in (None, ""):
|
|
return override
|
|
return model_config().get(section, {}).get(key, default)
|
|
|
|
|
|
#: (section, key) -> the Django setting that overrides it. Mirrors
|
|
#: ``yksa_tle.predictions.service._DECAY_OVERRIDES``.
|
|
_OVERRIDES = {
|
|
("decay", "altitude_km"): "DECAY_ALTITUDE_KM",
|
|
("decay", "strengths"): "DECAY_STRENGTHS",
|
|
("decay", "method"): "DECAY_METHOD",
|
|
("decay", "max_years"): "DECAY_MAX_YEARS",
|
|
("force_model", "gravity_degree"): "DECAY_GRAVITY_DEGREE",
|
|
("force_model", "gravity_order"): "DECAY_GRAVITY_ORDER",
|
|
("force_model", "srp"): "DECAY_SRP",
|
|
("force_model", "third_bodies"): "DECAY_THIRD_BODIES",
|
|
("drag_fit", "windows"): "DECAY_DRAG_FIT_WINDOWS",
|
|
("ensemble", "realizations"): "DECAY_ENSEMBLE_REALIZATIONS",
|
|
}
|