From 1cf6570c2d1e570d940ecabf932dfcea25a2609e Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 12 Oct 2021 16:34:17 +0200 Subject: [PATCH] Added RSSI and SNR reporting to packets on supported interfaces --- RNS/Interfaces/RNodeInterface.py | 2 ++ RNS/Packet.py | 17 +++++++++++---- RNS/Reticulum.py | 36 ++++++++++++++++++++++++++++++++ RNS/Transport.py | 25 +++++++++++++++++++++- RNS/Utilities/rnprobe.py | 22 ++++++++++++++++++- RNS/_version.py | 2 +- 6 files changed, 97 insertions(+), 7 deletions(-) diff --git a/RNS/Interfaces/RNodeInterface.py b/RNS/Interfaces/RNodeInterface.py index 4303f40..86334d2 100644 --- a/RNS/Interfaces/RNodeInterface.py +++ b/RNS/Interfaces/RNodeInterface.py @@ -283,6 +283,8 @@ class RNodeInterface(Interface): def processIncoming(self, data): self.rxb += len(data) self.owner.inbound(data, self) + self.r_stat_rssi = None + self.r_stat_snr = None def processOutgoing(self,data): diff --git a/RNS/Packet.py b/RNS/Packet.py index 9d3fa8a..4f28166 100755 --- a/RNS/Packet.py +++ b/RNS/Packet.py @@ -113,6 +113,8 @@ class Packet: self.attached_interface = attached_interface self.receiving_interface = None + self.rssi = None + self.snr = None def get_packed_flags(self): if self.context == Packet.LRPROOF: @@ -328,6 +330,7 @@ class PacketReceipt: self.destination = packet.destination self.callbacks = PacketReceiptCallbacks() self.concluded_at = None + self.proof_packet = None if packet.destination.type == RNS.Destination.LINK: self.timeout = packet.destination.rtt * packet.destination.traffic_timeout_factor @@ -344,12 +347,12 @@ class PacketReceipt: # Validate a proof packet def validate_proof_packet(self, proof_packet): if hasattr(proof_packet, "link") and proof_packet.link: - return self.validate_link_proof(proof_packet.data, proof_packet.link) + return self.validate_link_proof(proof_packet.data, proof_packet.link, proof_packet) else: - return self.validate_proof(proof_packet.data) + return self.validate_proof(proof_packet.data, proof_packet) # Validate a raw proof for a link - def validate_link_proof(self, proof, link): + def validate_link_proof(self, proof, link, proof_packet=None): # TODO: Hardcoded as explicit proofs for now if True or len(proof) == PacketReceipt.EXPL_LENGTH: # This is an explicit proof @@ -361,6 +364,8 @@ class PacketReceipt: self.status = PacketReceipt.DELIVERED self.proved = True self.concluded_at = time.time() + self.proof_packet = proof_packet + if self.callbacks.delivery != None: self.callbacks.delivery(self) return True @@ -388,7 +393,7 @@ class PacketReceipt: return False # Validate a raw proof - def validate_proof(self, proof): + def validate_proof(self, proof, proof_packet=None): if len(proof) == PacketReceipt.EXPL_LENGTH: # This is an explicit proof proof_hash = proof[:RNS.Identity.HASHLENGTH//8] @@ -399,6 +404,8 @@ class PacketReceipt: self.status = PacketReceipt.DELIVERED self.proved = True self.concluded_at = time.time() + self.proof_packet = proof_packet + if self.callbacks.delivery != None: self.callbacks.delivery(self) return True @@ -417,6 +424,8 @@ class PacketReceipt: self.status = PacketReceipt.DELIVERED self.proved = True self.concluded_at = time.time() + self.proof_packet = proof_packet + if self.callbacks.delivery != None: self.callbacks.delivery(self) return True diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index c65e0d2..2d03d3e 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -511,6 +511,12 @@ class Reticulum: if path == "next_hop": rpc_connection.send(self.get_next_hop(call["destination_hash"])) + if path == "packet_rssi": + rpc_connection.send(self.get_packet_rssi(call["packet_hash"])) + + if path == "packet_snr": + rpc_connection.send(self.get_packet_snr(call["packet_hash"])) + rpc_connection.close() except Exception as e: RNS.log("An error ocurred while handling RPC call from local client: "+str(e), RNS.LOG_ERROR) @@ -545,6 +551,7 @@ class Reticulum: rpc_connection.send({"get": "next_hop_if_name", "destination_hash": destination}) response = rpc_connection.recv() return response + else: return str(RNS.Transport.next_hop_interface(destination)) @@ -554,9 +561,38 @@ class Reticulum: rpc_connection.send({"get": "next_hop", "destination_hash": destination}) response = rpc_connection.recv() return response + else: return RNS.Transport.next_hop(destination) + def get_packet_rssi(self, packet_hash): + if self.is_connected_to_shared_instance: + rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) + rpc_connection.send({"get": "packet_rssi", "packet_hash": packet_hash}) + response = rpc_connection.recv() + return response + + else: + for entry in RNS.Transport.local_client_rssi_cache: + if entry[0] == packet_hash: + return entry[1] + + return None + + def get_packet_snr(self, packet_hash): + if self.is_connected_to_shared_instance: + rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) + rpc_connection.send({"get": "packet_snr", "packet_hash": packet_hash}) + response = rpc_connection.recv() + return response + + else: + for entry in RNS.Transport.local_client_snr_cache: + if entry[0] == packet_hash: + return entry[1] + + return None + @staticmethod def should_use_implicit_proof(): diff --git a/RNS/Transport.py b/RNS/Transport.py index 1676de7..4486910 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -76,6 +76,10 @@ class Transport: # Reticulum instance local_client_interfaces = [] + local_client_rssi_cache = [] + local_client_snr_cache = [] + LOCAL_CLIENT_CACHE_MAXSIZE = 512 + jobs_locked = False jobs_running = False job_interval = 0.250 @@ -583,10 +587,29 @@ class Transport: packet.receiving_interface = interface packet.hops += 1 - if len(Transport.local_client_interfaces) > 0: + if interface != None: + if hasattr(interface, "r_stat_rssi"): + if interface.r_stat_rssi != None: + packet.rssi = interface.r_stat_rssi + if len(Transport.local_client_interfaces) > 0: + Transport.local_client_rssi_cache.append([packet.packet_hash, packet.rssi]) + while len(Transport.local_client_rssi_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE: + Transport.local_client_rssi_cache.pop() + + if hasattr(interface, "r_stat_snr"): + if interface.r_stat_rssi != None: + packet.snr = interface.r_stat_snr + if len(Transport.local_client_interfaces) > 0: + Transport.local_client_snr_cache.append([packet.packet_hash, packet.snr]) + + while len(Transport.local_client_snr_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE: + Transport.local_client_snr_cache.pop() + + if len(Transport.local_client_interfaces) > 0: if Transport.is_local_client_interface(interface): packet.hops -= 1 + elif Transport.interface_to_shared_instance(interface): packet.hops -= 1 diff --git a/RNS/Utilities/rnprobe.py b/RNS/Utilities/rnprobe.py index c78fdfb..b22dd5c 100644 --- a/RNS/Utilities/rnprobe.py +++ b/RNS/Utilities/rnprobe.py @@ -101,11 +101,31 @@ def program_setup(configdir, destination_hexhash, size=DEFAULT_PROBE_SIZE, full_ rtt = round(rtt*1000, 3) rttstring = str(rtt)+" milliseconds" + reception_stats = "" + if reticulum.is_connected_to_shared_instance: + reception_rssi = reticulum.get_packet_rssi(receipt.proof_packet.packet_hash) + reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash) + + if reception_rssi != None: + reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" + + if reception_snr != None: + reception_stats += " [SNR "+str(reception_snr)+" dBm]" + + else: + if receipt.proof_packet != None: + if receipt.proof_packet.rssi != None: + reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]" + + if receipt.proof_packet.snr != None: + reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dBm]" + print( "Valid reply received from "+ RNS.prettyhexrep(receipt.destination.hash)+ "\nRound-trip time is "+rttstring+ - " over "+str(hops)+" hop"+ms + " over "+str(hops)+" hop"+ms+ + reception_stats ) diff --git a/RNS/_version.py b/RNS/_version.py index 9978c2e..467ab30 100644 --- a/RNS/_version.py +++ b/RNS/_version.py @@ -1 +1 @@ -__version__ = "0.2.8" \ No newline at end of file +__version__ = "0.2.9" \ No newline at end of file