Added rough implementation of Simulator class
This commit is contained in:
parent
9fab9bda68
commit
8f4b7e1fa1
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user