mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-11-22 21:50:18 +00:00
Migrated to Python3
This commit is contained in:
parent
a339ae3d28
commit
fa50e1d35b
@ -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
|
||||
|
@ -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
|
||||
@ -296,5 +296,4 @@ class AX25KISSInterface(Interface):
|
||||
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
|
||||
|
||||
def __str__(self):
|
||||
return "AX25KISSInterface["+self.name+"]"
|
||||
|
||||
return "AX25KISSInterface["+self.name+"]"
|
@ -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
|
||||
@ -248,5 +248,4 @@ class KISSInterface(Interface):
|
||||
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
|
||||
|
||||
def __str__(self):
|
||||
return "KISSInterface["+self.name+"]"
|
||||
|
||||
return "KISSInterface["+self.name+"]"
|
@ -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:
|
||||
|
@ -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
|
||||
|
10
RNS/Link.py
10
RNS/Link.py
@ -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()
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user