Added channel stat reporting and airtime controls to RNode interface

This commit is contained in:
Mark Qvist 2023-09-13 21:15:32 +02:00
parent 67c468884f
commit 13c45cc59a
2 changed files with 198 additions and 5 deletions

View File

@ -43,21 +43,21 @@ class KISS():
CMD_CR = 0x05
CMD_RADIO_STATE = 0x06
CMD_RADIO_LOCK = 0x07
CMD_ST_ALOCK = 0x0B
CMD_LT_ALOCK = 0x0C
CMD_DETECT = 0x08
CMD_IMPLICIT = 0x09
CMD_LEAVE = 0x0A
CMD_READY = 0x0F
CMD_STAT_RX = 0x21
CMD_STAT_TX = 0x22
CMD_STAT_RSSI = 0x23
CMD_STAT_SNR = 0x24
CMD_STAT_CHTM = 0x25
CMD_BLINK = 0x30
CMD_RANDOM = 0x40
CMD_FB_EXT = 0x41
CMD_FB_READ = 0x42
CMD_FB_WRITE = 0x43
CMD_FB_READL = 0x44
CMD_BT_CTRL = 0x46
CMD_PLATFORM = 0x48
CMD_MCU = 0x49
CMD_FW_VERSION = 0x50
@ -315,7 +315,7 @@ class RNodeInterface(Interface):
self, owner, name, port, frequency = None, bandwidth = None, txpower = None,
sf = None, cr = None, flow_control = False, id_interval = None,
allow_bluetooth = False, target_device_name = None,
target_device_address = None, id_callsign = None):
target_device_address = None, id_callsign = None, st_alock = None, lt_alock = None):
import importlib
if RNS.vendor.platformutils.is_android():
self.on_android = True
@ -373,6 +373,8 @@ class RNodeInterface(Interface):
self.cr = cr
self.state = KISS.RADIO_STATE_OFF
self.bitrate = 0
self.st_alock = st_alock
self.lt_alock = lt_alock
self.platform = None
self.display = None
self.mcu = None
@ -396,7 +398,13 @@ class RNodeInterface(Interface):
self.r_stat_tx = None
self.r_stat_rssi = None
self.r_stat_snr = None
self.r_st_alock = None
self.r_lt_alock = None
self.r_random = None
self.r_airtime_short = 0.0
self.r_airtime_long = 0.0
self.r_channel_load_short = 0.0
self.r_channel_load_long = 0.0
self.packet_queue = []
self.flow_control = flow_control
@ -427,6 +435,14 @@ class RNodeInterface(Interface):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if id_interval != None and id_callsign != None:
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
self.should_id = True
@ -616,6 +632,12 @@ class RNodeInterface(Interface):
self.setCodingRate()
time.sleep(0.15)
self.setSTALock()
time.sleep(0.15)
self.setLTALock()
time.sleep(0.15)
self.setRadioState(KISS.RADIO_STATE_ON)
time.sleep(0.15)
@ -741,6 +763,30 @@ class RNodeInterface(Interface):
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
def setSTALock(self):
if self.st_alock != None:
at = int(self.st_alock*100)
c1 = at >> 8 & 0xFF
c2 = at & 0xFF
data = KISS.escape(bytes([c1])+bytes([c2]))
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
def setLTALock(self):
if self.lt_alock != None:
at = int(self.lt_alock*100)
c1 = at >> 8 & 0xFF
c2 = at & 0xFF
data = KISS.escape(bytes([c1])+bytes([c2]))
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
def setRadioState(self, state):
self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
@ -994,6 +1040,57 @@ class RNodeInterface(Interface):
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
elif (command == KISS.CMD_STAT_SNR):
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
elif (command == KISS.CMD_ST_ALOCK):
if (byte == KISS.FESC):
escape = True
else:
if (escape):
if (byte == KISS.TFEND):
byte = KISS.FEND
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC):
escape = True
else:
if (escape):
if (byte == KISS.TFEND):
byte = KISS.FEND
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC):
escape = True
else:
if (escape):
if (byte == KISS.TFEND):
byte = KISS.FEND
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 8):
ats = command_buffer[0] << 8 | command_buffer[1]
atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0
self.r_channel_load_long = cul/100.0
elif (command == KISS.CMD_RANDOM):
self.r_random = byte
elif (command == KISS.CMD_PLATFORM):

View File

@ -43,6 +43,8 @@ class KISS():
CMD_CR = 0x05
CMD_RADIO_STATE = 0x06
CMD_RADIO_LOCK = 0x07
CMD_ST_ALOCK = 0x0B
CMD_LT_ALOCK = 0x0C
CMD_DETECT = 0x08
CMD_LEAVE = 0x0A
CMD_READY = 0x0F
@ -50,6 +52,7 @@ class KISS():
CMD_STAT_TX = 0x22
CMD_STAT_RSSI = 0x23
CMD_STAT_SNR = 0x24
CMD_STAT_CHTM = 0x25
CMD_BLINK = 0x30
CMD_RANDOM = 0x40
CMD_FB_EXT = 0x41
@ -98,7 +101,7 @@ class RNodeInterface(Interface):
RECONNECT_WAIT = 5
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None):
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None, st_alock = None, lt_alock = None):
if RNS.vendor.platformutils.is_android():
raise SystemError("Invlaid interface type. The Android-specific RNode interface must be used on Android")
@ -135,6 +138,8 @@ class RNodeInterface(Interface):
self.cr = cr
self.state = KISS.RADIO_STATE_OFF
self.bitrate = 0
self.st_alock = st_alock
self.lt_alock = lt_alock
self.platform = None
self.display = None
self.mcu = None
@ -158,7 +163,13 @@ class RNodeInterface(Interface):
self.r_stat_tx = None
self.r_stat_rssi = None
self.r_stat_snr = None
self.r_st_alock = None
self.r_lt_alock = None
self.r_random = None
self.r_airtime_short = 0.0
self.r_airtime_long = 0.0
self.r_channel_load_short = 0.0
self.r_channel_load_long = 0.0
self.packet_queue = []
self.flow_control = flow_control
@ -186,6 +197,14 @@ class RNodeInterface(Interface):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if id_interval != None and id_callsign != None:
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
self.should_id = True
@ -281,6 +300,8 @@ class RNodeInterface(Interface):
self.setTXPower()
self.setSpreadingFactor()
self.setCodingRate()
self.setSTALock()
self.setLTALock()
self.setRadioState(KISS.RADIO_STATE_ON)
def detect(self):
@ -385,6 +406,30 @@ class RNodeInterface(Interface):
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
def setSTALock(self):
if self.st_alock != None:
at = int(self.st_alock*100)
c1 = at >> 8 & 0xFF
c2 = at & 0xFF
data = KISS.escape(bytes([c1])+bytes([c2]))
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
def setLTALock(self):
if self.lt_alock != None:
at = int(self.lt_alock*100)
c1 = at >> 8 & 0xFF
c2 = at & 0xFF
data = KISS.escape(bytes([c1])+bytes([c2]))
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
def setRadioState(self, state):
self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
@ -622,6 +667,57 @@ class RNodeInterface(Interface):
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
elif (command == KISS.CMD_STAT_SNR):
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
elif (command == KISS.CMD_ST_ALOCK):
if (byte == KISS.FESC):
escape = True
else:
if (escape):
if (byte == KISS.TFEND):
byte = KISS.FEND
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC):
escape = True
else:
if (escape):
if (byte == KISS.TFEND):
byte = KISS.FEND
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC):
escape = True
else:
if (escape):
if (byte == KISS.TFEND):
byte = KISS.FEND
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 8):
ats = command_buffer[0] << 8 | command_buffer[1]
atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0
self.r_channel_load_long = cul/100.0
elif (command == KISS.CMD_RANDOM):
self.r_random = byte
elif (command == KISS.CMD_PLATFORM):