Compare commits

..

5 Commits

Author SHA1 Message Date
Mark Qvist fd010fa80c Version bump to 0.1.3 2020-05-21 14:48:00 +02:00
Mark Qvist 8c2cfb0349 Added periodic ID option to RNode interface. Fixed RNode SNR stat indication. 2020-05-21 14:17:14 +02:00
Mark Qvist 0140cd6eba Version change for setup.py 2020-05-15 10:06:40 +02:00
Mark Qvist 3819485c20 Better comments on outbound transport 2020-05-15 10:05:18 +02:00
Mark Qvist 3ca06bbf3a Fixed shared instance transport for 1-hop destinations. Path request forwarding to local clients behind shared instance. 2020-05-15 09:41:26 +02:00
4 changed files with 105 additions and 24 deletions
+30 -6
View File
@@ -70,7 +70,9 @@ class RNodeInterface(Interface):
RSSI_OFFSET = 157
SNR_OFFSET = 128
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False):
CALLSIGN_MAX_LEN = 32
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None):
self.serial = None
self.owner = owner
self.name = name
@@ -90,6 +92,8 @@ class RNodeInterface(Interface):
self.state = KISS.RADIO_STATE_OFF
self.bitrate = 0
self.last_id = 0
self.r_frequency = None
self.r_bandwidth = None
self.r_txpower = None
@@ -123,10 +127,22 @@ class RNodeInterface(Interface):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.cr < 5 or self.sf > 8):
if (self.cr < 5 or self.cr > 8):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if id_interval != None and id_callsign != None:
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
self.should_id = True
self.id_callsign = id_callsign
self.id_interval = id_interval
else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR)
self.validcfg = False
else:
self.id_interval = None
self.id_callsign = None
if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline")
@@ -259,7 +275,7 @@ class RNodeInterface(Interface):
try:
self.bitrate = self.r_sf * ( (4.0/self.cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
self.bitrate_kbps = round(self.bitrate/1000.0, 2)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_DEBUG)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_INFO)
except:
self.bitrate = 0
@@ -273,9 +289,17 @@ class RNodeInterface(Interface):
if self.flow_control:
self.interface_ready = False
data = KISS.escape(data)
frame = bytes([0xc0])+bytes([0x00])+data+bytes([0xc0])
frame = b""
if self.id_interval != None and self.id_callsign != None:
if self.last_id + self.id_interval < time.time():
self.last_id = time.time()
frame = bytes([0xc0])+bytes([0x00])+KISS.escape(self.id_callsign.encode("utf-8"))+bytes([0xc0])
data = KISS.escape(data)
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)))
else:
@@ -408,7 +432,7 @@ class RNodeInterface(Interface):
elif (command == KISS.CMD_STAT_RSSI):
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
elif (command == KISS.CMD_STAT_SNR):
self.r_stat_snr = byte-RNodeInterface.SNR_OFFSET
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
elif (command == KISS.CMD_RANDOM):
self.r_random = byte
elif (command == KISS.CMD_ERROR):
+27 -11
View File
@@ -248,8 +248,7 @@ class Reticulum:
txtail = int(c["txtail"]) if "txtail" in c else None
persistence = int(c["persistence"]) if "persistence" in c else None
slottime = int(c["slottime"]) if "slottime" in c else None
flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False
flow_control = c.as_bool("flow_control") if "flow_control" in c else False
port = c["port"] if "port" in c else None
speed = int(c["speed"]) if "speed" in c else 9600
databits = int(c["databits"]) if "databits" in c else 8
@@ -286,8 +285,7 @@ class Reticulum:
txtail = int(c["txtail"]) if "txtail" in c else None
persistence = int(c["persistence"]) if "persistence" in c else None
slottime = int(c["slottime"]) if "slottime" in c else None
flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False
flow_control = c.as_bool("flow_control") if "flow_control" in c else False
port = c["port"] if "port" in c else None
speed = int(c["speed"]) if "speed" in c else 9600
databits = int(c["databits"]) if "databits" in c else 8
@@ -330,7 +328,9 @@ class Reticulum:
txpower = int(c["txpower"]) if "txpower" in c else None
spreadingfactor = int(c["spreadingfactor"]) if "spreadingfactor" in c else None
codingrate = int(c["codingrate"]) if "codingrate" in c else None
flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False
flow_control = c.as_bool("flow_control") if "flow_control" in c else False
id_interval = int(c["id_interval"]) if "id_interval" in c else None
id_callsign = c["id_callsign"] if "id_callsign" in c else None
port = c["port"] if "port" in c else None
@@ -341,11 +341,14 @@ class Reticulum:
RNS.Transport,
name,
port,
frequency,
bandwidth,
txpower,
spreadingfactor,
flow_control
frequency = frequency,
bandwidth = bandwidth,
txpower = txpower,
sf = spreadingfactor,
cr = codingrate,
flow_control = flow_control,
id_interval = id_interval,
id_callsign = id_callsign
)
if "outgoing" in c and c["outgoing"].lower() == "true":
@@ -355,7 +358,7 @@ class Reticulum:
RNS.Transport.interfaces.append(interface)
else:
RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_VERBOSE)
RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_NOTICE)
except Exception as e:
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
@@ -533,6 +536,19 @@ loglevel = 4
# fastest, and 8 the longest range.
codingrate = 5
# You can configure the RNode to send
# out identification on the channel with
# a set interval by configuring the
# following two parameters. The trans-
# ceiver will only ID before making an
# actual transmission, and if the set
# interval has elapsed since it's last
# ID. Interval is configured in seconds
# This option is commented out and not
# used by default.
# id_callsign = MYCALL-0
# id_interval = 600
# An example KISS modem interface. Useful for running
# Reticulum over packet radio hardware.
+47 -6
View File
@@ -324,6 +324,11 @@ class Transport:
if packet.packet_type != RNS.Packet.ANNOUNCE and packet.destination_hash in Transport.destination_table:
outbound_interface = Transport.destination_table[packet.destination_hash][5]
# If there's more than one hop to the destination, and we know
# a path, we insert the packet into transport by adding the next
# transport nodes address to the header, and modifying the flags.
# This rule applies both for "normal" transport, and when connected
# to a local shared Reticulum instance.
if Transport.destination_table[packet.destination_hash][2] > 1:
if packet.header_type == RNS.Packet.HEADER_1:
# Insert packet into transport
@@ -333,19 +338,44 @@ class Transport:
new_raw += Transport.destination_table[packet.destination_hash][1]
new_raw += packet.raw[2:]
# TODO: Remove at some point
RNS.log("Packet was inserted into transport via "+RNS.prettyhexrep(Transport.destination_table[packet.destination_hash][1])+" on: "+str(outbound_interface), RNS.LOG_DEBUG)
RNS.log("Packet was inserted into transport via "+RNS.prettyhexrep(Transport.destination_table[packet.destination_hash][1])+" on: "+str(outbound_interface), RNS.LOG_EXTREME)
outbound_interface.processOutgoing(new_raw)
Transport.destination_table[packet.destination_hash][0] = time.time()
sent = True
# In the special case where we are connected to a local shared
# Reticulum instance, and the destination is one hop away, we
# also add transport headers to inject the packet into transport
# via the shared instance. Normally a packet for a destination
# one hop away would just be broadcast directly, but since we
# are "behind" a shared instance, we need to get that instance
# to transport it onto the network.
elif Transport.destination_table[packet.destination_hash][2] == 1 and Transport.owner.is_connected_to_shared_instance:
if packet.header_type == RNS.Packet.HEADER_1:
# Insert packet into transport
new_flags = (RNS.Packet.HEADER_2) << 6 | (Transport.TRANSPORT) << 4 | (packet.flags & 0b00001111)
new_raw = struct.pack("!B", new_flags)
new_raw += packet.raw[1:2]
new_raw += Transport.destination_table[packet.destination_hash][1]
new_raw += packet.raw[2:]
# TODO: Remove at some point
RNS.log("Packet was inserted into transport via "+RNS.prettyhexrep(Transport.destination_table[packet.destination_hash][1])+" on: "+str(outbound_interface), RNS.LOG_EXTREME)
outbound_interface.processOutgoing(new_raw)
Transport.destination_table[packet.destination_hash][0] = time.time()
sent = True
# If none of the above applies, we know the destination is
# directly reachable, and also on which interface, so we
# simply transmit the packet directly on that one.
else:
# Destination is directly reachable, and we know on
# what interface, so transmit only on that one
outbound_interface.processOutgoing(packet.raw)
sent = True
# If we don't have a known path for the destination, we'll
# broadcast the packet on all outgoing interfaces, or the
# just the relevant interface if the packet has an attached
# interface, or belongs to a link.
else:
# Broadcast packet on all outgoing interfaces, or the relevant
# interface if packet is for a link or has an attached interface
for interface in Transport.interfaces:
if interface.OUT:
should_transmit = True
@@ -961,7 +991,12 @@ class Transport:
local_rebroadcasts = 0
block_rebroadcasts = True
announce_hops = packet.hops
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
if is_from_local_client:
retransmit_timeout = now
else:
# TODO: Look at this timing
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
# This handles an edge case where a peer sends a past
# request for a destination just after an announce for
@@ -982,6 +1017,12 @@ class Transport:
if not interface == attached_interface:
Transport.requestPathOnInterface(destination_hash, interface)
elif not is_from_local_client and len(Transport.local_client_interfaces) > 0:
# Forward the path request on all local
# client interfaces
for interface in Transport.local_client_interfaces:
Transport.requestPathOnInterface(destination_hash, interface)
else:
RNS.log("No known path to requested destination, ignoring request", RNS.LOG_DEBUG)
+1 -1
View File
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup(
name="rns",
version="0.1.1",
version="0.1.3",
author="Mark Qvist",
author_email="mark@unsigned.io",
description="Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between",