Skip to content

Commit 4f52e83

Browse files
authored
Merge pull request #32 from BastienTr/master
BER estimator, K-best detection and channel correlation
2 parents 2f80e83 + 8b5adc0 commit 4f52e83

File tree

14 files changed

+692
-76
lines changed

14 files changed

+692
-76
lines changed

LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2012-2018, Veeresh Taranalli & contributors
3+
Copyright (c) 2012-2019, Veeresh Taranalli & contributors
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ Modulation/Demodulation
5252
- Phase Shift Keying (PSK)
5353
- Quadrature Amplitude Modulation (QAM)
5454
- OFDM Tx/Rx signal processing
55+
- MIMO Maximum Likelihood (ML) Detection.
56+
- MIMO K-best Schnorr-Euchner Detection.
57+
- Convert channel matrix to Bit-level representation.
5558

5659
Sequences
5760
---------
@@ -65,6 +68,11 @@ Utilities
6568
- Upsample
6669
- Power of a discrete-time signal
6770

71+
Links
72+
-----
73+
- Estimate the BER performance of a link model with Monte Carlo simulation.
74+
- Link model object.
75+
6876
FAQs
6977
----
7078
Why are you developing this?

THANKS.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
Please add names as needed so that we can keep up with all the contributors.
22

33
Veeresh Taranalli for initial creation and contribution of CommPy.
4-
Bastien Trotobas for adding SISO and MIMO channel models with Rayleigh or rician fading, bugfixes, maintenance.
4+
Bastien Trotobas for adding some features, bugfixes, maintenance.
55
Vladimir Fadeev for bugfixes, addition of convolutional code puncturing.
6+
Youness Akourim for adding features and fixing some bugs.
67
Rey Tucker for python3 compatibility fixes.
78
Dat Nguyen for type check fix for AWGN channel model.
89
Mateusz Michalski for bugfix in AWGN channel model.

commpy/channelcoding/tests/test_ldpc.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
# Authors: Veeresh Taranalli <veeresht@gmail.com>
22
# License: BSD 3-Clause
33

4+
import os
5+
6+
from nose.plugins.attrib import attr
47
from numpy import array, sqrt, zeros
58
from numpy.random import randn
69
from numpy.testing import assert_allclose
10+
711
from commpy.channelcoding.ldpc import get_ldpc_code_params, ldpc_bp_decode
812
from commpy.utilities import hamming_dist
9-
import os
1013

11-
from nose.plugins.attrib import attr
1214

1315
@attr('slow')
1416
class TestLDPCCode(object):
@@ -59,4 +61,4 @@ def test_ldpc_bp_decode(self):
5961
fer_array_test[idx] = float(fer_cnt_bp)/(iter_cnt+1)
6062
break
6163

62-
assert_allclose(fer_array_test, fer_array_ref, rtol=2e-1, atol=0)
64+
assert_allclose(fer_array_test, fer_array_ref, rtol=.5, atol=0)

commpy/channels.py

Lines changed: 126 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# Authors: Veeresh Taranalli <veeresht@gmail.com> & Bastien Trotobas <bastien.trotobas@gmail.com>
32
# License: BSD 3-Clause
43

@@ -20,7 +19,7 @@
2019

2120
from __future__ import division, print_function # Python 2 compatibility
2221

23-
from numpy import complex, abs, sqrt, sum, zeros, identity, hstack, einsum, trace, kron, absolute
22+
from numpy import complex, abs, sqrt, sum, zeros, identity, hstack, einsum, trace, kron, absolute, fromiter
2423
from numpy.random import randn, random, standard_normal
2524
from scipy.linalg import sqrtm
2625

@@ -71,7 +70,7 @@ def set_SNR_dB(self, SNR_dB, code_rate=1, Es=1):
7170
Average symbol energy
7271
"""
7372

74-
self.noise_std = sqrt((self.isComplex + 1) * self.nb_tx * Es / (code_rate * 10**(SNR_dB/10)))
73+
self.noise_std = sqrt((self.isComplex + 1) * self.nb_tx * Es / (code_rate * 10 ** (SNR_dB / 10)))
7574

7675
def set_SNR_lin(self, SNR_lin, code_rate=1, Es=1):
7776

@@ -99,7 +98,6 @@ def isComplex(self):
9998

10099

101100
class SISOFlatChannel(_FlatChannel):
102-
103101
"""
104102
Constructs a SISO channel with a flat fading.
105103
The channel coefficient are normalized i.e. the mean magnitude is 1.
@@ -108,10 +106,11 @@ class SISOFlatChannel(_FlatChannel):
108106
----------
109107
noise_std : float, optional
110108
Noise standard deviation.
111-
Default value is None and then the value must set later.
109+
*Default* value is None and then the value must set later.
112110
113111
fading_param : tuple of 2 floats, optional
114-
Parameters of the fading (see attribute for details). Default value is (1,0) i.e. no fading.
112+
Parameters of the fading (see attribute for details).
113+
*Default* value is (1,0) i.e. no fading.
115114
116115
Attributes
117116
----------
@@ -240,7 +239,6 @@ def k_factor(self):
240239

241240

242241
class MIMOFlatChannel(_FlatChannel):
243-
244242
"""
245243
Constructs a MIMO channel with a flat fading based on the Kronecker model.
246244
The channel coefficient are normalized i.e. the mean magnitude is 1.
@@ -255,15 +253,15 @@ class MIMOFlatChannel(_FlatChannel):
255253
256254
noise_std : float, optional
257255
Noise standard deviation.
258-
Default value is None and then the value must set later.
256+
*Default* value is None and then the value must set later.
259257
260258
fading_param : tuple of 3 floats, optional
261259
Parameters of the fading. The complete tuple must be set each time.
262-
Default value is (zeros((nb_rx, nb_tx)), identity(nb_tx), identity(nb_rx)) i.e. Rayleigh fading.
260+
*Default* value is (zeros((nb_rx, nb_tx)), identity(nb_tx), identity(nb_rx)) i.e. Rayleigh fading.
263261
264262
Attributes
265263
----------
266-
fading_param : tuple of 2 floats
264+
fading_param : tuple of 3 2D ndarray
267265
Parameters of the fading.
268266
Raise ValueError when sets with value that would lead to a non-normalized channel.
269267
@@ -275,9 +273,7 @@ class MIMOFlatChannel(_FlatChannel):
275273
276274
Classical fadings:
277275
278-
* (zeros((nb_rx, nb_tx)), identity(nb_tx), identity(nb_rx)): Rayleigh fading.
279-
280-
* Others: rician fading.
276+
* (zeros((nb_rx, nb_tx)), identity(nb_tx), identity(nb_rx)): Uncorrelated Rayleigh fading.
281277
282278
noise_std : float
283279
Noise standard deviation. None is the value has not been set yet.
@@ -407,6 +403,119 @@ def k_factor(self):
407403
LOS_gain = einsum('ij,ij->', absolute(self.fading_param[0]), absolute(self.fading_param[0]))
408404
return LOS_gain / NLOS_gain
409405

406+
def uncorr_rayleigh_fading(self, dtype):
407+
""" Set the fading parameters to an uncorrelated Rayleigh channel.
408+
409+
Parameters
410+
----------
411+
dtype : dtype
412+
Type of the channel
413+
"""
414+
self.fading_param = zeros((self.nb_rx, self.nb_tx), dtype), identity(self.nb_tx), identity(self.nb_rx)
415+
416+
def expo_corr_rayleigh_fading(self, t, r):
417+
""" Set the fading parameters to a complex correlated Rayleigh channel following the exponential model.
418+
419+
See: S. L. Loyka, "Channel capacity if MIMO architecture using the exponential correlation matrix ", IEEE
420+
Commun. Lett., vol.5, n. 9, p. 369-371, sept. 2001.
421+
422+
Parameters
423+
----------
424+
t : complex with abs(t) = 1
425+
Correlation coefficient for the transceiver.
426+
427+
r : complex with abs(r) = 1
428+
Correlation coefficient for the receiver.
429+
430+
Raises
431+
------
432+
ValueError
433+
If abs(t) != 1 or abs(r) != 1
434+
"""
435+
# Check inputs
436+
if abs(t) - 1 > 1e-4:
437+
raise ValueError('abs(t) must be one.')
438+
if abs(r) - 1 > 1e-4:
439+
raise ValueError('abs(r) must be one.')
440+
441+
# Construct the exponent matrix
442+
expo_tx = fromiter((j - i for i in range(self.nb_tx) for j in range(self.nb_tx)), int, self.nb_tx ** 2)
443+
expo_rx = fromiter((j - i for i in range(self.nb_rx) for j in range(self.nb_rx)), int, self.nb_rx ** 2)
444+
445+
# Reshape
446+
expo_tx = expo_tx.reshape(self.nb_tx, self.nb_tx)
447+
expo_rx = expo_rx.reshape(self.nb_rx, self.nb_rx)
448+
449+
# Set fading
450+
self.fading_param = zeros((self.nb_rx, self.nb_tx), complex), t ** expo_tx, r ** expo_rx
451+
452+
def uncorr_rician_fading(self, mean, k_factor):
453+
""" Set the fading parameters to an uncorrelated rician channel.
454+
455+
mean will be scaled to fit the required k-factor.
456+
457+
Parameters
458+
----------
459+
mean : ndarray (shape: nb_rx x nb_tx)
460+
Mean of the channel gain.
461+
462+
k_factor : positive float
463+
Requested k-factor (the power ratio between LOS and NLOS).
464+
"""
465+
nb_antennas = mean.size
466+
NLOS_gain = nb_antennas / (k_factor + 1)
467+
mean = mean * sqrt(k_factor * NLOS_gain / einsum('ij,ij->', absolute(mean), absolute(mean)))
468+
self.fading_param = mean, identity(self.nb_tx) * NLOS_gain / nb_antennas, identity(self.nb_rx)
469+
470+
def expo_corr_rician_fading(self, mean, k_factor, t, r):
471+
""" Set the fading parameters to a complex correlated rician channel following the exponential model.
472+
473+
See: S. L. Loyka, "Channel capacity if MIMO architecture using the exponential correlation matrix ", IEEE
474+
Commun. Lett., vol.5, n. 9, p. 369-371, sept. 2001.
475+
476+
mean and correlation matricies will be scaled to fit the required k-factor.
477+
478+
Parameters
479+
----------
480+
mean : ndarray (shape: nb_rx x nb_tx)
481+
Mean of the channel gain.
482+
483+
k_factor : positive float
484+
Requested k-factor (the power ratio between LOS and NLOS).
485+
486+
t : complex with abs(t) = 1
487+
Correlation coefficient for the transceiver.
488+
489+
r : complex with abs(r) = 1
490+
Correlation coefficient for the receiver.
491+
492+
Raises
493+
------
494+
ValueError
495+
If abs(t) != 1 or abs(r) != 1
496+
"""
497+
# Check inputs
498+
if abs(t) - 1 > 1e-4:
499+
raise ValueError('abs(t) must be one.')
500+
if abs(r) - 1 > 1e-4:
501+
raise ValueError('abs(r) must be one.')
502+
503+
# Scaling
504+
nb_antennas = mean.size
505+
NLOS_gain = nb_antennas / (k_factor + 1)
506+
mean = mean * sqrt(k_factor * NLOS_gain / einsum('ij,ij->', absolute(mean), absolute(mean)))
507+
508+
# Construct the exponent matrix
509+
expo_tx = fromiter((j - i for i in range(self.nb_tx) for j in range(self.nb_tx)), int, self.nb_tx ** 2)
510+
expo_rx = fromiter((j - i for i in range(self.nb_rx) for j in range(self.nb_rx)), int, self.nb_rx ** 2)
511+
512+
# Reshape
513+
expo_tx = expo_tx.reshape(self.nb_tx, self.nb_tx)
514+
expo_rx = expo_rx.reshape(self.nb_rx, self.nb_rx)
515+
516+
# Set fading
517+
self.fading_param = mean, t ** expo_tx * NLOS_gain / nb_antennas, r ** expo_rx
518+
410519

411520
def bec(input_bits, p_e):
412521
"""
@@ -475,14 +584,14 @@ def awgn(input_signal, snr_dB, rate=1.0):
475584
Output signal from the channel with the specified SNR.
476585
"""
477586

478-
avg_energy = sum(abs(input_signal) * abs(input_signal))/len(input_signal)
479-
snr_linear = 10**(snr_dB/10.0)
480-
noise_variance = avg_energy/(2*rate*snr_linear)
587+
avg_energy = sum(abs(input_signal) * abs(input_signal)) / len(input_signal)
588+
snr_linear = 10 ** (snr_dB / 10.0)
589+
noise_variance = avg_energy / (2 * rate * snr_linear)
481590

482591
if isinstance(input_signal[0], complex):
483592
noise = (sqrt(noise_variance) * randn(len(input_signal))) + (sqrt(noise_variance) * randn(len(input_signal))*1j)
484593
else:
485-
noise = sqrt(2*noise_variance) * randn(len(input_signal))
594+
noise = sqrt(2 * noise_variance) * randn(len(input_signal))
486595

487596
output_signal = input_signal + noise
488597

commpy/examples/plotConsModem.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Authors: Youness Akourim <akourim97@gmail.com>
2+
# License: BSD 3-Clause
3+
4+
from commpy.modulation import PSKModem, QAMModem
5+
6+
# =============================================================================
7+
# Example constellation plot of Modem
8+
# =============================================================================
9+
10+
# Constellation corresponding to PSKModem for 4 bits per symbols
11+
psk = PSKModem(16)
12+
psk.plot_constellation()
13+
14+
# Constellation corresponding to QAMModem for 2 bits per symbols
15+
qam = QAMModem(4)
16+
qam.plot_constellation()

0 commit comments

Comments
 (0)