/ RNS / Interfaces / RNodeMultiInterface.py
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+"]"