Skip to content

Commit 13c45cc

Browse files
committed
Added channel stat reporting and airtime controls to RNode interface
1 parent 67c4688 commit 13c45cc

File tree

2 files changed

+198
-5
lines changed

2 files changed

+198
-5
lines changed

RNS/Interfaces/Android/RNodeInterface.py

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@ class KISS():
4343
CMD_CR = 0x05
4444
CMD_RADIO_STATE = 0x06
4545
CMD_RADIO_LOCK = 0x07
46+
CMD_ST_ALOCK = 0x0B
47+
CMD_LT_ALOCK = 0x0C
4648
CMD_DETECT = 0x08
47-
CMD_IMPLICIT = 0x09
4849
CMD_LEAVE = 0x0A
4950
CMD_READY = 0x0F
5051
CMD_STAT_RX = 0x21
5152
CMD_STAT_TX = 0x22
5253
CMD_STAT_RSSI = 0x23
5354
CMD_STAT_SNR = 0x24
55+
CMD_STAT_CHTM = 0x25
5456
CMD_BLINK = 0x30
5557
CMD_RANDOM = 0x40
5658
CMD_FB_EXT = 0x41
5759
CMD_FB_READ = 0x42
5860
CMD_FB_WRITE = 0x43
59-
CMD_FB_READL = 0x44
60-
CMD_BT_CTRL = 0x46
6161
CMD_PLATFORM = 0x48
6262
CMD_MCU = 0x49
6363
CMD_FW_VERSION = 0x50
@@ -315,7 +315,7 @@ def __init__(
315315
self, owner, name, port, frequency = None, bandwidth = None, txpower = None,
316316
sf = None, cr = None, flow_control = False, id_interval = None,
317317
allow_bluetooth = False, target_device_name = None,
318-
target_device_address = None, id_callsign = None):
318+
target_device_address = None, id_callsign = None, st_alock = None, lt_alock = None):
319319
import importlib
320320
if RNS.vendor.platformutils.is_android():
321321
self.on_android = True
@@ -373,6 +373,8 @@ def __init__(
373373
self.cr = cr
374374
self.state = KISS.RADIO_STATE_OFF
375375
self.bitrate = 0
376+
self.st_alock = st_alock
377+
self.lt_alock = lt_alock
376378
self.platform = None
377379
self.display = None
378380
self.mcu = None
@@ -396,7 +398,13 @@ def __init__(
396398
self.r_stat_tx = None
397399
self.r_stat_rssi = None
398400
self.r_stat_snr = None
401+
self.r_st_alock = None
402+
self.r_lt_alock = None
399403
self.r_random = None
404+
self.r_airtime_short = 0.0
405+
self.r_airtime_long = 0.0
406+
self.r_channel_load_short = 0.0
407+
self.r_channel_load_long = 0.0
400408

401409
self.packet_queue = []
402410
self.flow_control = flow_control
@@ -427,6 +435,14 @@ def __init__(
427435
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
428436
self.validcfg = False
429437

438+
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
439+
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
440+
self.validcfg = False
441+
442+
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
443+
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
444+
self.validcfg = False
445+
430446
if id_interval != None and id_callsign != None:
431447
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
432448
self.should_id = True
@@ -616,6 +632,12 @@ def initRadio(self):
616632

617633
self.setCodingRate()
618634
time.sleep(0.15)
635+
636+
self.setSTALock()
637+
time.sleep(0.15)
638+
639+
self.setLTALock()
640+
time.sleep(0.15)
619641

620642
self.setRadioState(KISS.RADIO_STATE_ON)
621643
time.sleep(0.15)
@@ -741,6 +763,30 @@ def setCodingRate(self):
741763
if written != len(kiss_command):
742764
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
743765

766+
def setSTALock(self):
767+
if self.st_alock != None:
768+
at = int(self.st_alock*100)
769+
c1 = at >> 8 & 0xFF
770+
c2 = at & 0xFF
771+
data = KISS.escape(bytes([c1])+bytes([c2]))
772+
773+
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
774+
written = self.write_mux(kiss_command)
775+
if written != len(kiss_command):
776+
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
777+
778+
def setLTALock(self):
779+
if self.lt_alock != None:
780+
at = int(self.lt_alock*100)
781+
c1 = at >> 8 & 0xFF
782+
c2 = at & 0xFF
783+
data = KISS.escape(bytes([c1])+bytes([c2]))
784+
785+
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
786+
written = self.write_mux(kiss_command)
787+
if written != len(kiss_command):
788+
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
789+
744790
def setRadioState(self, state):
745791
self.state = state
746792
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
@@ -994,6 +1040,57 @@ def readLoop(self):
9941040
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
9951041
elif (command == KISS.CMD_STAT_SNR):
9961042
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
1043+
elif (command == KISS.CMD_ST_ALOCK):
1044+
if (byte == KISS.FESC):
1045+
escape = True
1046+
else:
1047+
if (escape):
1048+
if (byte == KISS.TFEND):
1049+
byte = KISS.FEND
1050+
if (byte == KISS.TFESC):
1051+
byte = KISS.FESC
1052+
escape = False
1053+
command_buffer = command_buffer+bytes([byte])
1054+
if (len(command_buffer) == 2):
1055+
at = command_buffer[0] << 8 | command_buffer[1]
1056+
self.r_st_alock = at/100.0
1057+
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
1058+
elif (command == KISS.CMD_LT_ALOCK):
1059+
if (byte == KISS.FESC):
1060+
escape = True
1061+
else:
1062+
if (escape):
1063+
if (byte == KISS.TFEND):
1064+
byte = KISS.FEND
1065+
if (byte == KISS.TFESC):
1066+
byte = KISS.FESC
1067+
escape = False
1068+
command_buffer = command_buffer+bytes([byte])
1069+
if (len(command_buffer) == 2):
1070+
at = command_buffer[0] << 8 | command_buffer[1]
1071+
self.r_lt_alock = at/100.0
1072+
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
1073+
elif (command == KISS.CMD_STAT_CHTM):
1074+
if (byte == KISS.FESC):
1075+
escape = True
1076+
else:
1077+
if (escape):
1078+
if (byte == KISS.TFEND):
1079+
byte = KISS.FEND
1080+
if (byte == KISS.TFESC):
1081+
byte = KISS.FESC
1082+
escape = False
1083+
command_buffer = command_buffer+bytes([byte])
1084+
if (len(command_buffer) == 8):
1085+
ats = command_buffer[0] << 8 | command_buffer[1]
1086+
atl = command_buffer[2] << 8 | command_buffer[3]
1087+
cus = command_buffer[4] << 8 | command_buffer[5]
1088+
cul = command_buffer[6] << 8 | command_buffer[7]
1089+
1090+
self.r_airtime_short = ats/100.0
1091+
self.r_airtime_long = atl/100.0
1092+
self.r_channel_load_short = cus/100.0
1093+
self.r_channel_load_long = cul/100.0
9971094
elif (command == KISS.CMD_RANDOM):
9981095
self.r_random = byte
9991096
elif (command == KISS.CMD_PLATFORM):

RNS/Interfaces/RNodeInterface.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,16 @@ class KISS():
4343
CMD_CR = 0x05
4444
CMD_RADIO_STATE = 0x06
4545
CMD_RADIO_LOCK = 0x07
46+
CMD_ST_ALOCK = 0x0B
47+
CMD_LT_ALOCK = 0x0C
4648
CMD_DETECT = 0x08
4749
CMD_LEAVE = 0x0A
4850
CMD_READY = 0x0F
4951
CMD_STAT_RX = 0x21
5052
CMD_STAT_TX = 0x22
5153
CMD_STAT_RSSI = 0x23
5254
CMD_STAT_SNR = 0x24
55+
CMD_STAT_CHTM = 0x25
5356
CMD_BLINK = 0x30
5457
CMD_RANDOM = 0x40
5558
CMD_FB_EXT = 0x41
@@ -98,7 +101,7 @@ class RNodeInterface(Interface):
98101

99102
RECONNECT_WAIT = 5
100103

101-
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):
104+
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):
102105
if RNS.vendor.platformutils.is_android():
103106
raise SystemError("Invlaid interface type. The Android-specific RNode interface must be used on Android")
104107

@@ -135,6 +138,8 @@ def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpowe
135138
self.cr = cr
136139
self.state = KISS.RADIO_STATE_OFF
137140
self.bitrate = 0
141+
self.st_alock = st_alock
142+
self.lt_alock = lt_alock
138143
self.platform = None
139144
self.display = None
140145
self.mcu = None
@@ -158,7 +163,13 @@ def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpowe
158163
self.r_stat_tx = None
159164
self.r_stat_rssi = None
160165
self.r_stat_snr = None
166+
self.r_st_alock = None
167+
self.r_lt_alock = None
161168
self.r_random = None
169+
self.r_airtime_short = 0.0
170+
self.r_airtime_long = 0.0
171+
self.r_channel_load_short = 0.0
172+
self.r_channel_load_long = 0.0
162173

163174
self.packet_queue = []
164175
self.flow_control = flow_control
@@ -186,6 +197,14 @@ def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpowe
186197
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
187198
self.validcfg = False
188199

200+
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
201+
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
202+
self.validcfg = False
203+
204+
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
205+
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
206+
self.validcfg = False
207+
189208
if id_interval != None and id_callsign != None:
190209
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
191210
self.should_id = True
@@ -281,6 +300,8 @@ def initRadio(self):
281300
self.setTXPower()
282301
self.setSpreadingFactor()
283302
self.setCodingRate()
303+
self.setSTALock()
304+
self.setLTALock()
284305
self.setRadioState(KISS.RADIO_STATE_ON)
285306

286307
def detect(self):
@@ -385,6 +406,30 @@ def setCodingRate(self):
385406
if written != len(kiss_command):
386407
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
387408

409+
def setSTALock(self):
410+
if self.st_alock != None:
411+
at = int(self.st_alock*100)
412+
c1 = at >> 8 & 0xFF
413+
c2 = at & 0xFF
414+
data = KISS.escape(bytes([c1])+bytes([c2]))
415+
416+
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
417+
written = self.serial.write(kiss_command)
418+
if written != len(kiss_command):
419+
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
420+
421+
def setLTALock(self):
422+
if self.lt_alock != None:
423+
at = int(self.lt_alock*100)
424+
c1 = at >> 8 & 0xFF
425+
c2 = at & 0xFF
426+
data = KISS.escape(bytes([c1])+bytes([c2]))
427+
428+
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
429+
written = self.serial.write(kiss_command)
430+
if written != len(kiss_command):
431+
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
432+
388433
def setRadioState(self, state):
389434
self.state = state
390435
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
@@ -622,6 +667,57 @@ def readLoop(self):
622667
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
623668
elif (command == KISS.CMD_STAT_SNR):
624669
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
670+
elif (command == KISS.CMD_ST_ALOCK):
671+
if (byte == KISS.FESC):
672+
escape = True
673+
else:
674+
if (escape):
675+
if (byte == KISS.TFEND):
676+
byte = KISS.FEND
677+
if (byte == KISS.TFESC):
678+
byte = KISS.FESC
679+
escape = False
680+
command_buffer = command_buffer+bytes([byte])
681+
if (len(command_buffer) == 2):
682+
at = command_buffer[0] << 8 | command_buffer[1]
683+
self.r_st_alock = at/100.0
684+
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
685+
elif (command == KISS.CMD_LT_ALOCK):
686+
if (byte == KISS.FESC):
687+
escape = True
688+
else:
689+
if (escape):
690+
if (byte == KISS.TFEND):
691+
byte = KISS.FEND
692+
if (byte == KISS.TFESC):
693+
byte = KISS.FESC
694+
escape = False
695+
command_buffer = command_buffer+bytes([byte])
696+
if (len(command_buffer) == 2):
697+
at = command_buffer[0] << 8 | command_buffer[1]
698+
self.r_lt_alock = at/100.0
699+
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
700+
elif (command == KISS.CMD_STAT_CHTM):
701+
if (byte == KISS.FESC):
702+
escape = True
703+
else:
704+
if (escape):
705+
if (byte == KISS.TFEND):
706+
byte = KISS.FEND
707+
if (byte == KISS.TFESC):
708+
byte = KISS.FESC
709+
escape = False
710+
command_buffer = command_buffer+bytes([byte])
711+
if (len(command_buffer) == 8):
712+
ats = command_buffer[0] << 8 | command_buffer[1]
713+
atl = command_buffer[2] << 8 | command_buffer[3]
714+
cus = command_buffer[4] << 8 | command_buffer[5]
715+
cul = command_buffer[6] << 8 | command_buffer[7]
716+
717+
self.r_airtime_short = ats/100.0
718+
self.r_airtime_long = atl/100.0
719+
self.r_channel_load_short = cus/100.0
720+
self.r_channel_load_long = cul/100.0
625721
elif (command == KISS.CMD_RANDOM):
626722
self.r_random = byte
627723
elif (command == KISS.CMD_PLATFORM):

0 commit comments

Comments
 (0)