From 341bb249fd9d5364ff56507f1a2e841ff0714cb5 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 15 Jun 2026 20:02:17 +0300 Subject: [PATCH] fix: four logic bugs in RadioController MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. StartRunning applied AGC mode via a raw enum cast (TWDSPAGCMode(FAGCMode)) instead of the index→enum table, inverting Fast↔Off on every connect/go-live (GUI and daemon). Now uses ApplyAGCToEngine like the rest of the unit. 2. SetFilterIdx indexed the per-mode BW/deviation tables with an unchecked Idx (FM tables hold only 2 entries) — out-of-bounds when driven from CAT/web. Now clamps Idx per mode (EnsureRange). 3. ConnectDevice loaded FActiveVfo / FCurrentBand straight from config without range validation; a corrupt/old blob could leave FCurrentBand invalid and make later SetMode/SetFilterIdx write past FBandCache. Both now clamped. 4. VfoSwap did not run the bandstack DSP restore that SetActiveVfo does, so a swap to a VFO on another band kept the old mode/filter/AGC while the band button highlighted the new band. Now mirrors SetActiveVfo. Co-Authored-By: Claude Opus 4.8 --- RadioController.pas | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/RadioController.pas b/RadioController.pas index 6631cdd..e9079d9 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -1472,18 +1472,27 @@ procedure TRadioController.VfoSwap; var T: Double; ActiveFreq: Double; - BandIdx: Integer; + NewBand: Integer; begin T := FVfoA; FVfoA := FVfoB; FVfoB := T; ActiveFreq := ActiveVfoHz; + // Bandstack: если активный после свопа VFO на другом ham-бэнде — сохраняем + // покидаемый бэнд и восстанавливаем DSP нового (mode/filter/AGC/CTUN/FM), как + // SetActiveVfo. Иначе своп на другой диапазон оставлял бы старые mode/фильтр/AGC + // при подсвеченной кнопке нового бэнда (рассинхрон). + if FCurrentXvtr < 0 then + begin + NewBand := FreqToBandIdx(ActiveFreq); + if (NewBand >= 0) and (NewBand <> FCurrentBand) then + begin + SaveCurrentBand; + FCurrentBand := NewBand; + ApplyBandDSP(FBandCache[NewBand]); // несёт Changed(rfBand) + рендер DSP-полей + FDriveLevel := CalcDriveByte; // PA-калибровка нового бэнда + end; + end; FCenterFreq := ActiveFreq; if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(0.0); - BandIdx := FreqToBandIdx(ActiveFreq); - if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then - begin - FCurrentBand := BandIdx; - Changed(rfBand); - end; PushNetworkState; Changed(rfVfoA); Changed(rfVfoB); @@ -1518,6 +1527,10 @@ end; procedure TRadioController.SetFilterIdx(Idx: Integer); begin + // Клампим индекс под текущий режим (как FilterBWFor) — публичная команда, + // Idx может прийти из CAT/web без проверки; FM-таблицы всего на 2 элемента. + if FMode = MODE_FM then Idx := EnsureRange(Idx, 0, FILT_FM_COUNT - 1) + else Idx := EnsureRange(Idx, 0, FILT_COUNT - 1); FFilter := Idx; case FMode of 0, 1: FFilterBW := FILT_SSB_BW[Idx]; @@ -2225,7 +2238,7 @@ begin ApplyNoiseFilterButtonsToDSP; // ChangeSampleRate пересоздаёт WDSP-канал — восстанавливаем все настройки FDSPEngine.SetAGCTop(FAGCTop); - FDSPEngine.SetAGC(TWDSPAGCMode(FAGCMode), 50.0); + ApplyAGCToEngine; // единый маппинг UI-индекс→enum (не прямой каст) end; // Сначала отправляем Run=1 — эмулятор/железо создают ddc_specific_thread @@ -2309,7 +2322,7 @@ begin if FTXSettings.TXSpecGridStep > 0 then FTXSpecGridStep := FTXSettings.TXSpecGridStep; FVolume := FLoadedGlobal.Volume; - FActiveVfo := FLoadedGlobal.ActiveVfo; + FActiveVfo := EnsureRange(FLoadedGlobal.ActiveVfo, 0, 1); // битый конфиг не должен дать невалидный VFO FPAMaxPower := FLoadedGlobal.PAMaxPower; for i := 0 to CFG_BAND_COUNT - 1 do FPABandCal[i] := FLoadedGlobal.PABandCal[i]; @@ -2318,7 +2331,9 @@ begin // drive% (0..100) — раньше грузился через TrkDrive.Position; теперь напрямую, // слайдер выставит rfDevice-рендер. Калиброванный байт считаем после FCurrentBand. FDrivePercent := EnsureRange(FLoadedGlobal.DriveLevel, 0, 100); - FCurrentBand := FLoadedGlobal.LastBand; + // Клампим LastBand — иначе битый/старый конфиг → RestoreBand выйдет по гарду, + // а последующие SetMode/SetFilterIdx запишут в FBandCache[невалид] за массив. + FCurrentBand := EnsureRange(FLoadedGlobal.LastBand, 0, CFG_BAND_COUNT - 1); FDriveLevel := CalcDriveByte; // SampleRate — глобальный, до RestoreBand if FLoadedGlobal.SampleRate > 0 then