Fix FT-2000 CAT: SWR meter dead during TX (only first of two coalesced replies parsed)#667
Conversation
…d replies parsed)
Yaesu38Rig ("YAESU FT-2000 series") polls meters by sending two reads
back-to-back — RM4; (ALC) then RM6; (SWR). The transport routinely
delivers both replies coalesced in a single onReceiveData chunk
("RM4nnn;RM6nnn;"). The old parser consumed only the first
';'-terminated command (ALC) and re-buffered the rest *with* its
terminator; the next meter poll's clearBufferData() then wiped that
carry-over, so the SWR (second) reply was permanently dropped. Result:
the SWR meter never moved during TX and the high-SWR safety alert never
fired.
Fix: onReceiveData now drains EVERY complete ';'-terminated command from
the accumulated buffer and carries only the unterminated tail into the
next read. The framing is extracted to a pure, package-private static
helper (splitCommands + Frames) so it is unit-testable without the rig's
Timer-scheduling constructor; per-command handling moves to
processCommand. Also collapses a latent double getFrequency() call.
This is the same root cause fixed for the FT-891 in #665, on a distinct
still-affected rig. Kept self-contained (no shared splitter class) to
avoid colliding with #665's in-flight CatLineSplitter; the identical
Yaesu gen-3 siblings (FTDX-450/DX10/Wolf-SDR) remain follow-ups.
Tests: Yaesu38RigTest — 8 pure-JVM cases (coalesced pair, split-across-
reads carry, partial tail, leading terminator, empty). Full
:app:testDebugUnitTest green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #667 +/- ##
============================================
+ Coverage 36.73% 36.79% +0.06%
- Complexity 197 207 +10
============================================
Files 216 218 +2
Lines 26888 26994 +106
Branches 3287 3311 +24
============================================
+ Hits 9877 9933 +56
- Misses 16785 16829 +44
- Partials 226 232 +6
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Fixes FT-2000-series (Yaesu gen-3) CAT reply parsing so coalesced ;-terminated replies (notably ALC + SWR meter polls arriving as "RM4...;RM6...;" in a single read) are fully drained and processed, preventing the SWR meter from going stale during TX.
Changes:
- Updated
Yaesu38Rig.onReceiveDatato split accumulated input into all complete;-terminated commands plus an unterminated remainder, and to process each command in order. - Extracted per-command handling into
processCommand(String)and framing logic into a pure helpersplitCommands(String)with a smallFramescarrier type. - Added a new pure-JVM unit test class
Yaesu38RigTestcovering coalesced replies, partial tails, leading terminators, and multi-command inputs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| ft8af/app/src/main/java/com/k1af/ft8af/rigs/Yaesu38Rig.java | Drains and processes all complete CAT commands per receive chunk, preserving only the unterminated tail to avoid dropping coalesced replies. |
| ft8af/app/src/test/java/com/k1af/ft8af/rigs/Yaesu38RigTest.java | Adds unit tests that lock in correct framing behavior, including the ALC+SWR coalesced-reply regression. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ly dropped) (#669) The FT-450, DX10-series and Wolf-SDR handlers share the same one-command- per-read reassembly the FT-891 (#665) and FT-2000 (#667) fixes already corrected: onReceiveData parsed only the FIRST ';'-terminated command in a read and re-buffered the remainder WITH its retained terminator. The meter poll sends the ALC and SWR reads back-to-back (setRead39Meters_ALC() then _SWR()), so the transport routinely coalesces both replies ("RM4nnn;RM6nnn;") into one read. The old parse consumed only the ALC frame; the next poll's clearBufferData() then wiped the re-buffered SWR frame, so the SWR reading was permanently lost and the high-SWR safety alert never fired during TX. The retained ';' also poisoned the following read's parse. Fix: drain every complete command per read via the shared CatLineSplitter (introduced by #665, the text sibling of CivFrameSplitter), carrying only the unterminated tail into the next call. Per-command handling is extracted verbatim into processCommand so onReceiveData stays a thin drain loop; this also consolidates three byte-identical copies onto the shared splitter and collapses a latent double getFrequency() call. No protocol/behavior change beyond no longer dropping coalesced replies. Tests: added a CatLineSplitterTest case modelling the gen-3 meter poll (coalesced ALC+partial-SWR read then the SWR tail) proving the SWR frame survives. Full :app:testDebugUnitTest green. Co-authored-by: Optio Agent <optio-agent@noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Yaesu38Rig("YAESU FT-2000 series") loses its SWR meter reading during TX. On every meter poll the rig is asked for two meters back-to-back —RM4;(ALC) thenRM6;(SWR) — and the transport routinely delivers both replies coalesced in a singleonReceiveDatachunk ("RM4nnn;RM6nnn;"). The old parser handled only the first;-terminated command and re-buffered the remainder with its terminator; the next poll'sclearBufferData()then wiped that carry-over, so the second (SWR) reply was permanently dropped.Net effect for FT-2000 users: the SWR bar never moves during transmit, and the high-SWR safety alert never fires.
This is the same root cause already fixed for the FT-891 in #665, on a distinct, still-affected rig.
Root cause
onReceiveDatawas a one-command-per-read parser:Any second complete command in the same chunk was left in the buffer with its trailing
;and then destroyed by the nextreadMeters()/readFreqFromRig()clearBufferData(). Because the SWR read is always the second of the two, it is the one consistently lost.Fix
onReceiveDatanow drains every complete;-terminated command from the accumulated buffer and carries only the unterminated tail into the next read. The framing decision is extracted into a pure, package-private static helper so it can be unit-tested without the rig'sTimer-scheduling constructor:splitCommands(String) -> Frames— pureString → {List<String> commands, String remainder}.processCommand(String)— the existing per-command FA/FB/RM handling, unchanged in behavior.Yaesu3Command.getFrequency()call.The
> 1000-char no-terminator overflow guard is preserved.Kept self-contained (no new shared class) deliberately: #665 introduces a shared
CatLineSplitterbut is still open, so adding my own copy would collide. Once #665 merges, the remaining identical Yaesu gen-3 siblings (FTDX-450 / DX10 / Wolf-SDR) can adopt the shared helper as follow-ups.Testing
Yaesu38RigTest— 8 pure-JVM cases (no Robolectric; the helper is Android-free): single command, two coalesced meter replies both drained (the regression), complete + partial tail, no terminator, empty input, leading terminator, split-across-reads reassembly via remainder carry, three-commands-plus-tail../gradlew :app:testDebugUnitTest— full suite green;Yaesu38RigTest8/8.Risk
Low and localized to one rig's read path. No protocol/DSP change; frame semantics are unchanged for the common single-command case, and the fix only adds recovery of previously-dropped coalesced commands. No hardware was available, so validation is via the unit tests and the identical-to-#665 root cause.
Related
Yaesu38_450Rig,YaesuDX10Rig,Wolf_sdr_450Rigremain follow-ups.🤖 Generated with Claude Code