From f33a1fbd0a7380f21357546f5f6f288c2b3372b9 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 14 Jul 2026 12:52:31 +0300 Subject: [PATCH] =?UTF-8?q?feat(pans):=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20?= =?UTF-8?q?=D0=B0=D0=BF=D0=BF=D0=B0=D1=80=D0=B0=D1=82=D0=BD=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20DDC=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=B0=D0=BD=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D0=BF=D1=82=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Клик по «NNN kHz · DDCn» в шапке пана — кроме сетки rate теперь есть пункты «→ DDC N»: пан переезжает на другой аппаратный DDC (MovePanDDC), при этом движок, анализатор и слайсы не пересоздаются — перепрошивается только hw-индекс. Пригодилось при отладке: конкретный DDC платы может вести себя хуже соседей, и пан можно просто увести на другой. Диапазон ограничен реальными DDC железа: MaxDDCIndex = 7 (DDC8+ openHPSDR enable-бит принимает, но поток не отдаёт). Ограничение действует и в меню, и в DDCFree, и в самом MovePanDDC. Co-Authored-By: Claude Opus 4.8 --- MainForm.pas | 20 +++++++++++++++-- RadioController.pas | 54 +++++++++++++++++++++++++++++++++++++++++++++ ewsdr.lpi | 8 +++++-- 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/MainForm.pas b/MainForm.pas index a895145..428e99a 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -47,6 +47,9 @@ uses RadioController, ChannelController; const + // Меню шапки пана: теги ниже — rate в кГц, от DDC_MENU_TAG — выбор hw-DDC. + DDC_MENU_TAG = 1000; + CLR_BG = TColor($00101010); CLR_PANEL = TColor($00181818); CLR_BORDER = TColor($00303030); @@ -5626,7 +5629,7 @@ procedure TMainForm.OnPanRateClick(Sender: TObject); // (фильтр PanRateAllowed — кратные 48 кГц). Текущий rate отмечен галкой. var P: TPanafallPanel; - i, kHz, Cur: Integer; + i, kHz, Cur, CurDDC: Integer; begin if not (Sender is TPanafallPanel) then Exit; P := TPanafallPanel(Sender); @@ -5645,6 +5648,11 @@ begin if not FController.PanRateAllowed(Word(kHz)) then Continue; FPanRateMenu.AddItem(Format('%d kHz', [kHz]), kHz, kHz = Cur); end; + // Аппаратный DDC пана: свободные индексы платы + текущий (отмечен галкой). + CurDDC := FController.PanDDCIndex(P.PanId); + for i := FController.PanDDCIndex(0) + 1 to FController.MaxDDCIndex do + if (i = CurDDC) or FController.DDCFree(i) then + FPanRateMenu.AddItem(Format('→ DDC %d', [i]), DDC_MENU_TAG + i, i = CurDDC); FPanRateMenuPanId := P.PanId; FPanRateMenu.PopupBelow(P.HeaderRateLabel); // дочерний контрол формы end; @@ -5659,6 +5667,12 @@ begin if (PanId < 1) or (PanId >= MAX_PANS) then Exit; P := FPans[PanId]; if P = nil then Exit; + // Теги DDC_MENU_TAG+ — переезд пана на другой hw DDC (движок не трогаем). + if Tag >= DDC_MENU_TAG then + begin + if FController.MovePanDDC(PanId, Tag - DDC_MENU_TAG) then UpdatePanHeaders; + Exit; + end; if not FController.SetPanDDCRate(PanId, Word(Tag)) then Exit; // Вьюха/шапка/флаги под новый span (слайсы могли клампнуться к краям). SyncPanViews; @@ -6098,7 +6112,9 @@ begin begin P.SetHeaderText(Format('PAN %d · %.6f MHz ·', [i, FController.PanDDCFreq(i) / 1e6])); - P.SetHeaderRate(Format('%d kHz', [FController.PanDDCRateKHz(i)])); + // hw-индекс DDC в шапке: по клику там же выбирается rate и DDC пана. + P.SetHeaderRate(Format('%d kHz · DDC%d', + [FController.PanDDCRateKHz(i), FController.PanDDCIndex(i)])); end; // Бейдж диапазона (3.6): только паны N на «настоящих КВ»; клик = меню. if (i > 0) and PanBandSelectorVisible then diff --git a/RadioController.pas b/RadioController.pas index d38b01e..6101ed2 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -596,6 +596,13 @@ type // Смена rate живого пана: движок переоткрывает аккумулятор/анализатор/слайсы, // DDC реконфигурируется; слайсы вне новой полосы клампятся к её краям. function SetPanDDCRate(PanId: Integer; RateKHz: Word): Boolean; + // Переезд пана на другой аппаратный DDC (пан/слайсы/движок не пересоздаются, + // меняется только hw-индекс): бывает, что конкретный DDC платы ведёт себя + // хуже соседей — тогда пан можно просто увести на другой. + function MovePanDDC(PanId, NewDDC: Integer): Boolean; + function PanDDCIndex(PanId: Integer): Integer; // hw-индекс DDC пана + function MaxDDCIndex: Integer; // последний DDC железа + function DDCFree(DDCIdx: Integer): Boolean; // индекс свободен? function PanDDCActive(PanId: Integer): Boolean; function PanDDCFreq(PanId: Integer): Double; function PanDDCRateKHz(PanId: Integer): Word; @@ -1311,6 +1318,53 @@ begin Result := True; end; +function TRadioController.MovePanDDC(PanId, NewDDC: Integer): Boolean; +// Тот же пан (движок/анализатор/слайсы не трогаем) — только другой аппаратный +// DDC: старый гасим, новый включаем с теми же freq/rate/ADC. Короткий разрыв IQ +// на время реконфига — норма. +begin + Result := False; + if not PanDDCActive(PanId) then Exit; + if not Assigned(FNetwork) then Exit; + if (NewDDC <= FActiveDDC) or (NewDDC > MaxDDCIndex) then Exit; + if FPanDDCIdx[PanId] = NewDDC then Exit(True); + if FDDCPanMap[NewDDC] >= 0 then Exit; // занят другим паном + + FNetwork.SetPanDDC(FPanDDCIdx[PanId], False, 0, 0, 0); + FDDCPanMap[FPanDDCIdx[PanId]] := -1; + // Seq-трекинг обоих индексов начинаем заново (новый поток — новый счётчик). + if FPanDDCIdx[PanId] <= High(FDDCSeqValid) then + FDDCSeqValid[FPanDDCIdx[PanId]] := False; + if NewDDC <= High(FDDCSeqValid) then + FDDCSeqValid[NewDDC] := False; + + FPanDDCIdx[PanId] := NewDDC; + FDDCPanMap[NewDDC] := PanId; + FNetwork.SetPanDDC(NewDDC, True, XvtrTranslate(FPanFreqHz[PanId]), + FPanRateKHz[PanId], FPanADC[PanId]); + Result := True; +end; + +function TRadioController.PanDDCIndex(PanId: Integer): Integer; +begin + if (PanId >= 1) and (PanId < MAX_PANS) then Result := FPanDDCIdx[PanId] + else if PanId = 0 then Result := FActiveDDC + else Result := -1; +end; + +function TRadioController.MaxDDCIndex: Integer; +// Железо openHPSDR (Orion/Saturn) поднимает DDC0..DDC7 — DDC8 и выше молчат +// (проверено на плате: enable-бит ставится, поток не идёт). +begin + Result := 7; +end; + +function TRadioController.DDCFree(DDCIdx: Integer): Boolean; +begin + Result := (DDCIdx > FActiveDDC) and (DDCIdx <= MaxDDCIndex) + and (FDDCPanMap[DDCIdx] < 0); +end; + procedure TRadioController.RemovePanDDC(PanId: Integer); var ddc, i: Integer; begin diff --git a/ewsdr.lpi b/ewsdr.lpi index 5dffa0b..36ed465 100644 --- a/ewsdr.lpi +++ b/ewsdr.lpi @@ -17,9 +17,9 @@ - + - + @@ -111,6 +111,10 @@ + + + +