RNodeMultiInterface.py
1 # Reticulum License 2 # 3 # Copyright (c) 2016-2025 Mark Qvist 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # - The Software shall not be used in any kind of system which includes amongst 13 # its functions the ability to purposefully do harm to human beings. 14 # 15 # - The Software shall not be used, directly or indirectly, in the creation of 16 # an artificial intelligence, machine learning or language model training 17 # dataset, including but not limited to any use that contributes to the 18 # training or development of such a model or algorithm. 19 # 20 # - The above copyright notice and this permission notice shall be included in 21 # all copies or substantial portions of the Software. 22 # 23 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 # SOFTWARE. 30 31 from RNS.Interfaces.Interface import Interface 32 from time import sleep 33 import sys 34 import threading 35 import time 36 import math 37 import RNS 38 39 class KISS(): 40 FEND = 0xC0 41 FESC = 0xDB 42 TFEND = 0xDC 43 TFESC = 0xDD 44 45 CMD_DATA = 0x00 46 47 CMD_UNKNOWN = 0xFE 48 CMD_FREQUENCY = 0x01 49 CMD_BANDWIDTH = 0x02 50 CMD_TXPOWER = 0x03 51 CMD_SF = 0x04 52 CMD_CR = 0x05 53 CMD_RADIO_STATE = 0x06 54 CMD_RADIO_LOCK = 0x07 55 CMD_ST_ALOCK = 0x0B 56 CMD_LT_ALOCK = 0x0C 57 CMD_DETECT = 0x08 58 CMD_LEAVE = 0x0A 59 CMD_READY = 0x0F 60 CMD_STAT_RX = 0x21 61 CMD_STAT_TX = 0x22 62 CMD_STAT_RSSI = 0x23 63 CMD_STAT_SNR = 0x24 64 CMD_STAT_CHTM = 0x25 65 CMD_STAT_PHYPRM = 0x26 66 CMD_BLINK = 0x30 67 CMD_RANDOM = 0x40 68 CMD_FB_EXT = 0x41 69 CMD_FB_READ = 0x42 70 CMD_FB_WRITE = 0x43 71 CMD_BT_CTRL = 0x46 72 CMD_PLATFORM = 0x48 73 CMD_MCU = 0x49 74 CMD_FW_VERSION = 0x50 75 CMD_ROM_READ = 0x51 76 CMD_RESET = 0x55 77 CMD_INTERFACES = 0x71 78 79 CMD_INT0_DATA = 0x00 80 CMD_INT1_DATA = 0x10 81 CMD_INT2_DATA = 0x20 82 CMD_INT3_DATA = 0x70 83 CMD_INT4_DATA = 0x75 84 CMD_INT5_DATA = 0x90 85 CMD_INT6_DATA = 0xA0 86 CMD_INT7_DATA = 0xB0 87 CMD_INT8_DATA = 0xC0 88 CMD_INT9_DATA = 0xD0 89 CMD_INT10_DATA = 0xE0 90 CMD_INT11_DATA = 0xF0 91 92 DETECT_REQ = 0x73 93 DETECT_RESP = 0x46 94 95 RADIO_STATE_OFF = 0x00 96 RADIO_STATE_ON = 0x01 97 RADIO_STATE_ASK = 0xFF 98 99 CMD_ERROR = 0x90 100 ERROR_INITRADIO = 0x01 101 ERROR_TXFAILED = 0x02 102 ERROR_EEPROM_LOCKED = 0x03 103 104 PLATFORM_AVR = 0x90 105 PLATFORM_ESP32 = 0x80 106 PLATFORM_NRF52 = 0x70 107 108 SX127X = 0x00 109 SX1276 = 0x01 110 SX1278 = 0x02 111 SX126X = 0x10 112 SX1262 = 0x11 113 SX128X = 0x20 114 SX1280 = 0x21 115 116 CMD_SEL_INT = 0x1F 117 118 def interface_type_to_str(interface_type): 119 if interface_type == KISS.SX126X or interface_type == KISS.SX1262: 120 return "SX126X" 121 elif interface_type == KISS.SX127X or interface_type == KISS.SX1276 or interface_type == KISS.SX1278: 122 return "SX127X" 123 elif interface_type == KISS.SX128X or interface_type == KISS.SX1280: 124 return "SX128X" 125 else: 126 return "SX127X" 127 128 @staticmethod 129 def escape(data): 130 data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd])) 131 data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc])) 132 return data 133 134 135 class RNodeMultiInterface(Interface): 136 MAX_CHUNK = 32768 137 DEFAULT_IFAC_SIZE = 8 138 139 CALLSIGN_MAX_LEN = 32 140 141 REQUIRED_FW_VER_MAJ = 1 142 REQUIRED_FW_VER_MIN = 74 143 144 RECONNECT_WAIT = 5 145 146 MAX_SUBINTERFACES = 11 147 148 def __init__(self, owner, configuration): 149 if RNS.vendor.platformutils.is_android(): 150 raise SystemError("Invalid interface type. The Android-specific RNode interface must be used on Android") 151 152 import importlib.util 153 if importlib.util.find_spec('serial') != None: 154 import serial 155 else: 156 RNS.log("Using the RNode interface requires a serial communication module to be installed.", RNS.LOG_CRITICAL) 157 RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL) 158 RNS.panic() 159 160 super().__init__() 161 162 c = Interface.get_config_obj(configuration) 163 name = c["name"] 164 165 count = 0 166 enabled_count = 0 167 168 # Count how many interfaces are in the file 169 for subinterface in c: 170 # if the retrieved entry is not a string, it must be a dictionary, which is what we want 171 if isinstance(c[subinterface], dict): 172 count += 1 173 174 # Count how many interfaces are enabled to allow for appropriate matrix sizing 175 for subinterface in c: 176 if isinstance(c[subinterface], dict): 177 subinterface_config = c[subinterface] 178 if (("interface_enabled" in subinterface_config) and subinterface_config.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True): 179 enabled_count += 1 180 181 # Create an array with a row for each subinterface 182 subint_config = [[None for x in range(11)] for y in range(enabled_count)] 183 subint_index = 0 184 185 for subinterface in c: 186 if isinstance(c[subinterface], dict): 187 subinterface_config = c[subinterface] 188 if (("interface_enabled" in subinterface_config) and subinterface_config.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True): 189 subint_vport = subinterface_config["vport"] if "vport" in subinterface_config else None 190 191 subint_config[subint_index][0] = subinterface 192 193 subint_config[subint_index][1] = subint_vport 194 195 frequency = int(subinterface_config["frequency"]) if "frequency" in subinterface_config else None 196 subint_config[subint_index][2] = frequency 197 bandwidth = int(subinterface_config["bandwidth"]) if "bandwidth" in subinterface_config else None 198 subint_config[subint_index][3] = bandwidth 199 txpower = int(subinterface_config["txpower"]) if "txpower" in subinterface_config else None 200 subint_config[subint_index][4] = txpower 201 spreadingfactor = int(subinterface_config["spreadingfactor"]) if "spreadingfactor" in subinterface_config else None 202 subint_config[subint_index][5] = spreadingfactor 203 codingrate = int(subinterface_config["codingrate"]) if "codingrate" in subinterface_config else None 204 subint_config[subint_index][6] = codingrate 205 flow_control = subinterface_config.as_bool("flow_control") if "flow_control" in subinterface_config else False 206 subint_config[subint_index][7] = flow_control 207 st_alock = float(subinterface_config["airtime_limit_short"]) if "airtime_limit_short" in subinterface_config else None 208 subint_config[subint_index][8] = st_alock 209 lt_alock = float(subinterface_config["airtime_limit_long"]) if "airtime_limit_long" in subinterface_config else None 210 subint_config[subint_index][9] = lt_alock 211 212 if "outgoing" in subinterface_config and subinterface_config.as_bool("outgoing") == False: 213 subint_config[subint_index][10] = False 214 else: 215 subint_config[subint_index][10] = True 216 217 subint_index += 1 218 219 # if no subinterfaces are defined 220 if count == 0: 221 raise ValueError("No subinterfaces configured for "+name) 222 # if no subinterfaces are enabled 223 elif enabled_count == 0: 224 raise ValueError("No subinterfaces enabled for "+name) 225 226 id_interval = int(c["id_interval"]) if "id_interval" in c else None 227 id_callsign = c["id_callsign"] if "id_callsign" in c else None 228 port = c["port"] if "port" in c else None 229 230 if port == None: 231 raise ValueError("No port specified for "+name) 232 233 self.HW_MTU = 508 234 235 self.clients = 0 236 self.pyserial = serial 237 self.serial = None 238 self.selected_index = 0 239 self.owner = owner 240 self.name = name 241 self.port = port 242 self.speed = 115200 243 self.databits = 8 244 self.stopbits = 1 245 self.timeout = 100 246 self.online = False 247 self.detached = False 248 self.reconnecting= False 249 250 self.bitrate = 0 251 self.platform = None 252 self.display = None 253 self.mcu = None 254 self.detected = False 255 self.firmware_ok = False 256 self.maj_version = 0 257 self.min_version = 0 258 self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL 259 260 self.last_id = 0 261 self.first_tx = None 262 self.reconnect_w = RNodeMultiInterface.RECONNECT_WAIT 263 264 self.subinterfaces = [0] * RNodeMultiInterface.MAX_SUBINTERFACES 265 self.subinterface_types = [] 266 self.subint_config = subint_config 267 268 self.r_stat_rx = None 269 self.r_stat_tx = None 270 self.r_stat_rssi = None 271 self.r_stat_snr = None 272 self.r_st_alock = None 273 self.r_lt_alock = None 274 self.r_random = None 275 276 self.packet_queue = [] 277 self.interface_ready = False 278 self.announce_rate_target = None 279 280 self.validcfg = True 281 if id_interval != None and id_callsign != None: 282 if (len(id_callsign.encode("utf-8")) <= RNodeMultiInterface.CALLSIGN_MAX_LEN): 283 self.should_id = True 284 self.id_callsign = id_callsign.encode("utf-8") 285 self.id_interval = id_interval 286 else: 287 RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeMultiInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR) 288 self.validcfg = False 289 else: 290 self.id_interval = None 291 self.id_callsign = None 292 293 if (not self.validcfg): 294 raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline") 295 296 def start(self): 297 try: 298 self.open_port() 299 300 if self.serial.is_open: 301 self.configure_device() 302 else: 303 raise IOError("Could not open serial port") 304 305 except Exception as e: 306 RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR) 307 RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) 308 RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR) 309 if not self.detached and not self.reconnecting: 310 thread = threading.Thread(target=self.reconnect_port) 311 thread.daemon = True 312 thread.start() 313 314 def open_port(self): 315 RNS.log("Opening serial port "+self.port+"...") 316 self.serial = self.pyserial.Serial( 317 port = self.port, 318 baudrate = self.speed, 319 bytesize = self.databits, 320 parity = self.pyserial.PARITY_NONE, 321 stopbits = self.stopbits, 322 xonxoff = False, 323 rtscts = False, 324 timeout = 0, 325 inter_byte_timeout = None, 326 write_timeout = None, 327 dsrdtr = False, 328 ) 329 330 331 def configure_device(self): 332 sleep(2.0) 333 334 thread = threading.Thread(target=self.readLoop) 335 thread.daemon = True 336 thread.start() 337 338 self.detect() 339 sleep(0.2) 340 341 if not self.detected: 342 RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR) 343 self.serial.close() 344 else: 345 if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52: 346 self.display = True 347 348 RNS.log("Serial port "+self.port+" is now open") 349 RNS.log("Creating subinterfaces...", RNS.LOG_VERBOSE) 350 for subint in self.subint_config: 351 subint_vport = int(subint[1]) 352 # check if index of vport exists in interface types array (the index corresponds to the vport for that interface) 353 if len(self.subinterface_types) >= (subint_vport+1): 354 # interface will add itself to the subinterfaces list automatically 355 interface = RNodeSubInterface( 356 RNS.Transport, 357 subint[0], 358 self, 359 subint_vport, 360 self.subinterface_types[subint_vport], 361 frequency = subint[2], 362 bandwidth = subint[3], 363 txpower = subint[4], 364 sf = subint[5], 365 cr = subint[6], 366 flow_control=subint[7], 367 st_alock=subint[8], 368 lt_alock=subint[9] 369 ) 370 371 interface.OUT = subint[10] 372 interface.IN = True 373 374 interface.announce_rate_target = self.announce_rate_target 375 interface.mode = self.mode 376 interface.HW_MTU = self.HW_MTU 377 interface.detected = True 378 RNS.Transport.interfaces.append(interface) 379 RNS.log("Spawned new RNode subinterface: "+str(interface), RNS.LOG_VERBOSE) 380 381 self.clients += 1 382 else: 383 raise ValueError("Virtual port \""+subint[1]+"\" for subinterface "+subint[0]+" does not exist on "+self.name) 384 self.online = True 385 386 def detect(self): 387 kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND, KISS.CMD_INTERFACES, 0x00, KISS.FEND]) 388 written = self.serial.write(kiss_command) 389 if written != len(kiss_command): 390 raise IOError("An IO error occurred while detecting hardware for "+str(self)) 391 392 def leave(self): 393 kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND]) 394 written = self.serial.write(kiss_command) 395 if written != len(kiss_command): 396 raise IOError("An IO error occurred while sending host left command to device") 397 398 def enable_external_framebuffer(self): 399 if self.display != None: 400 kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND]) 401 written = self.serial.write(kiss_command) 402 if written != len(kiss_command): 403 raise IOError("An IO error occurred while enabling external framebuffer on device") 404 405 def disable_external_framebuffer(self): 406 if self.display != None: 407 kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND]) 408 written = self.serial.write(kiss_command) 409 if written != len(kiss_command): 410 raise IOError("An IO error occurred while disabling external framebuffer on device") 411 412 FB_PIXEL_WIDTH = 64 413 FB_BITS_PER_PIXEL = 1 414 FB_PIXELS_PER_BYTE = 8//FB_BITS_PER_PIXEL 415 FB_BYTES_PER_LINE = FB_PIXEL_WIDTH//FB_PIXELS_PER_BYTE 416 def display_image(self, imagedata): 417 if self.display != None: 418 lines = len(imagedata)//8 419 for line in range(lines): 420 line_start = line*RNodeMultiInterface.FB_BYTES_PER_LINE 421 line_end = line_start+RNodeMultiInterface.FB_BYTES_PER_LINE 422 line_data = bytes(imagedata[line_start:line_end]) 423 self.write_framebuffer(line, line_data) 424 425 def write_framebuffer(self, line, line_data): 426 if self.display != None: 427 line_byte = line.to_bytes(1, byteorder="big", signed=False) 428 data = line_byte+line_data 429 escaped_data = KISS.escape(data) 430 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND]) 431 432 written = self.serial.write(kiss_command) 433 if written != len(kiss_command): 434 raise IOError("An IO error occurred while writing framebuffer data device") 435 436 def hard_reset(self): 437 kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND]) 438 written = self.serial.write(kiss_command) 439 if written != len(kiss_command): 440 raise IOError("An IO error occurred while restarting device") 441 sleep(2.25); 442 443 def setFrequency(self, frequency, interface): 444 c1 = frequency >> 24 445 c2 = frequency >> 16 & 0xFF 446 c3 = frequency >> 8 & 0xFF 447 c4 = frequency & 0xFF 448 data = KISS.escape(bytes([c1])+bytes([c2])+bytes([c3])+bytes([c4])) 449 450 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND]) 451 written = self.serial.write(kiss_command) 452 if written != len(kiss_command): 453 raise IOError("An IO error occurred while configuring frequency for "+str(self)) 454 455 def setBandwidth(self, bandwidth, interface): 456 c1 = bandwidth >> 24 457 c2 = bandwidth >> 16 & 0xFF 458 c3 = bandwidth >> 8 & 0xFF 459 c4 = bandwidth & 0xFF 460 data = KISS.escape(bytes([c1])+bytes([c2])+bytes([c3])+bytes([c4])) 461 462 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND]) 463 written = self.serial.write(kiss_command) 464 if written != len(kiss_command): 465 raise IOError("An IO error occurred while configuring bandwidth for "+str(self)) 466 467 def setTXPower(self, txpower, interface): 468 txp = txpower.to_bytes(1, byteorder="big", signed=True) 469 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND]) 470 written = self.serial.write(kiss_command) 471 if written != len(kiss_command): 472 raise IOError("An IO error occurred while configuring TX power for "+str(self)) 473 474 def setSpreadingFactor(self, sf, interface): 475 sf = bytes([sf]) 476 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND]) 477 written = self.serial.write(kiss_command) 478 if written != len(kiss_command): 479 raise IOError("An IO error occurred while configuring spreading factor for "+str(self)) 480 481 def setCodingRate(self, cr, interface): 482 cr = bytes([cr]) 483 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND]) 484 written = self.serial.write(kiss_command) 485 if written != len(kiss_command): 486 raise IOError("An IO error occurred while configuring coding rate for "+str(self)) 487 488 def setSTALock(self, st_alock, interface): 489 if st_alock != None: 490 at = int(st_alock*100) 491 c1 = at >> 8 & 0xFF 492 c2 = at & 0xFF 493 data = KISS.escape(bytes([c1])+bytes([c2])) 494 495 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND]) 496 written = self.serial.write(kiss_command) 497 if written != len(kiss_command): 498 raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self)) 499 500 def setLTALock(self, lt_alock, interface): 501 if lt_alock != None: 502 at = int(lt_alock*100) 503 c1 = at >> 8 & 0xFF 504 c2 = at & 0xFF 505 data = KISS.escape(bytes([c1])+bytes([c2])) 506 507 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND]) 508 written = self.serial.write(kiss_command) 509 if written != len(kiss_command): 510 raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self)) 511 512 def setRadioState(self, state, interface): 513 #self.state = state 514 kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND]) 515 written = self.serial.write(kiss_command) 516 if written != len(kiss_command): 517 raise IOError("An IO error occurred while configuring radio state for "+str(self)) 518 519 def validate_firmware(self): 520 if (self.maj_version >= RNodeMultiInterface.REQUIRED_FW_VER_MAJ): 521 if (self.min_version >= RNodeMultiInterface.REQUIRED_FW_VER_MIN): 522 self.firmware_ok = True 523 524 if self.firmware_ok: 525 return 526 527 RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR) 528 RNS.log("This version of Reticulum requires at least version "+str(RNodeMultiInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeMultiInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR) 529 RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/Reticulum/RNS/Utilities/rnodeconf.py") 530 RNS.panic() 531 532 def process_outgoing(self, data, interface = None): 533 if interface is None: 534 # do nothing if RNS tries to transmit on this interface directly 535 pass 536 else: 537 data = KISS.escape(data) 538 frame = bytes([KISS.FEND])+bytes([KISS.CMD_SEL_INT])+bytes([interface.index])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_DATA])+data+bytes([KISS.FEND]) 539 540 written = self.serial.write(frame) 541 self.txb += len(data) 542 543 if written != len(frame): 544 raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) 545 546 def received_announce(self, from_spawned=False): 547 if from_spawned: self.ia_freq_deque.append(time.time()) 548 549 def sent_announce(self, from_spawned=False): 550 if from_spawned: self.oa_freq_deque.append(time.time()) 551 552 def readLoop(self): 553 try: 554 in_frame = False 555 escape = False 556 command = KISS.CMD_UNKNOWN 557 data_buffer = b"" 558 command_buffer = b"" 559 last_read_ms = int(time.time()*1000) 560 561 while self.serial.is_open: 562 if self.serial.in_waiting: 563 byte = ord(self.serial.read(1)) 564 last_read_ms = int(time.time()*1000) 565 566 if (in_frame and byte == KISS.FEND and 567 (command == KISS.CMD_DATA)): 568 in_frame = False 569 self.subinterfaces[self.selected_index].process_incoming(data_buffer) 570 data_buffer = b"" 571 command_buffer = b"" 572 elif (byte == KISS.FEND): 573 in_frame = True 574 command = KISS.CMD_UNKNOWN 575 data_buffer = b"" 576 command_buffer = b"" 577 elif (in_frame and len(data_buffer) < self.HW_MTU): 578 if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN): 579 command = byte 580 elif (command == KISS.CMD_INT0_DATA or 581 command == KISS.CMD_INT1_DATA or 582 command == KISS.CMD_INT2_DATA or 583 command == KISS.CMD_INT3_DATA or 584 command == KISS.CMD_INT4_DATA or 585 command == KISS.CMD_INT5_DATA or 586 command == KISS.CMD_INT6_DATA or 587 command == KISS.CMD_INT7_DATA or 588 command == KISS.CMD_INT8_DATA or 589 command == KISS.CMD_INT9_DATA or 590 command == KISS.CMD_INT10_DATA or 591 command == KISS.CMD_INT11_DATA): 592 if (byte == KISS.FESC): 593 escape = True 594 else: 595 if (escape): 596 if (byte == KISS.TFEND): 597 byte = KISS.FEND 598 if (byte == KISS.TFESC): 599 byte = KISS.FESC 600 escape = False 601 data_buffer = data_buffer+bytes([byte]) 602 elif (command == KISS.CMD_FREQUENCY): 603 if (byte == KISS.FESC): 604 escape = True 605 else: 606 if (escape): 607 if (byte == KISS.TFEND): 608 byte = KISS.FEND 609 if (byte == KISS.TFESC): 610 byte = KISS.FESC 611 escape = False 612 command_buffer = command_buffer+bytes([byte]) 613 if (len(command_buffer) == 4): 614 self.subinterfaces[self.selected_index].r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] 615 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting frequency is "+str(self.subinterfaces[self.selected_index].r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG) 616 self.subinterfaces[self.selected_index].updateBitrate() 617 618 elif (command == KISS.CMD_BANDWIDTH): 619 if (byte == KISS.FESC): 620 escape = True 621 else: 622 if (escape): 623 if (byte == KISS.TFEND): 624 byte = KISS.FEND 625 if (byte == KISS.TFESC): 626 byte = KISS.FESC 627 escape = False 628 command_buffer = command_buffer+bytes([byte]) 629 if (len(command_buffer) == 4): 630 self.subinterfaces[self.selected_index].r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] 631 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting bandwidth is "+str(self.subinterfaces[self.selected_index].r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG) 632 self.subinterfaces[self.selected_index].updateBitrate() 633 634 elif (command == KISS.CMD_SEL_INT): 635 self.selected_index = byte 636 637 elif (command == KISS.CMD_TXPOWER): 638 txp = byte - 256 if byte > 127 else byte 639 self.subinterfaces[self.selected_index].r_txpower = txp 640 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting TX power is "+str(self.subinterfaces[self.selected_index].r_txpower)+" dBm", RNS.LOG_DEBUG) 641 elif (command == KISS.CMD_SF): 642 self.subinterfaces[self.selected_index].r_sf = byte 643 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting spreading factor is "+str(self.subinterfaces[self.selected_index].r_sf), RNS.LOG_DEBUG) 644 self.subinterfaces[self.selected_index].updateBitrate() 645 elif (command == KISS.CMD_CR): 646 self.subinterfaces[self.selected_index].r_cr = byte 647 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting coding rate is "+str(self.subinterfaces[self.selected_index].r_cr), RNS.LOG_DEBUG) 648 self.subinterfaces[self.selected_index].updateBitrate() 649 elif (command == KISS.CMD_RADIO_STATE): 650 self.subinterfaces[self.selected_index].r_state = byte 651 if self.subinterfaces[self.selected_index].r_state: 652 pass 653 #RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG) 654 else: 655 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting state is offline", RNS.LOG_DEBUG) 656 657 elif (command == KISS.CMD_RADIO_LOCK): 658 self.subinterfaces[self.selected_index].r_lock = byte 659 elif (command == KISS.CMD_FW_VERSION): 660 if (byte == KISS.FESC): 661 escape = True 662 else: 663 if (escape): 664 if (byte == KISS.TFEND): 665 byte = KISS.FEND 666 if (byte == KISS.TFESC): 667 byte = KISS.FESC 668 escape = False 669 command_buffer = command_buffer+bytes([byte]) 670 if (len(command_buffer) == 2): 671 self.maj_version = int(command_buffer[0]) 672 self.min_version = int(command_buffer[1]) 673 self.validate_firmware() 674 675 # not implemented in RNode_Firmware yet 676 #elif (command == KISS.CMD_STAT_RX): 677 # if (byte == KISS.FESC): 678 # escape = True 679 # else: 680 # if (escape): 681 # if (byte == KISS.TFEND): 682 # byte = KISS.FEND 683 # if (byte == KISS.TFESC): 684 # byte = KISS.FESC 685 # escape = False 686 # command_buffer = command_buffer+bytes([byte]) 687 # if (len(command_buffer) == 4): 688 # self.r_stat_rx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3]) 689 690 #elif (command == KISS.CMD_STAT_TX): 691 # if (byte == KISS.FESC): 692 # escape = True 693 # else: 694 # if (escape): 695 # if (byte == KISS.TFEND): 696 # byte = KISS.FEND 697 # if (byte == KISS.TFESC): 698 # byte = KISS.FESC 699 # escape = False 700 # command_buffer = command_buffer+bytes([byte]) 701 # if (len(command_buffer) == 4): 702 # self.r_stat_tx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3]) 703 704 elif (command == KISS.CMD_STAT_RSSI): 705 self.subinterfaces[self.selected_index].r_stat_rssi = byte-RNodeSubInterface.RSSI_OFFSET 706 elif (command == KISS.CMD_STAT_SNR): 707 self.subinterfaces[self.selected_index].r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25 708 try: 709 sfs = self.subinterfaces[self.selected_index].r_sf-7 710 snr = self.subinterfaces[self.selected_index].r_stat_snr 711 q_snr_min = RNodeSubInterface.Q_SNR_MIN_BASE-sfs*RNodeSubInterface.Q_SNR_STEP 712 q_snr_max = RNodeSubInterface.Q_SNR_MAX 713 q_snr_span = q_snr_max-q_snr_min 714 quality = round(((snr-q_snr_min)/(q_snr_span))*100,1) 715 if quality > 100.0: quality = 100.0 716 if quality < 0.0: quality = 0.0 717 self.subinterfaces[self.selected_index].r_stat_q = quality 718 except: 719 pass 720 elif (command == KISS.CMD_ST_ALOCK): 721 if (byte == KISS.FESC): 722 escape = True 723 else: 724 if (escape): 725 if (byte == KISS.TFEND): 726 byte = KISS.FEND 727 if (byte == KISS.TFESC): 728 byte = KISS.FESC 729 escape = False 730 command_buffer = command_buffer+bytes([byte]) 731 if (len(command_buffer) == 2): 732 at = command_buffer[0] << 8 | command_buffer[1] 733 self.subinterfaces[self.selected_index].r_st_alock = at/100.0 734 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting short-term airtime limit is "+str(self.subinterfaces[self.selected_index].r_st_alock)+"%", RNS.LOG_DEBUG) 735 elif (command == KISS.CMD_LT_ALOCK): 736 if (byte == KISS.FESC): 737 escape = True 738 else: 739 if (escape): 740 if (byte == KISS.TFEND): 741 byte = KISS.FEND 742 if (byte == KISS.TFESC): 743 byte = KISS.FESC 744 escape = False 745 command_buffer = command_buffer+bytes([byte]) 746 if (len(command_buffer) == 2): 747 at = command_buffer[0] << 8 | command_buffer[1] 748 self.subinterfaces[self.selected_index].r_lt_alock = at/100.0 749 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting long-term airtime limit is "+str(self.subinterfaces[self.selected_index].r_lt_alock)+"%", RNS.LOG_DEBUG) 750 elif (command == KISS.CMD_STAT_CHTM): 751 if (byte == KISS.FESC): 752 escape = True 753 else: 754 if (escape): 755 if (byte == KISS.TFEND): 756 byte = KISS.FEND 757 if (byte == KISS.TFESC): 758 byte = KISS.FESC 759 escape = False 760 command_buffer = command_buffer+bytes([byte]) 761 if (len(command_buffer) == 8): 762 ats = command_buffer[0] << 8 | command_buffer[1] 763 atl = command_buffer[2] << 8 | command_buffer[3] 764 cus = command_buffer[4] << 8 | command_buffer[5] 765 cul = command_buffer[6] << 8 | command_buffer[7] 766 767 self.r_airtime_short = ats/100.0 768 self.r_airtime_long = atl/100.0 769 self.r_channel_load_short = cus/100.0 770 self.r_channel_load_long = cul/100.0 771 elif (command == KISS.CMD_STAT_PHYPRM): 772 if (byte == KISS.FESC): 773 escape = True 774 else: 775 if (escape): 776 if (byte == KISS.TFEND): 777 byte = KISS.FEND 778 if (byte == KISS.TFESC): 779 byte = KISS.FESC 780 escape = False 781 command_buffer = command_buffer+bytes([byte]) 782 if (len(command_buffer) == 10): 783 lst = (command_buffer[0] << 8 | command_buffer[1])/1000.0 784 lsr = command_buffer[2] << 8 | command_buffer[3] 785 prs = command_buffer[4] << 8 | command_buffer[5] 786 prt = command_buffer[6] << 8 | command_buffer[7] 787 cst = command_buffer[8] << 8 | command_buffer[9] 788 789 if lst != self.subinterfaces[self.selected_index].r_symbol_time_ms or lsr != self.subinterfaces[self.selected_index].r_symbol_rate or prs != self.subinterfaces[self.selected_index].r_preamble_symbols or prt != self.subinterfaces[self.selected_index].r_premable_time_ms or cst != self.subinterfaces[self.selected_index].r_csma_slot_time_ms: 790 self.subinterfaces[self.selected_index].r_symbol_time_ms = lst 791 self.subinterfaces[self.selected_index].r_symbol_rate = lsr 792 self.subinterfaces[self.selected_index].r_preamble_symbols = prs 793 self.subinterfaces[self.selected_index].r_premable_time_ms = prt 794 self.subinterfaces[self.selected_index].r_csma_slot_time_ms = cst 795 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting symbol time is "+str(round(self.subinterfaces[self.selected_index].r_symbol_time_ms,2))+"ms (at "+str(self.subinterfaces[self.selected_index].r_symbol_rate)+" baud)", RNS.LOG_DEBUG) 796 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting preamble is "+str(self.subinterfaces[self.selected_index].r_preamble_symbols)+" symbols ("+str(self.subinterfaces[self.selected_index].r_premable_time_ms)+"ms)", RNS.LOG_DEBUG) 797 RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting CSMA slot time is "+str(self.subinterfaces[self.selected_index].r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG) 798 elif (command == KISS.CMD_RANDOM): 799 self.r_random = byte 800 elif (command == KISS.CMD_PLATFORM): 801 self.platform = byte 802 elif (command == KISS.CMD_MCU): 803 self.mcu = byte 804 elif (command == KISS.CMD_ERROR): 805 if (byte == KISS.ERROR_INITRADIO): 806 RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) 807 raise IOError("Radio initialisation failure") 808 elif (byte == KISS.ERROR_TXFAILED): 809 RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) 810 raise IOError("Hardware transmit failure") 811 else: 812 RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) 813 raise IOError("Unknown hardware failure") 814 elif (command == KISS.CMD_RESET): 815 if (byte == 0xF8): 816 if self.platform == KISS.PLATFORM_ESP32: 817 if self.online: 818 RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR) 819 raise IOError("ESP32 reset") 820 elif (command == KISS.CMD_READY): 821 self.process_queue() 822 elif (command == KISS.CMD_DETECT): 823 if byte == KISS.DETECT_RESP: 824 self.detected = True 825 else: 826 self.detected = False 827 elif (command == KISS.CMD_INTERFACES): 828 command_buffer = command_buffer+bytes([byte]) 829 if (len(command_buffer) == 2): 830 # add the interface to the back of the list, they're all given from vport 0 and up in order 831 self.subinterface_types.append(KISS.interface_type_to_str(command_buffer[1])) 832 command_buffer = b"" 833 834 else: 835 time_since_last = int(time.time()*1000) - last_read_ms 836 if len(data_buffer) > 0 and time_since_last > self.timeout: 837 RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING) 838 data_buffer = b"" 839 in_frame = False 840 command = KISS.CMD_UNKNOWN 841 escape = False 842 843 if self.id_interval != None and self.id_callsign != None: 844 if self.first_tx != None: 845 if time.time() > self.first_tx + self.id_interval: 846 interface_available = False 847 for interface in self.subinterfaces: 848 if interface != 0 and interface.online: 849 interface_available = True 850 self.subinterfaces[interface.index].process_outgoing(self.id_callsign) 851 852 if interface_available: 853 RNS.log("Interface "+str(self)+" is transmitting beacon data on all subinterfaces: "+str(self.id_callsign.decode("utf-8")), RNS.LOG_DEBUG) 854 855 sleep(0.08) 856 857 except Exception as e: 858 self.online = False 859 RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) 860 RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) 861 862 if RNS.Reticulum.panic_on_interface_error: 863 RNS.panic() 864 865 RNS.log("Reticulum will attempt to reconnect the interface periodically.", RNS.LOG_ERROR) 866 867 self.teardown_subinterfaces() 868 869 self.online = False 870 try: 871 self.serial.close() 872 except Exception as e: 873 pass 874 875 if not self.detached and not self.reconnecting: 876 self.reconnect_port() 877 878 def reconnect_port(self): 879 self.reconnecting = True 880 while not self.online and not self.detached: 881 try: 882 time.sleep(5) 883 RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) 884 self.open_port() 885 if self.serial.is_open: 886 self.configure_device() 887 except Exception as e: 888 RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) 889 890 self.reconnecting = False 891 if self.online: 892 RNS.log("Reconnected serial port for "+str(self)) 893 894 def detach(self): 895 self.detached = True 896 self.disable_external_framebuffer() 897 898 for interface in self.subinterfaces: 899 if interface != 0: 900 self.setRadioState(KISS.RADIO_STATE_OFF, interface) 901 self.leave() 902 903 def teardown_subinterfaces(self): 904 for interface in self.subinterfaces: 905 if interface != 0: 906 if interface in RNS.Transport.interfaces: 907 RNS.Transport.interfaces.remove(interface) 908 self.subinterfaces[interface.index] = 0 909 910 def should_ingress_limit(self): 911 return False 912 913 def process_queue(self): 914 for interface in self.subinterfaces: 915 if interface != 0: 916 interface.process_queue() 917 918 def __str__(self): 919 return "RNodeMultiInterface["+str(self.name)+"]" 920 921 class RNodeSubInterface(Interface): 922 LOW_FREQ_MIN = 137000000 923 LOW_FREQ_MAX = 1000000000 924 925 HIGH_FREQ_MIN = 2200000000 926 HIGH_FREQ_MAX = 2600000000 927 928 RSSI_OFFSET = 157 929 930 Q_SNR_MIN_BASE = -9 931 Q_SNR_MAX = 6 932 Q_SNR_STEP = 2 933 934 def __init__(self, owner, name, parent_interface, index, interface_type, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, st_alock = None, lt_alock = None,): 935 if RNS.vendor.platformutils.is_android(): 936 raise SystemError("Invalid interface type. The Android-specific RNode interface must be used on Android") 937 938 import importlib.util 939 if importlib.util.find_spec('serial') != None: 940 import serial 941 else: 942 RNS.log("Using the RNode interface requires a serial communication module to be installed.", RNS.LOG_CRITICAL) 943 RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL) 944 RNS.panic() 945 946 super().__init__() 947 948 self.owner = owner 949 self.name = name 950 self.index = index 951 self.interface_type= interface_type 952 self.flow_control= flow_control 953 self.online = False 954 955 self.frequency = frequency 956 self.bandwidth = bandwidth 957 self.txpower = txpower 958 self.sf = sf 959 self.cr = cr 960 self.state = KISS.RADIO_STATE_OFF 961 self.bitrate = 0 962 self.st_alock = st_alock 963 self.lt_alock = lt_alock 964 self.platform = None 965 self.display = None 966 self.mcu = None 967 968 self.r_frequency = None 969 self.r_bandwidth = None 970 self.r_txpower = None 971 self.r_sf = None 972 self.r_cr = None 973 self.r_state = None 974 self.r_lock = None 975 self.r_stat_rx = None 976 self.r_stat_tx = None 977 self.r_stat_rssi = None 978 self.r_stat_snr = None 979 self.r_st_alock = None 980 self.r_lt_alock = None 981 self.r_airtime_short = 0.0 982 self.r_airtime_long = 0.0 983 self.r_channel_load_short = 0.0 984 self.r_channel_load_long = 0.0 985 self.r_symbol_time_ms = None 986 self.r_symbol_rate = None 987 self.r_preamble_symbols = None 988 self.r_premable_time_ms = None 989 990 self.packet_queue = [] 991 self.interface_ready = False 992 self.parent_interface = parent_interface 993 self.announce_rate_target = None 994 995 self.mode = None 996 self.bitrate = None 997 self.ifac_size = None 998 999 # add this interface to the subinterfaces array 1000 self.parent_interface.subinterfaces[index] = self 1001 1002 self.validcfg = True 1003 if (self.interface_type == "SX126X" or self.interface_type == "SX127X"): 1004 if (self.frequency < RNodeSubInterface.LOW_FREQ_MIN or self.frequency > RNodeSubInterface.LOW_FREQ_MAX): 1005 RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR) 1006 self.validcfg = False 1007 elif (self.interface_type == "SX128X"): 1008 if (self.frequency < RNodeSubInterface.HIGH_FREQ_MIN or self.frequency > RNodeSubInterface.HIGH_FREQ_MAX): 1009 RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR) 1010 self.validcfg = False 1011 else: 1012 RNS.log("Invalid interface type configured for "+str(self), RNS.LOG_ERROR) 1013 self.validcfg = False 1014 1015 if (self.txpower < -9 or self.txpower > 37): 1016 RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR) 1017 self.validcfg = False 1018 1019 if (self.bandwidth < 7800 or self.bandwidth > 1625000): 1020 RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR) 1021 self.validcfg = False 1022 1023 if (self.sf < 5 or self.sf > 12): 1024 RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR) 1025 self.validcfg = False 1026 1027 if (self.cr < 5 or self.cr > 8): 1028 RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR) 1029 self.validcfg = False 1030 1031 if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)): 1032 RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR) 1033 self.validcfg = False 1034 1035 if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)): 1036 RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR) 1037 self.validcfg = False 1038 1039 if (not self.validcfg): 1040 raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline") 1041 1042 self.configure_device() 1043 1044 def configure_device(self): 1045 self.r_frequency = None 1046 self.r_bandwidth = None 1047 self.r_txpower = None 1048 self.r_sf = None 1049 self.r_cr = None 1050 self.r_state = None 1051 self.r_lock = None 1052 sleep(2.0) 1053 1054 RNS.log("Configuring RNode subinterface "+str(self)+"...", RNS.LOG_VERBOSE) 1055 self.initRadio() 1056 if (self.validateRadioState()): 1057 self.interface_ready = True 1058 RNS.log(str(self)+" is configured and powered up") 1059 sleep(0.3) 1060 self.online = True 1061 else: 1062 RNS.log("After configuring "+str(self)+", the reported radio parameters did not match your configuration.", RNS.LOG_ERROR) 1063 RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR) 1064 RNS.log("Aborting RNode startup", RNS.LOG_ERROR) 1065 1066 1067 def initRadio(self): 1068 self.parent_interface.setFrequency(self.frequency, self) 1069 self.parent_interface.setBandwidth(self.bandwidth, self) 1070 self.parent_interface.setTXPower(self.txpower, self) 1071 self.parent_interface.setSpreadingFactor(self.sf, self) 1072 self.parent_interface.setCodingRate(self.cr, self) 1073 self.parent_interface.setSTALock(self.st_alock, self) 1074 self.parent_interface.setLTALock(self.lt_alock, self) 1075 self.parent_interface.setRadioState(KISS.RADIO_STATE_ON, self) 1076 self.state = KISS.RADIO_STATE_ON 1077 1078 def validateRadioState(self): 1079 RNS.log("Waiting for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE) 1080 sleep(0.25); 1081 1082 self.validcfg = True 1083 if (self.r_frequency != None and abs(self.frequency - int(self.r_frequency)) > 100): 1084 RNS.log("Frequency mismatch", RNS.LOG_ERROR) 1085 self.validcfg = False 1086 if (self.bandwidth != self.r_bandwidth): 1087 RNS.log("Bandwidth mismatch", RNS.LOG_ERROR) 1088 self.validcfg = False 1089 if (self.txpower != self.r_txpower): 1090 RNS.log("TX power mismatch", RNS.LOG_ERROR) 1091 self.validcfg = False 1092 if (self.sf != self.r_sf): 1093 RNS.log("Spreading factor mismatch", RNS.LOG_ERROR) 1094 self.validcfg = False 1095 if (self.state != self.r_state): 1096 RNS.log("Radio state mismatch", RNS.LOG_ERROR) 1097 self.validcfg = False 1098 1099 if (self.validcfg): 1100 return True 1101 else: 1102 return False 1103 1104 1105 def updateBitrate(self): 1106 try: 1107 self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 1108 self.bitrate_kbps = round(self.bitrate/1000.0, 2) 1109 RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_VERBOSE) 1110 except: 1111 self.bitrate = 0 1112 1113 def process_incoming(self, data): 1114 self.rxb += len(data) 1115 self.owner.inbound(data, self) 1116 self.r_stat_rssi = None 1117 self.r_stat_snr = None 1118 1119 def process_outgoing(self,data): 1120 if self.online: 1121 if self.interface_ready: 1122 if self.flow_control: 1123 self.interface_ready = False 1124 1125 if data == self.parent_interface.id_callsign: 1126 self.parent_interface.first_tx = None 1127 else: 1128 if self.parent_interface.first_tx == None: 1129 self.parent_interface.first_tx = time.time() 1130 self.txb += len(data) 1131 self.parent_interface.process_outgoing(data, self) 1132 else: 1133 self.queue(data) 1134 1135 def queue(self, data): 1136 self.packet_queue.append(data) 1137 1138 1139 def process_queue(self): 1140 if len(self.packet_queue) > 0: 1141 data = self.packet_queue.pop(0) 1142 self.interface_ready = True 1143 self.process_outgoing(data) 1144 elif len(self.packet_queue) == 0: 1145 self.interface_ready = True 1146 1147 def __str__(self): 1148 return self.parent_interface.name+"["+self.name+"]"