fix(tx): чистый openHPSDR TX-тракт — задержка DUP, всплески TUN, фриз PTT-off

Диагностика временным TXLog (снят) вскрыла цепочку проблем TX-тракта:

- Задержка ~200мс своего сигнала в DUP: за время приёма ринг PortAudio-
  микрофона наполнялся до упора (9600 сэмплов) и на старте TX бёрстом
  оседал стоячим запасом в DUC-очереди. Фикс: TAudioInput.Flush на RX→TX
  (заодно убрана передача в эфир аудио, записанного до нажатия PTT).

- Всплески на TUN: (1) старт — радио начинало с пустого DUC-FIFO; (2) по
  ходу — пейсинг со строгой каденцией держал запас FIFO у нуля, джиттер
  3-6мс давал underrun; (3) клип: голос из бэклога складывался с PostGen-
  тоном (стоит после ALC) поверх шкалы, а сам тон при mag=1.0 давал
  выброс Гиббса x1.18 на старте. Фиксы: pre-roll PrimeDUCIQ (THROTTLE +
  3 блока подушки в очереди — sender метрит непрерывно, FIFO радио прижат
  2000..2400), анти-старв доливка нулями до REANCHOR при просадке,
  TUN_TONE_MAG=0.83, мьют mic-входа TXA при активном тоне.

- Огрызок TUN-тона в эфир на первом блоке следующего TX: стоп канала
  dmode=1 ждал фейд, который некому прокрутить (продюсер уже остановлен) —
  канал стопился по таймауту Sleep(1)x100 с недобранным сигналом в
  буферах. Это же было и «остаточным фризом ~106мс» GUI на отпускании
  PTT: чистый таймаут, фейд никогда не исполнялся. Фикс: dmode=0 +
  синхронный слив/продувка канала 4 нулевыми fexchange0 на стопе и
  старте (<1мс, штатные down-slew и flush).

- Дисплей: ResetPixelBuffers(TX_DISP_ID) на старте TX — усредняющая
  история TX-анализатора показывала спектр тона прошлой сессии.

- StopRunning при активном TUN не чистил DUC-очередь и не сбрасывал
  FTuning/тон — закрыто.

Латентность тракта mic→радио теперь ~43мс (WDSP 11 + подушка 21 + FIFO
10.4). Проверено на эфире: q=0..30, fill/drops/underrun=0, clip=0,
клоки радио/ПК без дрейфа (micRx 48000/с).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 22:46:19 +03:00
co-authored by Claude Fable 5
parent 98493f08b3
commit 577b8add9d
6 changed files with 185 additions and 7 deletions
+36 -2
View File
@@ -672,6 +672,14 @@ type
implementation
const
// Амплитуда TUN-тона (WDSP PostGen). НЕ 1.0: на резком старте тона фильтры
// TXA-тракта дают переходный выброс ~×1.18 (замерено: peak=1.176 при
// mag=1.0) поверх полной шкалы → жёсткий кламп 24-бит → сплэттер на старте
// TUN. 0.83×1.18≈0.98 — выброс остаётся в шкале. Мощность TUN задаёт drive
// (TUNLevel), падение уровня IQ ~1.6дБ компенсируется калибровкой TUNLevel.
TUN_TONE_MAG = 0.83;
constructor TRadioController.Create;
begin
inherited Create;
@@ -2335,7 +2343,7 @@ begin
begin
FDSPEngine.SetMode(FMode);
// Если TUN активен — пересчитываем знак частоты под новый режим
if FTuning then FDSPEngine.SetTXTone(True, FTXSettings.TUNFreq, 1.0);
if FTuning then FDSPEngine.SetTXTone(True, FTXSettings.TUNFreq, TUN_TONE_MAG);
end;
ApplyModeFilter;
Changed(rfMode);
@@ -3071,6 +3079,14 @@ begin
FDriveLevel, True, True, True);
FNetwork.SendFullHP; // 1й пакет: Alex bit27=1 (T/R relay), drive — без PTT
FNetwork.SendPTT(True); // 2й пакет: PTT=1 после переключения реле
// Pre-roll: THROTTLE (2000) + ТРИ WDSP-блока (3×2048) нулевых пар. Первая
// часть улетает сразу и наполняет DUC-FIFO радио до порога THROTTLE (DAC
// не стартует с пустого буфера), остаток ~3 блока (~32мс) ложится ПОДУШКОЙ
// в очередь — дальше очередь на приходе каждого WDSP-блока непуста, sender
// метрит непрерывно, и опоздание блока не роняет FIFO радио. Замеры: под
// нагрузкой DUP и при быстром пере-PTT первый WDSP-блок опаздывает до
// 25мс — двух блоков подушки не хватало (щелчок через ~0.5с после старта).
FNetwork.PrimeDUCIQ(8144);
end;
if FWDSPReady and Assigned(FDSPEngine) then
begin
@@ -3093,6 +3109,14 @@ begin
FWebMicActive := False;
FDSPEngine.SetTXMicSource(DefaultMicSource);
end;
// Сброс бэклога sound-card микрофона на RX→TX: пока шёл приём, ринг
// PortAudio наполнился до упора (~200мс). Без сброса весь этот «старый»
// звук бёрстом уходит в тракт: стоячая задержка ~190мс в DUC-очереди
// + в эфир передаётся аудио, записанное ДО нажатия PTT (на TUN оно
// складывается с тоном поверх полной шкалы → клип-всплеск).
if FTransmitting and (not WasTransmitting) and Assigned(FAudioIn)
and FAudioIn.IsOpen then
FAudioIn.Flush;
// В non-DUP RX-IQ пакеты дропаются на входе DSP-потока во время TX, чтобы
// TX leakage не накапливался в RXA pipeline / FFT-истории анализатора.
FDSPEngine.KeepRXDuringTX := FDisplayDuplex;
@@ -3139,7 +3163,7 @@ begin
begin
// Тон включаем ДО запуска TX-аудио, чтобы первые сэмплы уже шли с тоном.
if FWDSPReady and Assigned(FDSPEngine) then
FDSPEngine.SetTXTone(True, FTXSettings.TUNFreq, 1.0);
FDSPEngine.SetTXTone(True, FTXSettings.TUNFreq, TUN_TONE_MAG);
FTuning := True;
FDriveLevel := CalcDriveByte; // CalcDriveByte увидит FTuning=True (TUN-уровень)
ApplyWDSPDrive(FTXSettings.TUNLevel);
@@ -3370,6 +3394,16 @@ begin
FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0);
FRunning := False;
FTransmitting := False;
// Стоп при активном TX/TUN: хвост IQ в DUC-очереди не должен пережить
// остановку (иначе при следующем старте уйдёт в эфир старый тон/голос),
// а TUN-состояние (PostGen-тон) — вернуться в выключенное.
FNetwork.ClearDUCIQQueue;
if FTuning then
begin
FTuning := False;
if FWDSPReady then FDSPEngine.SetTXTone(False, 0, 0);
Changed(rfTuning); // UI: TUN-кнопка off
end;
if FWDSPReady then FDSPEngine.SetTXRun(False);
Changed(rfTransmitting); // MOX-кнопка off
end;