fix(icom): fall back to 0x27 0x10 when a rig has no 0x27 0x11
The IC-7851 rejects 0x27 0x11 in BOTH shapes: on that radio 0x27 0x10 is the waveform-output switch itself, not a display switch. Rejected both ways, take 0x10 as the output command and switch it off on disable too — there it stops a stream rather than blanking the operator's own scope screen. A mode/span/edge answer also settles the selector question directly: three bytes or more means the rig wants the main/sub form. Worth reading, because a wrong-shaped SET is answered with silence on this firmware, and a timeout is something the rejection retry cannot act on — which is why the fixed/centre sets were timing out.
This commit is contained in:
+45
-12
@@ -93,15 +93,18 @@ type IcomSerial struct {
|
|||||||
// leading main/sub selector byte (IC-7610/9700). scopeAmp is the latest
|
// leading main/sub selector byte (IC-7610/9700). scopeAmp is the latest
|
||||||
// reassembled sweep; scopeMu guards it (written by the scope goroutine, read
|
// reassembled sweep; scopeMu guards it (written by the scope goroutine, read
|
||||||
// via ScopeData from the binding goroutine).
|
// via ScopeData from the binding goroutine).
|
||||||
dualScope bool
|
dualScope bool
|
||||||
scopeMu sync.Mutex
|
// Which 0x27 subcommand switches the waveform stream on this rig: 0x11 where
|
||||||
scopeAmp []byte
|
// it exists, 0x10 on the ones that answer 0x11 with a rejection.
|
||||||
scopeLow int64 // spectrum left-edge frequency (from the sweep's header frame)
|
scopeOutSub byte
|
||||||
scopeHigh int64 // spectrum right-edge frequency
|
scopeMu sync.Mutex
|
||||||
scopeSeq int
|
scopeAmp []byte
|
||||||
scopeOn bool
|
scopeLow int64 // spectrum left-edge frequency (from the sweep's header frame)
|
||||||
scopeFixed bool // true = fixed-span mode (tracked optimistically)
|
scopeHigh int64 // spectrum right-edge frequency
|
||||||
scopeSeen bool // logged the first sweep's structure once (on-rig verification)
|
scopeSeq int
|
||||||
|
scopeOn bool
|
||||||
|
scopeFixed bool // true = fixed-span mode (tracked optimistically)
|
||||||
|
scopeSeen bool // logged the first sweep's structure once (on-rig verification)
|
||||||
|
|
||||||
curFreq int64 // last frequency read (for sideband choice)
|
curFreq int64 // last frequency read (for sideband choice)
|
||||||
curModeByte byte // last raw Icom mode byte (for filter re-send)
|
curModeByte byte // last raw Icom mode byte (for filter re-send)
|
||||||
@@ -848,6 +851,19 @@ func (b *IcomSerial) scopeLoop(spec chan civ.Decoded, done chan struct{}) {
|
|||||||
loggedCfg[f.Data[0]] = true
|
loggedCfg[f.Data[0]] = true
|
||||||
applog.Printf("icom scope cfg 0x%02X: data=[% X]", f.Data[0], f.Data)
|
applog.Printf("icom scope cfg 0x%02X: data=[% X]", f.Data[0], f.Data)
|
||||||
}
|
}
|
||||||
|
// The rig just told us its own layout: a mode/span/edge answer of
|
||||||
|
// three bytes or more carries the main/sub selector, one of two
|
||||||
|
// bytes does not. Worth reading, because the SET commands take the
|
||||||
|
// same shape and several firmwares answer a wrong-shaped set with
|
||||||
|
// silence rather than a rejection — which is not something the
|
||||||
|
// retry in execScope can act on.
|
||||||
|
if f.Data[0] == civ.SubScopeMode && len(f.Data) >= 2 {
|
||||||
|
if sel := len(f.Data) >= 3; sel != b.dualScope {
|
||||||
|
applog.Printf("icom scope: the rig answers 0x%02X with %d bytes — using the %s form",
|
||||||
|
f.Data[0], len(f.Data)-1, map[bool]string{true: "27 xx 00 …", false: "27 xx …"}[sel])
|
||||||
|
b.dualScope = sel
|
||||||
|
}
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if rawN < 24 {
|
if rawN < 24 {
|
||||||
@@ -1034,14 +1050,31 @@ func (b *IcomSerial) SetScope(on bool) error {
|
|||||||
// radio, and closing OpsLog (SetScope(false)) blanking a local IC-7300's
|
// radio, and closing OpsLog (SetScope(false)) blanking a local IC-7300's
|
||||||
// screen is exactly the regression this avoids. Some firmwares don't ack a
|
// screen is exactly the regression this avoids. Some firmwares don't ack a
|
||||||
// 0x27 set; a timeout isn't fatal, so log and continue.
|
// 0x27 set; a timeout isn't fatal, so log and continue.
|
||||||
if err := b.execScope("display on", civ.SubScopeOnOff, 0x01); err != nil {
|
if b.scopeOutSub != civ.SubScopeOnOff {
|
||||||
applog.Printf("icom scope: display on ack: %v", err)
|
if err := b.execScope("display on", civ.SubScopeOnOff, 0x01); err != nil {
|
||||||
|
applog.Printf("icom scope: display on ack: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Waveform data OUTPUT over CI-V: enabled with the scope, and — crucially —
|
// Waveform data OUTPUT over CI-V: enabled with the scope, and — crucially —
|
||||||
// the ONLY thing we switch off on disable, so the radio's own scope display is
|
// the ONLY thing we switch off on disable, so the radio's own scope display is
|
||||||
// left exactly as the operator had it.
|
// left exactly as the operator had it.
|
||||||
if err := b.execScope("data output", civ.SubScopeOn, boolByte(on)); err != nil {
|
//
|
||||||
|
// scopeOutSub is 0x11 on the radios that have it. The IC-7851 does not: it
|
||||||
|
// rejects 0x11 in both shapes, because on that radio 0x27 0x10 is itself the
|
||||||
|
// waveform-output switch rather than a display switch. Fall back to it, and
|
||||||
|
// from then on switch it off on disable too — on THAT radio doing so stops a
|
||||||
|
// data stream, not the operator's own scope screen.
|
||||||
|
if b.scopeOutSub == 0 {
|
||||||
|
b.scopeOutSub = civ.SubScopeOn
|
||||||
|
}
|
||||||
|
err := b.execScope("data output", b.scopeOutSub, boolByte(on))
|
||||||
|
if err != nil && b.scopeOutSub == civ.SubScopeOn && strings.Contains(err.Error(), "rejected") {
|
||||||
|
applog.Printf("icom scope: no 0x27 0x11 on this rig — 0x27 0x10 is its data-output switch")
|
||||||
|
b.scopeOutSub = civ.SubScopeOnOff
|
||||||
|
err = b.execScope("data output", b.scopeOutSub, boolByte(on))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
applog.Printf("icom scope: output on=%v ack: %v", on, err)
|
applog.Printf("icom scope: output on=%v ack: %v", on, err)
|
||||||
}
|
}
|
||||||
b.scopeMu.Lock()
|
b.scopeMu.Lock()
|
||||||
|
|||||||
Reference in New Issue
Block a user