# Наземные станции Станция (`Station`) — точка приёма в сети. Принадлежит пользователю, имеет координаты, набор антенн и настройки планирования. ## Статусы Состояние станции не хранится: оно выводится из трёх величин — `last_seen`, `is_available` и `testing` — каждый раз, когда его спрашивают, и потому не может разъехаться с сердцебиением. `Station.status_badge` возвращает слаг состояния, `Station.status_label` — его название. | Слаг | Значение | Условие | |---|---|---| | `online` | На связи и открыта сети | `last_seen` свежее `STATION_HEARTBEAT_TIME`, `is_available`, не `testing` | | `testing` | Работает, наблюдения считаются тестовыми | то же плюс `testing` | | `unavailable` | На связи, но владелец закрыл приём | то же плюс `is_available = False` | | `offline` | Не выходила на связь дольше сердцебиения | `last_seen` устарел | | `never` | Ни разу не выходила на связь | `last_seen` пуст | `unavailable` — состояние, которым владелец закрывает станцию для чужих наблюдений, оставаясь на связи: свои наблюдения он планирует как обычно, собственное расписание станции продолжает работать, сетевое автопланирование её не трогает. Флажок — в настройках станции. Переходы пишутся в `StationStatusLog` (три булевых: `is_connected`, `is_available`, `testing`). Строка появляется при сохранении станции, если состояние действительно изменилось; уход станции со связи никто не сохраняет, поэтому его раз в час дописывает задача `station_log_disconnect`. В API v1 (`/api/stations/`) поле `status` по-прежнему отдаёт три слова `Online`/`Testing`/`Offline`, и `unavailable` в них читается как `Offline` — станция, которая никогда не обновится, продолжает видеть тот же словарь. Фильтр `?status=2|1|0` работает по тем же правилам. ## Основные поля | Поле | Назначение | |---|---| | `owner` | Владелец, `network.users.User` | | `name` | Отображаемое имя | | `lat`, `lng`, `alt` | Широта (−90…90), долгота (−180…180), высота над уровнем моря в метрах (от −500: станция ниже уровня моря — реальный случай) | | `qthlocator` | QTH-локатор. Не колонка: считается из координат тем же `gridsquare()`, что и приём SiDS | | `horizon` | Минимальный угол места в градусах, ниже которого наблюдения не планируются | | `target_utilization` | Целевой коэффициент загрузки станции, 0…100 | | `client_version`, `client_id` | Версия и идентификатор клиента | | `reported_status`, `reported_status_at` | Что станция сообщила о себе через `POST /api/v2/stations//status/` или MQTT — слияние по ключам верхнего уровня: `modes` и `client_version` (клиент), `config` (итог применения конфигурации и фактические значения), `sdr` (найденные приёмники и их возможности), `calibration` (свип по усилению и рекомендованное значение), `scheduling` (за сколько секунд до начала станции нужно задание — порог планирования, пока она на связи), `connection` (мост MQTT). Планирование предлагает станции только заявленные режимы; без `modes` станция планируется на все | | `desired_config`, `config_generation`, `config_updated_at` | Конфигурация, сохранённая владельцем в форме «Настройки станции»: плоский словарь с именами переменных окружения клиента, номер сохранения, время. `0` — не сохранялась, станция на своём `.env` | | `commands` | Разовые просьбы к станции из формы настроек — `rescan_sdr`, `calibrate_gain` с отметкой `at` и частотами; уходят в документе `state/`, станция выполняет раз на отметку между проходами | | `release_channel`, `update_mode` | Канал релизов (`canary`/`stable`, digest образа — в `ReleaseChannel`, правят администраторы) и разрешение агенту обновлять станцию самому (`managed`) или только сообщать (`notify`) | | `violator_scheduling` | Кому разрешено планировать спутники-нарушители: «Никто» (0), «Только операторы» (1), «Все» (2) | | `featured_date` | Дата вывода станции в подборку | ## Конфигурация с портала Владелец настраивает приёмник, усиление, поправки частоты, поворотное устройство, запись IQ и уровни логов в форме «Настройки станции» (`stations/edit//`) — той же, где имя, координаты и антенны, с пояснением у каждого поля. Кнопку «Настройки станции» на странице станции видят владелец, модераторы и суперпользователи. Единый список полей — `STATION_CONFIG_FIELDS` в `network/base/station_config.py`; ключи совпадают с переменными окружения клиента и с его `MANAGED_FIELDS`. Сохранение, которое меняет поле конфигурации, — новое поколение: станция забирает документ `GET /api/v2/stations//state/` (и получает его по MQTT сразу), открывает приёмник с новыми параметрами и применяет между проходами; итог возвращается в `reported_status.config` и показывается на странице как «поколение N применено» или «отклонено: …». Сохранение без правок конфигурации — имя, координаты, антенны — поколения не создаёт. Пока владелец ничего не сохранил, форма предзаполняется фактическими значениями со станции, и станция остаётся на своём `.env`, пока в конфигурации что-нибудь не изменят; кнопка «Заполнить с станции» подставляет эти значения и позже. Что станция делает с документом — `docs/station/configuration.md` клиента. Там же — то, что станция узнала о себе сама. Блок «Найденные приёмники» показывает устройства SoapySDR, которые станция перечислила (на старте, раз в час в простое и по кнопке «Пересканировать»), с входами, диапазоном усиления и частотами дискретизации; «Использовать» подставляет их в поля формы. Блок «Калибровка усиления» по кнопке просит станцию прогнать свип по усилению на частотах антенн (середины диапазонов, можно править) и рисует шумовую полку графиком; «Применить рекомендованное» ставит усиление, на котором полка поднялась на порог станции. Обе кнопки — команды в `Station.commands`, часть документа `state/`. ## Антенны и диапазоны Антенна (`Antenna`) ссылается на тип (`AntennaType`) и содержит один или несколько диапазонов (`FrequencyRange`). Ограничения: * `MAX_ANTENNAS_PER_STATION` — антенн на станцию (по умолчанию 4); * `MAX_FREQUENCY_RANGES_PER_ANTENNA` — диапазонов на антенну; * `MIN_FREQUENCY_FOR_RANGE` / `MAX_FREQUENCY_FOR_RANGE` — допустимые границы. Диапазон автоматически классифицируется по радиодиапазонам (VHF/UHF/L/S) — границы задаются переменными `VHF_MIN_FREQUENCY`, `VHF_MAX_FREQUENCY`, `UHF_*`, `L_*`, `S_*` (см. [](configuration.md)). Планирование сопоставляет диапазоны антенн с частотами передатчиков спутника. Список поддерживаемых типов антенн загружается начальными фикстурами (`django-admin initialize`) и переведён на русский в интерфейсе: диполь, V-дипольная, дисконная, базовая, яги, кросс-яги, спиральная, параболическая, вертикальная, турникетная, квадрифилярная, «взбивалка», линденблад, паразитная линденблад и др. ## Регистрация станции Клиент регистрируется через `POST /api/station/register`. Дальше он забирает свои задания из `/api/jobs/` и отправляет результаты в `/api/observations/` и `/api/demoddata/` — см. [](api/endpoints.md). Проверить состояние станции можно через `GET /api/stations/check`. ## Настройки автопланирования У станции может быть до двух расписаний и одно общесетевое: | Модель | Связь | Назначение | |---|---|---| | `StationSchedule` | one-to-one со станцией | Основное расписание станции | | `StationScheduleSec` | one-to-one со станцией | Дополнительное расписание | | `NetworkSchedule` | одиночная запись | Общесетевое расписание | У всех трёх одинаковая структура: флаг `active`, список спутников (`satellites`, JSON) и параметры (`param`, JSON) — минимальный угол места, азимутальные окна и прочие ограничения. Как эти параметры применяются, описано в [](scheduling.md). ## Статистика `StationStat` хранит агрегаты по станции. Пересчитываются задачами `calculate_all_station_stat` / `calculate_station_stat` (ежедневно в 03:00 UTC). Суточные метрики по станциям ведёт отдельное приложение — см. [](analytics.md). ## Уведомления о проблемах Задача `notify_for_stations_without_results` находит станции, у которых подряд идут наблюдения без результатов (порог — `OBS_NO_RESULTS_MIN_COUNT`, по умолчанию 3), и отправляет уведомление на `EMAIL_FOR_STATIONS_ISSUES`. Период проверки — `OBS_NO_RESULTS_CHECK_PERIOD`.