Skip to content

Commit ee25547

Browse files
committed
Add link_performance as a method of LinkModel object.
1 parent ac897de commit ee25547

File tree

1 file changed

+81
-46
lines changed

1 file changed

+81
-46
lines changed

commpy/links.py

Lines changed: 81 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
def link_performance(link_model, SNRs, send_max, err_min, send_chunk=None, code_rate=1):
2929
"""
3030
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).
3132
3233
Parameters
3334
----------
@@ -58,52 +59,7 @@ def link_performance(link_model, SNRs, send_max, err_min, send_chunk=None, code_
5859
Estimated Bit Error Ratio corresponding to each SNRs
5960
"""
6061

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)
10763

10864

10965
class LinkModel:
@@ -191,6 +147,84 @@ def __init__(self, modulate, channel, receive, num_bits_symbol, constellation, E
191147
else:
192148
self.decoder = decoder
193149

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+
194228

195229
def idd_decoder(detector, decoder, decision, n_it):
196230
"""
@@ -241,6 +275,7 @@ def idd_decoder(detector, decoder, decision, n_it):
241275
bits_per_send : positive integer
242276
Number or bit send at each symbol vector.
243277
"""
278+
244279
def decode(y, h, constellation, noise_var, a_priori, bits_per_send):
245280
a_priori_decoder = a_priori.copy()
246281
nb_vect, nb_rx, nb_tx = h.shape

0 commit comments

Comments
 (0)