feat: colour the Yaesu S-meter like a real one — green, amber past S9, red at +20
The bar was one colour for its whole travel, so nothing distinguished a signal that is merely readable from one that belongs in the log as 59+20. Green to S9, amber through the S9+ range, red from +20 dB. The thresholds are computed from the SAME S9 point the label uses rather than hard-coded percentages: the S9 position is still a hypothesis on this rig, and when it is corrected the colours have to move with it or the meter would say 59+20 in amber.
This commit is contained in:
@@ -105,15 +105,31 @@ function bandOfHz(hz?: number): string {
|
|||||||
// falls; the front panel puts it at roughly half travel, which is the 50 used
|
// falls; the front panel puts it at roughly half travel, which is the 50 used
|
||||||
// here. That figure is a HYPOTHESIS — if reports come out consistently one S
|
// here. That figure is a HYPOTHESIS — if reports come out consistently one S
|
||||||
// unit off on a radio, this is the number to correct, not the RST helper.
|
// unit off on a radio, this is the number to correct, not the RST helper.
|
||||||
|
const S9_PCT = 50; // where S9 falls on the 0-100 reading (see above)
|
||||||
|
const DB_PER_PCT = 60 / 50; // above S9 the scale runs to roughly +60 dB
|
||||||
|
|
||||||
function sParts(v: number): { s: number; over: number; label: string } {
|
function sParts(v: number): { s: number; over: number; label: string } {
|
||||||
if (v >= 50) {
|
if (v >= S9_PCT) {
|
||||||
const over = Math.max(0, Math.round((v - 50) * 60 / 50));
|
const over = Math.max(0, Math.round((v - S9_PCT) * DB_PER_PCT));
|
||||||
return { s: 9, over, label: over > 0 ? `S9+${over}` : 'S9' };
|
return { s: 9, over, label: over > 0 ? `S9+${over}` : 'S9' };
|
||||||
}
|
}
|
||||||
const s = Math.max(0, Math.min(9, Math.round(v / 5.5)));
|
const s = Math.max(0, Math.min(9, Math.round(v / (S9_PCT / 9))));
|
||||||
return { s, over: 0, label: `S${s}` };
|
return { s, over: 0, label: `S${s}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Segment colour, the way a radio's own meter is printed: green up to S9, amber
|
||||||
|
// through the S9+ range, red once the signal is strong enough to be reported as
|
||||||
|
// 59+20 or more. Derived from the SAME S9 point as the label, so the colour
|
||||||
|
// change always lands exactly where the numbers say it should — if the S9 point
|
||||||
|
// is ever corrected, the colours follow on their own.
|
||||||
|
const RED_OVER_DB = 20;
|
||||||
|
function sSegColor(frac: number): string {
|
||||||
|
const pct = frac * 100;
|
||||||
|
if (pct < S9_PCT) return '#16a34a';
|
||||||
|
if ((pct - S9_PCT) * DB_PER_PCT < RED_OVER_DB) return '#f59e0b';
|
||||||
|
return '#dc2626';
|
||||||
|
}
|
||||||
|
|
||||||
function Slider({ value, onChange, disabled, accent = 'var(--primary)', min = 0, max = 100 }: {
|
function Slider({ value, onChange, disabled, accent = 'var(--primary)', min = 0, max = 100 }: {
|
||||||
value: number; onChange: (v: number) => void; disabled?: boolean; accent?: string; min?: number; max?: number;
|
value: number; onChange: (v: number) => void; disabled?: boolean; accent?: string; min?: number; max?: number;
|
||||||
}) {
|
}) {
|
||||||
@@ -311,7 +327,7 @@ export function YaesuPanel({ onReportRST }: { onReportRST?: (rst: string) => voi
|
|||||||
consoles read alike instead of each having its own instrument style. */}
|
consoles read alike instead of each having its own instrument style. */}
|
||||||
<Card icon={Activity} title={t('yaesu.meters')}>
|
<Card icon={Activity} title={t('yaesu.meters')}>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
|
||||||
<MeterBar label="S-METER" value={view.s_meter} lo={0} hi={100} accent="#16a34a"
|
<MeterBar label="S-METER" value={view.s_meter} lo={0} hi={100} accent="#16a34a" segColor={sSegColor}
|
||||||
display={sParts(view.s_meter).label}
|
display={sParts(view.s_meter).label}
|
||||||
onClick={onReportRST ? () => { const sp = sParts(view.s_meter); onReportRST(sMeterRST(sp.s, sp.over, view.mode)); } : undefined}
|
onClick={onReportRST ? () => { const sp = sParts(view.s_meter); onReportRST(sMeterRST(sp.s, sp.over, view.mode)); } : undefined}
|
||||||
title={onReportRST ? t('yaesu.sToRst') : undefined} />
|
title={onReportRST ? t('yaesu.sToRst') : undefined} />
|
||||||
|
|||||||
Reference in New Issue
Block a user