mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
Phase 5 (batch 24): move OnDDCIQ + seq telemetry into the controller
The DDC IQ receive handler (sequence-error tracking + feeding the active DDC into WDSP) moves verbatim into TRadioController.OnDDCIQ, along with the 12 RX/seq telemetry fields it owns (FRXPacketCount, FRXStartTime, FRXLastPktTime, FActiveDDC, FLastDDCSeq, FLastDDCIndex, FDDCLastSeq, FDDCSeqValid, FSeqErrorCount, FLastSeqErrorDDC, FLastSeqErrorDelta, FSeqOkStreak). FNetwork.OnDDCIQ is wired to the controller method; all MainForm readers (meter timer RX-running/stall detection, status-bar SEQ text, web seq text) and the START/STOP/connect resets now go through FController. Behaviour unchanged; backend-agnostic (a future Pluto backend synthesises the same TDDCIQPacket). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3d2d76452c
commit
239ff54942
+36
-108
@@ -149,18 +149,7 @@ type
|
||||
FSMeterPeak: Double; // верхняя граница светлой зоны
|
||||
FSMeterMin: Double; // нижняя граница светлой зоны
|
||||
FSMeterAvg: Double; // сглаженное среднее (EMA)
|
||||
FRXPacketCount: LongWord; // счётчик принятых IQ пакетов
|
||||
FRXStartTime: QWord; // GetTickCount64 момента нажатия START
|
||||
FRXLastPktTime: QWord; // GetTickCount64 последнего принятого пакета
|
||||
FActiveDDC: Integer; // DDC index для текущей платы (0 или 2)
|
||||
FLastDDCSeq: LongWord; // последний seq (пишется из сетевого потока)
|
||||
FLastDDCIndex: Integer; // последний DDC index
|
||||
FDDCLastSeq: array[0..6] of LongWord;
|
||||
FDDCSeqValid: array[0..6] of Boolean;
|
||||
FSeqErrorCount: LongWord;
|
||||
FLastSeqErrorDDC: Integer;
|
||||
FLastSeqErrorDelta: Int64;
|
||||
FSeqOkStreak: Integer;
|
||||
// RX/seq телеметрия переехала в TRadioController (пишется из OnDDCIQ).
|
||||
FLastFwdW: Double;
|
||||
FLastSWR: Double;
|
||||
// Эти значения приходят с HP Status пакетами (50..200 раз/с). UI обновляется
|
||||
@@ -370,7 +359,6 @@ type
|
||||
// Network callbacks
|
||||
procedure OnDeviceFound(const Dev: THPSDRDevice);
|
||||
procedure OnHPStatusCB(const Status: THighPriorityStatus);
|
||||
procedure OnDDCIQCB(DDCIndex: Integer; const Data: TDDCIQPacket);
|
||||
procedure OnWidebandCB(ADCIndex: Integer; const Samples: array of SmallInt;
|
||||
Count: Integer);
|
||||
procedure OnWDSPOpenDone(Success: Boolean);
|
||||
@@ -1068,16 +1056,16 @@ begin
|
||||
FSMeterPeak := -130;
|
||||
FSMeterMin := -130;
|
||||
FSMeterAvg := -130;
|
||||
FRXPacketCount := 0;
|
||||
FActiveDDC := 0;
|
||||
FLastDDCSeq := 0;
|
||||
FLastDDCIndex := 0;
|
||||
FillChar(FDDCLastSeq, SizeOf(FDDCLastSeq), 0);
|
||||
FillChar(FDDCSeqValid, SizeOf(FDDCSeqValid), 0);
|
||||
FSeqErrorCount := 0;
|
||||
FLastSeqErrorDDC := -1;
|
||||
FLastSeqErrorDelta:= 0;
|
||||
FSeqOkStreak := 0;
|
||||
FController.FRXPacketCount := 0;
|
||||
FController.FActiveDDC := 0;
|
||||
FController.FLastDDCSeq := 0;
|
||||
FController.FLastDDCIndex := 0;
|
||||
FillChar(FController.FDDCLastSeq, SizeOf(FController.FDDCLastSeq), 0);
|
||||
FillChar(FController.FDDCSeqValid, SizeOf(FController.FDDCSeqValid), 0);
|
||||
FController.FSeqErrorCount := 0;
|
||||
FController.FLastSeqErrorDDC := -1;
|
||||
FController.FLastSeqErrorDelta:= 0;
|
||||
FController.FSeqOkStreak := 0;
|
||||
FLastFwdW := 0;
|
||||
FLastSWR := 1;
|
||||
FLastSupplyV:= -1;
|
||||
@@ -1155,7 +1143,7 @@ begin
|
||||
FController.CreateEngines(FController.FSampleRate);
|
||||
FController.FNetwork.OnDeviceFound := OnDeviceFound;
|
||||
FController.FNetwork.OnHPStatus := OnHPStatusCB;
|
||||
FController.FNetwork.OnDDCIQ := OnDDCIQCB;
|
||||
FController.FNetwork.OnDDCIQ := FController.OnDDCIQ;
|
||||
FController.FNetwork.OnMicPacket := FController.OnMicPacket;
|
||||
FController.FNetwork.OnWideband := OnWidebandCB;
|
||||
// Дефолты TX, Alex и XVTR (на случай если устройство ещё не выбрано).
|
||||
@@ -2699,11 +2687,11 @@ begin
|
||||
FSpecView.ADCOverloadVisible := False;
|
||||
if PbSpectrum <> nil then
|
||||
PbSpectrum.Invalidate;
|
||||
FillChar(FDDCSeqValid, SizeOf(FDDCSeqValid), 0);
|
||||
FSeqErrorCount := 0;
|
||||
FLastSeqErrorDDC := -1;
|
||||
FLastSeqErrorDelta := 0;
|
||||
FSeqOkStreak := 0;
|
||||
FillChar(FController.FDDCSeqValid, SizeOf(FController.FDDCSeqValid), 0);
|
||||
FController.FSeqErrorCount := 0;
|
||||
FController.FLastSeqErrorDDC := -1;
|
||||
FController.FLastSeqErrorDelta := 0;
|
||||
FController.FSeqOkStreak := 0;
|
||||
if ClearDevice then
|
||||
begin
|
||||
SetStatusText(0, 'Board --');
|
||||
@@ -3054,9 +3042,9 @@ begin
|
||||
if FController.FRunning and FController.FNetwork.Running then
|
||||
begin
|
||||
NowTick := GetTickCount64;
|
||||
ElapsedTick := NowTick - FRXStartTime;
|
||||
ElapsedTick := NowTick - FController.FRXStartTime;
|
||||
// Ждём первый пакет 5 секунд после старта
|
||||
if (FRXLastPktTime = 0) and (ElapsedTick > 5000) then
|
||||
if (FController.FRXLastPktTime = 0) and (ElapsedTick > 5000) then
|
||||
begin
|
||||
BtnStartStop.Caption := 'START';
|
||||
StyleButton(BtnStartStop, False);
|
||||
@@ -3068,7 +3056,7 @@ begin
|
||||
Exit;
|
||||
end;
|
||||
// После первого пакета — следим чтобы поток не прерывался более 3 сек
|
||||
if (FRXLastPktTime > 0) and ((NowTick - FRXLastPktTime) > 3000) then
|
||||
if (FController.FRXLastPktTime > 0) and ((NowTick - FController.FRXLastPktTime) > 3000) then
|
||||
begin
|
||||
BtnStartStop.Caption := 'START';
|
||||
StyleButton(BtnStartStop, False);
|
||||
@@ -3085,8 +3073,8 @@ begin
|
||||
begin
|
||||
// Обновляем статус из таймера — без Synchronize в сетевом потоке.
|
||||
SetStatusText(4, 'RX running');
|
||||
if FSeqErrorCount > 0 then
|
||||
SetStatusText(6, Format('SEQ ERR DDC%d', [FLastSeqErrorDDC]))
|
||||
if FController.FSeqErrorCount > 0 then
|
||||
SetStatusText(6, Format('SEQ ERR DDC%d', [FController.FLastSeqErrorDDC]))
|
||||
else
|
||||
SetStatusText(6, 'SEQ OK');
|
||||
|
||||
@@ -3229,8 +3217,8 @@ begin
|
||||
else
|
||||
WebTXText := 'TX idle';
|
||||
|
||||
if FSeqErrorCount > 0 then
|
||||
WebSeqText := Format('SEQ ERR DDC%d', [FLastSeqErrorDDC])
|
||||
if FController.FSeqErrorCount > 0 then
|
||||
WebSeqText := Format('SEQ ERR DDC%d', [FController.FLastSeqErrorDDC])
|
||||
else if FController.FRunning then
|
||||
WebSeqText := 'SEQ OK'
|
||||
else
|
||||
@@ -3490,66 +3478,6 @@ begin
|
||||
SetStatusText(3, 'Supply --');
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnDDCIQCB(DDCIndex: Integer; const Data: TDDCIQPacket);
|
||||
var
|
||||
SamplesPerFrame: Integer;
|
||||
Seq, ExpectedSeq: LongWord;
|
||||
Delta: Int64;
|
||||
begin
|
||||
// Вызывается из сетевого потока — UI не трогаем напрямую
|
||||
|
||||
// Счётчик принятых пакетов
|
||||
Inc(FRXPacketCount);
|
||||
FRXLastPktTime := GetTickCount64; // фиксируем время последнего пакета
|
||||
Seq := (LongWord(Data.Seq[0]) shl 24) or (LongWord(Data.Seq[1]) shl 16)
|
||||
or (LongWord(Data.Seq[2]) shl 8) or LongWord(Data.Seq[3]);
|
||||
|
||||
if (DDCIndex >= Low(FDDCLastSeq)) and (DDCIndex <= High(FDDCLastSeq)) then
|
||||
begin
|
||||
if Seq <> 0 then
|
||||
begin
|
||||
ExpectedSeq := FDDCLastSeq[DDCIndex] + 1;
|
||||
if FDDCSeqValid[DDCIndex] and (Seq <> ExpectedSeq) then
|
||||
begin
|
||||
Inc(FSeqErrorCount);
|
||||
FLastSeqErrorDDC := DDCIndex;
|
||||
Delta := Int64(Seq) - Int64(ExpectedSeq);
|
||||
FLastSeqErrorDelta := Delta;
|
||||
FSeqOkStreak := 0;
|
||||
end
|
||||
else if FDDCSeqValid[DDCIndex] then
|
||||
begin
|
||||
if FSeqErrorCount > 0 then
|
||||
begin
|
||||
Inc(FSeqOkStreak);
|
||||
if FSeqOkStreak >= 20 then
|
||||
begin
|
||||
FSeqErrorCount := 0;
|
||||
FLastSeqErrorDDC := -1;
|
||||
FLastSeqErrorDelta := 0;
|
||||
FSeqOkStreak := 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
FDDCLastSeq[DDCIndex] := Seq;
|
||||
FDDCSeqValid[DDCIndex] := True;
|
||||
end;
|
||||
|
||||
// 1. Подаём IQ данные в DSP — только с активного DDC
|
||||
if (DDCIndex = FActiveDDC) and FController.FWDSPReady then
|
||||
begin
|
||||
SamplesPerFrame := (Integer(Data.SamplesPerFrame[0]) shl 8) or
|
||||
Integer(Data.SamplesPerFrame[1]);
|
||||
if SamplesPerFrame <= 0 then SamplesPerFrame := 238; // fallback
|
||||
FController.FDSPEngine.PushDDCPacket(Data.IQData, 0, SamplesPerFrame);
|
||||
end;
|
||||
|
||||
// 2. Запоминаем последний seq — статус обновит таймер (без Synchronize в горячем пути)
|
||||
FLastDDCSeq := Seq;
|
||||
FLastDDCIndex := DDCIndex;
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnWidebandCB(ADCIndex: Integer;
|
||||
const Samples: array of SmallInt; Count: Integer);
|
||||
begin
|
||||
@@ -3888,8 +3816,8 @@ begin
|
||||
if FController.FWDSPReady then FController.FDSPEngine.SetTXRun(False);
|
||||
StyleButton(BtnMOX, False);
|
||||
end;
|
||||
FRXPacketCount := 0;
|
||||
FActiveDDC := 0;
|
||||
FController.FRXPacketCount := 0;
|
||||
FController.FActiveDDC := 0;
|
||||
// Сохраняем текущую частоту/настройки чтобы следующий START восстановил их
|
||||
if FController.FDevConnected then
|
||||
begin
|
||||
@@ -4187,9 +4115,9 @@ begin
|
||||
FController.FNetwork.ConfigureWideband(0, FShowWideband, 512, 16, 70, 32);
|
||||
|
||||
if FController.FNetwork.Device.BoardType in [3, 4, 5] then
|
||||
FActiveDDC := 2
|
||||
FController.FActiveDDC := 2
|
||||
else
|
||||
FActiveDDC := 0;
|
||||
FController.FActiveDDC := 0;
|
||||
|
||||
// Синхронизируем samplerate WDSP если нужно
|
||||
if FController.FWDSPReady and (FController.FDSPEngine.SampleRate <> FController.FSampleRate) then
|
||||
@@ -4218,14 +4146,14 @@ begin
|
||||
FController.FNetwork.SetRunAndFreq(True, XvtrTranslate(FController.FCenterFreq), XvtrTranslate(FController.FCenterFreq), FController.FDriveLevel);
|
||||
FController.FRunning := True;
|
||||
InhibitSleep;
|
||||
FRXStartTime := GetTickCount64;
|
||||
FRXLastPktTime := 0;
|
||||
FRXPacketCount := 0;
|
||||
FillChar(FDDCSeqValid, SizeOf(FDDCSeqValid), 0);
|
||||
FSeqErrorCount := 0;
|
||||
FLastSeqErrorDDC := -1;
|
||||
FLastSeqErrorDelta := 0;
|
||||
FSeqOkStreak := 0;
|
||||
FController.FRXStartTime := GetTickCount64;
|
||||
FController.FRXLastPktTime := 0;
|
||||
FController.FRXPacketCount := 0;
|
||||
FillChar(FController.FDDCSeqValid, SizeOf(FController.FDDCSeqValid), 0);
|
||||
FController.FSeqErrorCount := 0;
|
||||
FController.FLastSeqErrorDDC := -1;
|
||||
FController.FLastSeqErrorDelta := 0;
|
||||
FController.FSeqOkStreak := 0;
|
||||
SetStatusText(6, 'SEQ OK');
|
||||
|
||||
// DDC и DUC Specific — теперь потоки эмулятора слушают на своих портах.
|
||||
|
||||
@@ -200,6 +200,20 @@ type
|
||||
FLastSMeter, FLastFwdW, FLastSWR, FLastSupplyV, FLastSupplyA: Double;
|
||||
FLastPLLLock: Boolean;
|
||||
|
||||
// ---- RX/seq телеметрия data-path (пишется из сетевого колбэка OnDDCIQ) ----
|
||||
FRXPacketCount: LongWord; // счётчик принятых IQ пакетов
|
||||
FRXStartTime: QWord; // GetTickCount64 момента START
|
||||
FRXLastPktTime: QWord; // GetTickCount64 последнего принятого пакета
|
||||
FActiveDDC: Integer; // DDC index для текущей платы (0 или 2)
|
||||
FLastDDCSeq: LongWord; // последний seq (из сетевого потока)
|
||||
FLastDDCIndex: Integer; // последний DDC index
|
||||
FDDCLastSeq: array[0..6] of LongWord;
|
||||
FDDCSeqValid: array[0..6] of Boolean;
|
||||
FSeqErrorCount: LongWord;
|
||||
FLastSeqErrorDDC: Integer;
|
||||
FLastSeqErrorDelta: Int64;
|
||||
FSeqOkStreak: Integer;
|
||||
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
@@ -215,6 +229,7 @@ type
|
||||
// вызывающим (GUI или демон) после CreateEngines.
|
||||
procedure OnMicPacket(const Data: TMicDataPacket);
|
||||
function PullSoundCardMic(MaxN: Integer): Integer;
|
||||
procedure OnDDCIQ(DDCIndex: Integer; const Data: TDDCIQPacket);
|
||||
|
||||
// Маршалинг команды в поток контроллера (GUI: TThread.Synchronize).
|
||||
procedure Invoke(M: TThreadMethod);
|
||||
@@ -424,6 +439,66 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TRadioController.OnDDCIQ(DDCIndex: Integer; const Data: TDDCIQPacket);
|
||||
// Сетевой поток: трекинг seq + подача IQ активного DDC в WDSP. UI не трогаем —
|
||||
// статус рисует таймер по полям ниже. Backend-агностичен (Pluto синтезирует
|
||||
// тот же TDDCIQPacket).
|
||||
var
|
||||
SamplesPerFrame: Integer;
|
||||
Seq, ExpectedSeq: LongWord;
|
||||
Delta: Int64;
|
||||
begin
|
||||
Inc(FRXPacketCount);
|
||||
FRXLastPktTime := GetTickCount64;
|
||||
Seq := (LongWord(Data.Seq[0]) shl 24) or (LongWord(Data.Seq[1]) shl 16)
|
||||
or (LongWord(Data.Seq[2]) shl 8) or LongWord(Data.Seq[3]);
|
||||
|
||||
if (DDCIndex >= Low(FDDCLastSeq)) and (DDCIndex <= High(FDDCLastSeq)) then
|
||||
begin
|
||||
if Seq <> 0 then
|
||||
begin
|
||||
ExpectedSeq := FDDCLastSeq[DDCIndex] + 1;
|
||||
if FDDCSeqValid[DDCIndex] and (Seq <> ExpectedSeq) then
|
||||
begin
|
||||
Inc(FSeqErrorCount);
|
||||
FLastSeqErrorDDC := DDCIndex;
|
||||
Delta := Int64(Seq) - Int64(ExpectedSeq);
|
||||
FLastSeqErrorDelta := Delta;
|
||||
FSeqOkStreak := 0;
|
||||
end
|
||||
else if FDDCSeqValid[DDCIndex] then
|
||||
begin
|
||||
if FSeqErrorCount > 0 then
|
||||
begin
|
||||
Inc(FSeqOkStreak);
|
||||
if FSeqOkStreak >= 20 then
|
||||
begin
|
||||
FSeqErrorCount := 0;
|
||||
FLastSeqErrorDDC := -1;
|
||||
FLastSeqErrorDelta := 0;
|
||||
FSeqOkStreak := 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
FDDCLastSeq[DDCIndex] := Seq;
|
||||
FDDCSeqValid[DDCIndex] := True;
|
||||
end;
|
||||
|
||||
// Подаём IQ в DSP — только с активного DDC
|
||||
if (DDCIndex = FActiveDDC) and FWDSPReady then
|
||||
begin
|
||||
SamplesPerFrame := (Integer(Data.SamplesPerFrame[0]) shl 8) or
|
||||
Integer(Data.SamplesPerFrame[1]);
|
||||
if SamplesPerFrame <= 0 then SamplesPerFrame := 238; // fallback
|
||||
FDSPEngine.PushDDCPacket(Data.IQData, 0, SamplesPerFrame);
|
||||
end;
|
||||
|
||||
// Запоминаем последний seq — статус обновит таймер (без Synchronize в hot path)
|
||||
FLastDDCSeq := Seq;
|
||||
FLastDDCIndex := DDCIndex;
|
||||
end;
|
||||
|
||||
procedure TRadioController.OnMicPacket(const Data: TMicDataPacket);
|
||||
// HW mic-стрим уходит в WDSP только если выбран источник Radio; иначе сэмплы
|
||||
// игнорируются (mic берётся со звуковой карты).
|
||||
|
||||
Reference in New Issue
Block a user