Skip to content

Publish CW decoded text to MQTT topic aethersdr/cw/decode #3215

Description

@K5PTB

What

When AetherSDR is connected to an MQTT broker and the CW decoder is
active, publish each decoded character to the topic
aethersdr/cw/decode as compact JSON:

{"text":"K","freq":14.025,"rx":true}

Fields:

  • text — one decoded character (or small chunk, as ggmorse emits it)
  • freq — active slice frequency in MHz (omitted if no slice is active)
  • rxtrue for the RX decoder, false for the TX sidetone decoder

The sensitivity threshold already configured in the CW panel
(m_cwCostThreshold) gates the publish, so the MQTT feed matches
exactly what the decoder window displays — no separate sensitivity
setting is needed.

Why

External tools — contest loggers, automated spotters, skimmers — need
access to the live CW decode stream. AetherSDR already has MQTT
infrastructure for station device control (antenna switches, band
decoders). Extending it to carry decoder output costs nothing in new
dependencies and requires no new port, no new protocol, and no
additional configuration beyond enabling MQTT.

How Other Clients Do It

  • SmartSDR — No external CW decode feed of any kind.
  • GQRX / SDR++ — No CW decoder; no MQTT.
  • WSJT-X / JTDX — Closest analogue: publish decoded QSO data via
    UDP broadcast (wsjtx_status, adif_log datagrams on port 2237).
    Same pattern — decode happens in-app, external tools subscribe.
  • AetherSDR — Already publishes nothing automatically; this would
    be the first outbound data topic.

Suggested Behavior

  1. When MQTT connects, AetherSDR begins publishing to
    aethersdr/cw/decode automatically — no opt-in toggle needed.
  2. Each textDecoded(text, cost) emission from CwDecoder that
    passes the active panel's m_cwCostThreshold is published
    immediately.
  3. The TX sidetone decoder (m_cwDecoderTx) publishes the same topic
    with "rx": false so subscribers can distinguish operator keying
    from received traffic.
  4. If no slice is active, freq is omitted rather than sending 0.
  5. When MQTT disconnects, publishing stops silently — no logged errors,
    no behavior change for users who don't use MQTT.

Protocol Hints

No FlexLib involvement — this is entirely within AetherSDR's client
process. Uses the existing MqttClient::publish(topic, payload) API
already present in src/core/MqttClient.h.

Implementation sketch:

// In MainWindow HAVE_MQTT block, after restoreConnectionState():
connect(&m_cwDecoder, &CwDecoder::textDecoded, this,
    [this](const QString& t, float cost) {
        publishCwDecodeMqtt(t, cost, true);
    });
connect(&m_cwDecoderTx, &CwDecoder::textDecoded, this,
    [this](const QString& t, float cost) {
        publishCwDecodeMqtt(t, cost, false);
    });

A working proof-of-concept lives on branch feat/mqtt-cw-decode-publish.
A minimal Python subscriber (FastAPI + aiomqtt + SSE → browser) confirmed
the pipeline end-to-end against a local Mosquitto broker.

Acceptance Criteria

  • Characters visible in the CW decoder panel appear on
    aethersdr/cw/decode within one character period
  • Characters suppressed by the sensitivity slider are NOT published
  • TX sidetone decoder output appears with "rx": false
  • freq contains the active slice frequency in MHz when a slice is active
  • No errors or warnings logged when MQTT is disconnected

Metadata

Metadata

Assignees

No one assigned

    Labels

    CWCW keying, decode, and operationNew FeatureNew feature requestmaintainer-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