mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-26 04:47:35 +00:00
feat(slices): независимый rate панадаптеров (селектор в шапке)
- Создание пана: rate наследуется от главного с клампом ≤768к (вне сетки → 384к); за сменой rate главного паны не следуют - Клик по «NNN kHz» в шапке пана N → меню полной сетки железа (PanRateAllowed = Caps.RatePresets ∩ кратные 48 кГц; захардкоженный PAN_DDC_RATES удалён) - WDSPEngine.SetPanRate: под FSliceLock пересбор аккумулятора/анализатора пана + переоткрытие слайс-каналов на новом rate (state сохраняется) - RadioController.SetPanDDCRate: движок → реконфиг DDC (SetPanDDC) → кламп слайсов к новой полосе; FPanADC[] хранится (база под 3.4) - Шапка: rate отдельным лейблом (у панов N кликабелен, акцент/рука) Смоук-тест без железа ALL PASS (тон: спектр+аудио слайса живы через 384→192→768). Проверено юзером на экране. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+56
-11
@@ -52,11 +52,10 @@ uses
|
||||
WDSPEngine, AudioOutput, AudioInput, BeaconDecoder, BeaconFEC,
|
||||
Settings, ChannelStore, FMRepeater, BoardUtils, DeviceStore;
|
||||
|
||||
const
|
||||
// Панадаптеры (этап 3): потолок MAX_PANS живёт в WDSPEngine (общий для
|
||||
// движка/контроллера/UI). Фактический лимит бэкенда — Caps.MaxPans (Pluto=1).
|
||||
// Допустимые rate доп. пан-DDC, кГц (дефолт 384).
|
||||
PAN_DDC_RATES: array[0..2] of Word = (192, 384, 768);
|
||||
// Панадаптеры (этап 3): потолок MAX_PANS живёт в WDSPEngine (общий для
|
||||
// движка/контроллера/UI). Фактический лимит бэкенда — Caps.MaxPans (Pluto=1).
|
||||
// Допустимые rate пан-DDC — полная сетка железа Caps.RatePresets (кратные
|
||||
// 48 кГц — требование движка), см. PanRateAllowed. Дефолт/фолбэк — 384 кГц.
|
||||
|
||||
type
|
||||
// QO-100 beacon lock — статус контура (для UI/маркеров).
|
||||
@@ -353,6 +352,7 @@ type
|
||||
FPanDDCIdx: array[1..MAX_PANS-1] of Integer;
|
||||
FPanFreqHz: array[1..MAX_PANS-1] of Double;
|
||||
FPanRateKHz: array[1..MAX_PANS-1] of Word;
|
||||
FPanADC: array[1..MAX_PANS-1] of Byte; // ADC-источник пана (3.4)
|
||||
FDDCPanMap: array[0..MAX_DDCS-1] of Integer;
|
||||
FLastDDCSeq: LongWord; // последний seq (из сетевого потока)
|
||||
FLastDDCIndex: Integer; // последний DDC index
|
||||
@@ -562,12 +562,17 @@ type
|
||||
|
||||
// ---- Панадаптеры на аппаратных DDC (этап 3.1; UI/движок — 3.2/3.3) ----
|
||||
// Создаёт пан PanId (1..MAX_PANS-1) на своём DDC: FreqHz = центр (сырые Гц
|
||||
// железа, XVTR-трансляции у панов нет), RateKHz ∈ PAN_DDC_RATES (иначе 384),
|
||||
// железа, XVTR-трансляции у панов нет), 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-пакет)
|
||||
// Rate ∈ сетке бэкенда (Caps.RatePresets) и кратен 48 кГц (движок).
|
||||
function PanRateAllowed(RateKHz: Word): Boolean;
|
||||
// Смена rate живого пана: движок переоткрывает аккумулятор/анализатор/слайсы,
|
||||
// DDC реконфигурируется; слайсы вне новой полосы клампятся к её краям.
|
||||
function SetPanDDCRate(PanId: Integer; RateKHz: Word): Boolean;
|
||||
function PanDDCActive(PanId: Integer): Boolean;
|
||||
function PanDDCFreq(PanId: Integer): Double;
|
||||
function PanDDCRateKHz(PanId: Integer): Word;
|
||||
@@ -1178,16 +1183,28 @@ begin
|
||||
FPanDDCIdx[i] := -1;
|
||||
FPanFreqHz[i] := 0;
|
||||
FPanRateKHz[i] := 0;
|
||||
FPanADC[i] := 0;
|
||||
end;
|
||||
for i := 0 to MAX_DDCS - 1 do
|
||||
FDDCPanMap[i] := -1;
|
||||
end;
|
||||
|
||||
function TRadioController.PanRateAllowed(RateKHz: Word): Boolean;
|
||||
// Полная сетка железа (Caps.RatePresets) минус некратные 48 кГц: движок
|
||||
// требует целого отношения rate пана к аудио-rate (блок/WDSP-каналы).
|
||||
var i: Integer;
|
||||
begin
|
||||
Result := False;
|
||||
if not Assigned(FNetwork) then Exit;
|
||||
if (RateKHz = 0) or ((Integer(RateKHz) * 1000) mod 48000 <> 0) then Exit;
|
||||
for i := 0 to High(FNetwork.Caps.RatePresets) do
|
||||
if FNetwork.Caps.RatePresets[i] = Integer(RateKHz) * 1000 then Exit(True);
|
||||
end;
|
||||
|
||||
function TRadioController.AddPanDDC(PanId: Integer; FreqHz: Double;
|
||||
RateKHz: Word; ADCSource: Byte): Boolean;
|
||||
var
|
||||
i, ddc: Integer;
|
||||
RateOk: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
if (PanId < 1) or (PanId >= MAX_PANS) then Exit;
|
||||
@@ -1195,10 +1212,7 @@ begin
|
||||
if not (Assigned(FNetwork) and FRunning) then Exit;
|
||||
if PanId >= FNetwork.Caps.MaxPans then Exit; // бэкенд не умеет (Pluto=1)
|
||||
|
||||
RateOk := False;
|
||||
for i := Low(PAN_DDC_RATES) to High(PAN_DDC_RATES) do
|
||||
if PAN_DDC_RATES[i] = RateKHz then begin RateOk := True; Break; end;
|
||||
if not RateOk then RateKHz := 384;
|
||||
if not PanRateAllowed(RateKHz) then RateKHz := 384;
|
||||
|
||||
// hw-индекс: следующий свободный после главного DDC (слот-стабильности не
|
||||
// нужно — hw-индекс скрыт за PanId, наружу торчит только PanId).
|
||||
@@ -1215,11 +1229,41 @@ begin
|
||||
FPanDDCIdx[PanId] := ddc;
|
||||
FPanFreqHz[PanId] := FreqHz;
|
||||
FPanRateKHz[PanId] := RateKHz;
|
||||
FPanADC[PanId] := ADCSource;
|
||||
FDDCPanMap[ddc] := PanId;
|
||||
FNetwork.SetPanDDC(ddc, True, FreqHz, RateKHz, ADCSource);
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function TRadioController.SetPanDDCRate(PanId: Integer; RateKHz: Word): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
LoHz, HiHz: Double;
|
||||
begin
|
||||
Result := False;
|
||||
if not PanDDCActive(PanId) then Exit;
|
||||
if not PanRateAllowed(RateKHz) then Exit;
|
||||
if FPanRateKHz[PanId] = RateKHz then Exit(True);
|
||||
// Движок первым: под FSliceLock пересобирает аккумулятор/анализатор и
|
||||
// переоткрывает слайс-каналы; провалился — состояние не трогаем.
|
||||
if FWDSPReady and not FDSPEngine.SetPanRate(PanId, Integer(RateKHz) * 1000) then
|
||||
Exit;
|
||||
FPanRateKHz[PanId] := RateKHz;
|
||||
// DDC железа: реконфиг rate (RebuildDDCSpecific + freq-слово).
|
||||
if Assigned(FNetwork) then
|
||||
FNetwork.SetPanDDC(FPanDDCIdx[PanId], True, FPanFreqHz[PanId], RateKHz,
|
||||
FPanADC[PanId]);
|
||||
// Слайсы, выпавшие из сузившейся полосы, — к её краям (абс. TargetHz + NCO).
|
||||
CapturedSpan(LoHz, HiHz, PanId);
|
||||
for i := 0 to MAX_SLICES - 1 do
|
||||
if FSlices[i].Active and (FSlices[i].PanId = PanId) then
|
||||
begin
|
||||
if FSlices[i].TargetHz < LoHz then SetSliceTarget(FSlices[i].Id, LoHz);
|
||||
if FSlices[i].TargetHz > HiHz then SetSliceTarget(FSlices[i].Id, HiHz);
|
||||
end;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
procedure TRadioController.RemovePanDDC(PanId: Integer);
|
||||
var ddc, i: Integer;
|
||||
begin
|
||||
@@ -1232,6 +1276,7 @@ begin
|
||||
FPanDDCIdx[PanId] := -1;
|
||||
FPanFreqHz[PanId] := 0;
|
||||
FPanRateKHz[PanId] := 0;
|
||||
FPanADC[PanId] := 0;
|
||||
for i := 0 to MAX_SLICES - 1 do
|
||||
if FSlices[i].Active and (FSlices[i].PanId = PanId) then
|
||||
RemoveSlice(FSlices[i].Id);
|
||||
|
||||
Reference in New Issue
Block a user