Спутники и передатчики

Satellite

Космический аппарат в базе сети.

Поле

Назначение

sat_id

Идентификатор вида CCCC-NNNN-NNNN-NNNN-NNNN

norad_cat_id

Номер в каталоге NORAD

name

Название

status

alive, dead, future, re-entered

source

Admin (заведён вручную) или CelesTrak

operator

Оператор (Operator)

network

Сеть (Network)

mass

Масса, килограммы

form_factor

3U, PocketQube, microsat — свободный текст

sources

[{"title": …, "url": …}], откуда взяты паспортные данные

locked_fields

Поля, которые импортёру запрещено перезаписывать

short_description_{ru,en,zh}

Описание миссии одной строкой

description_{ru,en,zh}

Развёрнутое описание миссии

Описания и языки

Описание хранится по колонке на язык, а не словарём в JSON: точечная правка в админке тривиальна, блокировка перезаписи ложится на каждый язык отдельно, а поиск по описанию остаётся обычным LIKE.

Пустой язык — законное состояние. Китайский может быть пуст всегда. Страница показывает описание на языке читателя, а если его нет — на английском, а если нет и его — на любом заполненном. Когда не заполнено ничего, блок описания не рисуется вовсе: заголовок над прочерком хуже отсутствующего блока.

Сервиса перевода в портале нет и не будет. Перевод делается снаружи, а обмен идёт двумя командами — см. Management-команды.

Блокировка автоматической перезаписи

locked_fields — список имён полей, которые импортёры не трогают. Это контракт между админкой и всеми источниками сразу, а не настройка одного скрейпера: любой импортёр обязан прочитать список и пропустить перечисленное.

Набор полей, которые вообще можно заблокировать, задан константой LOCKABLE_FIELDS в network/base/models.py. Форма спутника в админке рисует поверх одной этой колонки блок чекбоксов «Не перезаписывать».

Галочка ставится вручную: автоблокировки по факту правки нет намеренно. Отсюда цена, о которой надо помнить, — администратор, вычитавший описание и забывший отметить поле, потеряет свой текст на следующем обходе источника.

Откуда берутся паспортные данные

Два импортёра, оба уважают locked_fields и оба соединяются с каталогом только по номеру NORAD — подробности и расписание в Задачи Celery.

Источник

Что даёт

Лицензия

nanosats.eu

Наноспутники: масса, форм-фактор, описание одной строкой, ссылки на источники

Не объявлена — берём текст и числа, снимки не берём

Wikidata

Масса, описания на трёх языках, ссылка на статью, файл на Commons

CC0

Wikimedia Commons

Сами фотографии с автором и лицензией

По файлу, читается через API

Ручной ввод и правка через админку доступны всегда и имеют приоритет: галочка «Не перезаписывать» замораживает поле для всех импортёров сразу.

SatellitePhoto

У спутника может быть несколько фотографий. Satellite.image при этом остаётся тем, чем был: одна картинка, которую пишет импорт из SatNOGS DB и публикует API.

Поле

Назначение

image

Файл; те же ограничения, что у прочих картинок — jpg/png/gif/webp, до 2 МБ

source_url

Страница-первоисточник снимка, а не наша копия

author

Автор снимка

license

PD, CC BY-SA 4.0, пусто

caption, is_cover, order

Подпись, признак обложки, порядок

Обложка выбирается так: отмеченная is_cover → первая по order → Satellite.image → заглушка.

source_url, author и license заполняются обязательно и выводятся подписью под снимком. Это не оформление: ни nanosats.eu, ни Wikimedia Commons не отдают снимок безусловно — указание автора и лицензии есть условие, на котором его вообще можно показывать.

Статус выставляется вручную — с единственным исключением, переходом in orbit → re-entered (см. ниже). Поэтому для практических решений (например, публичной выдачи TLE) портал опирается не только на него, но и на возраст последнего набора TLE — см. Орбитальные данные (TLE).

Орбитальные параметры пересчитываются задачей calculate_all_satellite_orbital (ежедневно в 05:00 UTC), логика — в network/base/orbital_satellite_params.py.

Сошедшие с орбиты аппараты отображаются отдельным представлением (network/base/views/decay.py).

История орбиты

На вкладке «Орбита» страницы спутника под строками TLE — график средних элементов по всем наборам TLE за 365 дней: высота большой полуоси над Землёй (a − R⊕), наклонение, эксцентриситет, долгота восходящего узла и аргумент перигея. По оси времени — эпоха набора из самих строк, а в выборку строки попадают по Tle.updated. Так сделано потому, что у строк, записанных до появления столбца epoch, он пустой. Наборы с эпохой старше года отбрасываются: источник может заново опубликовать последние элементы замолчавшего аппарата, и одна такая точка растянула бы ось на годы. Каждый источник (Tle.tle_source) рисуется своей линией, легенда скрывает лишние. Если источник молчал больше недели, линия рвётся, а не тянется через пропуск. Скачок высоты означает манёвр или ошибочный набор, а расхождение линий показывает разницу между источниками. Средней аномалии на графике нет: на эпоху набора это случайное число от 0 до 360°.

Данные отдаёт /orbit_history/<sat_id>/ (orbit_history в network/base/views/satellite.py), кэш satellite-<pk>-orbit-history на CACHE_TTL. Страница запрашивает их, только когда открыта вкладка. Набор разбирает mean_elements в network/base/tle_utils.py, а строки, которые не разбираются, пропускаются. История загрузок TLE хранится целиком, см. Орбитальные данные (TLE).

Строка ответа — [эпоха, a − R⊕, i, e, Ω, ω, updated, R, T, N]: времена в миллисекундах, расхождения в километрах.

Кнопка «Возраст TLE» строит пилу по каждому источнику. В момент updated очередного набора возраст равен updated − эпоха самого свежего набора, что источник прислал до этого момента, и растёт до прихода следующего. updated — время записи строки у нас, а не публикации у источника. update_tle не записывает набор, повторённый без изменений, поэтому повтор возраст не сбрасывает. На dev prod_sync переписывает updated временем синхронизации, и пила там ничего не значит. Шкала логарифмическая: у Celestrak возраст измеряется часами, а у источника, молчавшего месяц, — сотнями часов.

Расхождение прогноза

Второй график вкладки строится из того же ответа. Наборы одного источника сортируются по эпохе. Предыдущий набор продолжается SGP4 до эпохи следующего и сравнивается с состоянием следующего набора на его эпоху, оба в TEME. Разность раскладывается по осям следующей орбиты: R — радиус, T — вдоль орбиты (плюс, когда прогноз впереди), N — нормаль к плоскости. Расчёт — prediction_error в network/base/tle_utils.py. Пары дальше PREDICTION_HORIZON_DAYS (7 суток) не сравниваются, у первого набора источника расхождения нет, и в обоих случаях там null. Источники не смешиваются.

Это согласованность наборов между собой, а не истинная ошибка: эталон — тоже TLE с ошибкой порядка километра, а ковариации в TLE нет, так что полос σ тоже нет. Манёвр между наборами виден как выброс: у МКС подъём орбиты даёт сотни километров вдоль орбиты за сутки, а обычное расхождение у Celestrak — метры–сотни метров. Поэтому шкала логарифмическая, по ней откладывается модуль расхождения, а знак виден в подсказке вместе с горизонтом прогноза.

Автоматический перевод в «Сошёл с орбиты»

Задача confirm_satellite_reentries (ежедневно в 06:00 UTC, логика — в network/base/decay_confirmation.py) переводит спутник из in orbit в re-entered и заполняет decayed. По прогнозной дате она этого не делает никогда: слежение часто ведут до последних сигналов, уточняя TLE уже после того, как прогнозная дата прошла, и такой аппарат должен остаться в планировании. Нужно одно из двух:

  • Space-Track опубликовал подтверждённый сход (MSG_TYPE=Historical; предсказательные TIP-сообщения приходят с MSG_TYPE=Prediction и игнорируются). Записывается реальная дата схода.

  • Ни один источник TLE не публиковал аппарат DECAY_TLE_SILENCE_DAYS дней и прогнозная decay_date уже прошла. Записывается эпоха последнего известного TLE — это нижняя граница. Правило работает и без учётки Space-Track.

Спутники с синтетическим номером NORAD (диапазон 70000–100000, выдаётся generate_norad_id) по первому правилу не переводятся никогда: в каталоге Space-Track тот же номер принадлежит другому объекту.

Каждый автоматический перевод пишется в SatelliteStatusLog (видно в админке): старый и новый статус, сработавшее правило, время. Ручные правки туда не попадают — они и так есть в истории объекта в админке.

Откат. Если перевод оказался ошибочным, верните статус в админке. Строка в SatelliteStatusLog после этого навсегда запрещает автоматике переигрывать решение человека; чтобы снова её включить, удалите эту строку.

У сошедшего аппарата остаются заполненными оба поля: decayed — подтверждённый факт, decay_date — замороженный прогноз, каким он был, пока приходили TLE. Вместе они дают вилку, поэтому прогноз намеренно не перезаписывается.

Будущие наблюдения переведённого спутника не удаляются. Их количество печатает команда confirm_satellite_reentries (см. Management-команды), которая по умолчанию работает в режиме предпросмотра.

Transmitter

Передатчик спутника — то, на что фактически настраивается станция.

Поле

Назначение

uplink_low, uplink_high

Границы диапазона передачи «вверх»

downlink_low, downlink_high

Границы диапазона передачи «вниз»

downlink_mode, uplink_mode

Режим модуляции (Mode)

type

Transmitter, Transceiver, Transponder

status

active, inactive, invalid

iaru_coordination

Статус координации IARU

Частоты валидируются в диапазоне от 0 Гц до 40 ГГц.

Планирование сопоставляет downlink_* передатчика с диапазонами антенн станции — см. Наземные станции и Планирование наблюдений.

Mode

Справочник режимов модуляции (Mode). Загружается начальными фикстурами и пополняется из SatNOGS DB.

Режим важен не только для приёма: от него зависит автоматическая оценка наблюдения — для аналоговых режимов (CW, FM) успешный приём кадра не означает «good», см. Оценка наблюдений.

TransmitterStats

Агрегированная статистика по передатчику. Пересчитывается задачей update_transmitters_stats каждые 15 минут; кэшируемые расчёты — в network/base/stats.py.

Telemetry

Telemetry описывает декодеры телеметрии, применяемые к демодулированным данным спутника.

Подписка на данные

Пользователь подписывается на спутник кнопкой «Подписаться на данные» на его странице. После этого каждый новый кадр спутника приходит на сервер пользователя (например, в ЦУП) по MQTT, сразу после приёма и попытки декодирования. Подписка доступна владельцу станции, которая сейчас в сети. Пока ни одна его станция не в сети, кадры ему не приходят. Подписка при этом остаётся и снова заработает, когда станция выйдет на связь.

Параметры подключения указаны в профиле, в блоке «Подписка на данные спутников»:

Параметр

Значение

Адрес

wss://sonik.space/mqtt (MQTT поверх WebSocket, порт 443)

Логин

subscriber-<id пользователя>

Пароль

Ключ подписки из профиля. Это не API-ключ: он открывает только потоки кадров

Топик

satellites/<sat_id>/frames, по одному на спутник. Шаблоны + и # не принимаются

Каждое сообщение — один кадр, тот же JSON, что элемент GET /api/demoddata/: demodulated_data в HEX и декодированные поля в payload_json. Если у спутника нет декодера, payload_json — пустая строка. Транслируются кадры станций сети и SiDS; суточный импорт из SatNOGS не транслируется. Пропущенное за время отключения брокер хранит в постоянной сессии: подключайтесь с постоянным client_id, clean_session=false и QoS 1. Сессия живёт 7 дней, очередь ограничена 1000 сообщениями. Для большего разрыва есть REST: GET /api/demoddata/?sat_id=…&start=….

import paho.mqtt.client as mqtt

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id="my-mission-control",  # постоянный: по нему брокер хранит очередь
    transport="websockets",
    clean_session=False,
)
client.username_pw_set("subscriber-42", "<ключ подписки>")
client.ws_set_options(path="/mqtt")
client.tls_set()
client.on_connect = lambda c, *_: c.subscribe("satellites/<sat_id>/frames", qos=1)
client.on_message = lambda c, userdata, msg: print(msg.payload.decode())
client.connect("sonik.space", 443)
client.loop_forever()

Устройство на стороне портала описано в Обзор эндпоинтов, раздел «Подписка на данные спутника».

Пополнение базы

Источник

Механизм

SatNOGS DB

Задача fetch_data (раз в час) и одноимённая management-команда — тянет спутники и передатчики с DB_API_ENDPOINT

SatNOGS Network

Задача parse_data_from_satnogs (ежедневно в 01:00 UTC, только при ENVIRONMENT=production) — кадры за прошедшие UTC-сутки

CelesTrak

Через TLE-конвейер, см. Орбитальные данные (TLE)

Вручную

Через /admin/, source остаётся Admin

Обнаружение

check_unknown_satellites — находит объекты, которых нет в базе

Ручной запуск обновления из SatNOGS DB — задача manual_update_db.

Примечание

Если db.satnogs.org недоступен с хостинга, задайте SATNOGS_DB_PROXY_URL — прокси применяется только к запросам к API SatNOGS DB и SatNOGS Network.

Синхронизация с продом

На тестовых стендах каталог отстаёт от sonik.space. Кнопка «Синхронизация с sonik.space» в списке спутников админки (везде, кроме ENVIRONMENT=production) ставит задачу sync_from_prod (network/base/prod_sync.py). В отличие от импорта SatNOGS она перезаписывает данные: спутники, передатчики и последний TLE каждого спутника становятся такими же, как на проде.

  • Источник — публичный API прода, без авторизации. Что API не отдаёт, то не меняется: описания, паспортные данные, launch, trans_decode, params передатчика.

  • Спутник сопоставляется по sat_id. Если такого нет — по NORAD вне синтетического диапазона, когда он у прода один: строка стенда получает sat_id прода.

  • Дубли сливаются. Лишняя строка стенда с тем же NORAD отдаёт спутнику с прода наблюдения, TLE, данные и передатчики, после чего удаляется.

  • Спутники и передатчики, которых на проде нет, остаются. Если прод занял их синтетический NORAD, они получают новый.

  • Последним TLE становится TLE прода с его tle_source. Следующий fetch_tle пересчитывает выбор по приоритетам стенда.

  • Всё пишется одной транзакцией, поэтому недоступный прод не оставит каталог наполовину обновлённым. Прогон занимает около минуты, счётчики пишутся в лог Celery.

Операторы, сети и запуски

  • Operator — эксплуатирующая организация.

  • Network — сеть, к которой отнесён аппарат.

  • Launch — запуск: носитель, площадка, оператор, орбита, статус; is_tracked отделяет запуски сети от мирового календаря Launch Library 2. См. Запуски.

  • SatelliteLaunch — аппарат в составе запуска: тип миссии, статус.

Планирование наблюдений сразу после запуска описано в Планирование наблюдений.