fix: the Yaesu power meter is not linear — calibrate it on three points

One point could not reveal the curve. Scaling 207 = 100 W straight down read
30 W where the radio showed 10, and 75 where it showed 50 — wrong everywhere
except at the single point it was fitted to.

Three readings taken against the rig's own display give the shape:

  raw  62 → 10 W
  raw 155 → 50 W
  raw 207 → 100 W

Interpolating between them reproduces the radio exactly at those points and stays
close in between. I did not fit a formula: three samples can be made to support
several curves, and the operator can check a table against their own meter.

Above the top the last segment's slope continues rather than clamping, so a rig
driving an amplifier does not sit pinned at 100 W. A test pins the measured pairs
and the monotonicity, so a later change that breaks them fails against the radio
rather than against taste.
This commit is contained in:
2026-07-29 14:11:25 +02:00
parent 113faede14
commit e0cefb5c41
3 changed files with 87 additions and 13 deletions
+43
View File
@@ -118,3 +118,46 @@ func TestMeterPeakHold(t *testing.T) {
t.Errorf("settled at %d, want 10 — the meter must converge on the truth", v)
}
}
// The power curve, pinned to the readings taken against the rig's own display.
//
// It is NOT linear, and one calibration point could not show that: scaling
// 207 = 100 W straight down read 30 W where the radio showed 10, and 75 where it
// showed 50. These are the three measured pairs, so a change to the curve that
// breaks them is a regression against the radio, not against a preference.
func TestYaesuPowerCurve(t *testing.T) {
cases := []struct {
raw int
watts float64
tol float64
}{
{0, 0, 0.1},
{62, 10, 0.5}, // measured
{155, 50, 0.5}, // measured
{207, 100, 0.5}, // measured
// Between the measured points it interpolates, so it must land inside the
// bracket rather than shooting past it.
{100, 30, 10},
{180, 75, 10},
}
for _, c := range cases {
got := yaesuWatts(c.raw)
if got < c.watts-c.tol || got > c.watts+c.tol {
t.Errorf("yaesuWatts(%d) = %.1f W, want %.1f ±%.1f", c.raw, got, c.watts, c.tol)
}
}
// Monotonic: more meter must never mean less power.
prev := -1.0
for raw := 0; raw <= 255; raw++ {
v := yaesuWatts(raw)
if v < prev {
t.Fatalf("power fell from %.1f to %.1f at raw=%d", prev, v, raw)
}
prev = v
}
// Above the top of the curve it keeps rising rather than flattening at 100 W —
// a rig driving an amplifier can read past its own full scale.
if v := yaesuWatts(230); v <= 100 {
t.Errorf("yaesuWatts(230) = %.1f, want more than 100 — the curve should extend", v)
}
}