diff --git a/sw/utility/simulation.py b/sw/utility/simulation.py index 896dce6..1a4450a 100644 --- a/sw/utility/simulation.py +++ b/sw/utility/simulation.py @@ -67,16 +67,18 @@ class Simulator: # Simulation state - self._current_decoder_index = 0 - self._current_SNRs_index = 0 + self._curr_decoder_index = 0 + self._curr_SNRs_index = 0 self._curr_num_frame_errors = 0 self._curr_num_bit_errors = 0 self._curr_num_iterations = 0 + self._curr_num_dec_fails = 0 # Results & Miscellaneous - self._BERs = [[]] + self._BERs = [np.zeros(len(SNRs)) for i in range(len(decoders))] + self._dec_fails = [np.zeros(len(SNRs)) for i in range(len(decoders))] self._create_pbars() @@ -90,7 +92,7 @@ class Simulator: bar_format="{l_bar}{bar}| {n_fmt}/{" "total_fmt}") - decoder = self._decoders[self._current_decoder_index] + decoder = self._decoders[self._curr_decoder_index] self._decoder_pbar = tqdm(total=len(self._SNRs), desc=f"Calculating" f"g BERs" @@ -126,8 +128,8 @@ class Simulator: self._create_pbars() - self._overall_pbar.update(self._current_decoder_index) - self._decoder_pbar.update(self._current_SNRs_index) + self._overall_pbar.update(self._curr_decoder_index) + self._decoder_pbar.update(self._curr_SNRs_index) self._snr_pbar.update(self._curr_num_frame_errors) self._overall_pbar.refresh() @@ -139,13 +141,18 @@ class Simulator: :return: Number of bit errors that occurred """ - SNR = self._SNRs[self._current_SNRs_index] - decoder = self._decoders[self._current_decoder_index] + SNR = self._SNRs[self._curr_SNRs_index] + decoder = self._decoders[self._curr_decoder_index] y = noise.add_awgn(self._x_bpsk, SNR, self._n, self._k) x_hat = decoder.decode(y) - return count_bit_errors(self._x, x_hat) + # Handle decoding failure + if x_hat is not None: + return count_bit_errors(self._x, x_hat) + else: + self._curr_num_dec_fails += 1 + return 0 def _update_statistics(self, bit_errors: int) -> None: """Update the statistics of the simulator. @@ -164,36 +171,42 @@ class Simulator: def _advance_state(self) -> None: """Advance the state of the simulator. - This function also appends a new BER value to the self._BERs array - if the number of target frame errors has been reached + This function also handles setting the result arrays and progress bars. """ if (self._curr_num_frame_errors >= self._target_frame_errors) or ( self._curr_num_iterations > self._max_num_iterations): - self._BERs[self._current_decoder_index] \ - .append(self._curr_num_bit_errors / ( - self._curr_num_iterations * self._n)) + # Adjust the number of iterations to ignore decoding failures + adj_num_iterations = self._curr_num_iterations - \ + self._curr_num_dec_fails + + self._BERs[self._curr_decoder_index][self._curr_SNRs_index] \ + = self._curr_num_bit_errors / ( + adj_num_iterations * self._n) + + self._dec_fails[self._curr_decoder_index][self._curr_SNRs_index] \ + = self._curr_num_dec_fails self._curr_num_frame_errors = 0 self._curr_num_bit_errors = 0 self._curr_num_iterations = 0 + self._curr_num_dec_fails = 0 - if self._current_SNRs_index < len(self._SNRs) - 1: - self._current_SNRs_index += 1 + if self._curr_SNRs_index < len(self._SNRs) - 1: + self._curr_SNRs_index += 1 self._snr_pbar.reset() self._snr_pbar.set_description( f"Simulating for SNR = " - f"{self._SNRs[self._current_SNRs_index]} dB") + f"{self._SNRs[self._curr_SNRs_index]} dB") self._decoder_pbar.update(1) else: - if self._current_decoder_index < len(self._decoders) - 1: - self._current_decoder_index += 1 - self._current_SNRs_index = 0 - self._BERs.append([]) + if self._curr_decoder_index < len(self._decoders) - 1: + self._curr_decoder_index += 1 + self._curr_SNRs_index = 0 self._decoder_pbar.reset() - decoder = self._decoders[self._current_decoder_index] + decoder = self._decoders[self._curr_decoder_index] self._decoder_pbar.set_description( f"Calculating BERs for {decoder.__class__.__name__}") self._overall_pbar.update(1) @@ -228,22 +241,15 @@ class Simulator: been calculated are set to 0. :return: pandas Dataframe with the columns ["SNR", "BER_1", "BER_2", - ...] + ..., "DecFails_1", "DecFails_2", ...] """ data = {"SNR": np.array(self._SNRs)} - # If the BERs of a decoder have not been calculated for all SNRs, - # fill the rest up with zeros to match the length of the 'SNRs' array - for i, decoder_BER_list in enumerate(self._BERs): - padded = np.pad(decoder_BER_list, - (0, len(self._SNRs) - len(decoder_BER_list))) - data[f"BER_{i}"] = padded + for i, decoder_BERs in enumerate(self._BERs): + data[f"BER_{i}"] = decoder_BERs - # If the BERs have not been calculated for all decoders, fill up the - # BERs list - # with zero-vectors to match the length of the 'decoders' list - for i in range(len(self._decoders), len(self._BERs)): - data[f"BER_{i}"] = np.zeros(len(self._SNRs)) + for i, decoder_dec_fails in enumerate(self._dec_fails): + data[f"DecFails_{i}"] = decoder_dec_fails return pd.DataFrame(data)