Архитектура¶
Слои¶
soniks-client-new
│ один вызов: flowgraph_dispatcher --mode <MODE> …
▼
┌─────────────────────────────────────────────────────────────┐
│ flowgraph_dispatcher.py ← Python, слой управления │
│ • таблица режимов → скрипт │
│ • переписывание аргументов │
│ • запуск и остановка 2–4 процессов │
└───────┬──────────────────────────────────┬──────────────────┘
│ subprocess │ subprocess ×3
▼ ▼
┌────────────────────┐ ┌──────────────────────┐
│ satnogs_<mode>.py │ ──ZMQ──▶ │ zmq_frame_decoder_sub│
│ (сгенерирован grcc │ │ zmq_iq_sub │
│ из .grc) │ │ zmq_audio_sub │
└────────────────────┘ └──────────┬───────────┘
▼ файлы
Слои общаются тремя способами, и все три — контракты, которые легко сломать незаметно:
Аргументы командной строки между диспетчером и флоуграфом;
ZMQ-сокеты на localhost между флоуграфом и подписчиками;
Файлы между подписчиками и клиентом станции.
Контракт 1: .grc — источник правды¶
Файлы .py флоуграфов генерируются grcc при сборке и не коммитятся
(generic/*.py и satellites/*.py перечислены в .gitignore). Править нужно
.grc, а не сгенерированный Python: любая правка .py исчезнет при следующей
сборке.
Каждый .grc — это YAML. Значимые для нас части:
блоки
id: parameter— становятся аргументами--имя-через-дефиссгенерированного скрипта;блоки
id: variable— вычисляются внутри, снаружи недоступны;блок
id: snippet— вставка произвольного Python в заданную точку (используется, чтобы передать блоку доплер-компенсации адресrigctld);секция
connections— рёбра графа.
Списки собираемых файлов в CMakeLists.txt явные, не по маске. Новый .grc,
не добавленный в set(flowgraphs …), просто молча не соберётся.
Контракт 2: имена аргументов¶
Диспетчер не знает, какие параметры есть у конкретного флоуграфа. Он берёт все
свои аргументы со значением не None и передаёт их как есть:
args = [script] + [f'--{key}={value}' for key, value in self.parameters.items() if value is not None]
Отсюда жёсткое требование: имя аргумента в client_argument_parser() должно
совпадать с именем блока parameter в .grc (дефисы против подчёркиваний —
единственное допустимое расхождение, grcc делает эту замену сам).
Что произойдёт при расхождении:
аргумент есть у диспетчера, но нет во флоуграфе →
grccгенерируетparse_args(), поэтому флоуграф немедленно завершается с кодом 2;параметр есть во флоуграфе, но нет у диспетчера → используется значение по умолчанию из
.grc, тихо и без предупреждения.
Второй случай коварнее: наблюдение выглядит успешным, а декодер работает не на
той скорости. Оба ловит tests/test_dispatcher.py — он строит командную строку
для каждого режима и сверяет её с блоками parameter целевого .grc.
Контракт 3: ZMQ и владение файлами¶
Флоуграф не пишет ни кадры, ни аудио, ни IQ — он публикует их в сокеты.
Записью владеют подписчики. Единственное исключение — водопад: его пишет сам
флоуграф блоком satnogs_waterfall_sink.
Практическое следствие: параметры decoded_data_file_path, file_path,
iq_file_path и enable_iq_dump объявлены почти в каждом .grc, но ни к чему
внутри не подключены. Они существуют только для того, чтобы флоуграф не упал,
когда диспетчер передаст их в общем цикле. См.
Рудиментарные параметры.
Порты и форматы описаны в Выходные данные. Все три выводятся из одного числа:
--zmq-base-port у диспетчера и параметр zmq_base_port в .grc. Кадры уходят
на base, IQ на base + 1, аудио на base + 2:
address: '"tcp://127.0.0.1:" + str(zmq_base_port + 2)'
Литеральных адресов в .grc быть не должно — tests/test_flowgraphs.py
проверяет это и заодно сверяет смещение с типом стока.
Порядок запуска¶
Подписчики стартуют раньше флоуграфа. Причина — семантика ZMQ pub/sub: издатель не буферизует сообщения для ещё не подключившихся подписчиков, и всё, что опубликовано до подписки, теряется безвозвратно. Порядок:
подписчик кадров (если у режима есть декодер);
подписчик IQ (если
--enable-iq-dump);подписчик аудио (всегда);
флоуграф.
Дальше main() опрашивает процесс флоуграфа каждые 250 мс и возвращает его код
завершения. Остановка — по SIGINT или SIGTERM: сначала флоуграф, затем все
подписчики разом, каждому 2 с на выход и SIGKILL при отказе; подробности в
Завершение.
Что где лежит¶
Путь |
Назначение |
Когда трогать |
|---|---|---|
|
флоуграфы общего назначения |
новая модуляция или общий протокол |
|
декодеры под конкретные спутники |
новый спутник |
|
генераторы сигнала, вне сборки |
отладка декодера без эфира |
|
раскладка общего фронтенда из |
правка приёмного тракта |
|
инварианты |
вместе с любой правкой кода |
|
таблицы режимов, разбор аргументов, управление процессами |
новый режим, новый аргумент |
|
запись кадров, IQ, аудио |
изменение формата выходных файлов |
|
общие разборщики аргументов, обработчик сигналов, метки времени |
общая для подписчиков логика |
|
явные списки собираемых файлов |
добавление или удаление |
|
метаданные пакета |
изменение зависимостей |
|
эта документация |
вместе с любым изменением поведения |
Чего в проекте нет¶
Python-упаковки (
setup.py,pyproject.toml) — точки входа создаются символическими ссылками из CMake;генерации API-документации из кода;
сборки
.debв CI.
Тесты (tests/) и линтер (flake8, настройки в setup.cfg) есть и выполняются
в CI. Тесты работают на чистом PyYAML, без GNU Radio.