feat(pluto): выбор антенного разъёма RX/TX на диапазон (LibreSDR/AntSDR)

Второй разъём у AD936x-плат — это второй КАНАЛ чипа (не порт B первого), и в
1R1T-образе он выбирается мультиплексором AD9361: debug-параметры
adi,1rx-1tx-mode-use-rx-num/-tx-num + биты RX1/RX2, TX1/TX2 регистров 0x3/0x2
(как switch_rfinput.sh/switch_rfoutput.sh прошивки). rf_port_select тут ни при
чём — прошивка его к тому же блокирует.

- IIOBindings: debug-атрибуты + прямой доступ к регистрам.
- PlutoBackend: детект второго разъёма (2R2T-каналы либо мультиплексор + модель
  платы), SetAntPorts(RxPort,TxPort), ApplyRxMux/ApplyTxMux; Caps.HasAntSwitch.
  ⚠ApplyGain(Force): драйвер пропускает запись gain_control_mode, если считает
  режим неизменным, — а у нового канала AGC-регистры не запрограммированы (в
  железе крутится auto-AGC, hardwaregain отвергается EOPNOTSUPP). Форсируем
  перепрог через другой режим после смены разъёма и после ad9361_set_bb_rate.
- RadioBackend: TBackendCaps.HasAntSwitch + virtual SetAntPorts (HPSDR — no-op,
  там антеннами рулит Alex).
- Settings: секция ant936x (разъём RX/TX на диапазон и на слот трансвертера);
  hw-AGC (режим + manual gain) переехал в TBandSettings/TXvtrEntry — теперь он
  свой на каждом диапазоне и трансвертере.
- RadioController: FAnt936x + ApplyAntPorts (RestoreBand/ActivateXvtr/
  StartRunning); ApplyBandDSP восстанавливает hw-AGC диапазона.
- SettingsForm: вкладка Antenna — Alex-таблица (openHPSDR) либо таблица
  разъёмов AD936x по диапазонам/трансвертерам.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 21:26:18 +03:00
co-authored by Claude Opus 4.8
parent a2e403af3d
commit 9dd38e1dd3
8 changed files with 673 additions and 32 deletions
+30 -4
View File
@@ -663,6 +663,7 @@ type
Shift: TShiftState; X, Y: Integer);
procedure LayoutPanStack(AWideH: Integer); // регионы панов + Layout каждого
function PanMinHeight(PanId: Integer): Integer; // мин. высота региона пана
function Pan0HeaderVisible: Boolean; // шапка пана 0: стек ИЛИ 2 входа
function Pan0HeaderH: Integer; // высота шапки пана 0 (0 без стека)
procedure PbSpectrumDblClick(Sender: TObject);
procedure TrkDriveChange(Sender: TObject);
@@ -674,6 +675,7 @@ type
procedure ApplyMOX(Active: Boolean);
procedure OnPASettingsChange(MaxPower: Double; const BandCal: array of Double);
procedure OnPlutoTxMaxAttChange(MaxAttDb: Double);
procedure OnAnt936xChange(const A: TAD936xAnt); // разъёмы AD936x (Antenna)
procedure OnVHFCalSettingsChange(const VHFCal: array of Double);
procedure OnTXSettingsChange(const T: TTXSettings);
procedure BtnAGCModeClick(Sender: TObject);
@@ -2629,9 +2631,18 @@ begin
PositionSampleRateOverlay;
end;
function TMainForm.Pan0HeaderVisible: Boolean;
// Шапка пана 0 — только при стеке панов (у одиночного пана она лишняя). Единый
// источник правды: LayoutPanStack (обе ветки), Pan0HeaderH, UpdatePanHeaders.
// Выбор приёмного входа у одноканальных по панам бэкендов (Pluto/LibreSDR) живёт
// не здесь, а во вкладке Antenna настроек.
begin
Result := PanCount > 1;
end;
function TMainForm.Pan0HeaderH: Integer;
begin
if PanCount > 1 then
if Pan0HeaderVisible then
Result := MulDiv(20, Screen.PixelsPerInch, 96) // = HEADER_H панели
else
Result := 0;
@@ -2711,7 +2722,8 @@ begin
for k := 0 to N - 1 do
begin
i := ActiveIds[k];
FPans[i].HeaderVisible := PanCount > 1;
if i = 0 then FPans[0].HeaderVisible := Pan0HeaderVisible
else FPans[i].HeaderVisible := PanCount > 1;
MinH[k] := PanMinHeight(i);
Inc(MinRest, MinH[k]);
end;
@@ -2758,7 +2770,7 @@ begin
Row0H := Round((TotalH - PAN_SPLIT_H) * FPanGridRowShare);
// Кламп ряда минимумами содержимого (шапки — до расчёта минимумов):
// ни пан 0, ни колонки нижнего ряда не смеют стать ниже своих минимумов.
FPans[0].HeaderVisible := PanCount > 1;
FPans[0].HeaderVisible := Pan0HeaderVisible;
BotMin := 0;
for k := 1 to N - 1 do
begin
@@ -6069,7 +6081,8 @@ begin
begin
P := FPans[i];
if P = nil then Continue;
P.HeaderVisible := (N > 1) or ((i > 0) and PanFloating(i));
if i = 0 then P.HeaderVisible := Pan0HeaderVisible
else P.HeaderVisible := (N > 1) or PanFloating(i);
if P.BtnClosePan <> nil then P.BtnClosePan.Visible := (N > 1) and (i > 0);
if P.BtnPopPan <> nil then
P.BtnPopPan.Visible := (N > 1) and (i > 0) and not PanFloating(i);
@@ -7120,6 +7133,13 @@ begin
UpdateDriveLabel; // изменился потолок → пересчитать дБ-подпись drive
end;
procedure TMainForm.OnAnt936xChange(const A: TAD936xAnt);
// Вкладка Antenna (AD936x): таблица «диапазон → разъём». Контроллер сохраняет её
// и сразу применяет разъёмы текущего диапазона/трансвертера.
begin
FController.SetAnt936x(A);
end;
procedure TMainForm.OnVHFCalSettingsChange(const VHFCal: array of Double);
var i: Integer;
begin
@@ -7906,6 +7926,7 @@ begin
SF.OnFPSChange := ApplyFPS;
SF.OnPAChange := OnPASettingsChange;
SF.OnPlutoTxChange := OnPlutoTxMaxAttChange;
SF.OnAnt936xChange := OnAnt936xChange;
SF.OnVHFCalChange := OnVHFCalSettingsChange;
SF.OnCATChange := OnCATSettingsChange;
SF.OnThemeChange := SetLightTheme;
@@ -7922,6 +7943,11 @@ begin
IfThen(FController.FNetwork.Connected, FController.FNetwork.Device.BoardType, FPendingBoardType));
SF.LoadAlexSettings(FController.FAlexSettings,
IfThen(FController.FNetwork.Connected, FController.FNetwork.Device.BoardType, FPendingBoardType));
// Вкладка Antenna: у AD936x (Pluto/LibreSDR) вместо Alex-таблицы — таблица
// физических разъёмов на диапазон/трансвертер (если разъёмов две пары).
SF.SetAntennaBackend(FController.BackendCaps.Kind = bkPluto,
FController.BackendCaps.HasAntSwitch);
SF.LoadAnt936x(FController.Ant936x, FController.FXvtrSettings);
SF.LoadXvtrSettings(FController.FXvtrSettings);
SF.LoadPASettings(FController.FPAMaxPower, FController.FPABandCal);
SF.LoadPlutoTxMaxAtt(FController.FPlutoTxMaxAttDb);