feat(cat): Andromeda/G2 front panel support (serial, LED indicators)

Bidirectional Apache Labs Andromeda/G2 front-panel protocol on top of CAT.
Panel logic lives in dedicated units; existing files get only thin hooks.

New units:
- AndromedaMap.pas   — data only: action enums + default button/encoder
  layout tables and LED (ZZZI) numbers (mirroring the Thetis defaults)
- AndromedaPanel.pas — TAndromedaPanel: inbound panel events -> controller
  actions (marshalled via Invoke), state subscription -> ZZZI push via SendProc

Hooks:
- CATEngine: parse ZZZD/ZZZU/ZZZE/ZZZP/ZZZS into generic TCATContext callbacks
- CATAdapter: creates the panel, routes engine callbacks to it, supplies the
  SendProc to the Andromeda port, AddStateListener, ZZZS handshake
- CATSerial: Andromeda flag in port config + HasAndromeda/SendToAndromeda
- RadioController: AddStateListener (multicast, does not steal MainForm's
  OnStateChanged)
- Settings/SettingsForm/MainForm: CATSerialAndromeda[0..3] + persistence + checkbox

First version: serial transport, hardcoded default mapping, LED indicators
(MOX/Tune/CTUN/VFO A-B/Split/NB/NR/SNB/ANF/Squelch). Doc: doc/ANDROMEDA.md.

TODO: default button mapping does not match the physical panel (hardcoded) —
to be fixed later; also ZZMF/LCD text, TCP G2V2 transport, configurable mapping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 11:40:10 +03:00
co-authored by Claude Opus 4.8
parent 4a92624e9b
commit 2d7290e44d
10 changed files with 704 additions and 9 deletions
+177
View File
@@ -0,0 +1,177 @@
# Поддержка передней панели Andromeda / G2 в EWSDR
Ветка разработки: `feature/cat-commands-expansion`.
Дата последнего обновления: 2026-06-26.
Документ описывает поддержку аппаратной передней панели Apache Labs
**Andromeda / ANAN-G2** (поворотные энкодеры, кнопки, LED-индикаторы, LCD).
За эталон протокола взят Thetis (`Andromeda/Andromeda.cs`, `CAT/CATCommands.cs`).
См. также [CAT_STATUS.md](CAT_STATUS.md) — общий статус CAT-подсистемы.
---
## 1. Что такое Andromeda и чем отличается от обычного CAT
Andromeda — это **не отдельный набор команд**, а двунаправленный протокол панели
поверх CAT-фрейминга. В отличие от обычного CAT (реактивный «запрос → ответ»),
панель требует, чтобы хост **сам инициативно отправлял** обновления подсветки.
```
ZZZD/ZZZU/ZZZE/ZZZP/ZZZS (панель → хост: ввод)
┌─────────┐ ───────────────────────────► ┌──────────┐
│ Панель │ │ EWSDR │
│ G2 / │ ◄─────────────────────────── │ (хост) │
│Andromeda│ ZZZI / ZZMF / ZZZS; └──────────┘
└─────────┘ (хост → панель: подсветка, LCD, рукопожатие)
```
| Направление | Команды | Назначение |
|---|---|---|
| **Панель → хост** | `ZZZS<v>` | панель сообщает версию HW/SW (рукопожатие) |
| | `ZZZU<nn>` / `ZZZD<nn>` | энкодер VFO повёрнут вверх/вниз на nn шагов |
| | `ZZZE<eees>` | энкодер #ee повёрнут (01–20 = +шаг, 5170 = −шаг) |
| | `ZZZP<bbs>` | кнопка #bb: s=1 нажата, 2 long-press, 0 отпущена |
| **Хост → панель** | `ZZZI<nn><s>` | LED-индикатор #nn (0019) вкл (1)/выкл (0) |
| | `ZZMF<30 цифр>` | текст мультифункц. энкодера на LCD (пока **не реализован**) |
| | `ZZZS;` | запрос версии у панели |
---
## 2. Архитектура реализации
Логика панели вынесена в отдельные юниты; в существующих файлах — только тонкие
хуки. Зависимости однонаправленные, без циклов.
```
┌────────────────────┐
serial-порт ──────► │ TCATEngine │ разбор ZZZ* → generic callbacks
(Andromeda) │ (CATEngine.pas) │ (OnPanelVfoStep/Encoder/Button/Version)
└─────────┬──────────┘
│ callbacks
┌─────────▼──────────┐ создаёт/связывает
│ TCATAdapter │ ◄───────────────────┐
│ (CATAdapter.pas) │ │
└─────────┬──────────┘ │
SendProc (ZZZI/ZZZS;) │ ▲ AddStateListener │
на Andromeda-порт ▼ │ (push индикаторов) │
┌────────────────────┐ ┌────────────┴───────┐
│ TAndromedaPanel │ ───────►│ TRadioController │
│ (AndromedaPanel.pas)│ Invoke │ (ядро) │
│ использует ▼ │ действия└────────────────────┘
│ AndromedaMap.pas │ (таблицы раскладки — только данные)
└────────────────────┘
```
| Файл | Роль |
|---|---|
| **`AndromedaMap.pas`** | Только данные: enum'ы `TAndButton` / `TAndEncoder` / `TAndIndicator`, дефолтные таблицы раскладки кнопок/энкодеров и номера LED (`ZZZI`). Без логики и зависимостей. |
| **`AndromedaPanel.pas`** | `TAndromedaPanel` — «мозг»: входящие события → действия контроллера (свой маршалинг через `FController.Invoke`); подписка на состояние контроллера → построение `ZZZI` → отправка через callback `SendProc`. Зависит только от `RadioController` + `AndromedaMap`. |
| `CATEngine.pas` | Разбор входящих `ZZZD/ZZZU/ZZZE/ZZZP/ZZZS` в 4 generic-callback'а `TCATContext`. Движок остаётся чистым парсером, про Andromeda «не знает». |
| `CATAdapter.pas` | Тонкий клей: создаёт панель, маршрутизирует callback'и движка в неё, даёт `SendProc` на Andromeda-порт, регистрирует панель как слушателя состояния, инициирует рукопожатие. |
| `CATSerial.pas` | Флаг `Andromeda` в конфиге порта + `HasAndromeda` / `SendToAndromeda`. |
| `RadioController.pas` | `AddStateListener` — multicast-наблюдатель состояния (push не «ворует» единственный `OnStateChanged`, занятый MainForm). |
| `Settings.pas` / `SettingsForm.pas` / `MainForm.pas` | Поле `CATSerialAndromeda[0..3]`, персист, чекбокс на каждом serial-порту. |
### Потоки
- Входящие хэндлеры (`HandleVfoStep/Encoder/Button/Version`) зовутся из потока
CAT-транспорта и маршалят действия в поток контроллера через `FController.Invoke`
(GUI = `TThread.Synchronize`).
- Слушатель состояния и `SendProc` работают из потока, вызвавшего `Changed()`;
отправка в порт потокобезопасна (serial `SendStr` под своим локом).
---
## 3. Раскладка по умолчанию (AndromedaMap.pas)
Раскладка **зашита в коде** (первая версия). Правится в `AndromedaMap.pas`.
### Кнопки (`ZZZP`, индекс 0-based после разбора)
| # | Действие | # | Действие |
|---|---|---|---|
| 0 | MOX | 9 | SNB |
| 1 | Tune (TUN) | 10 | Split (TX VFO-B) |
| 2 | Band Up | 11 | VFO Swap (актив. A/B) |
| 3 | Band Down | 12 | CTUN |
| 4 | Mode Up | 13 | Mute |
| 5 | Mode Down | 14 | CTCSS |
| 6 | NB | 15 | Squelch on/off |
| 7 | NR | 16 | Filter Up |
| 8 | ANF | 17 | Filter Down |
### Энкодеры (`ZZZE`, индекс 0-based)
| # | Параметр | Шаг |
|---|---|---|
| 0 | AF Gain (громкость) | ±1 |
| 1 | AGC-T | ±1 dB |
| 2 | Drive (мощность) | ±1 % |
| 3 | Squelch level | ±1 |
| 4 | Filter width | ±50 Гц (`AND_BW_STEP_HZ`) |
| VFO | энкодер VFO (`ZZZU/ZZZD`) | ±10 Гц/шаг (`AND_VFO_STEP_HZ`) |
### Индикаторы (`ZZZI`, номера зеркалят дефолт Thetis)
| LED # | Индикатор | LED # | Индикатор |
|---|---|---|---|
| 1 | MOX | 12 | Split |
| 2 | ATU Ready *(нет в ewsdr)* | 13 | NB |
| 3 | Tune | 14 | NR |
| 6 | Shift *(нет)* | 15 | SNB |
| 7 | CTUN | 16 | ANF |
| 8/9 | RIT/XIT *(нет)* | 17 | Squelch |
| 10 | VFO A/B | | |
| 11 | VFO Lock *(нет)* | | |
Драйвятся реально: **MOX, Tune, CTUN, VFO A/B, Split, NB, NR, SNB, ANF, Squelch**.
Остальные слоты дефолтной таблицы Thetis (ATU/Shift/RIT/XIT/VFOLock) в ewsdr
функций не имеют и не зажигаются.
---
## 4. Как включить
1. Подключить панель как обычный serial CAT-порт (USB).
2. Settings → CAT → выбрать нужный Serial Port, поставить **Enable** и параметры
порта (скорость и т.д. — как требует панель).
3. Поставить галку **«Andromeda / G2 front panel»** на этом порту.
4. При применении настроек EWSDR пошлёт `ZZZS;`; панель ответит версией — после
этого включается push индикаторов и панель управляет радио.
Только **один** порт может быть Andromeda-портом (первый помеченный).
Настройки сохраняются ключами `cat_serial_androm_N` / `ser_androm_N`
(N = 0..3) в JSON-конфиге.
---
## 5. Что сделано / что осталось
### Сделано (первая версия)
- Транспорт: **serial** (USB CAT).
- Вход: `ZZZD/ZZZU` (VFO), `ZZZE` (энкодеры), `ZZZP` (кнопки), `ZZZS` (рукопожатие).
- Выход: **`ZZZI`** — LED-индикаторы (push от `OnStateChanged`, дедуп повторов).
- Раскладка — зашитый дефолт.
- UI-чекбокс на каждый serial-порт + персист.
### Осталось / возможные улучшения
| Задача | Заметки |
|---|---|
| **`ZZMF`** — текст на LCD мультифункц. энкодера | кодировка: 15 ASCII-символов парами цифр (код−32) |
| **TCP-транспорт (G2 V2 panel)** | сейчас только serial; нужен режим в `CATTcp` |
| **Настраиваемая раскладка** | сейчас зашита; вынести в конфиг/UI (как `AndromedaEditForm` в Thetis) |
| **Long-press кнопок** | принимается (`ZZZP` state=2), но действий пока нет — обрабатывается только нажатие (state=1) |
| **Индикаторы RIT/XIT/PureSignal/Diversity/ATU** | нет соответствующих функций в `TRadioController` |
| **Мультифункц. энкодер (`aeMulti`)** | назначение не задано |
---
## 6. Точки расширения (чек-лист)
- **Добавить действие кнопки/энкодера:** новый элемент в `TAndButton`/`TAndEncoder`
(AndromedaMap.pas) + ветка в `TAndromedaPanel.SyncButton`/`SyncEncoder`
(AndromedaPanel.pas) + правка таблицы `AND_BUTTON_MAP`/`AND_ENCODER_MAP`.
- **Добавить индикатор:** элемент `TAndIndicator` + номер в `AND_INDICATOR_NUM` +
push в `SyncAllIndicators` и в `OnControllerState` (по нужному `rf*`-полю).
- **Изменить раскладку:** только `AndromedaMap.pas` (таблицы-константы).
- Сверяться с форматами Thetis: `Andromeda/Andromeda.cs` (логика панели,
`EIndicatorActions`/`EButtonBarActions`/`EEncoderActions`) и
`CAT/CATCommands.cs` (`ZZZ*`).