diff --git a/RadioController.pas b/RadioController.pas index b233a31..0474698 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -110,6 +110,8 @@ type procedure Changed(Field: TRadioField); procedure ApplyAGCToEngine; // применяет FAGCMode к WDSP (единый маппинг) procedure ApplyModeDefaults; // дефолтный фильтр/девиация под текущий FMode + function ActiveVfoHz: Double; // частота активного VFO (A или B) + procedure ApplyTuneCore(VfoHz: Double); // CTUN shift/center/scroll для актив. VFO public // ============================================================ // ПУБЛИЧНЫЕ ПОЛЯ (миграционные — имена совпадают с TMainForm). @@ -496,16 +498,54 @@ begin Result := Min(Round(audio_vol * 1.02 * 255.0), 255); end; +function TRadioController.ActiveVfoHz: Double; +begin + if FActiveVfo = 0 then Result := FVfoA else Result := FVfoB; +end; + +procedure TRadioController.ApplyTuneCore(VfoHz: Double); +// Общее ядро перестройки приёмника на частоту АКТИВНОГО VFO (A или B). +// CTUN OFF: DDC = VfoHz, сдвиг 0. +// CTUN ON: DDC фиксирован, демодулятор сдвигаем на VfoHz-FCenterFreq; +// если полоса фильтра вышла за край дисплея — прокручиваем центр. +// Ставит FCenterFreq / shift / FLastVfoScrolled. +var + Offset, FiltLo, FiltHi, HalfSpan: Double; +begin + FLastVfoScrolled := False; + if not FCTun then + begin + FCenterFreq := VfoHz; + if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(0.0); + end + else + begin + Offset := VfoHz - FCenterFreq; + if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(Offset); + HalfSpan := FSpanHz / 2; + case FMode of + 0: begin FiltLo := Offset - FFilterBW; FiltHi := Offset - 100; end; + 1: begin FiltLo := Offset + 100; FiltHi := Offset + FFilterBW; end; + else begin FiltLo := Offset - FFilterBW/2; FiltHi := Offset + FFilterBW/2; end; + end; + if (FiltHi > HalfSpan) or (FiltLo < -HalfSpan) then + begin + if FiltHi > HalfSpan then + FCenterFreq := VfoHz - HalfSpan * 0.5 + else + FCenterFreq := VfoHz + HalfSpan * 0.5; + if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(VfoHz - FCenterFreq); + FLastVfoScrolled := True; + end; + end; +end; + procedure TRadioController.SetVfoA(Hz: Double); -// Чистое ядро перестройки VFO A: XVTR-клэмп, CTUN shift/center (с прокруткой -// центра при выходе полосы фильтра за край), детект диапазона, FM авто-RPT, -// проталкивание в сеть. Рендер (FreqDisp/спектр/band-кнопки) — в UI через -// OnControllerState(rfVfoA/rfBand). Канальную оркестрацию (авто-CTCSS, -// pre-channel drive) делает вызывающий UI-путь. +// Перестройка VFO A: XVTR-клэмп, CTUN ядро, детект диапазона, FM авто-RPT, +// проталкивание в сеть. Рендер — в UI через OnControllerState(rfVfoA/rfBand). +// Канальную оркестрацию (авто-CTCSS, pre-channel drive) делает UI-путь. var NewFreq: Int64; - Offset: Double; - FiltLo, FiltHi, HalfSpan: Double; BandIdx: Integer; begin NewFreq := Round(Hz); @@ -519,36 +559,7 @@ begin NewFreq := Round(FXvtrSettings.Entries[FCurrentXvtr].FreqEnd); end; FVfoA := NewFreq; - FLastVfoScrolled := False; - - if not FCTun then - begin - // CTUN OFF: DDC = VFO, сдвига нет - FCenterFreq := FVfoA; - if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(0.0); - end - else - begin - // CTUN ON: DDC фиксирован, демодулятор сдвигаем на FVfoA-FCenterFreq - Offset := FVfoA - FCenterFreq; - if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(Offset); - // Полоса фильтра вышла за край дисплея → прокручиваем центр - HalfSpan := FSpanHz / 2; - case FMode of - 0: begin FiltLo := Offset - FFilterBW; FiltHi := Offset - 100; end; - 1: begin FiltLo := Offset + 100; FiltHi := Offset + FFilterBW; end; - else begin FiltLo := Offset - FFilterBW/2; FiltHi := Offset + FFilterBW/2; end; - end; - if (FiltHi > HalfSpan) or (FiltLo < -HalfSpan) then - begin - if FiltHi > HalfSpan then - FCenterFreq := FVfoA - HalfSpan * 0.5 - else - FCenterFreq := FVfoA + HalfSpan * 0.5; - if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(FVfoA - FCenterFreq); - FLastVfoScrolled := True; - end; - end; + ApplyTuneCore(FVfoA); // Детект диапазона (только HF: в XVTR клэмп держит VFO внутри band, // FreqToBandIdx вернёт -1 для VHF/UHF). @@ -571,17 +582,16 @@ begin end; procedure TRadioController.SetVfoB(Hz: Double); -// VFO B всегда сохраняется. Если B активен — перестраиваем приёмник (DDC=VFO B, -// без CTUN, сдвиг 0), детектим диапазон и толкаем в сеть. Если B неактивен -// (split, слушаем A) — только сохраняем частоту (используется для TX). +// VFO B всегда сохраняется. Если B активен — перестраиваем приёмник через +// ApplyTuneCore (с учётом CTUN), детектим диапазон и толкаем в сеть. Если B +// неактивен (split, слушаем A) — только сохраняем частоту (для TX). // Рендер FreqDispB/спектра — в UI через OnControllerState(rfVfoB). var BandIdx: Integer; begin FVfoB := Hz; if FActiveVfo = 1 then begin - FCenterFreq := FVfoB; - if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(0.0); + ApplyTuneCore(FVfoB); if FCurrentXvtr < 0 then begin BandIdx := FreqToBandIdx(FVfoB); @@ -788,9 +798,9 @@ begin FBandCache[FCurrentBand].CTun := FCTun; if not FCTun then begin - // Выключили CTUN: центрируемся на VFO, shift=0. (Включение CTUN не трогает - // движок — FCenterFreq остаётся как есть, спектр не прыгает.) - FCenterFreq := FVfoA; + // Выключили CTUN: центрируемся на АКТИВНОМ VFO, shift=0. (Включение CTUN не + // трогает движок — FCenterFreq остаётся как есть, спектр не прыгает.) + FCenterFreq := ActiveVfoHz; if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(0.0); PushNetworkState; end;