mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
feat(slices): этап 3.1 — per-DDC бэкенд HPSDR (пан-DDC) + фикс Saturn в StartRunning
- TBackendCaps: MaxPans/IndependentPanFreq (HPSDR 4/True, Pluto 1/False); SetPanDDC/SetPanDDCFreq — виртуальные no-op в TRadioBackend - HPSDRNetwork: ConfigureDDCs → кэш главного DDC + RebuildDDCSpecific (главный + активные пан-DDC, каждый со своим rate/ADC); SendFullHP пишет частотные слова всех активных пан-DDC; хелпер DDCBaseIndex; сброс пан-состояния в Connect - RadioController: MAX_PANS=4, PAN_DDC_RATES 192/384/768; AddPanDDC/ RemovePanDDC/SetPanDDCFreq/PanDDC* (аллокатор hw-индексов от FActiveDDC+1, обратный маппинг FDDCPanMap); OnDDCIQ роутит пан-DDC → PushPanIQ; паны гасятся в StopRunning, пул сбрасывается в StartRunning - WDSPEngine: PushPanIQ — заглушка-дроп (аккумулятор/анализатор — этап 3.2) - фикс: StartRunning не включал Saturn (BoardType 10) в список плат с DDCBase=2 — на Saturn гейт FActiveDDC ждал бы IQ с DDC0 - doc/SLICES_PLAN.md: статус 3.1 + B1 проверена на экране Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+117
-29
@@ -140,6 +140,22 @@ type
|
||||
FCachedDUCValid: Boolean;
|
||||
FResendCount: Integer;
|
||||
|
||||
// Панадаптеры на доп. аппаратных DDC (этап 3): состояние per-DDC.
|
||||
// Пишется из потока контроллера (SetPanDDC*), читается в SendFullHP /
|
||||
// RebuildDDCSpecific — та же дисциплина, что у FCurrentRXFreq (torn read
|
||||
// одного HP-кадра чинится следующим же кадром).
|
||||
FPanDDCEnabled: array[0..MAX_DDCS-1] of Boolean;
|
||||
FPanDDCFreqHz: array[0..MAX_DDCS-1] of Double;
|
||||
FPanDDCRateKHz: array[0..MAX_DDCS-1] of Word;
|
||||
FPanDDCADC: array[0..MAX_DDCS-1] of Byte;
|
||||
// Кэш параметров главного DDC для RebuildDDCSpecific (пере-сборка DDC
|
||||
// Specific при добавлении/удалении пан-DDC без повторного знания rate).
|
||||
FMainNumDDCs: Byte;
|
||||
FMainRateKHz: Word;
|
||||
FMainADC: Byte;
|
||||
FMainDither: Boolean;
|
||||
FMainRandom: Boolean;
|
||||
|
||||
// Текущее состояние для построения HP пакетов
|
||||
FCurrentRXFreq: Double;
|
||||
FCurrentTXFreq: Double;
|
||||
@@ -192,6 +208,11 @@ type
|
||||
function DequeueDUCIQ(var Pkt: TDUCIQPacket): Boolean;
|
||||
function NextSeq(var S: LongWord): LongWord;
|
||||
procedure PackSeqBytes(var B: array of Byte; Seq: LongWord);
|
||||
// Первый пользовательский DDC платы: 2 на ANGELIA/ORION/ORION2/SATURN
|
||||
// (DDC0/1 зарезервированы PureSignal), иначе 0.
|
||||
function DDCBaseIndex: Integer;
|
||||
// Сборка+отправка DDC Specific из кэша главного DDC + активных пан-DDC.
|
||||
procedure RebuildDDCSpecific;
|
||||
|
||||
protected
|
||||
// Полиморфные геттеры состояния (свойства Connected/Running/Device/LastError/
|
||||
@@ -219,6 +240,9 @@ type
|
||||
procedure SendDDCSpecific(const Pkt: TDDCSpecificPacket); override;
|
||||
procedure ConfigureDDCs(NumDDCs: Byte; SampleRate: Word; ADCSource: Byte = 0;
|
||||
DitherEnabled: Boolean = True; RandomEnabled: Boolean = True); override;
|
||||
procedure SetPanDDC(DDCIdx: Integer; Enabled: Boolean; FreqHz: Double;
|
||||
RateKHz: Word; ADCSource: Byte); override;
|
||||
procedure SetPanDDCFreq(DDCIdx: Integer; FreqHz: Double); override;
|
||||
procedure SendDUCSpecific(const Pkt: TDUCSpecificPacket); override;
|
||||
procedure SendHighPriority(const Pkt: THighPriorityPacket); override;
|
||||
procedure SetRunAndFreq(Run: Boolean; DDC0FreqHz, DUCFreqHz: Double;
|
||||
@@ -665,6 +689,10 @@ begin
|
||||
Result.RatePresets[5] := 1536000;
|
||||
Result.MinFreqHz := 0;
|
||||
Result.MaxFreqHz := 61440000;
|
||||
// Панадаптеры на аппаратных DDC: главный + до 3 доп. (кап по Ethernet/CPU,
|
||||
// см. doc/SLICES_PLAN.md 3.1). Каждый DDC тюнится независимо.
|
||||
Result.MaxPans := 4;
|
||||
Result.IndependentPanFreq := True;
|
||||
end;
|
||||
|
||||
constructor THPSDRNetwork.Create;
|
||||
@@ -1019,6 +1047,9 @@ begin
|
||||
FSeqHP := 0;
|
||||
FSeqAudio := 0;
|
||||
FSeqDUCIQ := 0;
|
||||
// Пан-DDC не переживают сессию (контроллер гасит их в StopRunning; здесь —
|
||||
// страховка от грязного завершения прошлой сессии).
|
||||
FillChar(FPanDDCEnabled, SizeOf(FPanDDCEnabled), 0);
|
||||
ResetDUCIQQueue;
|
||||
|
||||
StartThreads;
|
||||
@@ -1457,13 +1488,52 @@ begin
|
||||
if FConnected then SendFullHP;
|
||||
end;
|
||||
|
||||
function THPSDRNetwork.DDCBaseIndex: Integer;
|
||||
begin
|
||||
// Для ANGELIA/ORION/ORION2/SATURN DDC начинается с индекса 2 (DDC0/1 — PureSignal)
|
||||
// Для HERMES/HL2 — с 0
|
||||
if FDevice.BoardType in [3, 4, 5, 10] then // ANGELIA=3, ORION=4, ORION2=5, SATURN=10
|
||||
Result := 2
|
||||
else
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
procedure THPSDRNetwork.ConfigureDDCs(NumDDCs: Byte; SampleRate: Word;
|
||||
ADCSource: Byte;
|
||||
DitherEnabled: Boolean; RandomEnabled: Boolean);
|
||||
begin
|
||||
// Кэш главного DDC — RebuildDDCSpecific пере-собирает пакет и при
|
||||
// добавлении/удалении пан-DDC (SetPanDDC) без повторной передачи rate.
|
||||
FMainNumDDCs := NumDDCs;
|
||||
FMainRateKHz := SampleRate;
|
||||
FMainADC := ADCSource;
|
||||
FMainDither := DitherEnabled;
|
||||
FMainRandom := RandomEnabled;
|
||||
RebuildDDCSpecific;
|
||||
end;
|
||||
|
||||
procedure THPSDRNetwork.RebuildDDCSpecific;
|
||||
var
|
||||
Pkt: TDDCSpecificPacket;
|
||||
i, ddc: Integer;
|
||||
DDCBase: Integer;
|
||||
|
||||
procedure AddDDC(ddc: Integer; RateKHz: Word; ADC: Byte);
|
||||
begin
|
||||
// Enable bit для DDC ddc
|
||||
Pkt.DDCEnable[ddc div 8] := Pkt.DDCEnable[ddc div 8] or Byte(1 shl (ddc mod 8));
|
||||
// Config: 6 байт на DDC начиная с offset 17 в пакете → в массиве DDCConfig
|
||||
// DDCConfig[ddc*6 + 0] = ADC source
|
||||
// DDCConfig[ddc*6 + 1..2] = sample rate / 1000 (ksps)
|
||||
// DDCConfig[ddc*6 + 5] = bits per sample
|
||||
Pkt.DDCConfig[ddc * 6] := ADC;
|
||||
Pkt.DDCConfig[ddc * 6 + 1] := (RateKHz shr 8) and $FF;
|
||||
Pkt.DDCConfig[ddc * 6 + 2] := RateKHz and $FF;
|
||||
Pkt.DDCConfig[ddc * 6 + 3] := 0;
|
||||
Pkt.DDCConfig[ddc * 6 + 4] := 0;
|
||||
Pkt.DDCConfig[ddc * 6 + 5] := 24; // 24 bits per sample
|
||||
end;
|
||||
|
||||
begin
|
||||
FillChar(Pkt, SizeOf(Pkt), 0);
|
||||
|
||||
@@ -1474,34 +1544,21 @@ begin
|
||||
Pkt.NumADCs := 1;
|
||||
|
||||
// Dither/Random — биты 0..NumADCs-1 (один бит на ADC)
|
||||
if DitherEnabled then
|
||||
if FMainDither then
|
||||
Pkt.DitherADC := (1 shl Pkt.NumADCs) - 1;
|
||||
if RandomEnabled then
|
||||
if FMainRandom then
|
||||
Pkt.RandomADC := (1 shl Pkt.NumADCs) - 1;
|
||||
|
||||
// Для ANGELIA/ORION/ORION2/SATURN DDC начинается с индекса 2 (DDC0/1 — PureSignal)
|
||||
// Для HERMES/HL2 — с 0
|
||||
if FDevice.BoardType in [3, 4, 5, 10] then // ANGELIA=3, ORION=4, ORION2=5, SATURN=10
|
||||
DDCBase := 2
|
||||
else
|
||||
DDCBase := 0;
|
||||
DDCBase := DDCBaseIndex;
|
||||
|
||||
for i := 0 to NumDDCs - 1 do
|
||||
begin
|
||||
ddc := DDCBase + i;
|
||||
// Enable bit для DDC ddc
|
||||
Pkt.DDCEnable[ddc div 8] := Pkt.DDCEnable[ddc div 8] or Byte(1 shl (ddc mod 8));
|
||||
// Config: 6 байт на DDC начиная с offset 17 в пакете → в массиве DDCConfig
|
||||
// DDCConfig[ddc*6 + 0] = ADC source
|
||||
// DDCConfig[ddc*6 + 1..2] = sample rate / 1000 (ksps)
|
||||
// DDCConfig[ddc*6 + 5] = bits per sample
|
||||
Pkt.DDCConfig[ddc * 6] := ADCSource;
|
||||
Pkt.DDCConfig[ddc * 6 + 1] := (SampleRate shr 8) and $FF;
|
||||
Pkt.DDCConfig[ddc * 6 + 2] := SampleRate and $FF;
|
||||
Pkt.DDCConfig[ddc * 6 + 3] := 0;
|
||||
Pkt.DDCConfig[ddc * 6 + 4] := 0;
|
||||
Pkt.DDCConfig[ddc * 6 + 5] := 24; // 24 bits per sample
|
||||
end;
|
||||
for i := 0 to FMainNumDDCs - 1 do
|
||||
AddDDC(DDCBase + i, FMainRateKHz, FMainADC);
|
||||
|
||||
// Пан-DDC (этап 3): доп. панадаптеры на своих rate/ADC. Контроллер
|
||||
// аллоцирует hw-индексы после главного, конфликтов с циклом выше нет.
|
||||
for ddc := 0 to MAX_DDCS - 1 do
|
||||
if FPanDDCEnabled[ddc] then
|
||||
AddDDC(ddc, FPanDDCRateKHz[ddc], FPanDDCADC[ddc]);
|
||||
|
||||
SendDDCSpecific(Pkt);
|
||||
FCachedDDCSpec := Pkt;
|
||||
@@ -1509,6 +1566,28 @@ begin
|
||||
FResendCount := 0;
|
||||
end;
|
||||
|
||||
procedure THPSDRNetwork.SetPanDDC(DDCIdx: Integer; Enabled: Boolean;
|
||||
FreqHz: Double; RateKHz: Word; ADCSource: Byte);
|
||||
begin
|
||||
if (DDCIdx < 0) or (DDCIdx >= MAX_DDCS) then Exit;
|
||||
FPanDDCEnabled[DDCIdx] := Enabled;
|
||||
FPanDDCFreqHz[DDCIdx] := FreqHz;
|
||||
FPanDDCRateKHz[DDCIdx] := RateKHz;
|
||||
FPanDDCADC[DDCIdx] := ADCSource;
|
||||
if FConnected and FRunning then
|
||||
begin
|
||||
RebuildDDCSpecific; // enable-бит + rate/ADC
|
||||
SendFullHP; // частотное слово DDC
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure THPSDRNetwork.SetPanDDCFreq(DDCIdx: Integer; FreqHz: Double);
|
||||
begin
|
||||
if (DDCIdx < 0) or (DDCIdx >= MAX_DDCS) then Exit;
|
||||
FPanDDCFreqHz[DDCIdx] := FreqHz;
|
||||
if FConnected and FPanDDCEnabled[DDCIdx] then SendFullHP;
|
||||
end;
|
||||
|
||||
procedure THPSDRNetwork.SendDUCSpecific(const Pkt: TDUCSpecificPacket);
|
||||
var
|
||||
B: TDUCSpecificPacket;
|
||||
@@ -1557,6 +1636,7 @@ var
|
||||
Alex1: LongWord;
|
||||
IsOrion2: Boolean;
|
||||
DDCBase: Integer; // 0 для HERMES, 2 для ORION/ORION2/ANGELIA
|
||||
ddc: Integer;
|
||||
begin
|
||||
if not FConnected then Exit;
|
||||
|
||||
@@ -1580,13 +1660,9 @@ begin
|
||||
if FPTTActive then Buf[4] := Buf[4] or HP_PTT0;
|
||||
end;
|
||||
|
||||
// DDC base: ORION/ORION2/ANGELIA/SATURN начинают с DDC2
|
||||
// IsOrion2: платы с Alex BPF и TxRx Status bit18 (ORION_MK2=5, SATURN=10)
|
||||
IsOrion2 := FDevice.BoardType in [5, 10]; // ORION_MK2=5, SATURN=10
|
||||
if FDevice.BoardType in [3, 4, 5, 10] then // ANGELIA=3, ORION=4, ORION_MK2=5, SATURN=10
|
||||
DDCBase := 2
|
||||
else
|
||||
DDCBase := 0;
|
||||
DDCBase := DDCBaseIndex;
|
||||
|
||||
// DDC RX frequency (bytes 9 + DDC*4)
|
||||
Ph := FreqToPhaseWord(FCurrentRXFreq);
|
||||
@@ -1595,6 +1671,18 @@ begin
|
||||
Buf[11 + DDCBase*4] := (Ph shr 8) and $FF;
|
||||
Buf[12 + DDCBase*4] := Ph and $FF;
|
||||
|
||||
// Частоты пан-DDC (этап 3): своё частотное слово каждому активному DDC.
|
||||
// Макс. смещение 9+79*4=325 < 329 (DUC) — в частотную зону влезают все.
|
||||
for ddc := 0 to MAX_DDCS - 1 do
|
||||
if FPanDDCEnabled[ddc] then
|
||||
begin
|
||||
Ph := FreqToPhaseWord(FPanDDCFreqHz[ddc]);
|
||||
Buf[9 + ddc*4] := (Ph shr 24) and $FF;
|
||||
Buf[10 + ddc*4] := (Ph shr 16) and $FF;
|
||||
Buf[11 + ddc*4] := (Ph shr 8) and $FF;
|
||||
Buf[12 + ddc*4] := Ph and $FF;
|
||||
end;
|
||||
|
||||
// DUC TX frequency (bytes 329-332)
|
||||
Ph := FreqToPhaseWord(FCurrentTXFreq);
|
||||
Buf[329] := (Ph shr 24) and $FF;
|
||||
|
||||
Reference in New Issue
Block a user