mirror of
https://github.com/nonoo/kappanhang.git
synced 2026-01-25 10:00:21 +01:00
Increased precision of value changes by over double
instead of adjusting by percentage, adjust by single steps (IE 255 steps instead of 100) Implement BCD <-> decimal conversions via functions to reduce code duplication Corrected to use actual BCD
This commit is contained in:
parent
819a250c7e
commit
7673ffe0a9
150
civcontrol.go
150
civcontrol.go
|
|
@ -175,10 +175,10 @@ type civControlStruct struct {
|
|||
subFreq uint
|
||||
ptt bool
|
||||
tune bool
|
||||
pwrPercent int
|
||||
rfGainPercent int
|
||||
sqlPercent int
|
||||
nrPercent int
|
||||
pwrLevel int
|
||||
rfGainLevel int
|
||||
sqlLevel int
|
||||
nrLevel int
|
||||
nrEnabled bool
|
||||
operatingModeIdx int
|
||||
dataMode bool
|
||||
|
|
@ -595,7 +595,7 @@ func (s *civControlStruct) decodeDataModeAndOVF(d []byte) bool {
|
|||
func (s *civControlStruct) decodePowerRFGainSQLNRPwr(d []byte) bool {
|
||||
// all of these returns are expected to be three bytes long
|
||||
// subcmd, data msb, data lsb (where data is encoded as BCD)
|
||||
// code wouldbe easier to read if we check size and do value extraction first
|
||||
// code would be easier to read if we check size and do value extraction first
|
||||
// then take actions on appropriate entities in each case
|
||||
//
|
||||
|
||||
|
|
@ -604,8 +604,8 @@ func (s *civControlStruct) decodePowerRFGainSQLNRPwr(d []byte) bool {
|
|||
if len(d) < 3 { // has at least three bytes of data
|
||||
return !s.state.getRFGain.pending && !s.state.setRFGain.pending
|
||||
}
|
||||
s.state.rfGainPercent = returnedLevel
|
||||
statusLog.reportRFGain(s.state.rfGainPercent)
|
||||
s.state.rfGainLevel = returnedLevel
|
||||
statusLog.reportRFGain(s.state.rfGainLevel)
|
||||
if s.state.getRFGain.pending {
|
||||
s.removePendingCmd(&s.state.getRFGain)
|
||||
return false
|
||||
|
|
@ -622,8 +622,8 @@ func (s *civControlStruct) decodePowerRFGainSQLNRPwr(d []byte) bool {
|
|||
if len(data) < 2 {
|
||||
return !s.state.getRFGain.pending && !s.state.setRFGain.pending
|
||||
}
|
||||
s.state.rfGainPercent = BCDAsPct(data)
|
||||
statusLog.reportRFGain(s.state.rfGainPercent)
|
||||
s.state.rfGainLevel = BCDToDec(data)
|
||||
statusLog.reportRFGain(s.state.rfGainLevel)
|
||||
if s.state.getRFGain.pending {
|
||||
s.removePendingCmd(&s.state.getRFGain)
|
||||
return false
|
||||
|
|
@ -636,8 +636,8 @@ func (s *civControlStruct) decodePowerRFGainSQLNRPwr(d []byte) bool {
|
|||
if len(data) < 2 {
|
||||
return !s.state.getSQL.pending && !s.state.setSQL.pending
|
||||
}
|
||||
s.state.sqlPercent = BCDAsPct(data)
|
||||
statusLog.reportSQL(s.state.sqlPercent)
|
||||
s.state.sqlLevel = BCDToDec(data)
|
||||
statusLog.reportSQL(s.state.sqlLevel)
|
||||
if s.state.getSQL.pending {
|
||||
s.removePendingCmd(&s.state.getSQL)
|
||||
return false
|
||||
|
|
@ -649,8 +649,8 @@ func (s *civControlStruct) decodePowerRFGainSQLNRPwr(d []byte) bool {
|
|||
if len(data) < 2 {
|
||||
return !s.state.getNR.pending && !s.state.setNR.pending
|
||||
}
|
||||
s.state.nrPercent = BCDAsPct(data)
|
||||
statusLog.reportNR(s.state.nrPercent)
|
||||
s.state.nrLevel = BCDToDec(data)
|
||||
statusLog.reportNR(s.state.nrLevel)
|
||||
if s.state.getNR.pending {
|
||||
s.removePendingCmd(&s.state.getNR)
|
||||
return false
|
||||
|
|
@ -663,8 +663,8 @@ func (s *civControlStruct) decodePowerRFGainSQLNRPwr(d []byte) bool {
|
|||
if len(data) < 2 {
|
||||
return !s.state.getPwr.pending && !s.state.setPwr.pending
|
||||
}
|
||||
s.state.pwrPercent = BCDAsPct(data)
|
||||
statusLog.reportTxPower(s.state.pwrPercent)
|
||||
s.state.pwrLevel = BCDToDec(data)
|
||||
statusLog.reportTxPower(s.state.pwrLevel)
|
||||
if s.state.getPwr.pending {
|
||||
s.removePendingCmd(&s.state.getPwr)
|
||||
return false
|
||||
|
|
@ -800,7 +800,7 @@ func (s *civControlStruct) decodeVdSWRS(d []byte) bool {
|
|||
return !s.state.getSWR.pending
|
||||
}
|
||||
s.state.lastSWRReceivedAt = time.Now()
|
||||
statusLog.reportSWR(float64(BCDToSWR(data)))
|
||||
statusLog.reportSWR(BCDToSWR(data))
|
||||
if s.state.getSWR.pending {
|
||||
s.removePendingCmd(&s.state.getSWR)
|
||||
return false
|
||||
|
|
@ -809,7 +809,7 @@ func (s *civControlStruct) decodeVdSWRS(d []byte) bool {
|
|||
if len(d) < 3 {
|
||||
return !s.state.getVd.pending
|
||||
}
|
||||
statusLog.reportVd(float64(BCDToVd(data)))
|
||||
statusLog.reportVd(BCDToVd(data))
|
||||
if s.state.getVd.pending {
|
||||
s.removePendingCmd(&s.state.getVd)
|
||||
return false
|
||||
|
|
@ -1025,7 +1025,7 @@ func (s *civControlStruct) sendCmd(cmd *civCmd) error {
|
|||
}
|
||||
}
|
||||
|
||||
// noww actually send it to the serial stream
|
||||
// now actually send it to the serial stream
|
||||
return s.st.send(cmd.cmd)
|
||||
}
|
||||
|
||||
|
|
@ -1034,47 +1034,84 @@ func prepPacket(command string, data []byte) (pkt []byte) {
|
|||
pkt = append(pkt, CIV[command].cmdSeq...)
|
||||
pkt = append(pkt, data...)
|
||||
pkt = append(pkt, []byte{0xfd}...)
|
||||
|
||||
if debugPackets {
|
||||
debugPacket(command, pkt)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// encode to BCD using double dabble algorithm
|
||||
func encodeForSend(decimal int) (bcd []byte) {
|
||||
|
||||
v := uint32(decimal)
|
||||
v <<= 3
|
||||
for shifts := 3; shifts < 8; shifts++ {
|
||||
// when ONEs or TENs places are 5 or more, add 3 to that place prior to the shift left
|
||||
if v&0x00f00 > 0x00400 {
|
||||
v += 0x00300
|
||||
}
|
||||
// is TENs place >= 5, if so add 3 to it and shift left one bit
|
||||
if v&0x0f000 > 0x04000 {
|
||||
v += 0x03000
|
||||
}
|
||||
v <<= 1
|
||||
}
|
||||
|
||||
hundreds := (v & 0xf0000) >> 16
|
||||
tens := (v & 0x0f000) >> 12
|
||||
ones := (v & 0x00f00) >> 8
|
||||
lo := ((tens << 3) + (tens << 1)) + ones
|
||||
bcd = append(bcd, byte(hundreds))
|
||||
bcd = append(bcd, byte(lo))
|
||||
return
|
||||
}
|
||||
|
||||
func BCDToDec(bcd []byte) int {
|
||||
return int(bcd[0]*100 + bcd[1])
|
||||
}
|
||||
|
||||
/*
|
||||
func pctAsBCD(pct int) (BCD []byte) {
|
||||
v := uint16(0x0255 * (float64(pct) / 100))
|
||||
return []byte{byte(v >> 8), byte(v & 0xff)}
|
||||
scaled := uint16(255 * (float64(pct) / 100))
|
||||
return encodeForSend(scaled)
|
||||
}
|
||||
|
||||
func BCDAsPct(bcd []byte) (pct int) {
|
||||
hex := uint16(bcd[0])<<8 | uint16(bcd[1])
|
||||
pct = int(math.Round((float64(hex) / 0x0255) * 100))
|
||||
pct = int(100 * float64(BCDToDec(bcd)) / 0xff)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
func BCDToSLevel(bcd []byte) (sLevel int) {
|
||||
sLevel = (int(math.Round(((float64(int(bcd[0])<<8) + float64(bcd[1])) / 0x0241) * 18)))
|
||||
// BCD to S-level
|
||||
// 0000 => S0
|
||||
// 0120 => S9
|
||||
// 0241 => S9 + 60dB
|
||||
// we want 17 S-levels, 10 for S0-9, plus 7 for each +10dB, number them 1 - 18
|
||||
fullScale := float64(241) // FWIW, 241 = b11110001
|
||||
sLevel = int(float64(BCDToDec(bcd))/fullScale) * 18
|
||||
return
|
||||
}
|
||||
|
||||
func BCDToSWR(bcd []byte) (SWR int) {
|
||||
// BCD to SWR
|
||||
func BCDToSWR(bcd []byte) (SWR float64) {
|
||||
// BCD to SWR - note that this isn't linear
|
||||
// 0000 => 1.0
|
||||
// 0048 => 1.5
|
||||
// 0080 => 2.0
|
||||
// 0120 => 3.0
|
||||
SWR = 1 + (BCDAsPct(bcd)/0x0120)*2
|
||||
fullScale := float64(120) // FWIW, 120 = b01111000
|
||||
SWR = 1 + (float64(BCDToDec(bcd))/fullScale)*2
|
||||
return
|
||||
}
|
||||
|
||||
func BCDToVd(bcd []byte) (Vd int) {
|
||||
func BCDToVd(bcd []byte) (Vd float64) {
|
||||
// BCD to Vd
|
||||
// 0000 => 0v
|
||||
// 0075 => 5v
|
||||
// 0241 => 16v
|
||||
// IE - normalize full swing over 0x241, where full swing is 16 volts
|
||||
Vd = (BCDAsPct(bcd) / 0x241) * 16
|
||||
// IE - normalize full swing over 0-241, where full swing is 16 volts
|
||||
fullScale := float64(241) // FWIW, 241 = b11110001
|
||||
Vd = (float64(BCDToDec(bcd)) / fullScale) * 16
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1088,7 +1125,6 @@ func (s *civControlStruct) getDigit(v uint, decade int) byte {
|
|||
return byte(uint(asDecimal) % 10)
|
||||
}
|
||||
|
||||
// NOTE: mabye call this BCDtoDecimal? (esp. since the incoming BCD size isn't limited)
|
||||
func (s *civControlStruct) decodeFreqData(d []byte) (f uint) {
|
||||
var pos int
|
||||
for _, v := range d {
|
||||
|
|
@ -1102,83 +1138,83 @@ func (s *civControlStruct) decodeFreqData(d []byte) (f uint) {
|
|||
return
|
||||
}
|
||||
|
||||
func (s *civControlStruct) setPwr(percent int) error {
|
||||
s.initCmd(&s.state.setPwr, "setPwr", prepPacket("setPwr", pctAsBCD(percent)))
|
||||
func (s *civControlStruct) setPwr(level int) error {
|
||||
s.initCmd(&s.state.setPwr, "setPwr", prepPacket("setPwr", encodeForSend(level)))
|
||||
return s.sendCmd(&s.state.setPwr)
|
||||
}
|
||||
|
||||
func (s *civControlStruct) incPwr() error {
|
||||
if s.state.pwrPercent < 100 {
|
||||
return s.setPwr(s.state.pwrPercent + 1)
|
||||
if s.state.pwrLevel < 255 {
|
||||
return s.setPwr(s.state.pwrLevel + 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *civControlStruct) decPwr() error {
|
||||
if s.state.pwrPercent > 0 {
|
||||
return s.setPwr(s.state.pwrPercent - 1)
|
||||
if s.state.pwrLevel > 0 {
|
||||
return s.setPwr(s.state.pwrLevel - 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *civControlStruct) setRFGain(percent int) error {
|
||||
s.initCmd(&s.state.setRFGain, "setRFGain", prepPacket("setRFGain", pctAsBCD(percent)))
|
||||
func (s *civControlStruct) setRFGain(level int) error {
|
||||
s.initCmd(&s.state.setRFGain, "setRFGain", prepPacket("setRFGain", encodeForSend(level)))
|
||||
return s.sendCmd(&s.state.setRFGain)
|
||||
}
|
||||
|
||||
func (s *civControlStruct) incRFGain() error {
|
||||
if s.state.rfGainPercent < 100 {
|
||||
return s.setRFGain(s.state.rfGainPercent + 1)
|
||||
if s.state.rfGainLevel < 255 {
|
||||
return s.setRFGain(s.state.rfGainLevel + 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *civControlStruct) decRFGain() error {
|
||||
if s.state.rfGainPercent > 0 {
|
||||
return s.setRFGain(s.state.rfGainPercent - 1)
|
||||
if s.state.rfGainLevel > 0 {
|
||||
return s.setRFGain(s.state.rfGainLevel - 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *civControlStruct) setSQL(percent int) error {
|
||||
s.initCmd(&s.state.setSQL, "setSQL", prepPacket("setSQL", pctAsBCD(percent)))
|
||||
func (s *civControlStruct) setSQL(level int) error {
|
||||
s.initCmd(&s.state.setSQL, "setSQL", prepPacket("setSQL", encodeForSend(level)))
|
||||
return s.sendCmd(&s.state.setSQL)
|
||||
}
|
||||
|
||||
func (s *civControlStruct) incSQL() error {
|
||||
if s.state.sqlPercent < 100 {
|
||||
return s.setSQL(s.state.sqlPercent + 1)
|
||||
if s.state.sqlLevel < 255 {
|
||||
return s.setSQL(s.state.sqlLevel + 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *civControlStruct) decSQL() error {
|
||||
if s.state.sqlPercent > 0 {
|
||||
return s.setSQL(s.state.sqlPercent - 1)
|
||||
if s.state.sqlLevel > 0 {
|
||||
return s.setSQL(s.state.sqlLevel - 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *civControlStruct) setNR(percent int) error {
|
||||
func (s *civControlStruct) setNR(level int) error {
|
||||
if !s.state.nrEnabled {
|
||||
if err := s.toggleNR(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.initCmd(&s.state.setNR, "setNR", prepPacket("setSNR", pctAsBCD(percent)))
|
||||
s.initCmd(&s.state.setNR, "setNR", prepPacket("setSNR", encodeForSend(level)))
|
||||
return s.sendCmd(&s.state.setNR)
|
||||
}
|
||||
|
||||
func (s *civControlStruct) incNR() error {
|
||||
if s.state.nrPercent < 100 {
|
||||
return s.setNR(s.state.nrPercent + 1)
|
||||
if s.state.nrLevel < 255 {
|
||||
return s.setNR(s.state.nrLevel + 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *civControlStruct) decNR() error {
|
||||
if s.state.nrPercent > 0 {
|
||||
return s.setNR(s.state.nrPercent - 1)
|
||||
if s.state.nrLevel > 0 {
|
||||
return s.setNR(s.state.nrLevel - 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1693,7 +1729,7 @@ func debugPacket(command string, pkt []byte) {
|
|||
cmd := pkt[4]
|
||||
pld := pkt[5 : len(pkt)-1]
|
||||
|
||||
msg := fmt.Sprintf("'%v' [%x] ", command, pkt)
|
||||
msg := fmt.Sprintf("'%v' [% x] ", command, pkt)
|
||||
msg += "to "
|
||||
|
||||
if to == civAddress {
|
||||
|
|
|
|||
|
|
@ -201,5 +201,7 @@ func handleHotkey(k byte) {
|
|||
}
|
||||
case 'q':
|
||||
quitChan <- true
|
||||
default:
|
||||
log.Error(fmt.Sprintf("INFO: no action mapped to key [%v]\n", string(k)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
76
statuslog.go
76
statuslog.go
|
|
@ -107,6 +107,7 @@ var downArrow = "\u21d3"
|
|||
// var roundTripArrow = "\u2b6f\u200a" // widdershin circle w/arrow
|
||||
var roundTripArrow = "\u2b8c\u200a" // out and back arrow
|
||||
|
||||
// generate display string for round trip time latency
|
||||
func (s *statusLogStruct) reportRTTLatency(l time.Duration) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -117,6 +118,7 @@ func (s *statusLogStruct) reportRTTLatency(l time.Duration) {
|
|||
s.data.rttStr = fmt.Sprint(l.Milliseconds())
|
||||
}
|
||||
|
||||
// update string that displays current audio status
|
||||
func (s *statusLogStruct) updateAudioStateStr() {
|
||||
if s.data.audioRecOn {
|
||||
s.data.audioStateStr = s.preGenerated.audioStateStr.rec
|
||||
|
|
@ -127,6 +129,7 @@ func (s *statusLogStruct) updateAudioStateStr() {
|
|||
}
|
||||
}
|
||||
|
||||
// set audio monitoring status to off/on and make call to update string to display
|
||||
func (s *statusLogStruct) reportAudioMon(enabled bool) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -138,6 +141,7 @@ func (s *statusLogStruct) reportAudioMon(enabled bool) {
|
|||
s.updateAudioStateStr()
|
||||
}
|
||||
|
||||
// set audio recording status to off/on and make call to update string to display
|
||||
func (s *statusLogStruct) reportAudioRec(enabled bool) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -149,6 +153,7 @@ func (s *statusLogStruct) reportAudioRec(enabled bool) {
|
|||
s.updateAudioStateStr()
|
||||
}
|
||||
|
||||
// update main VFO frequency value held in status log data structure
|
||||
func (s *statusLogStruct) reportFrequency(f uint) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -159,6 +164,7 @@ func (s *statusLogStruct) reportFrequency(f uint) {
|
|||
s.data.frequency = f
|
||||
}
|
||||
|
||||
// update sub-VFO frequency value held status log data structure
|
||||
func (s *statusLogStruct) reportSubFrequency(f uint) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -169,6 +175,7 @@ func (s *statusLogStruct) reportSubFrequency(f uint) {
|
|||
s.data.subFrequency = f
|
||||
}
|
||||
|
||||
// update main VFO mode & predefined filter data held instatus log data structure
|
||||
func (s *statusLogStruct) reportMode(mode string, dataMode bool, filter string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -185,6 +192,7 @@ func (s *statusLogStruct) reportMode(mode string, dataMode bool, filter string)
|
|||
s.data.filter = filter
|
||||
}
|
||||
|
||||
// update sub-VFO mode & predefined filter data held instatus log data structure
|
||||
func (s *statusLogStruct) reportSubMode(mode string, dataMode bool, filter string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -201,6 +209,7 @@ func (s *statusLogStruct) reportSubMode(mode string, dataMode bool, filter strin
|
|||
s.data.subFilter = filter
|
||||
}
|
||||
|
||||
// generate display string for preamp status
|
||||
func (s *statusLogStruct) reportPreamp(preamp int) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -211,6 +220,7 @@ func (s *statusLogStruct) reportPreamp(preamp int) {
|
|||
s.data.preamp = fmt.Sprint("PAMP", preamp)
|
||||
}
|
||||
|
||||
// generate display string for AGC status
|
||||
func (s *statusLogStruct) reportAGC(agc string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -221,6 +231,7 @@ func (s *statusLogStruct) reportAGC(agc string) {
|
|||
s.data.agc = "AGC" + agc
|
||||
}
|
||||
|
||||
// set noise reduction status to off/on in status log data structure
|
||||
func (s *statusLogStruct) reportNREnabled(enabled bool) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -231,6 +242,7 @@ func (s *statusLogStruct) reportNREnabled(enabled bool) {
|
|||
s.data.nrEnabled = enabled
|
||||
}
|
||||
|
||||
// generate display string for (battery) voltage
|
||||
func (s *statusLogStruct) reportVd(voltage float64) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -241,6 +253,7 @@ func (s *statusLogStruct) reportVd(voltage float64) {
|
|||
s.data.vd = fmt.Sprintf("%.1fV", voltage)
|
||||
}
|
||||
|
||||
// set S-level value in status log data structure
|
||||
func (s *statusLogStruct) reportS(sValue string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -251,6 +264,7 @@ func (s *statusLogStruct) reportS(sValue string) {
|
|||
s.data.s = sValue
|
||||
}
|
||||
|
||||
// set over-volt fault true/fault status in status log data structure
|
||||
func (s *statusLogStruct) reportOVF(ovf bool) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -261,6 +275,7 @@ func (s *statusLogStruct) reportOVF(ovf bool) {
|
|||
s.data.ovf = ovf
|
||||
}
|
||||
|
||||
// generate display string for SWR status
|
||||
func (s *statusLogStruct) reportSWR(swr float64) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -271,6 +286,7 @@ func (s *statusLogStruct) reportSWR(swr float64) {
|
|||
s.data.swr = fmt.Sprintf("%.1f", swr)
|
||||
}
|
||||
|
||||
// generate display string for tuning step value
|
||||
func (s *statusLogStruct) reportTuningStep(ts uint) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -292,6 +308,7 @@ func (s *statusLogStruct) reportTuningStep(ts uint) {
|
|||
}
|
||||
}
|
||||
|
||||
// set push-to-talk (aka Tx) status in status log data structure
|
||||
func (s *statusLogStruct) reportPTT(ptt, tune bool) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -303,46 +320,57 @@ func (s *statusLogStruct) reportPTT(ptt, tune bool) {
|
|||
s.data.ptt = ptt
|
||||
}
|
||||
|
||||
func (s *statusLogStruct) reportTxPower(percent int) {
|
||||
// convert int value 0 - 255 to a floating point percentage
|
||||
func asPercentage(level int) (pct float64) {
|
||||
pct = 100.00 * (float64(level) / 0xff)
|
||||
return
|
||||
}
|
||||
|
||||
// generate the display string for transmit power value
|
||||
func (s *statusLogStruct) reportTxPower(level int) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
return
|
||||
}
|
||||
s.data.txPower = fmt.Sprint(percent, "%")
|
||||
s.data.txPower = fmt.Sprintf("%3.1f%%", asPercentage(level))
|
||||
}
|
||||
|
||||
func (s *statusLogStruct) reportRFGain(percent int) {
|
||||
// generate the display string for RF Gain value
|
||||
func (s *statusLogStruct) reportRFGain(level int) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
return
|
||||
}
|
||||
s.data.rfGain = fmt.Sprint(percent, "%")
|
||||
s.data.rfGain = fmt.Sprintf("%3.1f%%", asPercentage(level))
|
||||
}
|
||||
|
||||
func (s *statusLogStruct) reportSQL(percent int) {
|
||||
// generate the display string for squelch value
|
||||
func (s *statusLogStruct) reportSQL(level int) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
return
|
||||
}
|
||||
s.data.sql = fmt.Sprint(percent, "%")
|
||||
s.data.sql = fmt.Sprintf("%3.1f%%", asPercentage(level))
|
||||
}
|
||||
|
||||
func (s *statusLogStruct) reportNR(percent int) {
|
||||
// generate the display string for noise reduction level
|
||||
func (s *statusLogStruct) reportNR(level int) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
return
|
||||
}
|
||||
s.data.nr = fmt.Sprint(percent, "%")
|
||||
s.data.nr = fmt.Sprintf("%3.1f%%", asPercentage(level))
|
||||
}
|
||||
|
||||
// generate the display string for split frequency operating mode
|
||||
func (s *statusLogStruct) reportSplit(mode splitMode, split string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -358,10 +386,16 @@ func (s *statusLogStruct) reportSplit(mode splitMode, split string) {
|
|||
}
|
||||
}
|
||||
|
||||
// clears the entire line the cursor is located on
|
||||
func (s *statusLogStruct) clearStatusLine() {
|
||||
fmt.Print(termDetail.eraseLine)
|
||||
}
|
||||
|
||||
// displays the current status information
|
||||
//
|
||||
// (NOTE: s.isRealtimeInternal merely returns true/false for if in terminal, this should be cleaned up for clarity)
|
||||
// if running in a terminal, print the current status to the console and reposition the cursor to the first line of output)
|
||||
// if not, send just the last line (packet rate info) to the debugging log
|
||||
func (s *statusLogStruct) print() {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -378,6 +412,7 @@ func (s *statusLogStruct) print() {
|
|||
}
|
||||
}
|
||||
|
||||
// use whitespace padding on left to right-justify the string
|
||||
func (s *statusLogStruct) padLeft(str string, length int) string {
|
||||
if !s.isRealtimeInternal() {
|
||||
return str
|
||||
|
|
@ -388,6 +423,7 @@ func (s *statusLogStruct) padLeft(str string, length int) string {
|
|||
return str
|
||||
}
|
||||
|
||||
// use whitespace paddind on the right-hand side of the string for consisting formatting
|
||||
func (s *statusLogStruct) padRight(str string, length int) string {
|
||||
if !s.isRealtimeInternal() {
|
||||
return str
|
||||
|
|
@ -398,6 +434,7 @@ func (s *statusLogStruct) padRight(str string, length int) string {
|
|||
return str
|
||||
}
|
||||
|
||||
// update variables used for status output using current values to regenerate the strings to display
|
||||
func (s *statusLogStruct) update() {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -433,7 +470,7 @@ func (s *statusLogStruct) update() {
|
|||
if s.data.nr != "" {
|
||||
nrStr = " NR"
|
||||
if s.data.nrEnabled {
|
||||
nrStr += s.data.nr
|
||||
nrStr += " " + s.data.nr
|
||||
} else {
|
||||
nrStr += "-"
|
||||
}
|
||||
|
|
@ -522,6 +559,10 @@ func (s *statusLogStruct) update() {
|
|||
}
|
||||
}
|
||||
|
||||
// status logging loop
|
||||
//
|
||||
// listen to ticker channel for data which indicates an recalculate and display status should be done
|
||||
// listen to stop channel for indication to terminate logging
|
||||
func (s *statusLogStruct) loop() {
|
||||
for {
|
||||
select {
|
||||
|
|
@ -535,22 +576,26 @@ func (s *statusLogStruct) loop() {
|
|||
}
|
||||
}
|
||||
|
||||
// poorly named, this actually indicates if we're running in an interactive terminal or not
|
||||
func (s *statusLogStruct) isRealtimeInternal() bool {
|
||||
return keyboard.initialized
|
||||
}
|
||||
|
||||
// check if the ticker timer is being used, and if running in an interactive terminal
|
||||
func (s *statusLogStruct) isRealtime() bool {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
return s.ticker != nil && s.isRealtimeInternal()
|
||||
}
|
||||
|
||||
// check if the ticker timer is active
|
||||
func (s *statusLogStruct) isActive() bool {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
return s.ticker != nil
|
||||
}
|
||||
|
||||
// set initial values, start ticker timer, and start main loop in a goroutine
|
||||
func (s *statusLogStruct) startPeriodicPrint() {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
|
@ -570,7 +615,7 @@ func (s *statusLogStruct) startPeriodicPrint() {
|
|||
go s.loop()
|
||||
}
|
||||
|
||||
// stop the update timer and clear the status rows... but not any error/info that may have been printed
|
||||
// stop the ticker timer, send flag on stop channel, wait for stop finished channel data, then clear the status lines on the terminal
|
||||
func (s *statusLogStruct) stopPeriodicPrint() {
|
||||
if !s.isActive() {
|
||||
return
|
||||
|
|
@ -590,6 +635,10 @@ func (s *statusLogStruct) stopPeriodicPrint() {
|
|||
}
|
||||
}
|
||||
|
||||
// initialization tasks
|
||||
//
|
||||
// initialize keyboard/set log timer depending on if running in terminal or not
|
||||
// update display related variables in status log structure based on terminal characteristics
|
||||
func (s *statusLogStruct) initIfNeeded() {
|
||||
if s.data != nil { // Already initialized?
|
||||
return
|
||||
|
|
@ -605,11 +654,16 @@ func (s *statusLogStruct) initIfNeeded() {
|
|||
if err == nil {
|
||||
termDetail.cols = cols
|
||||
termDetail.rows = rows
|
||||
} else {
|
||||
// if redirecting to a file these are zeros, and that's a problem
|
||||
// yes, needs actual error check too
|
||||
termDetail.cols = 120
|
||||
termDetail.rows = 20
|
||||
}
|
||||
|
||||
// consider doing this with a nice looking start up screen too
|
||||
// what'd be kinda useful would be a nice map of the hotkeys
|
||||
vertWhitespace := strings.Repeat(termDetail.cursorDown, rows-10)
|
||||
vertWhitespace := strings.Repeat(termDetail.cursorDown, termDetail.rows-10)
|
||||
fmt.Printf("%v%v", termDetail.eraseScreen, vertWhitespace)
|
||||
|
||||
c := color.New(color.FgHiWhite)
|
||||
|
|
|
|||
Loading…
Reference in a new issue