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."
Parameters:
-
constellation
(ArrayLike
) –The constellation $\mathbf{X}$ of the modulation. Must be a 1D-array containing $M$ real or complex numbers.
-
labeling
(ArrayLike
) –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$.
>>> modulation = komm.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$.
>>> modulation = komm.Modulation( ... constellation=[0, -1, 1, 1j], ... labeling=[[0, 0], [0, 1], [1, 0], [1, 1]], ... )
constellation
NDArray[T]
cached
property
The constellation $\mathbf{X}$ of the modulation.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.constellation
array([-0.5, 0. , 0.5, 2. ])
labeling
NDArray[integer]
cached
property
The labeling $\mathbf{Q}$ of the modulation.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.labeling
array([[1, 0],
[1, 1],
[0, 1],
[0, 0]])
inverse_labeling
dict[tuple[int, ...], int]
cached
property
The inverse labeling of the modulation. It is a dictionary that maps each binary tuple to the corresponding constellation index.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.inverse_labeling
{(1, 0): 0, (1, 1): 1, (0, 1): 2, (0, 0): 3}
order
int
cached
property
The order $M$ of the modulation.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.order
4
bits_per_symbol
int
cached
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:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.bits_per_symbol
2
energy_per_symbol
float
cached
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)} \lVert x_i \rVert^2, $$ where $\lVert x_i \rVert^2$ is the energy of constellation symbol $x_i$, and $M$ is the order of the modulation.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.energy_per_symbol
1.125
>>> modulation = komm.Modulation(
... constellation=[0, -1, 1, 1j],
... labeling=[[0, 0], [0, 1], [1, 0], [1, 1]],
... )
>>> modulation.energy_per_symbol
0.75
energy_per_bit
float
cached
property
The average bit energy $E_\mathrm{b}$ of the constellation. It assumes equiprobable symbols. It is given by $$ E_\mathrm{b} = \frac{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:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.energy_per_bit
0.5625
>>> modulation = komm.Modulation(
... constellation=[0, -1, 1, 1j],
... labeling=[[0, 0], [0, 1], [1, 0], [1, 1]],
... )
>>> modulation.energy_per_bit
0.375
symbol_mean
float | complex
cached
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:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.symbol_mean
0.5
>>> modulation = komm.Modulation(
... constellation=[0, -1, 1, 1j],
... labeling=[[0, 0], [0, 1], [1, 0], [1, 1]],
... )
>>> modulation.symbol_mean
0.25j
minimum_distance
float
cached
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 } \lVert x_i - x_j \rVert. $$
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.minimum_distance
0.5
>>> modulation = komm.Modulation(
... constellation=[0, -1, 1, 1j],
... labeling=[[0, 0], [0, 1], [1, 0], [1, 1]],
... )
>>> modulation.minimum_distance
1.0
modulate()
Modulates one or more sequences of bits to their corresponding constellation symbols.
Parameters:
-
input
(ArrayLike
) –The input sequence(s). Can be either a single sequence whose length is a multiple of $m$, or a multidimensional array where the last dimension is a multiple of $m$.
Returns:
-
output
(NDArray[T]
) –The output sequence(s). Has the same shape as the input, with the last dimension divided by $m$.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.modulate([0, 0, 1, 1, 0, 0, 1, 0])
array([ 2. , 0. , 2. , -0.5])
>>> modulation.modulate([[0, 0, 1, 1], [0, 0, 1, 0]])
array([[ 2. , 0. ],
[ 2. , -0.5]])
demodulate_hard()
Demodulates one or more sequences of received points to their corresponding sequences of hard bits ($\mathtt{0}$ or $\mathtt{1}$) using hard-decision decoding.
Parameters:
-
input
(ArrayLike
) –The input sequence(s). Can be either a single sequence, or a multidimensional array.
Returns:
-
output
(NDArray[integer]
) –The output sequence(s). Has the same shape as the input, with the last dimension multiplied by $m$.
Note
This method implements the general minimum Euclidean distance hard demodulator, assuming uniformly distributed symbols.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.demodulate_hard([2.17, -0.06, 1.94, -0.61])
array([0, 0, 1, 1, 0, 0, 1, 0])
>>> modulation.demodulate_hard([[2.17, -0.06], [1.94, -0.61]])
array([[0, 0, 1, 1],
[0, 0, 1, 0]])
demodulate_soft()
Demodulates one or more sequences of received points to their corresponding sequences of soft bits (L-values) using soft-decision decoding. The soft bits are the log-likelihood ratios of the bits, where positive values correspond to bit $\mathtt{0}$ and negative values correspond to bit $\mathtt{1}$.
Parameters:
-
input
(ArrayLike
) –The received sequence(s). Can be either a single sequence, or a multidimensional array.
-
snr
(float
) –The signal-to-noise ratio (SNR) of the channel. It should be a positive real number. The default value is
1.0
.
Returns:
-
output
(NDArray[floating]
) –The output sequence(s). Has the same shape as the input, with the last dimension multiplied by $m$.
Note
This method implements the general soft demodulator, assuming uniformly distributed symbols.
Examples:
>>> modulation = komm.Modulation(
... constellation=[-0.5, 0.0, 0.5, 2.0],
... labeling=[[1, 0], [1, 1], [0, 1], [0, 0]],
... )
>>> modulation.demodulate_soft([2.17, -0.06, 1.94, -0.61], snr=100.0).round(1)
array([ 416. , 245.3, -27.6, -16.9, 334.2, 184. , -108.4, 32. ])
>>> modulation.demodulate_soft([[2.17, -0.06], [1.94, -0.61]], snr=100.0).round(1)
array([[ 416. , 245.3, -27.6, -16.9],
[ 334.2, 184. , -108.4, 32. ]])