diff --git a/changelog.json b/changelog.json index 54597ea..8f7f13a 100644 --- a/changelog.json +++ b/changelog.json @@ -3,6 +3,7 @@ "version": "0.21.9", "date": "2026-07-28", "en": [ + "CW speed is now one setting wherever you change it: the Yaesu console slider and the CW panel drive the keyer that is actually sending, and the rig own keyer follows so its front panel agrees.", "Antenna Genius: port A no longer jumps onto port B antenna a few seconds after a change. Only one port can hold the transmit antenna, so the switch reports the same one on both — the display now follows each port own receive antenna.", "CW through the Yaesu keyer: a fifth CW engine sends your macros with the radio own keyer (CAT \"KY\"), for the FTDX101 / FT-991A / FT-710 family. An FTDX10 does not accept it — DAKY included — and OpsLog says so, pointing to the serial DTR keyer on the rig second COM port, which works.", "CW through the Yaesu keyer: a fifth CW engine sends your macros with the radio own keyer (CAT \"KY\") — no WinKeyer, no second cable. Pick \"Yaesu (rig keyer)\" in Settings → CW keyer, with the Yaesu CAT backend active.", @@ -19,6 +20,7 @@ "CQ and ITU zones you correct by hand in your profile stay corrected. They are derived from cty.dat, which gives the zones of the whole entity — in a country spanning several, the automatic value is wrong and it came back at every restart. They now only fill in when empty, and recompute when the callsign or grid changes." ], "fr": [ + "La vitesse CW est désormais un réglage unique où que vous la changiez : le curseur de la console Yaesu et le panneau CW pilotent le manipulateur qui émet réellement, et le keyer interne de la radio suit pour que sa façade affiche la même valeur.", "Antenna Genius : le port A ne bascule plus sur l'antenne du port B quelques secondes après un changement. Un seul port peut porter l'antenne d'émission, le switch renvoie donc la même sur les deux — l'affichage suit désormais l'antenne de réception propre à chaque port.", "CW par le keyer Yaesu : un cinquième moteur CW envoie vos macros avec le keyer de la radio (CAT « KY »), pour la famille FTDX101 / FT-991A / FT-710. Un FTDX10 ne l'accepte pas — DAKY compris — et OpsLog le dit en renvoyant vers le keyer série DTR sur son second port COM, qui fonctionne.", "CW par le keyer Yaesu : un cinquième moteur CW envoie vos macros avec le keyer de la radio (CAT « KY ») — sans WinKeyer ni second câble. À choisir dans Réglages → Keyer CW sous « Yaesu (keyer de la radio) », avec le backend CAT Yaesu actif.", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c72be25..d1e1b78 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -503,6 +503,9 @@ export default function App() { // CAT — receives live rig state via Wails events. const [catState, setCatState] = useState({ enabled: false, connected: false } as any); + // Live copy for callbacks that must not close over a stale snapshot. + const catStateRef = useRef(catState); + useEffect(() => { catStateRef.current = catState; }, [catState]); // Configured CAT backend ('icom' USB vs 'icom-net'): the live catState.backend // is "icom" for BOTH, so we track the settings value to tell them apart (used to // hide the rig ON/OFF buttons on USB, where the interface is unpowered when the @@ -966,6 +969,25 @@ export default function App() { // auto-call and are shared; only the transport differs. const [wkEngine, setWkEngine] = useState('winkeyer'); const cwSource: 'winkeyer' | 'icom' | 'flex' | 'yaesu' = wkEngine === 'icom' ? 'icom' : wkEngine === 'flex' ? 'flex' : wkEngine === 'yaesu' ? 'yaesu' : 'winkeyer'; + // Setting the CW speed has to reach the keyer that is ACTUALLY sending, and + // both the CW panel and the Yaesu console can ask for it. With DTR/RTS line + // keying the PC does the timing, so the rig's internal keyer speed changes + // nothing audible — the Yaesu console's slider looked broken because it drove + // that one alone. It now drives both: the rig's keyer for its own front panel, + // and the engine doing the work. + const setCWSpeedEverywhere = useCallback((w: number) => { + setWkWpm(w); saveWk({ wpm: w }); + const src = cwSourceRef.current; + if (src === 'icom') IcomSetKeySpeed(w).catch(() => {}); + else if (src === 'flex') FlexSetKeySpeed(w).catch(() => {}); + else if (src === 'yaesu') SetYaesuKeySpeed(w).catch(() => {}); + else WinkeyerSetSpeed(w).catch(() => {}); + // The rig's own keyer follows too whenever a Yaesu is on CAT, even when it is + // not the sending engine: its front panel and OpsLog then agree. + if (src !== 'yaesu' && catStateRef.current?.backend === 'yaesu') { + SetYaesuKeySpeed(w).catch(() => {}); + } + }, []); 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 @@ -4295,7 +4317,7 @@ export default function App() { case 'yaesu': return (
- { setRstSent(r); rstUserEditedRef.current = true; }} /> + { setRstSent(r); rstUserEditedRef.current = true; }} onKeySpeed={setCWSpeedEverywhere} />
); case 'icom': @@ -5245,13 +5267,7 @@ export default function App() { onRefreshPorts={reloadWkPorts} onConnect={() => WinkeyerConnect().catch((e) => setError(String(e?.message ?? e)))} onDisconnect={() => WinkeyerDisconnect().catch(() => {})} - onSetSpeed={(w) => { - setWkWpm(w); saveWk({ wpm: w }); - if (cwSource === 'icom') IcomSetKeySpeed(w).catch(() => {}); - else if (cwSource === 'flex') FlexSetKeySpeed(w).catch(() => {}); - else if (cwSource === 'yaesu') SetYaesuKeySpeed(w).catch(() => {}); - else WinkeyerSetSpeed(w).catch(() => {}); - }} + onSetSpeed={setCWSpeedEverywhere} onSend={wkSend} onSendMacro={wkSendMacro} onStop={() => { stopAutoCall(); stopKeyerTx(); }} @@ -5813,7 +5829,7 @@ export default function App() { {/* Yaesu CAT control panel — only when the CAT backend is a Yaesu. */} {catState.backend === 'yaesu' && ( - { setRstSent(r); rstUserEditedRef.current = true; }} /> + { setRstSent(r); rstUserEditedRef.current = true; }} onKeySpeed={setCWSpeedEverywhere} /> )} diff --git a/frontend/src/components/YaesuPanel.tsx b/frontend/src/components/YaesuPanel.tsx index d0f4c5b..2c25867 100644 --- a/frontend/src/components/YaesuPanel.tsx +++ b/frontend/src/components/YaesuPanel.tsx @@ -223,7 +223,14 @@ function Row({ label, children }: { label: string; children: React.ReactNode }) ); } -export function YaesuPanel({ onReportRST }: { onReportRST?: (rst: string) => void }) { +export function YaesuPanel({ onReportRST, onKeySpeed }: { + onReportRST?: (rst: string) => void; + // Told whenever the operator moves the CW speed here, so the app can keep the + // keyer that is ACTUALLY sending in step. With DTR/RTS line keying the PC does + // the timing and the rig's internal keyer speed changes nothing audible — the + // slider looked broken because it was driving the wrong keyer. + onKeySpeed?: (wpm: number) => void; +}) { const { t } = useI18n(); const [st, setSt] = useState(ZERO); // The frequency being LISTENED to. RigState follows ADIF, where freq_hz is the @@ -465,7 +472,7 @@ export function YaesuPanel({ onReportRST }: { onReportRST?: (rst: string) => voi push('key_speed', v, () => SetYaesuKeySpeed(v))} /> + onChange={(v) => { push('key_speed', v, () => SetYaesuKeySpeed(v)); onKeySpeed?.(v); }} /> {view.key_speed || 20} wpm
diff --git a/internal/cat/yaesu.go b/internal/cat/yaesu.go index 8e82927..6a2a946 100644 --- a/internal/cat/yaesu.go +++ b/internal/cat/yaesu.go @@ -104,6 +104,8 @@ type Yaesu struct { panelLoaded bool // Commands this rig answered "?;" to — asked once, then never again. unsupported map[string]bool + // metersSurveyed: the one-shot RM1..RM6 dump during the first transmission. + metersSurveyed bool } func NewYaesu(portName string, baud int, digital string) *Yaesu { diff --git a/internal/cat/yaesu_panel.go b/internal/cat/yaesu_panel.go index d11b8c0..07ab58a 100644 --- a/internal/cat/yaesu_panel.go +++ b/internal/cat/yaesu_panel.go @@ -121,6 +121,26 @@ func (y *Yaesu) readPanel(mode string, split bool, txHz int64) { y.panel.SMeter = scale255(v) } if y.panel.Transmitting { + // One-shot survey of every meter while transmitting. + // + // Which RM index carries which meter is NOT the same across the family, and + // an operator reported an SWR bar at 80 with a real SWR of 1.1 — the shape + // of reading the wrong index (ALC, say) rather than of a scaling error. + // Guessing again would just move the wrong number; this prints all six + // once, and the log then says which is which on THIS radio. + if !y.metersSurveyed { + y.metersSurveyed = true + raw := make([]string, 0, 6) + for i := 1; i <= 6; i++ { + cmd := fmt.Sprintf("RM%d;", i) + if v, ok := y.askNum(cmd, fmt.Sprintf("RM%d", i), 3); ok { + raw = append(raw, fmt.Sprintf("RM%d=%d", i, v)) + } else { + raw = append(raw, fmt.Sprintf("RM%d=-", i)) + } + } + debugLog.Printf("yaesu: meters while transmitting: %s (send this if a bar reads wrong)", strings.Join(raw, " ")) + } if v, ok := y.askNum("RM4;", "RM4", 3); ok { y.panel.PowerMeter = scale255(v) }