add Alex setup

This commit is contained in:
2026-05-14 18:52:50 +03:00
parent ec596bb0c8
commit aea9f02df8
4 changed files with 559 additions and 52 deletions
+348 -11
View File
@@ -54,6 +54,7 @@ type
const SerStopBits: array of Integer;
const SerParity: array of Integer;
TcpEnabled: Boolean; TcpPort: Integer) of object;
TOnAlexChange = procedure(const A: TAlexSettings) of object;
{ TSettingsForm }
TSettingsForm = class(TForm)
@@ -176,6 +177,29 @@ type
FEdTXRange: TEdit;
FCmbTXGridStep: TComboBox;
// ---- Alex / Antenna tab controls ----
FPageAlex: TScrollBox;
FNavAlex: TFlatButton;
// Per-band antenna radio buttons (bands 0..10 = 160m..6m)
FAlexRxAnt: array[0..10, 1..3] of TRadioButton;
FAlexTxAnt: array[0..10, 1..3] of TRadioButton;
// Per-band secondary RX (взаимно исключающие checkboxes)
FAlexRxByps: array[0..10] of TCheckBox; // BYPS: Ext1+Bypass
FAlexRxExt1: array[0..10] of TCheckBox; // EXT1: Ext2+Bypass
FAlexRxXvtr: array[0..10] of TCheckBox; // XVTR: XvtrDDC+Bypass
// Per-band Do Not TX
FAlexDoNotTx: array[0..10] of TCheckBox;
// Global TX routing flags
FAlexRxBypassOnTx: TCheckBox;
FAlexExt1OnTx: TCheckBox;
FAlexGndBpf2OnTx: TCheckBox;
FAlexEnableXvtrHf: TCheckBox;
// Current Alex config state
FAlexConfig: TAlexSettings;
FAlexBoardType: Integer;
// Callback
FOnAlexChange: TOnAlexChange;
// ---- CAT tab controls ----
FCATSerialEn: array[0..3] of TCheckBox;
FCATSerialPort: array[0..3] of TEdit;
@@ -211,6 +235,11 @@ type
procedure BuildPATab;
procedure BuildCATTab;
procedure BuildTransmitTab;
procedure BuildAlexTab;
procedure CollectAlexFromUI;
procedure FireAlexChange;
procedure OnAlexAnyChange(Sender: TObject);
procedure OnAlexRxOnlyChange(Sender: TObject);
procedure CollectTXFromUI;
procedure FireTXChange;
procedure OnTXAnyChange(Sender: TObject);
@@ -288,8 +317,10 @@ type
const SerBaud, SerDataBits, SerStopBits, SerParity: array of Integer;
TcpEnabled: Boolean; TcpPort: Integer);
procedure LoadTXSettings(const T: TTXSettings);
procedure LoadAlexSettings(const A: TAlexSettings; BoardType: Integer);
property OnCATChange: TOnCATChange read FOnCATChange write FOnCATChange;
property OnAlexChange: TOnAlexChange read FOnAlexChange write FOnAlexChange;
property OnPAChange: TOnPASettingsChange read FOnPAChange write FOnPAChange;
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
@@ -508,6 +539,7 @@ begin
FNavPA := MakeNavButton('Power Amplifier', 254);
FNavAdvanced := MakeNavButton('Advanced', 290);
FNavCAT := MakeNavButton('CAT', 326);
FNavAlex := MakeNavButton('Antenna / Alex', 362);
FContentPanel := TPanel.Create(Self);
FContentPanel.Parent := Self;
@@ -523,6 +555,9 @@ begin
FPagePA := MakeScrollPage;
FPageAdvanced := MakeScrollPage;
FPageCAT := MakeScrollPage;
FPageAlex := MakeScrollPage;
FAlexBoardType := 0;
TSettingsManager.DefaultAlex(FAlexConfig);
BuildAudioTab;
BuildDisplayTab;
@@ -530,6 +565,7 @@ begin
BuildTransmitTab;
BuildPATab;
BuildCATTab;
BuildAlexTab;
FBtnClose := TFlatButton.Create(Self);
FBtnClose.Parent := Self;
@@ -558,28 +594,31 @@ begin
else if Sender = FNavTransmit then SelectPage(FPageTransmit, FNavTransmit)
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced)
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT);
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT)
else if Sender = FNavAlex then SelectPage(FPageAlex, FNavAlex);
end;
procedure TSettingsForm.SelectPage(APage: TWinControl; ANav: TFlatButton);
begin
FPageAudio.Visible := APage = FPageAudio;
FPageDisplay.Visible := APage = FPageDisplay;
FPageAudio.Visible := APage = FPageAudio;
FPageDisplay.Visible := APage = FPageDisplay;
FPageSpectrum.Visible := APage = FPageSpectrum;
FPageWaterfall.Visible := APage = FPageWaterfall;
FPageWaterfall.Visible:= APage = FPageWaterfall;
FPageTransmit.Visible := APage = FPageTransmit;
FPagePA.Visible := APage = FPagePA;
FPagePA.Visible := APage = FPagePA;
FPageAdvanced.Visible := APage = FPageAdvanced;
FPageCAT.Visible := APage = FPageCAT;
FPageCAT.Visible := APage = FPageCAT;
FPageAlex.Visible := APage = FPageAlex;
FNavAudio.Active := ANav = FNavAudio;
FNavDisplay.Active := ANav = FNavDisplay;
FNavAudio.Active := ANav = FNavAudio;
FNavDisplay.Active := ANav = FNavDisplay;
FNavSpectrum.Active := ANav = FNavSpectrum;
FNavWaterfall.Active := ANav = FNavWaterfall;
FNavWaterfall.Active:= ANav = FNavWaterfall;
FNavTransmit.Active := ANav = FNavTransmit;
FNavPA.Active := ANav = FNavPA;
FNavPA.Active := ANav = FNavPA;
FNavAdvanced.Active := ANav = FNavAdvanced;
FNavCAT.Active := ANav = FNavCAT;
FNavCAT.Active := ANav = FNavCAT;
FNavAlex.Active := ANav = FNavAlex;
end;
// ---------------------------------------------------------------------------
@@ -2194,4 +2233,302 @@ begin
end;
end;
// ---------------------------------------------------------------------------
// BuildAlexTab
// ---------------------------------------------------------------------------
procedure TSettingsForm.BuildAlexTab;
const
MARGIN = 22;
PAD = 12;
HDR1_H = 22; // height of "RX Ant / TX Ant" header row
HDR2_H = 20; // height of "1 2 3 / BYPS EXT1 XVTR" sub-header row
ROW_H = 24;
BANDS = 11;
BAND_X = PAD;
RX1_X = PAD + 50;
RX2_X = PAD + 76;
RX3_X = PAD + 102;
BYPS_X = PAD + 136;
EXT1_X = PAD + 192;
XVTR_X = PAD + 248;
TX1_X = PAD + 308;
TX2_X = PAD + 334;
TX3_X = PAD + 360;
DNTX_X = PAD + 398;
GRP_W = DNTX_X + 80;
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;
BAND_NAMES: array[0..10] of string =
('160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m');
var
Grp, GrpTX: TPanel;
b: Integer;
Y: Integer;
Chk: TCheckBox;
RxGrp, TxGrp: TPanel;
// In Lazarus, TRadioButton has no GroupIndex; buttons are grouped by parent.
// We use small invisible panels as parents so each band's RX and TX groups
// are independent of each other.
function MkRbGroup(AParent: TWinControl; AX, AY, AW: Integer): TPanel;
begin
Result := TPanel.Create(Self);
Result.Parent := AParent;
Result.SetBounds(AX, AY, AW, 20);
Result.BevelOuter := bvNone;
Result.Color := CLR_PANEL;
Result.Caption := '';
end;
function MkRb(AParent: TWinControl; AX: Integer): TRadioButton;
begin
Result := TRadioButton.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 := OnAlexAnyChange;
end;
function MkCh(AParent: TWinControl; const ACap: string;
AX, AY, AW: Integer): TCheckBox;
begin
Result := TCheckBox.Create(Self);
Result.Parent := AParent;
Result.Caption := ACap;
Result.SetBounds(AX, AY, AW, 20);
Result.Font.Color := CLR_TEXT;
Result.Font.Name := UI_FONT;
Result.Font.Size := 9;
Result.OnChange := OnAlexAnyChange;
end;
begin
FLoading := True;
try
with TLabel.Create(Self) do
begin
Parent := FPageAlex;
Caption := 'Antenna / Alex';
SetBounds(MARGIN, 18, 500, 28);
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;
// ---- Antenna grid group ----
Grp := MakeGroupPanel(FPageAlex, 'Antenna Selection', MARGIN, 86, GRP_W, GRP_H);
// Header row 1 — group labels
MakeLbl(Grp, 'Band', BAND_X, GRP_TITLE_H + 2, 44);
MakeLbl(Grp, 'RX Antenna', RX1_X, GRP_TITLE_H + 2, 80);
MakeLbl(Grp, 'Secondary RX', BYPS_X, GRP_TITLE_H + 2, 170);
MakeLbl(Grp, 'TX Antenna', TX1_X, GRP_TITLE_H + 2, 80);
MakeLbl(Grp, 'No TX', DNTX_X, GRP_TITLE_H + 2, 56);
// Header row 2 — sub-column labels
MakeLbl(Grp, '1', RX1_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
MakeLbl(Grp, '2', RX2_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
MakeLbl(Grp, '3', RX3_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
MakeLbl(Grp, 'BYPS', BYPS_X, GRP_TITLE_H + HDR1_H + 2, 52);
MakeLbl(Grp, 'EXT1', EXT1_X, GRP_TITLE_H + HDR1_H + 2, 52);
MakeLbl(Grp, 'XVTR', XVTR_X, GRP_TITLE_H + HDR1_H + 2, 52);
MakeLbl(Grp, '1', TX1_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
MakeLbl(Grp, '2', TX2_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
MakeLbl(Grp, '3', TX3_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
// Data rows (bands 0..10 = 160m..6m)
for b := 0 to BANDS - 1 do
begin
Y := DATA_Y0 + b * ROW_H;
MakeLbl(Grp, BAND_NAMES[b], BAND_X, Y + 2, 44);
// RX antenna — 3 radio buttons in a dedicated panel so they are mutually
// exclusive only within that band row (Lazarus groups by parent, not GroupIndex)
RxGrp := MkRbGroup(Grp, RX1_X, Y, RX3_X - RX1_X + 26);
FAlexRxAnt[b, 1] := MkRb(RxGrp, 0);
FAlexRxAnt[b, 2] := MkRb(RxGrp, RX2_X - RX1_X);
FAlexRxAnt[b, 3] := MkRb(RxGrp, RX3_X - RX1_X);
FAlexRxAnt[b, 1].Checked := True;
// Secondary RX checkboxes — mutually exclusive, handled in OnAlexRxOnlyChange
// Tag encodes: b=BYPS, b+100=EXT1, b+200=XVTR
Chk := MkCh(Grp, '', BYPS_X, Y, 22);
Chk.Tag := b;
Chk.OnChange := OnAlexRxOnlyChange;
FAlexRxByps[b] := Chk;
Chk := MkCh(Grp, '', EXT1_X, Y, 22);
Chk.Tag := b + 100;
Chk.OnChange := OnAlexRxOnlyChange;
FAlexRxExt1[b] := Chk;
Chk := MkCh(Grp, '', XVTR_X, Y, 22);
Chk.Tag := b + 200;
Chk.OnChange := OnAlexRxOnlyChange;
FAlexRxXvtr[b] := Chk;
// TX antenna — same pattern: dedicated panel per band row
TxGrp := MkRbGroup(Grp, TX1_X, Y, TX3_X - TX1_X + 26);
FAlexTxAnt[b, 1] := MkRb(TxGrp, 0);
FAlexTxAnt[b, 2] := MkRb(TxGrp, TX2_X - TX1_X);
FAlexTxAnt[b, 3] := MkRb(TxGrp, TX3_X - TX1_X);
FAlexTxAnt[b, 1].Checked := True;
// Do Not TX checkbox
FAlexDoNotTx[b] := MkCh(Grp, '', DNTX_X, Y, 22);
end;
// ---- TX Routing flags group ----
GrpTX := MakeGroupPanel(FPageAlex, 'TX Routing',
MARGIN, 86 + GRP_H + 12, 480, TXGRP_H);
FAlexRxBypassOnTx := MkCh(GrpTX, 'Rx BYPASS on TX',
PAD, GRP_TITLE_H + 8, 440);
FAlexExt1OnTx := MkCh(GrpTX, 'Ext 1 on TX',
PAD, GRP_TITLE_H + 8 + 28, 440);
FAlexGndBpf2OnTx := MkCh(GrpTX, 'Ground BPF2 on TX (ANAN-8000DLE)',
PAD, GRP_TITLE_H + 8 + 56, 440);
FAlexEnableXvtrHf := MkCh(GrpTX, 'Enable XVTR HF',
PAD, GRP_TITLE_H + 8 + 84, 440);
finally
FLoading := False;
end;
end;
// ---------------------------------------------------------------------------
// Alex event handlers and data collectors
// ---------------------------------------------------------------------------
procedure TSettingsForm.CollectAlexFromUI;
var b, a: Integer;
begin
for b := 0 to 10 do
begin
FAlexConfig.RxAnt[b] := 1;
for a := 1 to 3 do
if (FAlexRxAnt[b, a] <> nil) and FAlexRxAnt[b, a].Checked then
begin
FAlexConfig.RxAnt[b] := a;
Break;
end;
FAlexConfig.TxAnt[b] := 1;
for a := 1 to 3 do
if (FAlexTxAnt[b, a] <> nil) and FAlexTxAnt[b, a].Checked then
begin
FAlexConfig.TxAnt[b] := a;
Break;
end;
if (FAlexRxXvtr[b] <> nil) and FAlexRxXvtr[b].Checked then
FAlexConfig.RxOnly[b] := 3
else if (FAlexRxExt1[b] <> nil) and FAlexRxExt1[b].Checked then
FAlexConfig.RxOnly[b] := 2
else if (FAlexRxByps[b] <> nil) and FAlexRxByps[b].Checked then
FAlexConfig.RxOnly[b] := 1
else
FAlexConfig.RxOnly[b] := 0;
FAlexConfig.DoNotTx[b] := (FAlexDoNotTx[b] <> nil) and FAlexDoNotTx[b].Checked;
end;
FAlexConfig.RxBypassOnTx := (FAlexRxBypassOnTx <> nil) and FAlexRxBypassOnTx.Checked;
FAlexConfig.Ext1OnTx := (FAlexExt1OnTx <> nil) and FAlexExt1OnTx.Checked;
FAlexConfig.GndBpf2OnTx := (FAlexGndBpf2OnTx <> nil) and FAlexGndBpf2OnTx.Checked;
FAlexConfig.EnableXvtrHf := (FAlexEnableXvtrHf <> nil) and FAlexEnableXvtrHf.Checked;
end;
procedure TSettingsForm.FireAlexChange;
begin
if FLoading then Exit;
CollectAlexFromUI;
if Assigned(FOnAlexChange) then
FOnAlexChange(FAlexConfig);
end;
procedure TSettingsForm.OnAlexAnyChange(Sender: TObject);
begin
FireAlexChange;
end;
procedure TSettingsForm.OnAlexRxOnlyChange(Sender: TObject);
var
Tag, b: Integer;
begin
if FLoading then Exit;
Tag := TCheckBox(Sender).Tag;
if Tag >= 200 then
begin
b := Tag - 200; // XVTR
if TCheckBox(Sender).Checked then
begin
FLoading := True;
FAlexRxByps[b].Checked := False;
FAlexRxExt1[b].Checked := False;
FLoading := False;
end;
end
else if Tag >= 100 then
begin
b := Tag - 100; // EXT1
if TCheckBox(Sender).Checked then
begin
FLoading := True;
FAlexRxByps[b].Checked := False;
FAlexRxXvtr[b].Checked := False;
FLoading := False;
end;
end
else
begin
b := Tag; // BYPS
if TCheckBox(Sender).Checked then
begin
FLoading := True;
FAlexRxExt1[b].Checked := False;
FAlexRxXvtr[b].Checked := False;
FLoading := False;
end;
end;
FireAlexChange;
end;
procedure TSettingsForm.LoadAlexSettings(const A: TAlexSettings; BoardType: Integer);
var b, ant: Integer;
begin
FLoading := True;
try
FAlexConfig := A;
FAlexBoardType := BoardType;
for b := 0 to 10 do
begin
for ant := 1 to 3 do
if FAlexRxAnt[b, ant] <> nil then
FAlexRxAnt[b, ant].Checked := (A.RxAnt[b] = ant);
for ant := 1 to 3 do
if FAlexTxAnt[b, ant] <> nil then
FAlexTxAnt[b, ant].Checked := (A.TxAnt[b] = ant);
if FAlexRxByps[b] <> nil then FAlexRxByps[b].Checked := (A.RxOnly[b] = 1);
if FAlexRxExt1[b] <> nil then FAlexRxExt1[b].Checked := (A.RxOnly[b] = 2);
if FAlexRxXvtr[b] <> nil then FAlexRxXvtr[b].Checked := (A.RxOnly[b] = 3);
if FAlexDoNotTx[b] <> nil then FAlexDoNotTx[b].Checked := A.DoNotTx[b];
end;
if FAlexRxBypassOnTx <> nil then FAlexRxBypassOnTx.Checked := A.RxBypassOnTx;
if FAlexExt1OnTx <> nil then FAlexExt1OnTx.Checked := A.Ext1OnTx;
if FAlexGndBpf2OnTx <> nil then FAlexGndBpf2OnTx.Checked := A.GndBpf2OnTx;
if FAlexEnableXvtrHf <> nil then FAlexEnableXvtrHf.Checked := A.EnableXvtrHf;
finally
FLoading := False;
end;
end;
end.