mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
Phase 3 (batch 15): move band save/restore into the controller
Per the architecture decision, band state lifecycle belongs on the controller (it operates entirely on controller state + engines; persistence is already delegated to Settings.pas). A separate unit would need friend access to ~20 fields + engines — worse coupling. - MakeBandSettings, SaveCurrentBand: moved verbatim (pure over controller state; SaveCurrentBand handles the XVTR-slot vs HF band-cache split and delegates the write to FSettings). MainForm.SaveCurrentBand is now a thin delegate (22 call sites unchanged); MakeBandSettings removed from MainForm. - RestoreBand: state + DSP application moved to the controller; rendering is driven by per-field Changed events (rfBand/rfMode/rfFilter/rfAGC*/rfFM*/ rfCTun/rfVfoB). The FM filter-index sanitize (which UpdateFilterButtons did inline as a render side-effect) is extracted into a pure SanitizeFilterForMode so the DSP ApplyModeFilter still runs on the sanitized BW (preserves original ordering). Fixes the same AGC index-cast bug as earlier batches (now ApplyAGCToEngine). - MainForm.RestoreBand is a thin wrapper: ResetWfAvgBuf (band-switch-only, not per-tune) + controller.RestoreBand + ApplyVfoA(FVfoA) for the final tune with channel orchestration (auto-CTCSS / pre-channel drive). - Added rfFMStep render case (BtnFMStep on/off was styled ad-hoc). Builds clean, links. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a8a192a3a3
commit
24102343ea
+15
-135
@@ -412,7 +412,6 @@ type
|
|||||||
procedure RestoreWindowBounds;
|
procedure RestoreWindowBounds;
|
||||||
procedure SaveWindowBounds;
|
procedure SaveWindowBounds;
|
||||||
function MakeGlobalSettings: TGlobalSettings;
|
function MakeGlobalSettings: TGlobalSettings;
|
||||||
function MakeBandSettings: TBandSettings;
|
|
||||||
procedure FreqDispBChanged(Sender: TObject; NewFreq: Int64);
|
procedure FreqDispBChanged(Sender: TObject; NewFreq: Int64);
|
||||||
procedure BtnBandClick(Sender: TObject);
|
procedure BtnBandClick(Sender: TObject);
|
||||||
procedure BtnModeClick(Sender: TObject);
|
procedure BtnModeClick(Sender: TObject);
|
||||||
@@ -787,28 +786,6 @@ begin
|
|||||||
FController.FSettings.SaveWindowBounds(L, T, Wd, Ht, DPI, Maximized);
|
FController.FSettings.SaveWindowBounds(L, T, Wd, Ht, DPI, Maximized);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMainForm.MakeBandSettings: TBandSettings;
|
|
||||||
begin
|
|
||||||
Result.VfoA := FController.FVfoA;
|
|
||||||
Result.VfoB := FController.FVfoB;
|
|
||||||
Result.Mode := FController.FMode;
|
|
||||||
Result.FilterIdx := FController.FFilter;
|
|
||||||
Result.FilterBW := FController.FFilterBW;
|
|
||||||
Result.AGCMode := FController.FAGCMode;
|
|
||||||
Result.AGCTop := FController.FAGCTop;
|
|
||||||
Result.CTun := FController.FCTun;
|
|
||||||
Result.SpanHz := FController.FSpanHz;
|
|
||||||
Result.FMSQOn := FController.FFMSQOn;
|
|
||||||
Result.FMSQLevel := FController.FFMSQLevel;
|
|
||||||
Result.CTCSSOn := FController.FFMCTCSSOn;
|
|
||||||
Result.CTCSSToneIdx := FController.FFMCTCSSToneIdx;
|
|
||||||
Result.FMStepOn := FController.FFMStepOn;
|
|
||||||
Result.FMStepIdx := FController.FFMStepIdx;
|
|
||||||
Result.FMRptDir := FController.FFMRptDir;
|
|
||||||
Result.FMRptOffsetHz := FController.FFMRptOffsetHz;
|
|
||||||
// Waterfall AGC/NF — глобальные (не диапазонные)
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMainForm.MakeGlobalSettings: TGlobalSettings;
|
function TMainForm.MakeGlobalSettings: TGlobalSettings;
|
||||||
var i: Integer;
|
var i: Integer;
|
||||||
begin
|
begin
|
||||||
@@ -889,124 +866,21 @@ end;
|
|||||||
|
|
||||||
procedure TMainForm.SaveCurrentBand;
|
procedure TMainForm.SaveCurrentBand;
|
||||||
begin
|
begin
|
||||||
if not FController.FDevConnected then Exit;
|
// Логика сохранения (band-cache/XVTR-слот + конфиг) — в контроллере.
|
||||||
// В XVTR-режиме НЕ трогаем HF band-cache: FController.FBandCache[FController.FCurrentBand]
|
FController.SaveCurrentBand;
|
||||||
// относится к HF-диапазону, на котором пользователь был ДО активации
|
|
||||||
// трансвертера, и FController.FVfoA сейчас на XVTR-частоте (144.x, 432.x и т.д.).
|
|
||||||
// Запись в FController.FBandCache[FController.FCurrentBand] стерла бы корректное HF-состояние.
|
|
||||||
// Вместо этого сохраняем LastFreq в XVTR-настройках.
|
|
||||||
if FController.FCurrentXvtr >= 0 then
|
|
||||||
begin
|
|
||||||
if (FController.FCurrentXvtr < CFG_XVTR_COUNT) then
|
|
||||||
begin
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFreq := FController.FVfoA;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastMode := FController.FMode;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFilterIdx := FController.FFilter;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMSQOn := FController.FFMSQOn;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMSQLevel := FController.FFMSQLevel;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTCSSOn := FController.FFMCTCSSOn;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTCSSToneIdx := FController.FFMCTCSSToneIdx;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMStepOn := FController.FFMStepOn;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMStepIdx := FController.FFMStepIdx;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMRptDir := FController.FFMRptDir;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMRptOffsetHz := FController.FFMRptOffsetHz;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTun := FController.FCTun;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastAGCMode := FController.FAGCMode;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastAGCTop := FController.FAGCTop;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTCSSAutoActive := FController.FFMCTCSSAutoActive;
|
|
||||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMRptAutoActive := FController.FFMRptAutoActive;
|
|
||||||
end;
|
|
||||||
FController.FSettings.SaveXvtr(FController.FDevMAC, FController.FXvtrSettings);
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
// Не сохраняем, если VFO ушёл в VHF/UHF область (например, после применения
|
|
||||||
// канала на 2м без XVTR) — иначе HF-кэш диапазона будет перезаписан VHF-частотой.
|
|
||||||
if FController.FVfoA > 61000000 then Exit;
|
|
||||||
FController.FBandCache[FController.FCurrentBand] := MakeBandSettings;
|
|
||||||
FController.FSettings.SaveBand(FController.FDevMAC, FController.FCurrentBand, FController.FBandCache[FController.FCurrentBand]);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.RestoreBand(BandIdx: Integer);
|
procedure TMainForm.RestoreBand(BandIdx: Integer);
|
||||||
var
|
|
||||||
B: TBandSettings;
|
|
||||||
i: Integer;
|
|
||||||
begin
|
begin
|
||||||
if (BandIdx < 0) or (BandIdx >= CFG_BAND_COUNT) then Exit;
|
if (BandIdx < 0) or (BandIdx >= CFG_BAND_COUNT) then Exit;
|
||||||
B := FController.FBandCache[BandIdx];
|
// Сброс порогов водопада — только при явной смене диапазона (не на каждом
|
||||||
|
// пересечении границы при тюнинге, поэтому здесь, а не в rfBand-рендере).
|
||||||
// --- Кнопки диапазонов ---
|
|
||||||
for i := 0 to BAND_COUNT - 1 do
|
|
||||||
StyleButton(BtnBand[i], i = BandIdx);
|
|
||||||
// Сбрасываем waterfall thresholds при смене диапазона
|
|
||||||
FSpecView.ResetWfAvgBuf;
|
FSpecView.ResetWfAvgBuf;
|
||||||
|
// Состояние + DSP + события рендера — в контроллере.
|
||||||
// --- Режим: сначала устанавливаем FController.FMode, потом кнопки и WDSP ---
|
FController.RestoreBand(BandIdx);
|
||||||
FController.FMode := B.Mode;
|
// Финальная перестройка VFO A с канальной оркестрацией (auto-CTCSS,
|
||||||
for i := 0 to MODE_COUNT - 1 do
|
// pre-channel drive) — через UI-путь ApplyVfoA.
|
||||||
StyleButton(BtnMode[i], i = FController.FMode);
|
ApplyVfoA(Round(FController.FVfoA));
|
||||||
if FController.FWDSPReady then
|
|
||||||
FController.FDSPEngine.SetMode(FController.FMode);
|
|
||||||
|
|
||||||
// --- Фильтр ---
|
|
||||||
FController.FFilter := B.FilterIdx;
|
|
||||||
FController.FFilterBW := B.FilterBW;
|
|
||||||
UpdateFilterButtons; // обновляет кнопки фильтра
|
|
||||||
FController.ApplyModeFilter; // применяет Lo/Hi в WDSP с учётом нового FController.FMode
|
|
||||||
|
|
||||||
// --- AGC ---
|
|
||||||
FController.FAGCMode := B.AGCMode;
|
|
||||||
FController.FAGCTop := B.AGCTop;
|
|
||||||
for i := 0 to 4 do
|
|
||||||
StyleButton(BtnAGCMode[i], i = FController.FAGCMode);
|
|
||||||
TrkAGC.Position := FController.FAGCTop;
|
|
||||||
LblAGCTop.Caption := Format('%ddB', [FController.FAGCTop]);
|
|
||||||
if FController.FWDSPReady then
|
|
||||||
begin
|
|
||||||
FController.FDSPEngine.SetAGCTop(FController.FAGCTop);
|
|
||||||
FController.FDSPEngine.SetAGC(TWDSPAGCMode(FController.FAGCMode), 50.0);
|
|
||||||
end;
|
|
||||||
FSpecView.AGCTop := FController.FAGCTop;
|
|
||||||
|
|
||||||
// --- FM Squelch + CTCSS ---
|
|
||||||
FController.FFMSQOn := B.FMSQOn;
|
|
||||||
FController.FFMSQLevel := B.FMSQLevel;
|
|
||||||
// UI update happens inside UpdateFilterButtons (called above) when mode=FM
|
|
||||||
FController.FFMCTCSSOn := B.CTCSSOn;
|
|
||||||
if BtnFMCTCSS <> nil then StyleButton(BtnFMCTCSS, FController.FFMCTCSSOn);
|
|
||||||
SetFMCTCSSTone(B.CTCSSToneIdx);
|
|
||||||
ApplyFMSquelch;
|
|
||||||
FController.FFMStepOn := B.FMStepOn;
|
|
||||||
SetFMStep(B.FMStepIdx);
|
|
||||||
if BtnFMStep <> nil then StyleButton(BtnFMStep, FController.FFMStepOn);
|
|
||||||
FController.FFMRptDir := B.FMRptDir;
|
|
||||||
FController.FFMRptOffsetHz := B.FMRptOffsetHz;
|
|
||||||
if FController.FFMRptOffsetHz <= 0 then
|
|
||||||
FController.FFMRptOffsetHz := RptDefaultOffsetHz(B.VfoA);
|
|
||||||
if BtnFMRptMinus <> nil then StyleButton(BtnFMRptMinus, FController.FFMRptDir = RPT_MINUS);
|
|
||||||
if BtnFMRptPlus <> nil then StyleButton(BtnFMRptPlus, FController.FFMRptDir = RPT_PLUS);
|
|
||||||
if EdFMRptOffset <> nil then EdFMRptOffset.Text := RptFormatOffset(FController.FFMRptOffsetHz);
|
|
||||||
|
|
||||||
// --- CTUN ---
|
|
||||||
FController.FCTun := B.CTun;
|
|
||||||
StyleButton(BtnCTun, FController.FCTun);
|
|
||||||
if FController.FWDSPReady and not FController.FCTun then
|
|
||||||
FController.FDSPEngine.SetShift(0.0);
|
|
||||||
|
|
||||||
// --- Sample Rate / Span ---
|
|
||||||
// SampleRate — глобальный, не per-band. Не восстанавливаем из диапазона.
|
|
||||||
FController.FSpanHz := FController.FSampleRate;
|
|
||||||
if Assigned(FSampleRateOverlay) then
|
|
||||||
FSampleRateOverlay.SetCurrentRate(FController.FSampleRate);
|
|
||||||
|
|
||||||
// --- VFO A: центрируем на новой частоте, сбрасываем shift ---
|
|
||||||
FController.FCenterFreq := B.VfoA; // DDC = центр диапазона
|
|
||||||
FController.FVfoB := B.VfoB;
|
|
||||||
FreqDispB.Frequency := Round(FController.FVfoB);
|
|
||||||
|
|
||||||
// Пересчитываем drive byte для нового диапазона перед отправкой в сеть
|
|
||||||
FController.FDriveLevel := CalcDriveByte;
|
|
||||||
// ApplyVfoA обновит сеть, FreqDisp, shift, Ruler — перерисует.
|
|
||||||
ApplyVfoA(Round(B.VfoA));
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.SaveAllAndExit;
|
procedure TMainForm.SaveAllAndExit;
|
||||||
@@ -1492,6 +1366,12 @@ begin
|
|||||||
if (FController.FMode = MODE_FM) and (FSpecView <> nil) then
|
if (FController.FMode = MODE_FM) and (FSpecView <> nil) then
|
||||||
FSpecView.FMGridStepHz := FM_STEP_HZ[FController.FFMStepIdx];
|
FSpecView.FMGridStepHz := FM_STEP_HZ[FController.FFMStepIdx];
|
||||||
end;
|
end;
|
||||||
|
rfFMStep:
|
||||||
|
if BtnFMStep <> nil then
|
||||||
|
begin
|
||||||
|
StyleButton(BtnFMStep, FController.FFMStepOn);
|
||||||
|
BtnFMStep.Repaint;
|
||||||
|
end;
|
||||||
rfFMSQ:
|
rfFMSQ:
|
||||||
begin
|
begin
|
||||||
StyleButton(BtnFMSQ, FController.FFMSQOn);
|
StyleButton(BtnFMSQ, FController.FFMSQOn);
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ type
|
|||||||
procedure Changed(Field: TRadioField);
|
procedure Changed(Field: TRadioField);
|
||||||
procedure ApplyAGCToEngine; // применяет FAGCMode к WDSP (единый маппинг)
|
procedure ApplyAGCToEngine; // применяет FAGCMode к WDSP (единый маппинг)
|
||||||
procedure ApplyModeDefaults; // дефолтный фильтр/девиация под текущий FMode
|
procedure ApplyModeDefaults; // дефолтный фильтр/девиация под текущий FMode
|
||||||
|
procedure SanitizeFilterForMode; // FM: индекс фильтра 0/1 + BW/девиация (state)
|
||||||
function ActiveVfoHz: Double; // частота активного VFO (A или B)
|
function ActiveVfoHz: Double; // частота активного VFO (A или B)
|
||||||
procedure ApplyTuneCore(VfoHz: Double); // CTUN shift/center/scroll для актив. VFO
|
procedure ApplyTuneCore(VfoHz: Double); // CTUN shift/center/scroll для актив. VFO
|
||||||
public
|
public
|
||||||
@@ -222,6 +223,15 @@ type
|
|||||||
// (band/xvtr cal, TUN/drive%, PA max). Чистая функция над состоянием.
|
// (band/xvtr cal, TUN/drive%, PA max). Чистая функция над состоянием.
|
||||||
function CalcDriveByte: Byte;
|
function CalcDriveByte: Byte;
|
||||||
|
|
||||||
|
// ---- Управление диапазонами (состояние + DSP; рендер через события) ----
|
||||||
|
// MakeBandSettings — снимок текущего состояния в запись диапазона.
|
||||||
|
// SaveCurrentBand — сохраняет текущее состояние в кэш+конфиг (или в XVTR-слот).
|
||||||
|
// RestoreBand — загружает состояние диапазона, применяет к DSP, шлёт события
|
||||||
|
// рендера. Финальную перестройку VFO A делает вызывающий (UI: ApplyVfoA).
|
||||||
|
function MakeBandSettings: TBandSettings;
|
||||||
|
procedure SaveCurrentBand;
|
||||||
|
procedure RestoreBand(BandIdx: Integer);
|
||||||
|
|
||||||
// Полный HP-кадр в радио (RX/TX freq через трансвертор + drive + MOX).
|
// Полный HP-кадр в радио (RX/TX freq через трансвертор + drive + MOX).
|
||||||
// Вызывается командами смены частоты/центра и UI после правки drive.
|
// Вызывается командами смены частоты/центра и UI после правки drive.
|
||||||
procedure PushNetworkState;
|
procedure PushNetworkState;
|
||||||
@@ -500,6 +510,151 @@ begin
|
|||||||
Result := Min(Round(audio_vol * 1.02 * 255.0), 255);
|
Result := Min(Round(audio_vol * 1.02 * 255.0), 255);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.SanitizeFilterForMode;
|
||||||
|
// Для FM индексы фильтров только 0/1 (NFM/WFM); приводим FFilter и
|
||||||
|
// синхронизируем FFilterBW/FFMDeviation. Чистая (state) часть UpdateFilterButtons.
|
||||||
|
begin
|
||||||
|
if FMode = MODE_FM then
|
||||||
|
begin
|
||||||
|
if FFilter > 1 then FFilter := FILT_FM_DEF;
|
||||||
|
FFilterBW := FILT_FM_BW[FFilter];
|
||||||
|
FFMDeviation := FILT_FM_DEV[FFilter];
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TRadioController.MakeBandSettings: TBandSettings;
|
||||||
|
begin
|
||||||
|
Result.VfoA := FVfoA;
|
||||||
|
Result.VfoB := FVfoB;
|
||||||
|
Result.Mode := FMode;
|
||||||
|
Result.FilterIdx := FFilter;
|
||||||
|
Result.FilterBW := FFilterBW;
|
||||||
|
Result.AGCMode := FAGCMode;
|
||||||
|
Result.AGCTop := FAGCTop;
|
||||||
|
Result.CTun := FCTun;
|
||||||
|
Result.SpanHz := FSpanHz;
|
||||||
|
Result.FMSQOn := FFMSQOn;
|
||||||
|
Result.FMSQLevel := FFMSQLevel;
|
||||||
|
Result.CTCSSOn := FFMCTCSSOn;
|
||||||
|
Result.CTCSSToneIdx := FFMCTCSSToneIdx;
|
||||||
|
Result.FMStepOn := FFMStepOn;
|
||||||
|
Result.FMStepIdx := FFMStepIdx;
|
||||||
|
Result.FMRptDir := FFMRptDir;
|
||||||
|
Result.FMRptOffsetHz := FFMRptOffsetHz;
|
||||||
|
// Waterfall AGC/NF — глобальные (не диапазонные)
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.SaveCurrentBand;
|
||||||
|
// Сохраняет текущее состояние диапазона. В XVTR-режиме пишем в XVTR-слот
|
||||||
|
// (HF band-cache не трогаем — там сохранён HF-диапазон до активации XVTR).
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
if not FDevConnected then Exit;
|
||||||
|
if FCurrentXvtr >= 0 then
|
||||||
|
begin
|
||||||
|
if (FCurrentXvtr < CFG_XVTR_COUNT) then
|
||||||
|
begin
|
||||||
|
i := FCurrentXvtr;
|
||||||
|
FXvtrSettings.Entries[i].LastFreq := FVfoA;
|
||||||
|
FXvtrSettings.Entries[i].LastMode := FMode;
|
||||||
|
FXvtrSettings.Entries[i].LastFilterIdx := FFilter;
|
||||||
|
FXvtrSettings.Entries[i].LastFMSQOn := FFMSQOn;
|
||||||
|
FXvtrSettings.Entries[i].LastFMSQLevel := FFMSQLevel;
|
||||||
|
FXvtrSettings.Entries[i].LastCTCSSOn := FFMCTCSSOn;
|
||||||
|
FXvtrSettings.Entries[i].LastCTCSSToneIdx := FFMCTCSSToneIdx;
|
||||||
|
FXvtrSettings.Entries[i].LastFMStepOn := FFMStepOn;
|
||||||
|
FXvtrSettings.Entries[i].LastFMStepIdx := FFMStepIdx;
|
||||||
|
FXvtrSettings.Entries[i].LastFMRptDir := FFMRptDir;
|
||||||
|
FXvtrSettings.Entries[i].LastFMRptOffsetHz := FFMRptOffsetHz;
|
||||||
|
FXvtrSettings.Entries[i].LastCTun := FCTun;
|
||||||
|
FXvtrSettings.Entries[i].LastAGCMode := FAGCMode;
|
||||||
|
FXvtrSettings.Entries[i].LastAGCTop := FAGCTop;
|
||||||
|
FXvtrSettings.Entries[i].LastCTCSSAutoActive := FFMCTCSSAutoActive;
|
||||||
|
FXvtrSettings.Entries[i].LastFMRptAutoActive := FFMRptAutoActive;
|
||||||
|
end;
|
||||||
|
FSettings.SaveXvtr(FDevMAC, FXvtrSettings);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
// Не сохраняем, если VFO ушёл в VHF/UHF (например, после канала на 2м без
|
||||||
|
// XVTR) — иначе HF-кэш диапазона перезапишется VHF-частотой.
|
||||||
|
if FVfoA > 61000000 then Exit;
|
||||||
|
FBandCache[FCurrentBand] := MakeBandSettings;
|
||||||
|
FSettings.SaveBand(FDevMAC, FCurrentBand, FBandCache[FCurrentBand]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.RestoreBand(BandIdx: Integer);
|
||||||
|
// Загружает состояние диапазона из кэша, применяет к DSP и шлёт события рендера.
|
||||||
|
// Финальную перестройку VFO A (с канальной оркестрацией) делает вызывающий
|
||||||
|
// UI-путь через ApplyVfoA(FVfoA); здесь FVfoA лишь выставляется.
|
||||||
|
var B: TBandSettings;
|
||||||
|
begin
|
||||||
|
if (BandIdx < 0) or (BandIdx >= CFG_BAND_COUNT) then Exit;
|
||||||
|
B := FBandCache[BandIdx];
|
||||||
|
FCurrentBand := BandIdx;
|
||||||
|
|
||||||
|
// --- Режим ---
|
||||||
|
FMode := B.Mode;
|
||||||
|
if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetMode(FMode);
|
||||||
|
|
||||||
|
// --- Фильтр (санитизация FM до применения к DSP — как в оригинале) ---
|
||||||
|
FFilter := B.FilterIdx;
|
||||||
|
FFilterBW := B.FilterBW;
|
||||||
|
SanitizeFilterForMode;
|
||||||
|
ApplyModeFilter;
|
||||||
|
|
||||||
|
// --- AGC (единый маппинг — исправляет каст-баг RestoreBand) ---
|
||||||
|
FAGCMode := B.AGCMode;
|
||||||
|
FAGCTop := B.AGCTop;
|
||||||
|
if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetAGCTop(FAGCTop);
|
||||||
|
ApplyAGCToEngine;
|
||||||
|
|
||||||
|
// --- FM squelch / CTCSS / step / repeater ---
|
||||||
|
FFMSQOn := B.FMSQOn;
|
||||||
|
FFMSQLevel := B.FMSQLevel;
|
||||||
|
FFMCTCSSOn := B.CTCSSOn;
|
||||||
|
FFMCTCSSToneIdx := B.CTCSSToneIdx;
|
||||||
|
if FWDSPReady and Assigned(FDSPEngine) then
|
||||||
|
begin
|
||||||
|
FDSPEngine.SetTXCTCSS(FFMCTCSSOn, CTCSS_TONES[FFMCTCSSToneIdx]);
|
||||||
|
FDSPEngine.SetFMSquelch(FFMSQOn and (FMode = MODE_FM), FFMSQLevel);
|
||||||
|
end;
|
||||||
|
FFMStepOn := B.FMStepOn;
|
||||||
|
FFMStepIdx := EnsureRange(B.FMStepIdx, 0, 3);
|
||||||
|
FFMRptDir := B.FMRptDir;
|
||||||
|
FFMRptOffsetHz := B.FMRptOffsetHz;
|
||||||
|
if FFMRptOffsetHz <= 0 then FFMRptOffsetHz := RptDefaultOffsetHz(B.VfoA);
|
||||||
|
|
||||||
|
// --- CTUN ---
|
||||||
|
FCTun := B.CTun;
|
||||||
|
if FWDSPReady and Assigned(FDSPEngine) and not FCTun then FDSPEngine.SetShift(0.0);
|
||||||
|
|
||||||
|
// --- Span (sample rate глобальный, не per-band — лишь ресинк) ---
|
||||||
|
FSpanHz := FSampleRate;
|
||||||
|
|
||||||
|
// --- VFO / drive ---
|
||||||
|
FCenterFreq := B.VfoA;
|
||||||
|
FVfoB := B.VfoB;
|
||||||
|
FVfoA := B.VfoA;
|
||||||
|
FDriveLevel := CalcDriveByte;
|
||||||
|
|
||||||
|
// --- Рендер: единичные события (UI рисует через OnControllerState) ---
|
||||||
|
Changed(rfBand);
|
||||||
|
Changed(rfMode); // rfMode-рендер зовёт UpdateFilterButtons (FM-панели)
|
||||||
|
Changed(rfFilter);
|
||||||
|
Changed(rfAGCMode);
|
||||||
|
Changed(rfAGCTop);
|
||||||
|
Changed(rfFMSQ);
|
||||||
|
Changed(rfFMSQLevel);
|
||||||
|
Changed(rfFMCTCSS);
|
||||||
|
Changed(rfFMCTCSSTone);
|
||||||
|
Changed(rfFMStep); // кнопка вкл/выкл STEP
|
||||||
|
Changed(rfFMStepIdx); // дропдаун шага + web + grid
|
||||||
|
Changed(rfFMRpt);
|
||||||
|
Changed(rfCTun);
|
||||||
|
Changed(rfVfoB);
|
||||||
|
// rfVfoA НЕ шлём: финальную перестройку делает ApplyVfoA в UI-обёртке.
|
||||||
|
end;
|
||||||
|
|
||||||
function TRadioController.ActiveVfoHz: Double;
|
function TRadioController.ActiveVfoHz: Double;
|
||||||
begin
|
begin
|
||||||
if FActiveVfo = 0 then Result := FVfoA else Result := FVfoB;
|
if FActiveVfo = 0 then Result := FVfoA else Result := FVfoB;
|
||||||
|
|||||||
Reference in New Issue
Block a user