Compare commits

...

15 Commits

Author SHA1 Message Date
jacobeva
755f8ca245
Merge 9d744e2317 into c71ece6b8e 2024-09-17 16:07:36 +01:00
Mark Qvist
c71ece6b8e Updated version 2024-09-16 20:11:12 +02:00
Mark Qvist
1e45a002e1 Merge branch 'master' of github.com:markqvist/Reticulum 2024-09-16 20:10:55 +02:00
markqvist
68e64523b5
Merge pull request #552 from liamcottle/fix/ax25-kiss-interface
fix KISSInterface is not defined error for AX25KISSInterface
2024-09-16 19:56:13 +02:00
Mark Qvist
d9e6145034 Raise exception when SINGLE destination is created without identity 2024-09-16 18:20:53 +02:00
Mark Qvist
a91e67129e Update profiler output 2024-09-16 18:20:31 +02:00
liamcottle
76362bad4a fix KISSInterface is not defined error for AX25KISSInterface 2024-09-16 14:27:08 +12:00
Mark Qvist
421b5ef32e Recursive profiler results output 2024-09-15 16:46:52 +02:00
Mark Qvist
8d61ee8a81 Added performance profiler 2024-09-15 15:12:53 +02:00
Mark Qvist
2329181c88 Prioritize interfaces according to bitrate 2024-09-15 14:14:00 +02:00
markqvist
8ea0dc65c4
Merge pull request #551 from jacobeva/opencom_xl
Add support for openCom XL
2024-09-14 23:44:48 +02:00
jacob.eva
bba67836f0
Add support for openCom XL 2024-09-13 11:30:54 +01:00
Mark Qvist
a666bb6e73 Added minimum link traffic timeout 2024-09-12 17:52:40 +02:00
jacob.eva
9d744e2317
Allow for display use by master on NRF52 on Android 2024-09-04 11:54:32 +01:00
jacob.eva
d64064691a
Allow for use of display by master on NRF52 2024-09-04 11:52:41 +01:00
12 changed files with 175 additions and 6 deletions

View File

@ -167,6 +167,9 @@ class Destination:
identity = RNS.Identity() identity = RNS.Identity()
aspects = aspects+(identity.hexhash,) aspects = aspects+(identity.hexhash,)
if identity == None and direction == Destination.OUT and self.type != Destination.PLAIN:
raise ValueError("Can't create outbound SINGLE destination without an identity")
if identity != None and self.type == Destination.PLAIN: if identity != None and self.type == Destination.PLAIN:
raise TypeError("Selected destination type PLAIN cannot hold an identity") raise TypeError("Selected destination type PLAIN cannot hold an identity")

View File

@ -96,7 +96,7 @@ class AX25KISSInterface(Interface):
self.stopbits = stopbits self.stopbits = stopbits
self.timeout = 100 self.timeout = 100
self.online = False self.online = False
self.bitrate = KISSInterface.BITRATE_GUESS self.bitrate = AX25KISSInterface.BITRATE_GUESS
self.packet_queue = [] self.packet_queue = []
self.flow_control = flow_control self.flow_control = flow_control

View File

@ -81,6 +81,7 @@ class KISS():
PLATFORM_AVR = 0x90 PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80 PLATFORM_ESP32 = 0x80
PLATFORM_NRF52 = 0x70
@staticmethod @staticmethod
def escape(data): def escape(data):
@ -595,7 +596,7 @@ class RNodeInterface(Interface):
if not self.detected: if not self.detected:
raise IOError("Could not detect device") raise IOError("Could not detect device")
else: else:
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True self.display = True
if not self.firmware_ok: if not self.firmware_ok:

View File

@ -80,6 +80,7 @@ class KISS():
PLATFORM_AVR = 0x90 PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80 PLATFORM_ESP32 = 0x80
PLATFORM_NRF52 = 0x70
@staticmethod @staticmethod
def escape(data): def escape(data):
@ -285,7 +286,7 @@ class RNodeInterface(Interface):
RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR) RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR)
self.serial.close() self.serial.close()
else: else:
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True self.display = True
RNS.log("Serial port "+self.port+" is now open") RNS.log("Serial port "+self.port+" is now open")

View File

@ -106,6 +106,7 @@ class KISS():
PLATFORM_AVR = 0x90 PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80 PLATFORM_ESP32 = 0x80
PLATFORM_NRF52 = 0x70
SX127X = 0x00 SX127X = 0x00
SX1276 = 0x01 SX1276 = 0x01
@ -297,7 +298,7 @@ class RNodeMultiInterface(Interface):
RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR) RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR)
self.serial.close() self.serial.close()
else: else:
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True self.display = True
RNS.log("Serial port "+self.port+" is now open") RNS.log("Serial port "+self.port+" is now open")

View File

@ -68,6 +68,7 @@ class Link:
Timeout for link establishment in seconds per hop to destination. Timeout for link establishment in seconds per hop to destination.
""" """
TRAFFIC_TIMEOUT_MIN_MS = 5
TRAFFIC_TIMEOUT_FACTOR = 6 TRAFFIC_TIMEOUT_FACTOR = 6
KEEPALIVE_TIMEOUT_FACTOR = 4 KEEPALIVE_TIMEOUT_FACTOR = 4
""" """

View File

@ -381,7 +381,7 @@ class PacketReceipt:
self.proof_packet = None self.proof_packet = None
if packet.destination.type == RNS.Destination.LINK: if packet.destination.type == RNS.Destination.LINK:
self.timeout = packet.destination.rtt * packet.destination.traffic_timeout_factor self.timeout = max(packet.destination.rtt * packet.destination.traffic_timeout_factor, RNS.Link.TRAFFIC_TIMEOUT_MIN_MS/1000)
else: else:
self.timeout = RNS.Reticulum.get_instance().get_first_hop_timeout(self.destination.hash) self.timeout = RNS.Reticulum.get_instance().get_first_hop_timeout(self.destination.hash)
self.timeout += Packet.TIMEOUT_PER_HOP * RNS.Transport.hops_to(self.destination.hash) self.timeout += Packet.TIMEOUT_PER_HOP * RNS.Transport.hops_to(self.destination.hash)

View File

@ -160,6 +160,9 @@ class Reticulum:
RNS.Transport.exit_handler() RNS.Transport.exit_handler()
RNS.Identity.exit_handler() RNS.Identity.exit_handler()
if RNS.profiler_ran:
RNS.profiler_results()
@staticmethod @staticmethod
def sigint_handler(signal, frame): def sigint_handler(signal, frame):
RNS.Transport.detach_interfaces() RNS.Transport.detach_interfaces()

View File

@ -301,12 +301,23 @@ class Transport:
RNS.log("Transport instance "+str(Transport.identity)+" started", RNS.LOG_VERBOSE) RNS.log("Transport instance "+str(Transport.identity)+" started", RNS.LOG_VERBOSE)
Transport.start_time = time.time() Transport.start_time = time.time()
# Sort interfaces according to bitrate
Transport.prioritize_interfaces()
# Synthesize tunnels for any interfaces wanting it # Synthesize tunnels for any interfaces wanting it
for interface in Transport.interfaces: for interface in Transport.interfaces:
interface.tunnel_id = None interface.tunnel_id = None
if hasattr(interface, "wants_tunnel") and interface.wants_tunnel: if hasattr(interface, "wants_tunnel") and interface.wants_tunnel:
Transport.synthesize_tunnel(interface) Transport.synthesize_tunnel(interface)
@staticmethod
def prioritize_interfaces():
try:
Transport.interfaces.sort(key=lambda interface: interface.bitrate, reverse=True)
except Exception as e:
RNS.log(f"Could not prioritize interfaces according to bitrate. The contained exception was: {e}", RNS.LOG_ERROR)
@staticmethod @staticmethod
def jobloop(): def jobloop():
while (True): while (True):
@ -664,6 +675,7 @@ class Transport:
Transport.tables_last_culled = time.time() Transport.tables_last_culled = time.time()
if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval: if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval:
Transport.prioritize_interfaces()
for interface in Transport.interfaces: for interface in Transport.interfaces:
interface.process_held_announces() interface.process_held_announces()
Transport.interface_last_jobs = time.time() Transport.interface_last_jobs = time.time()

View File

@ -170,6 +170,11 @@ class ROM():
PRODUCT_RAK4631 = 0x10 PRODUCT_RAK4631 = 0x10
MODEL_11 = 0x11 MODEL_11 = 0x11
MODEL_12 = 0x12 MODEL_12 = 0x12
MODEL_13 = 0x13
MODEL_14 = 0x14
PRODUCT_OPENCOM_XL = 0x20
MODEL_21 = 0x21
PRODUCT_TECHO = 0x15 PRODUCT_TECHO = 0x15
MODEL_T4 = 0x16 MODEL_T4 = 0x16
@ -221,6 +226,7 @@ products = {
ROM.PRODUCT_H32_V3: "Heltec LoRa32 v3", ROM.PRODUCT_H32_V3: "Heltec LoRa32 v3",
ROM.PRODUCT_TECHO: "LilyGO T-Echo", ROM.PRODUCT_TECHO: "LilyGO T-Echo",
ROM.PRODUCT_RAK4631: "RAK4631", ROM.PRODUCT_RAK4631: "RAK4631",
ROM.PRODUCT_OPENCOM_XL: "openCom XL",
} }
platforms = { platforms = {
@ -263,8 +269,11 @@ models = {
0xE8: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1262"], 0xE8: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1262"],
0x11: [430000000, 510000000, 22, "430 - 510 MHz", "rnode_firmware_rak4631.zip", "SX1262"], 0x11: [430000000, 510000000, 22, "430 - 510 MHz", "rnode_firmware_rak4631.zip", "SX1262"],
0x12: [779000000, 928000000, 22, "779 - 928 MHz", "rnode_firmware_rak4631.zip", "SX1262"], 0x12: [779000000, 928000000, 22, "779 - 928 MHz", "rnode_firmware_rak4631.zip", "SX1262"],
0x11: [430000000, 510000000, 22, "430 - 510 MHz", "rnode_firmware_rak4631_sx1280.zip", "SX1262 + SX1280"],
0x12: [779000000, 928000000, 22, "779 - 928 MHz", "rnode_firmware_rak4631_sx1280.zip", "SX1262 + SX1280"],
0x16: [779000000, 928000000, 22, "430 - 510 Mhz", "rnode_firmware_techo.zip", "SX1262"], 0x16: [779000000, 928000000, 22, "430 - 510 Mhz", "rnode_firmware_techo.zip", "SX1262"],
0x17: [779000000, 928000000, 22, "779 - 928 Mhz", "rnode_firmware_techo.zip", "SX1262"], 0x17: [779000000, 928000000, 22, "779 - 928 Mhz", "rnode_firmware_techo.zip", "SX1262"],
0x21: [820000000, 960000000, 22, "820 - 960 MHz", "rnode_firmware_opencom_xl.zip", "SX1262 + SX1280"],
0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None, "Unknown"], 0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None, "Unknown"],
0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None, "Unknown"], 0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None, "Unknown"],
} }

View File

@ -270,6 +270,53 @@ def prettytime(time, verbose=False, compact=False):
else: else:
return tstr return tstr
def prettyshorttime(time, verbose=False, compact=False):
time = time*1e6
seconds = int(time // 1e6); time %= 1e6
milliseconds = int(time // 1e3); time %= 1e3
if compact:
microseconds = int(time)
else:
microseconds = round(time, 2)
ss = "" if seconds == 1 else "s"
sms = "" if milliseconds == 1 else "s"
sus = "" if microseconds == 1 else "s"
displayed = 0
components = []
if seconds > 0 and ((not compact) or displayed < 2):
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s")
displayed += 1
if milliseconds > 0 and ((not compact) or displayed < 2):
components.append(str(milliseconds)+" millisecond"+sms if verbose else str(milliseconds)+"ms")
displayed += 1
if microseconds > 0 and ((not compact) or displayed < 2):
components.append(str(microseconds)+" microsecond"+sus if verbose else str(microseconds)+"µs")
displayed += 1
i = 0
tstr = ""
for c in components:
i += 1
if i == 1:
pass
elif i < len(components):
tstr += ", "
elif i == len(components):
tstr += " and "
tstr += c
if tstr == "":
return "0us"
else:
return tstr
def phyparams(): def phyparams():
print("Required Physical Layer MTU : "+str(Reticulum.MTU)+" bytes") print("Required Physical Layer MTU : "+str(Reticulum.MTU)+" bytes")
print("Plaintext Packet MDU : "+str(Packet.PLAIN_MDU)+" bytes") print("Plaintext Packet MDU : "+str(Packet.PLAIN_MDU)+" bytes")
@ -285,3 +332,94 @@ def panic():
def exit(): def exit():
print("") print("")
sys.exit(0) sys.exit(0)
profiler_ran = False
profiler_tags = {}
def profiler(tag=None, capture=False, super_tag=None):
global profiler_ran, profiler_tags
try:
thread_ident = threading.get_ident()
if capture:
end = time.perf_counter()
if tag in profiler_tags and thread_ident in profiler_tags[tag]["threads"]:
if profiler_tags[tag]["threads"][thread_ident]["current_start"] != None:
begin = profiler_tags[tag]["threads"][thread_ident]["current_start"]
profiler_tags[tag]["threads"][thread_ident]["current_start"] = None
profiler_tags[tag]["threads"][thread_ident]["captures"].append(end-begin)
if not profiler_ran:
profiler_ran = True
else:
if not tag in profiler_tags:
profiler_tags[tag] = {"threads": {}, "super": super_tag}
if not thread_ident in profiler_tags[tag]["threads"]:
profiler_tags[tag]["threads"][thread_ident] = {"current_start": None, "captures": []}
profiler_tags[tag]["threads"][thread_ident]["current_start"] = time.perf_counter()
except Exception as e:
trace_exception(e)
def profiler_results():
from statistics import mean, median, stdev
results = {}
for tag in profiler_tags:
tag_captures = []
tag_entry = profiler_tags[tag]
for thread_ident in tag_entry["threads"]:
thread_entry = tag_entry["threads"][thread_ident]
thread_captures = thread_entry["captures"]
sample_count = len(thread_captures)
if sample_count > 2:
thread_results = {
"count": sample_count,
"mean": mean(thread_captures),
"median": median(thread_captures),
"stdev": stdev(thread_captures)
}
tag_captures.extend(thread_captures)
sample_count = len(tag_captures)
if sample_count > 2:
tag_results = {
"name": tag,
"super": tag_entry["super"],
"count": len(tag_captures),
"mean": mean(tag_captures),
"median": median(tag_captures),
"stdev": stdev(tag_captures)
}
results[tag] = tag_results
def print_results_recursive(tag, results, level=0):
print_tag_results(tag, level+1)
for tag_name in results:
sub_tag = results[tag_name]
if sub_tag["super"] == tag["name"]:
print_results_recursive(sub_tag, results, level=level+1)
def print_tag_results(tag, level):
ind = " "*level
name = tag["name"]; count = tag["count"]
mean = tag["mean"]; tag["median"]; stdev = tag["stdev"]
print(f"{ind}{name}")
print(f"{ind} Samples : {count}")
print(f"{ind} Mean : {prettyshorttime(mean)}")
print(f"{ind} Median : {prettyshorttime(median)}")
print(f"{ind} St.dev. : {prettyshorttime(stdev)}")
print("")
print("\nProfiler results:\n")
for tag_name in results:
tag = results[tag_name]
if tag["super"] == None:
print_results_recursive(tag, results)

View File

@ -1 +1 @@
__version__ = "0.7.7" __version__ = "0.7.8"