From fad296f7599ae9c546958e0f732f89929660b059 Mon Sep 17 00:00:00 2001 From: Andreas Tsouchlos Date: Thu, 10 Nov 2022 13:05:13 +0100 Subject: [PATCH] Finished first rough implementation of show_BER_curves() --- sw/main.py | 93 +++++++++++++++---------------------- sw/utility/visualization.py | 52 +++++++++++++++++++++ 2 files changed, 90 insertions(+), 55 deletions(-) create mode 100644 sw/utility/visualization.py diff --git a/sw/main.py b/sw/main.py index e9f1195..08079f7 100644 --- a/sw/main.py +++ b/sw/main.py @@ -9,75 +9,58 @@ from itertools import chain from timeit import default_timer from decoders import proximal, maximum_likelihood -from utility import simulations, codes +from utility import simulations, codes, visualization # TODO: Fix spacing between axes and margins def plot_results(): results_dir = "sim_results" - code_paths = {} + # Read data from files + + data = [] for file in os.listdir(results_dir): if file.endswith(".csv"): - code_paths[file.replace(".csv", "")] = os.path.join(results_dir, file) + df = pd.read_csv(os.path.join(results_dir, file)) + df = df.loc[:, ~df.columns.str.contains('^Unnamed')] + data.append(df) + + # Create and show graphs sns.set_theme() - - fig, axes = plt.subplots(2, len(code_paths) // 2, figsize=(12, 6)) - fig.suptitle("Bit-Error-Rates of various decoders for different codes") - - axes = list(chain.from_iterable(axes)) - - for i, code in enumerate(code_paths): - data = pd.read_csv(code_paths[code]) - - column_names = [column for column in data.columns.values.tolist() - if column.startswith("BER")] - - ax = axes[i] - - for column in column_names: - sns.lineplot(ax=ax, data=data, x="SNR", y=column, label=column.lstrip("BER_")) - - ax.set_title(code) - ax.set(yscale="log") - ax.set_xlabel("SNR") - ax.set_ylabel("BER") - ax.set_yticks([10e-5, 10e-4, 10e-3, 10e-2, 10e-1, 10e0]) - ax.legend() - + fig = visualization.show_BER_curves(data) plt.show() def main(): - Path("sim_results").mkdir(parents=True, exist_ok=True) - - # used_code = "Hamming_7_4" - # used_code = "Golay_24_12" - used_code = "BCH_31_16" - # used_code = "BCH_31_21" - # used_code = "BCH_63_16" - - G = codes.Gs[used_code] - H = codes.get_systematic_H(G) - - decoders = [ - maximum_likelihood.MLDecoder(G, H), - proximal.ProximalDecoder(H, gamma=0.01), - proximal.ProximalDecoder(H, gamma=0.05), - proximal.ProximalDecoder(H, gamma=0.15) - ] - - k, n = G.shape - SNRs, BERs = simulations.test_decoders(n, k, decoders, N_max=30000, target_frame_errors=100) - - df = pd.DataFrame({"SNR": SNRs}) - df["BER_ML"] = BERs[0] - df["BER_prox_0_01"] = BERs[0] - df["BER_prox_0_05"] = BERs[1] - df["BER_prox_0_15"] = BERs[2] - - df.to_csv(f"sim_results/{used_code}.csv") + # Path("sim_results").mkdir(parents=True, exist_ok=True) + # + # # used_code = "Hamming_7_4" + # # used_code = "Golay_24_12" + # used_code = "BCH_31_16" + # # used_code = "BCH_31_21" + # # used_code = "BCH_63_16" + # + # G = codes.Gs[used_code] + # H = codes.get_systematic_H(G) + # + # decoders = [ + # maximum_likelihood.MLDecoder(G, H), + # proximal.ProximalDecoder(H, gamma=0.01), + # proximal.ProximalDecoder(H, gamma=0.05), + # proximal.ProximalDecoder(H, gamma=0.15) + # ] + # + # k, n = G.shape + # SNRs, BERs = simulations.test_decoders(n, k, decoders, N_max=30000, target_frame_errors=100) + # + # df = pd.DataFrame({"SNR": SNRs}) + # df["BER_ML"] = BERs[0] + # df["BER_prox_0_01"] = BERs[0] + # df["BER_prox_0_05"] = BERs[1] + # df["BER_prox_0_15"] = BERs[2] + # + # df.to_csv(f"sim_results/{used_code}.csv") plot_results() diff --git a/sw/utility/visualization.py b/sw/utility/visualization.py new file mode 100644 index 0000000..08f66a8 --- /dev/null +++ b/sw/utility/visualization.py @@ -0,0 +1,52 @@ +import seaborn as sns +import matplotlib.pyplot as plt +import pandas as pd +import typing +from itertools import chain + + +def _get_num_rows(num_graphs: int, num_cols: int) -> int: + """Get the minimum number of rows needed to show a certain number of graphs, + given a certain number of columns. + + :param num_graphs: Number of graphs + :param num_cols: Number of columns + :return: Number of rows + """ + return num_graphs // num_cols + 1 + + +# TODO: Calculate fig size in relation to the number of rows and columns +# TODO: Set proper line labels +# TODO: Set proper axis titles +# TODO: Should unnamed columns be dropped by this function or by the caller? +def show_BER_curves(data: typing.List[pd.DataFrame], num_cols: int = 3) -> plt.figure: + """This function creates a matplotlib figure containing a number of BER curves. + + :param data: List of pandas DataFrames containing the data to be plotted. Each element in the list is plotted in + a new graph. Each dataframe is assumed to contain a column named "SNR" which is used as the x-axis + :param num_cols: Number of columns in which the graphs should be arranged in the resulting figure + :return: Matplotlib figure + """ + num_graphs = len(data) + num_rows = _get_num_rows(num_cols, num_cols) + + fig, axes = plt.subplots(num_rows, num_cols) + fig.suptitle("Bit-Error-Rates of various decoders for different codes") + + axes = list(chain.from_iterable(axes))[:num_graphs] # Flatten the 2d axes array + + for axis, df in zip(axes, data): + column_names = [column for column in df.columns.values.tolist() if not column == "SNR"] + + for column in column_names: + sns.lineplot(ax=axis, data=df, x="SNR", y=column, label=column) + + #axis.set_title(code) + axis.set(yscale="log") + axis.set_xlabel("SNR") + axis.set_ylabel("BER") + axis.set_yticks([10e-5, 10e-4, 10e-3, 10e-2, 10e-1, 10e0]) + axis.legend() + + return fig