From 28b7e0b7d50c422d07dd24a6f62cdd2c84625b83 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Tue, 10 Dec 2024 12:53:28 +0100 Subject: [PATCH] UL BLER vs SNR plot Added a tool for generating UL BLER vs SNR from nr_ulsim runs. --- tools/plots/README.md | 21 +++++ tools/plots/example.png | Bin 0 -> 40957 bytes tools/plots/requirements.txt | 2 + tools/plots/ul_bler_vs_snr_graph.py | 128 ++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 tools/plots/README.md create mode 100644 tools/plots/example.png create mode 100644 tools/plots/requirements.txt create mode 100755 tools/plots/ul_bler_vs_snr_graph.py diff --git a/tools/plots/README.md b/tools/plots/README.md new file mode 100644 index 0000000000..5d5175a882 --- /dev/null +++ b/tools/plots/README.md @@ -0,0 +1,21 @@ +# Plotting tools + +## `ul_bler_vs_snr_graph.py` + +This generates UL BLER vs SNR plots using nr_ulsim. It uses cache so subsequent runs are faster. +Assumes your nr_ulsim is in `../../cmake_targets/ran_build/build`. Run with -h flag to see options + +Example graph: + +![image](./example.png) + +### Cache usage and modifting `nr_ulsim` command + +Modify the script call to `nr_ulsim` if you want a different channel model or other flags in the `nr_ulsim` command. +Remember to clear the cache (remove `cache.pkl` from this folder) so that the results from a different run are not +taken from the cache. + +Same goes for any software modifications of `nr_ulsim` - the cache is not aware of the software version used to generate +the data - remember to clear your cache manually if you are comparing different `nr_ulsim` versions. + +The script also allows to skip usage of the cache with `--rerun` option. diff --git a/tools/plots/example.png b/tools/plots/example.png new file mode 100644 index 0000000000000000000000000000000000000000..56c8af15dfd54316f0376e0a79dd571692ce1c49 GIT binary patch literal 40957 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 literal 0 HcmV?d00001 diff --git a/tools/plots/requirements.txt b/tools/plots/requirements.txt new file mode 100644 index 0000000000..dbfeaf55f1 --- /dev/null +++ b/tools/plots/requirements.txt @@ -0,0 +1,2 @@ +matplotlib==3.8.3 +numpy==1.21.5 diff --git a/tools/plots/ul_bler_vs_snr_graph.py b/tools/plots/ul_bler_vs_snr_graph.py new file mode 100755 index 0000000000..83e7aa5093 --- /dev/null +++ b/tools/plots/ul_bler_vs_snr_graph.py @@ -0,0 +1,128 @@ +#!/bin/python3 +import os +import subprocess +import numpy as np +import re +import matplotlib.pyplot as plt +import matplotlib.cm as cm +import pickle +import argparse +import concurrent.futures +import traceback +import multiprocessing + +default_ulsim_cmd = "../../cmake_targets/ran_build/build/nr_ulsim -i 1,0 -t 100 -R 273" +ulsim_args = "-n {} -s {} -S {} -m {} -r {}" + +def find_throughput(line): + ### Example line + ### SNR 10.000000: Channel BLER (0.000000e+00,-nan,-nan,-nan Channel BER (1.548551e-03,-nan,-nan,-nan) Avg round 1.00, Eff Rate 1672.0000 bits/slot, Eff Throughput 100.00, TBS 1672 bits/slot + ### SNR -0.000000: Channel BLER (0.000000e+00,-nan,-nan,-nan Channel BER (1.888043e-01,-nan,-nan,-nan) Avg round 1.00, Eff Rate 2152.0000 bits/slot, Eff Throughput 100.00, TBS 2152 bits/slot + pattern = r"SNR (-?\d+.\d+).*BLER \((-?\d+\.\d*(?:[eE][+-]\d+)?),.*Eff Throughput (\d+.\d+)" + match = re.search(pattern, line) + if match: + snr, bler, thp = match.groups() + return float(snr), float(bler), float(thp) + return None, None, None + +def generate_mcs_thps(ulsim_cmd_with_args, snr, mcs, num_runs = 10, num_rbs = 50, cache = {}): + thp = 0 + bler = 0 + num_runs_cached = 0 + cache_string = "{},{},{}".format(snr, mcs, num_rbs) + if cache_string in cache: + thp, bler, num_runs_cached = cache[cache_string] + if num_runs_cached >= num_runs: + return bler, thp + + num_runs_left = num_runs - num_runs_cached + + ulsim_cmd_formatted = ulsim_cmd_with_args.format(num_runs_left, snr, snr+0.01, mcs, num_rbs) + print(ulsim_cmd_formatted, "result insufficient in cache, running") + process = subprocess.Popen(ulsim_cmd_formatted.split(" "), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout_data = process.communicate()[0] + output = "" + if stdout_data is not None: + output = stdout_data.decode() + for line in output.splitlines(): + _snr, _bler, _thp = find_throughput(line) + if _snr is not None: + bler = (bler * num_runs_cached + _bler * num_runs_left) / num_runs + thp = (thp * num_runs_cached + _thp * num_runs_left) / num_runs + cache[cache_string] = thp, bler, num_runs + return bler, thp + print("error processing "+ ulsim_cmd_with_args) + print(output) + print(process.communicate()[1].decode()) + return None, None + + + +parser = argparse.ArgumentParser(description="UL SNR/BLER plot generator") +parser.add_argument("--snr-start", help="Starting SNR value") +parser.add_argument("--snr-stop", help="Final SNR value") +parser.add_argument("--snr-step", help="SNR step value") +parser.add_argument("--mcs-start", help="First MCS to test") +parser.add_argument("--mcs-stop", help="Last MCS to test") +parser.add_argument("--num-runs", help="Number of simulations per SNR/MCS value") +parser.add_argument("--rerun", help="Do not use cache to read data, run from scrach", action="store_true") +parser.add_argument("--num-rbs", help="Number of RBs") +parser.add_argument("--ulsim-cmd", help="ULSIM command to run. Do not use `sSrnm` flags, as these are used by the script", default=default_ulsim_cmd) + +cpu_count = multiprocessing.cpu_count() +cpu_to_use = max(1, cpu_count - 2) + +parser.add_argument("--num-threads", help="Number of threads to run", default=cpu_to_use) +args = parser.parse_args() + +_snr_start = float(args.snr_start) +_snr_step = float(args.snr_step) +_snr_end = float(args.snr_stop) + _snr_step/2 +_mcs_start = int(args.mcs_start) +_mcs_stop = int(args.mcs_stop) +_num_runs = int(args.num_runs) +_num_rbs = int(args.num_rbs) +ulsim_cmd = args.ulsim_cmd + " " + ulsim_args +num_threads = int(args.num_threads) + +cmap = cm.viridis +plt.figure() +plt.ylim(-0.1, 1.1) +plt.title("BLER vs SNR for MCS{} to MCS{}, nRB={} (num_runs = {})".format(_mcs_start, _mcs_stop, _num_rbs, _num_runs)) +plt.xlabel("SNR [dB]") +plt.ylabel("BLER") + +cache = {} +if not args.rerun: + try: + with open("cache.pkl", "rb+") as f: + cache = pickle.load(f) + except FileNotFoundError: + pass # no cache found + +snrs_list = np.arange(_snr_start, _snr_end, _snr_step) +mcss_list = np.arange(_mcs_start, _mcs_stop + 1, 1) +plot_index = 0 +for mcs in mcss_list: + results = {} + with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: + result_to_snr_index = {executor.submit(generate_mcs_thps, ulsim_cmd, snrs_list[snr_index], mcs, _num_runs, _num_rbs, cache): snr_index for (snr_index, _) in enumerate(snrs_list)} + for future in concurrent.futures.as_completed(result_to_snr_index): + snr_index = result_to_snr_index[future] + bler, thp = future.result() + results[snr_index] = bler + blers = np.zeros_like(snrs_list) + for snr_index in results: + blers[snr_index] = results[snr_index] + + num_plots = len(mcss_list) + percentage = float(plot_index) / max(1, (num_plots - 1)) + color = cmap(percentage) + plot_index += 1 + plt.plot(snrs_list, blers, label="MCS{}".format(mcs), color = color) + plt.legend() + plt.savefig('MCS={}-{},SNR={}-{},RBs={}graph.png'.format(_mcs_start, _mcs_stop, _snr_start, _snr_end, _num_rbs)) +if not args.rerun: + with open("cache.pkl", "wb+") as f: + pickle.dump(cache, f) +