fix(yaesu): mic gain scale, 200 W models, and a NAR button that did nothing

Reported from a real FTDX101, and each one is a different kind of wrong.

MIC GAIN was read and written through the 0-255 scale the audio gains
use, but the CAT reference gives MG000-100. A rig set to 80 therefore
showed 38, and moving the slider sent 204 — outside the range the radio
accepts, so it refused the command and the slider sprang back. That
snap-back was the symptom; the scale was the cause.

POWER was capped at 100 W by the slider, not by the radio. There is no
CAT command for 'how much power can you make', so the ceiling comes from
the model name, with the rig believed if it ever reports more than the
table expects — it has just proved what it can do.

NAR is the narrow IF filter, and on a rig that does not implement NA the
button showed a state the radio never gave and did nothing when pressed,
which reads as a fault in the radio. Now shown only when the rig answers,
with a tooltip saying what it is.

Also: the frequency readout steps the Hz digits under the wheel, not just
the kHz ones. Zero-beating a CW signal is a few tens of Hz and it was the
one move the display would not make. And the split chaser is CW-only —
its marker comes from a CW skimmer.
This commit is contained in:
2026-08-24 17:59:13 +02:00
parent ea0789b191
commit 0848c275d2
7 changed files with 91 additions and 15 deletions
+39 -3
View File
@@ -43,7 +43,15 @@ type YaesuTXState struct {
PowerMeter int `json:"power_meter"` // 0-100, TX only
SWRMeter int `json:"swr_meter"` // 0-100, TX only
RFPower int `json:"rf_power"` // watts
RFPower int `json:"rf_power"` // watts
// MaxPower is what this model can actually be set to, so the slider stops
// where the radio does. An FTDX101MP is a 200 W transmitter and its operator
// could not ask for more than 100 W — the slider, not the rig, was the limit.
MaxPower int `json:"max_power"`
// NarrowSupported says the rig answered NA at all. A button that reports a
// state the radio never gave, and does nothing when pressed, is worse than
// an absent one: it looks like a fault in the radio.
NarrowSupported bool `json:"narrow_supported"`
MicGain int `json:"mic_gain"` // 0-100
AFGain int `json:"af_gain"` // 0-100
RFGain int `json:"rf_gain"` // 0-100
@@ -110,9 +118,29 @@ func (y *Yaesu) YaesuState() YaesuTXState {
st := y.panel
st.Available = y.port != nil
st.Model = y.model
st.MaxPower = yaesuMaxPower(y.model, st.RFPower)
return st
}
// yaesuMaxPower is how far the power slider may go on this model.
//
// The rig itself is never asked — there is no CAT command for "how much power
// can you make" — so it comes from the model name, with one safeguard: a rig
// REPORTING more than the table expects is believed, because it has just proved
// what it can do. Anything unknown gets 100 W, which is the family default and
// errs towards asking for too little.
func yaesuMaxPower(model string, reported int) int {
max := 100
switch {
case strings.Contains(model, "101MP"), strings.Contains(model, "5000"), strings.Contains(model, "9000"):
max = 200
}
if reported > max {
max = reported
}
return max
}
// readPanel refreshes the meters, and the settings on the slower beat. Called
// from ReadState with the mutex HELD, so it shares the same serialised link.
func (y *Yaesu) readPanel(mode string, split bool, txHz int64) {
@@ -226,8 +254,15 @@ func (y *Yaesu) readPanelSettings() {
if v, ok := y.askNum("PC;", "PC", 3); ok {
y.panel.RFPower = v // watts, not a 0-255 scale
}
// MIC GAIN IS 0-100, not 0-255.
//
// It was read through the 0-255 scale like the audio gains, so an FTDX101
// set to 80 showed 38 — and worse, writing sent 80 → 204, outside the range
// the rig accepts, so the setting was refused and the slider sprang back to
// where it had been. Reported from a real FTDX101; the CAT reference gives
// MG000-100 for the whole current family.
if v, ok := y.askNum("MG;", "MG", 3); ok {
y.panel.MicGain = scale255(v)
y.panel.MicGain = clampInt(v, 0, 100)
}
if v, ok := y.askNum("AG0;", "AG0", 3); ok {
y.panel.AFGain = scale255(v)
@@ -276,6 +311,7 @@ func (y *Yaesu) readPanelSettings() {
}
if v, ok := y.askNum("NA0;", "NA0", 1); ok {
y.panel.Narrow = v != 0
y.panel.NarrowSupported = true
}
if v, ok := y.askNum("VX;", "VX", 1); ok {
y.panel.VOX = v != 0
@@ -356,7 +392,7 @@ func (y *Yaesu) SetYaesuPower(w int) error {
}
func (y *Yaesu) SetYaesuMicGain(p int) error {
return y.setAndRefresh(fmt.Sprintf("MG%03d;", from100(p)))
return y.setAndRefresh(fmt.Sprintf("MG%03d;", clampInt(p, 0, 100)))
}
func (y *Yaesu) SetYaesuAFGain(p int) error {