From ba0ef22559c68da3d91856c29fd91eb241c27b18 Mon Sep 17 00:00:00 2001 From: madrigal Date: Thu, 16 Oct 2025 13:53:22 -0400 Subject: [PATCH] Refactored everything, creating separate functions and iperf folders & making Communication & Relay classes --- communication.py | 383 +++++------------- {remote_iperf => functions}/__init__.py | 0 at_commands.py => functions/at_commands.py | 2 +- .../helper_functions.py | 0 functions/location.py | 97 +++++ .../sierra_commands.py | 2 +- iperf/__init__.py | 0 .../end_to_relay_client.py | 2 +- iperf/iperf_functions.py | 126 ++++++ .../relay_to_ground_iperf.py | 0 processing/post_process.py | 2 +- relay.py | 288 ++++++------- 12 files changed, 472 insertions(+), 430 deletions(-) rename {remote_iperf => functions}/__init__.py (100%) rename at_commands.py => functions/at_commands.py (99%) rename helper_functions.py => functions/helper_functions.py (100%) create mode 100644 functions/location.py rename sierra_commands.py => functions/sierra_commands.py (97%) create mode 100644 iperf/__init__.py rename {remote_iperf => iperf}/end_to_relay_client.py (97%) create mode 100644 iperf/iperf_functions.py rename {remote_iperf => iperf}/relay_to_ground_iperf.py (100%) diff --git a/communication.py b/communication.py index 3915121..e76484f 100644 --- a/communication.py +++ b/communication.py @@ -4,9 +4,7 @@ import subprocess import threading import time -import serial - -from at_commands import ( +from functions.at_commands import ( get_modem_cops, get_modem_cpol, get_modem_creg, @@ -17,117 +15,120 @@ from at_commands import ( get_modem_rsrp, get_modem_rsrq, get_modem_sinr, + set_configs, ) -from helper_functions import ( - calculate_distance, - normalize, - parse_lat_lon, - save_data_to_json, -) -from sierra_commands import get_modem_nr_info, get_modem_status - -# Globals -running = False # To control start/stop -base_location = None # The basestation location +from functions.helper_functions import save_data_to_json +from functions.location import get_current_location, set_base_location +from functions.sierra_commands import get_modem_nr_info, get_modem_status +from iperf.iperf_functions import collect_iperf -def read_gps_data( - port: str = "/dev/ttyACM0", baudrate: int = 9600, timeout: int = 5 -) -> dict: - """ - Reads GPS data from a u-blox 7 GPS/GLONASS device and returns it as a dictionary. +class Communication: + def __init__(self): + self.running = False + self.base_location = None - Args: - port (str): The serial port the GPS device is connected to. Default is '/dev/ttyACM0'. - baudrate (int): Baud rate for serial communication. Default is 9600. - timeout (int): Timeout in seconds for the serial port. Default is 5 seconds. + set_modem = ( + input( + "Do you want limit the modem to NR5G without roaming? (Not recommended if PLMN is not 001) (y/n )" + ) + .strip() + .lower() + ) + while set_modem not in ("y", "yes", "n", "no"): + set_modem = input("Your response must be y or n ").strip().lower() + if set_modem in ("y", "yes"): + print("Setting configs...") + set_configs() - Returns: - dict: A dictionary containing parsed GPS data. - """ - gps_data = {} - attempts = 0 + self.ip_address = ( + input("Enter the ip address (Default is 10.45.0.1): ").strip().lower() + or "10.45.0.1" + ) + self.folder_name = ( + input("Enter the log folder name (Default is oct_range_test): ") + .strip() + .lower() + or "oct_range_test" + ) + self.interval = int( + input( + "Enter the time interval (s) in between pings/modem data collection (Default is 5): " + ) + .strip() + .lower() + or 5 + ) + self.iperf_duration = int( + input("Enter the iperf test duration (s) (Default is 5): ").strip().lower() + or 5 + ) - try: - with serial.Serial(port, baudrate=baudrate, timeout=timeout) as ser: - while True: - # Read a line from the GPS device - line = ser.readline().decode("ascii", errors="ignore").strip() + foldername = f"data/{self.folder_name}" + self.filename = foldername + "/test_" + str(int(time.time())) + ".json" + os.makedirs(foldername, exist_ok=True) - # Filter for GGA or RMC sentences for relevant data - if line.startswith("$GPGGA"): # Global Positioning System Fix Data - parts = line.split(",") - gps_data["utc_time"] = parts[1] - gps_data["latitude"] = parse_lat_lon(parts[2], parts[3]) - gps_data["longitude"] = parse_lat_lon(parts[4], parts[5]) - gps_data["altitude"] = float(parts[9]) if parts[9] else None + def set_location(self): + self.base_location = set_base_location() + save_data_to_json(data=self.base_location, filename=self.filename) - # Count number of times GPGGA has been queried - attempts = attempts + 1 + def run_iperf(self): + collect_iperf( + filename=self.filename, + is_client=True, + server_ip=self.ip_address, + duration=self.iperf_duration, + timeout=(self.iperf_duration + 5), + base_location=self.base_location, + ) - # Stop after collecting sufficient data - if ( - gps_data.get("utc_time") - and gps_data.get("latitude") - and gps_data.get("longitude") - ): - break - elif attempts >= 3: - gps_data = {} - break - except serial.SerialException: - pass - except Exception as e: - print(f"Error: {e}") + # Collect and send data continuously from Quectel + def collect_data(self): + while self.running: + data = {} + data = get_current_location(dictionary=data) + data = get_modem_cops(dictionary=data) + data = get_modem_creg(dictionary=data) + data = get_modem_csq(dictionary=data) + data = get_modem_rsrp(dictionary=data) + data = get_modem_rsrq(dictionary=data) + data = get_modem_sinr(dictionary=data) + data = get_modem_cpol(dictionary=data) + data = get_modem_qnwcfg(dictionary=data) + data = get_modem_qnwinfo(dictionary=data) + data = get_modem_qspn(dictionary=data) + data = ping_basestation(dictionary=data, address=self.ip_address) + data["timestamp"] = time.time() - return gps_data + # Send to server + print(f"\nDistance: {data.get('distance')}") + print(f"Service: {data.get('network information')}") + print(f"Ping Time: {data.get(f'{self.ip_address}_ping_time')}") + print( + f"RSRP: {data.get('RSRP PRX')} {data.get('RSRP DRX')} " + f"{data.get('RSRP RX2')} {data.get('RSRP RX3')}" + ) + save_data_to_json(data=data, filename=self.filename) + time.sleep(self.interval) -def set_base_location(): - global base_location - base_location = {} + # Collect and send data continuously from Sierra + def collect_sierra_data(self): + while self.running: + data = {} + data = get_current_location(dictionary=data) + data = get_modem_nr_info(dictionary=data) + data = get_modem_status(dictionary=data) + data = ping_basestation(dictionary=data, address=self.ip_address) + data["timestamp"] = time.time() - try: - gps_data = read_gps_data() - base_location["latitude"] = gps_data["latitude"] - base_location["longitude"] = gps_data["longitude"] - if gps_data.get("altitude"): - base_location["altitude"] = gps_data["altitude"] + # Send to server + print(f"\nDistance: {data.get('distance')}") + print(f"Ping Time: {data.get('ping_time')}") + print(f"RSRP: {data.get('RSRP')}") - print("Base location found") - except KeyError: - print("Base location could not be found") - except Exception as e: - print(f"Error finding location: {e}") - - return base_location - - -def get_current_location(dictionary={}): - global base_location - - try: - gps_data = read_gps_data() - dictionary["latitude"] = gps_data["latitude"] - dictionary["longitude"] = gps_data["longitude"] - if gps_data.get("altitude"): - dictionary["altitude"] = gps_data["altitude"] - - if base_location and "latitude" in base_location: - dictionary["baseLatitude"] = base_location["latitude"] - dictionary["baseLongitude"] = base_location["longitude"] - if base_location.get("altitude"): - dictionary["baseAltitude"] = base_location["altitude"] - - dictionary["distance"] = calculate_distance(base_location, gps_data) - except KeyError: - print("Location could not be found") - dictionary["distance"] = "Unknown" - except Exception as e: - print(f"Error finding location: {e}") - dictionary["distance"] = "Error" - - return dictionary + save_data_to_json(data=data, filename=self.filename) + time.sleep(self.interval) # Ping base station @@ -152,165 +153,8 @@ def ping_basestation(address="10.45.0.1", name="10.45.0.1", dictionary={}) -> di return dictionary -# Run iperf test -def collect_iperf( - filename, - server_ip="10.45.0.1", - duration=10, - is_client=True, - stations="", -): - if is_client: - commands = [ - ["iperf3", "-c", server_ip, "-t", str(duration)], - ["iperf3", "-c", server_ip, "-t", str(duration), "-R"], - ] - else: - commands = [["iperf3", "-s"]] - - for command in commands: - try: - try: - location = read_gps_data() - if base_location: - start_distance = calculate_distance(base_location, location) - else: - start_distance = None - except: - print("Could not collect location") - start_distance = None - - # Run the command - result = subprocess.run( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True - ) - output = result.stdout - - # Check for errors - if result.returncode != 0: - print(f"Error running iperf3: {result.stderr}") - return result.stderr - - matches = re.findall( - r"\[\s*\d+\]\s+\d+\.\d+\-\d+\.\d+\s+sec\s+[\d.]+\s+\w+Bytes\s+([\d.]+)\s+(Kbits/sec|Mbits/sec)", - output, - ) - - if len(matches) >= 1: - # Normalize the last entries - last_value, last_unit = matches[-1] - sender_bitrate = normalize(float(last_value), last_unit) - - # Try to differentiate sender vs receiver - receiver_match = re.search(r"receiver", output, re.IGNORECASE) - sender_match = re.search(r"sender", output, re.IGNORECASE) - - if receiver_match and sender_match and len(matches) >= 2: - recv_value, recv_unit = matches[-1] - send_value, send_unit = matches[-2] - receiver_bitrate = normalize(float(recv_value), recv_unit) - sender_bitrate = normalize(float(send_value), send_unit) - else: - receiver_bitrate = sender_bitrate - - else: - bitrates = re.findall(r"(\d+\.\d+) Mbits/sec", output) - sender_bitrate = float(bitrates[-2]) - receiver_bitrate = float(bitrates[-1]) - - if "-R" in command: - test_type = "downlink" - else: - test_type = "uplink" - - data = { - "iperf_full": output, - "sender_bitrate": sender_bitrate, - "receiver_bitrate": receiver_bitrate, - "start_distance": start_distance, - "location": location, - "type": test_type, - "stations": stations, - } - - print(f"\n{server_ip} {test_type} IPERF complete") - print(f"IPERF sender bitrate: {sender_bitrate}") - print(f"IPERF receiver bitrate: {receiver_bitrate}") - - save_data_to_json(data=data, filename=filename) - time.sleep(0.25) - - except Exception as e: - print(f"iPerf Error: {e}") - - -# Collect and send data continuously -def collect_data(filename, address): - global base_location - global running - - while running: - data = {} - data = get_current_location(dictionary=data) - data = get_modem_cops(dictionary=data) - data = get_modem_creg(dictionary=data) - data = get_modem_csq(dictionary=data) - data = get_modem_rsrp(dictionary=data) - data = get_modem_rsrq(dictionary=data) - data = get_modem_sinr(dictionary=data) - data = get_modem_cpol(dictionary=data) - data = get_modem_qnwcfg(dictionary=data) - data = get_modem_qnwinfo(dictionary=data) - data = get_modem_qspn(dictionary=data) - data = ping_basestation(dictionary=data, address=address) - data["timestamp"] = time.time() - - # Send to server - print(f"\nDistance: {data.get('distance')}") - print(f"Service: {data.get('network information')}") - print(f"Ping Time: {data.get(f'{address}_ping_time')}") - print( - f"RSRP: {data.get('RSRP PRX')} {data.get('RSRP DRX')} {data.get('RSRP RX2')} {data.get('RSRP RX3')}" - ) - - save_data_to_json(data=data, filename=filename) - - time.sleep(5) - - -# Collect and send data continuously -def collect_sierra_data(filename): - global base_location - global running - - while running: - data = {} - data = get_current_location(dictionary=data) - data = get_modem_nr_info(dictionary=data) - data = get_modem_status(dictionary=data) - data = ping_basestation(dictionary=data) - data["timestamp"] = time.time() - - # Send to server - print(f"\nDistance: {data.get('distance')}") - print(f"Ping Time: {data.get('ping_time')}") - print(f"RSRP: {data.get('RSRP')}") - - save_data_to_json(data=data, filename=filename) - - time.sleep(5) - - -# Main function -def main(): - global running - address = "10.46.0.1" - foldername = "data/office_test_sept_19" - os.makedirs(foldername, exist_ok=True) - filename = foldername + "/test_" + str(int(time.time())) + ".json" - - # print("Setting configs...") - # set_configs() +if __name__ == "__main__": + modem = Communication() print( "Type 'l' to set basestation location, 'b' to begin, " @@ -318,24 +162,19 @@ def main(): ) while True: command = input("> ").strip().lower() - if command == "b" and not running: + if command == "b" and not modem.running: print("Starting data collection...") - running = True - threading.Thread(target=collect_data, args=(filename, address)).start() + modem.running = True + threading.Thread(target=modem.collect_data).start() elif command == "l": - base_location_data = set_base_location() - save_data_to_json(data=base_location_data, filename=filename) + modem.set_location() elif command == "i": - threading.Thread(target=collect_iperf, args=(filename, address)).start() - elif command == "s" and running: + threading.Thread(target=modem.run_iperf).start() + elif command == "s" and modem.running: print("Stopping data collection...") - running = False + modem.running = False elif command == "x": - running = False + modem.running = False break elif command not in ["l", "b", "s", "i", "x"]: print("Invalid command. Type 'l', 'b', 's', 'i', or 'x'.") - - -if __name__ == "__main__": - main() diff --git a/remote_iperf/__init__.py b/functions/__init__.py similarity index 100% rename from remote_iperf/__init__.py rename to functions/__init__.py diff --git a/at_commands.py b/functions/at_commands.py similarity index 99% rename from at_commands.py rename to functions/at_commands.py index 82955a9..6eb6b9c 100644 --- a/at_commands.py +++ b/functions/at_commands.py @@ -1,6 +1,6 @@ import serial -from helper_functions import comma_split, extract_numbers +from functions.helper_functions import comma_split, extract_numbers # For more info on the commands: # https://files.waveshare.com/upload/8/8a/Quectel_RG520N%26RG52xF%26RG530F%26RM520N%26RM530N_Series_AT_Commands_Manual_V1.0.0_Preliminary_20220812.pdf diff --git a/helper_functions.py b/functions/helper_functions.py similarity index 100% rename from helper_functions.py rename to functions/helper_functions.py diff --git a/functions/location.py b/functions/location.py new file mode 100644 index 0000000..4d3f461 --- /dev/null +++ b/functions/location.py @@ -0,0 +1,97 @@ +import serial + +from functions.helper_functions import calculate_distance, parse_lat_lon + + +def read_gps_data( + port: str = "/dev/ttyACM0", baudrate: int = 9600, timeout: int = 5 +) -> dict: + """ + Reads GPS data from a u-blox 7 GPS/GLONASS device and returns it as a dictionary. + + Args: + port (str): The serial port the GPS device is connected to. Default is '/dev/ttyACM0'. + baudrate (int): Baud rate for serial communication. Default is 9600. + timeout (int): Timeout in seconds for the serial port. Default is 5 seconds. + + Returns: + dict: A dictionary containing parsed GPS data. + """ + gps_data = {} + attempts = 0 + + try: + with serial.Serial(port, baudrate=baudrate, timeout=timeout) as ser: + while True: + # Read a line from the GPS device + line = ser.readline().decode("ascii", errors="ignore").strip() + + # Filter for GGA or RMC sentences for relevant data + if line.startswith("$GPGGA"): # Global Positioning System Fix Data + parts = line.split(",") + gps_data["utc_time"] = parts[1] + gps_data["latitude"] = parse_lat_lon(parts[2], parts[3]) + gps_data["longitude"] = parse_lat_lon(parts[4], parts[5]) + gps_data["altitude"] = float(parts[9]) if parts[9] else None + + # Count number of times GPGGA has been queried + attempts = attempts + 1 + + # Stop after collecting sufficient data + if ( + gps_data.get("utc_time") + and gps_data.get("latitude") + and gps_data.get("longitude") + ): + break + elif attempts >= 3: + gps_data = {} + break + except serial.SerialException: + pass + except Exception as e: + print(f"Error: {e}") + + return gps_data + + +def set_base_location(base_location={}): + try: + gps_data = read_gps_data() + base_location["latitude"] = gps_data["latitude"] + base_location["longitude"] = gps_data["longitude"] + if gps_data.get("altitude"): + base_location["altitude"] = gps_data["altitude"] + + print("Base location found") + except KeyError: + print("Base location could not be found") + except Exception as e: + print(f"Error finding location: {e}") + + return base_location + + +def get_current_location(dictionary={}, base_location={}): + try: + gps_data = read_gps_data() + dictionary["latitude"] = gps_data["latitude"] + dictionary["longitude"] = gps_data["longitude"] + if gps_data.get("altitude"): + dictionary["altitude"] = gps_data["altitude"] + + if base_location and "latitude" in base_location: + dictionary["baseLatitude"] = base_location["latitude"] + dictionary["baseLongitude"] = base_location["longitude"] + if base_location.get("altitude"): + dictionary["baseAltitude"] = base_location["altitude"] + + dictionary["distance"] = calculate_distance(base_location, gps_data) + except KeyError: + print("Location could not be found") + dictionary["distance"] = "Unknown" + except Exception as e: + print(f"Error finding location: {e}") + dictionary["distance"] = "Error" + + return dictionary diff --git a/sierra_commands.py b/functions/sierra_commands.py similarity index 97% rename from sierra_commands.py rename to functions/sierra_commands.py index e4a88a3..e1d056e 100644 --- a/sierra_commands.py +++ b/functions/sierra_commands.py @@ -1,6 +1,6 @@ import serial -from helper_functions import extract_numbers +from functions.helper_functions import extract_numbers # Fetch operational status diff --git a/iperf/__init__.py b/iperf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/remote_iperf/end_to_relay_client.py b/iperf/end_to_relay_client.py similarity index 97% rename from remote_iperf/end_to_relay_client.py rename to iperf/end_to_relay_client.py index c89db4a..0b3eb32 100644 --- a/remote_iperf/end_to_relay_client.py +++ b/iperf/end_to_relay_client.py @@ -1,7 +1,7 @@ import re import socket -from helper_functions import normalize, save_data_to_json +from functions.helper_functions import normalize, save_data_to_json RELAY_IP = "10.45.0.1" # gNodeB IP of relay PORT = 5005 diff --git a/iperf/iperf_functions.py b/iperf/iperf_functions.py new file mode 100644 index 0000000..d15a368 --- /dev/null +++ b/iperf/iperf_functions.py @@ -0,0 +1,126 @@ +import re +import subprocess +import time + +from functions.helper_functions import calculate_distance, normalize, save_data_to_json +from functions.location import read_gps_data + + +def get_location(base_location): + try: + location = read_gps_data() + if base_location: + start_distance = calculate_distance(base_location, location) + else: + start_distance = None + except Exception as e: + print(f"Could not collect location: {e}") + start_distance = None + + return location, start_distance + + +def parse_iperf_output(output): + if output == "": + return None, None + + matches = re.findall( + r"\[\s*\d+\]\s+\d+\.\d+\-\d+\.\d+\s+sec\s+[\d.]+\s+\w+Bytes\s+([\d.]+)\s+(Kbits/sec|Mbits/sec)", + output, + ) + + if len(matches) >= 1: + # Normalize the last entries + last_value, last_unit = matches[-1] + sender_bitrate = normalize(float(last_value), last_unit) + + # Try to differentiate sender vs receiver + receiver_match = re.search(r"receiver", output, re.IGNORECASE) + sender_match = re.search(r"sender", output, re.IGNORECASE) + + if receiver_match and sender_match and len(matches) >= 2: + recv_value, recv_unit = matches[-1] + send_value, send_unit = matches[-2] + receiver_bitrate = normalize(float(recv_value), recv_unit) + sender_bitrate = normalize(float(send_value), send_unit) + else: + receiver_bitrate = sender_bitrate + + else: + bitrates = re.findall(r"(\d+\.\d+) Mbits/sec", output) + sender_bitrate = float(bitrates[-2]) + receiver_bitrate = float(bitrates[-1]) + + return sender_bitrate, receiver_bitrate + + +# Run iperf test +def collect_iperf( + filename, + is_client=True, + server_ip="10.45.0.1", + stations="", + duration=10, + timeout=20, + base_location=None, +): + if is_client: + commands = [ + ["iperf3", "-c", server_ip, "-t", str(duration)], + ["iperf3", "-c", server_ip, "-t", str(duration), "-R"], + ] + else: + commands = [["iperf3", "-s"]] + + for command in commands: + try: + location, start_distance = get_location(base_location=base_location) + + # Run iperf with timeout protection + try: + result = subprocess.run( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=timeout, + ) + output = result.stdout + + # Check for errors + if result.returncode != 0: + print(f"Error running iperf3: {result.stderr}") + output = "" + + except subprocess.TimeoutExpired: + print(f"iPerf test timed out after {timeout} seconds.") + output = "" + + sender_bitrate, receiver_bitrate = parse_iperf_output(output) + + if "-R" in command: + test_type = "downlink" + else: + test_type = "uplink" + + data = { + "iperf_full": output, + "sender_bitrate": sender_bitrate, + "receiver_bitrate": receiver_bitrate, + "start_distance": start_distance, + "location": location, + "type": test_type, + "stations": stations, + } + if output == "": + data["error"] = f"Test exceeded timeout of {timeout}s" + else: + print(f"\n{server_ip} {test_type} IPERF complete") + print(f"IPERF sender bitrate: {sender_bitrate}") + print(f"IPERF receiver bitrate: {receiver_bitrate}") + + save_data_to_json(data=data, filename=filename) + time.sleep(0.25) + + except Exception as e: + print(f"iPerf Error: {e}") diff --git a/remote_iperf/relay_to_ground_iperf.py b/iperf/relay_to_ground_iperf.py similarity index 100% rename from remote_iperf/relay_to_ground_iperf.py rename to iperf/relay_to_ground_iperf.py diff --git a/processing/post_process.py b/processing/post_process.py index 5a34437..f2b19ce 100644 --- a/processing/post_process.py +++ b/processing/post_process.py @@ -1,7 +1,7 @@ import json import re -from helper_functions import calculate_distance +from functions.helper_functions import calculate_distance def get_data_lists(data, entry_type, default_diconnect): diff --git a/relay.py b/relay.py index 2731337..77e2896 100644 --- a/relay.py +++ b/relay.py @@ -2,7 +2,8 @@ import os import threading import time -from at_commands import ( +from communication import ping_basestation +from functions.at_commands import ( get_modem_cops, get_modem_cpol, get_modem_creg, @@ -15,160 +16,146 @@ from at_commands import ( get_modem_sinr, set_configs, ) -from communication import ( - collect_iperf, - get_current_location, - ping_basestation, - set_base_location, -) -from helper_functions import save_data_to_json - -# Globals -running = False # To control start/stop +from functions.helper_functions import save_data_to_json +from functions.location import get_current_location, set_base_location +from iperf.iperf_functions import collect_iperf -def calculate_ping( - dictionary: dict, base_address: str, relay_address: str, name: str -) -> dict: - try: - base_ping = float(dictionary[f"{base_address}_ping_time"]) - relay_ping = float(dictionary[f"{relay_address}_ping_time"]) - calculated_ping = base_ping - relay_ping - dictionary[f"{name}_c_ping_time"] = calculated_ping - except Exception as e: - dictionary[f"{name}_c_ping error"] = f"{e}" +class Relay: + def __init__(self): + self.running = False + self.base_location = None - return dictionary + set_modem = ( + input( + "Do you want limit the modem to NR5G without roaming? (Not recommended if PLMN is not 001) (y/n )" + ) + .strip() + .lower() + ) + while set_modem not in ("y", "yes", "n", "no"): + set_modem = input("Your response must be y or n ").strip().lower() + if set_modem in ("y", "yes"): + print("Setting configs...") + set_configs() + self.base_address = ( + input("Enter the base station (ground) ip address (Default is 10.46.0.1): ") + .strip() + .lower() + or "10.46.0.1" + ) + self.relay_address = ( + input("Enter the relay station ip address (Default is 10.45.0.1): ") + .strip() + .lower() + or "10.45.0.1" + ) + self.folder_name = ( + input("Enter the log folder name (Default is boat_relay_oct_9): ") + .strip() + .lower() + or "boat_relay_oct_9" + ) + self.interval = int( + input( + "Enter the time interval (s) in between pings/modem data collection (Default is 5): " + ) + .strip() + .lower() + or 5 + ) + self.iperf_duration = int( + input("Enter the iperf test duration (s) (Default is 5): ").strip().lower() + or 5 + ) -def relay_iperf( - filename: str, base_address: str, relay_address: str, duration: str -) -> None: - try: + foldername = f"data/{self.folder_name}" + self.filename = foldername + "/test_" + str(int(time.time())) + ".json" + os.makedirs(foldername, exist_ok=True) + + def set_location(self): + self.base_location = set_base_location() + save_data_to_json(data=self.base_location, filename=self.filename) + + def calculate_ping( + self, dictionary: dict, base_address: str, relay_address: str, name: str + ) -> dict: + try: + base_ping = float(dictionary[f"{base_address}_ping_time"]) + relay_ping = float(dictionary[f"{relay_address}_ping_time"]) + calculated_ping = base_ping - relay_ping + dictionary[f"{name}_c_ping_time"] = calculated_ping + except Exception as e: + dictionary[f"{name}_c_ping error"] = f"{e}" + + return dictionary + + # Run iPerf test from End to Ground, then End to Relay + def relay_iperf(self) -> None: collect_iperf( - filename=filename, server_ip=base_address, duration=duration, stations="eg" + filename=self.filename, + is_client=True, + server_ip=self.base_address, + stations="eg", + duration=self.iperf_duration, + timeout=(self.iperf_duration + 5), + base_location=self.base_location, ) - except Exception as e: - print(f"iPerf error: {e}") - data = { - "iperf_full": f"Error: {e}", - "stations": "eg", - } - save_data_to_json(data=data, filename=filename) - try: collect_iperf( - filename=filename, server_ip=relay_address, duration=duration, stations="er" + filename=self.filename, + is_client=True, + server_ip=self.relay_address, + stations="er", + duration=self.iperf_duration, + timeout=(self.iperf_duration + 5), + base_location=self.base_location, ) - except Exception as e: - print(f"iPerf error: {e}") - data = { - "iperf_full": f"Error: {e}", - "stations": "er", - } - save_data_to_json(data=data, filename=filename) + # Collect and send data continuously + def collect_data(self) -> None: + while self.running: + data = {} + data = get_current_location(dictionary=data) + data = get_modem_cops(dictionary=data) + data = get_modem_creg(dictionary=data) + data = get_modem_csq(dictionary=data) + data = get_modem_rsrp(dictionary=data) + data = get_modem_rsrq(dictionary=data) + data = get_modem_sinr(dictionary=data) + data = get_modem_cpol(dictionary=data) + data = get_modem_qnwcfg(dictionary=data) + data = get_modem_qnwinfo(dictionary=data) + data = get_modem_qspn(dictionary=data) + data = ping_basestation( + dictionary=data, name="eg", address=self.base_address + ) # edge to ground + data = ping_basestation( + dictionary=data, name="er", address=self.relay_address + ) # edge to relay + data = self.calculate_ping( + dictionary=data, + base_address="eg", + relay_address="er", + name="rg", + ) # relay to ground + data["timestamp"] = time.time() -# Collect and send data continuously -def collect_data( - filename: str, base_address: str, relay_address: str, interval: int -) -> None: - global running + # Send to server + print(f"\nPing Time: {data.get('ping_time')}") + print( + f"RSRP: PRX: {data.get('RSRP PRX')} DRX: {data.get('RSRP DRX')} " + f"RX2: {data.get('RSRP RX2')} RX3: {data.get('RSRP RX3')}" + ) + print(f"Service: {data.get('network information')}") - while running: - data = {} - data = get_current_location(dictionary=data) - data = get_modem_cops(dictionary=data) - data = get_modem_creg(dictionary=data) - data = get_modem_csq(dictionary=data) - data = get_modem_rsrp(dictionary=data) - data = get_modem_rsrq(dictionary=data) - data = get_modem_sinr(dictionary=data) - data = get_modem_cpol(dictionary=data) - data = get_modem_qnwcfg(dictionary=data) - data = get_modem_qnwinfo(dictionary=data) - data = get_modem_qspn(dictionary=data) - data = ping_basestation( - dictionary=data, name="eg", address=base_address - ) # edge to ground - data = ping_basestation( - dictionary=data, name="er", address=relay_address - ) # edge to relay - data = calculate_ping( - dictionary=data, - base_address="eg", - relay_address="er", - name="rg", - ) # relay to ground - data["timestamp"] = time.time() + save_data_to_json(data=data, filename=self.filename) - # Send to server - print(f"\nPing Time: {data.get('ping_time')}") - print( - f"RSRP: PRX: {data.get('RSRP PRX')} DRX: {data.get('RSRP DRX')} \ - RX2: {data.get('RSRP RX2')} RX3: {data.get('RSRP RX3')}" - ) - print(f"Service: {data.get('network information')}") - - save_data_to_json(data=data, filename=filename) - - time.sleep(interval) - - -def set_parameters(): - set_modem = ( - input( - "Do you want limit the modem to NR5G without roaming? (Not recommended if PLMN is not 001) (y/n )" - ) - .strip() - .lower() - ) - while set_modem not in ("y", "yes", "n", "no"): - set_modem = input("Your response must be y or n ").strip().lower() - if set_modem in ("y", "yes"): - print("Setting configs...") - set_configs() - - base_address = ( - input("Enter the base station (ground) ip address (Default is 10.46.0.1): ") - .strip() - .lower() - or "10.46.0.1" - ) - relay_address = ( - input("Enter the relay station ip address (Default is 10.45.0.1): ") - .strip() - .lower() - or "10.45.0.1" - ) - folder_name = ( - input("Enter the log folder name (Default is boat_relay_oct_9): ") - .strip() - .lower() - or "boat_relay_oct_9" - ) - interval = int( - input( - "Enter the time interval (s) in between pings/modem data collection (Default is 5): " - ) - .strip() - .lower() - or 5 - ) - iperf_duration = int( - input("Enter the iperf test duration (s) (Default is 5): ").strip().lower() or 5 - ) - - return base_address, relay_address, folder_name, interval, iperf_duration + time.sleep(self.interval) if __name__ == "__main__": - base_address, relay_address, folder_name, interval, iperf_duration = ( - set_parameters() - ) - - foldername = f"data/{folder_name}" - filename = foldername + "/test_" + str(int(time.time())) + ".json" - os.makedirs(foldername, exist_ok=True) + relay = Relay() print( "Type 'l' to set basestation location, 'b' to begin, " @@ -176,26 +163,19 @@ if __name__ == "__main__": ) while True: command = input("> ").strip().lower() - if command == "b" and not running: + if command == "b" and not relay.running: print("Starting data collection...") - running = True - threading.Thread( - target=collect_data, - args=(filename, base_address, relay_address, interval), - ).start() + relay.running = True + threading.Thread(target=relay.collect_data).start() elif command == "l": - base_location_data = set_base_location() - save_data_to_json(data=base_location_data, filename=filename) + relay.set_location() elif command == "i": - threading.Thread( - target=relay_iperf, - args=(filename, base_address, relay_address, iperf_duration), - ).start() - elif command == "s" and running: + threading.Thread(target=relay.relay_iperf).start() + elif command == "s" and relay.running: print("Stopping data collection...") - running = False + relay.running = False elif command == "x": - running = False + relay.running = False break elif command not in ["l", "b", "s", "i", "x"]: print("Invalid command. Type 'l', 'b', 's', 'i', or 'x'.")