kappanhang/civcontrol.go

1253 lines
29 KiB
Go
Raw Normal View History

package main
2020-10-30 22:58:31 +01:00
import (
2020-11-03 22:06:51 +01:00
"fmt"
2020-10-30 22:58:31 +01:00
"math"
2020-11-08 20:48:39 +01:00
"sync"
2020-10-30 22:58:31 +01:00
"time"
)
const civAddress = 0xa4
2020-11-09 09:40:18 +01:00
const statusPollInterval = time.Second
const commandRetryTimeout = 500 * time.Millisecond
2020-11-05 08:19:16 +01:00
// Commands reference: https://www.icomeurope.com/wp-content/uploads/2020/08/IC-705_ENG_CI-V_1_20200721.pdf
type civOperatingMode struct {
name string
code byte
}
var civOperatingModes = []civOperatingMode{
{name: "LSB", code: 0x00},
{name: "USB", code: 0x01},
{name: "AM", code: 0x02},
{name: "CW", code: 0x03},
{name: "RTTY", code: 0x04},
{name: "FM", code: 0x05},
{name: "WFM", code: 0x06},
{name: "CW-R", code: 0x07},
{name: "RTTY-R", code: 0x08},
{name: "DV", code: 0x17},
}
type civFilter struct {
name string
code byte
}
var civFilters = []civFilter{
{name: "FIL1", code: 0x01},
{name: "FIL2", code: 0x02},
{name: "FIL3", code: 0x03},
}
2020-10-31 23:44:45 +01:00
type civBand struct {
2020-11-01 12:46:37 +01:00
freqFrom uint
freqTo uint
freq uint
2020-10-31 23:44:45 +01:00
}
var civBands = []civBand{
2020-11-01 12:46:37 +01:00
{freqFrom: 1800000, freqTo: 1999999}, // 1.9
{freqFrom: 3400000, freqTo: 4099999}, // 3.5
{freqFrom: 6900000, freqTo: 7499999}, // 7
{freqFrom: 9900000, freqTo: 10499999}, // 10
{freqFrom: 13900000, freqTo: 14499999}, // 14
{freqFrom: 17900000, freqTo: 18499999}, // 18
{freqFrom: 20900000, freqTo: 21499999}, // 21
{freqFrom: 24400000, freqTo: 25099999}, // 24
{freqFrom: 28000000, freqTo: 29999999}, // 28
{freqFrom: 50000000, freqTo: 54000000}, // 50
{freqFrom: 74800000, freqTo: 107999999}, // WFM
{freqFrom: 108000000, freqTo: 136999999}, // AIR
{freqFrom: 144000000, freqTo: 148000000}, // 144
{freqFrom: 420000000, freqTo: 450000000}, // 430
{freqFrom: 0, freqTo: 0}, // GENE
2020-10-31 23:44:45 +01:00
}
2020-11-07 18:09:09 +01:00
type splitMode int
const (
splitModeOff = iota
splitModeOn
splitModeDUPMinus
splitModeDUPPlus
)
2020-11-09 09:40:18 +01:00
type civCmd struct {
pending bool
sentAt time.Time
name string
cmd []byte
}
2020-10-30 20:28:45 +01:00
type civControlStruct struct {
2020-11-09 09:40:18 +01:00
st *serialStream
deinitNeeded chan bool
deinitFinished chan bool
resetSReadTimer chan bool
newPendingCmdAdded chan bool
2020-10-30 22:58:31 +01:00
state struct {
2020-11-09 09:40:18 +01:00
mutex sync.Mutex
pendingCmds []*civCmd
getFreq civCmd
getMode civCmd
getDataMode civCmd
getPwr civCmd
getS civCmd
getOVF civCmd
getSWR civCmd
getTransmitStatus civCmd
getPreamp civCmd
getAGC civCmd
getTuneStatus civCmd
getVd civCmd
getTS civCmd
getRFGain civCmd
getSQL civCmd
getNR civCmd
getNREnabled civCmd
getSplit civCmd
lastSReceivedAt time.Time
lastOVFReceivedAt time.Time
lastSWRReceivedAt time.Time
2020-11-09 09:40:18 +01:00
setPwr civCmd
setRFGain civCmd
setSQL civCmd
setNR civCmd
setFreq civCmd
setMode civCmd
setPTT civCmd
setTune civCmd
setDataMode civCmd
setPreamp civCmd
setAGC civCmd
setNREnabled civCmd
setTS civCmd
setVFO civCmd
setSplit civCmd
2020-11-08 20:48:39 +01:00
2020-11-01 12:46:37 +01:00
freq uint
ptt bool
tune bool
pwrPercent int
2020-11-04 09:41:36 +01:00
rfGainPercent int
sqlPercent int
2020-11-04 10:01:07 +01:00
nrPercent int
nrEnabled bool
operatingModeIdx int
filterIdx int
dataMode bool
2020-10-31 23:44:45 +01:00
bandIdx int
bandChanging bool
2020-11-03 16:19:37 +01:00
preamp int
2020-11-05 08:19:16 +01:00
agc int
tsValue byte
ts uint
vfoBActive bool
2020-11-07 18:09:09 +01:00
splitMode splitMode
2020-10-30 22:58:31 +01:00
}
}
var civControl *civControlStruct
// Returns false if the message should not be forwarded to the serial port TCP server or the virtual serial port.
func (s *civControlStruct) decode(d []byte) bool {
if len(d) < 6 || d[0] != 0xfe || d[1] != 0xfe || d[len(d)-1] != 0xfd {
return true
}
payload := d[5 : len(d)-1]
2020-11-08 20:48:39 +01:00
s.state.mutex.Lock()
defer s.state.mutex.Unlock()
switch d[4] {
case 0x00:
return s.decodeFreq(payload)
case 0x01:
return s.decodeMode(payload)
case 0x03:
return s.decodeFreq(payload)
case 0x04:
return s.decodeMode(payload)
case 0x05:
return s.decodeFreq(payload)
case 0x06:
return s.decodeMode(payload)
case 0x07:
return s.decodeVFO(payload)
2020-11-07 18:09:09 +01:00
case 0x0f:
return s.decodeSplit(payload)
case 0x10:
return s.decodeTS(payload)
2020-10-30 22:58:31 +01:00
case 0x1a:
return s.decodeDataModeAndOVF(payload)
case 0x14:
2020-11-09 09:40:18 +01:00
return s.decodePowerRFGainSQLNRPwr(payload)
case 0x1c:
return s.decodeTransmitStatus(payload)
2020-11-03 16:35:23 +01:00
case 0x15:
2020-11-04 10:41:12 +01:00
return s.decodeVdSWRS(payload)
2020-11-03 16:19:37 +01:00
case 0x16:
2020-11-09 09:40:18 +01:00
return s.decodePreampAGCNREnabled(payload)
}
return true
}
func (s *civControlStruct) decodeFreq(d []byte) bool {
if len(d) < 2 {
2020-11-09 09:40:18 +01:00
return !s.state.getFreq.pending && !s.state.setFreq.pending
}
2020-11-01 12:46:37 +01:00
var f uint
var pos int
for _, v := range d {
s1 := v & 0x0f
s2 := v >> 4
2020-11-01 12:46:37 +01:00
f += uint(s1) * uint(math.Pow(10, float64(pos)))
pos++
2020-11-01 12:46:37 +01:00
f += uint(s2) * uint(math.Pow(10, float64(pos)))
pos++
}
2020-11-01 12:46:37 +01:00
s.state.freq = f
2020-10-30 23:58:10 +01:00
statusLog.reportFrequency(s.state.freq)
2020-10-31 23:44:45 +01:00
s.state.bandIdx = len(civBands) - 1 // Set the band idx to GENE by default.
for i := range civBands {
if s.state.freq >= civBands[i].freqFrom && s.state.freq <= civBands[i].freqTo {
s.state.bandIdx = i
civBands[s.state.bandIdx].freq = s.state.freq
break
}
}
2020-11-09 09:40:18 +01:00
if s.state.getFreq.pending {
s.removePendingCmd(&s.state.getFreq)
return false
}
2020-11-09 09:40:18 +01:00
if s.state.setFreq.pending {
s.removePendingCmd(&s.state.setFreq)
return false
}
return true
}
func (s *civControlStruct) decodeFilterValueToFilterIdx(v byte) int {
for i := range civFilters {
if civFilters[i].code == v {
return i
}
2020-10-30 22:58:31 +01:00
}
return -1
2020-10-30 22:58:31 +01:00
}
func (s *civControlStruct) decodeMode(d []byte) bool {
if len(d) < 1 {
2020-11-09 09:40:18 +01:00
return !s.state.getMode.pending && !s.state.setMode.pending
}
var mode string
for i := range civOperatingModes {
if civOperatingModes[i].code == d[0] {
s.state.operatingModeIdx = i
mode = civOperatingModes[i].name
break
}
}
var filter string
if len(d) > 1 {
s.state.filterIdx = s.decodeFilterValueToFilterIdx(d[1])
filter = civFilters[s.state.filterIdx].name
}
statusLog.reportMode(mode, filter)
2020-10-30 22:58:31 +01:00
2020-11-09 09:40:18 +01:00
if s.state.getMode.pending {
s.removePendingCmd(&s.state.getMode)
return false
}
2020-11-09 09:40:18 +01:00
if s.state.setMode.pending {
s.removePendingCmd(&s.state.setMode)
return false
}
return true
2020-10-30 22:58:31 +01:00
}
func (s *civControlStruct) decodeVFO(d []byte) bool {
if len(d) < 1 {
2020-11-09 09:40:18 +01:00
return !s.state.setVFO.pending
}
if d[0] == 1 {
s.state.vfoBActive = true
log.Print("active vfo: B")
} else {
s.state.vfoBActive = false
log.Print("active vfo: A")
}
2020-11-09 09:40:18 +01:00
if s.state.setVFO.pending {
// The radio does not send the VFO's frequency automatically.
_ = s.getFreq()
2020-11-09 09:40:18 +01:00
s.removePendingCmd(&s.state.setVFO)
return false
}
return true
}
2020-11-07 18:09:09 +01:00
func (s *civControlStruct) decodeSplit(d []byte) bool {
if len(d) < 1 {
2020-11-09 09:40:18 +01:00
return !s.state.getSplit.pending && !s.state.setSplit.pending
2020-11-07 18:09:09 +01:00
}
var str string
switch d[0] {
default:
s.state.splitMode = splitModeOff
case 0x01:
s.state.splitMode = splitModeOn
str = "SPLIT"
case 0x11:
s.state.splitMode = splitModeDUPMinus
str = "DUP-"
case 0x12:
s.state.splitMode = splitModeDUPPlus
str = "DUP+"
}
statusLog.reportSplit(str)
2020-11-09 09:40:18 +01:00
if s.state.getSplit.pending {
s.removePendingCmd(&s.state.getSplit)
return false
}
if s.state.setSplit.pending {
s.removePendingCmd(&s.state.setSplit)
2020-11-07 18:09:09 +01:00
return false
}
return true
}
func (s *civControlStruct) decodeTS(d []byte) bool {
if len(d) < 1 {
2020-11-09 09:40:18 +01:00
return !s.state.getTS.pending && !s.state.setTS.pending
}
s.state.tsValue = d[0]
switch s.state.tsValue {
default:
s.state.ts = 1
case 1:
s.state.ts = 100
case 2:
s.state.ts = 500
case 3:
s.state.ts = 1000
case 4:
s.state.ts = 5000
case 5:
s.state.ts = 6250
case 6:
s.state.ts = 8330
case 7:
s.state.ts = 9000
case 8:
s.state.ts = 10000
case 9:
s.state.ts = 12500
case 10:
s.state.ts = 20000
case 11:
s.state.ts = 25000
case 12:
s.state.ts = 50000
case 13:
s.state.ts = 100000
}
statusLog.reportTS(s.state.ts)
2020-11-09 09:40:18 +01:00
if s.state.getTS.pending {
s.removePendingCmd(&s.state.getTS)
return false
}
if s.state.setTS.pending {
s.removePendingCmd(&s.state.setTS)
return false
2020-10-30 22:58:31 +01:00
}
return true
}
2020-10-30 22:58:31 +01:00
func (s *civControlStruct) decodeDataModeAndOVF(d []byte) bool {
switch d[0] {
case 0x06:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getDataMode.pending && !s.state.setDataMode.pending
}
var dataMode string
var filter string
if d[1] == 1 {
dataMode = "-D"
s.state.dataMode = true
s.state.filterIdx = s.decodeFilterValueToFilterIdx(d[2])
filter = civFilters[s.state.filterIdx].name
} else {
s.state.dataMode = false
}
2020-10-30 22:58:31 +01:00
statusLog.reportDataMode(dataMode, filter)
2020-11-09 09:40:18 +01:00
if s.state.getDataMode.pending {
s.removePendingCmd(&s.state.getDataMode)
return false
}
2020-11-09 09:40:18 +01:00
if s.state.setDataMode.pending {
s.removePendingCmd(&s.state.setDataMode)
return false
}
case 0x09:
if len(d) < 2 {
2020-11-09 09:40:18 +01:00
return !s.state.getOVF.pending
}
if d[1] != 0 {
statusLog.reportOVF(true)
} else {
statusLog.reportOVF(false)
}
s.state.lastOVFReceivedAt = time.Now()
2020-11-09 09:40:18 +01:00
if s.state.getOVF.pending {
s.removePendingCmd(&s.state.getOVF)
return false
}
}
return true
}
2020-11-09 09:40:18 +01:00
func (s *civControlStruct) decodePowerRFGainSQLNRPwr(d []byte) bool {
2020-11-04 09:41:36 +01:00
switch d[0] {
case 0x02:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getRFGain.pending && !s.state.setRFGain.pending
}
2020-11-04 09:41:36 +01:00
hex := uint16(d[1])<<8 | uint16(d[2])
s.state.rfGainPercent = int(math.Round((float64(hex) / 0x0255) * 100))
statusLog.reportRFGain(s.state.rfGainPercent)
2020-11-09 09:40:18 +01:00
if s.state.getRFGain.pending {
s.removePendingCmd(&s.state.getRFGain)
return false
}
if s.state.setRFGain.pending {
s.removePendingCmd(&s.state.setRFGain)
return false
}
case 0x03:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getSQL.pending && !s.state.setSQL.pending
}
hex := uint16(d[1])<<8 | uint16(d[2])
s.state.sqlPercent = int(math.Round((float64(hex) / 0x0255) * 100))
statusLog.reportSQL(s.state.sqlPercent)
2020-11-09 09:40:18 +01:00
if s.state.getSQL.pending {
s.removePendingCmd(&s.state.getSQL)
return false
}
if s.state.setSQL.pending {
s.removePendingCmd(&s.state.setSQL)
return false
}
2020-11-04 10:01:07 +01:00
case 0x06:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getNR.pending && !s.state.setNR.pending
}
2020-11-04 10:01:07 +01:00
hex := uint16(d[1])<<8 | uint16(d[2])
s.state.nrPercent = int(math.Round((float64(hex) / 0x0255) * 100))
statusLog.reportNR(s.state.nrPercent)
2020-11-09 09:40:18 +01:00
if s.state.getNR.pending {
s.removePendingCmd(&s.state.getNR)
return false
}
if s.state.setNR.pending {
s.removePendingCmd(&s.state.setNR)
return false
}
2020-11-04 09:41:36 +01:00
case 0x0a:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getPwr.pending && !s.state.setPwr.pending
}
2020-11-04 09:41:36 +01:00
hex := uint16(d[1])<<8 | uint16(d[2])
s.state.pwrPercent = int(math.Round((float64(hex) / 0x0255) * 100))
statusLog.reportTxPower(s.state.pwrPercent)
2020-11-09 09:40:18 +01:00
if s.state.getPwr.pending {
s.removePendingCmd(&s.state.getPwr)
return false
}
if s.state.setPwr.pending {
s.removePendingCmd(&s.state.setPwr)
return false
}
2020-11-04 09:41:36 +01:00
}
return true
}
func (s *civControlStruct) decodeTransmitStatus(d []byte) bool {
if len(d) < 2 {
2020-11-09 09:40:18 +01:00
return !s.state.getTuneStatus.pending && !s.state.getTransmitStatus.pending && !s.state.setPTT.pending
}
switch d[0] {
case 0:
if d[1] == 1 {
2020-10-30 22:58:31 +01:00
s.state.ptt = true
} else {
2020-11-03 16:49:48 +01:00
if s.state.ptt { // PTT released?
s.state.ptt = false
_ = s.getVd()
}
}
statusLog.reportPTT(s.state.ptt, s.state.tune)
2020-11-09 09:40:18 +01:00
if s.state.setPTT.pending {
s.removePendingCmd(&s.state.setPTT)
return false
}
case 1:
if d[1] == 2 {
2020-10-30 22:58:31 +01:00
s.state.tune = true
// The transceiver does not send the tune state after it's finished.
time.AfterFunc(time.Second, func() {
_ = s.getTransmitStatus()
})
} else {
2020-11-03 16:49:48 +01:00
if s.state.tune { // Tune finished?
s.state.tune = false
_ = s.getVd()
}
}
statusLog.reportPTT(s.state.ptt, s.state.tune)
2020-11-09 09:40:18 +01:00
if s.state.setTune.pending {
s.removePendingCmd(&s.state.setTune)
return false
}
}
2020-11-09 09:40:18 +01:00
if s.state.getTuneStatus.pending {
s.removePendingCmd(&s.state.getTuneStatus)
return false
}
if s.state.getTransmitStatus.pending {
s.removePendingCmd(&s.state.getTransmitStatus)
return false
}
return true
}
2020-11-04 10:41:12 +01:00
func (s *civControlStruct) decodeVdSWRS(d []byte) bool {
2020-11-03 16:35:23 +01:00
switch d[0] {
case 0x02:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getS.pending
}
2020-11-03 22:06:51 +01:00
sValue := (int(math.Round(((float64(int(d[1])<<8) + float64(d[2])) / 0x0241) * 18)))
sStr := "S"
if sValue <= 9 {
sStr += fmt.Sprint(sValue)
} else {
sStr += "9+"
2020-11-04 09:31:46 +01:00
switch sValue {
case 10:
sStr += "10"
case 11:
sStr += "20"
case 12:
sStr += "30"
case 13:
sStr += "40"
case 14:
sStr += "40"
case 15:
sStr += "40"
case 16:
sStr += "40"
case 17:
sStr += "50"
case 18:
sStr += "50"
default:
2020-11-03 22:06:51 +01:00
sStr += "60"
}
}
s.state.lastSReceivedAt = time.Now()
2020-11-03 22:06:51 +01:00
statusLog.reportS(sStr)
2020-11-09 09:40:18 +01:00
if s.state.getS.pending {
s.removePendingCmd(&s.state.getS)
return false
}
2020-11-04 10:41:12 +01:00
case 0x12:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getSWR.pending
2020-11-04 10:41:12 +01:00
}
s.state.lastSWRReceivedAt = time.Now()
2020-11-04 12:53:18 +01:00
statusLog.reportSWR(((float64(int(d[1])<<8)+float64(d[2]))/0x0120)*2 + 1)
2020-11-09 09:40:18 +01:00
if s.state.getSWR.pending {
s.removePendingCmd(&s.state.getSWR)
2020-11-04 10:41:12 +01:00
return false
}
2020-11-03 16:35:23 +01:00
case 0x15:
if len(d) < 3 {
2020-11-09 09:40:18 +01:00
return !s.state.getVd.pending
}
2020-11-03 16:35:23 +01:00
statusLog.reportVd(((float64(int(d[1])<<8) + float64(d[2])) / 0x0241) * 16)
2020-11-09 09:40:18 +01:00
if s.state.getVd.pending {
s.removePendingCmd(&s.state.getVd)
return false
}
2020-11-03 16:35:23 +01:00
}
return true
2020-11-03 16:35:23 +01:00
}
2020-11-09 09:40:18 +01:00
func (s *civControlStruct) decodePreampAGCNREnabled(d []byte) bool {
2020-11-03 16:19:37 +01:00
switch d[0] {
case 0x02:
if len(d) < 2 {
2020-11-09 09:40:18 +01:00
return !s.state.getPreamp.pending && !s.state.setPreamp.pending
}
2020-11-03 16:19:37 +01:00
s.state.preamp = int(d[1])
statusLog.reportPreamp(s.state.preamp)
2020-11-09 09:40:18 +01:00
if s.state.getPreamp.pending {
s.removePendingCmd(&s.state.getPreamp)
return false
}
if s.state.setPreamp.pending {
s.removePendingCmd(&s.state.setPreamp)
return false
}
2020-11-05 08:19:16 +01:00
case 0x12:
if len(d) < 2 {
2020-11-09 09:40:18 +01:00
return !s.state.getAGC.pending && !s.state.setAGC.pending
2020-11-05 08:19:16 +01:00
}
s.state.agc = int(d[1])
var agc string
switch s.state.agc {
case 1:
agc = "F"
case 2:
agc = "M"
case 3:
agc = "S"
}
statusLog.reportAGC(agc)
2020-11-09 09:40:18 +01:00
if s.state.getAGC.pending {
s.removePendingCmd(&s.state.getAGC)
return false
}
if s.state.setAGC.pending {
s.removePendingCmd(&s.state.setAGC)
2020-11-05 08:19:16 +01:00
return false
}
2020-11-04 10:01:07 +01:00
case 0x40:
if len(d) < 2 {
2020-11-09 09:40:18 +01:00
return !s.state.getNREnabled.pending && !s.state.setNREnabled.pending
}
2020-11-04 10:01:07 +01:00
if d[1] == 1 {
s.state.nrEnabled = true
} else {
s.state.nrEnabled = false
}
statusLog.reportNREnabled(s.state.nrEnabled)
2020-11-09 09:40:18 +01:00
if s.state.getNREnabled.pending {
s.removePendingCmd(&s.state.getNREnabled)
return false
}
if s.state.setNREnabled.pending {
s.removePendingCmd(&s.state.setNREnabled)
return false
}
2020-11-03 16:19:37 +01:00
}
return true
2020-11-03 16:19:37 +01:00
}
2020-11-09 09:40:18 +01:00
func (s *civControlStruct) initCmd(cmd *civCmd, name string, data []byte) {
*cmd = civCmd{}
cmd.name = name
cmd.cmd = data
}
func (s *civControlStruct) getPendingCmdIndex(cmd *civCmd) int {
for i := range s.state.pendingCmds {
if cmd == s.state.pendingCmds[i] {
return i
}
}
return -1
}
func (s *civControlStruct) removePendingCmd(cmd *civCmd) {
cmd.pending = false
index := s.getPendingCmdIndex(cmd)
if index < 0 {
return
}
s.state.pendingCmds[index] = s.state.pendingCmds[len(s.state.pendingCmds)-1]
s.state.pendingCmds[len(s.state.pendingCmds)-1] = nil
s.state.pendingCmds = s.state.pendingCmds[:len(s.state.pendingCmds)-1]
}
func (s *civControlStruct) sendCmd(cmd *civCmd) error {
cmd.pending = true
cmd.sentAt = time.Now()
if s.getPendingCmdIndex(cmd) < 0 {
s.state.pendingCmds = append(s.state.pendingCmds, cmd)
select {
case s.newPendingCmdAdded <- true:
default:
}
}
return s.st.send(cmd.cmd)
}
2020-10-30 23:06:34 +01:00
func (s *civControlStruct) setPwr(percent int) error {
v := uint16(0x0255 * (float64(percent) / 100))
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setPwr, "setPwr", []byte{254, 254, civAddress, 224, 0x14, 0x0a, byte(v >> 8), byte(v & 0xff), 253})
return s.sendCmd(&s.state.setPwr)
2020-10-30 23:06:34 +01:00
}
func (s *civControlStruct) incPwr() error {
if s.state.pwrPercent < 100 {
2020-10-30 23:58:10 +01:00
return s.setPwr(s.state.pwrPercent + 1)
2020-10-30 23:06:34 +01:00
}
return nil
}
func (s *civControlStruct) decPwr() error {
if s.state.pwrPercent > 0 {
2020-10-30 23:58:10 +01:00
return s.setPwr(s.state.pwrPercent - 1)
2020-10-30 23:06:34 +01:00
}
return nil
}
2020-11-04 09:41:36 +01:00
func (s *civControlStruct) setRFGain(percent int) error {
v := uint16(0x0255 * (float64(percent) / 100))
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setRFGain, "setRFGain", []byte{254, 254, civAddress, 224, 0x14, 0x02, byte(v >> 8), byte(v & 0xff), 253})
return s.sendCmd(&s.state.setRFGain)
2020-11-04 09:41:36 +01:00
}
func (s *civControlStruct) incRFGain() error {
if s.state.rfGainPercent < 100 {
return s.setRFGain(s.state.rfGainPercent + 1)
}
return nil
}
func (s *civControlStruct) decRFGain() error {
if s.state.rfGainPercent > 0 {
return s.setRFGain(s.state.rfGainPercent - 1)
}
return nil
}
func (s *civControlStruct) setSQL(percent int) error {
v := uint16(0x0255 * (float64(percent) / 100))
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setSQL, "setSQL", []byte{254, 254, civAddress, 224, 0x14, 0x03, byte(v >> 8), byte(v & 0xff), 253})
return s.sendCmd(&s.state.setSQL)
}
func (s *civControlStruct) incSQL() error {
if s.state.sqlPercent < 100 {
return s.setSQL(s.state.sqlPercent + 1)
}
return nil
}
func (s *civControlStruct) decSQL() error {
if s.state.sqlPercent > 0 {
return s.setSQL(s.state.sqlPercent - 1)
}
return nil
}
2020-11-04 10:01:07 +01:00
func (s *civControlStruct) setNR(percent int) error {
if !s.state.nrEnabled {
if err := s.toggleNR(); err != nil {
return err
}
}
2020-11-04 10:01:07 +01:00
v := uint16(0x0255 * (float64(percent) / 100))
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setNR, "setNR", []byte{254, 254, civAddress, 224, 0x14, 0x06, byte(v >> 8), byte(v & 0xff), 253})
return s.sendCmd(&s.state.setNR)
2020-11-04 10:01:07 +01:00
}
func (s *civControlStruct) incNR() error {
if s.state.nrPercent < 100 {
return s.setNR(s.state.nrPercent + 1)
}
return nil
}
func (s *civControlStruct) decNR() error {
if s.state.nrPercent > 0 {
return s.setNR(s.state.nrPercent - 1)
}
return nil
}
2020-11-01 12:46:37 +01:00
func (s *civControlStruct) getDigit(v uint, n int) byte {
f := float64(v)
2020-10-30 23:58:10 +01:00
for n > 0 {
2020-11-01 12:46:37 +01:00
f /= 10
2020-10-30 23:58:10 +01:00
n--
}
2020-11-01 12:46:37 +01:00
return byte(uint(f) % 10)
2020-10-30 23:58:10 +01:00
}
func (s *civControlStruct) incFreq() error {
return s.setFreq(s.state.freq + s.state.ts)
2020-10-30 23:58:10 +01:00
}
func (s *civControlStruct) decFreq() error {
return s.setFreq(s.state.freq - s.state.ts)
2020-10-30 23:58:10 +01:00
}
2020-11-01 12:46:37 +01:00
func (s *civControlStruct) setFreq(f uint) error {
2020-10-30 23:58:10 +01:00
var b [5]byte
v0 := s.getDigit(f, 9)
v1 := s.getDigit(f, 8)
b[4] = v0<<4 | v1
v0 = s.getDigit(f, 7)
v1 = s.getDigit(f, 6)
b[3] = v0<<4 | v1
v0 = s.getDigit(f, 5)
v1 = s.getDigit(f, 4)
b[2] = v0<<4 | v1
v0 = s.getDigit(f, 3)
v1 = s.getDigit(f, 2)
b[1] = v0<<4 | v1
v0 = s.getDigit(f, 1)
v1 = s.getDigit(f, 0)
b[0] = v0<<4 | v1
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setFreq, "setFreq", []byte{254, 254, civAddress, 224, 5, b[0], b[1], b[2], b[3], b[4], 253})
return s.sendCmd(&s.state.setFreq)
2020-10-30 23:58:10 +01:00
}
func (s *civControlStruct) incOperatingMode() error {
s.state.operatingModeIdx++
if s.state.operatingModeIdx >= len(civOperatingModes) {
s.state.operatingModeIdx = 0
}
return civControl.setOperatingModeAndFilter(civOperatingModes[s.state.operatingModeIdx].code,
civFilters[s.state.filterIdx].code)
}
func (s *civControlStruct) decOperatingMode() error {
s.state.operatingModeIdx--
if s.state.operatingModeIdx < 0 {
s.state.operatingModeIdx = len(civOperatingModes) - 1
}
return civControl.setOperatingModeAndFilter(civOperatingModes[s.state.operatingModeIdx].code,
civFilters[s.state.filterIdx].code)
}
func (s *civControlStruct) incFilter() error {
s.state.filterIdx++
if s.state.filterIdx >= len(civFilters) {
s.state.filterIdx = 0
}
return civControl.setOperatingModeAndFilter(civOperatingModes[s.state.operatingModeIdx].code,
civFilters[s.state.filterIdx].code)
}
func (s *civControlStruct) decFilter() error {
s.state.filterIdx--
if s.state.filterIdx < 0 {
s.state.filterIdx = len(civFilters) - 1
}
return civControl.setOperatingModeAndFilter(civOperatingModes[s.state.operatingModeIdx].code,
civFilters[s.state.filterIdx].code)
}
func (s *civControlStruct) setOperatingModeAndFilter(modeCode, filterCode byte) error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setMode, "setMode", []byte{254, 254, civAddress, 224, 0x06, modeCode, filterCode, 253})
if err := s.sendCmd(&s.state.setMode); err != nil {
return err
}
return s.getMode()
}
func (s *civControlStruct) setPTT(enable bool) error {
var b byte
if enable {
b = 1
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setPTT, "setPTT", []byte{254, 254, civAddress, 224, 0x1c, 0, b, 253})
return s.sendCmd(&s.state.setPTT)
}
2020-10-30 22:58:31 +01:00
func (s *civControlStruct) toggleTune() error {
if s.state.ptt {
return nil
}
var b byte
if !s.state.tune {
b = 2
} else {
b = 1
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setTune, "setTune", []byte{254, 254, civAddress, 224, 0x1c, 1, b, 253})
return s.sendCmd(&s.state.setTune)
2020-10-30 22:58:31 +01:00
}
2020-11-08 20:48:39 +01:00
func (s *civControlStruct) setDataMode(enable bool) error {
var b byte
var f byte
2020-11-08 20:48:39 +01:00
if enable {
b = 1
f = 1
} else {
b = 0
f = 0
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setDataMode, "setDataMode", []byte{254, 254, civAddress, 224, 0x1a, 0x06, b, f, 253})
return s.sendCmd(&s.state.setDataMode)
}
2020-11-08 20:48:39 +01:00
func (s *civControlStruct) toggleDataMode() error {
return s.setDataMode(!s.state.dataMode)
}
2020-10-31 23:44:45 +01:00
func (s *civControlStruct) incBand() error {
s.state.bandChanging = true
i := s.state.bandIdx + 1
if i >= len(civBands) {
i = 0
}
f := civBands[i].freq
if f == 0 {
f = (civBands[i].freqFrom + civBands[i].freqTo) / 2
}
return s.setFreq(f)
}
func (s *civControlStruct) decBand() error {
s.state.bandChanging = true
i := s.state.bandIdx - 1
if i < 0 {
i = len(civBands) - 1
}
f := civBands[i].freq
if f == 0 {
f = civBands[i].freqFrom
}
return s.setFreq(f)
}
2020-11-03 16:19:37 +01:00
func (s *civControlStruct) togglePreamp() error {
b := byte(s.state.preamp + 1)
if b > 2 {
b = 0
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setPreamp, "setPreamp", []byte{254, 254, civAddress, 224, 0x16, 0x02, b, 253})
return s.sendCmd(&s.state.setPreamp)
2020-11-03 16:19:37 +01:00
}
2020-11-05 08:19:16 +01:00
func (s *civControlStruct) toggleAGC() error {
b := byte(s.state.agc + 1)
if b > 3 {
b = 1
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setAGC, "setAGC", []byte{254, 254, civAddress, 224, 0x16, 0x12, b, 253})
return s.sendCmd(&s.state.setAGC)
2020-11-05 08:19:16 +01:00
}
2020-11-04 10:01:07 +01:00
func (s *civControlStruct) toggleNR() error {
var b byte
if !s.state.nrEnabled {
b = 1
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setNREnabled, "setNREnabled", []byte{254, 254, civAddress, 224, 0x16, 0x40, b, 253})
return s.sendCmd(&s.state.setNREnabled)
}
func (s *civControlStruct) setTS(b byte) error {
s.initCmd(&s.state.setTS, "setTS", []byte{254, 254, civAddress, 224, 0x10, b, 253})
return s.sendCmd(&s.state.setTS)
2020-11-04 10:01:07 +01:00
}
func (s *civControlStruct) incTS() error {
var b byte
if s.state.tsValue == 13 {
b = 0
} else {
b = s.state.tsValue + 1
}
2020-11-09 09:40:18 +01:00
return s.setTS(b)
}
func (s *civControlStruct) decTS() error {
var b byte
if s.state.tsValue == 0 {
b = 13
} else {
b = s.state.tsValue - 1
}
2020-11-09 09:40:18 +01:00
return s.setTS(b)
}
2020-11-08 20:48:39 +01:00
func (s *civControlStruct) setVFO(nr byte) error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setVFO, "setVFO", []byte{254, 254, civAddress, 224, 0x07, nr, 253})
return s.sendCmd(&s.state.setVFO)
2020-11-08 20:48:39 +01:00
}
func (s *civControlStruct) toggleVFO() error {
var b byte
if !s.state.vfoBActive {
b = 1
}
2020-11-08 20:48:39 +01:00
return s.setVFO(b)
}
2020-11-08 20:48:39 +01:00
func (s *civControlStruct) setSplit(mode splitMode) error {
2020-11-07 18:09:09 +01:00
var b byte
2020-11-08 20:48:39 +01:00
switch mode {
default:
b = 0x00
case splitModeOn:
b = 0x01
case splitModeDUPMinus:
b = 0x11
case splitModeDUPPlus:
b = 0x12
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.setSplit, "setSplit", []byte{254, 254, civAddress, 224, 0x0f, b, 253})
return s.sendCmd(&s.state.setSplit)
2020-11-08 20:48:39 +01:00
}
func (s *civControlStruct) toggleSplit() error {
var mode splitMode
2020-11-07 18:09:09 +01:00
switch s.state.splitMode {
case splitModeOff:
2020-11-08 20:48:39 +01:00
mode = splitModeOn
2020-11-07 18:09:09 +01:00
case splitModeOn:
2020-11-08 20:48:39 +01:00
mode = splitModeDUPMinus
2020-11-07 18:09:09 +01:00
case splitModeDUPMinus:
2020-11-08 20:48:39 +01:00
mode = splitModeDUPPlus
2020-11-07 18:09:09 +01:00
default:
2020-11-08 20:48:39 +01:00
mode = splitModeOff
2020-11-07 18:09:09 +01:00
}
2020-11-08 20:48:39 +01:00
return s.setSplit(mode)
2020-11-07 18:09:09 +01:00
}
2020-10-30 23:58:10 +01:00
func (s *civControlStruct) getFreq() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getFreq, "getFreq", []byte{254, 254, civAddress, 224, 3, 253})
return s.sendCmd(&s.state.getFreq)
2020-10-30 23:58:10 +01:00
}
func (s *civControlStruct) getMode() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getMode, "getMode", []byte{254, 254, civAddress, 224, 4, 253})
return s.sendCmd(&s.state.getMode)
}
2020-10-30 22:58:31 +01:00
func (s *civControlStruct) getDataMode() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getDataMode, "getDataMode", []byte{254, 254, civAddress, 224, 0x1a, 0x06, 253})
return s.sendCmd(&s.state.getDataMode)
}
func (s *civControlStruct) getPwr() error {
s.initCmd(&s.state.getPwr, "getPwr", []byte{254, 254, civAddress, 224, 0x14, 0x0a, 253})
return s.sendCmd(&s.state.getPwr)
2020-10-30 22:58:31 +01:00
}
func (s *civControlStruct) getTransmitStatus() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getTransmitStatus, "getTransmitStatus", []byte{254, 254, civAddress, 224, 0x1c, 0, 253})
if err := s.sendCmd(&s.state.getTransmitStatus); err != nil {
2020-10-30 22:58:31 +01:00
return err
}
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getTuneStatus, "getTuneStatus", []byte{254, 254, civAddress, 224, 0x1c, 1, 253})
return s.sendCmd(&s.state.getTuneStatus)
}
func (s *civControlStruct) getPreamp() error {
s.initCmd(&s.state.getPreamp, "getPreamp", []byte{254, 254, civAddress, 224, 0x16, 0x02, 253})
return s.sendCmd(&s.state.getPreamp)
}
func (s *civControlStruct) getAGC() error {
s.initCmd(&s.state.getAGC, "getAGC", []byte{254, 254, civAddress, 224, 0x16, 0x12, 253})
return s.sendCmd(&s.state.getAGC)
2020-10-30 22:58:31 +01:00
}
2020-11-03 16:35:23 +01:00
func (s *civControlStruct) getVd() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getVd, "getVd", []byte{254, 254, civAddress, 224, 0x15, 0x15, 253})
return s.sendCmd(&s.state.getVd)
2020-11-03 16:35:23 +01:00
}
func (s *civControlStruct) getS() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getS, "getS", []byte{254, 254, civAddress, 224, 0x15, 0x02, 253})
return s.sendCmd(&s.state.getS)
}
func (s *civControlStruct) getOVF() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getOVF, "getOVF", []byte{254, 254, civAddress, 224, 0x1a, 0x09, 253})
return s.sendCmd(&s.state.getOVF)
}
2020-11-04 10:41:12 +01:00
func (s *civControlStruct) getSWR() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getSWR, "getSWR", []byte{254, 254, civAddress, 224, 0x15, 0x12, 253})
return s.sendCmd(&s.state.getSWR)
2020-11-04 10:41:12 +01:00
}
func (s *civControlStruct) getTS() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getTS, "getTS", []byte{254, 254, civAddress, 224, 0x10, 253})
return s.sendCmd(&s.state.getTS)
}
2020-11-04 09:41:36 +01:00
func (s *civControlStruct) getRFGain() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getRFGain, "getRFGain", []byte{254, 254, civAddress, 224, 0x14, 0x02, 253})
return s.sendCmd(&s.state.getRFGain)
2020-11-04 09:41:36 +01:00
}
func (s *civControlStruct) getSQL() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getSQL, "getSQL", []byte{254, 254, civAddress, 224, 0x14, 0x03, 253})
return s.sendCmd(&s.state.getSQL)
}
2020-11-04 10:01:07 +01:00
func (s *civControlStruct) getNR() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getNR, "getNR", []byte{254, 254, civAddress, 224, 0x14, 0x06, 253})
return s.sendCmd(&s.state.getNR)
2020-11-04 10:01:07 +01:00
}
func (s *civControlStruct) getNREnabled() error {
2020-11-09 09:40:18 +01:00
s.initCmd(&s.state.getNREnabled, "getNREnabled", []byte{254, 254, civAddress, 224, 0x16, 0x40, 253})
return s.sendCmd(&s.state.getNREnabled)
}
func (s *civControlStruct) getSplit() error {
s.initCmd(&s.state.getSplit, "getSplit", []byte{254, 254, civAddress, 224, 0x0f, 253})
return s.sendCmd(&s.state.getSplit)
2020-11-04 10:01:07 +01:00
}
func (s *civControlStruct) loop() {
for {
2020-11-09 09:40:18 +01:00
s.state.mutex.Lock()
nextPendingCmdTimeout := time.Hour
for i := range s.state.pendingCmds {
diff := time.Since(s.state.pendingCmds[i].sentAt)
if diff >= commandRetryTimeout {
nextPendingCmdTimeout = 0
break
}
if diff < nextPendingCmdTimeout {
nextPendingCmdTimeout = diff
}
}
s.state.mutex.Unlock()
select {
case <-s.deinitNeeded:
s.deinitFinished <- true
return
2020-11-09 09:40:18 +01:00
case <-time.After(statusPollInterval):
if !s.state.getS.pending && time.Since(s.state.lastSReceivedAt) >= statusPollInterval {
_ = s.getS()
}
2020-11-09 09:40:18 +01:00
if !s.state.getOVF.pending && time.Since(s.state.lastOVFReceivedAt) >= statusPollInterval {
_ = s.getOVF()
}
2020-11-09 09:40:18 +01:00
if !s.state.getSWR.pending && time.Since(s.state.lastSWRReceivedAt) >= statusPollInterval &&
(s.state.ptt || s.state.tune) {
2020-11-05 16:38:35 +01:00
_ = s.getSWR()
}
case <-s.resetSReadTimer:
2020-11-09 09:40:18 +01:00
case <-s.newPendingCmdAdded:
case <-time.After(nextPendingCmdTimeout):
s.state.mutex.Lock()
for _, cmd := range s.state.pendingCmds {
if time.Since(cmd.sentAt) >= commandRetryTimeout {
log.Debug("retrying cmd send ", cmd.name)
_ = s.sendCmd(cmd)
}
}
s.state.mutex.Unlock()
}
}
}
func (s *civControlStruct) init(st *serialStream) error {
s.st = st
2020-10-30 23:58:10 +01:00
if err := s.getFreq(); err != nil {
return err
}
if err := s.getMode(); err != nil {
return err
}
2020-10-30 22:58:31 +01:00
if err := s.getDataMode(); err != nil {
return err
}
2020-11-09 09:40:18 +01:00
if err := s.getPwr(); err != nil {
return err
}
2020-10-30 22:58:31 +01:00
if err := s.getTransmitStatus(); err != nil {
return err
}
2020-11-09 09:40:18 +01:00
if err := s.getPreamp(); err != nil {
2020-11-03 16:19:37 +01:00
return err
}
2020-11-09 09:40:18 +01:00
if err := s.getAGC(); err != nil {
2020-11-05 08:19:16 +01:00
return err
}
2020-11-03 16:35:23 +01:00
if err := s.getVd(); err != nil {
return err
}
if err := s.getS(); err != nil {
return err
}
if err := s.getOVF(); err != nil {
return err
}
2020-11-04 10:41:12 +01:00
if err := s.getSWR(); err != nil {
return err
}
if err := s.getTS(); err != nil {
return err
}
2020-11-04 09:41:36 +01:00
if err := s.getRFGain(); err != nil {
return err
}
if err := s.getSQL(); err != nil {
return err
}
2020-11-04 10:01:07 +01:00
if err := s.getNR(); err != nil {
return err
}
if err := s.getNREnabled(); err != nil {
return err
}
2020-11-09 09:40:18 +01:00
if err := s.getSplit(); err != nil {
2020-11-07 18:09:09 +01:00
return err
}
s.deinitNeeded = make(chan bool)
s.deinitFinished = make(chan bool)
s.resetSReadTimer = make(chan bool)
2020-11-09 09:40:18 +01:00
s.newPendingCmdAdded = make(chan bool)
go s.loop()
return nil
}
func (s *civControlStruct) deinit() {
if s.deinitNeeded != nil {
s.deinitNeeded <- true
<-s.deinitFinished
}
s.deinitNeeded = nil
}