Skip to content
This repository was archived by the owner on Mar 25, 2024. It is now read-only.

Repository files navigation

Build StatusPyPiCoverage

ModulationPy

Digital baseband linear modems: M-PSK and M-QAM.

Motivation

The main idea is to develop a Python module that allows replacing related to baseband digital linear modulations MatLab/Octave functions and objects. This project is inspired by CommPy open-source project.

Theory basics

1. Linearity

Linear modulation schemes have a canonical form [1]:

 s(t) = s_I(t)cos(2\pi f_c t) - s_Q(t)cos(2\pi f_c t) \qquad (1)

where s_I(t) is the In-phase part, s_Q(t) is the Quadrature part, f_c is the carrier frequency, and t is the time moment. In-phase and Quadrature parts are low-pass signals that linearly correlate with an information signal.

2. Baseband representation

Modulation scheme can also be modeled without consideration of the carrier frequency and bit duration. The baseband analogs can be used for research due to the main properties depend on the envelope (complex symbols).

3. Modulation order

Modulation order means number of possible modulation symbols. The number of bits per modulation symbol depend on the modulation order:

 N = log_2(M) \qquad(2)

Modulation order relates to gross bit rate concept:

 R_b = R_slog_2(N) \qquad(3)

where R_s is the baud or symbol rate. Baud rate usually relates to the coherence bandwidth B_c (see more in [2]).

See more in "Basics of linear digital modulations" (slides).

Installation

Released version on PyPi:

$ pip install ModulationPy

To build by sources, clone from github and install as follows:

$ git clone https://github.com/kirlf/ModulationPy.git
$ cd ModulationPy
$ python3 setup.py install

What are modems available?

  • M-PSK: Phase Shift Keying
  • M-QAM: Quadratured Amplitude Modulation

where M is the modulation order.

1. M-PSK

M-PSK modem is available in class PSKModem with the following parameters:

ParameterPossible valuesDescription
Mpositive integer values power of 2Modulation order. Specify the number of points in the signal constellation as scalar value that is a positive integer power of two.
phifloat valuesPhase rotation. Specify the phase offset of the signal constellation, in radians, as a real scalar value. The default is 0.
gray_mapTrue or FalseSpecifies mapping rule. If parametr is True the modem works with Gray mapping, else it works with Binary mapping. The default is True.
bin_inputTrue or FalseSpecify whether the input of modulate() method is bits or integers. When you set this property to True, the modulate() method input requires a column vector of bit values. The length of this vector must an integer multiple of log2(M). The default is True.
soft_decisionTrue or FalseSpecify whether the output values of demodulate() method is demodulated as hard or soft decisions. If parametr is True the output will be Log-likelihood ratios (LLR's), else binary symbols. The default is True.
bin_outputTrue or FalseSpecify whether the output of demodulate() method is bits or integers. The default is True.

The mapping of into the modulation symbols is done by the following formula [3]:

 r = exp(j\phi + j2\pi m/M)

where  \phi is the phase rotation,  m is the mapped to decimals (in range between 0 and M-1) input symbol, and  M is the modulation order.

2. M-QAM

M-QAM modem is available in class QAMModem with the following parameters:

ParameterPossible valuesDescription
Mpositive integer values power of 2Modulation order. Specify the number of points in the signal constellation as scalar value that is a positive integer power of two.
gray_mapTrue or FalseSpecifies mapping rule. If parametr is True the modem works with Gray mapping, else it works with Binary mapping. The default is True.
bin_inputTrue or FalseSpecify whether the input of modulate() method is bits or integers. When you set this property to True, the modulate() method input requires a column vector of bit values. The length of this vector must an integer multiple of log2(M). The default is True.
soft_decisionTrue or FalseSpecify whether the output values of demodulate() method is demodulated as hard or soft decisions. If parametr is True the output will be Log-likelihood ratios (LLR's), else binary symbols. The default is True.
bin_outputTrue or FalseSpecify whether the output of demodulate() method is bits or integers. The default is True.

Now the QAM modulation is designed as in qammod Octave function [4]. It requires only "even" (in sense, that log2(M) is even) modulation schemes (4-QAM, 16-QAM, 64-QAM and so on - universal and simple). Anyway, there are no "odd" modulation schemes in popular wireless communication standards.

How to use?

1. Initialization.

E.g., QPSK with the pi/4 phase offset, binary input and Gray mapping:

fromModulationPyimportPSKModem, QAMModemimportnumpyasnpmodem=PSKModem(4, np.pi/4,
gray_map=True,
bin_input=True)

To show signal constellation use the plot_const() method:

modem.plot_const()

E.g. 16-QAM with decimal input and Gray mapping

modem=QAMModem(16,
gray_map=True, bin_input=False)
modem.plot_const()

2. Modulation and demodulation

To modulate and demodulate use modulate() and demodulate() methods.

The method modulate() has the one input argument:

  • decimal or binary stream to be modulated (1-D ndarray of ints).

The method demodulate() has the two input arguments:

  • data stream to be demodulated (1-D ndarray of complex symbols) and

  • additive noise variance (float, default is 1.0).

E.g., QPSK (binary input/output):

importnumpyasnpfromModulationPyimportPSKModemmodem=PSKModem(4, np.pi/4, bin_input=True,
soft_decision=False,
bin_output=True)
msg=np.array([0, 0, 0, 1, 1, 0, 1, 1]) # input messagemodulated=modem.modulate(msg) # modulationdemodulated=modem.demodulate(modulated) # demodulationprint("Modulated message:\n"+str(modulated))
print("Demodulated message:\n"+str(demodulated))
>>>Modulatedmessage:
[ 0.70710678+0.70710678j0.70710678-0.70710678j-0.70710678+0.70710678j-0.70710678-0.70710678j]
>>>Demodulatedmessage:
[0.0.0.1.1.0.1.1.]

E.g., QPSK (decimal input/output):

importnumpyasnpfromModulationPyimportPSKModemmodem=PSKModem(4, np.pi/4, bin_input=False,
soft_decision=False,
bin_output=False)
msg=np.array([0, 1, 2, 3]) # input messagemodulated=modem.modulate(msg) # modulationdemodulated=modem.demodulate(modulated) # demodulationprint("Modulated message:\n"+str(modulated))
print("Demodulated message:\n"+str(demodulated))
>>>Modulatedmessage:
[ 0.70710678+0.70710678j-0.70710678+0.70710678j0.70710678-0.70710678j-0.70710678-0.70710678j]
>>>Demodulatedmessage:
[0, 1, 2, 3]

E.g., 16-QAM (decimal input/output):

importnumpyasnpfromModulationPyimportQAMModemmodem=PSKModem(16, bin_input=False,
soft_decision=False,
bin_output=False)
msg=np.array([iforiinrange(16)]) # input messagemodulated=modem.modulate(msg) # modulationdemodulated=modem.demodulate(modulated) # demodulationprint("Demodulated message:\n"+str(demodulated))
>>>Demodulatedmessage:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

3. Bit-error ratio performance

Let us demonstrate this at example with the following system model:

Fig. 1. The structural scheme of the test communication model.

The simulation results will be compared with theoretical curves [5]:

 P_b = \frac{erfc \left( \sqrt{log_2(M)\frac{E_b}{N_o}}\sin\left( \frac{\pi}{M} \right) \right)}{log_2(M)}

where M >4 , erfc(*) denotes the complementary error function, E_b is the energy per one bit, and N_o is the noise spectral density.

In case of BPSK and QPSK the following formula should be used for error probability:

 P_{b, BQ} = \frac{1}{2}erfc \left( \sqrt{\frac{E_b}{N_o}}\right)} \qquad (6)

The results are presented below.

Fig. 2. Bit-error ratio curves in presence of AWGN (M-PSK).

The theoretical BER curves for M-QAM case can be obtained via the following formula [5]:

P_{b,QAM} = 2 \left(\frac{\sqrt{M}-1}{M\log_2M}\right)erfc\left( \sqrt{\frac{3\log_2M}{2(M-1)} \frac{E_b}{N_0}} \right) \qquad (7)

The theoretical BER curve for 4-QAM is identical to the BPSK/QPSK case.

Fig. 3. Bit-error ratio curves in presence of AWGN (M-QAM).

The source codes of the simulation are available here: M-PSK, M-QAM.

The dismatchings are appeared cause of small number of averages. Anyway, it works.

4. Execution time performance

To demonstrate execution time performance let us comparate our package with another implementation, e.g. with the mentioned above CommPy.

The demodulation algorithm is developed according to following fomula in our project [6],[7]:

 L(b) = -\frac{1}{\sigma^2} \left( \min_{s \epsilon S_0} \left( (x - s_x)^2 + (y - s_y)^2 \right) - \min_{s \epsilon S_1} \left( (x - s_x)^2 + (y - s_y)^2 \right)\right) \qquad(7)

where  x is the In-phase coordinate of the received symbol,  y is the Quadrature coordinate of the received symbol,  s_x is the In-phase coordinate of ideal symbol or constellation point,  s_y is the Quadrature coordinate of ideal symbol or constellation point,  S_0 is the ideal symbols or constellation points with bit 0, at the given bit position,  S_1 is the ideal symbols or constellation points with bit 1, at the given bit position,  b is the transmitted bit (one of the K bits in an M-ary symbol, assuming all M symbols are equally probable,  \sigma^2 is the noise variance of baseband signal.

Comparison information:

Results:

Method (package)Average execution time (ms)
modulation (ModulationPy): QPSK10.3
modulation (CommPy): QPSK15.7
demodulation (ModulationPy): QPSK0.4
demodulation (CommPy): QPSK319
modulation (ModulationPy): 256-QAM8.9
modulation (CommPy): 256-QAM11.3
demodulation (ModulationPy): 256-QAM42.6
demodulation (CommPy): 256-QAM22 000

Yes, I admit that our implementation is slower than MatLab blocks and functions (see "Examples"). However, I believe that this is a good start!

References

  1. Haykin S. Communication systems. – John Wiley & Sons, 2008. — p. 93
  2. Goldsmith A. Wireless communications. – Cambridge university press, 2005. – p. 88-92
  3. MathWorks: comm.PSKModulator (https://www.mathworks.com/help/comm/ref/comm.pskmodulator-system-object.html?s_tid=doc_ta)
  4. Octave: qammod (https://octave.sourceforge.io/communications/function/qammod.html)
  5. Link Budget Analysis: Digital Modulation, Part 3 (www.AtlantaRF.com)
  6. Viterbi, A. J. (1998). An intuitive justification and a simplified implementation of the MAP decoder for convolutional codes. IEEE Journal on Selected Areas in Communications, 16(2), 260-264.
  7. MathWorks: Approximate LLR Algorithm (https://www.mathworks.com/help/comm/ug/digital-modulation.html#brc6ymu)