mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
Phase 5 (batch 26): move OnTXIQ + DUC queue into the controller
The TX IQ handler (batch WDSP TXA output into 240-sample DUC packets, 24-bit clamp, FNetwork.SendDUCIQ) moves into TRadioController.OnTXIQ, along with the FDUCPendingI/Q/Count queue fields. The DUC-queue reset on a TX-state change moves from the rfTransmitting render into SetMOX itself (the render does not run headless). Both DSP-callback wiring sites (FormCreate and RecreateDSPEngine) now point at FController.OnTXIQ. Backend-agnostic: a Pluto backend converts 24-bit->int16 in SendDUCIQ. Pre-existing gap noted: RecreateDSPEngine never re-wires OnPullMicSamples, so sound-card mic is lost after a sample-rate change (unchanged here). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c1da506f73
commit
3a387e06ce
+4
-45
@@ -139,12 +139,7 @@ type
|
||||
FSpecDragX0: Integer; // X при нажатии
|
||||
FSpecDragFreq: Double; // FCenterFreq при нажатии
|
||||
FSpectrumDirty: Boolean; // таймер должен перерисовать спектр/водопад
|
||||
// DUC IQ packetizer: TXA отдаёт блок FTXOutBufSize пар, который
|
||||
// не обязан быть кратен 240 (DUC packet). Хвост переносим в следующий
|
||||
// вызов OnTXIQReady, иначе зануление давало 47 Гц гул.
|
||||
FDUCPendingI: array[0..239] of Integer;
|
||||
FDUCPendingQ: array[0..239] of Integer;
|
||||
FDUCPendingCount: Integer;
|
||||
// TX DUC-очередь (FDUCPending*) переехала в TRadioController (OnTXIQ).
|
||||
FSMeterPeak: Double; // верхняя граница светлой зоны
|
||||
FSMeterMin: Double; // нижняя граница светлой зоны
|
||||
FSMeterAvg: Double; // сглаженное среднее (EMA)
|
||||
@@ -577,7 +572,6 @@ type
|
||||
function ActiveTXFreqHz: Double;
|
||||
function DefaultMicSource: TTXMicSource;
|
||||
procedure ApplyMOX(Active: Boolean);
|
||||
procedure OnTXIQReady(const Buf: array of Double; Count: Integer);
|
||||
procedure OnPASettingsChange(MaxPower: Double; const BandCal: array of Double);
|
||||
procedure OnVHFCalSettingsChange(const VHFCal: array of Double);
|
||||
procedure OnTXSettingsChange(const T: TTXSettings);
|
||||
@@ -914,7 +908,6 @@ begin
|
||||
FController.FRunning := False;
|
||||
FController.FTransmitting := False;
|
||||
FController.FSplitTxB := False;
|
||||
FDUCPendingCount := 0;
|
||||
FController.FDisplayDuplex := False;
|
||||
FController.FTuning := False;
|
||||
FController.FMuted := False;
|
||||
@@ -1157,7 +1150,7 @@ begin
|
||||
FController.FDSPEngine.OnAudio := OnAudioReady;
|
||||
FController.FDSPEngine.OnSpectrum := OnSpectrumReady;
|
||||
FController.FDSPEngine.OnWaterfall := OnWaterfallReady;
|
||||
FController.FDSPEngine.OnTXIQ := OnTXIQReady;
|
||||
FController.FDSPEngine.OnTXIQ := FController.OnTXIQ;
|
||||
// Sound-card mic путь: WDSP TX-thread дёргает этот колбэк перед каждым
|
||||
// тиком, если выбран источник SoundCard. Для Radio колбэк не вызывается.
|
||||
FController.FDSPEngine.OnPullMicSamples := FController.PullSoundCardMic;
|
||||
@@ -1310,8 +1303,7 @@ begin
|
||||
FSpecView.TXMode := FController.FTransmitting and not FController.FDisplayDuplex;
|
||||
FSpecView.TXOverlay := FController.FTransmitting;
|
||||
ApplySpecViewGridFromState; // сетка спектра под активный тракт
|
||||
FDUCPendingCount := 0;
|
||||
end;
|
||||
end; // (DUC-очередь сбрасывает SetMOX)
|
||||
rfTuning:
|
||||
StyleButton(BtnTUN, FController.FTuning);
|
||||
rfADCOverload:
|
||||
@@ -3542,7 +3534,7 @@ begin
|
||||
FController.FDSPEngine.OnAudio := OnAudioReady;
|
||||
FController.FDSPEngine.OnSpectrum := OnSpectrumReady;
|
||||
FController.FDSPEngine.OnWaterfall := OnWaterfallReady;
|
||||
FController.FDSPEngine.OnTXIQ := OnTXIQReady;
|
||||
FController.FDSPEngine.OnTXIQ := FController.OnTXIQ;
|
||||
end;
|
||||
|
||||
function TMainForm.EnsureWDSPWisdom: Boolean;
|
||||
@@ -5758,39 +5750,6 @@ begin
|
||||
Result := FController.CalcDriveByte;
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnTXIQReady(const Buf: array of Double; Count: Integer);
|
||||
// FTXOutBufSize не обязан быть кратен 240, поэтому копим хвост между
|
||||
// вызовами и шлём только полные DUC-пакеты. Иначе зануление хвоста
|
||||
// последнего пакета давало периодический провал на каждом TX-блоке
|
||||
// (≈47 Гц гул при 192k IQ rate). Также клампим в 24-bit чтобы пики
|
||||
// от компрессора/EQ (значение чуть >1.0) не оборачивались через wrap.
|
||||
function ScaleIQ24(V: Double): Integer;
|
||||
var S: Double;
|
||||
begin
|
||||
S := V * 8388607.0;
|
||||
if S > 8388607.0 then Result := 8388607
|
||||
else if S < -8388608.0 then Result := -8388608
|
||||
else Result := Round(S);
|
||||
end;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if not FController.FNetwork.Connected then Exit;
|
||||
|
||||
for i := 0 to Count - 1 do
|
||||
begin
|
||||
FDUCPendingI[FDUCPendingCount] := ScaleIQ24(Buf[i * 2]);
|
||||
FDUCPendingQ[FDUCPendingCount] := ScaleIQ24(Buf[i * 2 + 1]);
|
||||
Inc(FDUCPendingCount);
|
||||
|
||||
if FDUCPendingCount >= 240 then
|
||||
begin
|
||||
FController.FNetwork.SendDUCIQ(FDUCPendingI, FDUCPendingQ);
|
||||
FDUCPendingCount := 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnPASettingsChange(MaxPower: Double;
|
||||
const BandCal: array of Double);
|
||||
var i: Integer;
|
||||
|
||||
@@ -215,6 +215,10 @@ type
|
||||
FLastSeqErrorDelta: Int64;
|
||||
FSeqOkStreak: Integer;
|
||||
FADCOverloadActive: Boolean; // ADC overload (рендерится по rfADCOverload)
|
||||
// ---- TX DUC-очередь (накопление IQ от WDSP в пакеты по 240, OnTXIQ) ----
|
||||
FDUCPendingI: array[0..239] of Integer;
|
||||
FDUCPendingQ: array[0..239] of Integer;
|
||||
FDUCPendingCount: Integer;
|
||||
// Pending-значения декода HP-статуса (сетевой поток → ApplyHPStatus через
|
||||
// Invoke; Synchronize блокирующий и последовательный, гонок нет).
|
||||
FHPpFwdW, FHPpSWR, FHPpSupplyV, FHPpSupplyA: Double;
|
||||
@@ -239,6 +243,7 @@ type
|
||||
procedure OnDDCIQ(DDCIndex: Integer; const Data: TDDCIQPacket);
|
||||
procedure OnHPStatus(const Status: THighPriorityStatus);
|
||||
procedure ApplyHPStatus; // маршалится из OnHPStatus (сглаживание/overload/HWPTT)
|
||||
procedure OnTXIQ(const Buf: array of Double; Count: Integer);
|
||||
|
||||
// Маршалинг команды в поток контроллера (GUI: TThread.Synchronize).
|
||||
procedure Invoke(M: TThreadMethod);
|
||||
@@ -508,6 +513,36 @@ begin
|
||||
FLastDDCIndex := DDCIndex;
|
||||
end;
|
||||
|
||||
procedure TRadioController.OnTXIQ(const Buf: array of Double; Count: Integer);
|
||||
// WDSP TXA отдаёт IQ-пары @ TX rate. Копим хвост между вызовами и шлём только
|
||||
// полные DUC-пакеты по 240 (зануление хвоста давало 47 Гц гул). Клампим в
|
||||
// 24-bit, чтобы пики компрессора/EQ (чуть >1.0) не оборачивались через wrap.
|
||||
// Backend-агностично: Pluto-backend в SendDUCIQ переведёт 24-bit→int16.
|
||||
function ScaleIQ24(V: Double): Integer;
|
||||
var S: Double;
|
||||
begin
|
||||
S := V * 8388607.0;
|
||||
if S > 8388607.0 then Result := 8388607
|
||||
else if S < -8388608.0 then Result := -8388608
|
||||
else Result := Round(S);
|
||||
end;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if not (Assigned(FNetwork) and FNetwork.Connected) then Exit;
|
||||
for i := 0 to Count - 1 do
|
||||
begin
|
||||
FDUCPendingI[FDUCPendingCount] := ScaleIQ24(Buf[i * 2]);
|
||||
FDUCPendingQ[FDUCPendingCount] := ScaleIQ24(Buf[i * 2 + 1]);
|
||||
Inc(FDUCPendingCount);
|
||||
if FDUCPendingCount >= 240 then
|
||||
begin
|
||||
FNetwork.SendDUCIQ(FDUCPendingI, FDUCPendingQ);
|
||||
FDUCPendingCount := 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TRadioController.OnHPStatus(const Status: THighPriorityStatus);
|
||||
// Сетевой поток: декод HP-статуса (board-specific supply/power/SWR) → pending →
|
||||
// маршалинг в ApplyHPStatus (сглаживание + overload + HWPTT уже на потоке UI/ядра).
|
||||
@@ -1597,6 +1632,7 @@ begin
|
||||
WasTransmitting := FTransmitting;
|
||||
FTransmitting := On_;
|
||||
if On_ then FDriveLevel := CalcDriveByte;
|
||||
FDUCPendingCount := 0; // сброс TX DUC-очереди на смене TX-состояния
|
||||
// Сетевая PTT-последовательность TX-on: сначала реле (Alex bit27=1, drive) без
|
||||
// PTT, затем PTT=1 — переключаем реле раньше передачи.
|
||||
if FTransmitting and Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
|
||||
Reference in New Issue
Block a user