Skip to content

Add FT4 mode (extensible to future modes)#163

Merged
patrickrb merged 3 commits into
devfrom
feat/ft4-mode
Jun 8, 2026
Merged

Add FT4 mode (extensible to future modes)#163
patrickrb merged 3 commits into
devfrom
feat/ft4-mode

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

What

Adds FT4 as a selectable operating mode alongside FT8. The design is built around a new ModeProfile descriptor so a future mode (e.g. FT2) is a one-entry add, not another FT8/FT4 boolean.

Highlights

  • Mode button on the decode page (pill in TxStrip, disabled mid-TX). Cycles FT8↔FT4 via MainViewModel.setOperatingMode, persisted across restarts.
  • FT4 TX + RX. FT4 decode was already supported by the prebuilt libft8cn.so (InitDecoder protocol flag). FT4 encode had no JNI entry point in the prebuilt, so a small shim (cpp/ft4_encode_jni.cpp) compiled into the CMake-built libft8af_usb.so bridges to the prebuilt's raw ft4_encode (linked via an IMPORTED target; DT_NEEDED libft8cn.so). See the inline comment for how to remove it once the native reconstruction replaces the prebuilt.
  • Radio-safety: switching mode retunes the dial within the same band only (FT8 14.074 → FT4 14.080). It never auto-QSYs to a band the user may not have tuned (high-SWR PA-damage risk). Bands with no FT4 allocation (160m/60m) keep their frequency.
  • Timing generalized: UtcTimer.sequential(utc, slotMillis) (FT8 output byte-identical), per-mode cycle timers rebuilt on switch, per-mode late-start/Costas-clip slack, SlotTimerBar parameterized by slot length.
  • Mode strings (QSL log, SWL log, PSKReporter query, POTA self-spot) now derive from ModeProfile.displayName.
  • Bands: mode-tagged FT4 dials added to bands.txt; frequency/settings pickers filter by current mode.

Tests

  • New ModeProfileTest (descriptor table, fromId fallback), OperationBand FT4 parsing + getModeBandFreq, UtcTimer.sequential per-mode.
  • Full testDebugUnitTest green; native build links across all 4 ABIs; installed on a Pixel 8 and both native libs load with no UnsatisfiedLinkError.

Manual verification still needed (requires RF)

  • Decode FT4 off-air on an FT4 sub-band (e.g. 14.080) → spots tagged FT4
  • TX FT4 into a second rig running WSJT-X FT4 → decodable (validates Costas timing + per-mode slack)
  • Confirm PSKReporter shows FT4 spots

Note

The native reconstruction sources (cpp/ft8_lib, cpp/ft8cn_glue, cpp/libsamplerate) remain untracked per existing project convention; this PR does not touch them. FT4 ships via the prebuilt + the new ft8af_usb shim.

🤖 Generated with Claude Code

Adds FT4 as a selectable operating mode alongside FT8, driven by a new
ModeProfile descriptor so future modes (e.g. FT2) are a one-entry add
rather than another FT8/FT4 boolean.

- ModeProfile enum: per-mode timing/symbol/protocol params keyed off the
  existing FT8Common.*_MODE ints; GeneralVariables.operatingMode + config
  persistence in DatabaseOpr.
- Encode: parameterize GenerateFT8.generateFt8ByA91 by ModeProfile (FT8
  output unchanged). The prebuilt libft8cn.so exposes no FT4 encode JNI, so
  a shim (cpp/ft4_encode_jni.cpp) in the CMake-built libft8af_usb.so bridges
  to the prebuilt's raw ft4_encode; GenerateFT8 now loads ft8af_usb too.
- Decode: pass the mode's protocol to InitDecoder, tag messages, use the
  mode's RX window.
- Timing: UtcTimer.sequential(utc, slotMillis) generalized (FT8 identical);
  FT8SignalListener/FT8TransmitSignal rebuild their cycle timers on mode
  change; late-start/Costas-clip slack now per-mode; SlotTimerBar
  parameterized by slot length.
- UI: mode pill on the decode page (TxStrip), disabled mid-TX; cycles
  FT8<->FT4 via MainViewModel.setOperatingMode, which retunes the dial
  WITHIN the same band only (never auto-QSY to an untuned band).
- Bands: mode-tagged FT4 dials in bands.txt; pickers filter by mode;
  OperationBand.getModeBandFreq for in-band retune.
- Mode strings (QSL log, SWL log, PSKReporter query, POTA self-spot) now
  derive from ModeProfile.displayName.
- Tests: ModeProfileTest, OperationBand mode parsing/getModeBandFreq,
  UtcTimer.sequential per-mode.

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

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 19.41176% with 137 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.58%. Comparing base (07fff06) to head (fd10605).
⚠️ Report is 4 commits behind head on dev.

Files with missing lines Patch % Lines
.../src/main/java/com/bg7yoz/ft8cn/MainViewModel.java 0.00% 33 Missing ⚠️
...app/src/main/kotlin/radio/ks3ckc/ft8us/FT8USApp.kt 0.00% 19 Missing ⚠️
...kotlin/radio/ks3ckc/ft8us/ui/components/TxStrip.kt 0.00% 18 Missing ⚠️
...om/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java 0.00% 13 Missing ⚠️
...om/bg7yoz/ft8cn/ft8transmit/FT8TransmitSignal.java 0.00% 13 Missing ⚠️
...java/com/bg7yoz/ft8cn/ft8transmit/GenerateFT8.java 0.00% 10 Missing ⚠️
...in/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java 0.00% 9 Missing ⚠️
.../java/com/bg7yoz/ft8cn/database/OperationBand.java 50.00% 5 Missing and 3 partials ⚠️
...n/radio/ks3ckc/ft8us/ui/components/SlotTimerBar.kt 0.00% 6 Missing ⚠️
...pp/src/main/java/com/bg7yoz/ft8cn/ModeProfile.java 83.33% 4 Missing ⚠️
... and 3 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##               dev    #163      +/-   ##
==========================================
+ Coverage     6.53%   6.58%   +0.05%     
- Complexity     682     695      +13     
==========================================
  Files          269     271       +2     
  Lines        31113   31412     +299     
  Branches      4897    4959      +62     
==========================================
+ Hits          2032    2070      +38     
- Misses       28939   29198     +259     
- Partials       142     144       +2     
Files with missing lines Coverage Δ
...c/main/java/com/bg7yoz/ft8cn/GeneralVariables.java 34.83% <100.00%> (+0.62%) ⬆️
...src/main/java/com/bg7yoz/ft8cn/timer/UtcTimer.java 26.37% <100.00%> (+0.81%) ⬆️
...adio/ks3ckc/ft8us/pskreporter/PskReporterClient.kt 70.94% <100.00%> (+0.25%) ⬆️
...n/kotlin/radio/ks3ckc/ft8us/ComposeMainActivity.kt 0.00% <0.00%> (ø)
...in/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaScreen.kt 0.00% <0.00%> (ø)
...ks3ckc/ft8us/ui/components/FrequencyPickerSheet.kt 0.00% <0.00%> (ø)
...pp/src/main/java/com/bg7yoz/ft8cn/ModeProfile.java 83.33% <83.33%> (ø)
...n/radio/ks3ckc/ft8us/ui/components/SlotTimerBar.kt 0.00% <0.00%> (ø)
.../java/com/bg7yoz/ft8cn/database/OperationBand.java 55.17% <50.00%> (-1.00%) ⬇️
...in/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java 0.00% <0.00%> (ø)
... and 6 more

... and 1 file 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

Adds FT4 as a first-class operating mode alongside FT8, refactoring timing/encoding/UI and band dial selection around a new ModeProfile descriptor so additional modes can be added with minimal churn.

Changes:

  • Introduces ModeProfile and threads mode-aware timing/slot length through RX/TX timers, UI slot bar, and logging/spotting strings.
  • Adds FT4 band dials and mode-aware band parsing/filtering so pickers show the correct per-mode frequencies.
  • Adds a native JNI shim to expose FT4 encoding via the prebuilt libft8cn.so and links it into the CMake-built libft8af_usb.so.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
ft8cn/app/src/test/java/com/bg7yoz/ft8cn/timer/UtcTimerTest.java Adds slot-length-aware sequential timer tests (FT8/FT4).
ft8cn/app/src/test/java/com/bg7yoz/ft8cn/ModeProfileTest.java New pure-logic tests validating ModeProfile descriptors and id resolution.
ft8cn/app/src/test/java/com/bg7yoz/ft8cn/database/OperationBandTest.java Extends band parsing tests for FT4-tagged entries and mode dial lookup.
ft8cn/app/src/main/res/values/strings_compose.xml Adds user-visible strings for mode switch toasts.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaScreen.kt Uses current mode display name for POTA spot mode field.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/components/TxStrip.kt Adds a mode “pill” UI control to cycle modes (disabled while TX).
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/components/SlotTimerBar.kt Parameterizes slot length (15s/7.5s) for progress/countdown rendering.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/components/FrequencyPickerSheet.kt Filters frequency picker entries by operating mode and recomposes on mode changes.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/pskreporter/PskReporterClient.kt Uses current mode display name in PSKReporter queries.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/FT8USApp.kt Wires mode state into UI (mode pill + slot bar) and calls setOperatingMode.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/timer/UtcTimer.java Adds sequential(utc, slotMillis) and makes default sequential mode-aware.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ModeProfile.java New descriptor table for per-mode timing/symbol/encode dispatch.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java Adds mode LiveData and setOperatingMode (persist + rebuild timers + in-band retune).
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/GeneralVariables.java Adds operatingMode global and currentMode() helper.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/GenerateFT8.java Generalizes tone/audio synthesis per mode; adds FT4 encode JNI method.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/FT8TransmitSignal.java Makes TX timer/mode timing mode-aware; rebuilds timer on mode switch.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java Makes RX timer/recording window/decoder init mode-aware; supports timer rebuild.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java Adds mode-tagged band parsing and mode-specific dial lookup; filters visible bands by mode.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java Persists mode string in SWL logs; loads operatingMode from config.
ft8cn/app/src/main/cpp/ft4_encode_jni.cpp New JNI bridge exposing ft4_encode from the prebuilt DSP library.
ft8cn/app/src/main/cpp/CMakeLists.txt Links ft8af_usb against imported prebuilt libft8cn.so for FT4 encode shim.
ft8cn/app/src/main/assets/bands.txt Adds FT4 dial entries tagged with :FT4.

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

Comment thread ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/components/SlotTimerBar.kt Outdated
Comment thread ft8cn/app/src/main/java/com/bg7yoz/ft8cn/database/DatabaseOpr.java
Comment thread ft8cn/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java
Comment thread ft8cn/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java Outdated
Comment thread ft8cn/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java Outdated
Comment thread ft8cn/app/src/main/java/com/bg7yoz/ft8cn/MainViewModel.java
patrickrb and others added 2 commits June 8, 2026 15:08
- MainViewModel.applyLoadedOperatingMode(): the RX/TX cycle timers are built
  (defaulting to FT8) in the constructor before the persisted mode is read from
  config asynchronously. Call this on config-load completion so a persisted FT4
  rebuilds the timers for 7.5s and syncs the mode LiveData, instead of running
  FT8 timing with a stale UI pill.
- Normalize the operating-mode id through ModeProfile.fromId(...).id when loading
  from config (DatabaseOpr) and in setOperatingMode, so an unknown id persisted by
  a future build degrades to FT8 everywhere (not just descriptor lookups) — e.g.
  the band picker won't filter against an unsupported id.
- SlotTimerBar: round the slot length up so a 7.5s FT4 slot peaks at 8s (integer
  division truncated to 7), and derive the slot index from the passed slotMillis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/FT8USApp.kt
#	ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/components/TxStrip.kt
@patrickrb patrickrb merged commit 4f2e2e4 into dev Jun 8, 2026
4 checks passed
@patrickrb patrickrb deleted the feat/ft4-mode branch June 8, 2026 20:29
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