"""This file contains utility functions relating to tests and simulations of the decoders.""" import numpy as np import typing from tqdm import tqdm from utility import noise def count_bit_errors(d: np.array, d_hat: np.array) -> int: """Count the number of wrong bits in a decoded codeword. :param d: Originally sent data :param d_hat: Received data :return: Number of bit errors """ return np.sum(d != d_hat) def test_decoder(x: np.array, decoder: typing.Any, SNRs: typing.Sequence[float] = np.linspace(1, 4, 7), target_bit_errors: int = 100, N_max: int = 10000) \ -> typing.Tuple[np.array, np.array]: """Calculate the Bit Error Rate (BER) for a given decoder for a number of SNRs. This function assumes the all-zeros assumption holds. Progress is printed to stdout. :param x: Codeword to be sent (Element of [0, 1]^n) :param decoder: Instance of the decoder to be tested :param SNRs: List of SNRs for which the BER should be calculated :param target_bit_errors: Number of bit errors after which to stop the simulation :param N_max: Maximum number of iterations to perform for each SNR :return: Tuple of numpy arrays of the form (SNRs, BERs) """ x_bpsk = 1 - 2 * x # Map x from [0, 1]^n to [-1, 1]^n BERs = [] for SNR in tqdm(SNRs, desc="Calculating Bit-Error-Rates", position=0, leave=False, bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}"): total_bit_errors = 0 total_bits = 0 for n in tqdm(range(N_max), desc=f"Simulating for SNR = {SNR} dB", position=1, leave=False, bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}"): y = noise.add_awgn(x_bpsk, SNR, signal_amp=np.sqrt(2)) y_hat = decoder.decode(y) total_bit_errors += count_bit_errors(x, y_hat) total_bits += x.size if total_bit_errors >= target_bit_errors: break BERs.append(total_bit_errors / total_bits) return np.array(SNRs), np.array(BERs)