From 8f4b7e1fa168aabfd0e3c35cdef04b2ae40df0c9 Mon Sep 17 00:00:00 2001 From: Andreas Tsouchlos Date: Mon, 14 Nov 2022 11:12:46 +0100 Subject: [PATCH] Added rough implementation of Simulator class --- sw/utility/simulation.py | 139 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/sw/utility/simulation.py b/sw/utility/simulation.py index 24cd8fb..26798e8 100644 --- a/sw/utility/simulation.py +++ b/sw/utility/simulation.py @@ -122,6 +122,145 @@ def count_bit_errors(d: np.array, d_hat: np.array) -> int: # return SNRs, result_BERs # + +class Simulator: + def __init__(self, n: int, k: int, + decoders: typing.Sequence[typing.Any], + SNRs: typing.Sequence[float], + target_frame_errors: int): + """Construct and object of type simulator. + + TODO: ... + + :param n: + :param k: + :param decoders: + :param SNRs: + :param target_frame_errors: + """ + # Simulation parameters + + self._n = n + self._k = k + self._decoders = decoders + self._SNRs = SNRs + self._target_frame_errors = target_frame_errors + + self._x = np.zeros(self._n) + self._x_bpsk = 1 - 2 * self._x # Map x from [0, 1]^n to [-1, 1]^n + + # Simulation state + + self._current_decoder_index = 0 + self._current_SNRs_index = 0 + + self._curr_num_frame_errors = 0 + self._curr_num_bit_errors = 0 + self._curr_num_total_bits = 0 + + # Results + + self._BERs = [] + + def _update_sim_state(self, bit_errors: int): + pass + + def _simulate_transmission(self, decoder: typing.Any, SNR: float) -> bool: + # Simulate channel + y = noise.add_awgn(self._x_bpsk, SNR, self._n, self._k) + + # Decode received frame + x_hat = decoder.decode(y) + + # Update statistics + bit_errors = count_bit_errors(self._x, x_hat) + self._curr_num_total_bits += self._x.size + + if bit_errors > 0: + self._curr_num_frame_errors += 1 + self._curr_num_total_bits += bit_errors + + return bit_errors > 0 + + def _simulate_SNR(self, SNR): + pbar = tqdm(total=self._target_frame_errors, + desc=f"Simulating for SNR = {SNR} dB", position=2, leave=False, + bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]") + + while self._curr_num_frame_errors < self._target_frame_errors: + error_occurred = self._simulate_transmission() + if error_occurred: + pbar.update(1) + pbar.close() + + self._BERs.append(self._curr_num_bit_errors / self._curr_num_total_bits) + + def + + def test_decoder(self) -> 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. + + :return: Tuple of numpy arrays of the form (SNRs, BERs) + """ + + decoder = self._decoders[self._current_decoder_index] + + for SNR in tqdm(self._SNRs[self._current_SNRs_index:], + desc=f"Calculating BERs for {decoder.__class__.__name__}", + position=1, leave=False, bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}"): + + pbar = tqdm(total=self._target_frame_errors, + desc=f"Simulating for SNR = {SNR} dB", position=2, leave=False, + bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]") + + while self._curr_num_frame_errors < self._target_frame_errors: + error_occurred = self._simulate_transmission() + if error_occurred: + pbar.update(1) + pbar.close() + + self._BERs.append(self._curr_num_bit_errors / self._curr_num_total_bits) + + return np.array(self._SNRs), np.array(self._BERs) + + + def test_decoders(n: int, + k: int, + decoders: typing.List, + SNRs: typing.Sequence[float] = np.linspace(1, 7, 7), + target_frame_errors: int = 100) \ + -> typing.Tuple[np.array, np.array]: + """Calculate the Bit Error Rate (BER) for a number of given decoders for a number of SNRs. + + This function assumes the all-zeros assumption holds. Progress is printed to stdout. + + :param n: Length of a codeword of the used code + :param k: Length of a dataword of the used code + :param decoders: List of decoder objects to be tested + :param SNRs: List of SNRs for which the BER should be calculated + :param target_frame_errors: Number of frame errors after which to stop the simulation + :return: Tuple of the form (SNRs, [BERs_1, BERs_2, ...]) where SNR and BERs_x are numpy arrays + """ + result_BERs = [] + + start_time = default_timer() + + for decoder in tqdm(decoders, + desc="Calculating the answer to life, the universe and everything", + position=0, + leave=False, + bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}"): + _, BERs = test_decoder(n, k, decoder, SNRs, target_frame_errors) + result_BERs.append(BERs) + + end_time = default_timer() + print(f"Elapsed time: {end_time - start_time:.2f}s") + + return SNRs, result_BERs + + @dataclass class SimulationParameters: n: int