# flowgraph_dispatcher Единственный бинарник, который вызывает клиент станции. Выбирает флоуграф по режиму, переписывает часть аргументов, поднимает процессы и следит за ними. ``` flowgraph_dispatcher --mode [аргументы приёмника, rigctl и путей] ``` ## Аргументы командной строки Все аргументы необязательны и по умолчанию равны `None`. Значение по умолчанию задаёт не диспетчер, а сам флоуграф: аргумент со значением `None` просто не передаётся дальше, и `.grc` использует своё значение. Каждый аргумент уходит во флоуграф как `--ключ=значение` без изменений, поэтому имя параметра в `.grc` обязано совпадать с именем аргумента (дефисы против подчёркиваний — единственная разница). ### Управление режимом | Аргумент | Тип | Назначение | |---|---|---| | `--mode` | строка | режим модуляции, см. [](modes.md). Во флоуграф **не передаётся** | | `--baud` | строка | скорость передачи; превращается в `--baudrate` либо в `--wpm` для CW. Во флоуграф под своим именем **не передаётся** | | `--framing` | строка | принимается для совместимости с клиентом и **игнорируется**: фрейминг задаёт таблица режимов. Переданное значение вызывает предупреждение | ### Приёмник (SoapySDR) | Аргумент | Тип | Назначение | |---|---|---| | `--soapy-rx-device` | строка | строка устройства, например `driver=rtlsdr` | | `--samp-rate-rx` | float | частота дискретизации приёмника, Гц | | `--rx-freq` | float | центральная частота приёма, Гц | | `--lo-offset` | строка | смещение гетеродина, Гц (по умолчанию во флоуграфах 100 кГц) | | `--bb-freq` | float | частота CORDIC основной полосы | | `--bw` | float | полоса аналогового фильтра, Гц | | `--gain` | float | суммарное усиление, дБ | | `--gain-mode` | строка | режим АРУ устройства (`Overall` по умолчанию) | | `--antenna` | строка | имя антенного входа устройства | | `--ppm` | float | поправка опорного генератора, ppm | | `--dc-removal` | int | автоматическое подавление постоянной составляющей | | `--dev-args` | строка | аргументы устройства SoapySDR | | `--stream-args` | строка | аргументы потока | | `--tune-args` | строка | аргументы перестройки | | `--other-settings` | строка | прочие настройки канала | ### Доплеровская компенсация | Аргумент | Тип | Назначение | |---|---|---| | `--rigctl-host` | строка | адрес `rigctld` | | `--rigctl-port` | int | порт `rigctld` (обычно 4532) | | `--doppler-correction-per-sec` | int | число коррекций в секунду | ### Выходные файлы | Аргумент | Тип | Кто использует | |---|---|---| | `--waterfall-file-path` | строка | сам флоуграф, блок `satnogs_waterfall_sink` | | `--decoded-data-file-path` | строка | подписчик `zmq_frame_decoder_sub`, как **префикс** имени | | `--file-path` | строка | подписчик `zmq_audio_sub`, как **полный путь** к OGG | | `--iq-file-path` | строка | подписчик `zmq_iq_sub`, как **префикс** имени | | `--enable-iq-dump` | int | диспетчер: `1` включает подписчика IQ | ### Живой поток | Аргумент | Тип | Назначение | |---|---|---| | `--udp-dump-host` | строка | адрес назначения UDP-потока IQ | | `--udp-dump-port` | int | порт назначения | ### Каналы ZMQ | Аргумент | Тип | Назначение | |---|---|---| | `--zmq-base-port` | int | первый из трёх портов ZMQ, по умолчанию 16887. Кадры уходят на него, IQ на `+1`, аудио на `+2`. Передаётся во флоуграф как `--zmq-base-port`, флоуграф собирает те же три адреса сам | Аргумент нужен, чтобы поднять две станции на одной машине: второй экземпляр запускается с `--zmq-base-port=17887`. :::{note} `--waterfall-file-path` — единственный путь, который обрабатывает сам флоуграф. Три остальных пути ему передаются, но ни к чему не подключены: запись файлов живёт в процессах-подписчиках. См. [](dev/grc-conventions.md#рудиментарные-параметры). ::: ## Правила переписывания аргументов Порядок действий в конструкторе диспетчера: 1. Скопировать все аргументы как есть. 2. Запомнить `mode`, `baud` и `framing`, затем **обнулить** `mode`, `baud`, `wpm` и `framing`, чтобы они не ушли во флоуграф. 3. Неизвестный режим заменить на `UNKNOWN`. 4. Взять скрипт из таблицы режимов. 5. Для `UNKNOWN` без `--baud` подставить 48000. 6. Если `--baud` задан и режим принимает бодрейт: - `CW` → добавить `wpm`, остальные → добавить `baudrate`; - `FSK` на 50000 или 400000 бод → подменить скрипт на PHASMA. 7. `GFSK Pkst` → добавить `frame-size=192`. 8. Если у режима собственный фрейминг → выставить `framing` значением из таблицы. ### Предупреждения Раньше подмены происходили молча — теперь каждая пишет жёлтую строку в `stderr`: - `--framing=<значение> ignored, framing follows --mode`; - `--baud=<значение> ignored, mode <режим> has no baudrate` — для `AFSK`, `DUV`, `APT`, `FM`, `SSTV`, `SSTV_PD120`; - `FSK at 50000 baud decoded as PHASMA`; - `unknown mode '<строка>', falling back to UNKNOWN`; - `ignored unknown arguments: <список>`. ### Аргументы, которых диспетчер не знает Разбор идёт через `parse_known_args()`, а не `parse_args()`. Клиент новее диспетчера больше не глушит станцию: раньше лишний аргумент завершал диспетчер с кодом 2 **до** запуска графа, и в лог станции это приходило как проход, закончившийся досрочно, — без единой строки об ошибке. Теперь это жёлтое предупреждение, а наблюдение продолжается. `--norad-cat-id` и `--lo-transverter` парсер принимает явно, но никуда не передаёт: ни один `.grc` их не объявляет, а `flowgraph_argv()` передаёт всё вслепую. Это позволяет клиенту слать их уже сегодня. `--norad-cat-id` начнёт что-то значить, когда будет подключён gr-satellites: он выбирает декодер по NORAD через satyaml. :::{note} Обнуление `framing` — не косметика. `grcc` генерирует `parse_args()`, поэтому неизвестный аргумент завершает флоуграф с кодом 2. Параметра `framing` нет в 13 из 20 флоуграфов, и раньше `--framing` от клиента ронял их все. ::: Подмена `FSK` → PHASMA происходит после подстановки бодрейта, поэтому PHASMA получает исходные 50000 или 400000. ## Жизненный цикл процессов Подписчики стартуют **раньше** флоуграфа — иначе первые сообщения ZMQ пропадут: 1. если режим отдаёт кадры → `zmq_frame_decoder_sub --zmq-dump-host=127.0.0.1 --zmq-dump-port= --decoded-data-file-path=…` 2. если `--enable-iq-dump` истинно → `zmq_iq_sub --zmq-dump-host=127.0.0.1 --zmq-dump-port= --iq-file-path=…` 3. **всегда** → `zmq_audio_sub --zmq-dump-host=127.0.0.1 --zmq-dump-port= --file-path=…` 4. сам флоуграф: `satnogs_<имя>.py --ключ=значение …` Здесь `` — значение `--zmq-base-port`, по умолчанию 16887. См. [](outputs.md). Затем `main()` крутится в цикле, опрашивая процесс флоуграфа каждые 250 мс. ### Завершение `SIGINT` (Ctrl+C) и `SIGTERM` обрабатываются диспетчером одинаково. Сначала `SIGINT` получает флоуграф, и диспетчер дожидается именно его: пока флоуграф жив, подписчики продолжают дочитывать сокеты. Затем `SIGINT` уходит всем подписчикам сразу, и только потом диспетчер ждёт каждого из них. Ожидание — 2 секунды (`TERMINATION_GRACE_SEC`), дальше `SIGKILL`. В худшем случае завершение занимает 4 секунды. Сигнал и ожидание разнесены намеренно. Пока диспетчер сигналил и ждал каждого подписчика по очереди, две секунды первого из них тратились на общем с остальными процессоре, и подписчик аудио — единственный, у кого закрытие делает реальную работу (сброс OGG-кодировщика) — под нагрузкой не успевал и получал `SIGKILL` посреди записи. Воспроизводилось `tests/e2e_check.py` примерно раз на три прогона. Если процесс пришлось убить, в вывод идёт предупреждение — например, «Flowgraph script did not close properly! Some data might be missing». Остановка гарантирована блоком `try/finally` в `main()`, а не сборщиком мусора: подписчики гасятся даже при исключении в самом диспетчере. ### Код возврата Диспетчер возвращает код завершения флоуграфа. Убитый сигналом флоуграф даёт `128 + номер сигнала` по соглашению оболочки. Ноль означает, что флоуграф отработал и корректно остановился, — по коду возврата можно отличить отказ приёма от пустого пролёта. ## Ошибки запуска Если скрипт флоуграфа не удалось запустить (нет файла, нет прав), диспетчер гасит уже поднятые подписчики и выходит с сообщением: ``` Error starting flowgraph: <причина> Aborting Observation! ``` Разбор типовых причин — [](troubleshooting.md).