Add Built-in RTTY (FSK/AFSK) Decoder with Text Output Panel
Labels: enhancement, audio, GUI
## What
A built-in RTTY (radioteletype) decoder that listens to the demodulated audio
from the active slice in DIGL/RTTY mode and decodes incoming 5-bit Baudot/ITA2
characters in real time, displaying the decoded text in a panel inside
AetherSDR — no external software required.
The decoder should support:
- Standard RTTY parameters: 45.45 baud (standard HF), 50 baud, 75 baud, 110 baud
- Mark/space shift: 170 Hz (standard), 200 Hz, 425 Hz, 850 Hz
- Baudot/ITA2 character set with LTRS/FIGS shift tracking
- USB and LSB carrier polarity (normal and reverse)
## Why
RTTY remains one of the most active HF digital modes, especially during
contesting (ARRL RTTY Roundup, CQ WW RTTY, EA RTTY Contest, etc.). Today,
AetherSDR users must run a separate application — MMTTY, fldigi, WSJT-X, or
Digi65 — alongside AetherSDR via DAX audio to decode RTTY. This adds latency,
complexity, and requires DAX to be enabled and routed correctly.
AetherSDR already ships a built-in CW decoder (`CwDecoder` via ggmorse). RTTY
decode is a natural complement for the second most common legacy digital mode
on HF. Keeping decode inside the client means zero-latency access to the same
audio pipeline already flowing through `AudioEngine`.
## How Other Clients Do It
**SmartSDR for Windows** does not include a built-in RTTY decoder. Users run
MMTTY or N1MM+ Logger with DAX audio routing.
**GQRX** does not include RTTY decode natively; users pipe audio to fldigi via
a virtual audio device.
**SDR++** has a community plugin (`rtty_decoder`) that accepts audio from the
demodulated output and renders decoded text in a dockable panel.
**fldigi** is the de-facto standard RTTY decoder on Linux: mark/space tone
detection via two narrow bandpass filters, zero-crossing timing, Baudot decode.
It is well-understood and its algorithm is well-documented.
The opportunity for AetherSDR is to be the **first FlexRadio client** to
include a native, zero-configuration RTTY decoder — matching or exceeding the
SmartSDR + MMTTY workflow without any external programs.
## Suggested Behavior
1. **Auto-activation:** When the active slice's mode is set to `RTTY`, `DIGL`,
or `DIGU`, a **RTTY decode panel** appears automatically below the waterfall
or as a togglable section in the `RxApplet` — similar to how the CW decode
overlay appears automatically in CW mode.
2. **RTTY tab in VfoWidget / RxApplet:** A new **RTTY** tab (alongside existing
DSP tabs) exposes:
- Baud rate selector: `45.45` (default) / `50` / `75` / `110`
- Shift selector: `170 Hz` (default) / `200 Hz` / `425 Hz` / `850 Hz`
- Polarity toggle: `Normal` / `Reverse`
- Mark frequency display (read from `digl_offset` via `SliceModel`)
3. **Decode text area:** A fixed-height (3–5 lines) scrolling text widget with:
- Dark background, monospace font (Courier or similar)
- Color-coded confidence (green = locked, yellow = marginal, red = no lock)
- Auto-scroll with a "Pause scroll" button
- "Clear" button to wipe the buffer
- Copy-to-clipboard button
4. **Spectrum integration:** Two thin vertical lines on `SpectrumWidget` mark
the mark and space frequencies (this already exists in partial form per
issue #660 — this feature depends on that being correct).
5. **Settings persistence:** Baud/shift/polarity saved per-band in
`AppSettings` under keys such as `RttyBaud_40m`, `RttyShift_40m`.
## Protocol Hints
Mark frequency and shift are controlled via slice properties in the
SmartSDR protocol:
C|slice set digl_offset=
`digl_offset` carries the mark audio offset in Hz. The shift (mark-to-space
distance) may be exposed as a separate field — verify against
`slice get <id>` output in DIGL mode.
Audio for decoding is already available in `AudioEngine::feedAudioData()` at
24 kHz (PCC 0x03E3) before the NR2/RN2 pipeline. The RTTY decoder should tap
the **pre-NR** audio (same tap point as `CwDecoder::feedAudio()`), as noise
reduction can distort FSK timing.
**Recommended decoder library:** [librtty](https://github.com/df9ry/librtty)
or a self-contained implementation based on the fldigi RTTY modem source
(`rtty.cxx`). Alternatively, a simple mark/space bandpass-filter + envelope
detector approach (similar to `CwDecoder`'s envelope approach) can be written
from scratch in ~300 lines.
No special FlexLib/protocol command is needed for decoding — it is entirely
client-side audio processing, identical in architecture to `CwDecoder`.
## Acceptance Criteria
- [ ] In DIGL/RTTY mode, a decode text panel appears automatically and
scrolls decoded Baudot text in real time at 45.45 baud / 170 Hz shift
with the radio tuned to an active RTTY signal.
- [ ] Baud rate, shift, and polarity are user-configurable via a RTTY tab
in `RxApplet` or `VfoWidget`, and settings persist across restarts via
`AppSettings`.
- [ ] The decoder taps audio before NR2/RN2 processing (same pipeline as
`CwDecoder`) and does not affect audio output in any way.
- [ ] The decode panel is hidden in non-RTTY modes and shown automatically
when mode switches to DIGL/RTTY (mirrors CW decoder auto-show behavior).
- [ ] No external processes, DAX routing, or virtual audio devices are
required for basic RTTY decode to function.
Suggested labels: enhancement, audio, GUI
Note: Issue #660 ("RTTY Mark/Space Overlay Lines Drawn at Wrong Frequency") is related but distinct — it's a positioning bug for the existing visual overlay. This request is for the decode pipeline itself. Resolving #660 is a prerequisite for the spectrum overlay part of this feature (acceptance criterion bullet 4 implicitly depends on it), so it would be worth referencing #660 in the issue body when you file it.
Add Built-in RTTY (FSK/AFSK) Decoder with Text Output Panel
Labels:
enhancement,audio,GUIC|slice set digl_offset=
Suggested labels:
enhancement,audio,GUINote: Issue #660 ("RTTY Mark/Space Overlay Lines Drawn at Wrong Frequency") is related but distinct — it's a positioning bug for the existing visual overlay. This request is for the decode pipeline itself. Resolving #660 is a prerequisite for the spectrum overlay part of this feature (acceptance criterion bullet 4 implicitly depends on it), so it would be worth referencing #660 in the issue body when you file it.