mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +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
@@ -110,6 +110,7 @@ type
|
||||
procedure Changed(Field: TRadioField);
|
||||
procedure ApplyAGCToEngine; // применяет FAGCMode к WDSP (единый маппинг)
|
||||
procedure ApplyModeDefaults; // дефолтный фильтр/девиация под текущий FMode
|
||||
procedure SanitizeFilterForMode; // FM: индекс фильтра 0/1 + BW/девиация (state)
|
||||
function ActiveVfoHz: Double; // частота активного VFO (A или B)
|
||||
procedure ApplyTuneCore(VfoHz: Double); // CTUN shift/center/scroll для актив. VFO
|
||||
public
|
||||
@@ -222,6 +223,15 @@ type
|
||||
// (band/xvtr cal, TUN/drive%, PA max). Чистая функция над состоянием.
|
||||
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).
|
||||
// Вызывается командами смены частоты/центра и UI после правки drive.
|
||||
procedure PushNetworkState;
|
||||
@@ -500,6 +510,151 @@ begin
|
||||
Result := Min(Round(audio_vol * 1.02 * 255.0), 255);
|
||||
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;
|
||||
begin
|
||||
if FActiveVfo = 0 then Result := FVfoA else Result := FVfoB;
|
||||
|
||||
Reference in New Issue
Block a user