Skip to content

Add FT2 mode (TX + RX)#168

Open
patrickrb wants to merge 2 commits into
devfrom
feat/ft2-mode
Open

Add FT2 mode (TX + RX)#168
patrickrb wants to merge 2 commits into
devfrom
feat/ft2-mode

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

What

Adds FT2 — the ultra-fast HF digital mode from ft2.it (IU8LMC) — as a fully working two-way operating mode (transmit and receive), alongside FT8/FT4.

FT2 is structurally identical to FT4 (4-GFSK, four Costas sync blocks, 87 data symbols, 77-bit payload, LDPC(174,91)) but runs at double the FT4 baud: 0.024s symbol period (NSPS 288 @12kHz), ~41.7 Hz tone spacing, ~167 Hz BW, 3.8s T/R cycle, ~2.52s audio. Parameters confirmed against Decodium-4.0-Core-Shannon's FtxFt2Stage7.cpp.

How

TX + mode plumbing (mirrors the FT4 PR #163's ModeProfile):

  • FT8Common.FT2_MODE + a one-line ModeProfile.FT2 entry. FT2's tones are bit-identical to FT4, so encode() reuses ft4Encode and synth runs at the FT2 symbol period — no new encode code.
  • bands.txt FT2-tagged dials; OperationBand parser resolves the mode tag via ModeProfile.displayName (future-proof). Mode pill now cycles FT8→FT4→FT2.

RX — parallel from-source decoder (the closed prebuilt libft8cn.so has no FT2 protocol):

  • Adds FTX_PROTOCOL_FT2 to the in-tree kgoba ft8_lib (pinned at 6f528128, the same commit the prebuilt was built from): constants.h period/slot, monitor.c symbol-period switch, decode.c FT4 branches broadened to FT2 (shared 4-Costas/105-symbol/XOR layout).
  • New ft2_decode_jni.cpp decode wrapper with distinct *Ft2 JNI entry points; CMake compiles the from-source decode slice into libft8af_usb.so with -fvisibility=hidden so its symbols stay internal and never clash with the prebuilt's. FT8/FT4 decode is byte-for-byte unchanged (still the prebuilt).
  • FT8SignalListener routes the decode loop to the FT2 backend when ModeProfile.usesFt2Decoder().

Note: This newly tracks the vendored kgoba ft8_lib (previously untracked reference / "reconstruction in progress") because the FT2 build now compiles it. MIT-licensed, LICENSE included.

Verification (Pixel 8)

  • ✅ Unit tests pass (ModeProfile, OperationBand, UtcTimer FT2 cases).
  • ✅ Native decoder compiles & links across all 4 ABIs (symbol isolation works).
  • ✅ APK installs and runs.
  • Native FT2 round-trip on-deviceft4_encode → GFSK@0.024s → FT2 decode recovers "CQ K1ABC FN42" (Block size = 288 confirms the FT2 symbol period; score 40). This is the gold-standard proof FT2 RX interoperates with real transmitters.

⚠️ Open item

The FT2 band frequencies are provisional placeholders (FT8 sub-band dials). ft2.it/Decodium publish only example QSOs and conflicting calling freqs, pointing to a HamPass ft2-bands.qrg file. Please provide the official per-band list and I'll swap them in before merge.

🤖 Generated with Claude Code

FT2 is the new ultra-fast HF digital mode from ft2.it (IU8LMC). It is
structurally identical to FT4 — 4-GFSK, four Costas sync blocks, 87 data
symbols, 77-bit payload, LDPC(174,91) — but runs at double the FT4 baud:
0.024s symbol period (NSPS 288 @12kHz), ~41.7Hz tone spacing, ~167Hz BW,
3.8s T/R cycle, ~2.52s audio. Confirmed against Decodium's FtxFt2Stage7.cpp.

TX + mode plumbing (mirrors the FT4 PR #163):
- FT8Common.FT2_MODE + ModeProfile.FT2 (one-entry add). Because FT2's tones
  are bit-identical to FT4, encode() reuses ft4Encode and synth runs at the
  FT2 symbol period — no new encode code.
- bands.txt FT2-tagged dials (PROVISIONAL: FT8 sub-band placeholders pending
  the official ft2.it/HamPass list); OperationBand parser resolves the mode
  tag via ModeProfile.displayName so future modes need no new branch.
- Mode pill cycles FT8->FT4->FT2 (iterates ModeProfile entries); PSKReporter
  mode string derives from displayName.

RX — parallel from-source decoder (the prebuilt libft8cn.so has no FT2):
- Adds FTX_PROTOCOL_FT2 to the in-tree kgoba ft8_lib (pinned at 6f528128,
  the same commit the prebuilt was built from): constants.h period/slot,
  monitor.c symbol-period switch, decode.c FT4 branches broadened to FT2
  (shared 4-Costas/105-symbol/XOR layout).
- ft2_decode_jni.cpp: FT2 decode JNI wrapper (adapted from the ft8_decoder.cpp
  reconstruction) with distinct *Ft2 entry points and protocol fixed to FT2.
- CMake compiles the from-source decode slice + wrapper into libft8af_usb.so
  with -fvisibility=hidden so its symbols stay internal and never clash with
  the prebuilt's exported copies; FT8/FT4 keep using the prebuilt unchanged.
- FT8SignalListener routes the decode loop to the FT2 backend when
  ModeProfile.usesFt2Decoder(); FT8/FT4 paths untouched.

This newly tracks the vendored kgoba ft8_lib (previously untracked reference,
"reconstruction in progress") because the FT2 build now compiles it.

Tests: ModeProfile/OperationBand/UtcTimer FT2 cases. Verified on a Pixel 8:
unit tests pass, native decoder links across all 4 ABIs, and a native FT2
round-trip (ft4_encode -> GFSK@0.024s -> FT2 decode) recovers the message
on-device (Block size=288, score 40).

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 13.11475% with 53 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.57%. Comparing base (4f2e2e4) to head (c22e7f2).
⚠️ Report is 5 commits behind head on dev.

Files with missing lines Patch % Lines
...om/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java 0.00% 37 Missing ⚠️
...va/com/bg7yoz/ft8cn/ft8listener/ReBuildSignal.java 0.00% 9 Missing ⚠️
...app/src/main/kotlin/radio/ks3ckc/ft8us/FT8USApp.kt 0.00% 3 Missing ⚠️
...adio/ks3ckc/ft8us/pskreporter/PskReporterSender.kt 0.00% 3 Missing ⚠️
.../java/com/bg7yoz/ft8cn/database/OperationBand.java 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##               dev    #168      +/-   ##
==========================================
+ Coverage     6.56%   6.57%   +0.01%     
- Complexity     695     697       +2     
==========================================
  Files          272     272              
  Lines        31563   31601      +38     
  Branches      4982    4984       +2     
==========================================
+ Hits          2071    2077       +6     
- Misses       29348   29380      +32     
  Partials       144     144              
Files with missing lines Coverage Δ
.../app/src/main/java/com/bg7yoz/ft8cn/FT8Common.java 0.00% <ø> (ø)
...pp/src/main/java/com/bg7yoz/ft8cn/ModeProfile.java 84.61% <100.00%> (+1.28%) ⬆️
.../java/com/bg7yoz/ft8cn/database/OperationBand.java 57.14% <83.33%> (+1.97%) ⬆️
...app/src/main/kotlin/radio/ks3ckc/ft8us/FT8USApp.kt 0.00% <0.00%> (ø)
...adio/ks3ckc/ft8us/pskreporter/PskReporterSender.kt 65.30% <0.00%> (+0.26%) ⬆️
...va/com/bg7yoz/ft8cn/ft8listener/ReBuildSignal.java 0.00% <0.00%> (ø)
...om/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java 0.00% <0.00%> (ø)
🚀 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 FT2 as a selectable operating mode alongside FT8/FT4, including transmit parameterization and a dedicated from-source native FT2 decoder backend (to avoid relying on the closed prebuilt decoder which only supports FT8/FT4).

Changes:

  • Add ModeProfile.FT2 + mode-cycling/UI plumbing and mode-tagged band dials.
  • Introduce a parallel from-source FT2 RX decoder (vendored ft8_lib slice + ft2_decode_jni.cpp) compiled into libft8af_usb.so with symbol isolation.
  • Expand unit test coverage for FT2 timing, mode descriptors, and band parsing.

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
ft8cn/app/src/test/java/com/bg7yoz/ft8cn/timer/UtcTimerTest.java Adds FT2-slot (3800ms) sequential timing test coverage.
ft8cn/app/src/test/java/com/bg7yoz/ft8cn/ModeProfileTest.java Validates FT2 descriptor constants and FT2-decoder routing flag.
ft8cn/app/src/test/java/com/bg7yoz/ft8cn/database/OperationBandTest.java Adds FT2 mode-tag parsing + per-mode dial frequency selection tests.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/pskreporter/PskReporterSender.kt Switches PSKReporter “mode” string derivation to ModeProfile.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/FT8USApp.kt Updates mode pill to cycle through ModeProfile entries.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ModeProfile.java Adds FT2 profile and usesFt2Decoder() routing helper.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/ReBuildSignal.java Loads ft8af_usb and adds FT2 subtract JNI entry point.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java Routes decode loop through FT2-vs-prebuilt dispatch helpers; loads ft8af_usb.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/FT8Common.java Adds FT2 mode id and slot timing constants.
ft8cn/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java Generalizes mode tag parsing to resolve via ModeProfile.displayName.
ft8cn/app/src/main/cpp/ft8cn_glue/ft2_decode_jni.cpp New JNI bridge implementing FT2 decode + deep-subtract atop from-source ft8_lib.
ft8cn/app/src/main/cpp/ft8_lib/LICENSE Adds MIT license for vendored ft8_lib.
ft8cn/app/src/main/cpp/ft8_lib/ft8/unpack.h Vendored ft8_lib unpack API header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/unpack.c Vendored ft8_lib unpack implementation.
ft8cn/app/src/main/cpp/ft8_lib/ft8/text.h Vendored ft8_lib text utilities header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/text.c Vendored ft8_lib text utilities implementation.
ft8cn/app/src/main/cpp/ft8_lib/ft8/pack.h Vendored ft8_lib pack API header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/pack.c Vendored ft8_lib pack implementation.
ft8cn/app/src/main/cpp/ft8_lib/ft8/message.h Vendored ft8_lib message types and encode/decode API header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/ldpc.h Vendored ft8_lib LDPC header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/ldpc.c Vendored ft8_lib LDPC implementation.
ft8cn/app/src/main/cpp/ft8_lib/ft8/encode.h Vendored ft8_lib encode header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/encode.c Vendored ft8_lib encode implementation.
ft8cn/app/src/main/cpp/ft8_lib/ft8/decode.h Vendored ft8_lib decode header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/decode.c Vendored ft8_lib decode implementation (with FT2 protocol handling).
ft8cn/app/src/main/cpp/ft8_lib/ft8/debug.h Vendored ft8_lib debug macros header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/crc.h Vendored ft8_lib CRC header.
ft8cn/app/src/main/cpp/ft8_lib/ft8/crc.c Vendored ft8_lib CRC implementation.
ft8cn/app/src/main/cpp/ft8_lib/ft8/constants.h Vendored ft8_lib constants header; adds FTX_PROTOCOL_FT2.
ft8cn/app/src/main/cpp/ft8_lib/ft8/constants.c Vendored ft8_lib constants implementation.
ft8cn/app/src/main/cpp/ft8_lib/FT8_LIB_PIN.txt Records the pinned upstream commit for the vendored ft8_lib snapshot.
ft8cn/app/src/main/cpp/ft8_lib/fft/kiss_fftr.h Vendored KISS FFT real FFT header.
ft8cn/app/src/main/cpp/ft8_lib/fft/kiss_fftr.c Vendored KISS FFT real FFT implementation.
ft8cn/app/src/main/cpp/ft8_lib/fft/kiss_fft.h Vendored KISS FFT header (BSD-3-Clause).
ft8cn/app/src/main/cpp/ft8_lib/fft/kiss_fft.c Vendored KISS FFT implementation (BSD-3-Clause).
ft8cn/app/src/main/cpp/ft8_lib/fft/_kiss_fft_guts.h Vendored KISS FFT internal header.
ft8cn/app/src/main/cpp/ft8_lib/common/monitor.h Vendored ft8_lib monitor API header.
ft8cn/app/src/main/cpp/ft8_lib/common/monitor.c Vendored ft8_lib monitor implementation with FT2 protocol selection.
ft8cn/app/src/main/cpp/ft8_lib/common/common.h Adds an M_PI definition helper header.
ft8cn/app/src/main/cpp/CMakeLists.txt Builds/links the from-source FT2 decoder slice into libft8af_usb.so.
ft8cn/app/src/main/assets/bands.txt Adds provisional FT2 dial entries (mode-tagged).

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

Comment on lines +157 to +158
// Mode string (FT8/FT4/FT2) derived from the message's mode descriptor.
val mode = ModeProfile.fromId(msg.signalFormat).displayName
Comment on lines 47 to 53
static {
System.loadLibrary("ft8cn");
// The FT2 decoder JNI entry points (InitDecoderFt2 etc.) live in ft8af_usb (our
// from-source ft8_lib build), not the prebuilt ft8cn. Load it so they resolve when
// the user operates in FT2 mode. Idempotent if GenerateFT8 already loaded it.
System.loadLibrary("ft8af_usb");
}
Comment on lines 14 to 18
static {
System.loadLibrary("ft8cn");
// doSubtractSignalFt2 lives in ft8af_usb (the from-source FT2 decoder), not ft8cn.
System.loadLibrary("ft8af_usb");
}
Comment on lines +1 to +11
#include "monitor.h"

#define LOG_LEVEL LOG_INFO
#include <ft8/debug.h>

#include <stdlib.h>

static float hann_i(int i, int N)
{
float x = sinf((float)M_PI * i / N);
return x * x;
Comment on lines +1 to +7
/*
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
*
* SPDX-License-Identifier: BSD-3-Clause
* See COPYING file for more information.
*/
- PskReporterSender: drop spots with an unknown/corrupt signalFormat instead of
  mislabelling them as FT8 (ModeProfile.fromId falls back to FT8); restores the
  prior drop-on-unknown behavior while still supporting FT2.
- FT8SignalListener / ReBuildSignal: wrap the native loadLibrary calls in
  try/catch (mirroring GenerateFT8) so class init doesn't crash when native libs
  aren't on the path (e.g. JVM unit tests).
- monitor.c: include <math.h> (sinf/log10f) and common.h (M_PI fallback) so the
  vendored source builds on toolchains where <math.h> doesn't define M_PI.
- Add ft8_lib/fft/COPYING (KISS FFT BSD-3-Clause) referenced by the kiss_fft
  headers but previously missing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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