# Планирование наблюдений Планирование — это подбор временных окон, в которые конкретная станция видит конкретный спутник, и создание из них наблюдений. В портале три сценария: ручной, автоматический и отдельный для новых запусков. ## Расчёт пролётов Геометрия пролёта считается через `skyfield`. Общие примитивы вынесены в `network/base/skyfield_pass_utils.py`: наблюдатель на WGS-84, `EarthSatellite` из TLE, разностный вектор и `find_events()` для событий восход/кульминация/заход, плюс угол места и азимут. ```{note} `EarthSatellite` разбирает TLE через `sgp4` (пин `sgp4==2.24`), который штатно понимает Alpha-5-кодированные номера каталога NORAD — в отличие от `ephem`, на котором этот код был раньше. ``` Основной расчёт живёт в `network/base/scheduling.py`: | Функция | Назначение | |---|---| | `predict_available_observation_windows()` | Главная точка входа: доступные окна для станции и передатчика | | `generate_overhead_observation_window()` | Окно для пролетающего спутника | | `generate_geo_observation_window()` | Окно для геостационарного спутника | | `create_station_window()`, `create_station_windows()` | Формирование окон станции | | `recalculate_window_parameters()` | Пересчёт параметров окна | | `split_long_window()` | Разбиение длинного окна на части | | `resolve_overlaps()` | Разрешение пересечений с уже запланированным | | `over_min_duration()` | Отсев слишком коротких окон | | `get_available_stations()` | Станции, которым доступен данный передатчик | | `create_new_observation()` | Создание наблюдения из окна | Ограничения задаются настройками (см. [](configuration.md)): `OBSERVATION_DATE_MIN_START` — насколько близко к «сейчас» разрешено планировать станции, которая не на связи по MQTT; станция на связи заявляет свой порог в секундах (`reported_status.scheduling.lead_seconds`, `Station.scheduling_lead()`), и начало внутри порога `create_new_observation()` сдвигает вперёд, а не отклоняет — пользователь видит сообщение о сдвиге. `OBSERVATION_DURATION_MIN` — минимальная длительность, `OBSERVATION_SPLIT_DURATION` и `OBSERVATION_SPLIT_BREAK_DURATION` — порог и пауза разбиения, `OBSERVATION_DATE_MAX_RANGE` — горизонт планирования. ## Ручное планирование Через веб-интерфейс (`network/base/views/scheduling.py`): пользователь выбирает спутник, передатчик и станцию, портал показывает доступные окна, пользователь подтверждает. Право планировать проверяется в `network/base/perms.py`. Для спутников-нарушителей действует поле станции `violator_scheduling` («Никто» / «Только операторы» / «Все») и квоты `OBSERVATIONS_PER_VIOLATOR_SATELLITE` за период `OBSERVATIONS_PER_VIOLATOR_SATELLITE_PERIOD`. ## Автопланирование `network/base/auto_scheduling.py` строит наблюдения без участия человека: | Функция | Назначение | |---|---| | `predict_candidate_windows()` | Окна-кандидаты по параметрам расписания | | `schedule_windows_by_priority()` | Отбор и постановка окон с учётом приоритета | | `schedule_windows_by_yield()` | Деление времени станции между пролётами по ожидаемой отдаче | | `satellite_yield_factors()` | Вес спутника по кадрам, принятым станцией за 14 суток | | `constrain_pass_to_az_window()` | Обрезка пролёта по азимутальному окну | | `check_az()` | Попадание азимута в разрешённый сектор | | `unpack_schedule_params()` | Разбор JSON-параметров расписания с учётом горизонта станции | | `window_validation()` | Финальная валидация окна | Параметры берутся из `StationSchedule`, `StationScheduleSec` и `NetworkSchedule` (см. [](stations.md)) — список спутников и JSON с ограничениями. Запускается задачей `auto_schedule_network` каждые 12 часов с горизонтом 36 часов. Внутри есть отдельные задачи по источникам расписания: `auto_schedule_station_primary`, `auto_schedule_station_second`, `auto_schedule_station_network`. У дополнительного расписания есть порядок планирования — ключ `mode` в его параметрах: | `mode` | Как делится время станции | |---|---| | `random` (по умолчанию) | Пролёты берутся по раннему окончанию; проигравший слот пролёт обрезается до свободного остатка | | `elevation` | То же, но по полосам кульминации 90–70, 70–50, 50–30 и ниже: верхняя полоса занимает слоты первой | | `yield` | Посекундно: каждые 10 секунд взвешиваются по текущей высоте спутника (`ELEVATION_YIELD`, кадров в минуту), и время делится так, чтобы сумма была наибольшей. Пересекающиеся пролёты сменяют друг друга около точки равной высоты | | `yield_history` | Как `yield`, но вес умножается на отдачу спутника на этой станции: кадров на секунду наблюдения за 14 суток к среднему по станции, в пределах 0,5–2. Наблюдения с более чем 100 кадрами (сбросы) не считаются, спутник без истории получает 1 | Первые два режима решают, какому пролёту достанется слот, и отдают его целиком, вместе с участками у горизонта. Два последних решают, какому спутнику достанется каждый отрезок времени: выше 20° отдача почти не растёт, ниже 10° её почти нет, поэтому хвост высокого пролёта стоит меньше, чем кульминация соседнего низкого. Наблюдений при этом больше, и они короче. Таблица `ELEVATION_YIELD` измерена на одной станции диапазона UHF с квадрифилярной антенной; для другой антенны её нужно перемерить. ## Планирование запусков Для только что запущенных аппаратов расчёт особый: точный TLE ещё не опубликован, а объекты идут плотной группой. Этим занимается `network/base/launch_scheduler.py`. Модели: `Launch` (запуск с типом ракеты, типом разгонного блока и космодромом) и `SatelliteLaunch` (аппарат в составе запуска, с типом миссии и статусом). | Функция | Назначение | |---|---| | `launch_scheduler()` | Основной проход планирования по запуску | | `update_station_windows()` | Обновление окон станций | | `get_available_transmitter()` | Подбор передатчика | | `create_new_launch_observations()` | Создание наблюдений | Класс `SatelliteStats` считает статистику уже запланированных наблюдений по аппарату, чтобы распределять их равномерно. Отдельный набор настроек: `LAUNCH_MIN_ELEVATION` (5°), `LAUNCH_GOOD_ELEVATION` (25°), `LAUNCH_MIN_HORIZON` (0°), `LAUNCH_MIN_PERIOD` (48), `LAUNCH_SPLIT_DURATION` (480 с), `LAUNCH_BREAK_DURATION` (20 с), `LAUNCH_WAITING_FOR_SATELLITE`. Слот — 8 минут, то есть наблюдение занимает пролёт почти целиком: на пролёт группы над станцией приходится одно наблюдение одного аппарата, а пролёты короче слота пропускаются. Короткий слот даёт больше строк (120 с — впятеро), но режет пролёт на куски, и заметная часть наблюдений достаётся краям у горизонта, где ничего не принять. Число наблюдений ограничено временем станций, а не числом аппаратов: в замере на элементах Transporter-18 и 13 станциях за 48 часов выходит около 150 наблюдений, будь аппаратов 12 или 70. Планировщик запускается сам, один раз, когда Launch Library 2 ставит отслеживаемому запуску «Успешный пуск», с этими настройками и от имени `NETWORK_SCHEDULER_USERNAME`; тогда же аппараты запуска переходят из `future` в `in orbit`. Дальнейшие запуски — кнопкой на вкладке «Планирование». Слоты, занятые другими наблюдениями станции, планировщик пропускает. Связанные задачи: `launch_scheduler_task`, `fetch_satellite_from_tle_source_for_all_launches` (ежедневно в 04:00 UTC) — заводит объекты запуска по международному обозначению и проставляет аппаратам настоящие номера, `fetch_launch_supgp` (каждые 2 часа) — даёт аппаратам TLE до старта, `check_unknown_satellites` — обнаружение неизвестных объектов (раз в сутки, и каждые 15 минут в режиме проверки запусков). ## Пересчёт по новым TLE Задача `update_future_observations_with_new_tle_sets` (каждые 30 минут) пересчитывает окна уже запланированных будущих наблюдений, когда для спутника появился более свежий набор TLE. Без этого окно, рассчитанное по устаревшим элементам, уезжает от фактического пролёта.