kappanhang/civcontrol.go

786 lines
17 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"
"time"
)
const civAddress = 0xa4
const sReadInterval = time.Second
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-10-30 20:28:45 +01:00
type civControlStruct struct {
st *serialStream
deinitNeeded chan bool
deinitFinished chan bool
resetSReadTimer chan bool
2020-10-30 22:58:31 +01:00
state struct {
getSSent bool
getOVFSent bool
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
tsValue byte
ts uint
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]
switch d[4] {
case 0x00:
s.decodeFreq(payload)
case 0x01:
s.decodeMode(payload)
case 0x03:
s.decodeFreq(payload)
case 0x04:
s.decodeMode(payload)
case 0x10:
s.decodeTS(payload)
2020-10-30 22:58:31 +01:00
case 0x1a:
return s.decodeDataModeAndOVF(payload)
case 0x14:
2020-11-04 10:01:07 +01:00
s.decodePowerRFGainSQLNR(payload)
case 0x1c:
s.decodeTransmitStatus(payload)
2020-11-03 16:35:23 +01:00
case 0x15:
return s.decodeVdAndS(payload)
2020-11-03 16:19:37 +01:00
case 0x16:
2020-11-04 10:01:07 +01:00
s.decodePreampAndNR(payload)
}
return true
}
2020-10-30 20:28:45 +01:00
func (s *civControlStruct) decodeFreq(d []byte) {
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 16:57:58 +01:00
if f == 0 {
return
}
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
}
}
}
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
}
2020-10-30 20:28:45 +01:00
func (s *civControlStruct) decodeMode(d []byte) {
if len(d) < 1 {
return
}
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
// The transceiver does not send the data mode setting automatically.
_ = s.getDataMode()
}
func (s *civControlStruct) decodeTS(d []byte) {
if len(d) < 1 {
return
}
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)
}
func (s *civControlStruct) decodeDataModeAndOVF(d []byte) bool {
if len(d) < 1 {
return true
2020-10-30 22:58:31 +01:00
}
switch d[0] {
case 0x06:
if len(d) < 3 {
return true
}
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)
case 0x09:
if len(d) < 2 {
return !s.state.getOVFSent
}
if d[1] != 0 {
statusLog.reportOVF(true)
} else {
statusLog.reportOVF(false)
}
if s.state.getOVFSent {
s.state.getOVFSent = false
return false
}
}
return true
}
2020-11-04 10:01:07 +01:00
func (s *civControlStruct) decodePowerRFGainSQLNR(d []byte) {
2020-11-04 09:41:36 +01:00
if len(d) < 3 {
return
}
2020-11-04 09:41:36 +01:00
switch d[0] {
case 0x02:
hex := uint16(d[1])<<8 | uint16(d[2])
s.state.rfGainPercent = int(math.Round((float64(hex) / 0x0255) * 100))
statusLog.reportRFGain(s.state.rfGainPercent)
case 0x03:
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-04 10:01:07 +01:00
case 0x06:
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-04 09:41:36 +01:00
case 0x0a:
hex := uint16(d[1])<<8 | uint16(d[2])
s.state.pwrPercent = int(math.Round((float64(hex) / 0x0255) * 100))
statusLog.reportTxPower(s.state.pwrPercent)
}
}
func (s *civControlStruct) decodeTransmitStatus(d []byte) {
if len(d) < 2 {
return
}
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()
}
}
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()
}
}
}
2020-10-30 22:58:31 +01:00
statusLog.reportPTT(s.state.ptt, s.state.tune)
}
func (s *civControlStruct) decodeVdAndS(d []byte) bool {
if len(d) < 1 {
return true
2020-11-03 16:35:23 +01:00
}
switch d[0] {
case 0x02:
if len(d) < 3 {
return !s.state.getSSent
}
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"
}
}
statusLog.reportS(sStr)
if s.state.getSSent {
s.state.getSSent = false
return false
}
2020-11-03 16:35:23 +01:00
case 0x15:
if len(d) < 3 {
return true
}
2020-11-03 16:35:23 +01:00
statusLog.reportVd(((float64(int(d[1])<<8) + float64(d[2])) / 0x0241) * 16)
}
return true
2020-11-03 16:35:23 +01:00
}
2020-11-04 10:01:07 +01:00
func (s *civControlStruct) decodePreampAndNR(d []byte) {
2020-11-03 16:19:37 +01:00
if len(d) < 2 {
return
}
switch d[0] {
case 0x02:
s.state.preamp = int(d[1])
statusLog.reportPreamp(s.state.preamp)
2020-11-04 10:01:07 +01:00
case 0x40:
if d[1] == 1 {
s.state.nrEnabled = true
} else {
s.state.nrEnabled = false
}
statusLog.reportNREnabled(s.state.nrEnabled)
2020-11-03 16:19:37 +01:00
}
}
2020-10-30 23:06:34 +01:00
func (s *civControlStruct) setPwr(percent int) error {
v := uint16(0x0255 * (float64(percent) / 100))
return s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x0a, byte(v >> 8), byte(v & 0xff), 253})
}
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))
return s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x02, byte(v >> 8), byte(v & 0xff), 253})
}
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))
return s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x03, byte(v >> 8), byte(v & 0xff), 253})
}
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 {
v := uint16(0x0255 * (float64(percent) / 100))
return s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x06, byte(v >> 8), byte(v & 0xff), 253})
}
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
if err := s.st.send([]byte{254, 254, civAddress, 224, 5, b[0], b[1], b[2], b[3], b[4], 253}); err != nil {
return err
}
// The transceiver does not send the new freq automatically.
return s.getFreq()
}
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 {
if err := s.st.send([]byte{254, 254, civAddress, 224, 0x06, modeCode, filterCode, 253}); err != nil {
return err
}
return s.getMode()
}
func (s *civControlStruct) setPTT(enable bool) error {
var b byte
if enable {
b = 1
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x1c, 0, b, 253})
}
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
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x1c, 1, b, 253})
}
func (s *civControlStruct) toggleDataMode() error {
var b byte
var f byte
if !s.state.dataMode {
b = 1
f = 1
} else {
b = 0
f = 0
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x1a, 0x06, b, f, 253})
}
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
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x16, 0x02, b, 253})
}
2020-11-04 10:01:07 +01:00
func (s *civControlStruct) toggleNR() error {
var b byte
if !s.state.nrEnabled {
b = 1
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x16, 0x40, b, 253})
}
func (s *civControlStruct) incTS() error {
var b byte
if s.state.tsValue == 13 {
b = 0
} else {
b = s.state.tsValue + 1
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x10, b, 253})
}
func (s *civControlStruct) decTS() error {
var b byte
if s.state.tsValue == 0 {
b = 13
} else {
b = s.state.tsValue - 1
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x10, b, 253})
}
2020-10-30 23:58:10 +01:00
func (s *civControlStruct) getFreq() error {
return s.st.send([]byte{254, 254, civAddress, 224, 3, 253})
}
func (s *civControlStruct) getMode() error {
return s.st.send([]byte{254, 254, civAddress, 224, 4, 253})
}
2020-10-30 22:58:31 +01:00
func (s *civControlStruct) getDataMode() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x1a, 0x06, 253})
}
func (s *civControlStruct) getTransmitStatus() error {
if err := s.st.send([]byte{254, 254, civAddress, 224, 0x1c, 0, 253}); err != nil {
return err
}
return s.st.send([]byte{254, 254, civAddress, 224, 0x1c, 1, 253})
}
2020-11-03 16:35:23 +01:00
func (s *civControlStruct) getVd() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x15, 0x15, 253})
}
func (s *civControlStruct) getS() error {
s.state.getSSent = true
return s.st.send([]byte{254, 254, civAddress, 224, 0x15, 0x02, 253})
}
func (s *civControlStruct) getOVF() error {
s.state.getOVFSent = true
return s.st.send([]byte{254, 254, civAddress, 224, 0x1a, 0x09, 253})
}
func (s *civControlStruct) getTS() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x10, 253})
}
2020-11-04 09:41:36 +01:00
func (s *civControlStruct) getRFGain() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x02, 253})
}
func (s *civControlStruct) getSQL() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x03, 253})
}
2020-11-04 10:01:07 +01:00
func (s *civControlStruct) getNR() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x06, 253})
}
func (s *civControlStruct) getNREnabled() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x16, 0x40, 253})
}
func (s *civControlStruct) loop() {
for {
select {
case <-s.deinitNeeded:
s.deinitFinished <- true
return
case <-time.After(sReadInterval):
_ = s.getS()
_ = s.getOVF()
case <-s.resetSReadTimer:
}
}
}
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
}
// Querying power.
if err := s.st.send([]byte{254, 254, civAddress, 224, 0x14, 0x0a, 253}); err != nil {
return err
}
2020-10-30 22:58:31 +01:00
if err := s.getTransmitStatus(); err != nil {
return err
}
2020-11-03 16:19:37 +01:00
// Querying preamp.
if err := s.st.send([]byte{254, 254, civAddress, 224, 0x16, 0x02, 253}); err != nil {
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
}
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
}
s.deinitNeeded = make(chan bool)
s.deinitFinished = make(chan bool)
s.resetSReadTimer = make(chan bool)
go s.loop()
return nil
}
func (s *civControlStruct) deinit() {
if s.deinitNeeded != nil {
s.deinitNeeded <- true
<-s.deinitFinished
}
s.deinitNeeded = nil
}