diff --git a/changelog.json b/changelog.json index a1a1b30..386301c 100644 --- a/changelog.json +++ b/changelog.json @@ -1,4 +1,16 @@ [ + { + "version": "0.27.1", + "date": "", + "en": [ + "Elecraft console: the S-meter is calibrated against a real K3 — S9 and the +dB readings now match the radio’s own display (they read about two S-units low).", + "Elecraft console: an SWR spike (the K3’s own blip at the end of an FT8 frame) no longer fades over twelve seconds — the peak shows briefly, then the meter returns straight to the live reading." + ], + "fr": [ + "Console Elecraft : le S-mètre est calibré sur un vrai K3 — S9 et les +dB correspondent désormais à l’affichage de la radio (il lisait environ deux points S trop bas).", + "Console Elecraft : un pic de ROS (le sursaut du K3 en fin de trame FT8) ne s’estompe plus pendant douze secondes — le pic s’affiche brièvement, puis le ros-mètre revient directement à la valeur réelle." + ] + }, { "version": "0.27.0", "date": "", diff --git a/frontend/src/components/ElecraftPanel.tsx b/frontend/src/components/ElecraftPanel.tsx index 0ec38ad..0908c7e 100644 --- a/frontend/src/components/ElecraftPanel.tsx +++ b/frontend/src/components/ElecraftPanel.tsx @@ -42,18 +42,22 @@ const ZERO: KenwoodState = { // nobody notices are missing. const FILTERS = [200, 400, 700, 1000, 1800, 2400, 2800, 4000]; -// Raw S-meter → S units. The K3 answers 0-21 across S0…S9+60; S9 is taken at -// raw 9 and each step above it as 6 dB. PROVISIONAL, like the rest of the -// scaling: the raw value is on screen and in the log, so a real radio settles -// it rather than this comment. -const S9_RAW = 9; -const DB_PER_RAW = 6; -function sParts(rawV: number): { s: number; over: number; label: string } { - if (rawV >= S9_RAW) { - const over = Math.max(0, Math.round((rawV - S9_RAW) * DB_PER_RAW)); +// Raw S-meter → S units, CALIBRATED against a real K3 beside its own display +// (2026-08): raw 5 reads S7 on the radio, raw 9 reads S9+20. So S9 sits near +// raw 6.5 — not 9, which showed everything two S-units low — and each raw step +// above it is worth ~8 dB, shown in the 10 dB steps the K3's own meter uses. +// A Kenwood answers 0-30 on the same command and keeps the simple 1-per-raw +// scale until someone calibrates one against a real radio too. +const K3_S9_RAW = 6.5; +const K3_DB_PER_RAW = 8; +function sParts(rawV: number, elecraft: boolean): { s: number; over: number; label: string } { + const s9raw = elecraft ? K3_S9_RAW : 9; + if (rawV >= s9raw) { + let over = Math.max(0, Math.round((rawV - s9raw) * (elecraft ? K3_DB_PER_RAW : 6))); + if (elecraft) over = Math.round(over / 10) * 10; return { s: 9, over, label: over > 0 ? `S9+${over}` : 'S9' }; } - const s = Math.max(0, Math.min(9, rawV)); + const s = Math.max(0, Math.min(9, Math.round(rawV * (elecraft ? 9 / K3_S9_RAW : 1)))); return { s, over: 0, label: `S${s}` }; } @@ -211,10 +215,10 @@ export function ElecraftPanel({ onReportRST }: { onReportRST?: (rst: string) =>
{ if (view.transmitting || !onReportRST) return; - const sp = sParts(view.s_meter_raw); + const sp = sParts(view.s_meter_raw, view.elecraft); onReportRST(sMeterRST(sp.s, sp.over, view.mode)); }} title={t('k3.sMeterHint', { raw: String(view.s_meter_raw) })} /> diff --git a/internal/cat/kenwood_panel.go b/internal/cat/kenwood_panel.go index f1b6604..72657f2 100644 --- a/internal/cat/kenwood_panel.go +++ b/internal/cat/kenwood_panel.go @@ -349,7 +349,7 @@ func (k *Kenwood) readTXMeters() { if v, ok := k.askNum("SW;", "SW", 3); ok { k.panel.SWRRaw = v if v > 0 { - k.panel.SWR = float64(k.swrPeak.update(v, now)) / 10 + k.panel.SWR = float64(k.swrPeak.updateSnap(v, now)) / 10 } } if k.metersLogged >= 20 { diff --git a/internal/cat/yaesu_panel.go b/internal/cat/yaesu_panel.go index 82db697..7683671 100644 --- a/internal/cat/yaesu_panel.go +++ b/internal/cat/yaesu_panel.go @@ -905,3 +905,22 @@ func yaesuSplitCommand(cmd, vfo string, on bool) string { } return cmd + "0;" } + +// updateSnap is update for a meter where lingering is misinformation: the peak +// stands for the hold, then the display returns to the live sample AT ONCE. +// Made for the K3's SWR — the radio throws a brief SWR spike as an FT8 frame +// ends, and the quarter-of-the-gap decay above turned that one bad sample into +// twelve seconds of alarming red on the next transmission. A needle's inertia +// suits a power meter; an SWR reading is a warning light, and a warning that +// fades slowly reads as a fault that is slowly getting better. +func (m *meterPeak) updateSnap(sample int, now time.Time) int { + if sample >= m.val { + m.val, m.at = sample, now + return m.val + } + if now.Sub(m.at) < meterHold { + return m.val + } + m.val, m.at = sample, now + return m.val +}