ba-thesis/sw/decoders/utility.py

91 lines
3.3 KiB
Python

"""This file contains various utility functions that can be used in combination with the decoders.
"""
import numpy as np
import typing
from tqdm import tqdm
def _get_noise_amp_from_SNR(SNR: float, signal_amp: float = 1) -> float:
"""Calculate the amplitude of the noise from an SNR and the signal amplitude.
:param SNR: Signal-to-Noise-Ratio in dB
:param signal_amp: Signal Amplitude (linear)
:return: Noise Amplitude (linear)
"""
SNR_linear = 10 ** (SNR / 10)
noise_amp = (1 / np.sqrt(SNR_linear)) * signal_amp
return noise_amp
def add_awgn(c: np.array, SNR: float, signal_amp: float = 1) -> np.array:
"""Add Additive White Gaussian Noise to a data vector. As this function adds random noise to the input,
the output changes, even if it is called multiple times with the same input.
:param c: Binary vector representing the data to be transmitted
:param SNR: Signal-to-Noise-Ratio in dB
:param signal_amp: Amplitude of the signal. Used for the noise amplitude calculation
:return: Data vector with added noise
"""
noise_amp = _get_noise_amp_from_SNR(SNR, signal_amp=signal_amp)
y = c + np.random.normal(scale=noise_amp, size=c.size)
return y
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(decoder: typing.Any,
c: np.array,
SNRs: typing.Sequence[float] = np.linspace(1, 4, 7),
target_bit_errors=100,
N_max=10000) \
-> typing.Tuple[np.array, np.array]:
"""Calculate the Bit Error Rate (BER) for a given decoder for a number of SNRs.
This function prints its progress to stdout.
:param decoder: Instance of the decoder to be tested
:param c: Codeword whose transmission is to be simulated (element of [0, 1]^n)
: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 = c * 2 - 1 # Map the codeword from [0, 1]^n to [-1, 1]^n
BERs = []
for SNR in tqdm(SNRs, desc="Calculating Bit-Error-Rates",
position=0,
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}"):
# TODO: Is this a valid simulation? Can we just add AWGN to the codeword, ignoring and modulation and (
# e.g. matched) filtering?
y = add_awgn(x, SNR, signal_amp=np.sqrt(2))
y_hat = decoder.decode(y)
total_bit_errors += count_bit_errors(c, y_hat)
total_bits += c.size
if total_bit_errors >= target_bit_errors:
break
BERs.append(total_bit_errors / total_bits)
return np.array(SNRs), np.array(BERs)