feat(ui): настройки показывают только то, что есть у активного железа

В TBackendCaps восемь флагов заполнялись обоими бэкендами и не читались нигде,
а в ApplyBackendCapsToUI на этом месте стояла расписка «(TODO) гейтинг полей,
которых нет у бэкенда». Из-за этого на AD936x оператору предлагали крутить то,
чего у платы нет, — и хуже всего страница PA: калибровка драйва по диапазонам
там просто ничего не делает (мощность задаётся железной аттенюацией, drive-байта
нет вовсе), но выглядела рабочей.

Теперь гейт идёт по возможностям, а не по виду устройства:
- HasPA — потолок мощности и калибровки драйва (HF + трансвертеры) против
  «TX att @100% drive»; заголовок и подзаголовок страницы следуют содержимому;
- HasHWMic — строка микрофонного разъёма (DUC Specific байт 50). Mic gain
  остаётся: он идёт в WDSP и работает с любым источником, включая звук с ПК,
  поэтому поднимается в освободившуюся строку;
- HasWideband — галки широкополосного АЦП; сам режим принудительно гасится,
  иначе сохранённый в конфиге wideband оставлял бы пустую панель без способа
  её выключить;
- HasDitherRandom — группа ADC целиком, Web Server встаёт на её место.
Вкладка QO-100 убрана на openHPSDR: TRadioController.InQO100 требует IsPluto,
то есть транспондер там недостижим. Симметрично OC Control, которого нет у
AD936x.

Измеритель: TSMeterView.HasPATelemetry. Без моста детектора FLastFwdW/FLastSWR
стоят на значениях инициализации, и «0W / SWR 1.0» на передаче — не показание,
а выдумка. Шкала рисуется пустой: деления и рамка на месте, заливки нет, ватты
на делениях не подписаны (они считаются от Max power, которого у AD936x нет).

★Отдельно — дефект раскладки, который всё это вскрыло. Перекладка страницы
настроек ломается в двух случаях, и оба случались при смене трансивера на живой
программе (Stop/Discover/Connect/Start), а не при старте:

1. Страница ПРОКРУЧЕНА. TScrollBox двигает детей, и SetBounds задаёт координаты
   относительно прокрученного клиента: всё уезжало вверх, а первая группа
   оставалась на месте — под переключателями Profile/EQ/Hardware зияла дыра.
   Лечение — сброс прокрутки первой строкой (так уже делал RelayoutCalTab).
2. Страница СПРЯТАНА. Тогда сброс не доходит до виджета, а сохранённое смещение
   возвращается при показе — и дыра та же. А попадает перекладка почти всегда
   именно на спрятанную страницу: гейты отрабатывают при открытии окна, когда
   активна одна вкладка, а до Transmit/Calibration оператор доходит потом.
   Лечение — перекладка спрятанной страницы взводит флаг и выходит, а SelectPage
   исполняет её в момент показа (RunDueRelayout). Гард стоит внутри каждой
   Relayout*, а не у вызывающих: у RelayoutCalTab их два, и второй
   (LoadXvtrSettings) приходит как раз на спрятанную страницу.

Проверено офскрин-рендером окна настроек в обе стороны (openHPSDR ↔ AD936x) для
Transmit/Power Amplifier/Calibration/Advanced, в том числе на прокрученной и
спрятанной странице. При полном железе координаты совпадают со старыми.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M37eCTZZ9aqppKWkZRxXkP
This commit is contained in:
2026-08-25 10:16:31 +03:00
co-authored by Claude Opus 5
parent eedaffad78
commit f99443620a
4 changed files with 435 additions and 27 deletions
+6
View File
@@ -230,6 +230,8 @@ type
procedure SetLastSWR(V: Double);
function GetPAMaxPower: Double;
procedure SetPAMaxPower(V: Double);
function GetHasPATelemetry: Boolean;
procedure SetHasPATelemetry(V: Boolean);
function GetTransmitting: Boolean;
procedure SetTransmitting(V: Boolean);
procedure SetPbSMeterRight(V: TPaintBox);
@@ -301,6 +303,8 @@ type
property LastFwdW: Double read GetLastFwdW write SetLastFwdW;
property LastSWR: Double read GetLastSWR write SetLastSWR;
property PAMaxPower: Double read GetPAMaxPower write SetPAMaxPower;
// Есть ли мост детектора мощности: без него шкала PWR/SWR рисуется пустой.
property HasPATelemetry: Boolean read GetHasPATelemetry write SetHasPATelemetry;
property Transmitting: Boolean read GetTransmitting write SetTransmitting;
// ── Флаги обновления ──────────────────────────────────────────────────────
@@ -612,6 +616,8 @@ function TSpectrumView.GetLastSWR: Double; begin Result := FSMeter.Las
procedure TSpectrumView.SetLastSWR(V: Double); begin FSMeter.LastSWR := V; end;
function TSpectrumView.GetPAMaxPower: Double; begin Result := FSMeter.PAMaxPower; end;
procedure TSpectrumView.SetPAMaxPower(V: Double); begin FSMeter.PAMaxPower := V; end;
function TSpectrumView.GetHasPATelemetry: Boolean; begin Result := FSMeter.HasPATelemetry; end;
procedure TSpectrumView.SetHasPATelemetry(V: Boolean); begin FSMeter.HasPATelemetry := V; end;
function TSpectrumView.GetTransmitting: Boolean; begin Result := FSMeter.Transmitting; end;
procedure TSpectrumView.SetTransmitting(V: Boolean); begin FSMeter.Transmitting := V; end;
procedure TSpectrumView.SetPbSMeterRight(V: TPaintBox); begin FSMeter.PbSMeterRight := V; end;