chore: release v0.21.3
This commit is contained in:
+62
-18
@@ -79,23 +79,23 @@ type IcomSerial struct {
|
||||
// leading main/sub selector byte (IC-7610/9700). scopeAmp is the latest
|
||||
// reassembled sweep; scopeMu guards it (written by the scope goroutine, read
|
||||
// via ScopeData from the binding goroutine).
|
||||
dualScope bool
|
||||
scopeMu sync.Mutex
|
||||
scopeAmp []byte
|
||||
scopeLow int64 // spectrum left-edge frequency (from the sweep's header frame)
|
||||
scopeHigh int64 // spectrum right-edge frequency
|
||||
scopeSeq int
|
||||
scopeOn bool
|
||||
dualScope bool
|
||||
scopeMu sync.Mutex
|
||||
scopeAmp []byte
|
||||
scopeLow int64 // spectrum left-edge frequency (from the sweep's header frame)
|
||||
scopeHigh int64 // spectrum right-edge frequency
|
||||
scopeSeq int
|
||||
scopeOn bool
|
||||
scopeFixed bool // true = fixed-span mode (tracked optimistically)
|
||||
scopeSeen bool // logged the first sweep's structure once (on-rig verification)
|
||||
scopeSeen bool // logged the first sweep's structure once (on-rig verification)
|
||||
|
||||
curFreq int64 // last frequency read (for sideband choice)
|
||||
curModeByte byte // last raw Icom mode byte (for filter re-send)
|
||||
pollN int // ReadState cycle counter (staggers slow reads)
|
||||
splitOn bool // last read split state (refreshed every few cycles)
|
||||
splitTXFreq int64 // last read unselected/TX VFO freq while in split
|
||||
readFails int // consecutive ReadState freq-read failures (transient tolerance)
|
||||
dspLoaded bool // readDSP has run since the rig became responsive (loads all
|
||||
curFreq int64 // last frequency read (for sideband choice)
|
||||
curModeByte byte // last raw Icom mode byte (for filter re-send)
|
||||
pollN int // ReadState cycle counter (staggers slow reads)
|
||||
splitOn bool // last read split state (refreshed every few cycles)
|
||||
splitTXFreq int64 // last read unselected/TX VFO freq while in split
|
||||
readFails int // consecutive ReadState freq-read failures (transient tolerance)
|
||||
dspLoaded bool // readDSP has run since the rig became responsive (loads all
|
||||
// the panel's set-once controls once the rig actually answers)
|
||||
lastSetFreq int64 // last frequency commanded (spot click: freq then mode)
|
||||
lastSetFreqAt time.Time
|
||||
@@ -341,7 +341,7 @@ func (b *IcomSerial) ReadState() (RigState, error) {
|
||||
}
|
||||
if b.splitOn && b.splitTXFreq > 0 && b.splitTXFreq != s.FreqHz {
|
||||
s.Split = true
|
||||
s.RxFreqHz = s.FreqHz // selected VFO = RX
|
||||
s.RxFreqHz = s.FreqHz // selected VFO = RX
|
||||
s.FreqHz = b.splitTXFreq // unselected VFO = TX
|
||||
}
|
||||
|
||||
@@ -374,10 +374,54 @@ func (b *IcomSerial) ReadState() (RigState, error) {
|
||||
if !b.dspLoaded {
|
||||
b.readDSP()
|
||||
b.dspLoaded = true
|
||||
} else {
|
||||
b.refreshFrontPanel()
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// refreshFrontPanel re-reads the few controls an operator actually reaches for on
|
||||
// the rig itself, so the console follows the radio instead of only driving it.
|
||||
//
|
||||
// readDSP loads everything but runs ONCE per connection (dspLoaded), which left
|
||||
// the panel showing whatever was set at connect: switch AGC from FAST to MID on
|
||||
// the front panel and OpsLog still said FAST, indefinitely. Commands worked, so
|
||||
// the link was plainly fine — only this direction was missing.
|
||||
//
|
||||
// ONE read per poll cycle, in rotation. The full snapshot is ~30 CI-V round trips
|
||||
// and refreshing it wholesale would hog the CAT thread for seconds at a time —
|
||||
// including the operator's own Set* commands, which is far worse than a stale
|
||||
// label. The rotation completes in 8 cycles; anything not covered here is still a
|
||||
// connect-time or ↻ Refresh read.
|
||||
func (b *IcomSerial) refreshFrontPanel() {
|
||||
switch b.pollN % 8 {
|
||||
case 1:
|
||||
if v, ok := b.readSwitch(civ.SubSwAGC); ok {
|
||||
b.dspMu.Lock()
|
||||
b.dsp.AGC = agcName(v)
|
||||
b.dspMu.Unlock()
|
||||
}
|
||||
case 3:
|
||||
if v, ok := b.readAtt(); ok {
|
||||
b.dspMu.Lock()
|
||||
b.dsp.Att = v
|
||||
b.dspMu.Unlock()
|
||||
}
|
||||
case 5:
|
||||
if v, ok := b.readSwitch(civ.SubSwPreamp); ok {
|
||||
b.dspMu.Lock()
|
||||
b.dsp.Preamp = int(v)
|
||||
b.dspMu.Unlock()
|
||||
}
|
||||
case 7:
|
||||
if _, f, ok := b.readModeFilter(); ok {
|
||||
b.dspMu.Lock()
|
||||
b.dsp.Filter = int(f)
|
||||
b.dspMu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *IcomSerial) SetFrequency(hz int64) error {
|
||||
if hz <= 0 {
|
||||
return fmt.Errorf("invalid frequency")
|
||||
@@ -583,8 +627,8 @@ func (b *IcomSerial) drainResp() {
|
||||
func (b *IcomSerial) scopeLoop(spec chan civ.Decoded, done chan struct{}) {
|
||||
regions := make(map[byte][]byte)
|
||||
var total byte
|
||||
rawN := 0 // diagnostic: dump the first few raw 0x27 frames
|
||||
loggedCfg := map[byte]bool{} // one-shot dump of each config read response
|
||||
rawN := 0 // diagnostic: dump the first few raw 0x27 frames
|
||||
loggedCfg := map[byte]bool{} // one-shot dump of each config read response
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
|
||||
Reference in New Issue
Block a user