A second measurement at a known mismatch settled both the index and the scale. At SWR 1.1: RM6=0. At SWR 1.5: RM6=52, while RM4 kept tracking the power. 52/255 = 0.204, which is the reflection coefficient of a 1.5 SWR to three decimals. So the raw value is rho scaled to 255, and the ratio is (1+rho)/(1-rho) — physics, not a curve fitted through two points, which is why 2.0 and 3.0 fall out of it correctly without ever having been measured. The panel now shows that ratio, the number the operator reads on the rig, rather than a percentage of meter travel — and a dash while receiving, since a stale SWR from the last transmission reads as a live one. Both measurements are recorded in the code and in a test, so the mapping is evidence rather than a table borrowed from another model — which is exactly how it came to read 81 in the first place.
79 lines
2.5 KiB
Go
79 lines
2.5 KiB
Go
package cat
|
|
|
|
import "testing"
|
|
|
|
// What SPLIT means when the operator presses it.
|
|
//
|
|
// Flipping the rig's split flag alone transmits wherever the OTHER VFO happens
|
|
// to sit — reported from a real FTDX10: listening on 14.244 with VFO B left on
|
|
// 18.115 from an earlier session, pressing SPLIT threw the transmitter onto
|
|
// another band. The other VFO is stale by nature, so the transmit frequency has
|
|
// to be derived from where the operator is listening now.
|
|
func TestYaesuDefaultSplitOffset(t *testing.T) {
|
|
cases := []struct {
|
|
raw string
|
|
want int64
|
|
}{
|
|
// CW and the data modes work 1 kHz up.
|
|
{"CW-U", 1000},
|
|
{"CW-L", 1000},
|
|
{"RTTY-U", 1000},
|
|
{"RTTY-L", 1000},
|
|
{"DATA-U", 1000},
|
|
{"DATA-L", 1000},
|
|
// Phone works 5 kHz up.
|
|
{"USB", 5000},
|
|
{"LSB", 5000},
|
|
{"AM", 5000},
|
|
{"FM", 5000},
|
|
// Unknown or not yet read: the phone offset is the safer default — too
|
|
// wide is audible and obvious, too narrow lands on top of the DX.
|
|
{"", 5000},
|
|
}
|
|
for _, c := range cases {
|
|
y := &Yaesu{}
|
|
y.panel.RawMode = c.raw
|
|
if got := y.defaultSplitOffset(); got != c.want {
|
|
t.Errorf("mode %q → split offset %d Hz, want %d", c.raw, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The SWR scale, pinned to the two measurements it was derived from.
|
|
//
|
|
// Taken on an FTDX10 (2026-07-29) against an operator watching the rig's own
|
|
// meter: raw 0 at SWR 1.1, raw 52 at SWR 1.5. The second point is what proved
|
|
// the raw value is the reflection coefficient scaled to 255 — 52/255 = 0.204,
|
|
// rho for a 1.5 SWR — rather than a percentage of meter travel, which is how the
|
|
// bar came to read 81 on a perfect antenna.
|
|
func TestSWRFromReflection(t *testing.T) {
|
|
cases := []struct {
|
|
raw int
|
|
want float64
|
|
tol float64
|
|
}{
|
|
{0, 1.0, 0.01}, // no reflected power
|
|
{52, 1.5, 0.02}, // the measured mismatch
|
|
{85, 2.0, 0.05}, // rho = 1/3
|
|
{128, 3.0, 0.1}, // rho = 0.5
|
|
{-5, 1.0, 0.01}, // nonsense reading — never below 1.0, which is physical
|
|
{255, 9.9, 0.01}, // full scale is capped rather than infinite
|
|
}
|
|
for _, c := range cases {
|
|
got := swrFromReflection(c.raw)
|
|
if got < c.want-c.tol || got > c.want+c.tol {
|
|
t.Errorf("swrFromReflection(%d) = %.2f, want %.2f ±%.2f", c.raw, got, c.want, c.tol)
|
|
}
|
|
}
|
|
// It must rise with the reflected power, or a worsening match would read
|
|
// better on the panel than on the rig.
|
|
prev := 0.0
|
|
for raw := 0; raw <= 200; raw += 20 {
|
|
v := swrFromReflection(raw)
|
|
if v < prev {
|
|
t.Fatalf("SWR fell from %.2f to %.2f at raw=%d", prev, v, raw)
|
|
}
|
|
prev = v
|
|
}
|
|
}
|