kappanhang/civcontrol.go
2020-11-04 10:41:12 +01:00

805 lines
17 KiB
Go

package main
import (
"fmt"
"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},
}
type civBand struct {
freqFrom uint
freqTo uint
freq uint
}
var civBands = []civBand{
{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
}
type civControlStruct struct {
st *serialStream
deinitNeeded chan bool
deinitFinished chan bool
resetSReadTimer chan bool
state struct {
getSSent bool
getOVFSent bool
getSWRSent bool
freq uint
ptt bool
tune bool
pwrPercent int
rfGainPercent int
sqlPercent int
nrPercent int
nrEnabled bool
operatingModeIdx int
filterIdx int
dataMode bool
bandIdx int
bandChanging bool
preamp int
tsValue byte
ts uint
}
}
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)
case 0x1a:
return s.decodeDataModeAndOVF(payload)
case 0x14:
s.decodePowerRFGainSQLNR(payload)
case 0x1c:
s.decodeTransmitStatus(payload)
case 0x15:
return s.decodeVdSWRS(payload)
case 0x16:
s.decodePreampAndNR(payload)
}
return true
}
func (s *civControlStruct) decodeFreq(d []byte) {
var f uint
var pos int
for _, v := range d {
s1 := v & 0x0f
s2 := v >> 4
f += uint(s1) * uint(math.Pow(10, float64(pos)))
pos++
f += uint(s2) * uint(math.Pow(10, float64(pos)))
pos++
}
if f == 0 {
return
}
s.state.freq = f
statusLog.reportFrequency(s.state.freq)
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
}
}
return -1
}
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)
// 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
}
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
}
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
}
func (s *civControlStruct) decodePowerRFGainSQLNR(d []byte) {
if len(d) < 3 {
return
}
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)
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)
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 {
s.state.ptt = true
} else {
if s.state.ptt { // PTT released?
s.state.ptt = false
_ = s.getVd()
}
}
case 1:
if d[1] == 2 {
s.state.tune = true
// The transceiver does not send the tune state after it's finished.
time.AfterFunc(time.Second, func() {
_ = s.getTransmitStatus()
})
} else {
if s.state.tune { // Tune finished?
s.state.tune = false
_ = s.getVd()
}
}
}
statusLog.reportPTT(s.state.ptt, s.state.tune)
}
func (s *civControlStruct) decodeVdSWRS(d []byte) bool {
if len(d) < 1 {
return true
}
switch d[0] {
case 0x02:
if len(d) < 3 {
return !s.state.getSSent
}
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+"
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:
sStr += "60"
}
}
statusLog.reportS(sStr)
if s.state.getSSent {
s.state.getSSent = false
return false
}
case 0x12:
if len(d) < 3 {
return !s.state.getSWRSent
}
statusLog.reportSWR(((float64(int(d[1])<<8) + float64(d[2])) / 0x0241) * 16)
if s.state.getSWRSent {
s.state.getSWRSent = false
return false
}
case 0x15:
if len(d) < 3 {
return true
}
statusLog.reportVd(((float64(int(d[1])<<8) + float64(d[2])) / 0x0241) * 16)
}
return true
}
func (s *civControlStruct) decodePreampAndNR(d []byte) {
if len(d) < 2 {
return
}
switch d[0] {
case 0x02:
s.state.preamp = int(d[1])
statusLog.reportPreamp(s.state.preamp)
case 0x40:
if d[1] == 1 {
s.state.nrEnabled = true
} else {
s.state.nrEnabled = false
}
statusLog.reportNREnabled(s.state.nrEnabled)
}
}
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 {
return s.setPwr(s.state.pwrPercent + 1)
}
return nil
}
func (s *civControlStruct) decPwr() error {
if s.state.pwrPercent > 0 {
return s.setPwr(s.state.pwrPercent - 1)
}
return nil
}
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
}
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
}
func (s *civControlStruct) getDigit(v uint, n int) byte {
f := float64(v)
for n > 0 {
f /= 10
n--
}
return byte(uint(f) % 10)
}
func (s *civControlStruct) incFreq() error {
return s.setFreq(s.state.freq + s.state.ts)
}
func (s *civControlStruct) decFreq() error {
return s.setFreq(s.state.freq - s.state.ts)
}
func (s *civControlStruct) setFreq(f uint) error {
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})
}
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})
}
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)
}
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})
}
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})
}
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})
}
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})
}
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) getSWR() error {
s.state.getSWRSent = true
return s.st.send([]byte{254, 254, civAddress, 224, 0x15, 0x12, 253})
}
func (s *civControlStruct) getTS() error {
return s.st.send([]byte{254, 254, civAddress, 224, 0x10, 253})
}
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})
}
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()
_ = s.getSWR()
case <-s.resetSReadTimer:
}
}
}
func (s *civControlStruct) init(st *serialStream) error {
s.st = st
if err := s.getFreq(); err != nil {
return err
}
if err := s.getMode(); err != nil {
return err
}
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
}
if err := s.getTransmitStatus(); err != nil {
return err
}
// Querying preamp.
if err := s.st.send([]byte{254, 254, civAddress, 224, 0x16, 0x02, 253}); err != nil {
return err
}
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.getSWR(); err != nil {
return err
}
if err := s.getTS(); err != nil {
return err
}
if err := s.getRFGain(); err != nil {
return err
}
if err := s.getSQL(); err != nil {
return err
}
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
}