feat(pans): сетка FM-каналов и шаг тюнинга на доп. панадаптерах

Доп. паны не имеют своего FMode — режим берётся со слайсов пана:
PanHasFMSlice(PanId) = есть слайс в FM/DMR/FMRAW. По нему SyncPanZoomUI
выставляет View.FMGridStepHz = FM_STEP_HZ[FFMStepIdx], как SyncSpecViewFreq
делает для главного пана (сеттер сам гасит кэши спектра и линейки).

Тюнинг слайса привязан к той же сетке:
  • колесо над слайсом/доп. паном — было зашито 12500 и только DMR/FMRAW,
    теперь FM_STEP_HZ[FFMStepIdx] во всех трёх канальных режимах (гейт FFMStepOn);
  • PanClickTune — там была та же зашитая 12500, теперь шаг режима целевого
    слайса, как в DoSpectrumClick главного пана.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 19:22:30 +03:00
co-authored by Claude Opus 4.8
parent c857aa9d4b
commit 02288292ff
2 changed files with 48 additions and 9 deletions
+31 -8
View File
@@ -5983,12 +5983,24 @@ end;
procedure TMainForm.SyncPanZoomUI(P: TPanafallPanel);
// Вьюха и зум-бар пана ← контроллер (после смены зума/центра/rate пана).
var VC, VS: Double;
var VC, VS, StepHz: Double;
begin
if (P = nil) or (P.PanId = 0) then Exit;
FController.GetPanViewWindow(P.PanId, VC, VS);
P.View.CenterFreq := VC;
P.View.SpanHz := VS;
// Сетка каналов по шагу FM — как на главном пане, но по режиму слайсов
// ЭТОГО пана (у доп. панов нет своего FMode).
if FController.PanHasFMSlice(P.PanId) then
StepHz := FM_STEP_HZ[FController.FFMStepIdx]
else
StepHz := 0;
if Abs(P.View.FMGridStepHz - StepHz) > 0.5 then
begin
P.View.FMGridStepHz := StepHz; // сеттер сам сбросит кэши спектра/линейки
MarkPanDirty(P);
if P.PbRuler <> nil then P.PbRuler.Invalidate;
end;
P.ZoomBar.SetFreq(FController.PanDDCFreq(P.PanId),
FController.PanDDCRateKHz(P.PanId) * 1000.0);
P.ZoomBar.SetState(FController.PanZoomFactor(P.PanId),
@@ -6422,7 +6434,7 @@ procedure TMainForm.PanClickTune(P: TPanafallPanel; X, W: Integer);
var
VC, VS, LoHz, HiHz: Double;
Hz, StepHz: Int64;
Id: Integer;
Id, ModeHere: Integer;
S: TCtrlSlice;
begin
PanViewWindow(P, VC, VS);
@@ -6432,11 +6444,18 @@ begin
Id := FActiveSliceId
else
Id := P.FirstSliceId;
// Канальные режимы: клик садится на ту же сетку, что нарисована на пане.
if Id <> 0 then
begin
if FController.GetSlice(Id, S) then ModeHere := S.Mode
else ModeHere := FController.FMode;
end
else
ModeHere := FController.FMode;
if (ModeHere in [MODE_FM, MODE_DMR, MODE_FMRAW]) and FController.FFMStepOn then
StepHz := FM_STEP_HZ[FController.FFMStepIdx]
else
StepHz := 100;
if ((Id <> 0) and FController.GetSlice(Id, S) and
(S.Mode in [MODE_DMR, MODE_FMRAW])) or
((Id = 0) and (FController.FMode in [MODE_DMR, MODE_FMRAW])) then
StepHz := 12500;
Hz := Round(VC + (X / W - 0.5) * VS);
Hz := ((Hz + StepHz div 2) div StepHz) * StepHz;
FController.CapturedSpan(LoHz, HiHz, P.PanId);
@@ -8003,8 +8022,12 @@ begin
end;
if (SliceId <> 0) and FController.GetSlice(SliceId, Sl) then
begin
if (Sl.Mode in [MODE_DMR, MODE_FMRAW]) and
not ((ssCtrl in Shift) or (ssShift in Shift)) then Step := 12500
// Канальные режимы: шаг = выбранный шаг сетки (тот же, что рисуется на
// пане), как на главном панадаптере. Ctrl/Shift — обычные мелкие шаги.
if (Sl.Mode in [MODE_FM, MODE_DMR, MODE_FMRAW]) and
FController.FFMStepOn and
not ((ssCtrl in Shift) or (ssShift in Shift)) then
Step := FM_STEP_HZ[FController.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
+16
View File
@@ -649,6 +649,7 @@ type
procedure SetSliceMute(Id: Integer; Mute: Boolean);
procedure SetSliceRxMuteOnTx(Id: Integer; On_: Boolean); // self-monitor слайса на TX
function PanPrimarySliceId(PanId: Integer): Integer; // первый созданный слайс пана
function PanHasFMSlice(PanId: Integer): Boolean; // есть слайс в канальном режиме (FM/DMR/FMRAW)?
procedure SetSliceDSP(Id, NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean);
procedure SetSliceFMSquelch(Id: Integer; On_: Boolean; Level: Integer);
function SliceSMeter(Id: Integer): Double; // dBm, из DSP-потока
@@ -2093,6 +2094,21 @@ begin
Result := FSlices[i].Id;
end;
function TRadioController.PanHasFMSlice(PanId: Integer): Boolean;
// Пан «канальный», если хотя бы один его слайс сидит в FM/DMR/FMRAW: под это
// UI рисует сетку частот по шагу FFMStepIdx (как на главном панадаптере).
var i: Integer;
begin
Result := False;
for i := 0 to MAX_SLICES - 1 do
if FSlices[i].Active and (FSlices[i].PanId = PanId) and
(FSlices[i].Mode in [MODE_FM, MODE_DMR, MODE_FMRAW]) then
begin
Result := True;
Exit;
end;
end;
procedure TRadioController.SetSliceDSP(Id, NRMode, NBMode: Integer;
SNBOn, ANFOn: Boolean);
var idx: Integer;