Skip to content

[RFC] Optional Digital FM reception and program metadata #6079

Open

Description

@Ozy311

Status and scope

Separate RFC, pending maintainer approval. I am moving the optional Digital FM proposal out of #5468 following review feedback. #5468 governs the analog RTL receive work. Its conditional approval does not approve this new modulation, dependencies or decoder worker.

Implementation reference: draft #6002. It remains an evaluation proposal, not part of the current analog RTL submission. ENABLE_HD_FM keeps its existing name and remains default OFF; official publishing workflows must explicitly keep it OFF. No request here changes analog RTL's release inclusion or requires analog PRs to wait for this proposal.

Problem

Analog broadcast FM reception cannot decode digital broadcast services, select their independent programs, or display their digital station/program metadata. This is a separate receive mode with new dependencies and execution/resource requirements, not a prerequisite for RTL FM/FM-N/WFM mono or stereo.

Proposal

Review the bounded embedded decoder design below as one independent feature. Aether owns USB acquisition; the optional decoder consumes original captured IQ. Preserve existing analog reception when the option is disabled or digital lock is absent. This proposal does not implicitly approve automatic analog/digital blending.

The decision covers dependency inclusion, a private decoder worker and teardown bounds, complete digital RF-footprint admission, 44.1 kHz selected-program PCM with explicit speaker-rate conversion, program selection, and measured digital state/metadata in the WFM applet and local overlay. Analog RDS decoding is not implemented by this proposal.

Cross-platform impact and qualification

The initial Linux/GNU-C checkpoint and later private macOS/Windows evaluation builds are evidence only for their exact revisions. The feature needs native configure/build/test coverage ON and OFF on each supported release platform, dependency/notices/source packaging, bounded startup/replacement/teardown and long-running audio/resource qualification. macOS arm64 results do not qualify Linux aarch64. Failed historical observations below remain open; operator acceptance does not erase them.

Alternatives considered

  • Ship analog RTL first and defer Digital FM: the current submission plan.
  • Run an external decoder process: not the proposed integration; Aether would lose its single in-process ownership and lifecycle model.
  • Embed the optional decoder behind the existing build flag: the proposal under review, conditional on maintainers accepting the dependency and lifetime design.

Decision requested

Jeremy: approve, reject or request changes to this standalone Digital FM design. A decision applies only to this RFC and #6002. Analog capture, free panning, analog stereo, PPM, multi-RX, DAX/TCI and RTL release packaging retain their own review criteria. Their approval does not depend on acceptance of this RFC.

Branch separation

Current separation — 2026-10-09: analog WFM and PPM are merged into main. Ready #6083 (a3458b09) contains only the remaining analog delivery on main d6f6a4f0. Draft #6002 (a8112373) is stacked on that analog head.

This Digital increment owns the actual default-OFF ENABLE_HD_FM option, the explicit OFF flags in all three release workflows and both local build/installer entry points, and the corresponding build documentation. Analog #6083 no longer declares or passes this option. Pinned decoder sources and the existing ON implementation are preserved. Fresh exact-head Linux/GCC focused validation passes: decoder OFF 15/15 and ON 17/17, zero skips/failures, including spectrum geometry, WFM controls, PCM/routing and native decoder tests as applicable. The full user-documentation build also passes. Details and limits are recorded in #6002; this does not change draft or release status.

Original separation checkpoint — historical evidence

Branch separation is complete. Analog #6082 (5e39790a) now follows #5974; #6083 (4b1816ac) follows #6082 without decoder code or dependencies. Draft #6002 (634fdd28) follows #6083. The extracted analog application builds and 39/39 selected native Mac tests pass. This is a real Git dependency change, not just a documentation edit. Digital production code/build implementation is preserved from a53423ac; prior history is retained on codex/rtl-digital-evaluation-preserved-20261001. No new Digital ON qualification is claimed.

Historical evaluation and proposed Digital FM architecture — 2026-09-28

The analog WFM applet (6e87be85, #5974), optional native Digital (dea570a2, #6002) and live PPM adjustment (7911324a, #6003) are published as draft PRs. 7911324 is now the normal Nobara evaluation build. The operator accepted Digital (“homerun man perfect”) and PPM (“confirmed the PPM adjust works great.. got it working. nice job”). These are subjective reports, not replacements for measured qualification. PPM uses the existing typed transaction and accepted per-device persistence; it selects no calibration value or sign automatically.

Exact Nobara builds passed 60 selected CTests on dea and 63 on 791, without failed, errored or skipped cases; unavailable optional effect subcases are not claimed as exercised. PPM timer and Digital contrast mutations failed their intended assertions, followed by exact artifact restoration. Recorded-IQ evidence remains pinned to decoder-identical predecessor 3a716c2e: direct and production worker/mixer replay passed for both discovered programs with real 44.1 kHz tap/48 kHz speaker output. Concealed-audio, incomplete-prefill and initial-boundary mutations were caught and restored. These finite recording results are not new live-radio evidence.

Live persistence, overlay OFF/ON/OFF and readable actual-window contrast were observed. Live overflowing text, instrumented acoustic/nonzero-PCM capture and uninterrupted playback remain unqualified. Both earlier strict muted observers remain failed: endpoint empty-application-queue increments were +1,140 (3a) and +1,011 (dea), despite separately valid sampled Digital state. This counter is not a proven audible-glitch count. Dea also accumulated 2,236 dropped PCM packets or Digital observation messages between initial accepted restoration and the later quiet observation. These are aggregate output-queue drops, not raw USB IQ; their cause and exact interval remain unresolved. Operator acceptance does not erase these results.

Digital FM architecture proposal — not yet approved

This changes the original “no additional third-party DSP dependency” proposal. Jeremy's approval is requested for:

  1. Default-OFF ENABLE_HD_FM, initially Linux/GNU C, embedding pinned nrsc5 0225922b and FAAD2 2.11.2/HDC 673a22a3 with source manifests, licenses and notices. Aether remains the sole USB owner; no external decoder process is launched.
  2. A prepared receiver's private synchronous-decoder worker with bounded IQ/PCM/observation queues and two resident leases for old/new replacement. Open/close use the existing FFTW planner lock. Internal allocation remains possible; upstream allocation-failure hardening and a hard cancellation bound for an in-progress call remain outstanding.
  3. Original IQ extraction before analog filtering at exactly 744187.5 complex samples/s. Complete ±225 kHz Digital sidebands plus guards must fit capture independently of saved analog filters. Selected-program slice PCM is 44.1 kHz stereo, with explicit speaker conversion to 48 kHz. Global defaults and mixer deadlines remain unchanged. Current Digital admission is one configured receiver.
  4. Explicit Digital/program controls with observed sync, valid selected audio and speaker readiness kept distinct. Existing transaction adoption and persistence remain authoritative. Session/receiver/audio epochs and original production age reject stale output. Proposed bounds are 32 IQ slots of at most 8192 samples, 250 ms queued-source distance, 32768-frame PCM storage, 8192-frame speaker prefill and 500 ms observation/audio age; finite tests are not universal timing guarantees.
  5. Bounded Digital station/ID3 text in the applet and passive local overlay. SpotHub Display's ordinary WFM RDS button defaults ON, preserves saved OFF and affects only the overlay—not decoding or network spots. Provenance identifies Digital, not analog RDS. Theme-backed word-boundary pages retain the frequency anchor, entry identity and accessible full text.

Implementation and dependency details. Original conditional approval does not approve this amendment; free-pan policy remains independently scoped to #5468. Mac/Windows/ARM Digital and current sanitizer qualification are outstanding. Keep #6002 draft pending the decision on this standalone proposal and its applicable evidence; analog successors are now independent of this feature.

The section above preserves the September 28 measurements and outstanding findings as historical evidence. Old platform/status statements describe those checkpoints. Current PR checks and later evidence remain revision-specific in #6002; this RFC is not a declaration of release readiness.


73, Ozy K6OZY · Codex

Activity

  1. self-assigned this
    on Oct 2, 2026
  2. added
    claude-activeAetherClaude is actively working on this issue
    audioAudio engine and streaming
    external devicesFlexControl, MIDI, serial port, USB peripherals
    GUIUser interface
    maintainer-reviewRequires maintainer review before any action is taken
    on Oct 2, 2026
  3. aethersdr-agent commented on Oct 2, 2026

    @aethersdr-agent
    Contributor

    Triage: maintainer-review — decision RFC, no information gap

    This asks for an approve/reject/request-changes call on a design, not for a bug to be root-caused, so there is nothing to ask the reporter for. Labelled New Feature + audio + GUI + external devices and handed to Jeremy. I verified the RFC's load-bearing claims against main and against #6002 (fetched pull/6002/head) so the decision isn't made from the prose alone.

    What's actually in the tree today

    Claims I confirmed (so the decision doesn't rest on prose)

    1. License compatibility is fine, with one concrete deliverable. This repo is GPL-3.0 (LICENSE:1-2). The pinned FAAD2/HDC fork is GPLv2-or-later (third_party/faad_hdc/upstream/COPYING:308-309, include/neaacdec.h:5-8) — upgradeable to GPLv3, so not a blocker. But that header also imposes a mandatory notice: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com". RTL: add opt-in native digital broadcast reception and local metadata #6002 carries it only in third_party/faad_hdc/README.aethersdr.md:26, which is not a user-visible notice surface. If this is approved, the string must also land wherever the shipped app presents third-party notices — a GPLv2 §2c obligation, not a nicety. nrsc5 ships COPYING.GPL-3.0 + LICENSE-SOURCE.json; that part is clean.
    2. Item 2's FFTW claim is accurate and correctly layered. src/core/dsp/FftwPlannerLock.h exists (fftwPlannerMutex()/fftwfPlannerMutex()), and RtlSdrDdc.cpp:2 already includes it — so a decoder open/close in backends/rtl reaching the planner lock raises no Principle II concern.
    3. Item 3's "exactly 744187.5 S/s" does not touch the device clock. clampSampleRate() (RtlSdrBackend.cpp:53-72) snaps to a 9-entry list that has no 1488375, and RTL: add opt-in native digital broadcast reception and local metadata #6002 leaves that list alone. The rate comes from a fractional resampler instead — HdFmIqAdapter.h:18, kOutputRate = 1488375.0 / 2.0. Correct approach; the RFC's wording implies a hardware rate it never requests.
    4. The ±225 kHz footprint admission is real code, not an aspiration: RtlCaptureTransaction.cpp:474-482 widens effectivePassband() to ±225 kHz with a ≥3 kHz guard when mode == Wfm && wfmHdStereo.

    The integration seam the RFC under-describes — and the one hypothesis worth testing

    RFC item 3 says "global defaults and mixer deadlines remain unchanged." The audio ingress is where that gets tested, and it's the weakest-specified part:

    • RTL analog PCM enters the main RX path at 24 kHz (RtlSdrDdc.cpp:290-300, fractional stage-2 to exactly 24 kSPS; AudioEngine.cpp:1897). A 44.1 kHz digital stream on the same slice means either flipping m_rxProducerRate at runtime — which rebuilds the resampler, AudioEngine.cpp:4257 — or a dedicated per-source resampler. Recommend the latter; m_radeRxResampler (AudioEngine.cpp:10347-10349) is the existing precedent for exactly this shape and is what "defaults unchanged" actually requires.
    • The conversion target must be m_rxOutputRate, not a literal 48000. AudioEngine.cpp:4456 shows QAudioSink falling back 48000 → 44100 → 24000. A hard-coded 44.1→48 stage produces wrong-pitch audio on a 24 kHz-only endpoint, and is a no-op resample on a 44.1 kHz one.
    • Hypothesis for the reporter's own open findings (+1,140/+1,011 empty-application-queue increments, 2,236 dropped PCM/observation items): the RX output mixer applies per-block 1/N scaling across active sources (AudioEngine.cpp ~2255-2362). Even with no automatic blending, the analog↔digital handoff instant flips N, which is a known pumping/discontinuity source on this path. Worth instrumenting in RTL: add opt-in native digital broadcast reception and local metadata #6002 before anyone calls those counters benign — they are aggregate output-queue drops, so this is a candidate cause, not a verdict.

    Naming bug to fix regardless of the decision

    Item 5 routes digital metadata display through SpotHub Display's button labelled "WFM RDS", while this RFC explicitly does not implement RDS. A control named for a feature that doesn't exist, gating a feature it isn't named for, will generate its own issue. Rename to something provenance-accurate (the RFC already says provenance identifies Digital, not analog RDS).

    Handoff

    Nothing here is a reason to block the analog RTL work — the RFC's own separation claim holds against the tree, and the one coupling is the unmerged #5974 applet that item 5 depends on. The decision is yours; the two items I'd attach conditions to if you lean approve are (a) the Nero §2c notice reaching a shipped user-visible surface, and (b) root-causing the output-queue drops before the flag is ever built ON in CI. For the CI shape the RFC asks for, ENABLE_NVIDIA_AFX is the established template for "built ON in CI, not necessarily shipped" (ci.yml:247,664, full-suite.yml:84, appimage.yml:269, windows-installer.yml:251).

    I have not reviewed #6002 line-by-line — that's a separate pass after aetherclaude-eligible, and at 104k insertions it needs its own scope decision.


    🤖 aethersdr-agent · cost: $3.0876 · model: claude-opus-5

  4. aethersdr-agent commented on Oct 2, 2026

    @aethersdr-agent
    Contributor

    Correction to my diff attribution above. My FETCH_HEAD had been overwritten between reads, so two of the size/scope figures described the whole stack rather than this RFC's increment. Re-fetched pull/6002/head at b6f87a9d and isolated it:

    • The third_party/wdsp edits are not part of this proposal. wbfm.c (+217) and iobuffs.c come from 208bc220 "Add dedicated WFM applet and observed stereo controls" and 290bca55 "Qualify opt-in WBFM filtering, headroom and stereo" — analog RTL WFM: native stereo, dedicated applet and bounded receive exchange #5974 work, already in RTL: add opt-in native digital broadcast reception and local metadata #6002's base (all 12 wdsp files, +746/-30, land at or before 6e87be85). I withdraw "not described anywhere in this RFC; part of what a yes approves." Approving this RFC adds no WDSP changes.
    • "366 files / ~104k insertions" was the stack vs main, not the HD increment. The analog base alone (main→6e87be85) is 827 files / +64,905. The HD-specific vendored footprint is 161 files / +80,351 — third_party/nrsc5 + third_party/faad_hdc. That's the number attached to the dependency-inclusion decision.

    The #5974 status in my comment holds, now tested rather than quoted from the RFC: 6e87be85 is an ancestor of #6002's head (exit 0) but not of main (exit 1) — unmerged. The WfmDeviceDialog.cpp on main is a pre-existing dialog last touched by #4279, not the #5974 applet. So item 5's prerequisite claim stands.

    Everything else is unchanged: GPLv2-or-later is compatible, the Nero §2c notice still needs a shipped user-visible surface, the planner lock is real and correctly layered, clampSampleRate is untouched with the rate conversion in HdFmIqAdapter, and effectivePassband() genuinely widens to ±225 kHz.


    🤖 aethersdr-agent · cost: $4.2239 · model: claude-opus-5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

GUIUser interfaceNew FeatureNew feature requestaudioAudio engine and streamingexternal devicesFlexControl, MIDI, serial port, USB peripheralsmaintainer-reviewRequires maintainer review before any action is takenrfc

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions