Skip to content

Fix Yaesu FT-817/857/897 set-frequency encoder tens-of-Hz nibble - #614

Merged
patrickrb merged 1 commit into
devfrom
optio/task-38cdc92a-0521-46ff-9112-529de8e453d3
Jul 22, 2026
Merged

Fix Yaesu FT-817/857/897 set-frequency encoder tens-of-Hz nibble#614
patrickrb merged 1 commit into
devfrom
optio/task-38cdc92a-0521-46ff-9112-529de8e453d3

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Root cause

Yaesu2RigConstant.setOperationFreq packs the low nibble of the last BCD frequency byte as freq % 100 — the entire 0–99 sub-100 Hz remainder crammed into a single 4-bit nibble:

, (byte) (((byte) (freq % 1000 / 100) << 4) + (byte) (freq % 100))   // last byte

But the rig's own decoder, Yaesu2Command.getFrequency, weights that same nibble ×10 as the tens-of-Hz digit:

+((int) (rawData[3] >> 4) & 0xf) * 100   // hundreds-of-Hz
+(int)  (rawData[3] & 0x0f)      * 10;   // tens-of-Hz

So the encoder is not the inverse of its own decoder. The 4-byte CAT block is 8 BCD nibbles with weights 1e8…1e1 (10 Hz LSB); the last nibble is the tens-of-Hz digit, i.e. freq % 100 / 10, not freq % 100.

Impact

  • setOperationFreq(14_074_050) (14.074 MHz + 50 Hz) round-trips to 14_074_320 (+270 Hz).
  • Remainders > 15 (e.g. ...58) produce a non-BCD nibble (0x3A+), corrupting the frequency by >1 kHz.
  • On live TX this keys the rig on the wrong VFO for any dial that isn't 100 Hz-aligned (10-Hz-resolution reads, custom bands — see issue Add ability to define custom dial frequencies in the band/frequency picker #470), pushing the FT8 signal off frequency so decodes/spots don't line up.

100 Hz-aligned dials (the common FT8 case, freq % 100 == 0) were unaffected, which is why this slipped past.

Fix

One-line change: pack the last nibble as the tens-of-Hz digit.

- , (byte) (((byte) (freq % 1000 / 100) << 4) + (byte) (freq % 100))
+ , (byte) (((byte) (freq % 1000 / 100) << 4) + (byte) (freq % 100 / 10))

The sub-10 Hz digit is below the rig's CAT resolution and is correctly dropped (the decoder has no nibble for it either). The encoder is now an exact inverse of the decoder for any 10 Hz-aligned VFO.

PR #497 fixed the matching decoder and explicitly left this encoder quirk untouched; this completes the pair.

Testing

  • Added setOperationFreq_packsTensOfHzInLastNibble — asserts 14.074050 MHz encodes the 50 Hz component to low nibble 0x05, not the raw remainder 0x32.
  • Updated getFrequency_roundTripsSetOperationFreqEncoding to a non-100 Hz-aligned dial (14_074_350) so it exercises the tens-of-Hz nibble that previously desynced.
  • Added getFrequency_dropsOnlySub10HzOnRoundTrip — documents that only the sub-10 Hz remainder is lost.
  • Refreshed the now-stale Javadoc in both test files describing the encoder as a known-broken limitation.
  • Confirmed red against the old encoder (3 failures) and green with the fix; full :app:testDebugUnitTest suite passes.

Risk

Very low. Pure integer-arithmetic change in one BCD encoder, no protocol/DSP/threading surface. Behavior for the dominant 100 Hz-aligned case is byte-for-byte unchanged; only non-100 Hz-aligned dials — previously encoded wrong — change, and now round-trip correctly.

Yaesu2RigConstant.setOperationFreq packed the low nibble of the last BCD
byte as `freq % 100` — the full 0-99 sub-100 Hz remainder crammed into a
single 4-bit nibble. The rig's own decoder (Yaesu2Command.getFrequency)
weights that nibble x10 as the tens-of-Hz digit, so the encoder and
decoder were not inverses: setOperationFreq(14_074_050) round-tripped to
14_074_320 (+270 Hz), and endings whose remainder exceeded 15 produced a
non-BCD nibble (>1 kHz off). On live TX this keys the rig on the wrong VFO
for any dial that is not 100 Hz-aligned (10-Hz-resolution reads, custom
bands per issue #470), pushing the FT8 signal off frequency.

Root cause: the last nibble must be the tens-of-Hz digit `freq % 100 / 10`
(the sub-10 Hz digit is below the rig's CAT resolution and is correctly
dropped). This makes the encoder an exact inverse of the decoder for any
10 Hz-aligned VFO.

PR #497 fixed the matching decoder and explicitly left this encoder quirk
untouched; this completes the pair. Pure-JVM tests added/updated in
Yaesu2RigConstantTest and Yaesu2CommandTest (tens-of-Hz encoding,
full round-trip, sub-10 Hz drop); confirmed red against the old encoder,
green with the fix. Full :app:testDebugUnitTest suite passes.

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

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 36.62%. Comparing base (c1696cb) to head (621df42).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##                dev     #614   +/-   ##
=========================================
  Coverage     36.62%   36.62%           
  Complexity      197      197           
=========================================
  Files           216      216           
  Lines         26885    26885           
  Branches       3294     3294           
=========================================
  Hits           9847     9847           
  Misses        16811    16811           
  Partials        227      227           
Flag Coverage Δ
android 15.03% <ø> (ø)
native 9.93% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 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

This pull request fixes Yaesu gen-2 (FT-817/857/897) CAT frequency encoding so that Yaesu2RigConstant.setOperationFreq correctly packs the tens-of-Hz digit into the low nibble of the last BCD byte, making the encoder consistent with the existing Yaesu2Command.getFrequency decoder (10 Hz CAT resolution).

Changes:

  • Fix setOperationFreq last-nibble packing from freq % 100 to freq % 100 / 10 (tens-of-Hz digit).
  • Add/adjust unit tests to cover non-100 Hz-aligned round-trips and document sub-10 Hz truncation.
  • Refresh test documentation comments to match the corrected encoder/decoder relationship.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
ft8af/app/src/main/java/com/k1af/ft8af/rigs/Yaesu2RigConstant.java Corrects the final BCD nibble packing to represent tens-of-Hz, aligning encoding with decoder expectations.
ft8af/app/src/test/java/com/k1af/ft8af/rigs/Yaesu2RigConstantTest.java Adds a focused test asserting the tens-of-Hz digit is packed into the last nibble.
ft8af/app/src/test/java/com/k1af/ft8af/rigs/Yaesu2CommandTest.java Updates the encoder↔decoder round-trip test to exercise sub-100 Hz digits and adds a test documenting sub-10 Hz loss.

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

@patrickrb
patrickrb merged commit 6ef5cca into dev Jul 22, 2026
18 checks passed
@patrickrb
patrickrb deleted the optio/task-38cdc92a-0521-46ff-9112-529de8e453d3 branch July 22, 2026 21:38
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