Skip to content

komm.Modulation

General modulation scheme. A modulation scheme of order $M = 2^m$ is defined by a constellation $\mathbf{X}$, which is a real or complex vector of length $M$, and a binary labeling $\mathbf{Q}$, which is an $M \times m$ binary matrix whose rows are all distinct. The $i$-th element of $\mathbf{X}$, for $i \in [0:M)$, is denoted by $x_i$ and is called the $i$-th constellation symbol. The $i$-th row of $\mathbf{Q}$, for $i \in [0:M)$, is called the binary representation of the $i$-th constellation symbol.

For more details, see SA15, Sec. 2.5.

__init__

Constructor for the class.

Parameters:

  • constellation (Array1D[float] | Array1D[complex])

    The constellation $\mathbf{X}$ of the modulation. Must be a 1D-array containing $M$ real or complex numbers.

  • labeling (Array2D[int])

    The binary labeling $\mathbf{Q}$ of the modulation. Must be a 2D-array of shape $(M, m)$ where each row is a distinct binary $m$-tuple.

Examples:

The real modulation scheme depicted in the figure below has $M = 4$ and $m = 2$.

Example for real modulation with M = 4

The constellation is given by $$ \mathbf{X} = \begin{bmatrix} -0.5 \\ 0.0 \\ 0.5 \\ 2.0 \end{bmatrix}, $$ and the binary labeling is given by $$ \mathbf{Q} = \begin{bmatrix} 1 & 0 \\ 1 & 1 \\ 0 & 1 \\ 0 & 0 \end{bmatrix}. $$

>>> komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])

The complex modulation scheme depicted in the figure below has $M = 4$ and $m = 2$.

Example for complex modulation with M = 4

The constellation is given by $$ \mathbf{X} = \begin{bmatrix} 0 \\ -1 \\ 1 \\ \mathrm{j} \end{bmatrix}, $$ and the binary labeling is given by $$ \mathbf{Q} = \begin{bmatrix} 0 & 0 \\ 0 & 1 \\ 1 & 0 \\ 1 & 1 \end{bmatrix}. $$

>>> komm.Modulation(constellation=[0, -1, 1, 1j], labeling=[[0, 0], [0, 1], [1, 0], [1, 1]])
Modulation(constellation=[0j, (-1+0j), (1+0j), 1j], labeling=[[0, 0], [0, 1], [1, 0], [1, 1]])

constellation property

The constellation $\mathbf{X}$ of the modulation.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.constellation
array([-0.5,  0. ,  0.5,  2. ])

labeling property

The binary labeling $\mathbf{Q}$ of the modulation.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.labeling
array([[1, 0],
       [1, 1],
       [0, 1],
       [0, 0]])

order property

The order $M$ of the modulation.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.order
4

bits_per_symbol property

The number $m$ of bits per symbol of the modulation. It is given by $m = \log_2 M$, where $M$ is the order of the modulation.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.bits_per_symbol
2

energy_per_symbol property

The average symbol energy $E_\mathrm{s}$ of the constellation. It assumes equiprobable symbols. It is given by $$ E_\mathrm{s} = \frac{1}{M} \sum_{i \in [0:M)} |x_i|^2, $$ where $|x_i|^2$ is the energy of constellation symbol $x_i$, and $M$ is the order of the modulation.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.energy_per_symbol
np.float64(1.125)
>>> mod = komm.Modulation(constellation=[0, -1, 1, 1j], labeling=[[0, 0], [0, 1], [1, 0], [1, 1]])
>>> mod.energy_per_symbol
np.float64(0.75)

energy_per_bit property

The average bit energy $E_\mathrm{b}$ of the constellation. It assumes equiprobable symbols. It is given by $E_\mathrm{b} = E_\mathrm{s} / m$, where $E_\mathrm{s}$ is the average symbol energy, and $m$ is the number of bits per symbol of the modulation.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.energy_per_bit
np.float64(0.5625)
>>> mod = komm.Modulation(constellation=[0, -1, 1, 1j], labeling=[[0, 0], [0, 1], [1, 0], [1, 1]])
>>> mod.energy_per_bit
np.float64(0.375)

symbol_mean property

The mean $\mu_\mathrm{s}$ of the constellation. It assumes equiprobable symbols. It is given by $$ \mu_\mathrm{s} = \frac{1}{M} \sum_{i \in [0:M)} x_i. $$

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.symbol_mean
np.float64(0.5)
>>> mod = komm.Modulation(constellation=[0, -1, 1, 1j], labeling=[[0, 0], [0, 1], [1, 0], [1, 1]])
>>> mod.symbol_mean
np.complex128(0.25j)

minimum_distance property

The minimum Euclidean distance $d_\mathrm{min}$ of the constellation. It is given by $$ d_\mathrm{min} = \min_{i, j \in [0:M), ~ i \neq j} |x_i - x_j|. $$

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.minimum_distance
np.float64(0.5)
>>> mod = komm.Modulation(constellation=[0, -1, 1, 1j], labeling=[[0, 0], [0, 1], [1, 0], [1, 1]])
>>> mod.minimum_distance
np.float64(1.0)

modulate

Modulates a sequence of bits to its corresponding constellation symbols.

Parameters:

  • bits (Array1D[int])

    The bits to be modulated. It should be a 1D-array of integers in the set $\{ 0, 1 \}$. Its length must be a multiple of $m$.

Returns:

  • symbols (Array1D[complex] | Array1D[float])

    The constellation symbols corresponding to bits. It is a 1D-array of real or complex numbers. Its length is equal to the length of bits divided by $m$.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> mod.modulate([0, 0, 1, 1, 0, 0, 1, 0])
array([ 2. ,  0. ,  2. , -0.5])

demodulate

Demodulates a sequence of received points to a sequence of bits.

Parameters:

  • received (Array1D[complex] | Array1D[float])

    The received points to be demodulated. It should be a 1D-array of real or complex numbers. It may be of any length.

  • decision_method (str)

    The decision method to be used. It should be either 'hard' (corresponding to hard-decision decoding) or 'soft' (corresponding to soft-decision decoding). The default value is 'hard'.

  • kwargs

    Keyword arguments to be passed to the demodulator.

Returns:

  • bits_or_soft_bits (Array1D[int] | Array1D[float])

    The (hard or soft) bits corresponding to received. In the case of hard-decision decoding, it is a 1D-array of bits (integers in the set $\{ 0, 1 \}$); in the case of of soft-decision decoding, it is a 1D-array of L-values (real numbers, where positive values correspond to bit $0$ and negative values correspond to bit $1$). Its length is equal to the length of received multiplied by $m$.

Examples:

>>> mod = komm.Modulation(constellation=[-0.5, 0.0, 0.5, 2.0], labeling=[[1, 0], [1, 1], [0, 1], [0, 0]])
>>> received = [2.17, -0.06, 1.94, -0.61]
>>> mod.demodulate(received)
array([0, 0, 1, 1, 0, 0, 1, 0])
>>> mod.demodulate(received, decision_method='soft', channel_snr=100.0)
array([ 416.        ,  245.33333333,  -27.5555556 ,  -16.88888889,
        334.22222222,  184.        , -108.44444444,   32.        ])