Files
ewsdr/doc/CAT_STATUS.md
T
ew8bakandClaude Opus 5 27a8d1d5cf feat(cat): TX-тракт и телеграф; ревизия алиасов, подписей и валидации
Со времён прошлой правки CAT в EWSDR появились TX-профили со всей «звуковой»
цепью и телеграф — команды, которые doc/CAT_STATUS.md объявлял незакрываемыми,
стали рабочими.

TX-тракт: MG/ZZMG (усиление микрофона), MO/ZZMO + ZZTM (монитор передачи и его
уровень), ZZTL/ZZTH (кромки TX-фильтра), PR/ZZPK + ZZPL (речевой компрессор),
ZZET + ZZEB (эквалайзер), ZZTO + ZZTU (мощность и кнопка настройки), ZZUT
(2TON), ZZLI + ZZUS (PureSignal), ZZTP (выбор профиля), ZZFD (девиация).

Телеграф (были проброшены только KS/KY/ZZKS/ZZKY): ZZCS скорость, ZZCL тон,
ZZCI иамбик, ZZCB/ZZCD break-in и hang-time, ZZCM сайдтон.

Ревизия подписей. Комментарии у заглушек писались по буквам кода, а не по
Thetis, и врали примерно в 150 местах. Хуже: часть РАБОТАЮЩИХ команд была
привязана не к своей функции — внешний софт получал осмысленный, но неверный
ответ, что хуже честной заглушки. Всё сверено с CATCommands.cs:

  ZZMA  режим        -> кнопка MUT            ZZNN  заглушка -> SNB
  ZZRX  переход в RX -> аттенюатор RX1        ZZNS  SNB      -> кнопка NR2
  ZZRV  версия ПО    -> напряжение питания    ZZVS  алиас ZZSP -> операции с VFO
  ZZMV  индекс режима-> счётчик памяти        ZZBM  режим    -> VFO B вниз на nn
  ZZKM  режим кейера -> запуск CW-макроса     ZZFT  всегда A -> TX-частота (split)
  ZZFI/ZZBS  были алиасами -> сами держат фильтр и диапазон

Ошибочные алиасы на громкость/мощность сняты (ZZAA ZZVG ZZOA ZZAP ZZDN ZZAC
ZZBI ZZMB ZZVA ZZPD ZZPO ZZQM ZZSR ZZRD ZZRU) — теперь честные заглушки с
указанием, где функция живёт на самом деле.

Кенвудовские команды сверены отдельно, покрытие полное (все 40 из Thetis),
ширины полей совпадают с CATStructs.xml. Исправлено: SM отдавал 4 цифры вместо
5; RD/RU перестраивали VFO, хотя это RIT; KY не срезал набивку поля пробелами
и гнал её в эфир паузами; CT принимала любой символ и «CT9;» молча гасил тон;
OF/OS несли реализацию сами, а ZZOT/ZZOS были заглушками — клиент Thetis
обращается как раз к ZZ* и не получал ничего.

Валидация. Команды без параметров не проверяли суффикс: «TXanything;» доходил
до CmdTX и ПОДНИМАЛ ПЕРЕДАЧУ (так же RX UP DN BD BU QI RC ID IF) — эталон
отбраковывает лишний суффикс в парсере, у нас теперь список в IsParamless.
ZZTX поднимал передачу на любом значении кроме нуля («2=TUNE» — выдумка).
ZZFL/ZZFH принимали поле любой длины от 4 символов и через StrToIntDef молча
схлопывали кромку в ноль. ZZMG принимал 1-2 символа. KY/ZZKY не ограничивали
текст 25 символами, и очередь передачи могла расти произвольно.

Осознанные отклонения от эталона сведены в отдельную таблицу документа:
ZZBS (индекс диапазона вместо кода), ZZMN (имя режима вместо пресетов
фильтров), ZZST (шаг FM вместо размера шага настройки), ZZCD (потолок 2000 мс —
наш предел, он же в поле HangDelay пакета DUC Specific).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 13:13:33 +03:00

371 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/SetCTun` → `SetCTun` |
| `ZZAR` | RX1 AGC-T / RF gain (`±nnn`) | новый callback `Get/SetAGCTop` → `SetAGCTop` (кламп 20120 dB) |
| `ZZAU`/`ZZBP` | сдвиг VFO A/B вверх на шаг `nn` (00–14) | **исправлен баг**: были заалиашены на `ZZFA`/`ZZAP` |
| `ZZSU` | шаг активного VFO вверх | `DoTuneUp` |
Шкала шагов `ZZAU/ZZBP` — `StepIdxToHz` (зеркало 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 символов) |
| `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`) | кнопка **MUT** → `SetMute` |
| `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`) · `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 символов со знаком, разбор строгий |
| `KY` / `ZZKY` | текст не ограничивался; поле у Kenwood фиксированное, 25 символов. Длиннее — `?;`: очередь передачи не должна расти произвольно, иначе один пакет уводит станцию в эфир на неопределённое время |
| `CATTcp.SendStr` | один `send` на ответ. TCP не обязан отдать весь буфер за раз — длинный ответ (`IF`, `ZZEB`, список режимов) мог уехать обрезанным, и молча: усечение здесь не ошибка. Теперь дописываем остаток в цикле |
| `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** | `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`](CAT_SLICES.md). Тот же движок, другой
> контекст; транспорты ниже принадлежат главному приёмнику (слайс A).
- **Serial** (`CATSerial.pas`): до 4 портов, параметры (порт/скорость/биты/
чётность/стоп) из `TGlobalSettings.CATSerial*`, применяются
`TCATAdapter.ApplySettings`. На текущей платформе сборки может быть
`{$DEFINE CAT_SERIAL_STUB}` (см. начало файла) — проверить перед релизом.
- **TCP** (`CATTcp.pas`): сервер на `CATTcpPort`, включается флагом
`CATTcpEnabled`. Клиенты идентифицируются GUID через `ZZGA`/`ZZGR`.
---
## 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 **не** содержит).