# Поддержка передней панели 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` | панель сообщает версию HW/SW (рукопожатие) | | | `ZZZU` / `ZZZD` | энкодер VFO повёрнут вверх/вниз на nn шагов | | | `ZZZE` | энкодер #ee повёрнут (01–20 = +шаг, 51–70 = −шаг) | | | `ZZZP` | кнопка #bb: s=1 нажата, 2 long-press, 0 отпущена | | **Хост → панель** | `ZZZI` | LED-индикатор #nn (00–19) вкл (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) Раскладка **зашита в коде** и зеркалит дефолтную таблицу Thetis (`MakeNewAndromedaDataset`): наш 0-based индекс = «Pushbutton/Encoder Number» Thetis − 1. Номера **проверены на реальной панели** (см. `andromeda_test/AndDecode.pas`). Правится в `AndromedaMap.pas`. ### Кнопки (`ZZZP`, индекс 0-based после разбора) | # | Действие | # | Действие | |---|---|---|---| | 0 | RX1 Mute | 35 | A>B (под shift → 17m) | | 28 | **Shift** (band-keypad) | 36 | B>A (под shift → 15m) | | 29 | Band Up (под shift → 160m) | 37 | Split (под shift → 12m) | | 30 | Mode Up (под shift → 80m) | 38 | *(под shift → 10m)* | | 31 | Filter Up (под shift → 60m) | 39 | *(под shift → 6m)* | | 32 | Band Down (под shift → 40m) | 42 | VFO Swap (toggle A/B) | | 33 | Mode Down (под shift → 30m) | 43 | **VFO Lock** | | 34 | Filter Down (под shift → 20m) | 44 | CTUN | | | | 45 | SDR ON (start/stop) | | | | 46 | MOX | | | | 47 | TUN | Не назначены (нет тракта в ewsdr): 2 (RX2 Mute), 40 (band GEN), 41 (RIT/XIT). NB/NR/ANF/SNB/CTCSS/Squelch у реальной панели на кнопках не выведены (в Thetis — на тачскрине), действия в enum сохранены для кастомной раскладки. ### Shift / band-keypad Кнопка #28 — модификатор-защёлка (как `eBBShift` в Thetis). Пока активен (LED `aiShift` #6), кнопки band-группы #29..39 выбирают диапазон напрямую (`SetBand` 0..10: 160m…6m); #40 = GEN — в ewsdr отсутствует. Снимается повторным Shift либо автоматически после выбора диапазона (non-sticky). Логика — в `TAndromedaPanel.HandleButton`. ### Энкодеры (`ZZZE`, индекс 0-based) | # | Параметр | Шаг | |---|---|---| | 0 | AF Gain (громкость) | ±1 | | 1 | AGC-T (в Thetis — слайдер «RF») | ±1 dB | | 4 | Filter High → ширина фильтра | ±50 Гц (`AND_BW_STEP_HZ`) | | 11 | Drive (мощность) | ±1 % | | VFO | энкодер VFO (`ZZZU/ZZZD`) | ±10 Гц/шаг (`AND_VFO_STEP_HZ`) | > #4/#5 на панели — независимые края фильтра (Filter High/Low). В ewsdr фильтр > симметричный (`SetFilterBW`), поэтому #4 крутит ширину, #5 пока не задействован. > Энкодеры RX2 AF/AGC (#2/#3), diversity (#6/#7), RIT/XIT (#8/#9) — нет тракта. ### Индикаторы (`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, VFO Lock, Shift, Split, NB, NR, SNB, ANF, Squelch**. Остальные слоты дефолтной таблицы Thetis (ATU/RIT/XIT) в 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`, дедуп повторов). - Раскладка — зашитый дефолт, выверена по Thetis и реальной панели. - **Shift / band-keypad** — прямой выбор диапазона (#29..39 → `SetBand`), LED `aiShift`. - **VFO Lock** — блокировка перестройки (`SetVfoLock`, гейт в `TuneActiveBy`), LED `aiVFOLock`. - A>B / B>A / Toggle A/B, SDR ON (start/stop) — на штатных кнопках панели. - UI-чекбокс на каждый serial-порт + персист. ### Осталось / возможные улучшения | Задача | Заметки | |---|---| | **Filter High/Low (`ZZZE` #4/#5)** | независимые края фильтра; сейчас #4 = ширина, #5 не задействован. Нужны `FFilterLow/High` + переработка `ApplyModeFilter` и passband дисплея (sideband-aware, как в Thetis) | | **Sticky shift** | сейчас shift всегда non-sticky (снимается band-нажатием); в Thetis есть `AndromedaStickyShift` | | **`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*`).