Соглашения в .grc¶
Все флоуграфы репозитория повторяют один и тот же набор решений. Ниже — то, что нужно воспроизвести в новом флоуграфе, чтобы он корректно работал с диспетчером.
Именование и режим генерации¶
Запускаемые флоуграфы |
Иерархические блоки |
|
|---|---|---|
Каталог |
|
|
|
|
|
|
|
|
Результат сборки |
|
|
id должен совпадать с именем файла: по нему grcc называет выходной скрипт, а
диспетчер ищет этот скрипт в PATH.
Общий набор параметров¶
Почти каждый запускаемый флоуграф объявляет один и тот же набор блоков
parameter. Копируйте его целиком из generic/example_flowgraph.grc или из
ближайшего по смыслу декодера:
antenna, bb_freq, bw, dc_removal, decoded_data_file_path, dev_args,
enable_iq_dump, file_path, gain, gain_mode, iq_file_path, lo_offset,
other_settings, ppm, rigctl_host, rigctl_port, rx_freq, samp_rate_rx,
soapy_rx_device, stream_args, tune_args, udp_dump_host, udp_dump_port,
waterfall_file_path, zmq_base_port
Плюс, по необходимости, baudrate и framing.
zmq_base_port (intx, 16887) обязателен: диспетчер передаёт его всегда, а
grcc генерирует parse_args(), поэтому флоуграф без этого параметра
завершится с кодом 2.
Имена обязаны совпадать с аргументами client_argument_parser() —
см. Контракт 2: имена аргументов.
Доплеровская компенсация¶
Источник сдвига объявляется переменной:
variable_rigctl = satnogs.rigctl_doppler_correction.make(rx_freq, Hamlib.RIG_MODEL_NETRIGCTL)
Адрес rigctld нельзя задать параметром блока — он подставляется фрагментом
кода. Блок snippet в секции main_after_init:
self.variable_rigctl.set_param("rig_pathname", str(self.rigctl_host) + ":" + str(self.rigctl_port))
Этот фрагмент дословно скопирован во все флоуграфы. Забыть его — значит получить попытку подключения к адресу по умолчанию.
Приёмник настраивается на rx_freq - lo_offset, обратный сдвиг делает сам блок
satnogs_doppler_compensation.
Децимация и частоты¶
Стандартная формула:
audio_samp_rate = 48000
decimation = max(4, satnogs.find_decimation(baudrate, 2, audio_samp_rate))
Для флоуграфов с фазовой манипуляцией добавляется число отсчётов на символ:
sps = 4
decimation = max(4, satnogs.find_decimation(baudrate, 2, audio_samp_rate, sps))
Нижняя граница max(4, …) обязательна: дальше по тракту встречается
целочисленное деление decimation // 2 и decimation // sps, и при малой
децимации оно даёт ноль. Именно это чинил коммит «Fix decimation for USP».
Выходная частота доплер-компенсации (out_samp_rate) различается по семействам:
Семейство |
|
Примеры |
|---|---|---|
Аудио-режимы |
|
|
Построчная развёртка |
|
|
Режимы с бодрейтом |
|
|
audio_samp_rate = 48000 объявлен и в APT, и в SSTV_PD120, но уходит только в
последний ресемплер аудио (rrate: audio_samp_rate / (4*4160*4)). Сама
доплер-компенсация идёт на 66560.
Эта же частота уходит в водопад, IQ-дамп и UDP-поток. Поэтому полоса и
разрешение водопада зависят от режима (БПФ везде на 1024 точки: бин от 46.9 Гц
при 48 кГц до 195 Гц при 200 кГц у PHASMA), а частоту дискретизации IQ-файла
нужно считать самому: в имени файла её нет. Геометрию водопад пишет в заголовок
.dat, и клиент с 2026-09-12 отдаёт её порталу вместе с нормированным на бин
уровнем шума (client_metadata.signal.noise_dbhz) — сравнивать noise_db
между режимами напрямую нельзя.
Три обязательных ответвления¶
Каждый запускаемый флоуграф обязан отдавать:
Все три адреса выводятся из одного параметра zmq_base_port — литеральных
tcp://127.0.0.1:1688x в .grc быть не должно:
Аудио, zmq_base_port + 2. zeromq_pub_sink типа float, адрес
'"tcp://127.0.0.1:" + str(zmq_base_port + 2)'. Подписчик жёстко пишет
48000 Гц моно — тракт обязан привести звук именно к этой частоте, обычно
pfb_arb_resampler. Рассогласование ничем не проверяется.
IQ, zmq_base_port + 1. blocks_complex_to_interleaved_short с масштабом
16384 и vlen=2, дальше zeromq_pub_sink типа short, адрес
'"tcp://127.0.0.1:" + str(zmq_base_port + 1)'.
Кадры, zmq_base_port. Только для флоуграфов с декодером:
zeromq_pub_msg_sink, адрес '"tcp://127.0.0.1:" + str(zmq_base_port)'.
Плюс водопад (satnogs_waterfall_sink, БПФ 1024, 10 строк в секунду) и
network_udp_sink (complex, полезная нагрузка 1472 байта) на
udp_dump_host:udp_dump_port.
Примечание
Смещения не взаимозаменяемы: сток типа float обязан быть на +2, типа
short — на +1. Перепутать их значит отправить аудио подписчику IQ. Это
проверяет tests/test_flowgraphs.py.
Рудиментарные параметры¶
Четыре параметра объявлены почти во всех флоуграфах, но ни к чему внутри не подключены:
decoded_data_file_pathfile_pathiq_file_pathenable_iq_dump
Пятый, doppler_correction_per_sec, снят 2026-09-12: его не читал ни один блок,
а диспетчер принимает и обнуляет его ради старых клиентов, которые продолжают
слать --doppler-correction-per-sec.
Запись файлов давно переехала в процессы-подписчики, а enable_iq_dump
обрабатывает диспетчер. Параметры остались только затем, чтобы флоуграф не упал,
когда диспетчер передаст их в общем цикле --ключ=значение.
Не документируйте их как рабочие и не пытайтесь чинить «неработающий»
--iq-file-path внутри .grc — он работает, просто в другом процессе.
Единственный путь, который действительно обрабатывает флоуграф, —
waterfall_file_path.
Общий фронтенд¶
Приёмный тракт до демодулятора одинаков во всех флоуграфах: soapy_source,
satnogs_doppler_compensation, satnogs_waterfall_sink, IQ в ZMQ
(blocks_complex_to_interleaved_short + zeromq_pub_sink типа short) и
network_udp_sink. Источник истины — generic/fm.grc. Правка параметра
тракта — это правка в нём и одна команда:
python3 tools/sync_frontend.py # разложить по остальным графам
python3 tools/sync_frontend.py --check # только сравнить (так делает тест)
Скрипт переписывает секцию parameters: каждого фронтенд-блока, который в
целевом графе уже есть; блоки и соединения он не добавляет и не удаляет, YAML не
переформатирует — дифф построчный. У каждого графа свои остаются out_samp_rate
доплер-компенсации и samp_rate водопада (полоса режима) и compensate у
iq_receiver. Ссылка на параметр, которого в целевом графе нет, — ошибка
проверки: grcc упал бы на сборке. test_frontend_matches_golden в
tests/test_flowgraphs.py держит графы в согласии с золотым.
Из soapy_source при этом вычищены 16 полей второго канала (nchan: '1', в
кодоген они не попадали, GRC подставляет дефолты block.yml); поля усиления
под конкретные devname оставлены — при devname: custom они тоже мертвы, но
трогать их без прогона на железе незачем.
Остальная копипаста живёт за фронтендом, и её при изменении по-прежнему надо править во всех файлах:
Что |
Где |
|---|---|
фрагмент |
все флоуграфы |
|
каждый |
формула |
каждый |
константы контура ЧМн (задержка 512, окно 1024, масштаб 1/1024) |
все ЧМн-декодеры |
словарь |
по-разному в трёх файлах: 1, 3 и 6 записей |
Проверка после правки¶
grcc satellites/my_decoder.grc -o /tmp # генерация без полной сборки
python3 /tmp/satnogs_my_decoder.py --help # список параметров
Второй командой удобно сверить, что набор параметров совпадает с тем, что шлёт диспетчер.