Add FT4 mode (extensible to future modes)#163
Merged
Merged
Conversation
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>
There was a problem hiding this comment.
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
ModeProfileand 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.soand links it into the CMake-builtlibft8af_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.
- 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
This was referenced Jun 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds FT4 as a selectable operating mode alongside FT8. The design is built around a new
ModeProfiledescriptor so a future mode (e.g. FT2) is a one-entry add, not another FT8/FT4 boolean.Highlights
TxStrip, disabled mid-TX). Cycles FT8↔FT4 viaMainViewModel.setOperatingMode, persisted across restarts.libft8cn.so(InitDecoderprotocol flag). FT4 encode had no JNI entry point in the prebuilt, so a small shim (cpp/ft4_encode_jni.cpp) compiled into the CMake-builtlibft8af_usb.sobridges to the prebuilt's rawft4_encode(linked via anIMPORTEDtarget;DT_NEEDED libft8cn.so). See the inline comment for how to remove it once the native reconstruction replaces the prebuilt.UtcTimer.sequential(utc, slotMillis)(FT8 output byte-identical), per-mode cycle timers rebuilt on switch, per-mode late-start/Costas-clip slack,SlotTimerBarparameterized by slot length.ModeProfile.displayName.bands.txt; frequency/settings pickers filter by current mode.Tests
ModeProfileTest(descriptor table,fromIdfallback),OperationBandFT4 parsing +getModeBandFreq,UtcTimer.sequentialper-mode.testDebugUnitTestgreen; native build links across all 4 ABIs; installed on a Pixel 8 and both native libs load with noUnsatisfiedLinkError.Manual verification still needed (requires RF)
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 newft8af_usbshim.🤖 Generated with Claude Code