Skip to content

Integrate liquid-dsp as comprehensive DSP toolkit (foundation for future modems + FEC + adaptive filters) #3043

Description

@ten9876

Background

Phase 7 of the client-side DSP roadmap. Tracks pulling in liquid-dsp (MIT) as the foundation library for future client-side DSP work that the current ad-hoc + FFTW3 + r8brain stack doesn't cover well.

Why now is the right time to plan, not to implement

The library is genuinely good (mature, MIT-licensed, designed for embedded/real-time use, zero external deps). We've correctly held off so far because adding a large dependency for "we might use it someday" reasons violates Principle X (no premature dependencies).

But the right moment to pull it in is when we have a specific concrete feature that justifies the cost. This issue exists to:

  1. Document what liquid-dsp would unlock so future feature discussions can reference it.
  2. List the triggers that would justify the pull-in — so when we hit one of those triggers, the decision is fast.
  3. Sketch the integration shape so the eventual implementation PR doesn't start from scratch.

What liquid-dsp would unlock

Capability liquid-dsp primitives Currently
Modems PSK, QAM, FSK, GMSK, OFDM (modem objects + symbol-rate-agnostic demod) Zero native modem support — we delegate to firmware or external apps
Forward error correction Hamming, Golay, Reed-Solomon, convolutional codes None — would be needed for any native FT8/FT4/PSK31 decoder
Adaptive filters LMS, RLS, normalized variants None — useful for HF channel equalization on poor paths
AGC Multiple algorithms (squelch-aware, hangover, etc.) Delegated to radio firmware
NCO / mixers Numerically-controlled oscillators, frequency translators Inline in a few places
Polyphase resampling Multi-stage decimators/interpolators r8brain covers our resampling needs
Equalizers Decision-feedback, LMS-based None
Synchronization Symbol-rate, frame-rate, carrier-phase recovery None

Triggers that would justify pull-in

In rough order of likelihood:

1. Native FT8 / FT4 decode (Phase 4 of #85)

Would need FEC (Reed-Solomon or LDPC) + GMSK demod primitives. Could potentially be served by ft8_lib alone, but liquid-dsp gives us the toolkit to also handle adjacent modes (FT4, JT65, etc.) without a separate library per mode. Strongest single trigger.

2. Native PSK31 / RTTY / Olivia decoder

Each of these is a tractable demod task with liquid-dsp's modem primitives + symbol synchronization. Without liquid-dsp we'd write each demodulator from scratch. With it: ~200 LOC per mode vs. ~2000.

3. HF channel equalization for digital voice

RADE is shipped. Codec2 and similar future digital voice modes might benefit from adaptive equalization on poor HF paths. liquid-dsp's LMS/RLS adaptive filters are the right primitives.

4. Sophisticated client-side AGC

Currently we let the radio firmware handle AGC. If a future use case (e.g., DAX-IQ post-processing, or a mode where we receive raw IQ) needs client-side AGC, liquid-dsp has multiple algorithms with different attack/release shapes.

5. Native CW skimmer / multi-decoder pan

ggmorse decodes one frequency at a time. A "decode every CW signal on the panadapter at once" feature (a la CW Skimmer) would benefit from liquid-dsp's NCO + symbol-rate recovery primitives for parallel decoders.

Why we shouldn't pull it in yet

  • No current feature consumes any of the unlocked primitives.
  • Adding ~5 MB of compiled code + a vendor dir for "future use" is exactly the kind of dependency creep that bites us during cross-platform CI debugging.
  • The library's surface area is huge — without a specific consumer driving the integration, we'd be vendoring it without a clear API surface.
  • Each potential consumer (FT8, PSK31, etc.) is its own architectural decision point — bundling them into one "add the toolkit" PR conflates concerns.

Integration sketch (for the eventual pull-in PR)

  1. Vendor liquid-dsp under third_party/liquid-dsp/ following the same pattern as third_party/rnnoise/, third_party/fftw3/, etc.
  2. CMake target: add_subdirectory(third_party/liquid-dsp) after configuring its options to disable simulation/benchmark targets we don't need.
  3. Cross-platform build verification: liquid-dsp is autotools-native; verify the CMake wrapper (existing community fork or upstream) builds clean on Linux/Windows/macOS via the project's existing third_party CMake conventions.
  4. The first PR that integrates liquid-dsp also implements the trigger feature — they ship together, so the integration is justified by an immediate consumer.
  5. Subsequent features consume the existing vendored copy.

Scope decisions to make before starting

When we hit a trigger and pull this in, decide:

  1. Vendor full library or subset? liquid-dsp is modular but the source tree is monolithic. Full vendor is simpler; subset is leaner.
  2. Build as static lib or object lib? Static keeps the main binary self-contained; object lib avoids dual-compile of headers.
  3. SIMD usage? liquid-dsp has hand-tuned SSE/NEON paths — verify they don't conflict with our existing AVX2 RTCD pattern (used in rnnoise).
  4. Threading? liquid-dsp objects are not thread-safe by default — same constraint as rnnoise. Per-slice instance pattern is the right model.

Acceptance criteria (for the eventual integration PR)

  • liquid-dsp vendored under third_party/liquid-dsp/ with documented version pin
  • CMake target builds clean on Linux/Windows/macOS
  • At least one concrete feature consumes the library (the trigger feature)
  • No measurable binary-size increase if we don't pull in features that need it (verify with nm / bloaty)
  • Documentation in docs/architecture-pipelines.md notes what subset of liquid-dsp is used + by which features

Parent

Tracks Phase 7 of #85. Holding open until a Phase 4 / future digital-mode trigger justifies pull-in.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dependenciesPull requests that update a dependency fileenhancementImprovement to existing feature

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions