fix: power is RM5, not RM4 — the CW keying was the experiment
An FM carrier could not answer it: constant by definition, so nothing to correlate. CW at 100 W did, because the keying itself varies the output: key down: RM4=25 RM5=207 RM6=13 key up: RM4=25 RM5=0 RM6=0 RM5 follows the RF envelope exactly — it IS the power meter. RM4 sits near 25 whether the key is down or up, so it is not measuring output at all, and reading it as power is what showed 8 W on a 100 W transmission. The operator's hunch was right and my first reading of the ramp was wrong: what I took for a needle rising was RM4 drifting, not tracking. Watts are now derived from that meter (207 = 100 W, measured) instead of the power SETTING scaled by a percentage — the setting says what was asked for, the meter says what left the radio. The bars also hold their peak with a gentle decay. In CW the meters genuinely read zero between elements, so following the raw value made them flash to nothing several times a second; a needle has inertia, and this only ever holds a value the radio really reported.
This commit is contained in:
+57
-12
@@ -58,7 +58,10 @@ type YaesuTXState struct {
|
||||
// SWR is the RATIO (1.0, 1.5…), computed from the reflection coefficient —
|
||||
// what an operator reads on the rig, not a percentage of meter travel.
|
||||
SWR float64 `json:"swr"`
|
||||
VOX bool `json:"vox"`
|
||||
// PowerW is the output in WATTS, taken from the METER rather than from the
|
||||
// power setting — the setting says what was asked for, not what came out.
|
||||
PowerW float64 `json:"power_w"`
|
||||
VOX bool `json:"vox"`
|
||||
// CW-only controls. Read (and shown) only in CW, where MIC and VOX mean
|
||||
// nothing and these are what an operator reaches for.
|
||||
KeySpeed int `json:"key_speed"` // WPM
|
||||
@@ -155,17 +158,24 @@ func (y *Yaesu) readPanel(mode string, split bool, txHz int64) {
|
||||
debugLog.Printf("yaesu: meters at PC=%dW: %s (compare two different power settings)",
|
||||
y.panel.RFPower, strings.Join(raw, " "))
|
||||
}
|
||||
// Measured on an FTDX10 (2026-07-29), a steady carrier for three seconds:
|
||||
// Measured on an FTDX10 (2026-07-29). The FM carrier was inconclusive —
|
||||
// constant by definition — so the answer came from CW at 100 W, where the
|
||||
// keying itself is the experiment:
|
||||
//
|
||||
// RM1=0 RM2=unsupported RM3=0 RM4=9→18→21→22 RM5=208 flat RM6=0
|
||||
// key down: RM4=25 RM5=207 RM6=13
|
||||
// key up: RM4=25 RM5=0 RM6=0
|
||||
//
|
||||
// RM4 is the one that RAMPS with the output, so RM4 is the power meter.
|
||||
// RM5 sat at 208 from the first sample to the last, unmoved by the power —
|
||||
// that is not SWR (the operator's was 1.1) and not a meter that reads
|
||||
// anything we can show; borrowing the FT-991A's table, which puts SWR
|
||||
// there, is exactly what made the bar read 81.
|
||||
if v, ok := y.askNum("RM4;", "RM4", 3); ok {
|
||||
y.panel.PowerMeter = scale255(v)
|
||||
// RM5 follows the RF envelope exactly, so RM5 is the POWER meter. RM4 sits
|
||||
// near 25 whether the key is down or up, so it is not measuring output at
|
||||
// all — reading it as power is what showed 8 W on a 100 W transmission.
|
||||
// Peak-hold. In CW the key is up between elements and the meters genuinely
|
||||
// read 0 there — the log shows RM5 alternating 207, 0, 207 — so following
|
||||
// the raw value makes the bars flicker to nothing several times a second
|
||||
// and the number unreadable. A real meter has needle inertia; this is the
|
||||
// same idea, and it only ever holds a value the radio actually reported.
|
||||
if v, ok := y.askNum("RM5;", "RM5", 3); ok {
|
||||
y.panel.PowerMeter = holdPeak(y.panel.PowerMeter, scale255(v))
|
||||
y.panel.PowerW = float64(holdPeak(int(y.panel.PowerW), int(yaesuWatts(v))))
|
||||
}
|
||||
// SWR is RM6, and a second measurement at a KNOWN mismatch settled both the
|
||||
// index and the scale: 0 at SWR 1.1, then 52 at SWR 1.5. 52/255 = 0.204,
|
||||
@@ -173,8 +183,12 @@ func (y *Yaesu) readPanel(mode string, split bool, txHz int64) {
|
||||
// the raw value is rho scaled to 255, and the ratio follows from physics
|
||||
// rather than from a fitted curve.
|
||||
if v, ok := y.askNum("RM6;", "RM6", 3); ok {
|
||||
y.panel.SWRMeter = scale255(v)
|
||||
y.panel.SWR = swrFromReflection(v)
|
||||
y.panel.SWRMeter = holdPeak(y.panel.SWRMeter, scale255(v))
|
||||
// SWR only means something while power is going out: between CW elements
|
||||
// the reading is 0, which would show as a perfect 1.0 match.
|
||||
if v > 0 {
|
||||
y.panel.SWR = swrFromReflection(v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Zeroed rather than frozen: a stale SWR bar from the last transmission
|
||||
@@ -694,3 +708,34 @@ func swrFromReflection(raw int) float64 {
|
||||
}
|
||||
return swr
|
||||
}
|
||||
|
||||
// yaesuWatts converts the power-meter reading to watts.
|
||||
//
|
||||
// Single-point calibration, measured rather than assumed: an FTDX10 keying CW at
|
||||
// 100 W reads 207. The PO meter is close enough to linear in power for one point
|
||||
// to carry the scale, and a figure taken from the METER beats the power SETTING,
|
||||
// which says what was asked for and not what left the radio.
|
||||
//
|
||||
// A second point at low power would refine it; until then the number is right at
|
||||
// 100 W and approximate below.
|
||||
const yaesuPowerFullScale = 207.0 // raw reading measured at 100 W
|
||||
|
||||
func yaesuWatts(raw int) float64 {
|
||||
if raw <= 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(raw) * 100.0 / yaesuPowerFullScale
|
||||
}
|
||||
|
||||
// holdPeak gives a meter the inertia a needle has: it jumps to a higher reading
|
||||
// at once and falls back gradually. Without it a CW transmission — key up
|
||||
// between every element — makes the bars flash to zero several times a second.
|
||||
//
|
||||
// The decay is a quarter of the gap per poll, so a real drop in power still
|
||||
// shows within about a second at the default rate.
|
||||
func holdPeak(current, sample int) int {
|
||||
if sample >= current {
|
||||
return sample
|
||||
}
|
||||
return current - (current-sample)/4
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user