Skip to content

AetherLink — Integrated Mobile Remote Server with Low-Bandwidth Transport + Android Client #3128

Description

@mike6000

Request preparation

  • I used an AI assistant to help structure this request
  • I checked for existing issues covering the same feature

What would you like?

[Feature] AetherLink — Integrated Mobile Remote Server with Low-Bandwidth Transport + Android Client


What

Add two tightly coupled components to the AetherSDR ecosystem:

1. AetherLink server — a built-in server module in AetherSDR (shack-side) that exposes a mobile-optimized remote protocol over WAN/LTE/5G. AetherSDR, already consuming the full VITA-49 streams from the radio, becomes the intelligent relay: compressing waterfall data, transcoding audio to Opus, managing sessions, enforcing TX/RX permissions, and handling reconnection and NAT traversal.

2. AetherLink Android app — a companion Android application that connects to the AetherLink server and provides a touch-optimized SDR remote control interface: panadapter with gesture tuning, PTT, audio routing, and background operation over mobile data.

The resulting architecture is:

FlexRadio
    ↓  SmartSDR / VITA-49 (LAN, full bandwidth)
AetherSDR + AetherLink server  (shack PC)
    ↓  AetherLink protocol (WAN/LTE, ~80 kbps)
AetherLink Android app

Raw TCI is not involved. AetherSDR already speaks the Flex protocol natively; AetherLink is a new outward-facing layer, not a wrapper around someone else's protocol.


Why

The raw VITA-49 stream from a FlexRadio — panadapter FFT tiles plus audio — runs at approximately 1.9–2 Mbps at full resolution. That bandwidth is fine on a gigabit LAN. It is completely impractical over an LTE uplink, a congested 5G cell, or a mobile data plan. Without compression and QoS, remote SDR operation over a mobile connection degrades or fails entirely: audio glitches, waterfall freezes, dropped connections on cell handoffs.

The existing SmartLink pathway (Flex's own cloud relay) forwards the raw protocol uncompressed and is therefore subject to the same bandwidth constraints. It also requires a Flex cloud account and is not under AetherSDR's control.

The ON7OFF TCI Remote system (see Prior Art) has demonstrated that a purpose-built compression and transport middleware layer can reduce the stream from ~2 Mbps to ~80 kbps — a 25× reduction — while maintaining a usable waterfall, real-time audio, and reliable PTT over LTE. That system proves the approach works in practice. The architecture described here adapts those principles natively into AetherSDR, where they are a significantly better fit: AetherSDR already holds all the data, does all the rendering, and owns all the state. It is the natural place to serve remote clients without any additional intermediary software.

Pain points without this feature:

  • Mobile operation over LTE/5G with a FlexRadio requires either Flex SmartLink (cloud-dependent, bandwidth-constrained, no compression) or a third-party hardware/software appliance like a RemoteRig or an ON7OFF Compactor setup (complex, TCI-specific, not native to AetherSDR)
  • AetherSDR's existing remote capability is limited to LAN or well-provisioned broadband — it does not adapt to constrained mobile connections
  • There is no permission model for shared remote access — a guest operator cannot be given RX-only access
  • No Android client exists for AetherSDR or for direct FlexRadio operation on mobile; operators are forced into workarounds

Prior Art: ON7OFF TCI Remote + Compactor (for reference only, we are making something else here that is more integrated, open source, is made for the Flex type of radio).

The ON7OFF system (by Luc ON7OFF) is the closest prior art and the primary inspiration for this feature. It consists of two components:

TCI Remote Compactor (shack-side) — a proxy/middleware layer that sits between the TCI server (e.g. Thetis/ExpertSDR) and the internet. It provides: stream compression (raw TCI ~2 Mbps → ~80 kbps), audio prioritization and QoS, packet loss recovery, automatic reconnection on cell handoff, NAT traversal and tunneling, session authentication, multi-user coordination with TX/RX privilege separation, an intercom overlay channel, and proxying of local shack web interfaces.

Android TCI Remote app — a touch-optimized UI providing panadapter gestures, PTT, audio routing, and background audio via WebRTC/Opus internally.

The Compactor is a separate program. It is not a VPN or a generic tunnel — it is a domain-aware transport layer that understands which packets are audio (high priority, loss-intolerant), which are waterfall tiles (compressible, loss-tolerant), and which are control commands (low-volume, must not be dropped), and treats them differently.

The key architectural difference with AetherLink: the ON7OFF Compactor must speak TCI because it fronts a TCI server it does not own. AetherSDR owns its own data pipeline from the radio upward. AetherLink can therefore define a cleaner, more efficient mobile protocol from scratch — it does not need TCI compatibility at all. The Android app similarly has no TCI constraint and can be designed ground-up for the AetherLink protocol, avoiding the WebRTC/PTT hardware mapping limitations ON7OFF encountered on Android.

We would like fully open source implementation just like AetherSDR for both the server functionality embedded into aetherSDR and for the Android app. This way several developers and a huge community can maintain and develop this critical technology.


Part 1: AetherLink Server (shack-side, integrated into AetherSDR)

Three-channel session model

A server module listening on a configurable port pair (default: 4992/TCP + 4993/UDP). TLS mandatory; self-signed cert generated on first run, with optional ACME/Let's Encrypt for operators with a domain. Three logical channels per client session, multiplexed over the same connection:

Control channel (TCP, reliable) — session setup, authentication, radio command forwarding (frequency, mode, filter, AGC, etc.), TX/RX permission negotiation, reconnection handshake.

Audio channel (UDP, prioritized) — RX audio encoded with Opus at a configurable bitrate (default: 24 kbps, range: 8–64 kbps). TX audio from the Android mic: same codec, opposite direction. Jitter buffer on the client side.

Waterfall channel (UDP, lossy-tolerant) — waterfall strips encoded as compressed delta tiles. The server renders waterfall rows (it already does this for the local GPU renderer), encodes each new row as a delta against the previous, and sends tiles at a client-configurable rate (default: 5 fps for mobile, up to 15 fps on WiFi) with WebP or RLE palette compression. Lost tiles are simply not rendered; no retransmit needed.

Bandwidth budget

At default settings (24 kbps Opus, 5 fps waterfall, 400 kHz span):

Stream Bitrate
Opus audio RX 24 kbps
Opus audio TX mic 16 kbps
Waterfall tiles ~25–35 kbps
Control channel < 2 kbps
Total ~70–80 kbps

Comfortable within a typical LTE uplink even under modest congestion.

Reconnection and NAT traversal

Cell handoffs cause brief IP changes that kill raw TCP. AetherLink handles this with:

  • A session token issued at authentication, valid for a configurable window (default: 60 s). Reconnecting with the same token resumes the session transparently — no re-auth, no re-negotiation
  • A keepalive heartbeat on the UDP audio channel (every 500 ms) for fast dead-session detection
  • STUN support for clients behind symmetric NAT. Configurable STUN server (default: stun.l.google.com:19302); the discovered public endpoint is embedded in the connection QR code
    AppSettings::setValue("AetherLink/StunServer", "stun.l.google.com:19302")

Authentication and multi-user

Local user database (stored in AetherSDR config directory, never in the cloud):

Username     TX allowed   Max bitrate   Intercom   Note
-----------  -----------  ------------  ---------  ----------------
operator     yes          64 kbps       yes        primary op
guest        no           24 kbps       no         RX-only listener
club_member  yes          48 kbps       yes        —

TX arbitration when multiple TX-permitted users are connected: configurable first-come/lockout-timer or operator-override-only policy. Only one user holds TX at a time; the server enforces this hard.

Intercom channel: users with intercom permission transmit a separate 16 kbps Opus stream mixed into other connected users' RX audio but not keying the radio — shack coordination without going on-air.

Local web UI served on a configurable port (default http://localhost:4994) for user management. Optionally proxied to connected clients over the control channel (matching the ON7OFF Compactor's web UI tunneling feature).

AetherLink settings tab (AetherSDR desktop)

AetherLink Remote Server
 
  [x] Enable AetherLink server
 
  Listen port:      [ 4992      ]
  Web UI port:      [ 4994      ]
  TLS certificate:  [● Auto-generated]  [○ Custom path: ________]
 
  STUN server:      [ stun.l.google.com:19302 ]
  Session resume:   [ 60 s ▾ ]
 
  Users:            [+ Add user]
  ┌────────────────────────────────────────────────────┐
  │ operator    TX ✓   64 kbps   [Edit] [Delete]       │
  │ guest       TX ✗   24 kbps   [Edit] [Delete]       │
  └────────────────────────────────────────────────────┘
 
  TX arbitration:   [● First-come / lockout timer: 30 s]
                    [○ Operator override only           ]
 
  Intercom:         [x] Enable intercom channel
 
  Connect QR code:  [Show QR]   aetherlink://...

The QR code encodes the server's public IP (discovered via STUN), port, and a one-time invite token — scanning it on Android pre-fills the connection screen.

Interaction with existing code (server side)

  • Waterfall data from PanadapterStream VITA-49 frames — same source as SpectrumWidget, no new radio subscriptions
  • Audio from AudioEngine RX pipeline; TX audio injected into existing DAX TX / AudioEngine TX path
  • Radio commands forwarded via existing RadioConnection command path — no new protocol commands toward the radio
  • TLS via Qt Network (already a dependency)
  • Opus codec: libopus (add as build dependency; BSD licence, widely packaged)
  • STUN: lightweight client only, no additional library required

Part 2: AetherLink Android App

A companion Android application distributed via Google Play and as a direct APK. Open-source, same GPLv3 licence as AetherSDR. Separate repository under the aethersdr GitHub organisation (e.g. aethersdr/AetherSDR-Android), with the AetherLink client protocol spec as the shared contract between the two projects.

Connection setup

On first launch the app presents a connection screen. The operator either:

  • Scans the QR code displayed in AetherSDR's AetherLink settings tab (pre-fills everything), or
  • Enters server address, port, username, and password manually
    Multiple saved connections supported (for operators with more than one radio or station). Each saved connection displays last-seen signal level and last-connected timestamp on the home screen.

Main operating screen layout

Designed for one-handed phone use in portrait and landscape. Key principle: PTT and audio must be reachable without repositioning the hand.

Portrait layout:

┌─────────────────────────────────────────┐
│  14.074.500        USB        S7  ██░░░ │  ← frequency / mode / S-meter
├─────────────────────────────────────────┤
│                                         │
│         WATERFALL / PANADAPTER          │  ← touch to tune (pinch = span)
│                                         │
│  ◀──────────●──────────▶               │  ← VFO line, draggable
│                                         │
├─────────────────────────────────────────┤
│  [AF ──●──────] [BW ──────●──] [AGC ▾] │  ← audio / filter / AGC
├─────────────────────────────────────────┤
│           ████  PTT  ████               │  ← large PTT, full width
└─────────────────────────────────────────┘

Landscape layout — waterfall takes the left ~60% of screen; controls and PTT stack on the right.

Panadapter gestures

Gesture Action
Single tap Tune VFO to tapped frequency
Horizontal drag Pan spectrum left/right
Pinch / spread Adjust span (zoom in/out)
Two-finger tap Set centre frequency to current VFO
Long press Drop a temporary marker; tap marker for frequency readout
Double tap Jump to band centre

Waterfall scroll direction configurable (up = newer or down = newer). Waterfall colour palette matches the desktop AetherSDR palette selected by the operator.

PTT

A large, high-contrast PTT button occupying the full width of the bottom of the screen. Two interaction modes (configurable):

  • Hold to transmit — TX active while finger is down, releases on lift
  • Toggle — first tap keys TX, second tap releases; displays a prominent red TX indicator while active
    Hardware PTT supported via:
  • Android volume buttons (configurable: volume-up or volume-down)
  • Bluetooth HID button (e.g. a remote shutter button or dedicated Bluetooth foot switch)
  • Wired headset button (TRRS mic button)
    TX lockout indicator: if the server denies PTT because another user holds TX, the button flashes amber and displays TX BUSY.

Audio routing

On connect, the app requests Android AUDIOFOCUS_GAIN and routes RX audio to the earpiece (phone held to ear) or speaker (phone flat), switching automatically based on proximity sensor. Bluetooth audio devices (headsets, earpieces) are enumerated and selectable from a quick-access menu.

TX audio captured from the device microphone (or Bluetooth headset mic) and encoded to Opus before transmission. The app applies Android's WebRtcAudioProcessing (echo cancellation, noise suppression, automatic gain) to the TX audio path — preventing the operator's own RX audio from being picked up and transmitted back.

Background operation: a persistent foreground Service with a notification keeps the audio session alive when the app is backgrounded or the screen locks. The notification shows current frequency, mode, and RX signal level, and includes a PTT button for direct PTT from the notification shade. This is the correct Android pattern for real-time audio apps and ensures the OS does not kill the session during a QSO.

Frequency and mode controls

  • Frequency entry: tap the frequency display to open a numeric keypad; enter in kHz or MHz
  • Band selector: a horizontal scrollable chip strip (160m 80m 60m 40m … 10m 6m) — tapping a chip tunes to that band's default frequency and switches the panadapter span to match
  • Mode selector: a compact button strip (LSB USB CW AM FM DIGI) — tap to change mode; current mode highlighted
  • Step tuning: hardware volume buttons optionally act as step-up/step-down tuning when not assigned to PTT (configurable step: 10 Hz / 100 Hz / 1 kHz / 10 kHz)

RX controls

  • AF gain: slider, 0–100%
  • Filter bandwidth: slider, with named presets (Wide / Normal / Narrow / CW) shown above the slider
  • AGC: dropdown (Fast / Med / Slow / Off)
  • NR / NB / ANF: toggle buttons, state synced from radio
  • Squelch: slider, shown only for FM/AM modes
    All controls map to existing SmartSDR slice parameters via the AetherLink control channel — no new radio protocol commands needed.

S-meter and signal display

A horizontal bar S-meter at the top of the screen, showing S-units with a numeric dBm readout. Updates at the same rate as the desktop AetherSDR S-meter. In TX mode, the bar switches to a power output / ALC indicator.

Intercom

When the server has intercom enabled and the user has intercom permission, a secondary IC button appears next to PTT. Hold IC to transmit to other connected users over the intercom channel without keying the radio. An intercom RX indicator appears when another user is on the intercom channel.

Connection resilience

The app handles cell handoffs transparently using the AetherLink session token mechanism. On detecting a connection loss (keepalive timeout), it immediately begins reconnecting, displaying a non-blocking Reconnecting… banner at the top of the screen. The UI remains responsive during reconnection — the operator can still see the last waterfall state and their current frequency. Audio resumes within one Opus buffer period of reconnection. No user action required.

Settings screen

Connection
  [Manage saved connections]
  Auto-reconnect on network change  [x]
  Session resume window             [60 s ▾]
 
Audio
  RX output:        [● Earpiece / speaker auto]  [○ Always speaker]
  Bluetooth device: [None ▾]
  TX echo cancel:   [x]
  TX noise suppress:[x]
 
PTT
  Interaction mode: [● Hold]  [○ Toggle]
  Hardware PTT:     [● Volume up]  [○ Volume down]  [○ Headset button]
 
Waterfall
  Colour palette:   [Matches desktop ▾]
  Scroll direction: [● Newer at bottom]  [○ Newer at top]
  Frame rate:       [5 fps ▾]  (reduce to save data)
 
Data usage
  Audio bitrate:    [24 kbps ▾]
  Show data counter:[x]

Data usage indicator

A small persistent readout in the corner of the main screen showing current session data consumption (kbps in/out and total MB transferred). Useful for operators on metered mobile plans.

Minimum Android version

Android 10 (API 29) — covers >90% of active Android devices as of 2026. Targets Android 15 (API 35). No proprietary SDKs; all dependencies open-source.

Technology stack

  • Kotlin + Jetpack Compose (modern Android UI toolkit; avoids legacy View system)
  • AetherLink protocol client: pure Kotlin coroutines over TCP/UDP sockets — no WebRTC dependency (avoiding the PTT hardware mapping limitations the ON7OFF app encountered)
  • Opus audio: libopus via Android NDK JNI wrapper (same library as the server side)
  • TLS: Android's built-in SSLSocket / SSLEngine
  • Background audio: Android MediaSession + foreground Service
  • Build: Gradle, distributed as APK and via Google Play open testing track

Phasing suggestion

Given the scope, a suggested delivery order:

  1. AetherLink server (no client yet) — allows third-party clients and testing with curl/netcat
  2. AetherLink Android app — RX only — waterfall, audio, tuning; no TX
  3. TX + PTT — once RX path is stable
  4. Multi-user + intercom — after single-user operation is solid

Labels: feature networking remote audio android priority: high

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUIUser interfaceNew FeatureNew feature requestSmartLinkSmartLink WAN remote connectionaudioAudio engine and streamingenhancementImprovement to existing featuremaintainer-reviewRequires maintainer review before any action is takenpriority: lowLow priorityprotocolSmartSDR 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