Skip to content

feat(modem): APRS message-services picker on the Recipient field (#3569)#3828

Merged
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:fix/3569-aprs-services
Jun 26, 2026
Merged

feat(modem): APRS message-services picker on the Recipient field (#3569)#3828
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:fix/3569-aprs-services

Conversation

@jensenpat

@jensenpat jensenpat commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

What & why

Closes #3569. Adds an APRS message-services picker — a small ▾ caret button
next to the "To" field on the AetherModem APRS tab. It addresses the
well-known APRS gateway/auto-responder services without operators having to
memorize their callsigns and message-text syntax. These services already work
by manually typing the right destination + body; the picker just makes them one
tap away (the discoverability/usability win the issue asks for).

Selecting an entry:

  1. sets the destination callsign in To,
  2. seeds the message text with the correct command template and drops the
    cursor right after the prefix, and
  3. prints a one-line hint (what it does + the ~67-char limit) to the system log.

Services (grouped by category)

Category To Seeded body Notes
SMS SMS @ NA7Q gateway, @<10-digit#> <msg>; replies return as APRS (SMSGTE is shut down)
Email EMAIL-2 (empty) <email> <msg>; register by sending your address once
Winlink WLNK-1 ? APRSLink help / command list
Winlink WLNK-1 SMS one-line SMS <email-or-call> <msg>
Weather WXBOT (empty) City,ST / KICAO / grid / blank = last position
Satellite / ISS (unchanged) (unchanged) routing-only — shows an ARISS/RS0ISS path hint, does not touch To or the TX path

Per the maintainer triage on the issue, the ISS/satellite entry deliberately
does not mutate the To field or the TX PATH — changing the path is more
intrusive and easy to forget to revert — it only surfaces a hint.

Gateway note: the original issue specified SMSGTE, but that service has been
temporarily shut down over spam. The active global APRS↔SMS gateway is now NA7Q's,
addressed to SMS (same @<number> <message> body). Shipped with SMS.

Scope

All changes are local to Ax25HfPacketDecodeDialog (.cpp + .h); no
encoder/protocol change. The send path keeps not uppercasing the message
body (sendAprsMessageFromUi only uppercases the addressee), so case-sensitive
EMAIL-2 addresses and SMSGTE aliases survive intact — the service templates
rely on that.

How the agent automation bridge proved it

Built RelWithDebInfo, launched with AETHER_AUTOMATION=1, and drove the
picker entries by objectName (RX-only — no TX; MODEM stayed in Standby).
The bridge triggers each menu action and reads the resulting To / message
fields back via dumpTree. Before→after, all crash-free on one running
instance:

BEFORE        : ['', '']                  (To, message text)
SMS       ->   ['SMS', '@']
EMAIL-2   ->   ['EMAIL-2', '']
WLNK help ->   ['WLNK-1', '?']
WLNK sms  ->   ['WLNK-1', 'SMS ']
WXBOT     ->   ['WXBOT', '']

ISS routing-only no-clobber:
  before ISS = ['WLNK-1', 'SMS ']
  after  ISS = ['WLNK-1', 'SMS ']   UNCHANGED = True

Hints reached the system log (lcAx25), e.g.:
WLNK-1 APRSLink: send "?" for the command list (L, R#, SP, SMS, …) and
ISS/ARISS digi (145.825 MHz): set TX PATH to "ARISS" (RS0ISS)….

Screenshot of the new ▾ picker in the APRS MESSAGE row (fields seeded from
the bridge; MODEM Standby):

Agent automation bridge — two gaps found (and worked around)

Dogfooding the bridge surfaced two real issues; the feature is shipped to dodge
both, and they're worth fixing in the bridge so the next feature doesn't have to:

  1. TX-guard deny-word cwx is an unanchored substring match. The guard
    flagged a weather (RX-only) menu entry as transmit-keying because the
    abbreviated objectName aprsSvcWXBOT.toLower() = aprssvcwxbot
    contains the trigram cwx (the c from svc + wx from wxbot).
    Workaround: objectNames use the spelled-out aprsService… prefix.
    Proposed fix: anchor the cwx/mox/ptt/transmit deny-words to word
    boundaries (or require an explicit TX marker) instead of bare contains().

  2. The bridge can't safely open a QToolButton popup menu on macOS. A
    programmatic click() runs synchronously inside the socket-read callback;
    showing the menu's native popup window from there re-enters the Cocoa GUI
    stack and segfaults intermittently (QMenu::popup → QCocoaWindow::setVisible → QWindow::geometry). Deferring the popup with singleShot(0) did not help
    — showing the native window is the hazard, not the call site.
    Workaround: ship pure InstantPopup (human press uses Qt's built-in path)
    with no synthetic-click affordance; the bridge still verifies seeding by
    triggering the menu actions while the menu is closed
    (triggerMenuAction then skips the popup path and just calls
    action->trigger() — crash-free, which is exactly how the proof above runs).
    Proposed fix: an openMenu/showMenu verb for button popups that posts
    the show onto the GUI event loop, and/or defer widget click() like QAction
    triggers are already deferred.

💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat

…rsdr#3569)

Add a small ▾ caret button next to the APRS "To" field that addresses the
well-known APRS gateway services without operators having to memorize their
callsigns and message-text syntax. Selecting an entry sets the destination
callsign, seeds the message text with the correct command template (cursor
after the prefix), and prints a one-line hint with the per-service ~67-char
limit to the system log.

Services (grouped by category in the menu):
- SMS — SMSGTE ("@<10-digit#> <msg>")
- Email — EMAIL-2 ("<email> <msg>")
- Winlink — WLNK-1 help ("?") and one-line email/SMS ("SMS <to> <msg>")
- Weather — WXBOT ("City,ST" / "KICAO" / grid / blank)
- Satellite/ISS — routing-only: shows an ARISS/RS0ISS path hint, does NOT
  mutate the To field or the TX path (less surprising to undo, per the issue's
  path-coupling note)

All changes are local to Ax25HfPacketDecodeDialog; no encoder/protocol change.
The send path keeps NOT uppercasing the body, so case-sensitive EMAIL-2
addresses and SMSGTE aliases survive intact.

Closes aethersdr#3569.
@jensenpat jensenpat changed the title feat(gui): APRS message-services picker on the Recipient field (#3569) feat(modem): APRS message-services picker on the Recipient field (#3569) Jun 26, 2026
…thersdr#3569)

The original SMSGTE service has been temporarily shut down over spam, and the
current global APRS↔SMS gateway is NA7Q's, addressed to "SMS" (body format is
unchanged: "@<number> <message>"; aliases via "#alias #add <name> <number>").
This is exactly the "confirm current callsigns/SSIDs at ship time" check the
issue called for. Bridge-verified: empty → To=SMS / text=@, crash-free.
@jensenpat jensenpat marked this pull request as ready for review June 26, 2026 03:11
@jensenpat jensenpat requested a review from a team as a code owner June 26, 2026 03:11

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, @jensenpat — nicely scoped (two files, no protocol/encoder changes), the routing-only ISS handling and the "don't uppercase the body" reasoning are well thought through, and the comments documenting the aprsService… objectName choice and the InstantPopup/bridge tradeoff are genuinely helpful for the next reader. One real bug though:

QString::fromLatin1 on UTF-8 literals → mojibake (blocking)

The kAprsServices table fields are const char* literals in a UTF-8 source file, but they're decoded with QString::fromLatin1, which maps each byte 1:1 onto U+0000–00FF. Any multi-byte UTF-8 character will therefore render as garbage:

  • Every menu label uses an em dash (U+2014, UTF-8 E2 80 94) — e.g. "SMS — text an SMS to a phone". Via fromLatin1 this displays as SMS â€" text an SMS to a phone in the dropdown.
  • Hints contain (ICAO→METAR), and ° (≤~60 chars, passes >30°), which corrupt in both the tooltip and the appendSystemLine system-log output.
QAction* act = serviceMenu->addAction(QString::fromLatin1(e.label));
act->setToolTip(QString::fromLatin1(e.hint));
...
const QString hint = QString::fromLatin1(e.hint);

Fix: use QString::fromUtf8 for label, section, and hint (the addressee/body fields are pure ASCII so they're unaffected, but switching them too keeps it uniform).

Worth noting this is exactly the class of bug the bridge proof couldn't catch: the verification triggers actions by objectName and reads back the To/message fields (all ASCII), so the corrupted label and hint text never showed up in the before→after dumps.

Otherwise looks good

  • Menu ownership is correct — serviceMenu is parented to msgFrame and QToolButton::setMenu doesn't take ownership, so no leak/double-free.
  • seedAprsService null-safety is fine (members are built before the menu can be triggered), the routing-only early-out correctly avoids clobbering an in-progress compose, and setCursorPosition(body.length()) is safe for the empty-body entries.
  • appendSystemLine already guards against a null m_log.

Just the encoding fix and this is good to go.


🤖 aethersdr-agent · cost: $4.3274 · model: claude-opus-4-8

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, correct, low-risk APRS convenience feature. Verified the two correctness-critical items: seedAprsService gates all field mutation on if (!addressee.isEmpty()), so the ISS routing-only entry leaves To/message untouched (matching the maintainer triage); and it does a plain setText(body) with no uppercasing, so case-sensitive EMAIL-2/templates survive (the send path only uppercases the addressee). Good details — cursor lands after the seeded prefix, no TX surface (seeds + logs a hint, never sends), and the pure-InstantPopup with no synthetic-click affordance deliberately avoids the macOS Cocoa re-entrancy segfault while menu objectNames let the bridge verify seeding via closed-menu trigger. The bridge gaps it surfaced (unanchored cwx deny-word, QToolButton popup driving) are real and being consolidated into a follow-up. Thanks @jensenpat.

@ten9876 ten9876 merged commit 2b8b775 into aethersdr:main Jun 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

APRS message-services dropdown on Recipient field (SMS, Email, Winlink, WXBOT, ISS/satellite)

2 participants