feat: CW through the Yaesu keyer — fifth CW engine
The radio has a keyer and a command to feed it (KY), so an FTDX10 needs no WinKeyer and no second cable, exactly as the Icom CI-V and Flex CWX engines already do. The rig keys with its own timing, which is why the spacing is right where a PC keying a line through USB latency drifts. Text is filtered to what the keyer can actually send: an unsupported byte does not produce an error on a Yaesu, it can abort the whole buffer, so the rest of a macro would vanish silently. It is then fed in 24-character pieces, waiting for room between them — the rig DROPS what does not fit, again with no error, so a contest CQ would lose its tail. STOP is the honest gap. Yaesu documents no buffer-clear, so it drops the transmitter (TX0) instead: nothing queued reaches the air, which is what Escape means to an operator. It deliberately does NOT send "KY0;" — a plausible-looking clear that the rig would read as the CHARACTER zero and transmit. A test caught a real one on the way: tabs and newlines were dropped as "unsupported", gluing the words either side together, so a macro written on two lines went out as CQCQ. Whitespace now becomes a word gap before filtering. Settings warn when the Yaesu keyer is selected without the Yaesu CAT backend — otherwise it simply never keys, with nothing on screen saying why. Send path follows the CAT reference and how Hamlib drives these rigs; NOT yet verified on the air.
This commit is contained in:
@@ -38,7 +38,7 @@ import {
|
||||
ListCountries,
|
||||
GetWinkeyerSettings, SaveWinkeyerSettings, ListSerialPorts, GetWinkeyerStatus,
|
||||
WinkeyerConnect, WinkeyerDisconnect, WinkeyerSend, WinkeyerStop, WinkeyerSetSpeed, WinkeyerBackspace,
|
||||
IcomSendCW, IcomStopCW, IcomSetKeySpeed, IcomSetBreakIn, GetIcomState,
|
||||
IcomSendCW, YaesuSendCW, YaesuStopCW, IcomStopCW, IcomSetKeySpeed, IcomSetBreakIn, GetIcomState,
|
||||
FlexSendCW, FlexStopCW, FlexSetKeySpeed, FlexBackspaceCW,
|
||||
GetDVKMessages, GetDVKStatus, DVKPlay, DVKStop,
|
||||
StartCWDecoder, StopCWDecoder, SetCWDecoderPitch,
|
||||
@@ -965,7 +965,7 @@ export default function App() {
|
||||
// CI-V 0x17 (no extra hardware — sends over the CAT connection). Macros,
|
||||
// auto-call and <LOGQSO> are shared; only the transport differs.
|
||||
const [wkEngine, setWkEngine] = useState<string>('winkeyer');
|
||||
const cwSource: 'winkeyer' | 'icom' | 'flex' = wkEngine === 'icom' ? 'icom' : wkEngine === 'flex' ? 'flex' : 'winkeyer';
|
||||
const cwSource: 'winkeyer' | 'icom' | 'flex' | 'yaesu' = wkEngine === 'icom' ? 'icom' : wkEngine === 'flex' ? 'flex' : wkEngine === 'yaesu' ? 'yaesu' : 'winkeyer';
|
||||
const cwSourceRef = useRef(cwSource);
|
||||
useEffect(() => { cwSourceRef.current = cwSource; }, [cwSource]);
|
||||
// CW break-in (0=OFF, 1=SEMI, 2=FULL) — must be on for the rig's 0x17 keyer to
|
||||
@@ -1001,6 +1001,7 @@ export default function App() {
|
||||
useEffect(() => {
|
||||
const connected = cwSource === 'icom' ? (catState.backend === 'icom' && catState.connected)
|
||||
: cwSource === 'flex' ? (catState.backend === 'flex' && catState.connected)
|
||||
: cwSource === 'yaesu' ? (catState.backend === 'yaesu' && catState.connected)
|
||||
: wkStatus.connected;
|
||||
wkActiveRef.current = wkEnabled && connected;
|
||||
}, [wkEnabled, wkStatus.connected, cwSource, catState.backend, catState.connected]);
|
||||
@@ -2558,7 +2559,7 @@ export default function App() {
|
||||
// current WPM, so it scales automatically.
|
||||
const keyed = resolved + ' ';
|
||||
setWkSent(resolved);
|
||||
const sendFn = cwSourceRef.current === 'flex' ? FlexSendCW : cwSourceRef.current === 'icom' ? IcomSendCW : null;
|
||||
const sendFn = cwSourceRef.current === 'flex' ? FlexSendCW : cwSourceRef.current === 'icom' ? IcomSendCW : cwSourceRef.current === 'yaesu' ? YaesuSendCW : null;
|
||||
if (sendFn) await sendFn(keyed).catch((e) => setError(String(e?.message ?? e)));
|
||||
else await WinkeyerSend(keyed).catch((e) => setError(String(e?.message ?? e)));
|
||||
// Only WAIT for the CW to finish on the FINAL segment — auto-call needs the
|
||||
@@ -2591,6 +2592,7 @@ export default function App() {
|
||||
wkSendGenRef.current++; // cancel any in-flight macro send (and its pending <LOGQSO> log)
|
||||
if (cwSourceRef.current === 'icom') IcomStopCW().catch(() => {});
|
||||
else if (cwSourceRef.current === 'flex') FlexStopCW().catch(() => {});
|
||||
else if (cwSourceRef.current === 'yaesu') YaesuStopCW().catch(() => {});
|
||||
else WinkeyerStop().catch(() => {});
|
||||
}
|
||||
// runAutoCall sends macro i, waits for the keyer to finish, waits the chosen
|
||||
@@ -3495,6 +3497,7 @@ export default function App() {
|
||||
stopAutoCall();
|
||||
wkSendGenRef.current++; // abort an in-flight macro send so a pending <LOGQSO> won't fire
|
||||
if (cwSourceRef.current === 'icom') IcomStopCW().catch(() => {});
|
||||
else if (cwSourceRef.current === 'yaesu') YaesuStopCW().catch(() => {});
|
||||
else if (cwSourceRef.current === 'flex') FlexStopCW().catch(() => {});
|
||||
else WinkeyerStop().catch(() => {});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user