|
28 | 28 | def link_performance(link_model, SNRs, send_max, err_min, send_chunk=None, code_rate=1): |
29 | 29 | """ |
30 | 30 | Estimate the BER performance of a link model with Monte Carlo simulation. |
| 31 | + Equivalent to call link_model.link_performance(SNRs, send_max, err_min, send_chunk, code_rate). |
31 | 32 |
|
32 | 33 | Parameters |
33 | 34 | ---------- |
@@ -58,52 +59,7 @@ def link_performance(link_model, SNRs, send_max, err_min, send_chunk=None, code_ |
58 | 59 | Estimated Bit Error Ratio corresponding to each SNRs |
59 | 60 | """ |
60 | 61 |
|
61 | | - # Initialization |
62 | | - BERs = np.zeros_like(SNRs, dtype=float) |
63 | | - # Set chunk size and round it to be a multiple of num_bits_symbol*nb_tx to avoid padding |
64 | | - if send_chunk is None: |
65 | | - send_chunk = err_min |
66 | | - divider = link_model.num_bits_symbol * link_model.channel.nb_tx |
67 | | - send_chunk = max(divider, send_chunk // divider * divider) |
68 | | - |
69 | | - receive_size = link_model.channel.nb_tx * link_model.num_bits_symbol |
70 | | - full_args_decoder = len(getfullargspec(link_model.decoder).args) > 1 |
71 | | - |
72 | | - # Computations |
73 | | - for id_SNR in range(len(SNRs)): |
74 | | - link_model.channel.set_SNR_dB(SNRs[id_SNR], code_rate, link_model.Es) |
75 | | - bit_send = 0 |
76 | | - bit_err = 0 |
77 | | - while bit_send < send_max and bit_err < err_min: |
78 | | - # Propagate some bits |
79 | | - msg = np.random.choice((0, 1), send_chunk) |
80 | | - symbs = link_model.modulate(msg) |
81 | | - channel_output = link_model.channel.propagate(symbs) |
82 | | - |
83 | | - # Deals with MIMO channel |
84 | | - if isinstance(link_model.channel, MIMOFlatChannel): |
85 | | - nb_symb_vector = len(channel_output) |
86 | | - received_msg = np.empty(int(math.ceil(len(msg) / link_model.rate))) |
87 | | - for i in range(nb_symb_vector): |
88 | | - received_msg[receive_size * i:receive_size * (i + 1)] = \ |
89 | | - link_model.receive(channel_output[i], link_model.channel.channel_gains[i], |
90 | | - link_model.constellation, link_model.channel.noise_std ** 2) |
91 | | - else: |
92 | | - received_msg = link_model.receive(channel_output, link_model.channel.channel_gains, |
93 | | - link_model.constellation, link_model.channel.noise_std ** 2) |
94 | | - # Count errors |
95 | | - if full_args_decoder: |
96 | | - decoded_bits = link_model.decoder(channel_output, link_model.channel.channel_gains, |
97 | | - link_model.constellation, link_model.channel.noise_std ** 2, |
98 | | - received_msg, link_model.channel.nb_tx * link_model.num_bits_symbol) |
99 | | - bit_err += (msg != decoded_bits[:len(msg)]).sum() |
100 | | - else: |
101 | | - bit_err += (msg != link_model.decoder(received_msg)[:len(msg)]).sum() |
102 | | - bit_send += send_chunk |
103 | | - BERs[id_SNR] = bit_err / bit_send |
104 | | - if bit_err < err_min: |
105 | | - break |
106 | | - return BERs |
| 62 | + return link_model.link_performance(SNRs, send_max, err_min, send_chunk, code_rate) |
107 | 63 |
|
108 | 64 |
|
109 | 65 | class LinkModel: |
@@ -191,6 +147,84 @@ def __init__(self, modulate, channel, receive, num_bits_symbol, constellation, E |
191 | 147 | else: |
192 | 148 | self.decoder = decoder |
193 | 149 |
|
| 150 | + def link_performance(self, SNRs, send_max, err_min, send_chunk=None, code_rate=1): |
| 151 | + """ |
| 152 | + Estimate the BER performance of a link model with Monte Carlo simulation. |
| 153 | +
|
| 154 | + Parameters |
| 155 | + ---------- |
| 156 | + SNRs : 1D arraylike |
| 157 | + Signal to Noise ratio in dB defined as :math:`SNR_{dB} = (E_b/N_0)_{dB} + 10 \log_{10}(R_cM_c)` |
| 158 | + where :math:`Rc` is the code rate and :math:`Mc` the modulation rate. |
| 159 | +
|
| 160 | + send_max : int |
| 161 | + Maximum number of bits send for each SNR. |
| 162 | +
|
| 163 | + err_min : int |
| 164 | + link_performance send bits until it reach err_min errors (see also send_max). |
| 165 | +
|
| 166 | + send_chunk : int |
| 167 | + Number of bits to be send at each iteration. This is also the frame length of the decoder if available |
| 168 | + so it should be large enough regarding the code type. |
| 169 | + *Default*: send_chunck = err_min |
| 170 | +
|
| 171 | + code_rate : float in (0,1] |
| 172 | + Rate of the used code. |
| 173 | + *Default*: 1 i.e. no code. |
| 174 | +
|
| 175 | + Returns |
| 176 | + ------- |
| 177 | + BERs : 1d ndarray |
| 178 | + Estimated Bit Error Ratio corresponding to each SNRs |
| 179 | + """ |
| 180 | + |
| 181 | + # Initialization |
| 182 | + BERs = np.zeros_like(SNRs, dtype=float) |
| 183 | + # Set chunk size and round it to be a multiple of num_bits_symbol*nb_tx to avoid padding |
| 184 | + if send_chunk is None: |
| 185 | + send_chunk = err_min |
| 186 | + divider = self.num_bits_symbol * self.channel.nb_tx |
| 187 | + send_chunk = max(divider, send_chunk // divider * divider) |
| 188 | + |
| 189 | + receive_size = self.channel.nb_tx * self.num_bits_symbol |
| 190 | + full_args_decoder = len(getfullargspec(self.decoder).args) > 1 |
| 191 | + |
| 192 | + # Computations |
| 193 | + for id_SNR in range(len(SNRs)): |
| 194 | + self.channel.set_SNR_dB(SNRs[id_SNR], code_rate, self.Es) |
| 195 | + bit_send = 0 |
| 196 | + bit_err = 0 |
| 197 | + while bit_send < send_max and bit_err < err_min: |
| 198 | + # Propagate some bits |
| 199 | + msg = np.random.choice((0, 1), send_chunk) |
| 200 | + symbs = self.modulate(msg) |
| 201 | + channel_output = self.channel.propagate(symbs) |
| 202 | + |
| 203 | + # Deals with MIMO channel |
| 204 | + if isinstance(self.channel, MIMOFlatChannel): |
| 205 | + nb_symb_vector = len(channel_output) |
| 206 | + received_msg = np.empty(int(math.ceil(len(msg) / self.rate))) |
| 207 | + for i in range(nb_symb_vector): |
| 208 | + received_msg[receive_size * i:receive_size * (i + 1)] = \ |
| 209 | + self.receive(channel_output[i], self.channel.channel_gains[i], |
| 210 | + self.constellation, self.channel.noise_std ** 2) |
| 211 | + else: |
| 212 | + received_msg = self.receive(channel_output, self.channel.channel_gains, |
| 213 | + self.constellation, self.channel.noise_std ** 2) |
| 214 | + # Count errors |
| 215 | + if full_args_decoder: |
| 216 | + decoded_bits = self.decoder(channel_output, self.channel.channel_gains, |
| 217 | + self.constellation, self.channel.noise_std ** 2, |
| 218 | + received_msg, self.channel.nb_tx * self.num_bits_symbol) |
| 219 | + bit_err += (msg != decoded_bits[:len(msg)]).sum() |
| 220 | + else: |
| 221 | + bit_err += (msg != self.decoder(received_msg)[:len(msg)]).sum() |
| 222 | + bit_send += send_chunk |
| 223 | + BERs[id_SNR] = bit_err / bit_send |
| 224 | + if bit_err < err_min: |
| 225 | + break |
| 226 | + return BERs |
| 227 | + |
194 | 228 |
|
195 | 229 | def idd_decoder(detector, decoder, decision, n_it): |
196 | 230 | """ |
@@ -241,6 +275,7 @@ def idd_decoder(detector, decoder, decision, n_it): |
241 | 275 | bits_per_send : positive integer |
242 | 276 | Number or bit send at each symbol vector. |
243 | 277 | """ |
| 278 | + |
244 | 279 | def decode(y, h, constellation, noise_var, a_priori, bits_per_send): |
245 | 280 | a_priori_decoder = a_priori.copy() |
246 | 281 | nb_vect, nb_rx, nb_tx = h.shape |
|
0 commit comments