fix(elecraft): MOX would not unkey, and the TX meters never moved
Both are the same bug. ReadState returns early while transmitting — the rig answers '?;' to IF; then, and treating that as a fault used to drop the link — but that early return also skipped the panel update and the meter read. So the panel believed the radio was still receiving. Pressing MOX again therefore sent ANOTHER transmit command instead of RX, and the K3 stayed keyed, exactly as reported. And the power and SWR bars were only ever read at the one moment they mean nothing. Reading meters while transmitting needs one more guard: a '?;' then says when the question was asked, not what the radio supports, so the unsupported-command memory is suspended for that read. Without it, one badly timed refusal would silence a meter for the rest of the session. Adds RIT/XIT offset control (±10/±100 Hz, offset shown), a 4.0 kHz filter button for FT8, and logs the K3's icon word when it changes: there is no command for 'is the ATU in line', the reference says the switch functions show up as icon changes, so an operator toggling the ATU while watching the log will name the bit and the button can then light up honestly.
This commit is contained in:
@@ -68,9 +68,14 @@ type KenwoodTXState struct {
|
||||
// offer a switch that goes nowhere.
|
||||
Antenna int `json:"antenna"`
|
||||
|
||||
RIT bool `json:"rit"`
|
||||
XIT bool `json:"xit"`
|
||||
KeySpeed int `json:"key_speed"` // WPM
|
||||
RIT bool `json:"rit"`
|
||||
XIT bool `json:"xit"`
|
||||
// RITOffset is the RIT/XIT offset in Hz — the number the buttons move and
|
||||
// the one an operator is actually reading when they look at RIT at all. A
|
||||
// lit RIT button with no offset beside it says the feature is on and
|
||||
// nothing about where it has put the receiver.
|
||||
RITOffset int `json:"rit_offset"`
|
||||
KeySpeed int `json:"key_speed"` // WPM
|
||||
|
||||
// MetersProvisional says the meter scaling has not been confirmed against a
|
||||
// real radio. The panel says so rather than presenting a guess as a
|
||||
@@ -97,6 +102,7 @@ type KenwoodPanelController interface {
|
||||
SetKenwoodAntenna(int) error
|
||||
SetKenwoodRIT(bool) error
|
||||
SetKenwoodXIT(bool) error
|
||||
NudgeKenwoodRIT(int) error
|
||||
ClearKenwoodRIT() error
|
||||
SetKenwoodTX(bool) error
|
||||
TuneKenwoodATU() error
|
||||
@@ -222,9 +228,16 @@ func (k *Kenwood) readPanelSettings() {
|
||||
if v, ok := k.askNum("XT;", "XT", 1); ok {
|
||||
k.panel.XIT = v != 0
|
||||
}
|
||||
// RO carries a sign, so it is read as a whole rather than through askNum.
|
||||
if r, err := k.ask("RO;"); err == nil {
|
||||
if hz, ok := parseKenwoodOffset(r); ok {
|
||||
k.panel.RITOffset = hz
|
||||
}
|
||||
}
|
||||
if v, ok := k.askNum("KS;", "KS", 3); ok {
|
||||
k.panel.KeySpeed = v
|
||||
}
|
||||
k.probeIcons()
|
||||
}
|
||||
|
||||
// The full-scale value of the analogue controls differs between an Elecraft and
|
||||
@@ -310,6 +323,10 @@ var kenwoodMeterProbes = []struct {
|
||||
// readTXMeters reads the transmit meters.
|
||||
func (k *Kenwood) readTXMeters() {
|
||||
now := time.Now()
|
||||
// Refusals here are about WHEN the question was asked, not about what the
|
||||
// radio can do: a K3 says "?;" to plenty while the carrier is up.
|
||||
k.noLatch = true
|
||||
defer func() { k.noLatch = false }()
|
||||
if v, ok := k.askNum("BG;", "BG", 2); ok {
|
||||
k.panel.PowerMeter = k.powerPeak.update(kenwoodBargraphPercent(v), now)
|
||||
}
|
||||
@@ -396,6 +413,31 @@ func (k *Kenwood) SetKenwoodAFGain(p int) error {
|
||||
return k.setPanel(fmt.Sprintf("AG%03d;", p*255/100))
|
||||
}
|
||||
|
||||
// probeIcons logs the K3's icon/status word whenever it changes.
|
||||
//
|
||||
// There is no command that asks "is the ATU in line": the reference says the
|
||||
// front-panel switch functions show up as icon changes readable through IC (or
|
||||
// DS), which means the answer is a bit in a word nobody here can name yet. So
|
||||
// the word is logged when it changes, and an operator toggling the ATU while
|
||||
// watching the log hands us the bit — after which the ATU button can light up
|
||||
// honestly instead of guessing from what it last sent.
|
||||
func (k *Kenwood) probeIcons() {
|
||||
if k.iconProbes >= 40 {
|
||||
return
|
||||
}
|
||||
r, err := k.ask("IC;")
|
||||
if err != nil {
|
||||
k.iconProbes = 40 // this rig has no IC; stop asking
|
||||
return
|
||||
}
|
||||
if r == k.lastIcons {
|
||||
return
|
||||
}
|
||||
k.lastIcons = r
|
||||
k.iconProbes++
|
||||
debugLog.Printf("kenwood: icon status changed → %s (toggle ATU/PRE/ATT while watching this line to name the bits)", r)
|
||||
}
|
||||
|
||||
// SetKenwoodRFGain sets the RF gain, 0-100 of the rig's own scale.
|
||||
func (k *Kenwood) SetKenwoodRFGain(p int) error {
|
||||
return k.setPanel(fmt.Sprintf("RG%03d;", clampPercentTo(p, k.rfGainFull())))
|
||||
@@ -456,6 +498,60 @@ func (k *Kenwood) SetKenwoodAntenna(n int) error {
|
||||
return k.setPanel(fmt.Sprintf("AN%d;", n))
|
||||
}
|
||||
|
||||
// parseKenwoodOffset reads "RO+0100;" / "RO-0250;" into Hz.
|
||||
func parseKenwoodOffset(frame string) (int, bool) {
|
||||
body := strings.TrimSuffix(strings.TrimPrefix(frame, "RO"), ";")
|
||||
if len(body) < 2 {
|
||||
return 0, false
|
||||
}
|
||||
sign := 1
|
||||
switch body[0] {
|
||||
case '-':
|
||||
sign = -1
|
||||
body = body[1:]
|
||||
case '+':
|
||||
body = body[1:]
|
||||
}
|
||||
n, err := strconv.Atoi(strings.TrimSpace(body))
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return sign * n, true
|
||||
}
|
||||
|
||||
// NudgeKenwoodRIT moves the RIT/XIT offset by delta Hz.
|
||||
//
|
||||
// The offset is SET rather than stepped, because RO is where the radio keeps
|
||||
// it and stepping commands differ across the family. The panel's own reading is
|
||||
// the starting point, so two quick presses do not both start from the same
|
||||
// stale value.
|
||||
func (k *Kenwood) NudgeKenwoodRIT(delta int) error {
|
||||
k.mu.Lock()
|
||||
cur := k.panel.RITOffset
|
||||
k.mu.Unlock()
|
||||
next := cur + delta
|
||||
// A pile-up is chased with a few hundred hertz of RIT; ±5 kHz is already
|
||||
// past anything an offset is for, and past what the rig accepts.
|
||||
if next > 5000 {
|
||||
next = 5000
|
||||
}
|
||||
if next < -5000 {
|
||||
next = -5000
|
||||
}
|
||||
sign := "+"
|
||||
if next < 0 {
|
||||
sign = "-"
|
||||
}
|
||||
mag := next
|
||||
if mag < 0 {
|
||||
mag = -mag
|
||||
}
|
||||
k.mu.Lock()
|
||||
k.panel.RITOffset = next // optimistic, like the sliders
|
||||
k.mu.Unlock()
|
||||
return k.setPanel(fmt.Sprintf("RO%s%04d;", sign, mag))
|
||||
}
|
||||
|
||||
func (k *Kenwood) SetKenwoodRIT(on bool) error {
|
||||
return k.setPanel(fmt.Sprintf("RT%d;", boolDigit(on)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user