Skip to content

js8core: compound-call commands and SNR as desktop, GCC includes - #108

Merged
punk-kaos merged 4 commits into
JS8Call-improved:masterfrom
randal007:compound-cmd-desktop
Oct 14, 2026
Merged

punk-kaos merged 4 commits into
JS8Call-improved:masterfrom
randal007:compound-cmd-desktop

Conversation

@randal007

@randal007 randal007 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Hi again! Thanks for pulling the #104 fixes into the core in #105. Two more from the X6100 port (a third, withdrawn, is struck out below), each its own commit:

1. Commands and SNR to and from compound calls (KG4UHM/6) packed as desktop. pack_cmd() / unpack_cmd() used (cmd & 0x1F) << 2 | (num & 0x03). Desktop's Varicode::packCmd() packs an SNR command as [1][type][6-bit number] (type 1 = HEARTBEAT SNR) and any other command as its 7-bit value. So every command in a compound-directed frame differed from desktop's, and an SNR kept only 2 bits. Found on the air 2026-10-11: my reply "KG4UHM/6 SNR -03" went out as SNR -31, and KG4UHM/6's heartbeat ACKs (from desktop) showed up here as "HW CPY?". Now desktop's two functions. varicode_roundtrip gets 14 new checks: frames to and from KG4UHM/6 against desktop's own buildMessageFrames() (built with Qt), and desktop's compound frames reading back with their command and SNR.

2. Ultra (JS8 60) decoded on Turbo's schedule. Withdrawn (reverted in 5a108cf): I misread desktop. It decodes JS8 60 once a slot at samplesNeeded(), as the engine already does; thanks @punk-kaos (see the comments).

3. <functional> and <cstring> includes. varicode.hpp uses std::function and jsc.cpp std::strncmp; libc++ pulls the headers in transitively, GCC's libstdc++ doesn't always.

Tested: these are the same changes as local patches 16 and 1 in the X6100 port's copy of the core (third-party/js8core, with your #105 changes there too). That copy's tests pass, including the new varicode_roundtrip checks, built with GCC. Patch 16 has been on the air on X6100s since beta 5.1 (2026-10-12). I haven't built this branch with the Android toolchain.

73, Randal VE7NHW

🤖 Generated with Claude Code

randal007 and others added 3 commits October 13, 2026 11:47
pack_cmd()/unpack_cmd() packed a compound-directed frame's command as
(cmd & 0x1F) << 2 | (num & 0x03). Desktop's Varicode::packCmd() packs an
SNR command as [1][type][6-bit number] (type 1 = HEARTBEAT SNR) and any
other command as its 7-bit value. So every command to or from a compound
call (KG4UHM/6, EA8/K1ABC) differed from desktop's, and an SNR kept only
2 bits: a reply "KG4UHM/6 SNR -03" went out as SNR -31, and a compound
station's heartbeat ACK read back as "HW CPY?". Seen on the air
2026-10-11 between an X6100 running js8core and KG4UHM/6 on desktop.

varicode_roundtrip: frames to and from KG4UHM/6 checked against desktop's
own buildMessageFrames() (built with Qt), and desktop's compound frames
read back with their command and SNR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Desktop JS8Call-improved (mainwindow.cpp, turboOrUltra) schedules JS8 60
exactly as Turbo: decode once the 79 symbols are in, retry every second.
The engine gave only Turbo (C) that path; Ultra (I) got one decode a slot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
varicode.hpp uses std::function and jsc.cpp std::strncmp without the
headers. Android's libc++ pulls them in transitively; GCC's libstdc++
doesn't always (building the core for the X6100 with GCC failed on them).

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

Copy link
Copy Markdown
Contributor

I see one small bug in your Ultra timing code, you removed
if (sm.id == protocol::SubmodeId::C ||
sm.id == protocol::SubmodeId::I) {
samples_needed = sm.symbol_samples * JS8_NUM_SYMBOLS;
retry_samples = sample_rate;

That removes the start-delay allowance and 500 ms guard from Ultra's decode threshold.
That means you're running your first decode at 2.528s, when its impossible to have all 79 symbols in less than 2.628s. That means you're trying to decode 100ms before you have enough symbols and essentially wasting the first decode attempt then catching it on the second.

Desktop decodes JS8 60 once a slot at samplesNeeded() (79 symbols + start
delay + 0.5 s), which is what the engine already did for I. The early
window made Ultra's first try 100 ms before the signal can be complete
(punk-kaos on JS8Call-improved#108).

This reverts commit 00aaf3f.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GD9GNPL51haF49BmUbaxRf
@randal007 randal007 changed the title js8core: compound-call commands and SNR as desktop, Ultra on Turbo's schedule, GCC includes js8core: compound-call commands and SNR as desktop, GCC includes Oct 14, 2026
@randal007

Copy link
Copy Markdown
Contributor Author

Thanks, you're right, and the bigger mistake was mine: I misread desktop. Its turboOrUltra branch decodes Turbo and Ultra once a slot at samplesNeeded() (79 symbols + start delay + 0.5 s), not as soon as the symbols are in. So the engine's own path for I was already desktop's. I've reverted the Ultra commit here (5a108cf) and in our copy. The other two commits stand.

Out of curiosity I measured how much the early try actually loses. Generated single-frame signals, decoded with legacy_decode on a window cut off where each attempt would fire, 20 noise seeds per point (DT = how late the sending station's clock is).

Ultra (first try 2.528 s, desktop 3.128 s, retry 3.528 s):

first desktop retry
−9 dB and up, DT −0.2 to +0.5 ≈ all ≈ all ≈ all
−11 dB, DT 0 17/20 18/20 18/20
−13 dB, DT +0.3 2/20 5/20 6/20
−13 dB, DT +0.5 0/20 6/20 6/20

So the first try does decode on-time signals (the missing 100 ms is mostly the last Costas block), but loses weak or late ones, which then show up 0.4 s after desktop would have had them.

Turbo, for your information only (it's your design, and it holds up): first try 3.95 s, desktop 4.55 s, retry 4.95 s. With DT −0.2 to +0.1 the first try equals desktop at every SNR. At DT +0.3 it misses some even when strong (−9 dB: 12/20, 0 dB: 18/20, desktop 20/20); the retry at 4.95 s gets every one desktop gets. Oddly DT +0.3 was worse than +0.5 on the first try (−9 dB: 12/20 vs 20/20); I haven't dug into why. The same 100 ms start delay applies to Turbo, so its first try is also before the end of the signal.

73, Randal VE7NHW

@punk-kaos
punk-kaos merged commit e4c39bd into JS8Call-improved:master Oct 14, 2026
1 check 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