aethersdr / aethersdr/AetherSDR

Feature Request: CQUAM AM Stereo Decoder

Open
#176 7 comments 0 reactions 1 assignee View on GitHub

@NF0T is already working on this.

Since Jul 5, 2026.

audio dependencies help wanted maintainer-review New Feature priority: low protocol VITA-49
Dominant language
C++
Stars
221
Forks
117
Avg merge
2d 7h
Merged PRs (30d)
299

Description

What

Add a client-side CQUAM (Compatible Quadrature Amplitude Modulation) AM Stereo decoder
to AetherSDR, enabling the radio to receive and decode AM Stereo broadcasts — those
transmitted using the Motorola C-QUAM standard — and output them as proper stereo audio.

When tuned to an AM station that is broadcasting C-QUAM stereo, the user would enable
the decoder (e.g. via a toggle in the RX Applet or VFO widget AM tab), and the audio
output would change from mono to stereo, with audible left/right channel separation
and a stereo indicator displayed in the UI.


Why

AM Stereo via C-QUAM is a real and still-active broadcast standard. A number of AM
stations worldwide (particularly in the US, Canada, Japan, and Australia) continue to
transmit C-QUAM stereo today. Conventional SDR clients receive AM as mono and discard
the stereo subcarrier entirely.

FlexRadio hardware is well-suited for AM Stereo reception — it has sufficient ADC
dynamic range and bandwidth, and delivers raw IQ-derived audio to the client. However,
SmartSDR for Windows has no C-QUAM decoder, so AetherSDR would have a genuine
capability advantage here. This is a feature that has long been requested by
broadcast-band listeners and vintage radio enthusiasts in the SDR community.

The problem being solved: AM Stereo audio is collapsed to mono by the current
AudioEngine pipeline. The stereo information encoded in the quadrature component
of the C-QUAM signal is simply discarded.


How Other Clients Do It

SDR# (SDRsharp) — Windows

SDR# supports AM Stereo via a community plugin that implements C-QUAM decoding
on the raw IQ baseband. It exposes a plugin panel with a stereo/mono lock indicator
and a pilot detection LED. When the decoder locks, audio switches to stereo
automatically.

GQRX — Linux/macOS

GQRX does not natively support C-QUAM as of writing. AM mode outputs mono only.
This is a known gap in the Linux SDR ecosystem — implementing this in AetherSDR
would be a meaningful differentiator.

Airspy HF+ Discovery / Spy Server

The Airspy ecosystem (via SDR#) supports C-QUAM natively when used with compatible
plugins. Documented in the Airspy community forums.

Vintage hardware reference

The Motorola MC13028A and MC13020P chips were the canonical C-QUAM decoder ICs.
Their datasheets describe the demodulation algorithm precisely and are freely
available — they are an excellent implementation reference for software decoding.


Suggested Behavior

Mode trigger
  • The decoder activates only when the slice mode is AM or SAM — it is
    irrelevant in USB/LSB/FM/CW/etc.
  • A "AM Stereo" toggle button appears in the RX Applet (or the VFO AM tab,
    similar to how CW decode options appear in the CW tab).
Signal flow

The C-QUAM signal is received as stereo float32 audio (PCC 0x03E3) at 24 kHz.
C-QUAM encodes stereo as:

L+R  →  AM envelope  (standard AM demodulation, already decoded by the radio DSP)
L-R  →  quadrature (Q) component of the composite carrier

The radio's AM mode demodulates only the envelope (L+R). To recover the Q
component (L-R difference signal), the decoder would need access to the IQ
baseband before AM envelope detection — or operate on the raw audio delivered
in the VITA-49 stream if the radio provides the full complex baseband.

Note: This may require requesting a DAX IQ stream (issue #124) rather
than the standard remote audio stream, since envelope-detected AM audio has
already discarded the Q component. Alternatively, if the radio can be put into
a wide USB/LSB mode centered on the AM carrier, the client could perform its
own envelope detection and recover both L+R and L-R from the IQ pair in the
audio stream. This needs investigation.

Decoding steps (software)
  1. Receive IQ pair from VITA-49 audio (float32 stereo, big-endian, 24 kHz)
  2. Envelope detect: mono = sqrt(I² + Q²) → L+R signal
  3. Phase-lock loop: lock onto the 25 Hz pilot (or the carrier itself) to
    establish the C-QUAM phase reference
  4. Multiply Q component by PLL reference → L-R difference signal
  5. Matrix: L = (L+R + L-R) / 2, R = (L+R - L-R) / 2
  6. Feed L and R to the stereo QAudioSink pipeline
UI indicators
  • "ST" indicator (cyan, similar to CwDecoder's confidence display) shown in
    the VFO widget or RX Applet when stereo lock is achieved
  • "MONO" fallback when pilot is not detected or SNR is too low
  • Optional: stereo separation meter (L-R level as a small horizontal gauge)
Settings persistence
// In AppSettings
s.setValue("AmStereoEnabled", "True");   // per-slice or global
s.setValue("AmStereoMinSnr", "15");      // dB threshold for auto-lock
New class: CquamDecoder

Modeled after CwDecoder — takes feedAudio(float* left, float* right, int frames)
and emits:

signals:
    void stereoLockChanged(bool locked);
    void audioReady(QVector<float> left, QVector<float> right);

Wired into AudioEngine::feedAudioData() when AM Stereo is enabled and mode is AM/SAM,
replacing the mono envelope output with the decoded stereo pair.


Protocol Hints

Standard remote audio (current)
stream create type=remote_audio_rx compression=none

PCC 0x03E3 delivers float32 stereo at 24 kHz — but in AM mode, the radio's own
DSP has already performed envelope detection, and both channels carry the same mono
audio. The Q component is lost at this point.

DAX IQ (needed — see issue #124)
stream create type=dax_iq daxIQ=1 pan=0x40000000

PCC unknown — needs Wireshark capture. This would deliver raw complex baseband IQ
before the radio's own demodulation, allowing full client-side C-QUAM decode.

Mode forcing workaround (to investigate)

Setting the slice to a narrow DSB or raw IQ mode while tuning to an AM carrier
may expose both I and Q components via the existing audio stream. Needs testing
with firmware v1.4.0.0.

Overall protocol status: Partially known — DAX IQ stream format needs research
(see #124). Standard remote audio stream is insufficient for full C-QUAM decode.


Labels

feature · audio · dsp · help wanted

Related Issues

  • #124 — DAX IQ streaming (likely prerequisite for full client-side decode)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.