Migrated to Python3

This commit is contained in:
Mark Qvist 2020-04-22 17:07:40 +02:00
parent a339ae3d28
commit fa50e1d35b
7 changed files with 171 additions and 156 deletions

View File

@ -5,7 +5,7 @@ Reticulum is a cryptography-based networking stack for low-bandwidth, high-laten
Reticulum is a complete networking stack, and does not use IP or higher layers, although it can be easily tunnelled through conventional IP networks. This frees up a lot of overhead, that has been utilised to implement a networking stack built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
Reticulum runs completely in userland, and can run on practically any system that runs Python.
Reticulum runs completely in userland, and can run on practically any system that runs Python 3.
For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects/reticulum/)
@ -47,9 +47,9 @@ An API- and wireformat-stable alpha release is coming in the near future. Until
Some countries still ban the use of encryption when operating under an amateur radio license. Reticulum offers several encryptionless modes, while still using cryptographic principles for station verification, link establishment, data integrity verification, acknowledgements and routing. It is therefore perfectly possible to include Reticulum in amateur radio use, even if your country bans encryption.
## Dependencies:
- Python 2.7
- Python 3
- cryptography.io
- pyserial 3.1
- pyserial
## How do I get started?
Full documentation and video tutorials are coming with the stable alpha release. Until then, you are mostly on your own. If you really want to experiment already, you could take a look in the "Examples" folder, for some well-documented example programs. Be sure to also read the [Reticulum Overview Document](http://unsigned.io/wp-content/uploads/2018/04/Reticulum_Overview_v0.4.pdf).
@ -58,7 +58,7 @@ To install dependencies and get started:
```
# Install dependencies
pip install cryptography pyserial
pip3 install cryptography pyserial
# Clone repository
git clone https://github.com/markqvist/Reticulum.git

View File

@ -8,25 +8,25 @@ import time
import RNS
class KISS():
FEND = chr(0xC0)
FESC = chr(0xDB)
TFEND = chr(0xDC)
TFESC = chr(0xDD)
CMD_UNKNOWN = chr(0xFE)
CMD_DATA = chr(0x00)
CMD_TXDELAY = chr(0x01)
CMD_P = chr(0x02)
CMD_SLOTTIME = chr(0x03)
CMD_TXTAIL = chr(0x04)
CMD_FULLDUPLEX = chr(0x05)
CMD_SETHARDWARE = chr(0x06)
CMD_READY = chr(0x0F)
CMD_RETURN = chr(0xFF)
FEND = 0xC0
FESC = 0xDB
TFEND = 0xDC
TFESC = 0xDD
CMD_UNKNOWN = 0xFE
CMD_DATA = 0x00
CMD_TXDELAY = 0x01
CMD_P = 0x02
CMD_SLOTTIME = 0x03
CMD_TXTAIL = 0x04
CMD_FULLDUPLEX = 0x05
CMD_SETHARDWARE = 0x06
CMD_READY = 0x0F
CMD_RETURN = 0xFF
class AX25():
PID_NOLAYER3 = chr(0xF0)
CTRL_UI = chr(0x03)
CRC_CORRECT = chr(0xF0)+chr(0xB8)
PID_NOLAYER3 = 0xF0
CTRL_UI = 0x03
CRC_CORRECT = bytes([0xF0])+bytes([0xB8])
HEADER_SIZE = 16
@ -45,9 +45,9 @@ class AX25KISSInterface(Interface):
self.serial = None
self.owner = owner
self.name = name
self.src_call = callsign.upper()
self.src_call = callsign.upper().encode("ascii")
self.src_ssid = ssid
self.dst_call = "APZRNS"
self.dst_call = "APZRNS".encode("ascii")
self.dst_ssid = 0
self.port = port
self.speed = speed
@ -57,8 +57,8 @@ class AX25KISSInterface(Interface):
self.timeout = 100
self.online = False
# TODO: Sane default and make this configurable
# TODO: Changed to 1ms instead of 100ms, check it
self.txdelay = 0.001
# TODO: Changed to 25ms instead of 100ms, check it
self.txdelay = 0.025
self.packet_queue = []
self.flow_control = flow_control
@ -129,7 +129,7 @@ class AX25KISSInterface(Interface):
if preamble > 255:
preamble = 255
kiss_command = KISS.FEND+KISS.CMD_TXDELAY+chr(preamble)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")")
@ -142,7 +142,7 @@ class AX25KISSInterface(Interface):
if txtail > 255:
txtail = 255
kiss_command = KISS.FEND+KISS.CMD_TXTAIL+chr(txtail)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")")
@ -153,7 +153,7 @@ class AX25KISSInterface(Interface):
if persistence > 255:
persistence = 255
kiss_command = KISS.FEND+KISS.CMD_P+chr(persistence)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface persistence to "+str(persistence))
@ -166,13 +166,13 @@ class AX25KISSInterface(Interface):
if slottime > 255:
slottime = 255
kiss_command = KISS.FEND+KISS.CMD_SLOTTIME+chr(slottime)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")")
def setFlowControl(self, flow_control):
kiss_command = KISS.FEND+KISS.CMD_READY+chr(0x01)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
if (flow_control):
@ -192,30 +192,30 @@ class AX25KISSInterface(Interface):
if self.flow_control:
self.interface_ready = False
encoded_dst_ssid = 0x60 | (self.dst_ssid << 1)
encoded_src_ssid = 0x60 | (self.src_ssid << 1) | 0x01
encoded_dst_ssid = bytes([0x60 | (self.dst_ssid << 1)])
encoded_src_ssid = bytes([0x60 | (self.src_ssid << 1) | 0x01])
addr = ""
addr = b""
for i in range(0,6):
if (i < len(self.dst_call)):
addr += chr(ord(self.dst_call[i])<<1)
addr += bytes([self.dst_call[i]<<1])
else:
addr += chr(0x20)
addr += chr(encoded_dst_ssid)
addr += bytes([0x20])
addr += encoded_dst_ssid
for i in range(0,6):
if (i < len(self.src_call)):
addr += chr(ord(self.src_call[i])<<1)
addr += bytes([self.src_call[i]<<1])
else:
addr += chr(0x20)
addr += chr(encoded_src_ssid)
addr += bytes([0x20])
addr += encoded_src_ssid
data = addr+AX25.CTRL_UI+AX25.PID_NOLAYER3+data
data = addr+bytes([AX25.CTRL_UI])+bytes([AX25.PID_NOLAYER3])+data
data = data.replace(chr(0xdb), chr(0xdb)+chr(0xdd))
data = data.replace(chr(0xc0), chr(0xdb)+chr(0xdc))
kiss_frame = chr(0xc0)+chr(0x00)+data+chr(0xc0)
data = data.replace(bytes([0xdb]), bytes([0xdb])+bytes([0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb])+bytes([0xdc]))
kiss_frame = bytes([KISS.FEND])+bytes([0x00])+data+bytes([KISS.FEND])
if (self.txdelay > 0):
RNS.log(str(self.name)+" delaying TX for "+str(self.txdelay)+" seconds", RNS.LOG_EXTREME)
@ -250,7 +250,7 @@ class AX25KISSInterface(Interface):
while self.serial.is_open:
if self.serial.in_waiting:
byte = self.serial.read(1)
byte = ord(self.serial.read(1))
last_read_ms = int(time.time()*1000)
if (in_frame and byte == KISS.FEND and command == KISS.CMD_DATA):
@ -259,12 +259,12 @@ class AX25KISSInterface(Interface):
elif (byte == KISS.FEND):
in_frame = True
command = KISS.CMD_UNKNOWN
data_buffer = ""
data_buffer = b""
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU+AX25.HEADER_SIZE):
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
# We only support one HDLC port for now, so
# strip off the port nibble
byte = chr(ord(byte) & 0x0F)
byte = byte & 0x0F
command = byte
elif (command == KISS.CMD_DATA):
if (byte == KISS.FESC):
@ -276,7 +276,7 @@ class AX25KISSInterface(Interface):
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
data_buffer = data_buffer+byte
data_buffer = data_buffer+bytes([byte])
elif (command == KISS.CMD_READY):
# TODO: add timeout and reset if ready
# command never arrives
@ -297,4 +297,3 @@ class AX25KISSInterface(Interface):
def __str__(self):
return "AX25KISSInterface["+self.name+"]"

View File

@ -8,20 +8,20 @@ import time
import RNS
class KISS():
FEND = chr(0xC0)
FESC = chr(0xDB)
TFEND = chr(0xDC)
TFESC = chr(0xDD)
CMD_UNKNOWN = chr(0xFE)
CMD_DATA = chr(0x00)
CMD_TXDELAY = chr(0x01)
CMD_P = chr(0x02)
CMD_SLOTTIME = chr(0x03)
CMD_TXTAIL = chr(0x04)
CMD_FULLDUPLEX = chr(0x05)
CMD_SETHARDWARE = chr(0x06)
CMD_READY = chr(0x0F)
CMD_RETURN = chr(0xFF)
FEND = 0xC0
FESC = 0xDB
TFEND = 0xDC
TFESC = 0xDD
CMD_UNKNOWN = 0xFE
CMD_DATA = 0x00
CMD_TXDELAY = 0x01
CMD_P = 0x02
CMD_SLOTTIME = 0x03
CMD_TXTAIL = 0x04
CMD_FULLDUPLEX = 0x05
CMD_SETHARDWARE = 0x06
CMD_READY = 0x0F
CMD_RETURN = 0xFF
class KISSInterface(Interface):
MAX_CHUNK = 32768
@ -108,8 +108,7 @@ class KISSInterface(Interface):
if preamble > 255:
preamble = 255
RNS.log("Setting preamble to "+str(preamble)+" "+chr(preamble))
kiss_command = KISS.FEND+KISS.CMD_TXDELAY+chr(preamble)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")")
@ -122,7 +121,7 @@ class KISSInterface(Interface):
if txtail > 255:
txtail = 255
kiss_command = KISS.FEND+KISS.CMD_TXTAIL+chr(txtail)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")")
@ -133,7 +132,7 @@ class KISSInterface(Interface):
if persistence > 255:
persistence = 255
kiss_command = KISS.FEND+KISS.CMD_P+chr(persistence)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface persistence to "+str(persistence))
@ -146,13 +145,13 @@ class KISSInterface(Interface):
if slottime > 255:
slottime = 255
kiss_command = KISS.FEND+KISS.CMD_SLOTTIME+chr(slottime)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")")
def setFlowControl(self, flow_control):
kiss_command = KISS.FEND+KISS.CMD_READY+chr(0x01)+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
if (flow_control):
@ -171,9 +170,10 @@ class KISSInterface(Interface):
if self.flow_control:
self.interface_ready = False
data = data.replace(chr(0xdb), chr(0xdb)+chr(0xdd))
data = data.replace(chr(0xc0), chr(0xdb)+chr(0xdc))
frame = chr(0xc0)+chr(0x00)+data+chr(0xc0)
data = data.replace(bytes([0xdb]), bytes([0xdb])+bytes([0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb])+bytes([0xdc]))
frame = bytes([KISS.FEND])+bytes([0x00])+data+bytes([KISS.FEND])
written = self.serial.write(frame)
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
@ -202,7 +202,7 @@ class KISSInterface(Interface):
while self.serial.is_open:
if self.serial.in_waiting:
byte = self.serial.read(1)
byte = ord(self.serial.read(1))
last_read_ms = int(time.time()*1000)
if (in_frame and byte == KISS.FEND and command == KISS.CMD_DATA):
@ -211,12 +211,12 @@ class KISSInterface(Interface):
elif (byte == KISS.FEND):
in_frame = True
command = KISS.CMD_UNKNOWN
data_buffer = ""
data_buffer = b""
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
# We only support one HDLC port for now, so
# strip off port nibble
byte = chr(ord(byte) & 0x0F)
# strip off the port nibble
byte = byte & 0x0F
command = byte
elif (command == KISS.CMD_DATA):
if (byte == KISS.FESC):
@ -228,7 +228,7 @@ class KISSInterface(Interface):
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
data_buffer = data_buffer+byte
data_buffer = data_buffer+bytes([byte])
elif (command == KISS.CMD_READY):
# TODO: add timeout and reset if ready
# command never arrives
@ -249,4 +249,3 @@ class KISSInterface(Interface):
def __str__(self):
return "KISSInterface["+self.name+"]"

View File

@ -9,46 +9,47 @@ import math
import RNS
class KISS():
FEND = chr(0xC0)
FESC = chr(0xDB)
TFEND = chr(0xDC)
TFESC = chr(0xDD)
FEND = 0xC0
FESC = 0xDB
TFEND = 0xDC
TFESC = 0xDD
CMD_UNKNOWN = chr(0xFE)
CMD_DATA = chr(0x00)
CMD_FREQUENCY = chr(0x01)
CMD_BANDWIDTH = chr(0x02)
CMD_TXPOWER = chr(0x03)
CMD_SF = chr(0x04)
CMD_CR = chr(0x05)
CMD_RADIO_STATE = chr(0x06)
CMD_RADIO_LOCK = chr(0x07)
CMD_DETECT = chr(0x08)
CMD_READY = chr(0x0F)
CMD_STAT_RX = chr(0x21)
CMD_STAT_TX = chr(0x22)
CMD_STAT_RSSI = chr(0x23)
CMD_BLINK = chr(0x30)
CMD_RANDOM = chr(0x40)
CMD_FW_VERSION = chr(0x50)
CMD_ROM_READ = chr(0x51)
CMD_UNKNOWN = 0xFE
CMD_DATA = 0x00
CMD_FREQUENCY = 0x01
CMD_BANDWIDTH = 0x02
CMD_TXPOWER = 0x03
CMD_SF = 0x04
CMD_CR = 0x05
CMD_RADIO_STATE = 0x06
CMD_RADIO_LOCK = 0x07
CMD_DETECT = 0x08
CMD_READY = 0x0F
CMD_STAT_RX = 0x21
CMD_STAT_TX = 0x22
CMD_STAT_RSSI = 0x23
CMD_STAT_SNR = 0x24
CMD_BLINK = 0x30
CMD_RANDOM = 0x40
CMD_FW_VERSION = 0x50
CMD_ROM_READ = 0x51
DETECT_REQ = chr(0x73)
DETECT_RESP = chr(0x46)
DETECT_REQ = 0x73
DETECT_RESP = 0x46
RADIO_STATE_OFF = chr(0x00)
RADIO_STATE_ON = chr(0x01)
RADIO_STATE_ASK = chr(0xFF)
RADIO_STATE_OFF = 0x00
RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF
CMD_ERROR = chr(0x90)
ERROR_INITRADIO = chr(0x01)
ERROR_TXFAILED = chr(0x02)
ERROR_EEPROM_LOCKED = chr(0x03)
CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02
ERROR_EEPROM_LOCKED = 0x03
@staticmethod
def escape(data):
data = data.replace(chr(0xdb), chr(0xdb)+chr(0xdd))
data = data.replace(chr(0xc0), chr(0xdb)+chr(0xdc))
data = data.replace(bytes([0xdb]), bytes([0xdb])+bytes([0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb])+bytes([0xdc]))
return data
@ -66,9 +67,10 @@ class RNodeInterface(Interface):
FREQ_MIN = 137000000
FREQ_MAX = 1020000000
RSSI_OFFSET = 292
RSSI_OFFSET = 157
SNR_OFFSET = 128
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, flow_control = True):
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, flow_control = False):
self.serial = None
self.owner = owner
self.name = name
@ -157,7 +159,7 @@ class RNodeInterface(Interface):
RNS.log(str(self)+" is configured and powered up")
sleep(1.0)
else:
RNS.log("After configuring "+str(self)+", the actual radio parameters did not match your configuration.", RNS.LOG_ERROR)
RNS.log("After configuring "+str(self)+", the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
self.serial.close()
@ -178,9 +180,9 @@ class RNodeInterface(Interface):
c2 = self.frequency >> 16 & 0xFF
c3 = self.frequency >> 8 & 0xFF
c4 = self.frequency & 0xFF
data = KISS.escape(chr(c1)+chr(c2)+chr(c3)+chr(c4))
data = KISS.escape(bytes([c1])+bytes([c2])+bytes([c3])+bytes([c4]))
kiss_command = KISS.FEND+KISS.CMD_FREQUENCY+data+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring frequency for "+self(str))
@ -190,36 +192,39 @@ class RNodeInterface(Interface):
c2 = self.bandwidth >> 16 & 0xFF
c3 = self.bandwidth >> 8 & 0xFF
c4 = self.bandwidth & 0xFF
data = KISS.escape(chr(c1)+chr(c2)+chr(c3)+chr(c4))
data = KISS.escape(bytes([c1])+bytes([c2])+bytes([c3])+bytes([c4]))
kiss_command = KISS.FEND+KISS.CMD_BANDWIDTH+data+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring bandwidth for "+self(str))
def setTXPower(self):
txp = chr(self.txpower)
kiss_command = KISS.FEND+KISS.CMD_TXPOWER+txp+KISS.FEND
txp = bytes([self.txpower])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring TX power for "+self(str))
def setSpreadingFactor(self):
sf = chr(self.sf)
kiss_command = KISS.FEND+KISS.CMD_SF+sf+KISS.FEND
sf = bytes([self.sf])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring spreading factor for "+self(str))
def setCodingRate(self):
cr = chr(self.cr)
kiss_command = KISS.FEND+KISS.CMD_CR+cr+KISS.FEND
cr = bytes([self.cr])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+self(str))
def setRadioState(self, state):
kiss_command = KISS.FEND+KISS.CMD_RADIO_STATE+state+KISS.FEND
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring radio state for "+self(str))
@ -265,7 +270,7 @@ class RNodeInterface(Interface):
self.interface_ready = False
data = KISS.escape(data)
frame = chr(0xc0)+chr(0x00)+data+chr(0xc0)
frame = bytes([0xc0])+bytes([0x00])+data+bytes([0xc0])
written = self.serial.write(frame)
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
@ -288,25 +293,25 @@ class RNodeInterface(Interface):
in_frame = False
escape = False
command = KISS.CMD_UNKNOWN
data_buffer = ""
command_buffer = ""
data_buffer = b""
command_buffer = b""
last_read_ms = int(time.time()*1000)
while self.serial.is_open:
if self.serial.in_waiting:
byte = self.serial.read(1)
byte = ord(self.serial.read(1))
last_read_ms = int(time.time()*1000)
if (in_frame and byte == KISS.FEND and command == KISS.CMD_DATA):
in_frame = False
self.processIncoming(data_buffer)
data_buffer = ""
command_buffer = ""
data_buffer = b""
command_buffer = b""
elif (byte == KISS.FEND):
in_frame = True
command = KISS.CMD_UNKNOWN
data_buffer = ""
command_buffer = ""
data_buffer = b""
command_buffer = b""
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
command = byte
@ -320,7 +325,7 @@ class RNodeInterface(Interface):
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
data_buffer = data_buffer+byte
data_buffer = data_buffer+bytes([byte])
elif (command == KISS.CMD_FREQUENCY):
if (byte == KISS.FESC):
escape = True
@ -331,9 +336,9 @@ class RNodeInterface(Interface):
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+byte
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_frequency = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting frequency is "+str(self.r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG)
self.updateBitrate()
@ -347,27 +352,27 @@ class RNodeInterface(Interface):
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+byte
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_bandwidth = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting bandwidth is "+str(self.r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_TXPOWER):
self.r_txpower = ord(byte)
self.r_txpower = byte
RNS.log(str(self)+" Radio reporting TX power is "+str(self.r_txpower)+" dBm", RNS.LOG_DEBUG)
elif (command == KISS.CMD_SF):
self.r_sf = ord(byte)
self.r_sf = byte
RNS.log(str(self)+" Radio reporting spreading factor is "+str(self.r_sf), RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_CR):
self.r_cr = ord(byte)
self.r_cr = byte
RNS.log(str(self)+" Radio reporting coding rate is "+str(self.r_cr), RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE):
self.r_state = ord(byte)
self.r_state = byte
elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = ord(byte)
self.r_lock = byte
elif (command == KISS.CMD_STAT_RX):
if (byte == KISS.FESC):
escape = True
@ -378,7 +383,7 @@ class RNodeInterface(Interface):
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+byte
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_stat_rx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
@ -392,14 +397,16 @@ class RNodeInterface(Interface):
if (byte == KISS.TFESC):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+byte
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_stat_tx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
elif (command == KISS.CMD_STAT_RSSI):
self.r_stat_rssi = ord(byte)-RNodeInterface.RSSI_OFFSET
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
elif (command == KISS.CMD_STAT_SNR):
self.r_stat_snr = byte-RNodeInterface.SNR_OFFSET
elif (command == KISS.CMD_RANDOM):
self.r_random = ord(byte)
self.r_random = byte
elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
@ -408,8 +415,10 @@ class RNodeInterface(Interface):
else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
elif (command == KISS.CMD_READY):
# TODO: add timeout and reset if ready
# command never arrives
# TODO: Flow control is disabled by default now.
# Add timed flow control ready-inidication to the
# RNode firmware to make sure flow control doesn't
# hang if it is enabled
self.process_queue()
else:

View File

@ -1,3 +1,8 @@
# TODO: This should be reworked for Python3 support,
# and maybe framing should be introduced to improve
# performance. The current 100ms wait is a bit stupid.
# Probably also need to add queue support like the
# other interfaces.
from .Interface import Interface
from time import sleep

View File

@ -31,7 +31,7 @@ class Link:
# first-hop RTT latency and distance
DEFAULT_TIMEOUT = 15.0
TIMEOUT_FACTOR = 3
KEEPALIVE = 120
KEEPALIVE = 180
PENDING = 0x00
HANDSHAKE = 0x01
@ -330,12 +330,12 @@ class Link:
def send_keepalive(self):
keepalive_packet = RNS.Packet(self, chr(0xFF), context=RNS.Packet.KEEPALIVE)
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
keepalive_packet.send()
def receive(self, packet):
self.watchdog_lock = True
if not self.status == Link.CLOSED and not (self.initiator and packet.context == RNS.Packet.KEEPALIVE and packet.data == chr(0xFF)):
if not self.status == Link.CLOSED and not (self.initiator and packet.context == RNS.Packet.KEEPALIVE and packet.data == bytes([0xFF])):
if packet.receiving_interface != self.attached_interface:
RNS.log("Link-associated packet received on unexpected interface! Someone might be trying to manipulate your communication!", RNS.LOG_ERROR)
else:
@ -400,8 +400,8 @@ class Link:
resource.cancel()
elif packet.context == RNS.Packet.KEEPALIVE:
if not self.initiator and packet.data == chr(0xFF):
keepalive_packet = RNS.Packet(self, chr(0xFE), context=RNS.Packet.KEEPALIVE)
if not self.initiator and packet.data == bytes([0xFF]):
keepalive_packet = RNS.Packet(self, bytes([0xFE]), context=RNS.Packet.KEEPALIVE)
keepalive_packet.send()

View File

@ -233,6 +233,7 @@ class Reticulum:
bandwidth = int(c["bandwidth"]) if "bandwidth" in c else None
txpower = int(c["txpower"]) if "txpower" in c else None
spreadingfactor = int(c["spreadingfactor"]) if "spreadingfactor" in c else None
flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False
port = c["port"] if "port" in c else None
@ -246,7 +247,8 @@ class Reticulum:
frequency,
bandwidth,
txpower,
spreadingfactor
spreadingfactor,
flow_control
)
if "outgoing" in c and c["outgoing"].lower() == "true":
@ -259,6 +261,7 @@ class Reticulum:
except Exception as e:
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
raise e
def createDefaultConfig(self):