fix(yaesu): mic gain scale, 200 W models, and a NAR button that did nothing
Reported from a real FTDX101, and each one is a different kind of wrong. MIC GAIN was read and written through the 0-255 scale the audio gains use, but the CAT reference gives MG000-100. A rig set to 80 therefore showed 38, and moving the slider sent 204 — outside the range the radio accepts, so it refused the command and the slider sprang back. That snap-back was the symptom; the scale was the cause. POWER was capped at 100 W by the slider, not by the radio. There is no CAT command for 'how much power can you make', so the ceiling comes from the model name, with the rig believed if it ever reports more than the table expects — it has just proved what it can do. NAR is the narrow IF filter, and on a rig that does not implement NA the button showed a state the radio never gave and did nothing when pressed, which reads as a fault in the radio. Now shown only when the rig answers, with a tooltip saying what it is. Also: the frequency readout steps the Hz digits under the wheel, not just the kHz ones. Zero-beating a CW signal is a few tens of Hz and it was the one move the display would not make. And the split chaser is CW-only — its marker comes from a CW skimmer.
This commit is contained in:
+19
-5
@@ -244,10 +244,15 @@ function fmtFreqDots(mhzStr: string): string {
|
||||
}
|
||||
|
||||
// FreqWheelDisplay renders a frequency (MHz string) like fmtFreqDots — MHz.kHz.Hz
|
||||
// — but makes the three kHz digits scroll-sensitive: rolling the mouse wheel over
|
||||
// the hundreds / tens / units-of-kHz digit steps the frequency by 100 / 10 / 1 kHz
|
||||
// (up = wheel up). onNudge receives the delta in Hz. The MHz and Hz digits are
|
||||
// static (only kHz stepping was requested). Used in the header + compact top bar.
|
||||
// — and makes the kHz AND Hz digits scroll-sensitive: rolling the wheel over a
|
||||
// digit steps the frequency by that digit (100/10/1 kHz, then 100/10/1 Hz; up =
|
||||
// wheel up). onNudge receives the delta in Hz.
|
||||
//
|
||||
// The Hz digits were static at first, on the grounds that only kHz stepping had
|
||||
// been asked for. An FTDX101 operator put it plainly: the fine tuning is exactly
|
||||
// what a mouse is wanted for — zero-beating a CW signal or nudging onto an SSB
|
||||
// voice is a few tens of Hz, and it was the one move the display would not make.
|
||||
// The MHz digits stay static: a wheel notch that changes band is not fine tuning.
|
||||
function FreqWheelDisplay({ mhz, onNudge, className, placeholder = '—.———.———' }: {
|
||||
mhz: string; onNudge: (deltaHz: number) => void; className?: string; placeholder?: string;
|
||||
}) {
|
||||
@@ -257,6 +262,7 @@ function FreqWheelDisplay({ mhz, onNudge, className, placeholder = '—.——
|
||||
const khz = frac.slice(0, 3); // [hundreds, tens, units] of kHz
|
||||
const hz = frac.slice(3, 6);
|
||||
const stepHz = [100_000, 10_000, 1_000]; // per kHz digit: 100 / 10 / 1 kHz
|
||||
const stepHzFine = [100, 10, 1]; // per Hz digit
|
||||
return (
|
||||
<span className={className}>
|
||||
{intPart}.
|
||||
@@ -268,7 +274,15 @@ function FreqWheelDisplay({ mhz, onNudge, className, placeholder = '—.——
|
||||
{d}
|
||||
</span>
|
||||
))}
|
||||
.{hz}
|
||||
.
|
||||
{hz.split('').map((d, i) => (
|
||||
<span key={'h' + i}
|
||||
onWheel={(e) => { if (e.ctrlKey || e.metaKey) return; e.preventDefault(); e.stopPropagation(); onNudge(e.deltaY < 0 ? stepHzFine[i] : -stepHzFine[i]); }}
|
||||
className="cursor-ns-resize rounded-[2px] hover:bg-primary/25 transition-colors"
|
||||
title="Scroll to change frequency">
|
||||
{d}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -677,6 +677,10 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
|
||||
in the log, which is done mid-QSO with one hand. The offset
|
||||
sits beside it because it is the one number retuned while
|
||||
chasing — everything else about the feature is configured once. */}
|
||||
{/* CW ONLY. The marker comes from a skimmer decoding a CW report,
|
||||
so on SSB or FT8 there is nothing to chase — and a switch that
|
||||
cannot fire is an invitation to wonder why it does nothing. */}
|
||||
{isCW && (<>
|
||||
<button type="button" disabled={off}
|
||||
title={t('flxp.rstChaseHint', { m: rstChase.markers })}
|
||||
onContextMenu={(e) => { e.preventDefault(); setRstChaseEditing(true); }}
|
||||
@@ -725,6 +729,7 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
|
||||
Hz
|
||||
</label>
|
||||
)}
|
||||
</>)}
|
||||
{st.split && !!st.tx_freq_hz && (
|
||||
<span className="text-[11px] font-mono text-muted-foreground whitespace-nowrap">
|
||||
TX {(st.tx_freq_hz / 1e6).toFixed(3)}
|
||||
|
||||
@@ -18,7 +18,8 @@ type YaesuState = {
|
||||
s_meter: number; power_meter: number; swr_meter: number;
|
||||
rf_power: number; mic_gain: number; af_gain: number; rf_gain: number; squelch: number;
|
||||
agc?: string; preamp: number; att: number; antenna: number;
|
||||
nb: boolean; nr: boolean; nr_level: number; narrow: boolean; vox: boolean;
|
||||
nb: boolean; nr: boolean; nr_level: number; narrow: boolean; narrow_supported?: boolean; vox: boolean;
|
||||
max_power?: number;
|
||||
split_tx_hz?: number; key_speed?: number; break_in?: boolean; swr?: number; power_w?: number;
|
||||
};
|
||||
|
||||
@@ -445,7 +446,13 @@ export function YaesuPanel({ onReportRST, onKeySpeed }: {
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Chip on={view.nb} onClick={() => push('nb', !view.nb, () => SetYaesuNB(!view.nb))} label="NB" />
|
||||
<Chip on={view.nr} onClick={() => push('nr', !view.nr, () => SetYaesuNR(!view.nr))} label="DNR" />
|
||||
<Chip on={view.narrow} onClick={() => push('narrow', !view.narrow, () => SetYaesuNarrow(!view.narrow))} label="NAR" />
|
||||
{/* NAR is the narrow IF filter. Shown only when the rig ANSWERED NA:
|
||||
a button that does nothing when pressed reads as a fault in the
|
||||
radio, which is how it was reported from an FTDX101. */}
|
||||
{view.narrow_supported && (
|
||||
<Chip on={view.narrow} onClick={() => push('narrow', !view.narrow, () => SetYaesuNarrow(!view.narrow))}
|
||||
label="NAR" title={t('yaesu.narrowHint')} />
|
||||
)}
|
||||
</div>
|
||||
<Row label="DNR">
|
||||
{/* 1-15 on the rig, shown as-is rather than rescaled to a percentage:
|
||||
@@ -462,7 +469,7 @@ export function YaesuPanel({ onReportRST, onKeySpeed }: {
|
||||
<Row label="PWR">
|
||||
{/* Watts, not a percentage: the rig reports and takes watts, and a
|
||||
percentage would be a second unit to reconcile every time. */}
|
||||
<Slider value={view.rf_power || 5} min={5} max={100} accent="var(--destructive)"
|
||||
<Slider value={view.rf_power || 5} min={5} max={Math.max(100, view.max_power || 100)} accent="var(--destructive)"
|
||||
onChange={(v) => push('rf_power', v, () => SetYaesuPower(v))} />
|
||||
<span className="w-10 text-right text-xs font-mono tabular-nums text-muted-foreground">{view.rf_power}W</span>
|
||||
</Row>
|
||||
|
||||
@@ -109,7 +109,7 @@ const en: Dict = {
|
||||
'settings.pane.map1': 'Map — great-circle + beam', 'settings.pane.map2': 'Map — locator (street)',
|
||||
'settings.pane.cluster': 'Cluster spots', 'settings.pane.worked': 'Worked before',
|
||||
'settings.pane.recent': 'Recent QSOs', 'settings.pane.netcontrol': 'Net control', 'settings.pane.decodes': 'FT decodes',
|
||||
'settings.pane.flex': 'Flex Console', 'settings.pane.icom': 'Icom Console', 'settings.pane.yaesu': 'Yaesu Console', 'yaesu.notConnected': 'Yaesu CAT not connected', 'yaesu.meters': 'Meters', 'yaesu.bandMode': 'Band & mode', 'yaesu.receive': 'Receive', 'yaesu.noiseFilter': 'Noise & filter', 'yaesu.transmit': 'Transmit', 'yaesu.refresh': 'Refresh', 'yaesu.tuneHint': 'Start an antenna-tuner cycle', 'yaesu.sToRst': 'Click to fill the RST sent', 'yaesu.sidebandHint': 'Click to select this mode; click again to switch sideband (U/L)', 'yaesu.splitUpHint': 'Transmit this far above the receive frequency, and turn split on', 'yaesu.txOn': 'TX', 'yaesu.cw': 'CW', 'yaesu.breakInHint': 'Break-in: the rig switches to receive between characters', 'yaesu.zinHint': 'Zero-in: retune so the station you hear lands on your CW pitch',
|
||||
'settings.pane.flex': 'Flex Console', 'settings.pane.icom': 'Icom Console', 'settings.pane.yaesu': 'Yaesu Console', 'yaesu.notConnected': 'Yaesu CAT not connected', 'yaesu.meters': 'Meters', 'yaesu.bandMode': 'Band & mode', 'yaesu.receive': 'Receive', 'yaesu.noiseFilter': 'Noise & filter', 'yaesu.transmit': 'Transmit', 'yaesu.refresh': 'Refresh', 'yaesu.narrowHint': 'Narrow IF filter (NAR) — tightens the receive bandwidth. Only shown when the radio answers the command.', 'yaesu.tuneHint': 'Start an antenna-tuner cycle', 'yaesu.sToRst': 'Click to fill the RST sent', 'yaesu.sidebandHint': 'Click to select this mode; click again to switch sideband (U/L)', 'yaesu.splitUpHint': 'Transmit this far above the receive frequency, and turn split on', 'yaesu.txOn': 'TX', 'yaesu.cw': 'CW', 'yaesu.breakInHint': 'Break-in: the rig switches to receive between characters', 'yaesu.zinHint': 'Zero-in: retune so the station you hear lands on your CW pitch',
|
||||
'theme.auto': 'Auto (system)', 'theme.light-warm': 'Warm light', 'theme.light-cool': 'Cool light',
|
||||
'theme.light-sage': 'Sage light', 'theme.light-nordic': 'Nordic light', 'theme.sahara': 'Sahara', 'theme.dim-slate': 'Dim slate', 'theme.dark-warm': 'Warm dark',
|
||||
'theme.dark-graphite': 'Graphite dark', 'theme.dark-indigo': 'Indigo', 'theme.dark-teal': 'Ocean', 'theme.dark-plum': 'Plum', 'theme.high-contrast': 'High contrast',
|
||||
@@ -596,7 +596,7 @@ const fr: Dict = {
|
||||
'settings.pane.map1': 'Carte — orthodromie + faisceau', 'settings.pane.map2': 'Carte — locator (rue)',
|
||||
'settings.pane.cluster': 'Spots cluster', 'settings.pane.worked': 'Déjà contactés',
|
||||
'settings.pane.recent': 'QSO récents', 'settings.pane.netcontrol': 'Gestion de net', 'settings.pane.decodes': 'Decodes FT',
|
||||
'settings.pane.flex': 'Flex Console', 'settings.pane.icom': 'Icom Console', 'settings.pane.yaesu': 'Yaesu Console', 'yaesu.notConnected': 'CAT Yaesu non connecté', 'yaesu.meters': 'Mesures', 'yaesu.bandMode': 'Bande et mode', 'yaesu.receive': 'Réception', 'yaesu.noiseFilter': 'Bruit et filtre', 'yaesu.transmit': 'Émission', 'yaesu.refresh': 'Actualiser', 'yaesu.tuneHint': "Lancer un cycle d'accord d'antenne", 'yaesu.sToRst': 'Cliquer pour remplir le RST envoyé', 'yaesu.sidebandHint': 'Cliquer pour choisir ce mode ; recliquer pour changer de bande latérale (U/L)', 'yaesu.splitUpHint': "Émettre à cette distance au-dessus de la fréquence de réception, et activer le split", 'yaesu.txOn': 'TX', 'yaesu.cw': 'CW', 'yaesu.breakInHint': "Break-in : la radio repasse en réception entre les caractères", 'yaesu.zinHint': "Zéro-in : réaccorde pour que la station entendue tombe sur votre note CW",
|
||||
'settings.pane.flex': 'Flex Console', 'settings.pane.icom': 'Icom Console', 'settings.pane.yaesu': 'Yaesu Console', 'yaesu.notConnected': 'CAT Yaesu non connecté', 'yaesu.meters': 'Mesures', 'yaesu.bandMode': 'Bande et mode', 'yaesu.receive': 'Réception', 'yaesu.noiseFilter': 'Bruit et filtre', 'yaesu.transmit': 'Émission', 'yaesu.refresh': 'Actualiser', 'yaesu.narrowHint': "Filtre FI étroit (NAR) — resserre la bande passante de réception. N'apparaît que si la radio répond à la commande.", 'yaesu.tuneHint': "Lancer un cycle d'accord d'antenne", 'yaesu.sToRst': 'Cliquer pour remplir le RST envoyé', 'yaesu.sidebandHint': 'Cliquer pour choisir ce mode ; recliquer pour changer de bande latérale (U/L)', 'yaesu.splitUpHint': "Émettre à cette distance au-dessus de la fréquence de réception, et activer le split", 'yaesu.txOn': 'TX', 'yaesu.cw': 'CW', 'yaesu.breakInHint': "Break-in : la radio repasse en réception entre les caractères", 'yaesu.zinHint': "Zéro-in : réaccorde pour que la station entendue tombe sur votre note CW",
|
||||
'theme.auto': 'Auto (système)', 'theme.light-warm': 'Clair chaud', 'theme.light-cool': 'Clair froid',
|
||||
'theme.light-sage': 'Clair sauge', 'theme.light-nordic': 'Clair nordique', 'theme.sahara': 'Sahara', 'theme.dim-slate': 'Ardoise tamisé', 'theme.dark-warm': 'Sombre chaud',
|
||||
'theme.dark-graphite': 'Sombre graphite', 'theme.dark-indigo': 'Indigo', 'theme.dark-teal': 'Océan', 'theme.dark-plum': 'Prune', 'theme.high-contrast': 'Contraste élevé',
|
||||
|
||||
Reference in New Issue
Block a user