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:
Uladzimir Karpenka
2026-06-05 23:42:22 +03:00
co-authored by Claude Opus 4.8
parent c1da506f73
commit 3a387e06ce
2 changed files with 40 additions and 45 deletions
+4 -45
View File
@@ -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;