Skip to content

TX: free text is a one-shot, sent immediately (WSJT-X Tx5 style)#350

Merged
patrickrb merged 2 commits into
devfrom
feat/free-text-one-shot
Jun 26, 2026
Merged

TX: free text is a one-shot, sent immediately (WSJT-X Tx5 style)#350
patrickrb merged 2 commits into
devfrom
feat/free-text-one-shot

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Problem

Tester feedback: "when I set the free text, it might need to send once. afaik, in wsjtx, free text is alternative option of the 73 not a CQ" and "i feel too long waiting time."

Today, sending free text calls setTransmitFreeText(true) and then activates like a CQ, so:

  • it repeats every cycle until you hit STOP, and
  • it only goes out at the next slot boundary — up to a full cycle (~15 s on FT8) of dead air after you tap.

Change

New sendFreeTextOnce() on FT8TransmitSignal:

  • arms the free-text message with a freeTextOneShot flag,
  • setActivated(true) then transmitNow() — so it transmits this cycle if we're within the late-start tolerance window (the same immediate-TX path tapping a decode already uses), otherwise at the next boundary,
  • after the single transmission, afterPlayAudio() auto-stops the sequencer and reverts transmitFreeText to false so the next CQ is a normal CQ.

The app (onCallCQ) consumes the armed free text on send (isFreeTextMode = false), so the UI reverts to standard CQ after one shot.

Safety: the one-shot stop runs at the end of afterPlayAudio(), after playback has drained (isTransmitting already false), so setActivated(false) doesn't abort live audio. Activation blocked by SWR lockout / invalid callsign disarms the one-shot cleanly.

Tests

  • New shouldStopAfterOneShot() pure predicate + 4 unit tests (stops only for a free-text one-shot; never for a normal CQ).
  • testDebugUnitTest green; built and installed on the Pixel 8, launches cleanly.

Follow-up (not in this PR)

A general "transmit immediately" option for standard CQ (the tester mentioned "for free text and other cases") could reuse transmitNow() on the CQ activate path. Scoped out here to keep the change focused on the free-text behavior.

Part of a series addressing tester feedback on the Android UI (item 2 of 4).

Free text was queued like a CQ: setTransmitFreeText(true) left it
repeating every cycle until STOP, and it only went out at the next slot
boundary (up to a full cycle of dead air). A tester noted free text
should behave like WSJT-X's Tx5 — an alternative to a 73, sent once —
and that the wait felt too long.

Add sendFreeTextOnce(): set the text, arm a freeTextOneShot flag,
activate, and transmitNow() so it fires this cycle if we're within the
late-start tolerance (immediate TX, same path tapping a decode uses).
After the single send, afterPlayAudio() auto-stops the sequencer and
reverts to standard messages. The app consumes the armed free text on
send so the next CQ tap calls a normal CQ.

Extract shouldStopAfterOneShot() as a pure predicate and unit-test it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 11.46%. Comparing base (6bc083c) to head (ba71f00).
⚠️ Report is 29 commits behind head on dev.

Files with missing lines Patch % Lines
...app/src/main/kotlin/radio/ks3ckc/ft8af/FT8AFApp.kt 0.00% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #350      +/-   ##
============================================
- Coverage     11.70%   11.46%   -0.25%     
- Complexity      113      120       +7     
============================================
  Files            85       89       +4     
  Lines         11691    12565     +874     
  Branches       2110     2247     +137     
============================================
+ Hits           1369     1441      +72     
- Misses        10191    10993     +802     
  Partials        131      131              
Files with missing lines Coverage Δ
...app/src/main/kotlin/radio/ks3ckc/ft8af/FT8AFApp.kt 0.58% <0.00%> (-0.27%) ⬇️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR changes the “free text” TX behavior to be a WSJT-X Tx5-style one-shot: when armed from the UI it should transmit once (optionally immediately via the existing late-start tolerance path) and then auto-stop/revert to normal CQ messaging.

Changes:

  • Add sendFreeTextOnce() and a freeTextOneShot flag to arm a single free-text transmission and auto-stop after afterPlayAudio().
  • Update the Compose UI CQ handler to consume/reset the armed free-text message after sending.
  • Add a pure predicate (shouldStopAfterOneShot) plus unit tests to validate auto-stop conditions.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
ft8af/app/src/test/java/com/k1af/ft8af/ft8transmit/FT8TransmitSignalTest.java Adds unit tests for the one-shot stop predicate.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/FT8AFApp.kt Switches CQ action to call sendFreeTextOnce() and clears the UI’s free-text state.
ft8af/app/src/main/java/com/k1af/ft8af/ft8transmit/FT8TransmitSignal.java Implements one-shot arming + auto-stop logic and extracts a testable predicate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

sendFreeTextOnce() called transmitNow(), which dereferences toCallsign (for
its status toast). toCallsign is null until the first setTransmit/resetToCQ,
so using a free-text one-shot as the very first TX action of a session
(before any CQ press or decode tap) threw an NPE. Seed a CQ baseline when
toCallsign is null before transmitting. The free-text message is built
independently of toCallsign, so this only supplies the baseline target.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@patrickrb patrickrb merged commit b06a800 into dev Jun 26, 2026
16 checks passed
@patrickrb patrickrb deleted the feat/free-text-one-shot branch June 26, 2026 18:58
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.

2 participants