Skip to content

Fix DAX TX latency and jitter on Linux (PipeWire)#671

Closed
SA7LAV wants to merge 1 commit into
aethersdr:mainfrom
SA7LAV:fix/dax-tx-latency-jitter
Closed

Fix DAX TX latency and jitter on Linux (PipeWire)#671
SA7LAV wants to merge 1 commit into
aethersdr:mainfrom
SA7LAV:fix/dax-tx-latency-jitter

Conversation

@SA7LAV

@SA7LAV SA7LAV commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Reduce pipe-sink buffer from default (~64KB) to 2048 bytes (~42ms at 24kHz mono s16le) to minimize latency in the DAX TX audio path
  • Switch TX poll timer from 10ms CoarseTimer to 5ms Qt::PreciseTimer for more consistent audio delivery to the radio
  • Drain pipe fully per tick (read loop until EAGAIN) instead of single-read, preventing bufferbloat when the timer fires late

Problem

When using WSJT-X (or similar digital mode software) with AetherSDR on Linux via PipeWire, the DAX TX audio path had ~300ms+ total latency and produced a jittery signal on the waterfall. This caused:

  1. FT8/FT4 signals arriving too late in the time slot to be decoded by receiving stations
  2. Unstable TX power output (~10W fluctuating vs 60W expected)
  3. Visible signal instability ("wobble") on the waterfall display

Root cause

The module-pipe-sink default pipe buffer is large (~64KB), adding ~200ms of latency. Combined with the 10ms QTimer::CoarseTimer (which can jitter to 15-20ms) and single-read-per-tick behavior, audio arrived at the radio in irregular bursts with significant delay.

Scope

Linux onlyPipeWireAudioBridge.cpp is compiled only on Linux (elseif(UNIX) in CMakeLists.txt). Windows and macOS builds are completely unaffected.

Test plan

  • Tested FT4 and FT8 with WSJT-X Improved on FLEX-8400
  • Stable power output matching tune power level
  • Clean waterfall signal, no jitter
  • Successful QSOs (stations decoding and responding)
  • Verify no regression on voice TX via DAX
  • Test with PulseAudio (non-PipeWire) setup

🤖 Generated with Claude Code

The DAX TX audio path via module-pipe-sink suffered from high latency
(~300ms+) and audible signal jitter, making FT8/FT4 digital modes
unreliable — transmitted signals arrived too late in the time slot to
be decoded by receiving stations.

Three changes to PipeWireAudioBridge (Linux-only, no impact on
Windows/macOS builds):

1. Reduce pipe-sink buffer from default (~64KB) to 2048 bytes (~42ms
   at 24kHz mono s16le). The large default buffer added unnecessary
   latency before audio reached the read side of the pipe.

2. Switch TX poll timer from 10ms (CoarseTimer) to 5ms with
   Qt::PreciseTimer for more consistent audio delivery.

3. Drain the pipe fully on each timer tick (read loop until EAGAIN)
   instead of a single read per tick. When the timer fires late,
   the old single-read left stale data in the pipe that compounded
   into bufferbloat; the drain loop delivers all available audio
   immediately.

Tested with WSJT-X Improved running FT4 and FT8 on a FLEX-8400.
Before: ~10W output (vs 60W tune), jittery waterfall, no decodes by
remote stations. After: stable power output, clean signal, successful
QSOs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 enabled auto-merge (squash) April 4, 2026 16:34
@SA7LAV SA7LAV closed this Apr 4, 2026
auto-merge was automatically disabled April 4, 2026 16:35

Pull request was closed

@SA7LAV SA7LAV deleted the fix/dax-tx-latency-jitter branch April 4, 2026 16:36
ten9876 added a commit that referenced this pull request Apr 4, 2026
Reduce pipe-sink buffer from default ~64KB to 2048 bytes (~42ms at
24kHz mono s16le), switch TX poll timer from 10ms CoarseTimer to 5ms
PreciseTimer, and drain pipe fully per tick to prevent bufferbloat
when the timer fires late.

Original PR by @SA7LAV — branch was deleted before auto-merge completed,
applied from cached diff.

Co-Authored-By: SA7LAV <SA7LAV@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ten9876

ten9876 commented Apr 4, 2026

Copy link
Copy Markdown
Collaborator

Claude here. Your branch was deleted before CI finished, which cancelled the auto-merge. The fix looked solid — we reviewed the diff, verified the O_NONBLOCK fd, and applied it in PR #674 with your Co-Authored-By attribution.

Thanks for the contribution — this will help all the FT8/FT4 operators on PipeWire.

73, Jeremy KK7GWY & Claude (AI dev partner)

ten9876 added a commit that referenced this pull request Apr 4, 2026
Reduce pipe-sink buffer from default ~64KB to 2048 bytes (~42ms at
24kHz mono s16le), switch TX poll timer from 10ms CoarseTimer to 5ms
PreciseTimer, and drain pipe fully per tick to prevent bufferbloat
when the timer fires late.

Original PR by @SA7LAV — branch was deleted before auto-merge completed,
applied from cached diff.

Co-authored-by: SA7LAV <SA7LAV@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants