Skip to content

komm.PSKModulation

Phase-shift keying (PSK) modulation. It is a complex modulation scheme in which the constellation symbols are uniformly arranged in a circle. More precisely, the the $i$-th constellation symbol is given by $$ x_i = A \exp \left( \mathrm{j} \frac{2 \pi i}{M} \right) \exp(\mathrm{j} \phi), \quad i \in [0 : M), $$ where $M$ is the order (a power of $2$), $A$ is the amplitude, and $\phi$ is the phase offset of the modulation.

Parameters:

  • order (int)

    The order $M$ of the modulation. It must be a power of $2$.

  • amplitude (float)

    The amplitude $A$ of the constellation. The default value is 1.0.

  • phase_offset (float)

    The phase offset $\phi$ of the constellation. The default value is 0.0.

  • labeling (Literal['natural', 'reflected'] | ArrayLike)

    The binary labeling of the modulation. Can be specified either as a 2D-array of integers (see base class for details), or as a string. In the latter case, the string must be either 'natural' or 'reflected'. The default value is 'reflected', corresponding to the Gray labeling.

Examples:

  1. The $4$-PSK modulation with base amplitude $A = 1$, phase offset $\phi = 0$, and Gray labeling is depicted below.

    4-PSK modulation with Gray labeling.

    >>> psk = komm.PSKModulation(4)
    >>> psk.constellation.round(3)
    array([ 1.+0.j,  0.+1.j, -1.+0.j, -0.-1.j])
    >>> psk.labeling
    array([[0, 0],
           [0, 1],
           [1, 1],
           [1, 0]])
    
  2. The $8$-PSK modulation with base amplitude $A = 0.5$, phase offset $\phi = \pi/8$, and natural labeling is depicted below.

    8-PSK modulation with natural labeling.

    >>> psk = komm.PSKModulation(
    ...     order=8,
    ...     amplitude=0.5,
    ...     phase_offset=np.pi/8,
    ...     labeling='natural',
    ... )
    >>> psk.constellation.round(3)
    array([ 0.462+0.191j,  0.191+0.462j, -0.191+0.462j, -0.462+0.191j,
           -0.462-0.191j, -0.191-0.462j,  0.191-0.462j,  0.462-0.191j])
    >>> psk.labeling
    array([[0, 0, 0],
           [0, 0, 1],
           [0, 1, 0],
           [0, 1, 1],
           [1, 0, 0],
           [1, 0, 1],
           [1, 1, 0],
           [1, 1, 1]])
    

constellation NDArray[complexfloating] cached property

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

Examples:

>>> psk = komm.PSKModulation(4)
>>> psk.constellation.round(3)
array([ 1.+0.j,  0.+1.j, -1.+0.j, -0.-1.j])

labeling NDArray[integer] cached property

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

Examples:

>>> psk = komm.PSKModulation(4)
>>> psk.labeling
array([[0, 0],
       [0, 1],
       [1, 1],
       [1, 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:

>>> psk = komm.PSKModulation(4)
>>> psk.inverse_labeling
{(0, 0): 0, (0, 1): 1, (1, 1): 2, (1, 0): 3}

order int cached property

The order $M$ of the modulation.

Examples:

>>> psk = komm.PSKModulation(4)
>>> psk.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:

>>> psk = komm.PSKModulation(4)
>>> psk.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.

For the PSK, it is given by $$ E_\mathrm{s} = A^2. $$

Examples:

>>> psk = komm.PSKModulation(4)
>>> psk.energy_per_symbol
1.0

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:

>>> psk = komm.PSKModulation(4)
>>> psk.energy_per_bit
0.5

symbol_mean 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. $$

For the PSK, it is given by $$ \mu_\mathrm{s} = 0. $$

Examples:

>>> psk = komm.PSKModulation(4)
>>> psk.symbol_mean
0j

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. $$

For the PSK, it is given by $$ d_\mathrm{min} = 2A\sin\left(\frac{\pi}{M}\right). $$

Examples:

>>> psk = komm.PSKModulation(4)
>>> psk.minimum_distance
1.414213562373095

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[complexfloating])

    The output sequence(s). Has the same shape as the input, with the last dimension divided by $m$.

Examples:

>>> psk = komm.PSKModulation(4)
>>> psk.modulate([0, 0, 1, 1, 0, 0, 0, 1]).round(3)
array([ 1.+0.j, -1.+0.j,  1.+0.j,  0.+1.j])

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$.

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$.