Skip to content

Native Soft-TNC + APRS Integration #2701

Description

@ten9876

Claude here on Jeremy's behalf.

Summary

Add a native software TNC (Terminal Node Controller) inside AetherSDR so packet radio, KISS, and APRS become first-class features — no Direwolf bridge, no virtual audio cables, no external hardware TNC required.

AetherSDR already has every piece needed: per-slice 24 kHz post-demod audio, TX audio injection (used by WAVE / PUDU monitor), PTT control, settings infrastructure, applets, and the PersistentDialog pattern. A soft-TNC turns APRS into a one-click feature.

This issue tracks the umbrella plan. Subtasks below will be filed as separate issues once Phase 1 design is locked.

Why native (not a Direwolf bridge)

Option Pros Cons
Native soft-TNC (this plan) Tight PTT/audio timing, single integrated UI, no audio routing pain, no external deps ~3 weeks of DSP work
Direwolf bridge Ships in a weekend, inherits Direwolf's tuning Runtime dep, PipeWire/PulseAudio routing, looser PTT timing, two UIs to learn

We're going native because packet TX timing matters (the radio's PTT-attack window is unforgiving) and because keeping the user experience inside one app is the project's stated goal.

Scope for v1

  • Modulation: Bell 202 1200-baud AFSK (VHF APRS, ~99% of traffic) + 300-baud HF AFSK (30 m HF APRS — same demod, different constants).
  • Layer 2: AX.25 v2.0 UI-frame TX/RX (APRS uses UI almost exclusively).
  • KISS server: TCP on port 8001, multi-client, so Xastir / YAAC / kissattach / APRSdroid (via reverse-tunnel) work as external apps.
  • APRS layer: Position/Message/Status/Object/Weather/Mic-E parser + builder.
  • Native UI: applet tile, map dialog (QtLocation/OSM), message log, station list, settings dialog.
  • Smart Beaconing: HamHUD-style variable-rate scheduler from day one.
  • Auto-create FM slice when TNC is first enabled (144.39 MHz, 15 kHz BW, named APRS).
  • Non-destructive audio tap: TNC copies slice audio, RX continues to speakers normally.
  • APRS-IS uplink/downlink: optional Tier-2 server connection (rotate.aprs2.net:14580) so AetherSDR acts as an i-gate.

Architecture

```
RadioModel / SliceModel (existing)
↓ 24 kHz post-demod audio per slice
AudioEngine
↓ tap @ TNC slice
ClientTnc (lock-free atomics, AudioEngine thread)
AFSK demod → HDLC bits → AX.25 frame
AX.25 frame → HDLC bits → AFSK mod (TX)
↓ queued signal
KissTcpServer + AprsModel

AprsApplet / AprsMapDialog / AprsMessagesDialog / AprsStationListDialog
```

DSP runs on the existing AudioEngine thread alongside RX audio processing — no new threads. KISS TCP server is a parallel integration path so users who already use Xastir or YAAC keep their workflow.

Files (v1)

File Change
`src/core/ClientTnc.{h,cpp}` NEW — AFSK demod/mod + HDLC + AX.25 frame in/out
`src/core/Ax25Frame.{h,cpp}` NEW — frame parser/builder, address/control/FCS
`src/core/HdlcCodec.{h,cpp}` NEW — NRZI, bit-stuff, flag-sync, FCS-16
`src/core/KissProtocol.{h,cpp}` NEW — KISS frame encode/decode (no I/O)
`src/core/KissTcpServer.{h,cpp}` NEW — QTcpServer wrapping KissProtocol
`src/core/AprsParser.{h,cpp}` NEW — info-field parser (position, message, weather, Mic-E…)
`src/models/AprsModel.{h,cpp}` NEW — station registry, message log, beacon scheduler
`src/core/AprsIsClient.{h,cpp}` NEW — optional uplink to APRS-IS Tier 2
`src/core/AudioEngine.{h,cpp}` Wire ClientTnc tap into per-slice audio path
`src/gui/AprsApplet.{h,cpp}` NEW — applet tile
`src/gui/AprsMapDialog.{h,cpp}` NEW — QtLocation map with station markers
`src/gui/AprsMessagesDialog.{h,cpp}` NEW — chat-style message log
`src/gui/AprsStationListDialog.{h,cpp}` NEW — heard-stations table
`src/gui/AprsSettingsDialog.{h,cpp}` NEW — callsign, path, beacon, KISS, APRS-IS
`tests/ax25_frame_test.cpp` NEW
`tests/hdlc_codec_test.cpp` NEW
`tests/kiss_protocol_test.cpp` NEW
`tests/aprs_parser_test.cpp` NEW
`tests/client_tnc_test.cpp` NEW — loopback: frame → mod → demod → parse

Phasing — three shippable releases

v26.6.0 (Phases 1–3) — AetherSDR is a KISS TNC

  • Phase 1: Protocol core (HDLC + AX.25 + KISS as a pure library, fully unit-tested, no audio).
  • Phase 2: AFSK demodulator + RX path tap; decoded frames in a log.
  • Phase 3: KISS TCP server; Xastir / YAAC / kissattach work as external clients.

v26.7.0 (Phase 4) — TX works

  • AFSK modulator, TX state machine (KeyUp → TxDelay → Frame → TxTail → KeyDown), CSMA collision avoidance, PTT integration via existing TX path.
  • External KISS clients can beacon and send messages through AetherSDR.

v26.8.0 (Phases 5–7) — Full native APRS

  • APRS info-field parser (including Mic-E).
  • Native UI: applet, map, message log, station list, beacon scheduler.
  • APRS-IS uplink/downlink with passcode and filter expression.

Implementation details

Phase 1 — Protocol core

Pure C++, no Qt except `QByteArray`. Fully testable in CI without audio.

HDLC gotchas (these are 2-hour debugging sessions if wrong):

  • Bit order on the wire: LSB first within each byte.
  • NRZI: 0 = transition, 1 = no transition.
  • Bit stuffing: insert 0 after five consecutive 1s in payload — but NOT inside flag bytes.
  • FCS: CRC-16-CCITT, init 0xFFFF, polynomial 0x1021, LSB-first, byte-reversed, complemented. Lots of wrong CRC code online; copy from Direwolf's `fcs_calc.c` and verify against canonical test vectors.

AX.25 address gotchas:

  • Each callsign byte left-shifted 1 bit.
  • 7th byte is SSID + bits: `0bCxxxSSIDL` (C = command/response, L = last address).
  • "Has been digipeated" bit (H-bit) at top of each digipeater's SSID byte.

50+ unit tests across `ax25_frame_test`, `hdlc_codec_test`, `kiss_protocol_test`. Round-trip: build frame → toWire → HDLC encode → HDLC decode → parse → assert structural equality.

Phase 2 — AFSK demod

Bell 202: mark 1200 Hz, space 2200 Hz, 1200 bps, 24000/1200 = 20 samples/symbol at AetherSDR's native audio rate.

Demod approach: correlator-difference (most accurate; ~20-sample window of cos/sin references at 1200/2200 Hz, output bit metric = `|markCorr| - |spaceCorr|`). Symbol clock recovery via early-late gate, locks in 3-5 symbols (well within the flag preamble before every frame).

Phase 3 — KISS TCP server

`QTcpServer` on localhost:8001 by default (loopback only, LAN opt-in). Multi-client. Each client gets its own streaming KISS decoder. `KissTcpServer::txRequested` → queued signal → `ClientTnc::queueTx`.

Phase 4 — AFSK mod + TX path

Phase-continuous sin generation (discontinuous phase produces splatter outside the channel and breaks narrow-band filters). TX state machine: `Idle → KeyUp → TxDelay (flags) → Frame → TxTail → KeyDown`. PTT timing: assert PTT before audio starts, hold until after audio stops, reuse the existing interlock-confirmation path used by SSB/CW.

CSMA: p-persistent collision avoidance — when ready to TX and DCD clear, draw `rand(0..255)`; if < persistence (default 63), TX; else wait `slotTime` ms (default 100) and retry.

Phase 5 — APRS parser

Data-type byte selects format: `!`/`=` position, `/`/`@` position+timestamp, `>` status, `:` message, `;` object, `)` item, `_` weather, `T` telemetry, `?` query, `<` capabilities, `}` third-party.

Coordinate formats: standard `DDMM.MM[N/S]`, compressed base91, and Mic-E (lat encoded in destination callsign, lon in info field — ~30% of mobile traffic).

Phase 6 — Native UI

QtLocation for the map (approved dependency). PersistentDialog base for all popouts. Frameless mode respected.

Marker layer: bright icon for stations heard in last 30 min, dimmed for 24h. Click → station detail popup (last position, comment, path, packet count). Filters: All / Direct only / Via specific digi.

Message dialog: bidirectional chat-style log. Beacon scheduler implements full Smart Beaconing (see Maintainer Decisions §3) — variable rate by speed + corner-pegging on heading change. Fixed stations degrade naturally to a slow fixed rate.

Phase 7 — APRS-IS

Tier-2 connection (`rotate.aprs2.net:14580`). Standard login string with callsign-derived passcode (open algorithm, ref `aprslib.passcode()`). Filter expression like `r/47.6/-122.3/200` for 200 km area.

Uplink: local RF decodes forwarded to APRS-IS (i-gate function), q-construct `,qAR,N0CALL-1` appended.
Downlink: filtered packets back, map populates with stations beyond RF range.

Settings persistence

Per `AppSettings` conventions (PascalCase keys, `AetherSDR.settings` XML):

```
AprsTncEnabled, AprsTncSliceId, AprsCallsign, AprsSymbolTable, AprsSymbolCode,
AprsPath, AprsTxDelayMs, AprsTxTailMs, AprsSquelchDb,
AprsBeaconEnabled, AprsBeaconPeriodSec, AprsBeaconCommentText, AprsBeaconLat, AprsBeaconLon,
AprsBeaconPositionSrc (Fixed|RadioGps|Gpsd), AprsGpsdHost, AprsGpsdPort,
AprsSmartBeaconEnabled, AprsSmartBeaconRateMinSec, AprsSmartBeaconRateMaxSec,
AprsSmartBeaconLowSpeed, AprsSmartBeaconHighSpeed,
AprsSmartBeaconTurnMinDeg, AprsSmartBeaconTurnTimeSec, AprsSmartBeaconTurnSlope,
AprsKissEnabled, AprsKissPort, AprsKissBindAddr,
AprsIsEnabled, AprsIsServer, AprsIsPort, AprsIsFilter
```

Edge cases

  • Slice mode: APRS needs FM with flat audio response. Force the TNC slice into FM (or PKT mode if available on the radio). Warn user if they switch out.
  • DCD: prefer "valid AFSK lock" over RSSI threshold for carrier detection — more accurate.
  • TX while another stage is keyed: ClientTnc must check existing TX-busy state before requesting PTT. Queue frame until channel is free.
  • APRS-IS lost connection: exponential backoff (1s, 2s, 4s, … capped at 60s).
  • No callsign: TNC enabled but `AprsCallsign` blank → log warning, refuse to TX. RX still works.
  • Multi-radio: TNC tied to a specific slice on a specific radio. Settings key should be radio-scoped.

Verification (full system, v26.8.0)

  1. Tune to 144.39 MHz (US) or local APRS freq; slice = FM, 15 kHz BW.
  2. Enable TNC on that slice; set callsign.
  3. Within minutes, AprsMessagesDialog shows live decoded stations.
  4. AprsMapDialog populates with markers.
  5. Enable beacon at 5-min period; aprs.fi shows your callsign within ~6 min.
  6. Send a directed message to a known callsign; verify ACK in the log.
  7. Enable APRS-IS uplink; aprs.fi shows AetherSDR as the receiving i-gate for nearby packets.
  8. `kissattach /dev/ptmx ax0 ax25.conf` pointed at `localhost:8001`; `axlisten -ar` shows decoded frames.
  9. Point Xastir at "Networked KISS TNC, localhost:8001"; it decodes independently.

Estimated effort

  • Phase 1 (protocol core + tests): ~3-4 days
  • Phase 2 (AFSK demod + integration): ~2-3 days
  • Phase 3 (KISS server): ~1 day
  • Phase 4 (AFSK mod + TX): ~2-3 days
  • Phase 5 (APRS parser, inc. Mic-E): ~2 days
  • Phase 6 (UI): ~4-5 days
  • Phase 7 (APRS-IS): ~1-2 days
  • Integration, polish, docs: ~2 days

Total: ~17-22 working days, split across three releases.


Forward-looking enhancements (v2+, post-26.8.0)

Filing these here so they don't get lost. None are blocking v1; each could be a standalone issue when prioritised.

Modulation modes

  • 9600-baud G3RUH — flat-FM-discriminator scrambled NRZI. Needs a "raw FM audio" tap separate from the audio-channel filtering that 1200 AFSK uses. Doubles VHF/UHF packet throughput. Standard on satellites (AO-91, AO-92, ISS digipeater).
  • 300-baud HF AFSK — narrow-shift HF packet, see dedicated section below.
  • Robust Packet Radio (RPR) — high-value HF mode, see dedicated section below.
  • IL2P — Improved Layer 2 Protocol. Reed-Solomon FEC wrapper around AX.25 frames. Direwolf supports IL2P; FX.25 too. Significant range/reliability improvement on noisy channels.
  • IL2P-X4 — heavy FEC mode for emergency comms.
  • PSK-APRS — PSK63/PSK125 carrying APRS frames. Niche but used on HF.
  • Codec2 voice-over-packet — FreeDV-style digital voice carried in AX.25 UI frames.

Robust Packet Radio (RPR) — HF packet for poor conditions

RPR deserves its own subsection because it solves the biggest weakness of 1200 AFSK and 9600 G3RUH on HF: both fall apart below ~10 dB SNR, exactly the conditions where HF APRS operators actually need them. RPR is the de-facto HF packet standard for emergency comms in Europe, growing in North America, and is on the wishlist for almost every soft-modem project.

What it is

  • Developed by SCS GmbH (Special Communications Systems, Germany) — the same vendor as PACTOR.
  • 8-tone OFDM, 500 Hz total occupied bandwidth, ~200/600 bps gross with FEC.
  • Two speeds: RPR-200 (rock-solid below 0 dB SNR) and RPR-600 (faster, needs ~+3 dB).
  • Strong Reed-Solomon + Viterbi FEC; works under selective fading where 300-baud AFSK fails.
  • Compatible with the existing AX.25 / KISS layer — RPR replaces the PHY only. UI frames, addressing, FCS unchanged.

Hardware landscape today

  • SCS Tracker / DSP-TNC: ~€450, the canonical hardware modem.
  • SCS P4dragon (HF data modem): ~€1500, overkill for APRS.
  • Direwolf does NOT support RPR. Open feature request for years. This is the gap a native AetherSDR implementation closes for the whole soft-modem community.
  • A few research GNU Radio flowgraphs exist (DK7XE published reference designs); no production-quality open soft-modem.

Why AetherSDR is well-positioned

  • We already have per-slice 24 kHz audio, FFT infrastructure, Qt models for visualisation, and an OFDM-adjacent DSP mindset (the existing waterfall + filter code is in the same neighbourhood).
  • 8-tone OFDM at 50 baud per tone is computationally trivial vs the AFSK demod (smaller FFT, longer integration window — symbol rate is much slower).
  • Native PTT timing matters even more on RPR than on 1200 AFSK because the symbol period is ~20 ms; a 30 ms PTT-attack delay eats an entire symbol.

Frequencies (HF APRS RPR)

  • 10.147.6 MHz USB (30 m)
  • 14.103.0 MHz USB (20 m, most active)
  • 18.106.0 MHz USB (17 m)
  • 21.103.0 MHz USB (15 m)
  • 28.123.0 MHz USB (10 m)

(Note: USB dial; the 1500 Hz centre puts the RPR signal at the listed frequency + 1.5 kHz.)

Implementation sketch (when v2 work begins)

  • New ClientRpr DSP stage parallel to ClientTnc. Shares the audio tap point and KISS framing layer; the radio just sees a different PHY.
  • 8-FFT bin demod, hard or soft-decision per bin.
  • Viterbi decoder (free implementations available — Phil Karn's libfec is the canonical reference, MIT-licensed).
  • Reed-Solomon outer code (libfec again).
  • Synchroniser uses the unique-word preamble per the SCS spec.

Effort estimate

  • Modem PHY (demod + sync + FEC): ~5-7 days. The FEC libraries do the heavy lifting; the trick is sync and equalisation under fading.
  • Modulator: ~2-3 days. OFDM TX is conceptually simpler than the demod.
  • AetherSDR integration (slice settings, applet mode toggle, settings): ~1-2 days, since the AX.25/KISS/APRS layers above already exist by then.
  • Field testing across HF bands and propagation conditions: open-ended. Plan on 2-3 weeks of operator-feedback iteration before declaring v1 RPR stable.

Total: ~2-3 weeks of focused work, post-v26.8.0 (after the AFSK/AX.25/KISS/APRS foundation is in place).

Spec / reference material

  • SCS Robust Packet documentation (download from scs-ptc.com).
  • Patrick Lindecker's (F6CTE) RPR analysis articles.
  • DK7XE GNU Radio reference flowgraphs.
  • Phil Karn's libfec for Viterbi + Reed-Solomon.

Open question: should RPR be a v2.0 milestone (post the v1 APRS releases) or interleaved into v26.8.0? The HF emergency-comms community would benefit from it sooner, but it's a much bigger DSP undertaking than 1200 AFSK and would push the v1 timeline.

300-baud HF AFSK — the classic 30 m HF APRS mode

If RPR is the "emergency-comms heavyweight" HF mode, 300 baud AFSK is the workhorse — it's been the standard HF APRS modulation since the 1990s, every existing hardware TNC (KAM, KPC, PK-232, MFJ-1278, TNC-X) supports it, and there's an active worldwide HF APRS i-gate network using it on 30 m specifically. Native AetherSDR 300-baud support brings the world's most popular HF APRS network online with effectively zero additional DSP code over what Phase 2 already builds.

Why 30 m is the canonical band

  • Quiet, narrow, regulated for digital-only above 10.140 MHz (no SSB QRM).
  • Propagates well day and night, low absorption, modest QSB.
  • The single HF band where APRS has continuous activity globally — North America, Europe, Australia, South America all converge on 30 m.

Frequencies (HF APRS 300-baud)

  • 10.151.00 MHz LSB — North America / VK (most active worldwide, this is where i-gates live).
  • 10.147.30 MHz USB — Europe (Region 1).
  • 7.040.00 MHz LSB — 40 m, secondary (sparse activity).
  • 14.105.00 MHz LSB — 20 m, regional/experimental.

LSB vs USB matters because the mark/space tones are at audio frequencies (1600/1800 Hz typical) and flipping sideband flips which audio tone maps to mark vs space — get it wrong and the radio decodes nothing.

Modulation details

  • 300 bps AFSK, Bell 103-derived.
  • Mark 1600 Hz, space 1800 Hz (200 Hz shift — fits HF's narrow channel).
  • Same AX.25 / HDLC / KISS upper layers as VHF 1200 AFSK — only the PHY changes.
  • 24000 / 300 = 80 samples per symbol — 4× the integration window of 1200 AFSK, which means better noise performance for free.

Why it's almost free given the v1 codebase
The v1 `ClientTnc` already builds a correlator-difference Bell 202 demod. 300-baud HF is the same demod with three constants changed:

Constant 1200 AFSK (Bell 202) 300 AFSK (Bell 103-style HF)
Mark freq 1200 Hz 1600 Hz
Space freq 2200 Hz 1800 Hz
Samples/symbol 20 80
Shift 1000 Hz 200 Hz

Strategy: extend `ClientTnc::prepare()` to accept a "PHY profile" enum (`Bell202_1200`, `Hf300`, and later `G3RUH_9600`); the rest of the modem stays identical.

TX considerations

  • HF SSB transmitters have ~3 kHz audio bandwidth — both tones fit easily, no concern.
  • Phase-continuous mod is still required.
  • TXDelay should be longer than VHF (HF ALC / amplifier settle times) — recommend 500-800 ms vs 300 ms on VHF. Make it per-PHY in settings.
  • HF QSB / fading: TX retransmission strategy (send the frame 2-3× back to back) is common practice — should be a togglable option.

Effort estimate

  • PHY profile enum + constant swap: ~half a day.
  • Audio-bandwidth filter retuning (the AFSK bandpass needs to centre on the new tones): ~half a day.
  • HF-specific TX timing tuning + settings UI: ~1 day.
  • Field testing on 30 m: a few evenings of i-gate audibility tests.

Total: ~2 days of focused work, layered on top of the v1 `ClientTnc`. Realistically lands in v26.7.0 or v26.8.0 alongside the rest of the APRS stack — no good reason to defer to v2.

Slice configuration for HF

  • Mode: LSB (or USB depending on band/region).
  • Filter: 500 Hz tight filter is ideal (narrows out QRM).
  • Centre audio tone target: ~1700 Hz (midway between 1600/1800).
  • The Settings dialog should ship preset buttons: "30 m HF APRS (NA)", "30 m HF APRS (EU)", "144.39 VHF APRS" — one-click sideband + tone + freq + filter + TXDelay setup.

Why this matters in the v1 phasing
Adding 300 baud unlocks HF APRS for every AetherSDR user with an HF-capable Flex (which is all of them — even the 6300/6400/6500 series cover HF). The user base for HF APRS is small but passionate (Pacific Northwest fire-net ops, sailing cruisers, off-grid operators), and it's an obvious differentiator vs Direwolf-bridge setups that struggle with sideband-dependent audio routing.

Layer 2 / connected mode

  • AX.25 v2.2 connected mode — SABM/UA/I-frames/RR/REJ/SREJ + T1/T2/T3 timers. Required for Winlink BBS, packet BBS (DXSpider, F6FBV, fBB), and any "connect to a remote node" workflow. ~2-3 weeks of additional work (state machine is non-trivial; v2.2 vs v2.0 differences are tricky).
  • NET/ROM — packet routing layer above AX.25. Old but still in use by mesh nets.
  • ROSE / FlexNet — alternative L3 routing protocols.

Multi-port / multi-radio

  • Multi-port TNC — simultaneous decode on multiple slices. HF APRS on 30 m + VHF APRS on 144 + UHF on 440 all at once. Each port gets its own KISS sub-channel (KISS supports 16 ports natively).
  • Cross-band repeating — RX on one slice, retransmit on another.
  • Multi-radio TNC — one TNC instance spanning multiple connected radios.

Other TNC protocols

  • AGW Packet Engine (AGWPE) compatibility — many older APRS clients (UI-View32, APRSpoint, some Winlink versions) speak AGWPE, not KISS. Adding an AGWPE server alongside the KISS server picks up that audience.
  • AGW Express / Async Packet — variants in the wild.
  • BPQ / Linux AX.25 kernel modules — direct integration with the in-kernel AX.25 stack instead of going through KISS.

Internet bridges

  • Winlink integration — VARA HF/FM modem bridge or RMS Express compatibility. Big lift but high-value for emcomm operators.
  • JS8Call APRS bridge — JS8Call has an APRS bridge mode; we could speak its API natively.
  • DMR APRS bridge — relay APRS packets to/from BrandMeister APRS network.
  • Yaesu System Fusion APRS — C4FM APRS messages over Wires-X.
  • D-STAR DPRS gateway — DPRS is APRS-over-D-STAR; relay to APRS-IS.
  • LoRa-APRS — uplink/downlink via separate LoRa hardware (RAK831, Heltec).
  • APRS-via-ISS — Doppler-aware beacon scheduler for the ISS digipeater (145.825 MHz, ±2.5 kHz Doppler over a pass).
  • APRS over Reticulum / mesh — bridge to mesh networking stacks (Reticulum, NextNet).
  • CWOP integration — Citizens Weather Observer Program submission (weather-only APRS-IS server).

APRS feature depth

  • Telemetry decoder + grapher — APRS T-type packets (5 analog + 8 digital channels). Plot history.
  • Weather station integration — read Davis/Acu-Rite/etc. via existing weatherd/wview, format as APRS WX packets.
  • Bulletin / announcement support — BLN1..BLN9, NWS weather bulletins, general announcements.
  • Object/Item tracking — moving objects, `?APRSO` query response.
  • GPS integration — connect to gpsd or serial NMEA for live position beaconing.
  • Position privacy / fuzzing — round coordinates to N km for privacy-conscious users.
  • Custom symbol overlays — extended symbol table characters, custom icons.
  • Position ambiguity — APRS spec ambiguity levels (10s, 1°, 0.1°, exact).
  • Net coordinator mode — track checked-in stations during a net by APRS position.
  • Auto-reply / auto-ack canned messages — "off the grid", "busy", etc.
  • Heard-list export — CSV / ADIF / JSON of all heard stations.

UI enhancements

  • OpenStreetMap offline tiles — packaged tile bundles for off-grid / portable operation.
  • Map themes — dark mode tiles, terrain, satellite imagery (where licensed).
  • Trail history — draw past-N-hours track for moving stations.
  • Distance / bearing widget — to selected station from operator's QTH.
  • Heatmap overlay — coverage analysis over time.
  • Replay mode — scrub backward through the message/position log.
  • Audio waveform of decoded packet — debug aid; play back the captured audio.
  • Symbol picker — visual grid of all APRS symbols (replaces typing a code).
  • Path visualizer — show the digipeater chain for each decoded frame graphically.
  • Streaming APRS feed — websocket export so external dashboards / visualizations can subscribe.
  • Stream Deck tile — beacon-now button, new-message indicator (Jeremy's hardware supports this; see existing Stream Deck plugin work).
  • MIDI mapping — map MIDI controllers to APRS actions (beacon, send canned msg).
  • Voice / TTS announcements — speak callsign of new heard station, alert on directed message.
  • Hot stations notification — desktop notification when specific callsigns are heard.
  • Logbook integration — drop APRS-spotted stations into AetherSDR's ADIF log as informal QSOs (similar to existing DX cluster integration).

Protocol / standards work

  • APRS Spec 1.2 features — current spec compliance is 1.0.1; 1.2 adds Base-91 telemetry, extended addresses, etc.
  • OpenAPRS / OGN protocol — Open Glider Network protocol for aviation tracking (overlap with APRS).
  • AIS bridge — Automatic Identification System (marine) — same UI works, different L2.

Other digital modes adjacent to packet

  • MT63 — robust HF emergency comms mode (1000 Hz / 2000 Hz variants).
  • Olivia — 8/250, 32/1000, etc. — popular emcomm digital mode.
  • Throb / MFSK — exotic but lightweight modes.
  • RTTY / SITOR-B / NAVTEX — old-school FSK modes (could share the AFSK demod scaffolding).
  • FT4/FT8 APRS gateway — bridge JS8/FT8 grid-square spots into APRS positions.
  • PACTOR — proprietary but documented; reverse-engineered modems exist.

Hardware / driver

  • Hardware TNC passthrough — for users who want to keep their KPC-3+ or TNC-X, expose AetherSDR's audio path as a serial-equivalent so the hardware TNC can still drive it.
  • Soundmodem compatibility — old Linux soundmodem driver compatibility layer.

Maintainer decisions (locked in)

All five open questions answered. These decisions are load-bearing — re-read this section before starting Phase 1.

1. Default TNC slice — auto-create FM slice

When the user enables TNC for the first time and no AprsTncSliceId exists, AetherSDR creates a new slice on the radio: mode FM, 144.39 MHz (US default — region-configurable later), 15 kHz bandwidth, name APRS. The returned slice ID is saved as AprsTncSliceId.

  • RadioModel already exposes a slice-creation path (used by the "+ slice" toolbar) — hook into it.
  • On TNC disable, leave the slice alone. User removes it explicitly via SLICE menu. Auto-destroying surprises users.
  • If the saved slice ID no longer exists on the radio on next connect (user manually closed it), AprsApplet shows "TNC slice missing — re-enable to create". Don't auto-recreate on reconnect.
  • HF 300-baud presets ("30 m HF APRS NA / EU") override the auto-created slice's freq + mode + sideband when activated.

2. Map UI — QtLocation approved

  • find_package(Qt6 COMPONENTS Location Positioning REQUIRED) added to CMakeLists.txt in Phase 6.
  • .github/docker/Dockerfile adds qt6-location-dev plus runtime qml-module-qtlocation / qml-module-qtpositioning. Rebuild the CI image and wait for docker-ci-image.yml (~3 min) before the next CI run.
  • OSM tile plugin is the default; no API key needed.
  • Adds ~30 MB to runtime image; accepted.

3. Beacon scheduler — Smart Beaconing in v1

HamHUD-style variable-rate scheduler from day one, not deferred.

Setting Default Meaning
AprsSmartBeaconEnabled True Master toggle
AprsSmartBeaconRateMinSec 60 Fastest interval (high speed)
AprsSmartBeaconRateMaxSec 1800 Slowest interval (stopped)
AprsSmartBeaconLowSpeed 5 kn Below: use max-sec
AprsSmartBeaconHighSpeed 60 kn Above: use min-sec
AprsSmartBeaconTurnMinDeg 30 Min heading change for corner peg
AprsSmartBeaconTurnTimeSec 15 Deadband after corner peg
AprsSmartBeaconTurnSlope 240 kn × deg for turn threshold

Algorithm (1 Hz against position source):

  1. speed < LowSpeed → beacon every RateMaxSec.
  2. speed > HighSpeed → beacon every RateMinSec.
  3. Else: linear interp between min and max by speed.
  4. ALSO: if heading changed > TurnMinDeg + TurnSlope/speed since last beacon AND last beacon > TurnTimeSec ago → beacon immediately (corner peg).

Fixed stations (no GPS, speed=0): degrades to RateMaxSec (~30 min) — exactly what fixed users want. No special case needed.

Position source priority:

  1. Radio GPS slice (Maestro / 6700-series).
  2. gpsd on localhost:2947 (if reachable).
  3. Static fixed lat/lon from settings.

4. Tile cache — always fetch

QtLocation's OSM plugin caches tiles internally in ~/.cache/QtProject/.... No AetherSDR-managed tile storage. Document QtLocation's cache path in user docs so off-grid users can pre-warm before going offline.

5. Audio tap — always copy

ClientTnc::processRx() takes a const pointer to the slice's audio buffer. Slice's normal audio path (DSP chain → speakers, WAVE recording, etc.) is unaffected. Users hear AFSK bursts through speakers while the TNC decodes in parallel. No "mute while TNC enabled" option.


Related

  • Plan file: `~/.claude/plans/native-soft-tnc-aprs.md` (this issue is the public version of that plan).
  • Existing TX DSP stage pattern: PUDU / Reverb plans (reference for atomics + applet structure).

73, Jeremy KK7GWY & Claude (AI dev partner)

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUIUser interfaceNew FeatureNew feature requestaudioAudio engine and streamingenhancementImprovement to existing featuremaintainer-reviewRequires maintainer review before any action is takenprotocolSmartSDR protocol

    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