# Запуски Раздел `/launches/` — афиша и дашборд запусков. Он отвечает на три вопроса: какие запуски ждёт сеть, что она услышала после прошлых и что ещё стоит в мировом календаре. На странице два раздела, переключатель под заголовком: «Отслеживаемые сетью» (сегменты «Сеть ждёт» и «Прошедшие») и «Календарь пусков». Без `?scope` открывается «Сеть ждёт», а если сеть ничего не ждёт — «Прошедшие». Страница запуска показывает состав, отсчёт до старта и **раскрытие** — как аппараты запуска проходят стадии от манифеста до первого принятого кадра. ## Два вида запусков `Launch.is_tracked` — галочка администратора, а не вычисляемый признак. **Отслеживаемый запуск** (`is_tracked=True`) — тот, что интересен сети. У него ведётся состав (`SatelliteLaunch` и `Satellite.launch`), до старта состав и TLE берутся из предстартового файла CelesTrak, после старта по номеру COSPAR приходят настоящие номера каталога, работает планировщик наблюдений на старте и считается воронка раскрытия. На `/launches/` такие запуски живут в сегментах «Сеть ждёт» и «Прошедшие», на главной блок «Запуски» показывает только их. **Календарный запуск** (`is_tracked=False`) — «просто информация». Страница есть, аппаратов в базе нет, планировщика нет. Все предстоящие орбитальные пуски мира заводятся из Launch Library 2 автоматически; после старта запись выпадает из раздела «Календарь пусков», но остаётся в базе и открывается по прямой ссылке. ```{warning} Галочка — предохранитель, а не фильтр витрины. Launch Library 2 проставляет номер COSPAR каждому мировому пуску после старта, и без `is_tracked` сигнал `post_save` и задача `fetch_satellite_from_tle_source_for_all_launches` завели бы в `Satellite` содержимое всех этих пусков — по два десятка Starlink за раз. Оба пути фильтруются по `is_tracked=True`. ``` Галочка ставится в админке (поле, фильтр и действие «Отметить отслеживаемыми») и кнопкой «Отслеживать» в шапке страницы запуска — она видна `is_staff`. Решать нужно **до** старта: COSPAR приходит после, и импорт срабатывает сразу за ним. ## Launch Library 2 Источник календаря — [Launch Library 2](https://thespacedevs.com/llapi) (TheSpaceDevs). Клиент — `network/base/ll2_client.py`, синхронизация — задача `sync_launches_from_ll2` в `network/base/tasks.py`. ### Что хранится Настоящие колонки — только то, по чему сортируем, фильтруем и считаем: `ll2_id`, `ll2_slug`, `launch_id` (COSPAR), `launch_name`, `launch_date` (LL2 называет его `net`), `net_precision`, `status`, `rocket_name`, `provider_name`, `pad_name`, `orbit`, `mission_type`, `mission_description`, `country`. Всё остальное — агентство, ступени и посадка ускорителя, координаты площадки, лента обновлений, трансляции, циклограмма — лежит в `Launch.ll2` (JSON-снимок ответа LL2) и читается шаблоном напрямую. Новое поле LL2 доступно странице без миграции. Три перечисления переводятся словарями в `models.py`: статус пуска (`LAUNCH_STATUSES`, ключ — id LL2), орбита (`LAUNCH_ORBITS`, id LL2) и тип миссии (`LAUNCH_MISSION_TYPES`, ключ — английское название, как отдаёт LL2). Имена собственные — носитель, площадка, оператор — остаются по-английски. `net_precision` — точность T-0 по LL2. `Launch.net_display` переводит её в «сколько печатать»: до часа включительно — время, до недели — день, до полугодия — месяц, иначе год. Отсчёт до старта рисуется только при точном времени. ### Человек главнее импорта Импорт пишет **только** через `apply_facts` (`network/base/importers.py`) со списком `LAUNCH_LOCKABLE_FIELDS` — тем же механизмом, что паспортные данные спутника. Поле из `Launch.locked_fields` не перезаписывается никогда, пустое значение не затирает заполненное, равное значение — не запись. В админке это блок галочек «Не перезаписывать». ### Изображение Скачивается одно изображение — главное фото запуска, — если LL2 называет для него лицензию; с `Unknown` картинка не берётся вовсе. Рядом сохраняются `image_credit` и `image_license` и печатаются подписью под фотографией. `image_source_url` — адрес, откуда взято: второй прогон узнаёт свою картинку и не качает её снова, а загруженную вручную (без `image_source_url`) импорт не трогает. Логотипы, инфографика и нашивки не скачиваются и не показываются. ### Бюджет запросов Анонимному клиенту боевой хост даёт **15 запросов в час на IP**, считая по скользящему часу. Бюджет один на весь сервер, поэтому тратит его только задача `sync_launches_from_ll2` (каждые 5 минут), по приоритетам: 1. **Живое окно** — отслеживаемый запуск от T−1 ч до T+72 ч опрашивается в каждый тик (`mode=detailed`), пока бюджет выше резерва в 4 запроса. Один запуск в окне — 12 запросов в час; два — по 6 на каждый. 2. **Календарь** — раз в час на резерв: страницы `launches/upcoming/` (`mode=normal`, по 100), одна страница `launches/previous/` (чтобы статус чужого запуска сменился с «Готов к пуску» на «Успешный пуск») и один отслеживаемый запуск с самым старым снимком — в `mode=detailed`. Отметки времени запросов живут в кэше (`ll2_request_stamps`); на холодном кэше задача спрашивает `api-throttle/`, сколько уже потрачено. HTTP 429 останавливает задачу на час. **Тик страницы в LL2 не ходит никогда**: живой режим бьёт в `/launches//live/`, который читает базу и кэш. Переменные: `LL2_API_URL` (по умолчанию боевой хост; для локальной работы — `https://lldev.thespacedevs.com/2.3.0`, без лимита, но с усечёнными данными) и `LL2_HOURLY_BUDGET`. См. [](configuration.md). ## Состав до старта: SupGP За несколько дней до группового пуска CelesTrak публикует [предстартовый файл SupGP](https://celestrak.org/NORAD/elements/supplemental/) — элементы каждого объекта начального развёртывания, посчитанные по векторам состояния от оператора. Эпоха этих элементов — момент отделения, то есть в будущем. Администратор вписывает имя файла в поле `supgp_file` запуска в админке (`transporter-18` из `sup-gp.php?FILE=transporter-18`). Задача `fetch_launch_supgp` (`network/base/launch_supgp.py`) раз в два часа опрашивает файлы отслеживаемых запусков, у которых даты ещё нет или после старта прошло не больше 14 суток. Ждать двух часов не обязательно: сохранение файла в админке само ставит задачу в очередь, и её можно запустить со страницы «Система». Там же, в строке задачи, видно, что она сделала или почему ничего не сделала: `rate_limited: True` — CelesTrak ответил 403/429 кому-то из читателей портала, и два часа его не спрашивают. На каждый объект файла: 1. **Ищется спутник** — по имени и алиасам (`name` и «Другие названия», без регистра и знаков) среди аппаратов этого запуска и аппаратов со статусом `future` без запуска. Так находятся аппараты, заранее заведённые руками или пришедшие из SatNOGS DB под другим временным номером. 2. **Если спутника нет, заводится заглушка** — `Satellite` со статусом `future`, `unknown=True`, `source="CelesTrak"` и свободным временным номером из диапазона 70000–100000. Как и объекты, заведённые импортом по COSPAR, заглушка не утверждена (`approved=False`): в общем списке спутников её нет, на странице запуска и по прямой ссылке — есть. 3. **Спутник получает TLE** с источником `Celestrak SupGP`. Строки пишутся с номером спутника в портале: CelesTrak нумерует эти объекты девятью цифрами, в строку TLE такой номер не помещается (сам CelesTrak в формате TLE эти данные не отдаёт), и нигде в портале он не хранится. Повторный опрос того же файла ничего не пишет; изменившиеся элементы — новый набор, на который будущие наблюдения переедут сами (см. [](tle.md)). ### Ручная привязка Имена совпадают не всегда. Чтобы привязать спутник к объекту файла, впишите имя объекта в «Другие названия» спутника и, если у спутника статус не `future`, поставьте ему этот запуск. При следующем опросе: * заглушка, заведённая импортом под этим именем, **сливается** в спутник: её TLE и наблюдения переезжают, сама она удаляется. Заглушкой считается спутник с `source="CelesTrak"` без передатчиков; заглушка, которой дали передатчик, — уже самостоятельный спутник, и её импорт не трогает; * два аппарата, которые выходят из диспенсера сцепленными и значатся у CelesTrak одним объектом, получают его элементы оба — достаточно вписать имя объекта в алиасы каждого. ```{warning} Имя заглушки — её связь с файлом. Переименовывая заглушку, оставьте исходное имя в «Других названиях», иначе следующий опрос заведёт под этим именем новую. ``` Чего импорт не делает: не удаляет аппарат, пропавший из файла или переименованный CelesTrak (он остаётся с прежним TLE, убирает администратор), и не передвигает уже запланированные наблюдения при сдвиге пуска — TLE у них обновится, окна нет. ### После старта Когда Launch Library 2 ставит отслеживаемому запуску «Успешный пуск», его аппараты переходят из `future` в `in orbit`, и один раз запускается планировщик наблюдений на старте с настройками по умолчанию (см. [](scheduling.md)). Это не зависит от того, откуда у аппаратов TLE — из SupGP или заведён руками. Настоящие номера NORAD приходят импортом по COSPAR. У запуска, в котором уже есть аппараты, он новых не заводит, а ищет по имени те, что ещё под временным номером, и записывает им номер каталога; имена-заглушки вида `OBJECT X` не сопоставляются. Задача возвращается к такому запуску раз в сутки в течение 90 суток после пуска. С настоящим номером аппарат начинает получать TLE от Space-Track и CelesTrak, и они вытесняют предстартовый по приоритету. Аппарат, имя которого в каталоге оказалось другим, опознаётся так же, как привязывается: алиасом или номером вручную. ## Раскрытие запуска Запуск — когорта аппаратов, которая проходит стадии: | Стадия | Условие | |---|---| | заявлен | строка манифеста `SatelliteLaunch` или `Satellite` под временным номером (70000–100000) — аппарат известен только по имени | | опознан | у `Satellite` настоящий номер каталога или задан `norad_follow_id` | | услышан | у аппарата заполнен `first_signal_at` | | передаёт | есть `DemodData` за последние 7 дней | | молчит | услышан, кадров за 7 дней нет | `Satellite.first_signal_at` и `first_signal_station` заполняются задачей `record_launch_first_signals` (каждые 15 минут, по первому наблюдению со статусом `good`) **один раз и не пересчитываются** — момент первого сигнала не меняется. Живой режим страницы вызывает ту же функцию для своего запуска, чтобы не ждать тика задачи. Стадия «передаёт/молчит» считается на запрос; набор передающих аппаратов кэшируется на 5 минут по ключу `launch-transmitting-`. Строка манифеста и спутник каталога склеиваются по `sat_id` или NORAD, так что аппарат — одна строка, сколько бы таблиц его ни знали. ## Страница запуска Три состояния (`launch_state` в `views/launch.py`): * **до старта** — крупный отсчёт, сколько наблюдений уже запланировано и на скольких станциях, сколько аппаратов заявлено и у скольких известны частоты. Рядом с отсчётом — фотография, а если у аппаратов уже есть TLE — карта их расчётных орбит; * **живой режим** — от T−1 ч до T+72 ч у отслеживаемого запуска: карта аппаратов (если есть TLE), «сейчас слушает», воронка и стадии обновляются раз в минуту скриптом `launch_live.js` из `/launches//live/`. Для проверки без пуска: `?live=1` под `is_staff` включает окно принудительно; * **архив** — воронка и время/станция первого сигнала. В таблице состава у каждого аппарата — эпоха и источник TLE, над ней — ссылка «Скачать TLE запуска» (`/launches//tle/`, текст в формате 3LE). Вкладки: «Обзор», «Состав и раскрытие», «Носитель и площадка» (из снимка LL2; площадка рисуется своей картой по координатам), «Хроника» (лента обновлений, циклограмма T-минус, трансляции, ссылки) и «Планирование» — форма планировщика наблюдений на старте, только `is_staff` и только у отслеживаемого запуска. См. [](scheduling.md). ## Миграция 0053 `rocket_type` → `rocket_name`, `space_port` → `pad_name` (данные сохранены, справочники `choices` сняты). Свободный текст `status` сопоставлен со статусами LL2 по основам слов («успе» → успешный пуск, «неуда»/«авари» → авария, «частич» → частичная неудача, «отлож» → отложен, «ожида»/«план» → дата не определена и т. д.); значение, которое не подошло ни к одной основе, сбрасывается и печатается в лог миграции. `is_tracked=True` проставлен запускам, у которых уже есть аппараты или строки манифеста.