Files
ewsdr/doc/CAT_STATUS.md
ew8bakandClaude Opus 5 4081ea0e57 fix(cat): нечисловое поле = индекс 0 ещё в семнадцати командах, ZZGT и ZZBE
Всё найдено новым стендом (test/cat), все правки им же и закрыты.

Нечисловое поле. Правку «TryStrToInt вместо StrToIntDef(s, 0)» однажды получили
пять команд (ZZAU, ZZBP, ZZBM, ZZBS, ZZFI), а остальные остались как были — в
том числе КЕНВУДОВСКИЕ ДВОЙНИКИ тех же самых величин, ходящие в те же сеттеры:
FWxxxx;, SHxx; и SLxx; ставили фильтр 0 (тот же индекс, что чинили у ZZFI),
AG0xxx; и SQ0xxx; — громкость и порог шумоподавителя в ноль, GTxxx; — АРУ в
FAST, PCxxx; и ZZPCxxx; — мощность в ноль. Всего семнадцать команд: CN, FW, GT,
NB, PC, SH/SL, AG, SQ и ZZAG, ZZAR, ZZNA, ZZNB, ZZNR, ZZPC, ZZSQ, ZZST, ZZTB.
Разбор везде приведён к идиоме ZZFL/ZZFH: не число — ошибка формата.

Не тронуты три места, где мягкий разбор безвреден или намеренный: FR сам
сверяет поле с '0'/'1' до преобразования; MD и ZZMD от нечислового получают 0, а
установка идёт от 1, то есть ничего не делают; ZZOS трактует мусор как симплекс
по эталону (default в String2OffsetDirection), и клиенты на это рассчитывают.

ZZGT. Опрос отвечал тремя цифрами (как кенвудовская GT), а установка принимала
ровно один символ: клиент, прочитавший «ZZGT000;» и написавший его назад,
получал «?;» — а читать значение и писать его обратно умеет любой логгер.
Принимаются обе ширины, поле разбирается строго.

ZZBE. Формы были перевёрнуты: опрос «ZZBE;» отвечал «?;», а установка
«ZZBE01;» возвращала данные ('1'), причём саму установку никто не исполнял.
Вся семья «сдвиг VFO на nn шагов» (ZZAD ZZAE ZZAF ZZBF ZZSG ZZSH) — однострочные
заглушки в таблице, и документ числит ZZBE среди нереализованных; приведено к
ним.

ZZEB. Выдача клампится туда же, куда и приём: сетка эквалайзера бывает только
трёх- или десятиполосной. Иначе опрос отдавал число полос, которое разбор той же
команды отвергал.

Стенд: 50 проверок, все зелёные; на коде до этого коммита падают 19.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:21:38 +03:00

34 KiB
Raw Permalink Blame History

CAT-система EWSDR — статус реализации

Ветка разработки: feature/cat-tx-cw. Дата последнего обновления: 2026-08-17.

Документ описывает текущее состояние CAT-подсистемы EWSDR: архитектуру, полный перечень реализованных команд, осознанные заглушки и план дальнейших работ. За эталон протокола взят Thetis / PowerSDR (CATParser.cs, CATCommands.cs, CATStructs.xml) — диалект Kenwood TS-2000 + расширения ZZ*.


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

CAT-транспорты ──► TCATEngine (парсер протокола) ──► TCATContext ──► TCATAdapter ──► TRadioController
  serial (4×)         CATEngine.pas                  (callbacks)      CATAdapter.pas      (ядро)
  TCP-сервер          разбор/формат команд                            мост, маршалинг
Файл Назначение
CATEngine.pas (~2740 строк) Движок протокола: разбор <PREFIX><SUFFIX>;, формирование ответов, реализации всех команд Kenwood + ZZ*. Зависит только от RTL.
CATAdapter.pas (~910 строк) Мост к TRadioController. Строит TCATContext: геттеры читают поля контроллера напрямую (CAT-поток, read-only), сеттеры/команды маршалятся в поток контроллера через FController.Invoke. Владеет движком + транспортами.
CATSerial.pas (~330 строк) Менеджер до 4 последовательных портов (виртуальные COM-пары для rigctld/N1MM/WSJT-X).
CATTcp.pas (~507 строк) TCP-сервер (Thetis-совместимый), управление клиентами по GUID (ZZGA/ZZGR).

1.1 Принципы

  • CAT — равноправный клиент контроллера наряду с WebAdapter и GUI. Никаких обращений к MainForm. Только TRadioController.
  • Потокобезопасность. Геттеры читают поля контроллера напрямую (атомарное чтение в CAT-потоке). Сеттеры пишут параметр в scratch-поля (FSyncFreq/FSyncInt/FSyncBool) и зовут FController.Invoke(@SyncXxx) — GUI исполняет через TThread.Synchronize, демон — своей очередью.
  • TCATContext — плоская запись callback'ов (Get*/Set*/Do*). Чтобы пробросить новую возможность контроллера в CAT, достаточно: (1) добавить callback в запись, (2) реализовать в адаптере геттер/сеттер/sync, (3) зарегистрировать в TCATAdapter.Create, (4) переписать тело команды в движке.
  • Формат команд:
    SET:  <PREFIX><SUFFIX>;     (suffix = значение)
    GET:  <PREFIX>;             (suffix пустой)
    Resp: <PREFIX><VALUE>;
    Err:  ?;  (неверный формат)  |  E;  (ошибка выполнения)
    
    Расширенные ZZ* дублируют Kenwood-команды и добавляют то, чего в Kenwood нет.

1.2 Статистика покрытия (ZZ-команды)

Кол-во
Всего ZZ* в Thetis 315
Реально реализовано в EWSDR ~120
Заглушки (отвечают валидным dummy, функции нет) ~190
Совсем не обрабатываются (вернут ?;) 5

Подписи всех 315 команд сверены с CATCommands.cs (см. §2.5) — комментарию у заглушки теперь можно верить. Оставшиеся заглушки в большинстве закрыть нечем: в EWSDR физически отсутствует соответствующий тракт (см. §4).

1.3 Осознанные отклонения от Thetis

Три места, где EWSDR намеренно расходится с эталоном. Больше таких нет.

Команда У Thetis У нас Почему
ZZBS код диапазона, 3 символа (160/040/WWV) индекс диапазона, 2 цифры формат заложен давно, на нём уже сидят клиенты
ZZMN пресеты DSP-фильтров, 180 символов имя режима по номеру имён режимов в протоколе нет вовсе, а ZZML опирается на эту команду
ZZST размер шага настройки (read-only) индекс шага FM (00..03) глобального шага у EWSDR нет, слот иначе мёртв; у шага FM своей команды в протоколе нет

Плюс одно ограничение диапазона, не формата: ZZCD (hang-time break-in) эталон принимает в 150..5000 мс, у нас потолок 2000 — столько же стоит в настройках телеграфа и уходит в поле HangDelay пакета DUC Specific. Значения выше подрезаются, как и всё прочее у Thetis.


2. Что сделано

2.1 Реализованные команды Kenwood (одиночный префикс)

Команда Функция Маппинг на контроллер
FA / FB VFO A / B частота (11 цифр, Гц) Set/GetVfoA, Set/GetVfoB
FR приёмный VFO (0/1) Set/GetActiveVfo
FT передающий VFO / split ZZSP
FW / SH / SL ширина фильтра (индекс пресета) Set/GetFilterIdx
MD режим (Kenwood-код) Set/GetMode
GT режим AGC (0=FAST…4=OFF) Set/GetAGCMode
AG громкость AF Set/GetVolume
PC мощность TX 0100 % Set/GetDriveLevel
NB шумоподавитель (NB) Set/GetNBMode
NT автонотч (ANF) Set/GetANFEnabled
SM S-метр (шкала Kenwood 00000030) GetSMeter
IF составной статус (37 байт) freq/mode/tx/split
ID идентификатор (TS-2000 = 019) const
PS питание/состояние GetRunning
TX / RX передача / приём SetTransmitting
BU / BD смена диапазона ±1 DoBandUp/Down
UP / DN / RU / RD шаг настройки ±1 DoTuneUp/Down
SQ шумоподавитель FM (шкала 0–255) Set/GetSquelchLevel+On
CN / CT CTCSS тон / вкл Set/GetCTCSSTone, …On
OF / OS репитер: offset / направление Set/GetFMRptOffset, …Dir

2.2 Реализованные расширенные ZZ* (выборочно, наиболее значимые)

VFO/режим/фильтр: ZZFA ZZFB ZZFI ZZFL ZZFH ZZFM ZZMD ZZFT ZZSP ZZVS, диапазоны: ZZBS ZZBU ZZBD, AGC/громкость/мощность/аттенюатор: ZZGT ZZAG ZZMA ZZPC ZZAR ZZRX, шумодав/нотч: ZZNA ZZNB ZZNR ZZNS ZZNT ZZNN, FM: ZZSQ ZZSO ZZTA ZZTB ZZST, CTUN: ZZCN, S-метр/телеметрия: ZZSM ZZRV, память: ZZMV, шаги VFO: ZZAU ZZBP ZZBM ZZSU ZZSA ZZSB, передача: ZZTX, сервис: ZZID ZZGA ZZGR.

2.3 Сделано в ветке feature/cat-commands-expansion

Все команды ниже были заглушками и стали рабочими — функционал в TRadioController уже существовал, но не был проброшен в CAT.

Группа 1 — Split TX

  • ZZSP, FT — split (TX на VFO-B), FR — выбор приёмного VFO, split-байт в составном IF.
  • В TRadioController добавлен публичный метод SetSplit(TxOnB) (флаг FSplitTxB + PushNetworkState + Changed(rfActiveVfo)). TX-частоту радио берёт из ActiveTXFreqHz (учитывает split).

Группа 2 — FM Squelch

  • SQ (шкала Kenwood 0255), ZZSQ (0100 %), ZZSO (вкл/выкл).
  • Маппинг: SetFMSquelch / SetFMSquelchLevel. Уровень>0 включает шумоподавитель.

Группа 3 — FM CTCSS

  • CT/ZZTA (вкл/выкл), CN (1-based тон 0138), ZZTB (0-based 0037).
  • Маппинг: SetFMCTCSS / SetFMCTCSSTone, таблица CTCSS_TONES[38].

Группа 4 — FM-репитер

  • OF (offset, 9 цифр Гц), OS (направление).
  • Трансляция направлений Kenwood (0=simplex,1=+,2=) ↔ внутренние RPT_NONE/MINUS/PLUS. Маппинг: SetFMRpt / SetFMRptOffset.

Группа 5 — FM tuning step

  • ZZST (индекс 0003: 6.25/12.5/20/25 кГц). Маппинг: SetFMStep.

Доп. команды (вторая итерация)

Команда Что Примечание
ZZSO Squelch on/off завершает группу squelch
ZZNT ANF on/off раньше ошибочно был алиасом NB
ZZCN CTUN (click-tune) RX1 новый callback Get/SetCTunSetCTun
ZZAR RX1 AGC-T / RF gain (±nnn) новый callback Get/SetAGCTopSetAGCTop (кламп 20120 dB)
ZZAU/ZZBP сдвиг VFO A/B вверх на шаг nn (0014) исправлен баг: были заалиашены на ZZFA/ZZAP
ZZSU шаг активного VFO вверх DoTuneUp

Шкала шагов ZZAU/ZZBPStepIdxToHz (зеркало Thetis Step2Freq): 1 Гц, 10, 25, 50, 100, 250, 500, 1к, 5к, 9к, 10к, 100к, 250к, 500к, 1 МГц.

Новые callback'и в TCATContext (этой ветки): split, squelch on/level, CTCSS on/tone, FM repeater dir/offset, FM step, AGC-T, CTUN.

2.4 Сделано в ветке feature/cat-tx-cw

К этому моменту в EWSDR появились TX-профили со всей «звуковой» цепью и телеграф (кейер, сайдтон, break-in) — команды, которые §4 когда-то объявил незакрываемыми, стали рабочими.

TX-тракт (правки уезжают в активный TX-профиль):

Команда Что Маппинг
MG / ZZMG усиление микрофона MicGainDB; MG — шкала Kenwood 100:70, ZZMG — дБ со знаком
MO / ZZMO кнопка MON self-monitor даунлинка: SetRxMuteOnTx (инверсия)
ZZTM громкость монитора SetTXMonVolume (новый метод контроллера)
ZZTL / ZZTH кромки TX-фильтра, Гц FilterLow / FilterHigh
PR / ZZPK речевой компрессор вкл/выкл CompressorOn
ZZPL усиление компрессора, дБ CompressorGain (0..20)
ZZET кнопка TXEQ EQOn
ZZEB значения TX-эквалайзера EQNumBands + EQGains (формат Thetis, 36 символов). Полос бывает только 3 или 10 — движок (PushTXEQProfile) и редактор знают ровно эти два случая, остальное отвергается и не выдаётся
ZZTO мощность настройки TUNLevel
ZZTU кнопка TUN SetTune
ZZUT двухтональник 2TON SetTwoTone
ZZLI кнопка PS-A SetPureSignal
ZZUS одиночная калибровка PS PSSingleCal (write-only)
ZZTP активный TX-профиль SelectTXProfile / TXProfileCount
ZZFD девиация передачи (2.5/5 кГц) TTXSettings.FMDeviation

Телеграф (раньше были проброшены только KS/KY/ZZKM/ZZKS/ZZKY):

Команда Что Маппинг (TCWSettings)
ZZCS скорость кейера, WPM (2 разряда) .Speed — та же величина, что у KS
ZZCL тон приёма/сайдтона, Гц .Pitch
ZZCI иамбик вкл/выкл .KeyerMode (вкл выбирает iambic B, если стоял прямой ключ)
ZZCB break-in прошивки .BreakIn
ZZCD hang-time break-in, мс .HangTimeMS
ZZCM сайдтон (у Thetis — «monitor disable», значение инвертировано) .SidetoneSW или .SidetoneHW — тот, что отвечает за текущий источник манипуляции

Новое в контроллере. Правка TX-настроек получила одну публичную дверь — SetTXSettings(const T; Notify), по образцу SetCWSettings: применение к WDSP, перепосыл DUC Specific, живой TUN, запись в активный профиль и персист теперь там, а не в обработчике MainForm (тот делегирует с Notify=False, иначе rfTXProfile перезагрузил бы вкладку Transmit под руками у правящего). Добавлен SetTXMonVolume — громкость self-monitor'а адресно, вне TX-контекста.

Новое в движке. Хелперы OnOff и NumField — разбор двухпозиционных и числовых полей в одном месте, чтобы тела команд остались про смысл. Границы значений (MIC_GAIN_MIN… в шапке юнита) повторяют контролы вкладок Transmit и CW: по CAT прилетает что угодно, а контроллер принимает запись целиком и уже не знает, кто её правил.

Команды слайсов (CATSliceEndpoint) этих callback'ов не получают: TX-тракт один на радио, а профиль слайса переключается своим путём (SetSliceTXProfile). Все новые команды nil-безопасны и на слайс-порту отвечают дефолтом.

2.5 Ревизия алиасов и подписей

Подписи писались по буквам кода, а не по Thetis, и врали примерно в 150 местах. Хуже: часть работающих команд была привязана не к своей функции — внешний софт получал осмысленный, но неверный ответ, что хуже честной заглушки. Всё сверено с CATCommands.cs и исправлено.

Команда делала не своё дело — теперь делает своё:

Команда Было Стало
ZZMA режим (алиас ZZMD) кнопка MUTSetMute
ZZRX переход на приём шаговый аттенюатор RX1 0..31 дБ → SetAtten
ZZRV версия ПО напряжение питания PA (телеметрия)
ZZMV индекс текущего режима число записей памяти каналов
ZZNN заглушка «ручной нотч» SNB (спектральный бланкер)
ZZNS SNB кнопка NR2 (режим NR 2)
ZZVS алиас ZZSP (split) операции с парой VFO: 0=A→B, 1=B→A, 2=обмен, 3=IF→VFO
ZZBM режим (алиас ZZMD) VFO B вниз на nn шагов (пара к ZZBP)
ZZFI алиас ZZBI сам индекс фильтра
ZZBS алиас ZZAC сам выбор диапазона
ZZKM режим кейера запуск CW-макроса (ячейка памяти сообщений)
ZZFT всегда VFO A TX-частота с учётом split
ZZTX любое значение ≠ 0 поднимало передачу («2=TUNE» — выдумка) строго 0/1, иначе ?; и передача НЕ включается (настройка — ZZTU)

Ошибочные алиасы сняты — теперь честные заглушки (в скобках — где эта функция живёт на самом деле): ZZAA, ZZVG (громкость — ZZAG) · ZZOA (антенна RX1) · ZZAP (мощность — ZZPC) · ZZDN (мощность — ZZPC; сам ZZDN = низ водопада) · ZZAC (диапазон — ZZBS) · ZZBI (фильтр — ZZFI) · ZZMB (режим — ZZMD) · ZZVA (обмен VFO — ZZVS) · ★ZZVB (усиление приёма в VAC; опрос копировал VFO A в VFO B, то есть терял сплит оператора, — тело переписали позже комментария) · ZZPD, ZZPO (мощность и питание — ZZPC/ZZPS) · ZZQM (режим — ZZMD) · ZZSR (опорный уровень спектра) · ZZRD/ZZRU (RIT; шаг настройки — ZZSA/ZZSB).

Что заметят внешние клиенты: ZZRX; больше не уводит в приём (для этого есть ZZTX0; и кенвудовская RX;), ZZVA; больше не меняет VFO местами (теперь ZZVS0;), ZZKM не выставляет режим кейера (иамбик вкл/выкл — ZZCI), RD/RU больше не перестраивают VFO (шаг — UP/DN). Во всех случаях старое поведение было отсебятиной.

Кенвудовские команды. Сверены отдельно: все 40 команд, реализованных в Thetis, у нас есть, ширины полей совпадают с CATStructs.xml. Исправлено:

Команда Что было не так
SM ответ был 4 цифры вместо 5 (SM0015;SM00019;) — парсеры логгеров ждут пять; заодно принимаем селектор 2, как шлёт Thetis
RD / RU перестраивали VFO, хотя это RIT (его у нас нет) — стали заглушками, принимающими 5-значный аргумент
KY поле текста у Kenwood фиксированной ширины и добито пробелами — хвост уходил в эфир словесными паузами, теперь срезается
IF в комментарии значилось «37 байт», реальная и правильная длина ответа — 35
SH / SL обе крутят один и тот же индекс пресета; раздельных «сторон» у главного приёмника нет — оговорка добавлена в код
CT принимала любой символ: CT9; молча ГАСИЛ тон вместо ответа ?;. Теперь обёртка над ZZTA, как в эталоне, и сама ZZTA проверяет значение строго
OF / OS несли реализацию сами, а ZZOT/ZZOS были заглушками — клиент Thetis обращается как раз к ZZ* и не получал ничего. Реализация переехала в ZZOT/ZZOS, кенвудовские стали обёртками. Заодно OF с нечисловым полем больше не обнуляет сдвиг молча

Разбор и транспорты. Отдельный проход по валидации и вводу-выводу:

Где Что было не так
TCATEngine.Parse команды без параметров не проверяли суффикс: TXanything; доходил до CmdTX и поднимал передачу; так же вели себя RX UP DN BD BU QI RC ID IF. Эталон отбраковывает лишний суффикс в парсере, по таблице ширин; у нас таблицы нет — список безаргументных команд теперь в IsParamless
ZZFL / ZZFH принимали поле любой длины от 4 символов и гнали его через StrToIntDef: ZZFLabcd; молча схлопывал кромку в ноль. Поле фиксированное — ровно 5 символов со знаком, разбор строгий
ZZAU ZZBP ZZBM ZZBS ZZFI длину поля проверяли, а содержимое — нет: StrToIntDef(s, 0) превращал любую нечисловую пару символов в индекс 0. То есть ZZBSxx; переключал диапазон на нулевой вместо ?;, ZZBMxx; и ZZAUxx;/ZZBPxx; двигали VFO, а ZZFIxx; выбирал фильтр 0. Разбор приведён к идиоме ZZFL/ZZFH: TryStrToInt, иначе ошибка формата
CN FW GT NB PC SH/SL AG SQ, ZZAG ZZAR ZZNA ZZNB ZZNR ZZPC ZZSQ ZZST ZZTB ★тот же дефект, найденный сплошным прогоном (test/cat): правку получили только пять команд выше, а у остальных StrToIntDef остался — в том числе у кенвудовских двойников уже исправленных величин. FWxxxx;/SHxx;/SLxx; ставили фильтр 0 (тот же индекс, что ZZFI), AG0xxx; и SQ0xxx; — громкость и порог в ноль, GTxxx; — АРУ в FAST, PCxxx;/ZZPCxxx; — мощность в ноль. Разбор везде строгий. Не тронуты FR (сам сверяет 0/1), MD/ZZMD (нечисловое даёт 0, а установка идёт от 1) и ZZOS — там мусор трактуется как симплекс намеренно, по эталону
ZZGT опрос отвечал тремя цифрами (как кенвудовская GT), а установка принимала ровно один символ: клиент, прочитавший ZZGT000; и написавший его назад, получал ?;. Теперь принимаются обе ширины
ZZBE формы были перевёрнуты: опрос ZZBE; отвечал ?;, а установка ZZBE01; возвращала данные ('1'). Вся семья «сдвиг VFO на nn шагов» (ZZAD ZZAE ZZAF ZZBF ZZSG ZZSH) — однострочные заглушки, ZZBE приведён к ним
KY / ZZKY текст не ограничивался; поле у Kenwood фиксированное, 25 символов. Длиннее — ?;: очередь передачи не должна расти произвольно, иначе один пакет уводит станцию в эфир на неопределённое время
CATTcp.SendStr один send на ответ. TCP не обязан отдать весь буфер за раз — длинный ответ (IF, ZZEB, список режимов) мог уехать обрезанным, и молча: усечение здесь не ошибка. Теперь дописываем остаток в цикле. ★И пишем через WebUtils.SockSend, а не голым fpSend: в нём MSG_NOSIGNAL, без которого запись в закрытый клиентом сокет приходит как SIGPIPE и убивает процесс целиком (обработчика сигнала в дереве нет, а цикл дозаписи умножает число попыток)
CATSerial порт помечался активным ДО SerOpen; при отказе он навсегда оставался «работающим» в ActiveCount и UI, а причина нигде не оседала. Открытие переехало из потока в TCATSerialPort.Start (синхронно), появилось свойство LastError, поток теперь только читает, а закрывает владелец в Stop. Заодно Andromeda-порт назначается только на реально поднявшийся порт

Формат ZZOT сверен отдельно: эталон читает 9 цифр как МГц с шестью знаками после запятой (вставляет точку после третьего разряда) — численно это ровно те же герцы, что пишем мы, поле совместимо. Мусор в ZZOS эталон трактует как симплекс (default в String2OffsetDirection), и мы намеренно повторяем это, а не отвечаем ошибкой.

Формат ответа на ZZ* (ZZ + код + эхо суффикса запроса + значение) сверен с CATParser.ParseExtended — совпадает; это важно для ZZSM0;, где запрос несёт селектор приёмника.


3. Что нужно сделать (актуально / есть чем закрыть)

Эти команды — заглушки, но соответствующая функция в EWSDR уже есть или легко добавляется. Приоритетные кандидаты:

Команда(ы) Что Чем закрыть Сложность
ZZVL, ZZUX/ZZUY блокировка VFO (общая и раздельная) SetVfoLock низкая
ZZSW, ZZZW обмен VFO A/B VfoSwap низкая
ZZAD ZZAE ZZAF ZZBE ZZBF, ZZSG/ZZSH сдвиг VFO на nn шагов StepIdxToHz + DoTuneUp/Down, как у ZZAU/ZZBP низкая
ZZCU загрузка CPU (read) телеметрия процесса низкая
ZZSF фильтр «центр + ширина» одной командой SetFilterEdges низкая
ZZSG/ZZSH, ZZSD шаг VFO B и колеса — по одному шагу те же SetVfoB/DoTune* низкая
ZZMY ZZMZ ZZMW ZZMX ZZQR ZZQS ZZQM запись/чтение памяти каналов (счётчик ZZMV уже есть) ChannelStore / ChannelController средняя
ZZDN/ZZDO, ZZDP/ZZDQ/ZZDR низ/верх водопада, сетка спектра FSpecRefLevel/FSpecRange, per-pan дисплей средняя
ZZPD/ZZPE, ZZPY/ZZPZ пан и зум дисплея SetPanZoomPan средняя
ZZSN, ZZZM, ZZZV серийник, модель, версия железа BoardUtils низкая
ZZXN / ZZXV составные статусы RX1/VFO сборка из готовых геттеров средняя
ZZOC/ZZOD/ZZOE антенны SetAnt936x (порты AD936x) средняя
ZZDX / ZZDY кнопка DX и порог спотов флаг ShowSpots живёт в MainForm — сперва поднять в контроллер средняя
ZZAS RX2 AGC-T — только при появлении RX2

Прежде чем реализовывать новую команду, сверяйся с форматом поля в Thetis/.../CAT/CATStructs.xml (<nsetparms> / <ngetparms>) и телом метода в CATCommands.cs, чтобы ширина/знак значения совпадали. И проверяй смысл: подпись у заглушки в диспетчере может врать (см. §1.2).

3.1 Совсем не обрабатываются (вернут ?;)

ZZCU (CPU), ZZDY (DX threshold), ZZQA/ZZQB (Quick Play/Rec), ZZVF (VAC stereo). Из них реалистично добавить только ZZCU и ZZDY.


4. Осознанные заглушки (закрывать нечем)

Команды отвечают валидным dummy-значением для совместимости с управляющим ПО (rigctld, N1MM, WSJT-X, Log4OM), но функции в EWSDR нет. Это ~70 % всех заглушек.

Область Команды (примеры) Причина
Второй приёмник RX2 / sub-RX ZZAB ZZAS ZZBA ZZBB ZZBT ZZMB ZZOB ZZRS ZZSV ZZSX ZZNC ZZND ZZNF ZZNO ZZNU ZZNW ZZFJ ZZFR ZZFS ZZGU ZZLE ZZLF ZZPB ZZRY ZZXO один приёмник
VAC / VAC2 ZZVA ZZVB ZZVC ZZVD ZZVF ZZVI ZZVK ZZVM ZZVO ZZVQ ZZVR ZZVT ZZVU ZZVV ZZVX ZZVY ZZVZ ZZYA ZZYB нет виртуального аудиокабеля
RTTY / DIGI-офсеты ZZRA ZZRB ZZRH ZZRL нет RTTY-движка
RIT / XIT RT XT RC, ZZRF ZZRT ZZXC ZZXD ZZXF ZZXS ZZXU нет тракта смещения приёма (CTUN — не замена)
Diversity ZZDB ZZDC ZZDD ZZDE ZZDF ZZDG ZZDH (но не ZZDA — это усреднение спектра) нет диверсити-приёма
Компандер, noise gate, APF, VOX ZZCP ZZCT, ZZGE ZZGL, ZZAA ZZAB ZZAT ZZAY, ZZVE ZZXH таких блоков в тракте нет (речевой компрессор — есть, это ZZPK/ZZPL)
Микшер Flex 5000 / F1500, FlexWire ZZWA…ZZWS, ZZWT…ZZWW, ZZFV ZZFW ZZFX ZZFY железо другого вендора
ATU (Aries/Ganymede) ZZOV ZZOW ZZOX ZZOZ ZZZA нет тюнера
DSP-буферы, JSON-команды ZZHA ZZHR ZZHT ZZHU ZZHV ZZHW ZZHX, ZZJP ZZJQ ZZJR ZZJS внутренние крутилки Thetis

5. Транспорты

CAT дополнительных слайсов (свой TCP-порт на слайс B..G + Auto TX) вынесен в отдельный документ: CAT_SLICES.md. Тот же движок, другой контекст; транспорты ниже принадлежат главному приёмнику (слайс A).

  • Serial (CATSerial.pas): до 4 портов, параметры (порт/скорость/биты/ чётность/стоп) из TGlobalSettings.CATSerial*, применяются TCATAdapter.ApplySettings. На текущей платформе сборки может быть {$DEFINE CAT_SERIAL_STUB} (см. начало файла) — проверить перед релизом.
  • TCP (CATTcp.pas): сервер на CATTcpPort, включается флагом CATTcpEnabled. Клиенты идентифицируются GUID через ZZGA/ZZGR.

5.1 Стенд

test/cat/run.sh — сборка и прогон test/cat/cattest.pas (железа и движка не требует, команды ходят по подставному радио). Проверяет не список команд, а свойства всего набора, и по кодам ходит перебором AA..ZZ, поэтому новая команда попадает под проверку сама:

  • круговой прогон — ответ на опрос обязан приниматься обратно (так нашлась разница ширин у ZZGT);
  • мусор в поле — разбор не падает (сборка с -Criot), отвечает одним корректным кадром, а нечисловое поле ничего не перестраивает (так нашлись FW/SH/SL/AG/SQ/GT и десяток ZZ-двойников);
  • точечные регрессии — заглушки, которые молча правили радио.

Подробности — test/cat/README.md. После правки любой команды прогон обязателен: он дешёвый (секунды) и ловит ровно тот класс дефектов, которым эта подсистема болеет.


6. Как добавить новую CAT-команду (чек-лист)

  1. Сверить формат в CATStructs.xml + CATCommands.cs (Thetis).
  2. Если нужна новая возможность контроллера — есть ли публичный метод/поле в TRadioController? Если нет — добавить (по образцу SetSplit).
  3. В CATEngine.pas: добавить callback(и) в TCATContext, SafeGet*-хелпер для GET, переписать тело команды (CmdXX или ZZXX).
  4. В CATAdapter.pas: геттер (чтение FController.F*), сеттер + SyncXX (через Invoke), регистрация в TCATAdapter.Create.
  5. Если команда ZZ* ещё не в диспетчере — добавить ветку else if ext = '..'.
  6. Сборка-проверка: lazbuild -B --ws=qt6 ewsdr.lpr (GUI-граф включает CAT; headless-демон ewsdrd CAT не содержит).
  7. Прогнать стенд: test/cat/run.sh. Новая команда попадёт в круговой прогон и в прогон мусором автоматически — если ширины GET и SET разошлись или поле разбирается через StrToIntDef, стенд скажет об этом сразу.