mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
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:
+215
-8
@@ -29,7 +29,7 @@ uses
|
||||
StdCtrls, ExtCtrls,
|
||||
FlatButton, FlatCheckBox, FlatComboBox, FlatEdit, FlatSpinEdit, FlatFloatSpinEdit,
|
||||
FlatRadioButton,
|
||||
EqualizerControl, AudioOutput, AudioInput, AppTheme, Settings;
|
||||
EqualizerControl, AudioOutput, AudioInput, AppTheme, Settings, BoardUtils;
|
||||
|
||||
type
|
||||
// PA: MaxPower в Вт, BandCal[0..10] — калибровка 38.8..100.0 на диапазон
|
||||
@@ -37,6 +37,8 @@ type
|
||||
const BandCal: array of Double) of object;
|
||||
// Pluto TX: аттенюация выходного тракта на drive=100% (потолок), дБ<0
|
||||
TOnPlutoTxChange = procedure(MaxAttDb: Double) of object;
|
||||
// Антенные разъёмы AD936x (Pluto/LibreSDR): таблица «диапазон → RX/TX разъём»
|
||||
TOnAnt936xChange = procedure(const A: TAD936xAnt) of object;
|
||||
// VHF (XVTR) PA calibration: 8 slots, 38.8..100.0
|
||||
TOnVHFCalChange = procedure(const VHFCal: array of Double) of object;
|
||||
// TX-настройки целиком (любая правка → один callback с актуальной структурой)
|
||||
@@ -228,6 +230,21 @@ type
|
||||
FAlexExt1OnTx: TFlatCheckBox;
|
||||
FAlexGndBpf2OnTx: TFlatCheckBox;
|
||||
FAlexEnableXvtrHf: TFlatCheckBox;
|
||||
// Группы вкладки Antenna: Alex-таблица (openHPSDR) vs выбор приёмного входа
|
||||
// AD936x (Pluto/LibreSDR) — показывается одна из двух (SetAntennaBackend).
|
||||
FAlexSubtitle: TLabel;
|
||||
FAlexGrpAnt: TPanel;
|
||||
FAlexGrpTX: TPanel;
|
||||
FPlutoAntGrp: TPanel;
|
||||
// На диапазон (VU-план, 12 слотов) и на слот трансвертера — по паре радио-
|
||||
// кнопок RX (RX1/RX2) и TX (TX1/TX2).
|
||||
FAntRx: array[0..CFG_BAND_COUNT-1, 0..1] of TFlatRadioButton;
|
||||
FAntTx: array[0..CFG_BAND_COUNT-1, 0..1] of TFlatRadioButton;
|
||||
FAntXvtrRx: array[0..CFG_XVTR_COUNT-1, 0..1] of TFlatRadioButton;
|
||||
FAntXvtrTx: array[0..CFG_XVTR_COUNT-1, 0..1] of TFlatRadioButton;
|
||||
FAntXvtrRow: array[0..CFG_XVTR_COUNT-1] of TPanel; // строка слота (скрыта, если выкл.)
|
||||
FAntXvtrLbl: array[0..CFG_XVTR_COUNT-1] of TLabel;
|
||||
FAnt936x: TAD936xAnt;
|
||||
// Current Alex config state
|
||||
FAlexConfig: TAlexSettings;
|
||||
FAlexBoardType: Integer;
|
||||
@@ -292,6 +309,7 @@ type
|
||||
FOnCATChange: TOnCATChange;
|
||||
FOnPAChange: TOnPASettingsChange;
|
||||
FOnPlutoTxChange: TOnPlutoTxChange;
|
||||
FOnAnt936xChange: TOnAnt936xChange;
|
||||
FOnVHFCalChange: TOnVHFCalChange;
|
||||
FOnADCChange: TOnADCChange;
|
||||
FOnWebSettingsChange: TOnWebSettingsChange;
|
||||
@@ -379,6 +397,7 @@ type
|
||||
procedure OnFreqMhzDigitsCmbChange(Sender: TObject);
|
||||
procedure OnMaxPowerChange(Sender: TObject);
|
||||
procedure OnPlutoTxMaxAttChange(Sender: TObject);
|
||||
procedure Ant936xUIChange(Sender: TObject);
|
||||
procedure OnBandCalChange(Sender: TObject);
|
||||
procedure OnVHFCalEdit(Sender: TObject);
|
||||
procedure OnCATSerialChange(Sender: TObject);
|
||||
@@ -425,6 +444,10 @@ type
|
||||
procedure LoadFreqMhzDigits(Digits: Integer);
|
||||
procedure LoadPASettings(MaxPower: Double; const BandCal: array of Double);
|
||||
procedure LoadPlutoTxMaxAtt(MaxAttDb: Double);
|
||||
// Вкладка Antenna: Alex-таблица (openHPSDR) либо таблица разъёмов AD936x.
|
||||
procedure SetAntennaBackend(IsAD936x, HasAntSwitch: Boolean);
|
||||
// Таблица разъёмов + имена/видимость слотов трансвертеров.
|
||||
procedure LoadAnt936x(const A: TAD936xAnt; const X: TXvtrSettings);
|
||||
procedure LoadVHFCalSettings(const VHFCal: array of Double);
|
||||
procedure LoadCATSettings(
|
||||
const SerEnabled: array of Boolean;
|
||||
@@ -448,6 +471,7 @@ type
|
||||
property OnXvtrChange: TOnXvtrChange read FOnXvtrChange write FOnXvtrChange;
|
||||
property OnPAChange: TOnPASettingsChange read FOnPAChange write FOnPAChange;
|
||||
property OnPlutoTxChange: TOnPlutoTxChange read FOnPlutoTxChange write FOnPlutoTxChange;
|
||||
property OnAnt936xChange: TOnAnt936xChange read FOnAnt936xChange write FOnAnt936xChange;
|
||||
property OnVHFCalChange: TOnVHFCalChange read FOnVHFCalChange write FOnVHFCalChange;
|
||||
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
|
||||
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
|
||||
@@ -2977,6 +3001,12 @@ const
|
||||
TX3_X = PAD + 360;
|
||||
DNTX_X = PAD + 398;
|
||||
GRP_W = DNTX_X + 80;
|
||||
// Таблица разъёмов AD936x (Pluto/LibreSDR) — та же сетка, свои колонки.
|
||||
ANT_RX1_X = PAD + 90;
|
||||
ANT_RX2_X = PAD + 116;
|
||||
ANT_TX1_X = PAD + 160;
|
||||
ANT_TX2_X = PAD + 186;
|
||||
ANT_GRP_W = ANT_TX2_X + 60;
|
||||
GRP_H = GRP_TITLE_H + HDR1_H + HDR2_H + BANDS * ROW_H + PAD;
|
||||
DATA_Y0 = GRP_TITLE_H + HDR1_H + HDR2_H + 4;
|
||||
TXGRP_H = GRP_TITLE_H + 4 * 28 + PAD;
|
||||
@@ -3029,6 +3059,44 @@ var
|
||||
Result.OnChange := OnAlexAnyChange;
|
||||
end;
|
||||
|
||||
// ---- Таблица разъёмов AD936x ----
|
||||
function MkAntRb(AParent: TWinControl; AX: Integer): TFlatRadioButton;
|
||||
begin
|
||||
Result := TFlatRadioButton.Create(Self);
|
||||
Result.Parent := AParent;
|
||||
Result.Caption := '';
|
||||
Result.SetBounds(AX, 0, 22, 20);
|
||||
Result.Font.Color := CLR_TEXT;
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
Result.OnChange := Ant936xUIChange;
|
||||
end;
|
||||
|
||||
function MkAntLbl(AParent: TWinControl; const ACap: string;
|
||||
AX, AY: Integer): TLabel;
|
||||
begin
|
||||
Result := TLabel.Create(Self);
|
||||
Result.Parent := AParent;
|
||||
Result.Caption := ACap;
|
||||
Result.SetBounds(AX, AY, 84, 18);
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
Result.Font.Color := CLR_TEXT;
|
||||
end;
|
||||
|
||||
procedure MkAntHdr(AParent: TWinControl; const ACap: string; AX, AY: Integer);
|
||||
begin
|
||||
with TLabel.Create(Self) do
|
||||
begin
|
||||
Parent := AParent;
|
||||
Caption := ACap;
|
||||
SetBounds(AX, AY, 30, 16);
|
||||
Font.Name := UI_FONT;
|
||||
Font.Size := 9;
|
||||
Font.Color := CLR_TEXTDIM;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
@@ -3040,13 +3108,14 @@ begin
|
||||
Font.Name := UI_FONT; Font.Size := 16;
|
||||
Font.Style := [fsBold]; Font.Color := CLR_TEXT;
|
||||
end;
|
||||
with TLabel.Create(Self) do
|
||||
begin
|
||||
Parent := FPageAlex;
|
||||
Caption := 'Per-band RX/TX antenna and secondary path routing for the Alex filter board.';
|
||||
SetBounds(MARGIN, 48, 680, 20);
|
||||
Font.Name := UI_FONT; Font.Size := 9; Font.Color := CLR_TEXTDIM;
|
||||
end;
|
||||
FAlexSubtitle := TLabel.Create(Self);
|
||||
FAlexSubtitle.Parent := FPageAlex;
|
||||
FAlexSubtitle.Caption :=
|
||||
'Per-band RX/TX antenna and secondary path routing for the Alex filter board.';
|
||||
FAlexSubtitle.SetBounds(MARGIN, 48, 680, 20);
|
||||
FAlexSubtitle.Font.Name := UI_FONT;
|
||||
FAlexSubtitle.Font.Size := 9;
|
||||
FAlexSubtitle.Font.Color := CLR_TEXTDIM;
|
||||
|
||||
// ---- Antenna grid group ----
|
||||
Grp := MakeGroupPanel(FPageAlex, 'Antenna Selection', MARGIN, 86, GRP_W, GRP_H);
|
||||
@@ -3123,11 +3192,149 @@ begin
|
||||
PAD, GRP_TITLE_H + 8 + 56, 440);
|
||||
FAlexEnableXvtrHf := MkCh(GrpTX, 'Enable XVTR HF',
|
||||
PAD, GRP_TITLE_H + 8 + 84, 440);
|
||||
FAlexGrpAnt := Grp;
|
||||
FAlexGrpTX := GrpTX;
|
||||
|
||||
// ---- Антенные разъёмы AD936x (Pluto/LibreSDR) ----
|
||||
// Alex-таблицы у таких плат нет; «антенна» = какой из физических разъёмов
|
||||
// (RX1/RX2, TX1/TX2) используется на диапазоне. Строки — VU-план + слоты
|
||||
// трансвертеров (QO-100 и пр.). Группа занимает место Alex-групп и
|
||||
// показывается вместо них (SetAntennaBackend).
|
||||
FPlutoAntGrp := MakeGroupPanel(FPageAlex, 'Antenna Ports',
|
||||
MARGIN, 86, ANT_GRP_W,
|
||||
GRP_TITLE_H + HDR2_H + (CFG_BAND_COUNT + CFG_XVTR_COUNT) * ROW_H + PAD);
|
||||
FPlutoAntGrp.Visible := False;
|
||||
|
||||
MkAntHdr(FPlutoAntGrp, 'RX', ANT_RX1_X - 4, GRP_TITLE_H - 2);
|
||||
MkAntHdr(FPlutoAntGrp, 'TX', ANT_TX1_X - 4, GRP_TITLE_H - 2);
|
||||
MkAntHdr(FPlutoAntGrp, '1', ANT_RX1_X + 4, GRP_TITLE_H + 14);
|
||||
MkAntHdr(FPlutoAntGrp, '2', ANT_RX2_X + 4, GRP_TITLE_H + 14);
|
||||
MkAntHdr(FPlutoAntGrp, '1', ANT_TX1_X + 4, GRP_TITLE_H + 14);
|
||||
MkAntHdr(FPlutoAntGrp, '2', ANT_TX2_X + 4, GRP_TITLE_H + 14);
|
||||
|
||||
Y := GRP_TITLE_H + HDR2_H + 4;
|
||||
for b := 0 to CFG_BAND_COUNT - 1 do
|
||||
begin
|
||||
// Пустой слот VU-плана (12-й у HF) не рисуем — строка остаётся пустой.
|
||||
if BandName(b, True) <> '' then
|
||||
begin
|
||||
MkAntLbl(FPlutoAntGrp, BandName(b, True), BAND_X, Y + 2);
|
||||
RxGrp := MkRbGroup(FPlutoAntGrp, ANT_RX1_X, Y, 60);
|
||||
FAntRx[b, 0] := MkAntRb(RxGrp, 0);
|
||||
FAntRx[b, 1] := MkAntRb(RxGrp, ANT_RX2_X - ANT_RX1_X);
|
||||
TxGrp := MkRbGroup(FPlutoAntGrp, ANT_TX1_X, Y, 60);
|
||||
FAntTx[b, 0] := MkAntRb(TxGrp, 0);
|
||||
FAntTx[b, 1] := MkAntRb(TxGrp, ANT_TX2_X - ANT_TX1_X);
|
||||
end;
|
||||
Inc(Y, ROW_H);
|
||||
end;
|
||||
|
||||
// Слоты трансвертеров: строка на слот, показываем только включённые
|
||||
// (LoadAnt936x подставит имя и видимость).
|
||||
for b := 0 to CFG_XVTR_COUNT - 1 do
|
||||
begin
|
||||
FAntXvtrRow[b] := TPanel.Create(Self);
|
||||
FAntXvtrRow[b].Parent := FPlutoAntGrp;
|
||||
FAntXvtrRow[b].SetBounds(0, Y, ANT_GRP_W, ROW_H);
|
||||
FAntXvtrRow[b].BevelOuter := bvNone;
|
||||
FAntXvtrRow[b].Color := CLR_PANEL;
|
||||
FAntXvtrRow[b].Caption := '';
|
||||
FAntXvtrRow[b].Visible := False;
|
||||
FAntXvtrLbl[b] := MkAntLbl(FAntXvtrRow[b], '', BAND_X, 2);
|
||||
RxGrp := MkRbGroup(FAntXvtrRow[b], ANT_RX1_X, 0, 60);
|
||||
FAntXvtrRx[b, 0] := MkAntRb(RxGrp, 0);
|
||||
FAntXvtrRx[b, 1] := MkAntRb(RxGrp, ANT_RX2_X - ANT_RX1_X);
|
||||
TxGrp := MkRbGroup(FAntXvtrRow[b], ANT_TX1_X, 0, 60);
|
||||
FAntXvtrTx[b, 0] := MkAntRb(TxGrp, 0);
|
||||
FAntXvtrTx[b, 1] := MkAntRb(TxGrp, ANT_TX2_X - ANT_TX1_X);
|
||||
Inc(Y, ROW_H);
|
||||
end;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.SetAntennaBackend(IsAD936x, HasAntSwitch: Boolean);
|
||||
// Переключает вкладку Antenna: Alex-таблица (openHPSDR) либо таблица разъёмов
|
||||
// AD936x. HasAntSwitch=False (стоковый Pluto, один разъём) — Alex-таблица тоже
|
||||
// не нужна: показываем пустую вкладку с поясняющим подзаголовком.
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
if FAlexGrpAnt <> nil then FAlexGrpAnt.Visible := not IsAD936x;
|
||||
if FAlexGrpTX <> nil then FAlexGrpTX.Visible := not IsAD936x;
|
||||
if FPlutoAntGrp <> nil then FPlutoAntGrp.Visible := IsAD936x and HasAntSwitch;
|
||||
if FAlexSubtitle <> nil then
|
||||
begin
|
||||
if IsAD936x and HasAntSwitch then
|
||||
FAlexSubtitle.Caption :=
|
||||
'Which physical connector (RX1/RX2, TX1/TX2) is used on each band.'
|
||||
else if IsAD936x then
|
||||
FAlexSubtitle.Caption := 'This board has a single antenna connector pair.'
|
||||
else
|
||||
FAlexSubtitle.Caption :=
|
||||
'Per-band RX/TX antenna and secondary path routing for the Alex filter board.';
|
||||
end;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadAnt936x(const A: TAD936xAnt; const X: TXvtrSettings);
|
||||
// Таблица разъёмов + строки трансвертеров (видимы только включённые слоты).
|
||||
var b: Integer;
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
FAnt936x := A;
|
||||
for b := 0 to CFG_BAND_COUNT - 1 do
|
||||
begin
|
||||
if FAntRx[b, 0] = nil then Continue;
|
||||
FAntRx[b, 0].Checked := A.RxAnt[b] = 0;
|
||||
FAntRx[b, 1].Checked := A.RxAnt[b] = 1;
|
||||
FAntTx[b, 0].Checked := A.TxAnt[b] = 0;
|
||||
FAntTx[b, 1].Checked := A.TxAnt[b] = 1;
|
||||
end;
|
||||
for b := 0 to CFG_XVTR_COUNT - 1 do
|
||||
begin
|
||||
if FAntXvtrRow[b] = nil then Continue;
|
||||
FAntXvtrRow[b].Visible := X.Entries[b].Enabled;
|
||||
if X.Entries[b].ButtonText <> '' then
|
||||
FAntXvtrLbl[b].Caption := string(X.Entries[b].ButtonText)
|
||||
else
|
||||
FAntXvtrLbl[b].Caption := Format('XVTR %d', [b + 1]);
|
||||
FAntXvtrRx[b, 0].Checked := A.XvtrRxAnt[b] = 0;
|
||||
FAntXvtrRx[b, 1].Checked := A.XvtrRxAnt[b] = 1;
|
||||
FAntXvtrTx[b, 0].Checked := A.XvtrTxAnt[b] = 0;
|
||||
FAntXvtrTx[b, 1].Checked := A.XvtrTxAnt[b] = 1;
|
||||
end;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.Ant936xUIChange(Sender: TObject);
|
||||
// Любая радиокнопка таблицы разъёмов: собрать состояние и отдать наружу.
|
||||
var b: Integer;
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
for b := 0 to CFG_BAND_COUNT - 1 do
|
||||
begin
|
||||
if FAntRx[b, 0] = nil then Continue;
|
||||
if FAntRx[b, 1].Checked then FAnt936x.RxAnt[b] := 1 else FAnt936x.RxAnt[b] := 0;
|
||||
if FAntTx[b, 1].Checked then FAnt936x.TxAnt[b] := 1 else FAnt936x.TxAnt[b] := 0;
|
||||
end;
|
||||
for b := 0 to CFG_XVTR_COUNT - 1 do
|
||||
begin
|
||||
if FAntXvtrRx[b, 0] = nil then Continue;
|
||||
if FAntXvtrRx[b, 1].Checked then FAnt936x.XvtrRxAnt[b] := 1
|
||||
else FAnt936x.XvtrRxAnt[b] := 0;
|
||||
if FAntXvtrTx[b, 1].Checked then FAnt936x.XvtrTxAnt[b] := 1
|
||||
else FAnt936x.XvtrTxAnt[b] := 0;
|
||||
end;
|
||||
if Assigned(FOnAnt936xChange) then FOnAnt936xChange(FAnt936x);
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Alex event handlers and data collectors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user