# Орбитальные данные (TLE) Наборы TLE — основа всех расчётов пролётов. Портал собирает их из нескольких источников, выбирает победителя по приоритету и отдаёт наружу через API. ## Модели `Tle` : Одна запись на «источник × загрузка». Источник хранится в `tle_source`. `LatestTleSet` : Указатель на текущий выбранный набор для спутника (`latest`). То есть история загрузок сохраняется целиком, а `LatestTleSet` — это результат отбора. ## Источники | Источник | Откуда | |---|---| | `Manual` | Заведён вручную через админку | | `SpaceTrack` | Каталог Space-Track (`SPACE_TRACK_GP_QUERY_URL`) | | `Celestrak` | CelesTrak, группа `active` | | `Celestrak SupGP` | Предстартовый файл SupGP запуска, см. [](launches.md) | | `SatNOGS` | `https://db.satnogs.org/api/tle/?format=3le` | Список источников задан кодом: три блока внутри `fetch_tle`, по одному на Space-Track, CelesTrak и SatNOGS DB. Настройки, добавляющей источник снаружи, нет — новый источник заводится отдельным блоком в задаче. Загрузка идёт задачей `fetch_tle` каждые 4 часа. `Celestrak SupGP` стоит особняком: его пишет своя задача `fetch_launch_supgp`, и только аппаратам запусков, у которых назван файл. ### Клиент Space-Track `network/base/spacetrack_client.py` — минимальный клиент для массовой загрузки элементов. Space-Track требует логина перед любым запросом и жёстко ограничивает число сессий и запросов на аккаунт, поэтому клиент логинится один раз за вызов и делает **один** массовый запрос активного каталога вместо запроса на спутник. Одна загрузка раз в 4 часа укладывается в лимиты. ```{important} Стенд и прод используют разные учётки Space-Track — они подставляются при деплое из CI/CD-переменных (`SPACE_TRACK_USERNAME_DEV`/`_PROD`), а не лежат в `env-dist` на сервере. Общий аккаунт на два контура выбирает лимит вдвое быстрее. ``` ## Выбор победителя `network/base/tle_priority.py` решает, какой из наборов станет `LatestTleSet.latest`. Фиксированный приоритет источников: ``` Manual > SpaceTrack > Celestrak > Celestrak SupGP > SatNOGS ``` Предстартовый вектор состояния лучше пересказа его же из SatNOGS и хуже каталога: как только у аппарата появляется настоящий номер, `SpaceTrack` и `Celestrak` его вытесняют. Приоритет ограничен окном свежести: источник с более высоким приоритетом выигрывает, только если его данные не старше `TLE_PRIORITY_FRESHNESS_DAYS` (по умолчанию 7 дней). Если ни один приоритетный источник не свеж, побеждает просто самый недавно обновлённый набор — независимо от источника. Из двух наборов одного источника с одной эпохой побеждает записанный позже: источник, исправивший элементы без сдвига эпохи, иначе не был бы услышан. ### Имена аппаратов Имя спутника обогащается **независимо** от выбора орбитальных параметров. Заглушки вида `OBJECT S` (Space-Track) или `TRANSPORTER-15 OBJECT L` (CelesTrak) распознаются по шаблону и заменяются первым осмысленным именем, найденным при обходе источников в том же порядке приоритета. ## Публичная выдача `GET /api/latesttles/` отдаёт актуальные наборы. Особенности: * Доступен без авторизации, лимит — `LATEST_TLES_THROTTLE_RATE` (60 запросов в минуту). * Форматы: `?FORMAT=tle` и `?FORMAT=json`. * Отдаются **только аппараты на орбите**. Поскольку `Satellite.status` проставляется руками, основным признаком служит возраст TLE: сошедший с орбиты объект выпадает из выборки Space-Track (`decay_date/null-val`) и перестаёт обновляться. Порог — `LATEST_TLES_MAX_AGE_DAYS` (30 дней), его можно поднять, если источник временно сломан. Полная история — `GET /api/tles/`. Экспорт наборов наружу выполняет задача `export_tle`. ## Публикация TLE `POST /api/tles/` принимает наборы извне — для внешних сервисов определения орбиты, которые считают элементы по результатам наблюдений и возвращают результат в каталог. Без этого эндпоинта такая запись возможна только через админку. Опубликованный набор сохраняется с источником `Manual` — первым в списке приоритета. Это сделано осознанно: проверенный вручную, посчитанный извне набор и есть ручной ввод, а `Manual` уже стоит первым, так что новый свежий набор выигрывает `select_latest_tle` и начинает определять планирование всей сети. Клиент не может выбрать значение источника сам. Из этого следуют два ограничения: * право публикации проверяется точечно, по конкретному спутнику (`publish_tle_perms`); * набор проходит те же проверки, что и форма в админке (`validate_tle`): контрольные суммы, совпадение номеров объекта в обеих строках, диапазоны элементов, положительные перигей и апогей. После сохранения сразу пересчитывается `LatestTleSet` для этого спутника. Ответ — `201` с идентификатором созданной записи. Аутентификация здесь шире, чем в остальном API: помимо токена и сессии принимается Bearer-токен через OIDC, чтобы сервис мог действовать от имени пользователя, инициировавшего публикацию. Общепроектные классы аутентификации при этом не меняются. Спутник выбирается по `sat_id`, если он передан, иначе по `norad_cat_id`. Номер каталога не уникален в схеме, поэтому при поиске по номеру из нескольких совпадений выбирается детерминированно первый по `id`. Клиенту, который знает спутник, следует передавать `sat_id`. `norad_cat_id` обязателен в любом случае: это номер в строках набора. Если спутник не найден, ответ `404`. Так публикует, например, суточный расчёт TLE по ГНСС-маякам спутника gscn: отдельный репозиторий [soniks-tle-from-gnss](https://gitlab.com/space-education-development/soniks/network/soniks-tle-from-gnss) с запуском по расписанию GitLab, не функция сети. ## Пересчёт запланированных наблюдений Появление свежего TLE не переписывает уже созданные наблюдения само по себе — этим занимается `update_future_observations_with_new_tle_sets` (каждые 30 минут), пересчитывая окна будущих наблюдений. См. [](scheduling.md). ## Тесты Логика отбора и разбора покрыта отдельно: `network/base/test_tle_priority.py`, `network/base/test_tle_utils.py`, `network/base/test_tle_admin_form.py`, `network/base/test_spacetrack_client.py`.