From b95a86c97f9ca20066dd9922ec6dbb1a80a5a4b9 Mon Sep 17 00:00:00 2001 From: QIU Date: Sun, 16 Aug 2026 09:47:26 +0000 Subject: [PATCH] fix(radio): reject FT-817 frequencies the CAT protocol cannot express The FT-817 CAT frequency field is 4 BCD bytes at 10 Hz resolution, so the largest representable value is 999,999,990 Hz. encodeFrequencyBcd is exact below that, but for anything above it the %08d formatting silently drops the leading digit: 1,267.6 MHz encodes as 126.76 MHz. Verified against the release bytecode - 1,000,000,000 Hz -> [10 00 00 00] -> 100,000,000 Hz, ten times lower - and the SatNOGS catalogue has 17 transmitters with uplinks over 1 GHz (QO-100 at 2400.05 MHz, several 23 cm links), so the wrong value is reachable via RadioTrackingService when an FT-817 is mis-configured as the TX radio. The tracking loop's read-back then locks onto the wrong band with no warning. Reject out-of-range frequencies at setFrequency with a log and return false instead of sending a corrupted command. In-range values are unaffected (probe: 7.074/145.5/435.1 MHz and both 999,999,98x/99x MHz round-trip exactly; every value above the limit is refused before the encoder runs). --- .../look4sat/core/data/framework/Ft817Controller.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/data/src/main/java/com/rtbishop/look4sat/core/data/framework/Ft817Controller.kt b/core/data/src/main/java/com/rtbishop/look4sat/core/data/framework/Ft817Controller.kt index 6affe684..f3180e46 100644 --- a/core/data/src/main/java/com/rtbishop/look4sat/core/data/framework/Ft817Controller.kt +++ b/core/data/src/main/java/com/rtbishop/look4sat/core/data/framework/Ft817Controller.kt @@ -42,6 +42,9 @@ class Ft817Controller( private val commandDelayMs = 200L private val maxAckReadFailures = 3 + /** Largest frequency the 4-byte BCD / 10 Hz CAT field can represent. */ + private val maxFrequencyHz = 999_999_990L + private var socket: BluetoothSocket? = null private var outputStream: OutputStream? = null private var inputStream: InputStream? = null @@ -100,6 +103,16 @@ class Ft817Controller( } override suspend fun setFrequency(frequencyHz: Long): Boolean = withContext(Dispatchers.IO) { + // The FT-817 CAT frequency field is 4 BCD bytes at 10 Hz resolution, + // so the protocol cannot express anything above 999,999,990 Hz. Below + // that the encoder is exact; above it, the %08d formatting silently + // drops the leading digit and the radio receives a frequency ten + // times lower (e.g. 1267.6 MHz becomes 126.76 MHz), and the tracking + // loop's read-back then locks onto the wrong band. Reject instead. + if (frequencyHz > maxFrequencyHz) { + Log.e(tag, "setFrequency rejected: $frequencyHz Hz exceeds FT-817 CAT limit $maxFrequencyHz") + return@withContext false + } ioMutex.withLock { sendCommandWithAck(Ft817CatProtocol.buildSetFreqCommand(frequencyHz)) }