Skip to content

Add safe Xiegu G106/DE-19 support and fixed receive timing - #922

Merged
patrickrb merged 5 commits into
patrickrb:devfrom
jedisdead666:codex/g106-support
Oct 27, 2026
Merged

patrickrb merged 5 commits into
patrickrb:devfrom
jedisdead666:codex/g106-support

Conversation

@jedisdead666

@jedisdead666 jedisdead666 commented Oct 10, 2026 •

Copy link
Copy Markdown

Summary

  • add Android G106/G106C profiles with CI-V address 0x70, 19,200 baud, and the required CAT PTT keepalive
  • add a focused desktop G106 direct-CI-V backend and a pinned rig-picker option
  • prevent DE-19 hardware PTT assertion by requesting DTR low on open and explicitly clearing both DTR and RTS before CAT initialization and on close
  • fail the connection unless CAT PTT-off succeeds before mode/frequency setup
  • label generic ALSA C-Media devices as DE-19 audio choices and document Linux dependencies/setup
  • disable the unsafe desktop receive DT auto-calibration introduced before 0.1.2
  • clear legacy rx_offset_ms values at startup and anchor decode/waterfall windows to UTC slots

Field bugs fixed

  • Linux serial open could assert a DE-19 hardware PTT line and key the G106 immediately
  • rx_offset_ms=-4000 could prevent every decode after restart
  • a successful cycle at DT +0.7 to +1.0 could save approximately +528 ms, chop the next slot leading sync block, and prevent later decodes

Safety and timing regression coverage

  • verifies G106 CAT PTT on/off frame polarity
  • verifies both DTR and RTS are released, including when the first release fails
  • verifies PTT-off happens before data-mode initialization and failure rejects the connection
  • verifies the 1 Hz keepalive is limited to keyed radios that require it
  • verifies legacy -4000 and +528 RX offsets reset to zero on restart
  • verifies a decoded batch at DT +0.7 through +1.0 cannot move future receive windows

Verification

  • frontend: 33 tests passed; production TypeScript/Vite build passed
  • Rust: 135 tests passed (124 library, 9 binary, 2 integration)
  • version 0.1.2 AppImage built, extracted, and launched from an isolated empty profile
  • packaged runtime reset a deliberately seeded rx_offset_ms=-4000 to zero on restart
  • Android unit tests are included; local execution is unavailable because this Linux host has no Android SDK configured

@jedisdead666 jedisdead666 changed the title Add safe Xiegu G106 and DE-19 support Add safe Xiegu G106/DE-19 support and fixed receive timing Oct 10, 2026
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.22680% with 75 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.43%. Comparing base (4d67bc0) to head (df59e40).
⚠️ Report is 2 commits behind head on dev.

Files with missing lines Patch % Lines
desktop/src-tauri/src/rig.rs 69.95% 61 Missing ⚠️
desktop/src-tauri/src/engine.rs 84.09% 14 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #922      +/-   ##
============================================
+ Coverage     41.88%   42.43%   +0.54%     
  Complexity      309      309              
============================================
  Files           283      283              
  Lines         34651    34909     +258     
  Branches       4219     4219              
============================================
+ Hits          14515    14812     +297     
+ Misses        19807    19768      -39     
  Partials        329      329              
Flag Coverage Δ
android 18.77% <ø> (ø)
desktop 66.61% <74.22%> (+2.66%) ⬆️
native 12.70% <ø> (ø)

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

Files with missing lines Coverage Δ
desktop/src-tauri/src/engine.rs 46.22% <84.09%> (+14.49%) ⬆️
desktop/src-tauri/src/rig.rs 40.69% <69.95%> (+21.39%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Android: XIEGU_G106 collided with HAMLIB (26), a duplicate switch
  case; move it to 28 (27 is claimed by the open QMX PR patrickrb#946) and add
  a uniqueness test over InstructionSet.
- Android: the keepalive re-entered setPTT(true) from the poll thread,
  so racing an unkey could re-key the rig indefinitely. It now resends
  the frame only, under a lock shared with setPTT.
- rigaddress.txt is append-only: move the new G106(USB) row to the end
  and pin it in GOLDEN_ORDER.
- Desktop: only the G106 backend rejects a connection on failed
  PTT-off (FLrig/Hamlib keep their lazy behaviour); drive the keepalive
  off a monotonic clock; end the over when a keepalive fails.
- Desktop: move audioDeviceLabel to a tested module and stop calling
  every generic C-Media card a DE-19.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@patrickrb

Copy link
Copy Markdown
Owner

Review round 1

This is the Claude code-review (high) fallback, used because Copilot review isn't available on this repo.

# Finding Outcome
1 Android build break: InstructionSet.XIEGU_G106 = 26 reuses InstructionSet.HAMLIB = 26. MainViewModel's rig switch then has two identical case labels, which does not compile. The ,26 rows in rigaddress.txt would also have been treated as Hamlib rigs. Fixed. The constant is now XIEGU_G106 = 28, and both G106 rows use 28. 27 is skipped because the open QMX PR #946 claims it. A new test checks that every InstructionSet id is unique.
2 Android stuck-TX race: the keepalive ran on the poll Timer thread and called super.setPTT(true). That also writes isPttOn = true and fires onPttChanged. If the tick saw "on" and the TX thread then unkeyed, the tick sent PTT-on after PTT-off and set the flag back to true. Every later tick then refreshed PTT-on, so the radio stayed keyed. Fixed. The keepalive now only resends the CI-V PTT-on frame and never touches the PTT state or listeners. It shares a lock with setPTT, so its frame cannot reach the wire after a PTT-off. Tests cover the frame-only refresh, a keepalive after unkey, and a deterministic race (keepalive parked mid-send while the TX thread unkeys).
3 rigaddress.txt insert: XIEGU G106(USB) was inserted mid-file. The selected rig is persisted as a list index, so every rig below it moves down one for existing users (RigAddressAssetTest fails). Fixed. The row is now appended at the end and added to GOLDEN_ORDER. Changing the existing G106C(USB) row's instruction set in place is fine, because the index stays the same. Merge order: #933 and #946 also append to the end of this file and of GOLDEN_ORDER. Whichever of the three merges second or third gets a trivial conflict on the last line and must keep all entries in merge order.
4 Desktop: initialize_connection failed the connection on a failed PTT-off for every backend. That breaks FLrig when FT8AF starts before FLrig (it used to recover lazily), and Hamlib rigs without CAT PTT (VOX, receivers) whose rig_set_ptt always errors. Fixed. A new RigTransport::ptt_off_required_on_connect() defaults to false. Only the G106 backend sets it, so the G106 still fails closed and FLrig/Hamlib behave as before. There are tests for both paths.
5 Desktop: the G106 keepalive rode the 1 Hz UI tick, which runs on the NTP-corrected clock. A backward clock step mid-over could stall it past the G106's ~2 s PTT release. Fixed. The keepalive is now checked on every loop pass against a monotonic Instant, recorded when PTT-on was last sent, with a 1 s interval.
6 Desktop: a failed keepalive only emitted an error, once per second. Audio kept playing into a radio that drops PTT on its own. Fixed. On keepalive failure the engine ends the over: it stops playback and sends a best-effort PTT-off.
7 audioDeviceLabel had no test (CLAUDE.md rule). It also labeled every ALSA CARD=Device card as "DE-19", but that is the generic name for many CM108-style codecs (Digirig, SignaLink-style, dongles). Fixed. It moved to desktop/src/audioDevice.ts with a vitest suite. The label is now "USB audio codec (C-Media, e.g. DE-19)".
8 Removing RX auto-calibration removes the only compensation for capture latency. The decode trigger is fixed at 13.2 s UTC, 0.56 s after the waveform ends. Left for the maintainer. I think disabling it is right: the old median-DT feedback could run away (the -4000 clamp in the field report), and WSJT-X doesn't auto-shift its RX window either. The UTC slot math is correct: the slice is the last into_cycle ms of audio, front-aligned. The cost is that decoded DT now includes your capture latency, and setups with more than about 0.5 s of input latency (some PipeWire/VB-Cable/SDR/DAX chains) can lose the waveform tail. Open question: add a bounded manual RX offset setting for those users, or accept the WSJT-X-like behaviour.
9 XIEGU G106C(U-DIG) still uses instruction set 13 (XieGu6100Rig), which has no keepalive. A G106C user on that row with CAT PTT would still be dropped after about 2 s. Left for the maintainer. This PR only covers and was only field-tested on the USB rows. Moving the U-DIG row to the G106 driver also changes its mode command (USB instead of U-DIG), so it needs someone with the radio to confirm.
10 The desktop manifests (package.json, Cargo.toml, tauri.conf.json) are hand-bumped 0.1.0 → 0.1.2. Left for the maintainer. desktop.yml derives release versions from desktop-v* tags, so the manifest bump may disagree with the tag-derived release number. Keep it or drop it.
11 The Android G106 poll interval drops to 1 s for RX frequency reads too, not just for the TX keepalive. Not changed. A 6-byte CI-V read once a second at 19,200 baud is negligible. A separate TX-only timer would add a second thread writing to the port, which is what the lock in #2 avoids.
12 rx_offset_ms is written as "0" on every launch, and the field/event plumbing remains. Not changed. The write is cheap and keeps a downgraded build from re-reading a bad legacy offset. The IPC field is kept for compatibility, as the PR intends.
13 Review note: "isPttOn is not volatile." Not changed. False positive: BaseRig.isPttOn is already volatile. The real problem was the check-then-act race in #2.

PTT safety on open, close and failure otherwise checks out. The G106 desktop backend opens with dtr_on_open(false) and then clears both DTR and RTS, attempting both even if one fails. It sends PTT-off before any mode or frequency command and refuses the connection if that fails. On drop it releases both lines again. The keepalive runs only while keyed (self.ptt) and stops on finalize_tx / unkey / disconnect. If the link drops, the G106's own ~2 s CAT PTT release is the backstop.

Fix commit: 89d062f

Tests run locally (Windows):

  • Android: testDebugUnitTest --tests com.k1af.ft8af.rigs.* --tests com.k1af.ft8af.database.RigNameListTest --tests com.k1af.ft8af.database.RigAddressAssetTest: BUILD SUCCESSFUL (XieGuG106RigTest 8/8, RigAddressAssetTest 5/5, RigNameListTest 9/9; the build also compiles MainViewModel, which hit the duplicate case before the fix). RigAddressAssetTest.ic7300mk2_isTheLastEntry is now lastEntry_isTheLastGoldenName, the same rename Add Yaesu FTX-1 to the rig list (#931) #933 makes, so those two PRs conflict less.
  • Desktop Rust: cargo test --lib: 128 passed (includes the new connect/keepalive tests).
  • Desktop frontend: vitest run 36 passed (includes the new audioDevice.test.ts), tsc --noEmit clean.

Not verified: anything on a real G106/DE-19 (Linux DTR-on-open behaviour, keepalive timing on air), on Android or desktop.

@patrickrb

Copy link
Copy Markdown
Owner

Maintainer decisions on the open review items

Thanks for the thorough work on this. Here's how we're handling the items left open in review round 1:

# Item Decision
8 RX auto-calibration removed Accepted as-is. The old median-DT feedback could run away, and fixed UTC slots like WSJT-X are the safer default. A bounded manual RX offset for high-latency setups is tracked in #956. It isn't needed for this PR.
9 G106C(U-DIG) row has no keepalive Out of scope here. It isn't a regression. Tracked in #957: let XieGuG106Rig take the mode so the U-DIG row keeps its U-DIG mode command and gets the keepalive. If you or someone you know runs a G106C on U-DIG, an on-air test there would be very welcome.
10 Desktop manifests hand-bumped to 0.1.2 Please revert the version changes in desktop/package.json, package-lock.json, src-tauri/Cargo.toml, Cargo.lock and tauri.conf.json. The real problem was that CI never wrote the tag version into the manifests, so every release reported 0.1.0. #955 fixes that in CI, so feature PRs should leave the manifests alone.
11–13 Not changed Agreed on all three.

Before merge

  1. Revert the version bump (item 10).
  2. On-air test of the final commit on a G106 with DE-19, on Android and/or desktop: key up for a full over (well past the ~2 s CAT PTT release) and confirm the radio stays keyed, unkeys cleanly at the end, and the transmission decodes (PSKReporter spot or a second station). One quick test with a G106 on Linux desktop would also cover the DTR-on-open behaviour.
  3. Merge order with Add Yaesu FTX-1 to the rig list (#931) #933 / Add QRP Labs QMX to Android rig picker (#945) #946: whichever lands later keeps every appended row in rigaddress.txt and GOLDEN_ORDER, in merge order.

🤖 Generated with Claude Code

patrickrb and others added 2 commits October 27, 2026 08:36
Resolves the rig-list conflict with patrickrb#933 (Yaesu FTX-1, already on dev):
FTX-1 keeps its index and "XIEGU G106(USB)" is appended after it in
rigaddress.txt and GOLDEN_ORDER; dev's ftx1_usesTheFt891991UsbProfile
test is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the rig-list conflict with patrickrb#946 and patrickrb#925 (both QMX entries are on
dev now): "QRP Labs QMX" and "Qrp Labs QMX (Hamlib)" keep their indexes and
"XIEGU G106(USB)" is appended after them in rigaddress.txt and
GOLDEN_ORDER. InstructionSet's comment no longer calls 27 an open PR's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@patrickrb
patrickrb merged commit 5a1f7f5 into patrickrb:dev Oct 27, 2026
21 checks passed
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