-
Notifications
You must be signed in to change notification settings - Fork 194
Expand file tree
/
Copy pathmodulation.py
More file actions
202 lines (154 loc) · 6.63 KB
/
modulation.py
File metadata and controls
202 lines (154 loc) · 6.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# Authors: Veeresh Taranalli <veeresht@gmail.com>
# License: BSD 3-Clause
"""
==================================================
Modulation Demodulation (:mod:`commpy.modulation`)
==================================================
.. autosummary::
:toctree: generated/
PSKModem -- Phase Shift Keying (PSK) Modem.
QAMModem -- Quadrature Amplitude Modulation (QAM) Modem.
mimo_ml -- MIMO Maximum Likelihood (ML) Detection.
"""
from numpy import arange, array, zeros, pi, cos, sin, sqrt, log2, argmin, \
hstack, repeat, tile, dot, sum, shape, concatenate, exp, log
from itertools import product
from commpy.utilities import bitarray2dec, dec2bitarray
from numpy.fft import fft, ifft
__all__ = ['PSKModem', 'QAMModem', 'mimo_ml']
class Modem:
def modulate(self, input_bits):
""" Modulate (map) an array of bits to constellation symbols.
Parameters
----------
input_bits : 1D ndarray of ints
Inputs bits to be modulated (mapped).
Returns
-------
baseband_symbols : 1D ndarray of complex floats
Modulated complex symbols.
"""
index_list = map(lambda i: bitarray2dec(input_bits[i:i+self.num_bits_symbol]), \
xrange(0, len(input_bits), self.num_bits_symbol))
baseband_symbols = self.constellation[index_list]
return baseband_symbols
def demodulate(self, input_symbols, demod_type, noise_var = 0):
""" Demodulate (map) a set of constellation symbols to corresponding bits.
Supports hard-decision demodulation only.
Parameters
----------
input_symbols : 1D ndarray of complex floats
Input symbols to be demodulated.
demod_type : string
'hard' for hard decision output (bits)
'soft' for soft decision output (LLRs)
noise_var : float
AWGN variance. Needs to be specified only if demod_type is 'soft'
Returns
-------
demod_bits : 1D ndarray of ints
Corresponding demodulated bits.
"""
if demod_type == 'hard':
index_list = map(lambda i: argmin(abs(input_symbols[i] - self.constellation)), \
xrange(0, len(input_symbols)))
demod_bits = hstack(map(lambda i: dec2bitarray(i, self.num_bits_symbol),
index_list))
elif demod_type == 'soft':
demod_bits = zeros(len(input_symbols) * self.num_bits_symbol)
for i in arange(len(input_symbols)):
current_symbol = input_symbols[i]
for bit_index in arange(self.num_bits_symbol):
llr_num = 0
llr_den = 0
for const_index in self.symbol_mapping:
if (const_index >> bit_index) & 1:
llr_num = llr_num + exp((-abs(current_symbol - self.constellation[const_index])**2)/noise_var)
else:
llr_den = llr_den + exp((-abs(current_symbol - self.constellation[const_index])**2)/noise_var)
demod_bits[i*self.num_bits_symbol + self.num_bits_symbol - 1 - bit_index] = log(llr_num/llr_den)
else:
pass
# throw an error
return demod_bits
def mapping(self, input_symbols, map_type, noise_var = 0):
if map_type == 'hard':
index_list = map(lambda i: self.constellation[argmin(abs(input_symbols[i] - self.constellation))], \
xrange(0, len(input_symbols)))
else:
pass
return index_list
class PSKModem(Modem):
""" Creates a Phase Shift Keying (PSK) Modem object. """
def _constellation_symbol(self, i):
return cos(2*pi*(i-1)/self.m) + sin(2*pi*(i-1)/self.m)*(0+1j)
def __init__(self, m):
""" Creates a Phase Shift Keying (PSK) Modem object.
Parameters
----------
m : int
Size of the PSK constellation.
"""
self.m = m
self.num_bits_symbol = int(log2(self.m))
self.symbol_mapping = arange(self.m)
self.constellation = array(map(self._constellation_symbol,
self.symbol_mapping))
class QAMModem(Modem):
""" Creates a Quadrature Amplitude Modulation (QAM) Modem object."""
def _constellation_symbol(self, i):
return (2*i[0]-1) + (2*i[1]-1)*(1j)
def __init__(self, m):
""" Creates a Quadrature Amplitude Modulation (QAM) Modem object.
Parameters
----------
m : int
Size of the QAM constellation.
"""
self.m = m
self.num_bits_symbol = int(log2(self.m))
self.symbol_mapping = arange(self.m)
mapping_array = arange(1, sqrt(self.m)+1) - (sqrt(self.m)/2)
self.constellation = array(map(self._constellation_symbol,
list(product(mapping_array, repeat=2))))
def ofdm_tx(x, nfft, nsc, cp_length):
""" OFDM Transmit signal generation """
nfft = float(nfft)
nsc = float(nsc)
cp_length = float(cp_length)
ofdm_tx_signal = array([])
for i in xrange(0, shape(x)[1]):
symbols = x[:,i]
ofdm_sym_freq = zeros(nfft, dtype=complex)
ofdm_sym_freq[1:(nsc/2)+1] = symbols[nsc/2:]
ofdm_sym_freq[-(nsc/2):] = symbols[0:nsc/2]
ofdm_sym_time = ifft(ofdm_sym_freq)
cp = ofdm_sym_time[-cp_length:]
ofdm_tx_signal = concatenate((ofdm_tx_signal, cp, ofdm_sym_time))
return ofdm_tx_signal
def ofdm_rx(y, nfft, nsc, cp_length):
""" OFDM Receive Signal Processing """
num_ofdm_symbols = int(len(y)/(nfft + cp_length))
x_hat = zeros([nsc, num_ofdm_symbols], dtype=complex)
for i in xrange(0, num_ofdm_symbols):
ofdm_symbol = y[i*nfft + (i+1)*cp_length:(i+1)*(nfft + cp_length)]
symbols_freq = fft(ofdm_symbol)
x_hat[:,i] = concatenate((symbols_freq[-nsc/2:], symbols_freq[1:(nsc/2)+1]))
return x_hat
def mimo_ml(y, h, constellation):
""" MIMO ML Detection.
parameters
----------
y : 1D ndarray of complex floats
Received complex symbols (shape: num_receive_antennas x 1)
h : 2D ndarray of complex floats
Channel Matrix (shape: num_receive_antennas x num_transmit_antennas)
constellation : 1D ndarray of complex floats
Constellation used to modulate the symbols
"""
m = len(constellation)
x_ideal = array([tile(constellation, m), repeat(constellation, m)])
y_vector = tile(y, m*m)
min_idx = argmin(sum(abs(y_vector - dot(h, x_ideal)), axis=0))
x_r = x_ideal[:, min_idx]
return x_r