Соглашения в .grc

Все флоуграфы репозитория повторяют один и тот же набор решений. Ниже — то, что нужно воспроизвести в новом флоуграфе, чтобы он корректно работал с диспетчером.

Именование и режим генерации

Запускаемые флоуграфы

Иерархические блоки

Каталог

generic/, satellites/

test_flowgraphs/

generate_options

no_gui

hb

id

satnogs_<имя файла>

satnogs_<имя файла>

Результат сборки

satnogs_<имя>.py в bin/

.py + .block.yml в персистентном каталоге GNU Radio

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) различается по семействам:

Семейство

out_samp_rate

Примеры

Аудио-режимы

audio_samp_rate = 48000

fm.grc

Построчная развёртка

4*4160*4 = 66560

noaa_apt_decoder.grc, sstv_pd120_demod.grc

Режимы с бодрейтом

baudrate × decimation

fsk_ax25.grc, bpsk.grc, Geoscan, USP

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_path

  • file_path

  • iq_file_path

  • enable_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 они тоже мертвы, но трогать их без прогона на железе незачем.

Остальная копипаста живёт за фронтендом, и её при изменении по-прежнему надо править во всех файлах:

Что

Где

фрагмент rig_pathname

все флоуграфы

audio_samp_rate = 48000

каждый .grc

формула decimation

каждый .grc с бодрейтом

константы контура ЧМн (задержка 512, окно 1024, масштаб 1/1024)

все ЧМн-декодеры

словарь available_framings

по-разному в трёх файлах: 1, 3 и 6 записей

Проверка после правки

grcc satellites/my_decoder.grc -o /tmp        # генерация без полной сборки
python3 /tmp/satnogs_my_decoder.py --help     # список параметров

Второй командой удобно сверить, что набор параметров совпадает с тем, что шлёт диспетчер.