Skip to content

Yo3gnd live qso log#218

Open
yo3gnd wants to merge 35 commits into
ham2k:mainfrom
yo3gnd:yo3gnd-live-qso-log
Open

Yo3gnd live qso log#218
yo3gnd wants to merge 35 commits into
ham2k:mainfrom
yo3gnd:yo3gnd-live-qso-log

Conversation

@yo3gnd

@yo3gnd yo3gnd commented May 5, 2026

Copy link
Copy Markdown

Live QSO integrations: HTTP, UDP ADIF / WSJT-X, and N1MM-style broadcast

PoLo already exports well, but exporting later is not the same thing as logging live.

This PR adds a live outbound integration layer for operators who use PoLo with other software or hardware. In practice that usually means portable operation rather than a fixed shack setup: a phone in a park, on a summit, in a car, or on a DXpedition-style activation, with something else that wants to see the QSO as it happens.

What this adds

  • a generic HTTP live-QSO sender
  • UDP ADIF sending
  • WSJT-X-compatible UDP framing
  • N1MM-style XML UDP messages
  • manual test sends for each transport
  • Android-only gating for UDP-based transports
  • unit tests for the core shaping and formatting logic

Why this is useful

This is mostly about removing awkward glue from otherwise ordinary operating.

  • One is simply using PoLo beside a laptop or another radio app and wanting the QSO mirrored live. Up until recently I had to keep three logs in parallel: CW running on paper so my phone could record for later rechecking, FT8 on my steam deck, SSB and CW hunting in PoLo. I am better at CW now and can type it straight into PoLo, which helps, but live logging would still make merging FT8 with CW and SSB much less messy. On rover activations, when this turns into 10-15 parks and 2-3 operators, merging gets error-prone rather quickly.
  • Another is showing live DXpedition-style activity in systems like Club Log. I have had this asked of me repeatedly while running a popular special callsign for POTA. On multi-op work, the workaround was often a second operator retyping what I logged in PoLo into something else. On solo activations, the workaround was exporting every few minutes into intermediate diff-and-forward glue. It works in the way improvised things usually work, but it is clumsy, easy to get wrong, and exactly the sort of problem that should not require custom plumbing if the app can already emit the QSO live.
  • Then there is streaming. A tablet or second screen can show the worked callsign, perhaps the other station's park or summit, perhaps a QRZ picture, without screen-sharing the whole phone UI or turning the operation into a small live video production exercise just to expose what was typed into PoLo. My girlfriend asked for exactly that, and I have had friends ask for my PoLo integration glue.

Scope and design choices

This is intentionally a live interoperability feature, not a sync system.

  • HTTP is kept generic and sends ADIF as plain text to a configured endpoint.
  • The HTTP endpoint is also the most portable transport here, but in practice it is primarily aimed at custom integrations. In the compatibility testing for this PR, the off-the-shelf receiver wins were on the UDP / WSJT-X / N1MM side. On iOS, where those transports are currently not implemented, HTTP is mainly the path for custom tooling rather than ready-made logger interoperability.
  • UDP has two modes because some software wants raw ADIF and some wants WSJT-X-style framing.
  • N1MM-style XML is separate because that ecosystem has its own packet shapes and update/delete semantics.
  • A fair amount of the N1MM code is simply the shape of that XML format. N1MM-style UDP/XML is a recognised interoperability target in contest and digital logging software. This PR keeps that support fairly plain: field mapping, XML envelopes, and transport.
  • Live sending is tied to the live logging flow, not imports, sync merges, or bulk maintenance work.

UDP is Android-only for now. The repo did not have a cross-platform UDP socket layer, and iOS UDP, especially broadcast-style behaviour, comes with extra platform friction anyway. The UI is gated accordingly.

Implementation notes

  • The settings UI now follows the existing extension/settings pattern rather than adding another top-level service entry. I considered adding a new post-save extension hook and turning the live sender into a proper extension that reacts to it, but left that out of this PR on purpose. That would have turned a transport/integration change into a broader extension-architecture refactor. Keeping the runtime integration in the existing QSO save path is less focused, but much narrower in scope.
  • The HTTP sender intentionally reuses PoLo's existing ADIF export path and sticks to the default full-ADIF exporter selection, so the live payload stays aligned with normal exports and the endpoint remains generic rather than turning into a service-specific API patch.

Validation

This was tested on Android against N1MM, Log4OM, DXKeeper, HRD, Swisslog, and AC Log, covering raw ADIF, WSJT-X, and N1MM-style XML. Targeted unit tests were added for ADIF request splitting, WSJT-X message shaping, N1MM XML generation, queue filtering / previous-QSO context, and settings defaults and normalization.

One awkward note: the repo-wide lint/test baseline is already red outside this branch. The targeted tests added here pass.

There is also a companion docs PR covering the user-facing setup for the tested integrations.

Regarding LoFi Sync: This feature mirrors live logging actions to external consumers in real time. Ham2K LoFi Sync solves a different problem: account-linked cloud sync, managed restore onto a new device, and continuity across devices. A user will, of course, point these transports at software running back home, on a nearby laptop, or in a cloud-hosted logger, and in that limited sense it can become part of a personal backup routine. Even so, I do not think that meaningfully overlaps with Ham2K LoFi Sync. This is outbound mirroring only, not a sync system, and it does not try to solve merge or account-switch workflows.

yo3gnd added 2 commits May 12, 2026 07:16
Replace the custom Android UDP bridge with react-native-udp and keep the WSJT-X wire encoding in JS.

Use a small patch-package patch to add a one-shot sendDirect datagram path in react-native-udp so Android avoids the library's bound MulticastSocket broadcast path, which hung on real devices.

Also remove the old Android UDP module entirely, expose the UDP and N1MM settings on iPhone, add the iOS local-network permission string, and reword N1MM "broadcast" copy to "message" on iPhone where the transport is unicast-oriented.
@yo3gnd

yo3gnd commented May 12, 2026

Copy link
Copy Markdown
Author

Small follow-up, because the original PR body is now a touch stale. The UDP side has been moved onto react-native-udp, with a small patch-package fix for the actual send path, and the old custom Android UDP bridge has been removed entirely. The menu wording was also cleaned up so both platforms now present this as Live QSO logging, and N1MM Message is used consistently instead of the earlier broadcast-heavy wording.

Added iPhone compatibility. The UDP and N1MM settings are now exposed as well, with NSLocalNetworkUsageDescription added for local-network access. The companion docs PR was updated to match the new menu tree and platform notes.

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.

1 participant