From b3edf3a4adeff11d5bc24326440c92bf66735625 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Sun, 12 Jul 2026 16:02:54 +0300 Subject: [PATCH] =?UTF-8?q?fix(slices):=20XVTR-=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D1=81=D0=BB=D1=8F=D1=86=D0=B8=D1=8F=20=D1=87=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D1=82=20=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20N?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Частота пана хранится ВИДИМОЙ (как FVfoA/FCenterFreq) и транслируется в IF через XvtrTranslate только на границе с железом (AddPanDDC, SetPanDDCFreq, SetPanDDCRate, SetPanADC) — раньше DDC пана получал видимую частоту сырьём, а drag водопада пана N клампился пределами АЦП (~61 МГц) вместо диапазона трансвертера. Новое: controller.VisibleFreqBounds (диапазон активного XVTR | пределы железа) для UI-клампа драга; RefreshPanDDCs — на входе/выходе XVTR и правке LO/LOError клампит видимые центры живых панов, перешивает freq-слова DDC и прижимает выпавшие слайсы к краям полосы захвата. Подтверждено юзером на трансвертере. Co-Authored-By: Claude Fable 5 --- MainForm.pas | 8 +++-- RadioController.pas | 82 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/MainForm.pas b/MainForm.pas index 913195b..4f4efc7 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -6154,8 +6154,10 @@ begin end; PanViewWindow(P, VC, VS); NewHz := FPanCenterDragF0 - (X - FPanCenterDragX0) * VS / W; - NewHz := Max(FController.BackendCaps.MinFreqHz, - Min(FController.BackendCaps.MaxFreqHz, NewHz)); + // Кламп в ВИДИМЫХ частотах: при активном XVTR — его диапазон (иначе + // 1296 МГц «падала» на MaxFreqHz железа ~61 МГц), без XVTR — железо. + FController.VisibleFreqBounds(LoHz, HiHz); + NewHz := Max(LoHz, Min(HiHz, NewHz)); FController.SetPanDDCFreq(P.PanId, NewHz); SyncPanZoomUI(P); // окно вьюхи с учётом зума + freq зум-бара P.LayoutFlags; @@ -6885,6 +6887,8 @@ begin DeactivateXvtr; // Перепрошить XVTR в сеть (если параметры текущего изменились) ApplyXvtrToNetwork; + // Правка LO/LOError при живых панах: freq-слова их DDC — через свежую трансляцию. + FController.RefreshPanDDCs; UpdateWidebandFrequencyView; if PbWideband <> nil then PbWideband.Invalidate; if PbWidebandRuler <> nil then PbWidebandRuler.Invalidate; diff --git a/RadioController.pas b/RadioController.pas index 7dc88ae..062d51c 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -565,13 +565,21 @@ type function SliceFitsCapture(TargetHz: Double; PanId: Integer = 0): Boolean; // ---- Панадаптеры на аппаратных DDC (этап 3.1; UI/движок — 3.2/3.3) ---- - // Создаёт пан PanId (1..MAX_PANS-1) на своём DDC: FreqHz = центр (сырые Гц - // железа, XVTR-трансляции у панов нет), RateKHz ∈ PanRateAllowed (иначе 384), - // ADCSource — АЦП платы. False = нет слота/бэкенд не умеет/не Running. + // Создаёт пан PanId (1..MAX_PANS-1) на своём DDC: FreqHz = центр в ВИДИМЫХ + // Гц (как FVfoA/FCenterFreq: при активном XVTR транслируется в IF только + // на границе с железом, XvtrTranslate), RateKHz ∈ PanRateAllowed (иначе + // 384), ADCSource — АЦП платы. False = нет слота/бэкенд не умеет/не Running. function AddPanDDC(PanId: Integer; FreqHz: Double; RateKHz: Word = 384; ADCSource: Byte = 0): Boolean; procedure RemovePanDDC(PanId: Integer); procedure SetPanDDCFreq(PanId: Integer; FreqHz: Double); // ретюн (HP-пакет) + // Допустимое окно ВИДИМОЙ частоты пана: активный XVTR — его диапазон, + // иначе пределы железа (UI-кламп drag/тюна панов N). + procedure VisibleFreqBounds(out LoHz, HiHz: Double); + // Вход/выход/перекалибровка XVTR при живых панах: клампит видимые частоты + // панов в новое окно, перешивает freq-слова DDC (XvtrTranslate), слайсы + // панов — к краям их полос захвата. + procedure RefreshPanDDCs; // Rate ∈ сетке бэкенда (Caps.RatePresets) и кратен 48 кГц (движок). function PanRateAllowed(RateKHz: Word): Boolean; // Смена rate живого пана: движок переоткрывает аккумулятор/анализатор/слайсы, @@ -1252,7 +1260,8 @@ begin FPanZoomF[PanId] := 0.0; FPanPanSl[PanId] := 0.5; FDDCPanMap[ddc] := PanId; - FNetwork.SetPanDDC(ddc, True, FreqHz, RateKHz, ADCSource); + // В железо — IF-частота: FPanFreqHz хранит видимую (как FCenterFreq). + FNetwork.SetPanDDC(ddc, True, XvtrTranslate(FreqHz), RateKHz, ADCSource); Result := True; end; @@ -1270,10 +1279,10 @@ begin if FWDSPReady and not FDSPEngine.SetPanRate(PanId, Integer(RateKHz) * 1000) then Exit; FPanRateKHz[PanId] := RateKHz; - // DDC железа: реконфиг rate (RebuildDDCSpecific + freq-слово). + // DDC железа: реконфиг rate (RebuildDDCSpecific + freq-слово, IF при XVTR). if Assigned(FNetwork) then - FNetwork.SetPanDDC(FPanDDCIdx[PanId], True, FPanFreqHz[PanId], RateKHz, - FPanADC[PanId]); + FNetwork.SetPanDDC(FPanDDCIdx[PanId], True, XvtrTranslate(FPanFreqHz[PanId]), + RateKHz, FPanADC[PanId]); // Слайсы, выпавшие из сузившейся полосы, — к её краям (абс. TargetHz + NCO). CapturedSpan(LoHz, HiHz, PanId); for i := 0 to MAX_SLICES - 1 do @@ -1312,9 +1321,9 @@ var i: Integer; begin if (PanId < 1) or (PanId >= MAX_PANS) then Exit; if FPanDDCIdx[PanId] < 0 then Exit; - FPanFreqHz[PanId] := FreqHz; + FPanFreqHz[PanId] := FreqHz; // видимая; в железо — IF при активном XVTR if Assigned(FNetwork) then - FNetwork.SetPanDDCFreq(FPanDDCIdx[PanId], FreqHz); + FNetwork.SetPanDDCFreq(FPanDDCIdx[PanId], XvtrTranslate(FreqHz)); // Слайсы пана держат абсолютный TargetHz — обновляем их NCO-сдвиги // относительно нового центра (аналог RefreshSliceShifts главного). if Assigned(FDSPEngine) then @@ -1323,6 +1332,57 @@ begin FDSPEngine.SetSliceShift(FSlices[i].Id, FSlices[i].TargetHz - FreqHz); end; +procedure TRadioController.VisibleFreqBounds(out LoHz, HiHz: Double); +begin + if (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT) and + FXvtrSettings.Entries[FCurrentXvtr].Enabled then + begin + // Активный XVTR: видимые частоты живут в его диапазоне, в железо уходит + // IF (XvtrTranslate) — клампить видимую частоту пределами АЦП нельзя. + LoHz := FXvtrSettings.Entries[FCurrentXvtr].FreqBegin; + HiHz := FXvtrSettings.Entries[FCurrentXvtr].FreqEnd; + end + else if Assigned(FNetwork) then + begin + LoHz := FNetwork.Caps.MinFreqHz; + HiHz := FNetwork.Caps.MaxFreqHz; + end + else + begin + LoHz := 0; + HiHz := 61440000; + end; +end; + +procedure TRadioController.RefreshPanDDCs; +var + p, i: Integer; + LoV, HiV, LoHz, HiHz: Double; +begin + VisibleFreqBounds(LoV, HiV); + for p := 1 to MAX_PANS - 1 do + if PanDDCActive(p) then + begin + // Видимый центр пана — в новое допустимое окно, freq-слово DDC — через + // свежую трансляцию (вход/выход XVTR, правка LO/LOError). + FPanFreqHz[p] := EnsureRange(FPanFreqHz[p], LoV, HiV); + if Assigned(FNetwork) then + FNetwork.SetPanDDCFreq(FPanDDCIdx[p], XvtrTranslate(FPanFreqHz[p])); + // Слайсы держат абсолютный видимый TargetHz: выпавшие из полосы захвата + // пана — к её краям (SetSliceTarget обновит NCO), остальные — свежий NCO. + CapturedSpan(LoHz, HiHz, p); + for i := 0 to MAX_SLICES - 1 do + if FSlices[i].Active and (FSlices[i].PanId = p) then + begin + if FSlices[i].TargetHz < LoHz then SetSliceTarget(FSlices[i].Id, LoHz) + else if FSlices[i].TargetHz > HiHz then SetSliceTarget(FSlices[i].Id, HiHz) + else if Assigned(FDSPEngine) then + FDSPEngine.SetSliceShift(FSlices[i].Id, + FSlices[i].TargetHz - FPanFreqHz[p]); + end; + end; +end; + function TRadioController.PanDDCActive(PanId: Integer): Boolean; begin Result := (PanId >= 1) and (PanId < MAX_PANS) and (FPanDDCIdx[PanId] >= 0); @@ -1364,7 +1424,7 @@ begin if not PanDDCActive(PanId) then Exit; if FPanADC[PanId] = ADC then Exit(True); FPanADC[PanId] := ADC; - FNetwork.SetPanDDC(FPanDDCIdx[PanId], True, FPanFreqHz[PanId], + FNetwork.SetPanDDC(FPanDDCIdx[PanId], True, XvtrTranslate(FPanFreqHz[PanId]), FPanRateKHz[PanId], ADC); Result := True; end; @@ -3007,6 +3067,7 @@ begin // QO-100 / split-LO: авто display-duplex (мониторинг своего downlink при TX). if E.FullDuplexTx and not FDisplayDuplex then SetDuplex(True); PushNetworkState; + RefreshPanDDCs; // живые паны — в диапазон XVTR, DDC — на IF Changed(rfXvtr); Changed(rfActiveVfo); @@ -3023,6 +3084,7 @@ begin if FDevConnected then FSettings.Save; FCurrentXvtr := -1; ApplyXvtrToNetwork; // снять XVTR enable bit + RefreshPanDDCs; // живые паны — обратно в пределы железа if (FCurrentBand >= 0) and (FCurrentBand < CFG_BAND_COUNT) then RestoreBand(FCurrentBand); // HF-возврат (состояние/DSP/retune) Changed(rfXvtr); // погасить XVTR-кнопки + wideband/web (rfXvtr-рендер)