Files
ewsdr/AndromedaMap.pas
T
ew8bakandClaude Opus 4.8 0e550dd9f9 fix(cat): align Andromeda panel mapping with real G2 hardware (Thetis)
Button/encoder indices follow Thetis MakeNewAndromedaDataset (our 0-based
index = Thetis Number - 1), verified against andromeda_test/AndDecode.pas.
The old map assumed sequential 0,1,2.. and truncated at 32; the real panel
sends sparse indices up to 47.

- AndromedaMap: AND_MAX_BUTTONS 32->48, renumbered button/encoder tables;
  new actions abVFOAtoB/abVFOBtoA/abVFOLock/abStartStop/abShift.
- RadioController: VFO Lock (FVfoLock, rfVfoLock, SetVfoLock, gate in
  TuneActiveBy).
- AndromedaPanel: wire A>B/B>A/SDR-ON/Lock; Shift band-keypad layer
  (#28 latch, #29..39 -> SetBand 160m..6m, non-sticky); aiVFOLock/aiShift
  indicators.
- Filter High/Low (#4/#5) left as width approximation per decision.
- doc/ANDROMEDA.md updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:11:12 +03:00

179 lines
7.7 KiB
ObjectPascal
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.
unit AndromedaMap;
{
AndromedaMap.pas — таблицы раскладки передней панели Apache Labs Andromeda / G2.
Только ДАННЫЕ: перечисления действий и дефолтные таблицы
«номер кнопки → действие», «номер энкодера → параметр»,
«индикатор → номер LED (ZZZI)». Без логики и без зависимостей от
контроллера — этим занимается AndromedaPanel.pas.
Соответствие протоколу Thetis:
• действия кнопок/энкодеров — подмножество EButtonBarActions / EEncoderActions;
• номера LED-индикаторов (ZZZI nn) зеркалят дефолтную таблицу Thetis
(MakeNewAndromedaDataset): 1=MOX, 3=Tune, 7=CTUN и т.д. Свободные слоты
12..17 заняты функциями ewsdr (Split/NB/NR/SNB/ANF/Squelch).
}
{$mode objfpc}{$H+}
interface
type
// Действие кнопки панели (подмножество, поддержанное ewsdr).
TAndButton = (
abNone, abMOX, abTune, abBandUp, abBandDown, abModeUp, abModeDown,
abMute, abNB, abNR, abSNB, abANF, abVFOSplit, abVFOSwap, abCTUN,
abCTCSS, abSquelch, abFilterUp, abFilterDown,
abVFOAtoB, // копировать VFO A → B (Thetis eBBVFOAtoB)
abVFOBtoA, // копировать VFO B → A (Thetis eBBVFOBtoA)
abVFOLock, // блокировка перестройки VFO (Thetis eBBVFOLockOnOff)
abStartStop, // старт/стоп SDR (Thetis eBBStartStop)
abShift); // модификатор band-keypad (Thetis eBBShift) — обрабатывается в панели
// Параметр, управляемый поворотным энкодером панели.
TAndEncoder = (
aeNone, aeAFGain, aeAGCLevel, aeDrive, aeSquelch,
aeFilterWidth, aeMulti);
// Индикатор (LED) панели. Порядок зеркалит EIndicatorActions Thetis.
TAndIndicator = (
aiNone, aiMOX, aiTune, aiRIT, aiXIT, aiSplit, aiCTune, aiVFOSync,
aiVFOAB, aiVFOLock, aiNB, aiNR, aiSNB, aiANF, aiSquelch,
aiCompander, aiPuresignal, aiDiversity, aiATUReady, aiShift);
const
// Размеры дефолтных таблиц (кнопки/энкодеры нумеруются с 0 после разбора ZZZP/ZZZE).
AND_MAX_BUTTONS = 48;
AND_MAX_ENCODERS = 20;
// Раскладка кнопок: индекс = номер кнопки (0-based), значение = действие.
// Нумерация совпадает с дефолтом Thetis (MakeNewAndromedaDataset): наш 0-based
// индекс = «Pushbutton Number» Thetis 1. Проверено на реальной панели
// (см. andromeda_test/AndDecode.pas). Неназначенные / отсутствующий тракт → abNone.
AND_BUTTON_MAP: array[0..AND_MAX_BUTTONS-1] of TAndButton = (
abMute, // 0 RX1 Mute
abNone, // 1
abNone, // 2 RX2 Mute — нет второго приёмника
abNone, // 3
abNone, // 4
abNone, // 5
abNone, // 6
abNone, // 7
abNone, // 8
abNone, // 9
abNone, // 10
abNone, // 11
abNone, // 12
abNone, // 13
abNone, // 14
abNone, // 15
abNone, // 16
abNone, // 17
abNone, // 18
abNone, // 19
abNone, // 20
abNone, // 21
abNone, // 22
abNone, // 23
abNone, // 24
abNone, // 25
abNone, // 26
abNone, // 27
abShift, // 28 Shift (band-keypad модификатор)
abBandUp, // 29 Band + (под shift → 160m)
abModeUp, // 30 Mode + (под shift → 80m)
abFilterUp, // 31 Filter + (под shift → 60m)
abBandDown, // 32 Band - (под shift → 40m)
abModeDown, // 33 Mode - (под shift → 30m)
abFilterDown, // 34 Filter - (под shift → 20m)
abVFOAtoB, // 35 A>B (под shift → 17m)
abVFOBtoA, // 36 B>A (под shift → 15m)
abVFOSplit, // 37 Split (под shift → 12m)
abNone, // 38 (под shift → 10m)
abNone, // 39 (под shift → 6m)
abNone, // 40 (под shift → GEN, нет в ewsdr)
abNone, // 41 RIT/XIT toggle — нет RIT/XIT
abVFOSwap, // 42 Toggle A/B
abVFOLock, // 43 VFO Lock
abCTUN, // 44 CTUNE
abStartStop, // 45 SDR ON (start/stop)
abMOX, // 46 MOX
abTune); // 47 TUN
// Раскладка энкодеров: индекс = номер энкодера (0-based), значение = параметр.
// Нумерация = «Encoder Number» Thetis 1 (дефолт MakeNewAndromedaDataset).
// Энкодер #1 в Thetis — AGC Level (слайдер RF), поэтому aeAGCLevel.
// #4/#5 на панели — Filter High/Low (независимые края), но в ewsdr фильтр
// только симметричный (SetFilterBW): #4 крутит ширину, #5 пока не задействован.
AND_ENCODER_MAP: array[0..AND_MAX_ENCODERS-1] of TAndEncoder = (
aeAFGain, // 0 RX1 AF Gain
aeAGCLevel, // 1 RX1 AGC Level (Thetis «RF»)
aeNone, // 2 RX2 AF — нет второго приёмника
aeNone, // 3 RX2 AGC
aeFilterWidth, // 4 Filter High → ширина фильтра (краёв нет)
aeNone, // 5 Filter Low — не реализован
aeNone, // 6 Diversity gain
aeNone, // 7 Diversity phase
aeNone, // 8 RIT
aeNone, // 9 XIT
aeMulti, // 10 Multifunction
aeDrive, // 11 TX Drive
aeNone, aeNone, aeNone, aeNone, aeNone, aeNone, aeNone, aeNone);
// Номер LED (ZZZI nn) для каждого индикатора; 0 = панель этот индикатор не имеет.
// 1..11 — дефолт Thetis; 12..17 — функции ewsdr на свободных слотах.
AND_INDICATOR_NUM: array[TAndIndicator] of Integer = (
{aiNone} 0,
{aiMOX} 1,
{aiTune} 3,
{aiRIT} 8,
{aiXIT} 9,
{aiSplit} 12,
{aiCTune} 7,
{aiVFOSync} 0,
{aiVFOAB} 10,
{aiVFOLock} 11,
{aiNB} 13,
{aiNR} 14,
{aiSNB} 15,
{aiANF} 16,
{aiSquelch} 17,
{aiCompander} 0,
{aiPuresignal} 0,
{aiDiversity} 0,
{aiATUReady} 2,
{aiShift} 6);
{ Возвращает действие для номера кнопки (0-based); abNone вне диапазона. }
function ButtonAction(BtnIndex: Integer): TAndButton;
{ Возвращает параметр для номера энкодера (0-based); aeNone вне диапазона. }
function EncoderParam(EncIndex: Integer): TAndEncoder;
{ Номер ZZZI для индикатора; 0 если панель его не отображает. }
function IndicatorNum(Ind: TAndIndicator): Integer;
implementation
function ButtonAction(BtnIndex: Integer): TAndButton;
begin
if (BtnIndex >= 0) and (BtnIndex < AND_MAX_BUTTONS) then
Result := AND_BUTTON_MAP[BtnIndex]
else
Result := abNone;
end;
function EncoderParam(EncIndex: Integer): TAndEncoder;
begin
if (EncIndex >= 0) and (EncIndex < AND_MAX_ENCODERS) then
Result := AND_ENCODER_MAP[EncIndex]
else
Result := aeNone;
end;
function IndicatorNum(Ind: TAndIndicator): Integer;
begin
Result := AND_INDICATOR_NUM[Ind];
end;
end.