|
| 1 | +# Authors: Youness Akourim <akourim97@gmail.com> & Bastien Trotobas <bastien.trotobas@gmail.com> |
| 2 | +# License: BSD 3-Clause |
| 3 | + |
| 4 | +""" |
| 5 | +============================================ |
| 6 | +Links (:mod:`commpy.links`) |
| 7 | +============================================ |
| 8 | +
|
| 9 | +.. autosummary:: |
| 10 | + :toctree: generated/ |
| 11 | +
|
| 12 | + link_performance -- Estimate the BER performance of a link model with Monte Carlo simulation. |
| 13 | + linkModel -- Link model object. |
| 14 | +""" |
| 15 | +from __future__ import division # Python 2 compatibility |
| 16 | + |
| 17 | +import numpy as np |
| 18 | +from commpy.channels import MIMOFlatChannel |
| 19 | + |
| 20 | +__all__ = ['link_performance', 'linkModel'] |
| 21 | + |
| 22 | + |
| 23 | +def link_performance(link_model, SNRs, send_max, err_min, send_chunk=None, code_rate=1): |
| 24 | + """ |
| 25 | + Estimate the BER performance of a link model with Monte Carlo simulation. |
| 26 | +
|
| 27 | + Parameters |
| 28 | + ---------- |
| 29 | + link_model : linkModel object. |
| 30 | +
|
| 31 | + SNRs : 1D arraylike |
| 32 | + Signal to Noise ratio in dB defined as :math:`SNR_{dB} = (E_b/N_0)_{dB} + 10 \log_{10}(R_cM_c)` |
| 33 | + where :math:`Rc` is the code rate and :math:`Mc` the modulation rate. |
| 34 | +
|
| 35 | + send_max : int |
| 36 | + Maximum number of bits send for each SNR. |
| 37 | +
|
| 38 | + err_min : int |
| 39 | + link_performance send bits until it reach err_min errors (see also send_max). |
| 40 | +
|
| 41 | + send_chunk : int |
| 42 | + Number of bits to be send at each iteration. |
| 43 | + *Default*: send_chunck = err_min |
| 44 | +
|
| 45 | + code_rate : float in (0,1] |
| 46 | + Rate of the used code. |
| 47 | + *Default*: 1 i.e. no code. |
| 48 | +
|
| 49 | + Returns |
| 50 | + ------- |
| 51 | + BERs : 1d ndarray |
| 52 | + Estimated Bit Error Ratio corresponding to each SNRs |
| 53 | + """ |
| 54 | + |
| 55 | + # Initialization |
| 56 | + BERs = np.empty_like(SNRs, dtype=float) |
| 57 | + # Set chunk size and round it to be a multiple of num_bits_symbol*nb_tx to avoid padding |
| 58 | + if send_chunk is None: |
| 59 | + send_chunk = err_min |
| 60 | + divider = link_model.num_bits_symbol * link_model.channel.nb_tx |
| 61 | + send_chunk = max(divider, send_chunk // divider * divider) |
| 62 | + |
| 63 | + # Computations |
| 64 | + for id_SNR in range(len(SNRs)): |
| 65 | + link_model.channel.set_SNR_dB(SNRs[id_SNR], code_rate, link_model.Es) |
| 66 | + bit_send = 0 |
| 67 | + bit_err = 0 |
| 68 | + while bit_send < send_max and bit_err < err_min: |
| 69 | + # Propagate some bits |
| 70 | + msg = np.random.choice((0, 1), send_chunk) |
| 71 | + symbs = link_model.modulate(msg) |
| 72 | + channel_output = link_model.channel.propagate(symbs) |
| 73 | + |
| 74 | + # Deals with MIMO channel |
| 75 | + if isinstance(link_model.channel, MIMOFlatChannel): |
| 76 | + nb_symb_vector = len(channel_output) |
| 77 | + received_msg = np.empty(nb_symb_vector * link_model.channel.nb_tx, dtype=channel_output.dtype) |
| 78 | + for i in range(nb_symb_vector): |
| 79 | + received_msg[link_model.channel.nb_tx * i:link_model.channel.nb_tx * (i+1)] = \ |
| 80 | + link_model.receive(channel_output[i], link_model.channel.channel_gains[i], link_model.constellation) |
| 81 | + else: |
| 82 | + received_msg = channel_output |
| 83 | + # Count errors |
| 84 | + bit_err += (msg != received_msg[:len(msg)]).sum() # Remove MIMO padding |
| 85 | + bit_send += send_chunk |
| 86 | + BERs[id_SNR] = bit_err / bit_send |
| 87 | + return BERs |
| 88 | + |
| 89 | + |
| 90 | +class linkModel: |
| 91 | + """ |
| 92 | + Construct a link model. |
| 93 | +
|
| 94 | + Parameters |
| 95 | + ---------- |
| 96 | + modulate : function with same prototype as Modem.modulate |
| 97 | +
|
| 98 | + channel : _FlatChannel object |
| 99 | +
|
| 100 | + receive : function with prototype receive(y, H, constellation) that return a binary array. |
| 101 | + y : 1D ndarray of floats |
| 102 | + Received complex symbols (shape: num_receive_antennas x 1) |
| 103 | +
|
| 104 | + h : 2D ndarray of floats |
| 105 | + Channel Matrix (shape: num_receive_antennas x num_transmit_antennas) |
| 106 | +
|
| 107 | + constellation : 1D ndarray of floats |
| 108 | +
|
| 109 | + num_bits_symbols : int |
| 110 | +
|
| 111 | + constellation : array of float or complex |
| 112 | +
|
| 113 | + Es : float |
| 114 | + Average energy per symbols. |
| 115 | + *Default* Es=1. |
| 116 | +
|
| 117 | + Attributes |
| 118 | + ---------- |
| 119 | + modulate : function with same prototype as Modem.modulate |
| 120 | +
|
| 121 | + channel : _FlatChannel object |
| 122 | +
|
| 123 | + receive : function with prototype receive(y, H, constellation) that return a binary array. |
| 124 | + y : 1D ndarray of floats |
| 125 | + Received complex symbols (shape: num_receive_antennas x 1) |
| 126 | +
|
| 127 | + h : 2D ndarray of floats |
| 128 | + Channel Matrix (shape: num_receive_antennas x num_transmit_antennas) |
| 129 | +
|
| 130 | + constellation : 1D ndarray of floats |
| 131 | +
|
| 132 | + num_bits_symbols : int |
| 133 | +
|
| 134 | + constellation : array of float or complex |
| 135 | +
|
| 136 | + Es : float |
| 137 | + Average energy per symbols. |
| 138 | + *Default* Es=1. |
| 139 | + """ |
| 140 | + def __init__(self, modulate, channel, receive, num_bits_symbol, constellation, Es=1): |
| 141 | + self.modulate = modulate |
| 142 | + self.channel = channel |
| 143 | + self.receive = receive |
| 144 | + self.num_bits_symbol = num_bits_symbol |
| 145 | + self.constellation = constellation |
| 146 | + self.Es = Es |
0 commit comments