fix UpdateRptAutoState

This commit is contained in:
2026-05-27 13:47:52 +03:00
parent e6f02db399
commit d08317e835
3 changed files with 192 additions and 1 deletions
+173 -1
View File
@@ -37,7 +37,8 @@ uses
StatusBar,
PlatformUtils,
WinFirewall,
BoardUtils, WisdomBuilder, UISync;
BoardUtils, WisdomBuilder, UISync,
FlatEdit, FMRepeater;
const
CLR_BG = TColor($00101010);
@@ -254,6 +255,9 @@ type
FFMSQLevel: Integer;
FFMStepOn: Boolean;
FFMStepIdx: Integer;
FFMRptDir: Integer; // RPT_NONE / RPT_MINUS / RPT_PLUS
FFMRptOffsetHz: Double;
FFMRptAutoActive: Boolean; // True = текущий minus включён автоматически
FWfManualHigh: Double;
FWfManualLow: Double;
FWfAGCOffset: Double;
@@ -373,6 +377,10 @@ type
BtnFMStep: TFlatButton;
BtnFMStepSel: TFlatButton;
FStepDropDown: TFlatDropDown;
PanelFMRpt: TPanel;
BtnFMRptMinus: TFlatButton;
BtnFMRptPlus: TFlatButton;
EdFMRptOffset: TFlatEdit;
PanelAGC: TPanel;
BtnAGCMode: array[0..4] of TFlatButton; // FAST MED SLOW LONG OFF
@@ -498,6 +506,11 @@ type
procedure BtnFMCTCSSToneClick(Sender: TObject);
procedure SetFMCTCSSTone(Idx: Integer);
procedure CloseCTCSSPopup;
procedure BtnFMRptMinusClick(Sender: TObject);
procedure BtnFMRptPlusClick(Sender: TObject);
procedure EdFMRptOffsetChange(Sender: TObject);
procedure ApplyFMRpt;
procedure UpdateRptAutoState;
procedure SetFMStep(Idx: Integer);
procedure CloseFMStepPopup;
procedure BtnFMStepClick(Sender: TObject);
@@ -859,6 +872,8 @@ begin
Result.CTCSSToneIdx := FFMCTCSSToneIdx;
Result.FMStepOn := FFMStepOn;
Result.FMStepIdx := FFMStepIdx;
Result.FMRptDir := FFMRptDir;
Result.FMRptOffsetHz := FFMRptOffsetHz;
// Waterfall AGC/NF — глобальные (не диапазонные)
end;
@@ -959,6 +974,8 @@ begin
FXvtrSettings.Entries[FCurrentXvtr].LastCTCSSToneIdx := FFMCTCSSToneIdx;
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepOn := FFMStepOn;
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepIdx := FFMStepIdx;
FXvtrSettings.Entries[FCurrentXvtr].LastFMRptDir := FFMRptDir;
FXvtrSettings.Entries[FCurrentXvtr].LastFMRptOffsetHz := FFMRptOffsetHz;
FXvtrSettings.Entries[FCurrentXvtr].LastCTun := FCTun;
FXvtrSettings.Entries[FCurrentXvtr].LastAGCMode := FAGCMode;
FXvtrSettings.Entries[FCurrentXvtr].LastAGCTop := FAGCTop;
@@ -1022,6 +1039,13 @@ begin
FFMStepOn := B.FMStepOn;
SetFMStep(B.FMStepIdx);
if BtnFMStep <> nil then StyleButton(BtnFMStep, FFMStepOn);
FFMRptDir := B.FMRptDir;
FFMRptOffsetHz := B.FMRptOffsetHz;
if FFMRptOffsetHz <= 0 then
FFMRptOffsetHz := RptDefaultOffsetHz(B.VfoA);
if BtnFMRptMinus <> nil then StyleButton(BtnFMRptMinus, FFMRptDir = RPT_MINUS);
if BtnFMRptPlus <> nil then StyleButton(BtnFMRptPlus, FFMRptDir = RPT_PLUS);
if EdFMRptOffset <> nil then EdFMRptOffset.Text := RptFormatOffset(FFMRptOffsetHz);
// --- CTUN ---
FCTun := B.CTun;
@@ -1111,6 +1135,9 @@ begin
FFMSQLevel := 30;
FFMStepOn := True;
FFMStepIdx := FM_STEP_DEF;
FFMRptDir := RPT_NONE;
FFMRptOffsetHz := RPT_DEFAULT_2M;
FFMRptAutoActive := False;
FSpectrumBufCount := 1024;
FWaterfallBufCount := 1024;
FWaterfallFrameInterval := 2;
@@ -1917,6 +1944,31 @@ begin
FStepDropDown.SetItemIndex(FFMStepIdx);
FStepDropDown.OnSelect := OnStepDropDownSelect;
// FM Repeater offset panel (visible only in FM mode, below CTCSS)
PanelFMRpt := TPanel.Create(Self);
PanelFMRpt.Parent := PanelLeft;
PanelFMRpt.SetBounds(0, Y, LEFT_W, 50);
PanelFMRpt.BevelOuter := bvNone;
PanelFMRpt.Visible := False;
MakeLbl(PanelFMRpt, 'RPT', 4, 2);
BtnFMRptMinus := MakeBtn(PanelFMRpt, '-',
LeftPanelButtonLeft(LEFT_W, 5, 0), 16,
LeftPanelButtonWidth(LEFT_W, 5, 0), BTN_H, BtnFMRptMinusClick);
StyleButton(BtnFMRptMinus, False);
BtnFMRptPlus := MakeBtn(PanelFMRpt, '+',
LeftPanelButtonLeft(LEFT_W, 5, 4), 16,
LeftPanelButtonWidth(LEFT_W, 5, 4), BTN_H, BtnFMRptPlusClick);
StyleButton(BtnFMRptPlus, False);
W := LeftPanelButtonLeft(LEFT_W, 5, 1);
EdFMRptOffset := TFlatEdit.Create(Self);
EdFMRptOffset.Parent := PanelFMRpt;
EdFMRptOffset.Left := W;
EdFMRptOffset.Top := 18;
EdFMRptOffset.Width := LeftPanelButtonLeft(LEFT_W, 5, 4) - W - LEFT_PANEL_BTN_GAP;
EdFMRptOffset.Height := BTN_H - 4;
EdFMRptOffset.Text := RptFormatOffset(FFMRptOffsetHz);
EdFMRptOffset.OnChange := EdFMRptOffsetChange;
// ---- Right panel ----
PanelRight := TPanel.Create(Self);
PanelRight.Parent := Self;
@@ -2501,6 +2553,8 @@ begin
if PanelFMSQ <> nil then DP(PanelFMSQ);
if PanelFMCTCSS <> nil then DP(PanelFMCTCSS);
if PanelFMStep <> nil then DP(PanelFMStep);
if PanelFMRpt <> nil then DP(PanelFMRpt);
if EdFMRptOffset <> nil then EdFMRptOffset.SetAppTheme(T);
DP(PanelRXBlock); DP(PanelAGC); DP(PanelTX);
DP(PanelRight);
@@ -2541,6 +2595,8 @@ begin
if TrkFMSQ <> nil then DS(TrkFMSQ);
if BtnFMCTCSS <> nil then StyleButton(BtnFMCTCSS, BtnFMCTCSS.Active);
if BtnFMCTCSSTone <> nil then StyleButton(BtnFMCTCSSTone, False);
if BtnFMRptMinus <> nil then StyleButton(BtnFMRptMinus, BtnFMRptMinus.Active);
if BtnFMRptPlus <> nil then StyleButton(BtnFMRptPlus, BtnFMRptPlus.Active);
if FCTCSSDropDown <> nil then FCTCSSDropDown.ApplyStyle(StyleButton, T.Panel);
if FStepDropDown <> nil then FStepDropDown.ApplyStyle(StyleButton, T.Panel);
StyleButton(BtnNR, BtnNR.Active);
@@ -4383,6 +4439,13 @@ begin
if FStepDropDown <> nil then FStepDropDown.SetItemIndex(FFMStepIdx);
PanelFMStep.Visible := True;
end;
if PanelFMRpt <> nil then
begin
StyleButton(BtnFMRptMinus, FFMRptDir = RPT_MINUS);
StyleButton(BtnFMRptPlus, FFMRptDir = RPT_PLUS);
EdFMRptOffset.Text := RptFormatOffset(FFMRptOffsetHz);
PanelFMRpt.Visible := True;
end;
RelayoutBelowBands;
Exit;
end;
@@ -4392,6 +4455,7 @@ begin
if PanelFMSQ <> nil then PanelFMSQ.Visible := False;
if PanelFMCTCSS <> nil then PanelFMCTCSS.Visible := False;
if PanelFMStep <> nil then begin CloseFMStepPopup; PanelFMStep.Visible := False; end;
if PanelFMRpt <> nil then PanelFMRpt.Visible := False;
for i := 0 to FILT_COUNT - 1 do
begin
BtnFilter[i].Left := LeftPanelButtonLeft(PanelFilter.Width, 5, i mod 5);
@@ -4529,6 +4593,9 @@ begin
end;
end;
// Auto-enable repeater minus for 2 m repeater input segment
if FMode = MODE_FM then UpdateRptAutoState;
// DDC: передаём FCenterFreq (при CTUN OFF = FVfoA, при CTUN ON = фиксирован)
if FNetwork.Connected and FNetwork.Running then
begin
@@ -5141,6 +5208,95 @@ begin
CloseFMStepPopup;
end;
procedure TMainForm.BtnFMRptMinusClick(Sender: TObject);
begin
if FFMRptDir = RPT_MINUS then
FFMRptDir := RPT_NONE
else
FFMRptDir := RPT_MINUS;
FFMRptAutoActive := False; // ручное действие снимает авто-флаг
StyleButton(BtnFMRptMinus, FFMRptDir = RPT_MINUS);
StyleButton(BtnFMRptPlus, FFMRptDir = RPT_PLUS);
BtnFMRptMinus.Repaint;
BtnFMRptPlus.Repaint;
ApplyFMRpt;
SaveCurrentBand;
end;
procedure TMainForm.BtnFMRptPlusClick(Sender: TObject);
begin
if FFMRptDir = RPT_PLUS then
FFMRptDir := RPT_NONE
else
FFMRptDir := RPT_PLUS;
FFMRptAutoActive := False; // ручное действие снимает авто-флаг
StyleButton(BtnFMRptMinus, FFMRptDir = RPT_MINUS);
StyleButton(BtnFMRptPlus, FFMRptDir = RPT_PLUS);
BtnFMRptMinus.Repaint;
BtnFMRptPlus.Repaint;
ApplyFMRpt;
SaveCurrentBand;
end;
procedure TMainForm.EdFMRptOffsetChange(Sender: TObject);
var Hz: Double;
begin
Hz := RptParseOffset(EdFMRptOffset.Text);
if Hz > 0.0 then
begin
FFMRptOffsetHz := Hz;
if FFMRptDir <> RPT_NONE then
begin
ApplyFMRpt;
SaveCurrentBand;
end;
end;
end;
procedure TMainForm.ApplyFMRpt;
begin
if FNetwork.Connected and FNetwork.Running then
begin
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP;
end;
end;
procedure TMainForm.UpdateRptAutoState;
var InRange: Boolean;
begin
InRange := RptAutoDir(FVfoA) = RPT_MINUS;
if InRange then
begin
// Auto-enable minus only when currently off.
if FFMRptDir = RPT_NONE then
begin
FFMRptDir := RPT_MINUS;
FFMRptAutoActive := True;
if BtnFMRptMinus <> nil then
begin
StyleButton(BtnFMRptMinus, True);
BtnFMRptMinus.Repaint;
end;
end;
end
else
begin
// Auto-disable only if this minus was set automatically.
if (FFMRptDir = RPT_MINUS) and FFMRptAutoActive then
begin
FFMRptDir := RPT_NONE;
FFMRptAutoActive := False;
if BtnFMRptMinus <> nil then
begin
StyleButton(BtnFMRptMinus, False);
BtnFMRptMinus.Repaint;
end;
end;
end;
end;
procedure TMainForm.CloseFMStepPopup;
begin
if FStepDropDown <> nil then FStepDropDown.ClosePopup;
@@ -5283,6 +5439,11 @@ begin
if FSplitTxB then Result := FVfoB
else if FActiveVfo = 0 then Result := FVfoA
else Result := FVfoB;
if FMode = MODE_FM then
case FFMRptDir of
RPT_MINUS: Result := Result - FFMRptOffsetHz;
RPT_PLUS: Result := Result + FFMRptOffsetHz;
end;
end;
procedure TMainForm.ApplyMOX(Active: Boolean);
@@ -6527,6 +6688,10 @@ begin
begin
PanelFMCTCSS.Top := Y; Y := Y + PanelFMCTCSS.Height + 2;
end;
if (PanelFMRpt <> nil) and PanelFMRpt.Visible then
begin
PanelFMRpt.Top := Y; Y := Y + PanelFMRpt.Height + 2;
end;
end;
procedure TMainForm.RebuildXvtrButtons;
@@ -6650,6 +6815,13 @@ begin
FFMStepOn := E.LastFMStepOn;
SetFMStep(E.LastFMStepIdx);
if BtnFMStep <> nil then StyleButton(BtnFMStep, FFMStepOn);
FFMRptDir := E.LastFMRptDir;
FFMRptOffsetHz := E.LastFMRptOffsetHz;
if FFMRptOffsetHz <= 0 then
FFMRptOffsetHz := RptDefaultOffsetHz(E.FreqBegin);
if BtnFMRptMinus <> nil then StyleButton(BtnFMRptMinus, FFMRptDir = RPT_MINUS);
if BtnFMRptPlus <> nil then StyleButton(BtnFMRptPlus, FFMRptDir = RPT_PLUS);
if EdFMRptOffset <> nil then EdFMRptOffset.Text := RptFormatOffset(FFMRptOffsetHz);
// AGC
FAGCMode := E.LastAGCMode;
FAGCTop := E.LastAGCTop;
+15
View File
@@ -40,6 +40,8 @@ type
CTCSSToneIdx: Integer; // 0..37
FMStepOn: Boolean;
FMStepIdx: Integer; // 0..3 (6.25/12.5/20/25 kHz)
FMRptDir: Integer; // RPT_NONE/RPT_MINUS/RPT_PLUS
FMRptOffsetHz: Double; // Hz, 0 = use band default
end;
// TX-настройки per-device. Хранятся в JSON-секции "tx" под MAC,
@@ -150,6 +152,8 @@ type
LastCTun: Boolean;
LastAGCMode: Integer;
LastAGCTop: Integer;
LastFMRptDir: Integer;
LastFMRptOffsetHz: Double;
end;
TXvtrSettings = record
@@ -356,6 +360,8 @@ begin
B.CTCSSToneIdx := 0;
B.FMStepOn := True;
B.FMStepIdx := 3; // 25 kHz
B.FMRptDir := 0;
B.FMRptOffsetHz := 0.0;
end;
class procedure TSettingsManager.DefaultGlobal(out G: TGlobalSettings);
@@ -737,6 +743,7 @@ begin
JW(O,'fmsq_on',B.FMSQOn); JW(O,'fmsq_level',B.FMSQLevel);
JW(O,'ctcss_on',B.CTCSSOn); JW(O,'ctcss_idx',B.CTCSSToneIdx);
JW(O,'fmstep_on',B.FMStepOn); JW(O,'fmstep_idx',B.FMStepIdx);
JW(O,'fmrpt_dir',B.FMRptDir); JW(O,'fmrpt_offset',B.FMRptOffsetHz);
end;
function TSettingsManager.LoadBand(const MAC: array of Byte; BandIdx: Integer;
@@ -763,6 +770,8 @@ begin
B.CTCSSToneIdx := EnsureRange(JI(O,'ctcss_idx', B.CTCSSToneIdx), 0, 37);
B.FMStepOn := JB(O,'fmstep_on', B.FMStepOn);
B.FMStepIdx := EnsureRange(JI(O,'fmstep_idx', B.FMStepIdx), 0, 3);
B.FMRptDir := EnsureRange(JI(O,'fmrpt_dir', B.FMRptDir), 0, 2);
B.FMRptOffsetHz := JD(O,'fmrpt_offset', B.FMRptOffsetHz);
end;
procedure TSettingsManager.SaveWindowBounds(L, T, W, H, DPI: Integer; Maximized: Boolean);
@@ -1173,6 +1182,8 @@ begin
X.Entries[i].LastCTun := False;
X.Entries[i].LastAGCMode := 1;
X.Entries[i].LastAGCTop := 90;
X.Entries[i].LastFMRptDir := 0;
X.Entries[i].LastFMRptOffsetHz := 0.0;
end;
// Шаблоны (пользователь должен сам Enable, чтобы трансверторы появились)
X.Entries[0].ButtonText := '2m';
@@ -1229,6 +1240,8 @@ begin
X.Entries[i].LastCTun := JB(EObj, 'last_ctun', X.Entries[i].LastCTun);
X.Entries[i].LastAGCMode := EnsureRange(JI(EObj, 'last_agc_mode', X.Entries[i].LastAGCMode), 0, 4);
X.Entries[i].LastAGCTop := EnsureRange(JI(EObj, 'last_agc_top', X.Entries[i].LastAGCTop), 0, 120);
X.Entries[i].LastFMRptDir := EnsureRange(JI(EObj, 'last_fmrpt_dir', X.Entries[i].LastFMRptDir), 0, 2);
X.Entries[i].LastFMRptOffsetHz := JD(EObj, 'last_fmrpt_offset', X.Entries[i].LastFMRptOffsetHz);
// Валидация: LastFreq должен быть в [FreqBegin..FreqEnd]
if (X.Entries[i].LastFreq < X.Entries[i].FreqBegin) or
(X.Entries[i].LastFreq > X.Entries[i].FreqEnd) then
@@ -1270,6 +1283,8 @@ begin
JW(EObj, 'last_ctun', X.Entries[i].LastCTun);
JW(EObj, 'last_agc_mode', X.Entries[i].LastAGCMode);
JW(EObj, 'last_agc_top', X.Entries[i].LastAGCTop);
JW(EObj, 'last_fmrpt_dir', X.Entries[i].LastFMRptDir);
JW(EObj, 'last_fmrpt_offset', X.Entries[i].LastFMRptOffsetHz);
end;
end;
+4
View File
@@ -146,6 +146,10 @@
<Filename Value="FlatEdit.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="FMRepeater.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="FlatSpinEdit.pas"/>
<IsPartOfProject Value="True"/>