|
12 | 12 |
|
13 | 13 | PSKModem -- Phase Shift Keying (PSK) Modem. |
14 | 14 | QAMModem -- Quadrature Amplitude Modulation (QAM) Modem. |
| 15 | + ofdm_tx -- OFDM Transmit Signal Generation |
| 16 | + ofdm_rx -- OFDM Receive Signal Processing |
15 | 17 | mimo_ml -- MIMO Maximum Likelihood (ML) Detection. |
| 18 | + kbest -- MIMO K-best Schnorr-Euchner Detection. |
16 | 19 |
|
17 | 20 | """ |
18 | | -from numpy import arange, array, zeros, pi, cos, sin, sqrt, log2, argmin, \ |
19 | | - hstack, repeat, tile, dot, sum, shape, concatenate, exp, \ |
20 | | - log, vectorize |
21 | 21 | from itertools import product |
22 | | -from commpy.utilities import bitarray2dec, dec2bitarray |
| 22 | + |
| 23 | +from numpy import arange, array, zeros, pi, cos, sin, sqrt, log2, argmin, \ |
| 24 | + hstack, repeat, tile, dot, sum, shape, concatenate, exp, \ |
| 25 | + log, vectorize, empty |
23 | 26 | from numpy.fft import fft, ifft |
| 27 | +from numpy.linalg import qr |
| 28 | + |
| 29 | +from commpy.utilities import bitarray2dec, dec2bitarray |
| 30 | + |
| 31 | +__all__ = ['PSKModem', 'QAMModem', 'ofdm_tx', 'ofdm_rx', 'mimo_ml', 'kbest'] |
24 | 32 |
|
25 | | -__all__ = ['PSKModem', 'QAMModem', 'mimo_ml'] |
26 | 33 |
|
27 | 34 | class Modem: |
28 | 35 |
|
@@ -193,3 +200,68 @@ def mimo_ml(y, h, constellation): |
193 | 200 | x_r = x_ideal[:, min_idx] |
194 | 201 |
|
195 | 202 | return x_r |
| 203 | + |
| 204 | + |
| 205 | +def kbest(y, h, constellation, K): |
| 206 | + """ MIMO K-best Schnorr-Euchner Detection. |
| 207 | +
|
| 208 | + Reference: Zhan Guo and P. Nilsson, 'Algorithm and implementation of the K-best sphere decoding for MIMO detection', |
| 209 | + IEEE Journal on Selected Areas in Communications, vol. 24, no. 3, pp. 491-503, Mar. 2006. |
| 210 | +
|
| 211 | + parameters |
| 212 | + ---------- |
| 213 | + y : 1D ndarray of floats |
| 214 | + Received complex symbols (shape: num_receive_antennas x 1) |
| 215 | +
|
| 216 | + h : 2D ndarray of floats |
| 217 | + Channel Matrix (shape: num_receive_antennas x num_transmit_antennas) |
| 218 | +
|
| 219 | + constellation : 1D ndarray of floats |
| 220 | + Constellation used to modulate the symbols |
| 221 | +
|
| 222 | + K : positive integer |
| 223 | + Number of candidates kept at each step |
| 224 | + """ |
| 225 | + # QR decomposition |
| 226 | + q, r = qr(h) |
| 227 | + yt = q.conj().T.dot(y) |
| 228 | + |
| 229 | + # Initialization |
| 230 | + m = len(constellation) |
| 231 | + n = len(yt) |
| 232 | + nb_can = 1 |
| 233 | + |
| 234 | + if isinstance(constellation[0], complex): |
| 235 | + const_type = complex |
| 236 | + else: |
| 237 | + const_type = float |
| 238 | + X = empty((n, K * m), dtype=const_type) # Set of current candidates |
| 239 | + d = tile(yt[:, None], (1, K * m)) # Corresponding distance vector |
| 240 | + d_tot = zeros(K * m, dtype=float) # Corresponding total distance |
| 241 | + hyp = empty(K * m, dtype=const_type) # Hypothesis vector |
| 242 | + |
| 243 | + # Processing |
| 244 | + for coor in range(n-1, -1, -1): |
| 245 | + nb_hyp = nb_can * m |
| 246 | + |
| 247 | + # Copy best candidates m times |
| 248 | + X[:, :nb_hyp] = tile(X[:, :nb_can], (1, m)) |
| 249 | + d[:, :nb_hyp] = tile(d[:, :nb_can], (1, m)) |
| 250 | + d_tot[:nb_hyp] = tile(d_tot[:nb_can], (1, m)) |
| 251 | + |
| 252 | + # Make hypothesis |
| 253 | + hyp[:nb_hyp] = repeat(constellation, nb_can) |
| 254 | + X[coor, :nb_hyp] = hyp[:nb_hyp] |
| 255 | + d[coor, :nb_hyp] -= r[coor, coor] * hyp[:nb_hyp] |
| 256 | + d_tot[:nb_hyp] += abs(d[coor, :nb_hyp])**2 |
| 257 | + |
| 258 | + # Select best candidates |
| 259 | + argsort = d_tot[:nb_hyp].argsort() |
| 260 | + nb_can = min(nb_hyp, K) # Update number of candidate |
| 261 | + |
| 262 | + # Update accordingly |
| 263 | + X[:, :nb_can] = X[:, argsort[:nb_can]] |
| 264 | + d[:, :nb_can] = d[:, argsort[:nb_can]] |
| 265 | + d[:coor, :nb_can] -= r[:coor, coor, None] * hyp[argsort[:nb_can]] |
| 266 | + d_tot[:nb_can] = d_tot[argsort[:nb_can]] |
| 267 | + return X[:, 0] |
0 commit comments