mirror of
https://github.com/jhshi/openofdm.git
synced 2024-12-25 08:21:18 +00:00
195 lines
6.3 KiB
Python
195 lines
6.3 KiB
Python
|
|
# 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
|
|
|
|
|
|
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
|