Skip to content

fix(halikey): pre-roll straight-key sending so dahs stay inside characters - #4

Open
tcpreplay-dev wants to merge 1 commit into
feature/halikey-straight-key-bugfrom
feature/straight-key-preroll
Open

tcpreplay-dev wants to merge 1 commit into
feature/halikey-straight-key-bugfrom
feature/straight-key-preroll

Conversation

@tcpreplay-dev

Copy link
Copy Markdown
Owner

Stacked on #3. Fixes a defect confirmed by ear on real hardware: a hand-sent "V" was
copied as "S T"
— every dah following a dit rendered as a letter break.

Cause

An element's duration isn't known until the key is released, so by the time the command is
sent the radio has finished the previous element and gone silent. That silence becomes the
gap:

on-air gap = your gap + (this element − previous element)

Dit-to-dit cancels, which is why "S" was always clean and only the dah broke.

Fix

Set KZL, documented in the K4 reference (Rev D5, resources/k4_cat_commands.json) as
"remote paddle key-down initial delay", 0–1000 ms. Measured onset:

KZL onset
0 ~90 ms (baseline)
500 608 ms
1000 ~1100 ms

Linear and exact. Holding the radio's element queue non-empty is what stops it going idle
between elements.

Required pre-roll exceeds one gap plus the longest element — 320 ms failed where 560 ms was
needed and 600 ms sufficed, hence the 1.3 margin:

L = (1200 / min_wpm) × (1 + dah_dit_ratio) × 1.3, capped at KZL's 1000 ms

Expressed to the operator as speed bounds rather than milliseconds (UI in the follow-up PR).

Notable

The existing gap arithmetic was already correct — when the radio is busy it resolves to
the operator's true gap. Only busyUntil needed to account for the pre-roll, since the radio
applies its initial delay solely on a key-down after idle. No client-side queue, timer or
buffering was required.

Also corrects two sites that sent KZL as 1200/wpm in a 2-digit field believing it to be
element length. Their behaviour for the iambic path is unchanged, but they no longer stomp
the straight-key value.

Off by default.

Testing

Verified on a K4 by ear: broken without the pre-roll, correct with it, including a
beginner's 500 ms dah once the pre-roll covers it. IambicKeyerTests passes.

…cters

Every dah following a dit was rendering as a letter break — a hand-sent "V"
copied as "S T", confirmed by ear on real hardware. Cause is structural: an
element's duration isn't known until the key is released, so by the time the
command is sent the radio has finished the previous element and gone silent,
and that silence becomes the gap. On-air gap was therefore

    your gap + (this element - previous element)

which cancels for dit-to-dit (hence "S" was always clean) but inflates every
dit-to-dah transition to roughly a letter space.

Fixed by setting KZL, which the K4 reference (Rev D5, resources/k4_cat_commands.json)
documents as "remote paddle key-down initial delay", 0-1000 ms. Measured: it delays
onset linearly and exactly (0 -> ~90ms baseline, 500 -> 608ms, 1000 -> ~1100ms).
Holding the radio's element queue non-empty is what stops it going idle between
elements. Confirmed by ear: broken without it, correct with it, including a
beginner's 500ms dah once the pre-roll covers it.

Required pre-roll exceeds one gap plus the longest element; 320ms failed where
560ms was needed and 600ms sufficed, so:

    L = (1200 / min_wpm) * (1 + dah_dit_ratio) * 1.3, capped at KZL's 1000ms

expressed to the operator as speed bounds rather than milliseconds. Defaults
15 WPM and 3:1 give 416ms.

Notably the existing gap arithmetic was already correct — when the radio is busy
it resolves to the operator's true gap. Only busyUntil needed to account for the
pre-roll, since the radio applies its initial delay solely on a key-down after
idle. No client-side queue, timer or buffering was needed.

Also corrects two sites that sent KZL as 1200/wpm in a 2-digit field believing it
to be element length (cwcontroller.cpp, mainwindow.cpp). Their behaviour for the
iambic path is unchanged, but they no longer stomp the straight-key value, and
the comments now say what KZL actually is.

Off by default; UI to follow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tcpreplay-dev
tcpreplay-dev force-pushed the feature/straight-key-preroll branch from e5d986a to dc25d28 Compare August 25, 2026 21:34
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.

1 participant