feat: added RIT and XIT for Icom
This commit is contained in:
@@ -277,6 +277,12 @@ func (b *IcomSerial) SetPTT(on bool) error {
|
||||
// ── helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
func (b *IcomSerial) write(payload ...byte) error {
|
||||
// Not connected (rig off / port dropped): fail cleanly instead of
|
||||
// dereferencing a nil port — a Set* dispatched while disconnected (e.g.
|
||||
// clicking Scope ON with the radio off) would otherwise panic the app.
|
||||
if b.port == nil {
|
||||
return fmt.Errorf("icom: not connected")
|
||||
}
|
||||
// Drop any stale/unsolicited frames buffered from before this command so
|
||||
// recv() only sees the reply to THIS request (avoids a previous command's ack
|
||||
// or an unsolicited dial-turn update being mistaken for our response).
|
||||
@@ -514,6 +520,68 @@ func (b *IcomSerial) SetScopeMode(fixed bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetRIT sets the RIT/ΔTX offset (signed Hz, ±9999).
|
||||
func (b *IcomSerial) SetRIT(hz int) error {
|
||||
if err := b.exec(append([]byte{civ.CmdRIT, civ.SubRITFreq}, civ.RITToBCD(hz)...)...); err != nil {
|
||||
return err
|
||||
}
|
||||
if hz < -9999 {
|
||||
hz = -9999
|
||||
}
|
||||
if hz > 9999 {
|
||||
hz = 9999
|
||||
}
|
||||
b.setCache(func(s *IcomTXState) { s.RITHz = hz })
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *IcomSerial) SetRITOn(on bool) error {
|
||||
if err := b.exec(civ.CmdRIT, civ.SubRITOn, boolByte(on)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.setCache(func(s *IcomTXState) { s.RITOn = on })
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *IcomSerial) SetXITOn(on bool) error {
|
||||
if err := b.exec(civ.CmdRIT, civ.SubXITOn, boolByte(on)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.setCache(func(s *IcomTXState) { s.XITOn = on })
|
||||
return nil
|
||||
}
|
||||
|
||||
// readRIT reads the offset + RIT/ΔTX on-off flags into st (best-effort).
|
||||
func (b *IcomSerial) readRIT(st *IcomTXState) {
|
||||
if err := b.write(civ.CmdRIT, civ.SubRITFreq); err == nil {
|
||||
if f, err := b.recv(icomDSPTimeout, func(d civ.Decoded) bool {
|
||||
return d.Cmd == civ.CmdRIT && len(d.Data) >= 4 && d.Data[0] == civ.SubRITFreq
|
||||
}); err == nil {
|
||||
st.RITHz = civ.BCDToRIT(f.Data[1:4])
|
||||
}
|
||||
}
|
||||
if v, ok := b.readSwitchSub(civ.CmdRIT, civ.SubRITOn); ok {
|
||||
st.RITOn = v != 0
|
||||
}
|
||||
if v, ok := b.readSwitchSub(civ.CmdRIT, civ.SubXITOn); ok {
|
||||
st.XITOn = v != 0
|
||||
}
|
||||
}
|
||||
|
||||
// readSwitchSub reads a 1-byte on/off value for cmd+sub (generalises readSwitch).
|
||||
func (b *IcomSerial) readSwitchSub(cmd, sub byte) (byte, bool) {
|
||||
if err := b.write(cmd, sub); err != nil {
|
||||
return 0, false
|
||||
}
|
||||
f, err := b.recv(icomDSPTimeout, func(d civ.Decoded) bool {
|
||||
return d.Cmd == cmd && len(d.Data) >= 2 && d.Data[0] == sub
|
||||
})
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return f.Data[1], true
|
||||
}
|
||||
|
||||
// ScopeData returns a copy of the latest reassembled sweep as a number array.
|
||||
func (b *IcomSerial) ScopeData() ScopeSweep {
|
||||
b.scopeMu.Lock()
|
||||
@@ -743,6 +811,7 @@ func (b *IcomSerial) readDSP() {
|
||||
if _, f, ok := b.readModeFilter(); ok {
|
||||
st.Filter = int(f)
|
||||
}
|
||||
b.readRIT(&st)
|
||||
|
||||
b.dspMu.Lock()
|
||||
b.dsp = st
|
||||
|
||||
Reference in New Issue
Block a user