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 @@
+
+
+
+