Files
ewsdr/doc/CAT_SLICES.md
T

12 KiB
Raw Blame History

CAT дополнительных слайсов (Slice CAT)

Ветка: feature/slice-cat. Дата: 2026-07-25.

Внешняя программа (WSJT-X, FLDIGI, лог) управляет отдельным слайсом EWSDR как отдельным трансивером — через свой TCP-порт. COM-порты и основной TCP-порт (19090) остаются за главным приёмником (слайс A) и не тронуты.

Каждый порт привязан к определённому слайсу. Флаг Auto TX разрешает команде передачи автоматически назначить этот слайс источником TX; без флага источник выбирается оператором вручную.


1. Архитектура

Протокол не дублируется. TCATEngine — чистый парсер/формировщик поверх плоской записи callback'ов TCATContext, TCATTcpServer — транспорт поверх движка. Значит «CAT слайса» = тот же движок с другим контекстом:

TCATAdapter ──┬── главный контекст ──── TCATEngine ── serial ×4 + TCP 19090   (слайс A)
              ├── TCATSliceEndpoint ─── TCATEngine ── TCP 19091                (слайс B)
              ├── TCATSliceEndpoint ─── TCATEngine ── TCP 19092                (слайс C)
              └── … по слоту на букву (B..G, потолок = WDSPEngine.MAX_SLICES)
Файл Что добавлено
CATSliceEndpoint.pas (новый) TCATSliceEndpoint — контекст поверх FSlices[слот], свой движок + свой TCP-сервер. Константа CAT_SLICE_SLOTS.
CATAdapter.pas Владеет массивом эндпоинтов; ApplySliceSettings поднимает/опускает порты и кладёт Auto TX в контроллер.
CATEngine.pas ZZFL/ZZFH перестали быть заглушками: новые callback'и Get/SetFilterLow/High (у главного nil → прежний дефолт режима). Хелпер SignedPad.
RadioController.pas RequestSliceTx, FSliceAutoTx[слот], слот-хелперы, FilterIdxFor, AGCModeToUI, зеркало DSP-состояния слайса в TCtrlSlice.
Settings.pas CATSliceEnabled/CATSlicePort/CATSliceAutoTX[0..5], персист в корневой секции cat и в device-blob.
SettingsForm.pas Вкладка Slices.
VfoOverlay.pas, PanafallPanel.pas Бейдж AutoTX вместо TX на флаге слайса.

Привязка к слоту, а не к Id слайса. Слот 0..5 = буква B..G. Слайс на слоте создаётся/удаляется в любой момент, порт при этом остаётся поднятым: пока слайса нет, эндпоинт отвечает «радио выключено» (PS0;) и игнорирует команды. Настройка (порт, Auto TX) переживает удаление слайса.

Потокобезопасность — как у главного адаптера: геттеры читают состояние в CAT-потоке, сеттеры кладут параметр под локом эндпоинта и маршалятся в поток контроллера через Invoke (GUI = TThread.Synchronize).


2. Auto TX и защита передачи

Вся логика — в одной точке: TRadioController.RequestSliceTx(SliceId, On_), исполняется в потоке контроллера, поэтому проверка «уже в эфире» и смена TX-источника атомарны относительно GUI.

TX; / ZZTX1; на порту слайса:

  1. Слайса на слоте нет → игнор.
  2. Кто-то уже передаёт (другой слайс, главный VFO, кнопка MOX/TUNE, аппаратная PTT) → игнор. Перехвата эфира нет никогда — передаёт всегда только один слайс.
  3. Режим слайса DMR → игнор (AMBE-передатчика нет).
  4. Слайс не выбран TX-источником:
    • Auto TX включёнSetTxSlice(слайс), затем SetMOX(True);
    • Auto TX выключен → игнор (оператор назначает источник руками — бейдж TX на флаге слайса).
  5. Слайс уже TX-источник → просто SetMOX(True).

RX; / ZZTX0;: снимает передачу только свою — если TX-источник именно этот слайс. RX; на порту слайса C не обрывает эфир слайса B. TUNE снимается тоже.

Выбор TX-источника после отпускания PTT остаётся на этом слайсе. Бейдж на флаге это показывает.

GetTransmitting (для TX/IF/ZZTX) на порту слайса = «передаёт этот слайс». Чужой эфир виден как приём — иначе управляющее ПО решило бы, что его PTT прошла.


3. Что поддерживает CAT-порт слайса

Команды Маппинг на слайс
FA ZZFA (VFO A) TuneSliceInBand — свободно в пределах включённого диапазона, см. §3.1
FB ZZFB (VFO B) теневое значение: читается/пишется, эфир не двигает (второго VFO у слайса нет)
MD ZZMD ZZMA SetSliceMode
FW SH SL ZZFI (индекс фильтра) FilterIdxFor/FilterBWForSetSliceModeBW
ZZFL ZZFH (кромки, Гц) FilterLo/FilterHiSetSliceFilter
GT ZZGT (AGC) AGCSetSliceAGCMode (AGCModeToUI/FromUI)
AG ZZAG (громкость) VolumeSetSliceVolume
ZZNR ZZNB ZZNS ZZNT (DSP) SetSliceDSP (состояние зеркалится в TCtrlSlice)
SQ ZZSQ ZZSO (squelch) FMSQOn/FMSQLevelSetSliceFMSquelch
SM ZZSM SliceSMeter
TX RX ZZTX RequestSliceTx (см. §2)
PS слайс существует и радио запущено
IF ID составной статус/идентификатор по данным слайса
UP DN RU RD шаг ±10 Гц по частоте слайса
PC ZZPC (мощность), ZZBS (диапазон, read) глобальные — передатчик один на всё радио

3.1 Частота слайса: свобода в пределах диапазона

TRadioController.TuneSliceInBand(Id, TargetHz) — единственная точка перестройки слайса из CAT:

  1. Другой диапазон → отказ. Границы берутся у включённого сейчас диапазона: активный трансвертер → его FreqBegin/FreqEnd (2 м = 144…146 МГц), иначе band-план бэкенда (BoardUtils.BandEdges). Стоим на 2 м, программа просит 145.500 → 144.174 — можно; 7.074 МГц — команда отбрасывается. Смена диапазона остаётся решением оператора.
  2. Цель внутри захваченной полосы (SliceFitsCapture) → просто SetSliceTarget.
  3. Цель внутри диапазона, но вне окна DDC → окно переезжает центром на цель:
    • слайс на дополнительном пане (свой DDC) — свободно (SetPanDDCFreq + Changed(rfPanFreq), UI перекладывает флаги/шапку/зум-бар);
    • слайс на пане 0 — окно общее с главным приёмником, поэтому центр ставится ПОСЕРЕДИНЕ между целью и активным VFO: так в окно попадают оба. Если они дальше, чем окно (|цель VFO| > 0.95·полоса), команда отбрасывается — перестраивать главный приёмник ради слайса нельзя.

Практический вывод: чтобы внешняя программа гуляла по всему диапазону, слайс лучше держать на своём пане (дополнительный DDC). На пане 0 её свобода — в пределах общего окна вокруг главного VFO.

Осознанно не проброшено (движок ответит валидной заглушкой): split/FR/FT (второго VFO нет), смена диапазона BU/BD/ZZBS-set (диапазон — свойство пана, на котором живёт слайс), CTUN, AGC-T, CTCSS/репитер/FM-шаг (настройки главного тракта), Andromeda (панель одна и висит на главном).


4. Настройки

Вкладка Slices: на каждый слайс B..G — Enable CAT TCP server, Port (по умолчанию 19091, 19092, …), Auto TX, Audio out, Audio in.

4.1 Аудио слота

Audio out/Audio in — это устройство слота (буквы), а не шаблон:

  • смена настройки применяется немедленно к слайсу, который стоит на слоте сейчас (TRadioController.SetSliceSlotAudio);
  • слайс, который встанет на слот позже (создание/восстановление из персиста), получает то же устройство — оно сильнее того, что попросил вызывающий AddSlice;
  • выбор во вкладке AUD флага слайса пишется обратно в настройку слота (SetSliceDevice/SetSliceInDeviceMainForm.StoreSliceSlotAudio), так что вкладка всегда показывает реальное устройство — одна истина, два места правки;
  • пусто (Default (app output) / None (app input)) — как раньше: общий выход приложения и общий вход.

Хранятся имена PortAudio-устройств, а не индексы: порядок перечисления между запусками плавает. Имя отсутствующего сейчас устройства (кабель не поднят) не теряется — комбо добавляет его в список и держит выбранным.

  • Хранятся вместе с остальным CAT: корневая секция cat (slice_en_N, slice_port_N, slice_autotx_N) + device-blob (cat_slice_*), не привязаны к MAC — доступны до подключения железа.
  • Порт, совпадающий с основным TCP-портом или с портом слайса меньшей буквы, не поднимается (иначе второй bind просто упал бы молча).
  • Правка одной строки не рвёт живые сессии остальных: эндпоинт перезапускается только при реальном изменении своего enable/порта.

5. Проверка

# слайс B на 19091
printf 'ZZFA;' | nc -q1 127.0.0.1 19091     # частота слайса
printf 'ZZMD;' | nc -q1 127.0.0.1 19091     # режим
printf 'TX;'   | nc -q1 127.0.0.1 19091     # PTT (нужен Auto TX либо выбранный TX-слайс)
printf 'RX;'   | nc -q1 127.0.0.1 19091

Статус: собрано (lazbuild --ws=qt6 + build-ewsdrd.sh), на эфире и на экране не проверено.