Refactored everything, creating separate functions and iperf folders & making Communication & Relay classes
This commit is contained in:
parent
406a92b66f
commit
ba0ef22559
387
communication.py
387
communication.py
|
|
@ -4,9 +4,7 @@ import subprocess
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import serial
|
from functions.at_commands import (
|
||||||
|
|
||||||
from at_commands import (
|
|
||||||
get_modem_cops,
|
get_modem_cops,
|
||||||
get_modem_cpol,
|
get_modem_cpol,
|
||||||
get_modem_creg,
|
get_modem_creg,
|
||||||
|
|
@ -17,117 +15,120 @@ from at_commands import (
|
||||||
get_modem_rsrp,
|
get_modem_rsrp,
|
||||||
get_modem_rsrq,
|
get_modem_rsrq,
|
||||||
get_modem_sinr,
|
get_modem_sinr,
|
||||||
|
set_configs,
|
||||||
)
|
)
|
||||||
from helper_functions import (
|
from functions.helper_functions import save_data_to_json
|
||||||
calculate_distance,
|
from functions.location import get_current_location, set_base_location
|
||||||
normalize,
|
from functions.sierra_commands import get_modem_nr_info, get_modem_status
|
||||||
parse_lat_lon,
|
from iperf.iperf_functions import collect_iperf
|
||||||
save_data_to_json,
|
|
||||||
|
|
||||||
|
class Communication:
|
||||||
|
def __init__(self):
|
||||||
|
self.running = False
|
||||||
|
self.base_location = None
|
||||||
|
|
||||||
|
set_modem = (
|
||||||
|
input(
|
||||||
|
"Do you want limit the modem to NR5G without roaming? (Not recommended if PLMN is not 001) (y/n )"
|
||||||
)
|
)
|
||||||
from sierra_commands import get_modem_nr_info, get_modem_status
|
.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()
|
||||||
|
|
||||||
# Globals
|
self.ip_address = (
|
||||||
running = False # To control start/stop
|
input("Enter the ip address (Default is 10.45.0.1): ").strip().lower()
|
||||||
base_location = None # The basestation location
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
foldername = f"data/{self.folder_name}"
|
||||||
|
self.filename = foldername + "/test_" + str(int(time.time())) + ".json"
|
||||||
|
os.makedirs(foldername, exist_ok=True)
|
||||||
|
|
||||||
def read_gps_data(
|
def set_location(self):
|
||||||
port: str = "/dev/ttyACM0", baudrate: int = 9600, timeout: int = 5
|
self.base_location = set_base_location()
|
||||||
) -> dict:
|
save_data_to_json(data=self.base_location, filename=self.filename)
|
||||||
"""
|
|
||||||
Reads GPS data from a u-blox 7 GPS/GLONASS device and returns it as a dictionary.
|
|
||||||
|
|
||||||
Args:
|
def run_iperf(self):
|
||||||
port (str): The serial port the GPS device is connected to. Default is '/dev/ttyACM0'.
|
collect_iperf(
|
||||||
baudrate (int): Baud rate for serial communication. Default is 9600.
|
filename=self.filename,
|
||||||
timeout (int): Timeout in seconds for the serial port. Default is 5 seconds.
|
is_client=True,
|
||||||
|
server_ip=self.ip_address,
|
||||||
|
duration=self.iperf_duration,
|
||||||
|
timeout=(self.iperf_duration + 5),
|
||||||
|
base_location=self.base_location,
|
||||||
|
)
|
||||||
|
|
||||||
Returns:
|
# Collect and send data continuously from Quectel
|
||||||
dict: A dictionary containing parsed GPS data.
|
def collect_data(self):
|
||||||
"""
|
while self.running:
|
||||||
gps_data = {}
|
data = {}
|
||||||
attempts = 0
|
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()
|
||||||
|
|
||||||
try:
|
# Send to server
|
||||||
with serial.Serial(port, baudrate=baudrate, timeout=timeout) as ser:
|
print(f"\nDistance: {data.get('distance')}")
|
||||||
while True:
|
print(f"Service: {data.get('network information')}")
|
||||||
# Read a line from the GPS device
|
print(f"Ping Time: {data.get(f'{self.ip_address}_ping_time')}")
|
||||||
line = ser.readline().decode("ascii", errors="ignore").strip()
|
print(
|
||||||
|
f"RSRP: {data.get('RSRP PRX')} {data.get('RSRP DRX')} "
|
||||||
|
f"{data.get('RSRP RX2')} {data.get('RSRP RX3')}"
|
||||||
|
)
|
||||||
|
|
||||||
# Filter for GGA or RMC sentences for relevant data
|
save_data_to_json(data=data, filename=self.filename)
|
||||||
if line.startswith("$GPGGA"): # Global Positioning System Fix Data
|
time.sleep(self.interval)
|
||||||
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
|
# Collect and send data continuously from Sierra
|
||||||
attempts = attempts + 1
|
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()
|
||||||
|
|
||||||
# Stop after collecting sufficient data
|
# Send to server
|
||||||
if (
|
print(f"\nDistance: {data.get('distance')}")
|
||||||
gps_data.get("utc_time")
|
print(f"Ping Time: {data.get('ping_time')}")
|
||||||
and gps_data.get("latitude")
|
print(f"RSRP: {data.get('RSRP')}")
|
||||||
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
|
save_data_to_json(data=data, filename=self.filename)
|
||||||
|
time.sleep(self.interval)
|
||||||
|
|
||||||
def set_base_location():
|
|
||||||
global 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={}):
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
# Ping base station
|
# Ping base station
|
||||||
|
|
@ -152,165 +153,8 @@ def ping_basestation(address="10.45.0.1", name="10.45.0.1", dictionary={}) -> di
|
||||||
return dictionary
|
return dictionary
|
||||||
|
|
||||||
|
|
||||||
# Run iperf test
|
if __name__ == "__main__":
|
||||||
def collect_iperf(
|
modem = Communication()
|
||||||
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()
|
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"Type 'l' to set basestation location, 'b' to begin, "
|
"Type 'l' to set basestation location, 'b' to begin, "
|
||||||
|
|
@ -318,24 +162,19 @@ def main():
|
||||||
)
|
)
|
||||||
while True:
|
while True:
|
||||||
command = input("> ").strip().lower()
|
command = input("> ").strip().lower()
|
||||||
if command == "b" and not running:
|
if command == "b" and not modem.running:
|
||||||
print("Starting data collection...")
|
print("Starting data collection...")
|
||||||
running = True
|
modem.running = True
|
||||||
threading.Thread(target=collect_data, args=(filename, address)).start()
|
threading.Thread(target=modem.collect_data).start()
|
||||||
elif command == "l":
|
elif command == "l":
|
||||||
base_location_data = set_base_location()
|
modem.set_location()
|
||||||
save_data_to_json(data=base_location_data, filename=filename)
|
|
||||||
elif command == "i":
|
elif command == "i":
|
||||||
threading.Thread(target=collect_iperf, args=(filename, address)).start()
|
threading.Thread(target=modem.run_iperf).start()
|
||||||
elif command == "s" and running:
|
elif command == "s" and modem.running:
|
||||||
print("Stopping data collection...")
|
print("Stopping data collection...")
|
||||||
running = False
|
modem.running = False
|
||||||
elif command == "x":
|
elif command == "x":
|
||||||
running = False
|
modem.running = False
|
||||||
break
|
break
|
||||||
elif command not in ["l", "b", "s", "i", "x"]:
|
elif command not in ["l", "b", "s", "i", "x"]:
|
||||||
print("Invalid command. Type 'l', 'b', 's', 'i', or 'x'.")
|
print("Invalid command. Type 'l', 'b', 's', 'i', or 'x'.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import serial
|
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:
|
# 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
|
# https://files.waveshare.com/upload/8/8a/Quectel_RG520N%26RG52xF%26RG530F%26RM520N%26RM530N_Series_AT_Commands_Manual_V1.0.0_Preliminary_20220812.pdf
|
||||||
97
functions/location.py
Normal file
97
functions/location.py
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
from helper_functions import extract_numbers
|
from functions.helper_functions import extract_numbers
|
||||||
|
|
||||||
|
|
||||||
# Fetch operational status
|
# Fetch operational status
|
||||||
0
iperf/__init__.py
Normal file
0
iperf/__init__.py
Normal file
|
|
@ -1,7 +1,7 @@
|
||||||
import re
|
import re
|
||||||
import socket
|
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
|
RELAY_IP = "10.45.0.1" # gNodeB IP of relay
|
||||||
PORT = 5005
|
PORT = 5005
|
||||||
126
iperf/iperf_functions.py
Normal file
126
iperf/iperf_functions.py
Normal file
|
|
@ -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}")
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from helper_functions import calculate_distance
|
from functions.helper_functions import calculate_distance
|
||||||
|
|
||||||
|
|
||||||
def get_data_lists(data, entry_type, default_diconnect):
|
def get_data_lists(data, entry_type, default_diconnect):
|
||||||
|
|
|
||||||
234
relay.py
234
relay.py
|
|
@ -2,7 +2,8 @@ import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from at_commands import (
|
from communication import ping_basestation
|
||||||
|
from functions.at_commands import (
|
||||||
get_modem_cops,
|
get_modem_cops,
|
||||||
get_modem_cpol,
|
get_modem_cpol,
|
||||||
get_modem_creg,
|
get_modem_creg,
|
||||||
|
|
@ -15,106 +16,16 @@ from at_commands import (
|
||||||
get_modem_sinr,
|
get_modem_sinr,
|
||||||
set_configs,
|
set_configs,
|
||||||
)
|
)
|
||||||
from communication import (
|
from functions.helper_functions import save_data_to_json
|
||||||
collect_iperf,
|
from functions.location import get_current_location, set_base_location
|
||||||
get_current_location,
|
from iperf.iperf_functions import collect_iperf
|
||||||
ping_basestation,
|
|
||||||
set_base_location,
|
|
||||||
)
|
|
||||||
from helper_functions import save_data_to_json
|
|
||||||
|
|
||||||
# Globals
|
|
||||||
running = False # To control start/stop
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_ping(
|
class Relay:
|
||||||
dictionary: dict, base_address: str, relay_address: str, name: str
|
def __init__(self):
|
||||||
) -> dict:
|
self.running = False
|
||||||
try:
|
self.base_location = None
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def relay_iperf(
|
|
||||||
filename: str, base_address: str, relay_address: str, duration: str
|
|
||||||
) -> None:
|
|
||||||
try:
|
|
||||||
collect_iperf(
|
|
||||||
filename=filename, server_ip=base_address, duration=duration, stations="eg"
|
|
||||||
)
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
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(
|
|
||||||
filename: str, base_address: str, relay_address: str, interval: int
|
|
||||||
) -> None:
|
|
||||||
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, 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()
|
|
||||||
|
|
||||||
# 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 = (
|
set_modem = (
|
||||||
input(
|
input(
|
||||||
"Do you want limit the modem to NR5G without roaming? (Not recommended if PLMN is not 001) (y/n )"
|
"Do you want limit the modem to NR5G without roaming? (Not recommended if PLMN is not 001) (y/n )"
|
||||||
|
|
@ -128,25 +39,25 @@ def set_parameters():
|
||||||
print("Setting configs...")
|
print("Setting configs...")
|
||||||
set_configs()
|
set_configs()
|
||||||
|
|
||||||
base_address = (
|
self.base_address = (
|
||||||
input("Enter the base station (ground) ip address (Default is 10.46.0.1): ")
|
input("Enter the base station (ground) ip address (Default is 10.46.0.1): ")
|
||||||
.strip()
|
.strip()
|
||||||
.lower()
|
.lower()
|
||||||
or "10.46.0.1"
|
or "10.46.0.1"
|
||||||
)
|
)
|
||||||
relay_address = (
|
self.relay_address = (
|
||||||
input("Enter the relay station ip address (Default is 10.45.0.1): ")
|
input("Enter the relay station ip address (Default is 10.45.0.1): ")
|
||||||
.strip()
|
.strip()
|
||||||
.lower()
|
.lower()
|
||||||
or "10.45.0.1"
|
or "10.45.0.1"
|
||||||
)
|
)
|
||||||
folder_name = (
|
self.folder_name = (
|
||||||
input("Enter the log folder name (Default is boat_relay_oct_9): ")
|
input("Enter the log folder name (Default is boat_relay_oct_9): ")
|
||||||
.strip()
|
.strip()
|
||||||
.lower()
|
.lower()
|
||||||
or "boat_relay_oct_9"
|
or "boat_relay_oct_9"
|
||||||
)
|
)
|
||||||
interval = int(
|
self.interval = int(
|
||||||
input(
|
input(
|
||||||
"Enter the time interval (s) in between pings/modem data collection (Default is 5): "
|
"Enter the time interval (s) in between pings/modem data collection (Default is 5): "
|
||||||
)
|
)
|
||||||
|
|
@ -154,21 +65,97 @@ def set_parameters():
|
||||||
.lower()
|
.lower()
|
||||||
or 5
|
or 5
|
||||||
)
|
)
|
||||||
iperf_duration = int(
|
self.iperf_duration = int(
|
||||||
input("Enter the iperf test duration (s) (Default is 5): ").strip().lower() or 5
|
input("Enter the iperf test duration (s) (Default is 5): ").strip().lower()
|
||||||
|
or 5
|
||||||
)
|
)
|
||||||
|
|
||||||
return base_address, relay_address, folder_name, interval, iperf_duration
|
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=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,
|
||||||
|
)
|
||||||
|
collect_iperf(
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
# 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')}")
|
||||||
|
|
||||||
|
save_data_to_json(data=data, filename=self.filename)
|
||||||
|
|
||||||
|
time.sleep(self.interval)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
base_address, relay_address, folder_name, interval, iperf_duration = (
|
relay = Relay()
|
||||||
set_parameters()
|
|
||||||
)
|
|
||||||
|
|
||||||
foldername = f"data/{folder_name}"
|
|
||||||
filename = foldername + "/test_" + str(int(time.time())) + ".json"
|
|
||||||
os.makedirs(foldername, exist_ok=True)
|
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"Type 'l' to set basestation location, 'b' to begin, "
|
"Type 'l' to set basestation location, 'b' to begin, "
|
||||||
|
|
@ -176,26 +163,19 @@ if __name__ == "__main__":
|
||||||
)
|
)
|
||||||
while True:
|
while True:
|
||||||
command = input("> ").strip().lower()
|
command = input("> ").strip().lower()
|
||||||
if command == "b" and not running:
|
if command == "b" and not relay.running:
|
||||||
print("Starting data collection...")
|
print("Starting data collection...")
|
||||||
running = True
|
relay.running = True
|
||||||
threading.Thread(
|
threading.Thread(target=relay.collect_data).start()
|
||||||
target=collect_data,
|
|
||||||
args=(filename, base_address, relay_address, interval),
|
|
||||||
).start()
|
|
||||||
elif command == "l":
|
elif command == "l":
|
||||||
base_location_data = set_base_location()
|
relay.set_location()
|
||||||
save_data_to_json(data=base_location_data, filename=filename)
|
|
||||||
elif command == "i":
|
elif command == "i":
|
||||||
threading.Thread(
|
threading.Thread(target=relay.relay_iperf).start()
|
||||||
target=relay_iperf,
|
elif command == "s" and relay.running:
|
||||||
args=(filename, base_address, relay_address, iperf_duration),
|
|
||||||
).start()
|
|
||||||
elif command == "s" and running:
|
|
||||||
print("Stopping data collection...")
|
print("Stopping data collection...")
|
||||||
running = False
|
relay.running = False
|
||||||
elif command == "x":
|
elif command == "x":
|
||||||
running = False
|
relay.running = False
|
||||||
break
|
break
|
||||||
elif command not in ["l", "b", "s", "i", "x"]:
|
elif command not in ["l", "b", "s", "i", "x"]:
|
||||||
print("Invalid command. Type 'l', 'b', 's', 'i', or 'x'.")
|
print("Invalid command. Type 'l', 'b', 's', 'i', or 'x'.")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user