mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
Fix XVTR/FM frequency, mode and step persistence
- Fix web spectrum scroll resetting freq to XVTR minimum (same root cause as desktop: hardcoded 60 MHz clamp; now delegates to server) - Save/restore FM mode, NFM/WFM filter, squelch, CTCSS per XVTR slot and per HF band (CTCSSOn/ToneIdx added to TBandSettings; full state added to TXvtrEntry; ActivateXvtrBand restores all on slot switch) - Fix UpdateFilterButtons always resetting NFM/WFM to default on mode restore; now preserves saved filter index - Add FM Step tuning: panel with STEP toggle + 6.25/12.5/20/25 kHz selector (popup); mouse wheel uses selected step in FM mode; state saved per band and XVTR slot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+180
-14
@@ -104,6 +104,12 @@ const
|
||||
'156.7', '162.2', '167.9', '173.8', '179.9', '186.2', '192.8', '203.5',
|
||||
'210.7', '218.1', '225.7', '233.6', '241.8', '250.3');
|
||||
|
||||
// FM Step tuning
|
||||
FM_STEP_COUNT = 4;
|
||||
FM_STEP_HZ: array[0..3] of Integer = (6250, 12500, 20000, 25000);
|
||||
FM_STEP_NAMES: array[0..3] of string = ('6.25k', '12.5k', '20k', '25k');
|
||||
FM_STEP_DEF = 3; // 25 kHz
|
||||
|
||||
BAND_NAMES: array[0..BAND_COUNT-1] of string = (
|
||||
'160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m');
|
||||
BAND_FREQ: array[0..BAND_COUNT-1] of Double = (
|
||||
@@ -258,6 +264,8 @@ type
|
||||
FFMCTCSSToneIdx: Integer;
|
||||
FFMSQOn: Boolean;
|
||||
FFMSQLevel: Integer;
|
||||
FFMStepOn: Boolean;
|
||||
FFMStepIdx: Integer;
|
||||
FWfManualHigh: Double;
|
||||
FWfManualLow: Double;
|
||||
FWfAGCOffset: Double;
|
||||
@@ -373,6 +381,11 @@ type
|
||||
BtnFMCTCSSTone: TFlatButton;
|
||||
PanelFMCTCSSPop: TPanel;
|
||||
FBtnCTCSSTones: array[0..CTCSS_COUNT - 1] of TFlatButton;
|
||||
PanelFMStep: TPanel;
|
||||
BtnFMStep: TFlatButton;
|
||||
BtnFMStepSel: TFlatButton;
|
||||
PanelFMStepPop: TPanel;
|
||||
FBtnFMSteps: array[0..FM_STEP_COUNT - 1] of TFlatButton;
|
||||
|
||||
PanelRX: TPanel;
|
||||
BtnAGCMode: array[0..4] of TFlatButton; // FAST MED SLOW LONG OFF
|
||||
@@ -491,6 +504,11 @@ type
|
||||
procedure BtnFMToneSelectClick(Sender: TObject);
|
||||
procedure SetFMCTCSSTone(Idx: Integer);
|
||||
procedure CloseCTCSSPopup;
|
||||
procedure SetFMStep(Idx: Integer);
|
||||
procedure CloseFMStepPopup;
|
||||
procedure BtnFMStepClick(Sender: TObject);
|
||||
procedure BtnFMStepSelClick(Sender: TObject);
|
||||
procedure BtnFMStepSelectClick(Sender: TObject);
|
||||
procedure PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
||||
Shift: TShiftState; X, Y: Integer);
|
||||
procedure PbSpectrumMouseMove(Sender: TObject; Shift: TShiftState;
|
||||
@@ -910,8 +928,12 @@ begin
|
||||
Result.AGCTop := FAGCTop;
|
||||
Result.CTun := FCTun;
|
||||
Result.SpanHz := FSpanHz;
|
||||
Result.FMSQOn := FFMSQOn;
|
||||
Result.FMSQLevel := FFMSQLevel;
|
||||
Result.FMSQOn := FFMSQOn;
|
||||
Result.FMSQLevel := FFMSQLevel;
|
||||
Result.CTCSSOn := FFMCTCSSOn;
|
||||
Result.CTCSSToneIdx := FFMCTCSSToneIdx;
|
||||
Result.FMStepOn := FFMStepOn;
|
||||
Result.FMStepIdx := FFMStepIdx;
|
||||
// Waterfall AGC/NF — глобальные (не диапазонные)
|
||||
end;
|
||||
|
||||
@@ -998,7 +1020,17 @@ begin
|
||||
if FCurrentXvtr >= 0 then
|
||||
begin
|
||||
if (FCurrentXvtr < CFG_XVTR_COUNT) then
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastFreq := FVfoA;
|
||||
begin
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastFreq := FVfoA;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastMode := FMode;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastFilterIdx := FFilter;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastFMSQOn := FFMSQOn;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastFMSQLevel := FFMSQLevel;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastCTCSSOn := FFMCTCSSOn;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastCTCSSToneIdx := FFMCTCSSToneIdx;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepOn := FFMStepOn;
|
||||
FXvtrSettings.Entries[FCurrentXvtr].LastFMStepIdx := FFMStepIdx;
|
||||
end;
|
||||
FSettings.SaveXvtr(FDevMAC, FXvtrSettings);
|
||||
Exit;
|
||||
end;
|
||||
@@ -1047,10 +1079,17 @@ begin
|
||||
end;
|
||||
FSpecView.AGCTop := FAGCTop;
|
||||
|
||||
// --- FM Squelch ---
|
||||
// --- FM Squelch + CTCSS ---
|
||||
FFMSQOn := B.FMSQOn;
|
||||
FFMSQLevel := B.FMSQLevel;
|
||||
// UI update happens inside UpdateFilterButtons (called above) when mode=FM
|
||||
FFMCTCSSOn := B.CTCSSOn;
|
||||
if BtnFMCTCSS <> nil then StyleButton(BtnFMCTCSS, FFMCTCSSOn);
|
||||
SetFMCTCSSTone(B.CTCSSToneIdx);
|
||||
ApplyFMSquelch;
|
||||
FFMStepOn := B.FMStepOn;
|
||||
SetFMStep(B.FMStepIdx);
|
||||
if BtnFMStep <> nil then StyleButton(BtnFMStep, FFMStepOn);
|
||||
|
||||
// --- CTUN ---
|
||||
FCTun := B.CTun;
|
||||
@@ -1139,6 +1178,8 @@ begin
|
||||
FFMCTCSSToneIdx := 0;
|
||||
FFMSQOn := False;
|
||||
FFMSQLevel := 30;
|
||||
FFMStepOn := True;
|
||||
FFMStepIdx := FM_STEP_DEF;
|
||||
FSpectrumBufCount := 1024;
|
||||
FWaterfallBufCount := 1024;
|
||||
FWaterfallFrameInterval := 2;
|
||||
@@ -1793,6 +1834,37 @@ begin
|
||||
FBtnCTCSSTones[i] := B;
|
||||
end;
|
||||
|
||||
// FM Step panel (visible only in FM mode, between Filter and SQL)
|
||||
PanelFMStep := TPanel.Create(Self);
|
||||
PanelFMStep.Parent := PanelLeft;
|
||||
PanelFMStep.SetBounds(0, Y, LEFT_W, 50);
|
||||
PanelFMStep.BevelOuter := bvNone;
|
||||
PanelFMStep.Visible := False;
|
||||
MakeLbl(PanelFMStep, 'STEP', 4, 4);
|
||||
W := LEFT_W div 3 - 2;
|
||||
BtnFMStep := MakeBtn(PanelFMStep, 'STEP', 2, 20, W, BTN_H, BtnFMStepClick);
|
||||
StyleButton(BtnFMStep, FFMStepOn);
|
||||
BtnFMStepSel := MakeBtn(PanelFMStep, FM_STEP_NAMES[FFMStepIdx],
|
||||
W + 4, 20, LEFT_W - W - 8, BTN_H, BtnFMStepSelClick);
|
||||
StyleButton(BtnFMStepSel, False);
|
||||
|
||||
// Step picker popup (child of PanelLeft)
|
||||
PanelFMStepPop := TPanel.Create(Self);
|
||||
PanelFMStepPop.Parent := PanelLeft;
|
||||
PanelFMStepPop.BevelOuter := bvNone;
|
||||
PanelFMStepPop.Color := CLR_PANEL;
|
||||
PanelFMStepPop.Visible := False;
|
||||
W := (LEFT_W - 4) div FM_STEP_COUNT;
|
||||
PanelFMStepPop.SetBounds(0, 0, LEFT_W, BTN_SM);
|
||||
for i := 0 to FM_STEP_COUNT - 1 do
|
||||
begin
|
||||
B := MakeBtn(PanelFMStepPop, FM_STEP_NAMES[i],
|
||||
2 + i * W, 0, W - 2, BTN_SM, BtnFMStepSelectClick);
|
||||
B.Tag := i;
|
||||
StyleButton(B, i = FFMStepIdx);
|
||||
FBtnFMSteps[i] := B;
|
||||
end;
|
||||
|
||||
// CTUN / DUP buttons (display-related)
|
||||
BtnCTun := MakeBtn(PanelLeft, 'CTUN', 2, Y, LEFT_W div 3 - 2, BTN_H, BtnCTunClick);
|
||||
BtnCTun.Tag := 0;
|
||||
@@ -2394,6 +2466,8 @@ begin
|
||||
if PanelFMSQ <> nil then DP(PanelFMSQ);
|
||||
if PanelFMCTCSS <> nil then DP(PanelFMCTCSS);
|
||||
if PanelFMCTCSSPop <> nil then DP(PanelFMCTCSSPop);
|
||||
if PanelFMStep <> nil then DP(PanelFMStep);
|
||||
if PanelFMStepPop <> nil then DP(PanelFMStepPop);
|
||||
DP(PanelRX); DP(PanelTX);
|
||||
DP(PanelRight); DP(PanelSpanButtons);
|
||||
|
||||
@@ -4037,9 +4111,9 @@ begin
|
||||
begin
|
||||
// FM: show only NFM and WFM buttons, resize them to fill the row
|
||||
CloseCTCSSPopup;
|
||||
FFilter := FILT_FM_DEF;
|
||||
FFilterBW := FILT_FM_BW[FILT_FM_DEF];
|
||||
FFMDeviation := FILT_FM_DEV[FILT_FM_DEF];
|
||||
if FFilter > 1 then FFilter := FILT_FM_DEF; // sanitize, keep NFM/WFM as-is
|
||||
FFilterBW := FILT_FM_BW[FFilter];
|
||||
FFMDeviation := FILT_FM_DEV[FFilter];
|
||||
W := (PanelFilter.Width - 6) div 2;
|
||||
BtnFilter[0].Caption := FILT_FM_NAMES[0];
|
||||
BtnFilter[0].Left := 2;
|
||||
@@ -4064,6 +4138,12 @@ begin
|
||||
LblFMSQ.Caption := IntToStr(FFMSQLevel);
|
||||
end;
|
||||
if PanelFMCTCSS <> nil then PanelFMCTCSS.Visible := True;
|
||||
if PanelFMStep <> nil then
|
||||
begin
|
||||
StyleButton(BtnFMStep, FFMStepOn);
|
||||
BtnFMStepSel.Caption := FM_STEP_NAMES[FFMStepIdx];
|
||||
PanelFMStep.Visible := True;
|
||||
end;
|
||||
RelayoutBelowBands;
|
||||
Exit;
|
||||
end;
|
||||
@@ -4072,6 +4152,7 @@ begin
|
||||
PanelFilter.Height := 74;
|
||||
if PanelFMSQ <> nil then PanelFMSQ.Visible := False;
|
||||
if PanelFMCTCSS <> nil then PanelFMCTCSS.Visible := False;
|
||||
if PanelFMStep <> nil then begin CloseFMStepPopup; PanelFMStep.Visible := False; end;
|
||||
W := (PanelFilter.Width - 6) div 5;
|
||||
for i := 0 to FILT_COUNT - 1 do
|
||||
begin
|
||||
@@ -4697,6 +4778,63 @@ procedure TMainForm.CloseCTCSSPopup;
|
||||
begin
|
||||
if (PanelFMCTCSSPop <> nil) and PanelFMCTCSSPop.Visible then
|
||||
PanelFMCTCSSPop.Visible := False;
|
||||
CloseFMStepPopup;
|
||||
end;
|
||||
|
||||
procedure TMainForm.CloseFMStepPopup;
|
||||
begin
|
||||
if (PanelFMStepPop <> nil) and PanelFMStepPop.Visible then
|
||||
PanelFMStepPop.Visible := False;
|
||||
end;
|
||||
|
||||
procedure TMainForm.SetFMStep(Idx: Integer);
|
||||
var OldIdx: Integer;
|
||||
begin
|
||||
OldIdx := FFMStepIdx;
|
||||
FFMStepIdx := Idx;
|
||||
if BtnFMStepSel <> nil then
|
||||
begin
|
||||
BtnFMStepSel.Caption := FM_STEP_NAMES[Idx];
|
||||
BtnFMStepSel.Invalidate;
|
||||
end;
|
||||
if (OldIdx <> Idx) and (FBtnFMSteps[OldIdx] <> nil) then
|
||||
FBtnFMSteps[OldIdx].Active := False;
|
||||
if FBtnFMSteps[Idx] <> nil then
|
||||
FBtnFMSteps[Idx].Active := True;
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnFMStepClick(Sender: TObject);
|
||||
begin
|
||||
FFMStepOn := not FFMStepOn;
|
||||
StyleButton(BtnFMStep, FFMStepOn);
|
||||
BtnFMStep.Repaint;
|
||||
SaveCurrentBand;
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnFMStepSelClick(Sender: TObject);
|
||||
var Y0: Integer;
|
||||
begin
|
||||
if PanelFMStepPop.Visible then
|
||||
begin
|
||||
CloseFMStepPopup;
|
||||
Exit;
|
||||
end;
|
||||
CloseCTCSSPopup;
|
||||
Y0 := PanelFMStep.Top + PanelFMStep.Height + 2;
|
||||
if Y0 + PanelFMStepPop.Height > PanelLeft.ClientHeight then
|
||||
Y0 := PanelFMStep.Top - PanelFMStepPop.Height - 2;
|
||||
if Y0 < 0 then Y0 := 0;
|
||||
PanelFMStepPop.Top := Y0;
|
||||
PanelFMStepPop.Left := 0;
|
||||
PanelFMStepPop.BringToFront;
|
||||
PanelFMStepPop.Visible := True;
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnFMStepSelectClick(Sender: TObject);
|
||||
begin
|
||||
CloseFMStepPopup;
|
||||
SetFMStep((Sender as TFlatButton).Tag);
|
||||
SaveCurrentBand;
|
||||
end;
|
||||
|
||||
procedure TMainForm.SetFMCTCSSTone(Idx: Integer);
|
||||
@@ -5967,6 +6105,10 @@ begin
|
||||
Y := PanelBands.Top + PanelBands.Height + 2;
|
||||
PanelMode.Top := Y; Y := Y + PanelMode.Height + 2;
|
||||
PanelFilter.Top := Y; Y := Y + PanelFilter.Height + 2;
|
||||
if (PanelFMStep <> nil) and PanelFMStep.Visible then
|
||||
begin
|
||||
PanelFMStep.Top := Y; Y := Y + PanelFMStep.Height + 2;
|
||||
end;
|
||||
if (PanelFMSQ <> nil) and PanelFMSQ.Visible then
|
||||
begin
|
||||
PanelFMSQ.Top := Y; Y := Y + PanelFMSQ.Height + 2;
|
||||
@@ -6066,16 +6208,16 @@ procedure TMainForm.ActivateXvtrBand(Idx: Integer);
|
||||
var
|
||||
Vis: Double;
|
||||
i: Integer;
|
||||
E: TXvtrEntry;
|
||||
begin
|
||||
if (Idx < 0) or (Idx >= CFG_XVTR_COUNT) then Exit;
|
||||
if not FXvtrSettings.Entries[Idx].Enabled then Exit;
|
||||
FCurrentXvtr := Idx;
|
||||
E := FXvtrSettings.Entries[Idx];
|
||||
// Выбор начальной частоты: LastFreq если в диапазоне, иначе середина
|
||||
Vis := FXvtrSettings.Entries[Idx].LastFreq;
|
||||
if (Vis < FXvtrSettings.Entries[Idx].FreqBegin) or
|
||||
(Vis > FXvtrSettings.Entries[Idx].FreqEnd) then
|
||||
Vis := (FXvtrSettings.Entries[Idx].FreqBegin +
|
||||
FXvtrSettings.Entries[Idx].FreqEnd) / 2.0;
|
||||
Vis := E.LastFreq;
|
||||
if (Vis < E.FreqBegin) or (Vis > E.FreqEnd) then
|
||||
Vis := (E.FreqBegin + E.FreqEnd) / 2.0;
|
||||
// Отключаем кнопки HF band
|
||||
for i := 0 to BAND_COUNT - 1 do
|
||||
StyleButton(BtnBand[i], False);
|
||||
@@ -6085,6 +6227,22 @@ begin
|
||||
StyleButton(BtnXvtrBand[i], i = Idx);
|
||||
// Применяем XVTR режим к сети (XVTR enable bit + DDC IN + DisablePA + RX ant)
|
||||
ApplyXvtrToNetwork;
|
||||
// Восстанавливаем Mode + FM Squelch + CTCSS для этого XVTR-слота
|
||||
FFMSQOn := E.LastFMSQOn;
|
||||
FFMSQLevel := E.LastFMSQLevel;
|
||||
FMode := E.LastMode;
|
||||
FFilter := E.LastFilterIdx;
|
||||
for i := 0 to MODE_COUNT - 1 do StyleButton(BtnMode[i], i = FMode);
|
||||
if FWDSPReady then FDSPEngine.SetMode(FMode);
|
||||
UpdateFilterButtons; // показывает/скрывает FM-панели, обновляет SQL UI
|
||||
ApplyModeFilter;
|
||||
FFMCTCSSOn := E.LastCTCSSOn;
|
||||
if BtnFMCTCSS <> nil then StyleButton(BtnFMCTCSS, FFMCTCSSOn);
|
||||
SetFMCTCSSTone(E.LastCTCSSToneIdx);
|
||||
ApplyFMSquelch;
|
||||
FFMStepOn := E.LastFMStepOn;
|
||||
SetFMStep(E.LastFMStepIdx);
|
||||
if BtnFMStep <> nil then StyleButton(BtnFMStep, FFMStepOn);
|
||||
// VFO на видимую частоту; FCenterFreq тоже visible (как и для HF — DDC переход
|
||||
// в IF происходит только в момент UpdateState/SetRunAndFreq через XvtrTranslate).
|
||||
FVfoA := Vis;
|
||||
@@ -6210,7 +6368,10 @@ var
|
||||
Base: Int64;
|
||||
NewFreq: Int64;
|
||||
begin
|
||||
if (ssCtrl in Shift) and (ssShift in Shift) then Step := 1000000
|
||||
if (FMode = MODE_FM) and FFMStepOn and
|
||||
not ((ssCtrl in Shift) or (ssShift in Shift)) then
|
||||
Step := FM_STEP_HZ[FFMStepIdx]
|
||||
else if (ssCtrl in Shift) and (ssShift in Shift) then Step := 1000000
|
||||
else if ssCtrl in Shift then Step := 1000
|
||||
else if ssShift in Shift then Step := 10
|
||||
else Step := 100;
|
||||
@@ -6230,7 +6391,12 @@ begin
|
||||
NewFreq := Base; // снэп к нижней границе
|
||||
end;
|
||||
|
||||
NewFreq := Max(30000, Min(60000000, NewFreq));
|
||||
// В XVTR-режиме HF-клэмп не применяем: ApplyVfoA сам ограничит по FreqBegin/FreqEnd
|
||||
if (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT) and
|
||||
FXvtrSettings.Entries[FCurrentXvtr].Enabled then
|
||||
NewFreq := Max(0, NewFreq)
|
||||
else
|
||||
NewFreq := Max(30000, Min(60000000, NewFreq));
|
||||
ApplyVfoA(NewFreq);
|
||||
|
||||
Handled := True;
|
||||
|
||||
Reference in New Issue
Block a user