feat: Yaesu panel — sideband on re-click, split readout, one-touch up 1/up 5
Three corrections from the operator's second pass. The sideband gesture was wrong: I used double-click, which hides the action. Clicking a button that is ALREADY active now flips its sideband — CW-U → CW-L → CW-U. One button, one finger, nothing to discover. A lit SPLIT chip does not tell an operator anything useful: it says split is on, not where they transmit. The header now shows the TX frequency and the offset in kHz whenever split is active. And the offset that matters is set in one action: up 1 kHz on CW, up 5 kHz on phone. Doing it by hand means swapping VFOs, retuning and swapping back — exactly the fumbling a panel exists to remove. Both are offered rather than picked from the mode, because which one is idiomatic is the operator's call, and the button turns split on at the same time. The offset is measured from the RECEIVE frequency and written to the VFO we are not listening on, so it stays correct when the operator works on VFO B, where the roles are mirrored.
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
SetYaesuPower, SetYaesuMicGain, SetYaesuAFGain, SetYaesuRFGain, SetYaesuSquelch,
|
||||
SetYaesuAGC, SetYaesuPreamp, SetYaesuAtt, SetYaesuNB, SetYaesuNR, SetYaesuNRLevel,
|
||||
SetYaesuNarrow, SetYaesuVOX, SetYaesuSplit, SetYaesuBand, TuneYaesuATU,
|
||||
SetYaesuModeRaw, GetCATState,
|
||||
SetYaesuModeRaw, SetYaesuSplitOffset, GetCATState,
|
||||
} from '../../wailsjs/go/main/App';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
@@ -18,6 +18,7 @@ type YaesuState = {
|
||||
rf_power: number; mic_gain: number; af_gain: number; rf_gain: number; squelch: number;
|
||||
agc?: string; preamp: number; att: number;
|
||||
nb: boolean; nr: boolean; nr_level: number; narrow: boolean; vox: boolean;
|
||||
split_tx_hz?: number;
|
||||
};
|
||||
|
||||
const ZERO: YaesuState = {
|
||||
@@ -33,9 +34,10 @@ const ZERO: YaesuState = {
|
||||
const BANDS = ['160m', '80m', '40m', '30m', '20m', '17m', '15m', '12m', '10m', '6m'];
|
||||
|
||||
// Mode buttons. CW, RTTY, DIGI and PSK exist on BOTH sidebands on a Yaesu and
|
||||
// the operator is the one who knows which they want, so each carries its
|
||||
// current sideband and DOUBLE-CLICK flips it. SSB picks its sideband from the
|
||||
// frequency, as the band plan dictates, and AM/FM have none.
|
||||
// the operator is the one who knows which they want, so each shows its sideband
|
||||
// and CLICKING AN ACTIVE BUTTON AGAIN flips it: CW-U → CW-L → CW-U. One button,
|
||||
// one finger, no hidden gesture. SSB takes its sideband from the frequency, as
|
||||
// the band plan dictates, and AM/FM have none.
|
||||
//
|
||||
// PSK rides on the rig's DATA mode, like the other digital modes — the button
|
||||
// exists because the operator thinks in modes, not in what the radio calls them.
|
||||
@@ -294,12 +296,27 @@ export function YaesuPanel({ onReportRST }: { onReportRST?: (rst: string) => voi
|
||||
<div>
|
||||
<div className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">{st.model || 'Yaesu'}</div>
|
||||
<div className="text-2xl font-mono tabular-nums font-bold">{fmtVFO(freqHz)}</div>
|
||||
{view.split && (
|
||||
<div className="text-[11px] font-mono tabular-nums text-warning">
|
||||
{t('yaesu.txOn')} {fmtVFO(view.split_tx_hz)}
|
||||
{view.split_tx_hz && freqHz ? ' (' + (view.split_tx_hz > freqHz ? '+' : '') + Math.round((view.split_tx_hz - freqHz) / 100) / 10 + ' kHz)' : ''}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{st.transmitting && (
|
||||
<span className="px-2 py-1 rounded-md text-[11px] font-bold bg-destructive text-destructive-foreground">TX</span>
|
||||
)}
|
||||
<Chip on={view.split} onClick={() => push('split', !view.split, () => SetYaesuSplit(!view.split))} label="SPLIT" />
|
||||
{/* The usual pile-up offsets. Which one is idiomatic depends on the
|
||||
mode — up 5 on phone, up 1 on CW — so both are offered rather than
|
||||
guessed, and each turns split on in the same action. */}
|
||||
<button type="button" onClick={() => SetYaesuSplitOffset(1000).catch((e) => setErr(String(e?.message ?? e)))}
|
||||
title={t('yaesu.splitUpHint')}
|
||||
className="px-2 py-1 rounded-md text-[11px] font-bold border border-border bg-card text-muted-foreground hover:bg-muted">+1k</button>
|
||||
<button type="button" onClick={() => SetYaesuSplitOffset(5000).catch((e) => setErr(String(e?.message ?? e)))}
|
||||
title={t('yaesu.splitUpHint')}
|
||||
className="px-2 py-1 rounded-md text-[11px] font-bold border border-border bg-card text-muted-foreground hover:bg-muted">+5k</button>
|
||||
<button type="button" onClick={() => TuneYaesuATU().catch((e) => setErr(String(e?.message ?? e)))}
|
||||
title={t('yaesu.tuneHint')}
|
||||
className="px-2 py-1 rounded-md text-[11px] font-bold border border-border bg-card text-muted-foreground hover:bg-muted">
|
||||
@@ -342,8 +359,13 @@ export function YaesuPanel({ onReportRST }: { onReportRST?: (rst: string) => voi
|
||||
const flip: 'U' | 'L' = side === 'U' ? 'L' : 'U';
|
||||
return (
|
||||
<button key={m.id} type="button"
|
||||
onClick={() => { if (m.id === 'SSB') { SetYaesuModeRaw(freqHz > 0 && freqHz < 10_000_000 ? 'LSB' : 'USB').catch((e) => setErr(String(e?.message ?? e))); } else { SetYaesuModeRaw(m.rig(side)).catch((e) => setErr(String(e?.message ?? e))); } }}
|
||||
onDoubleClick={() => { if (m.sideband) SetYaesuModeRaw(m.rig(flip)).catch((e) => setErr(String(e?.message ?? e))); }}
|
||||
onClick={() => {
|
||||
// Already on this mode → the click means "the other sideband".
|
||||
const target = m.id === 'SSB'
|
||||
? (freqHz > 0 && freqHz < 10_000_000 ? 'LSB' : 'USB')
|
||||
: m.rig(on && m.sideband ? flip : side);
|
||||
SetYaesuModeRaw(target).catch((e) => setErr(String(e?.message ?? e)));
|
||||
}}
|
||||
title={m.sideband ? t('yaesu.sidebandHint') : undefined}
|
||||
className={cn('px-2 py-1 rounded-md text-[11px] font-bold border transition-colors',
|
||||
on ? 'bg-primary border-primary text-primary-foreground' : 'bg-card text-muted-foreground border-border hover:bg-muted')}>
|
||||
|
||||
Reference in New Issue
Block a user