comnum.Dpsk#

class comnum.Dpsk(m)#

Differential M-PSK (DBPSK, DQPSK, D8PSK, …) with Gray-labelled phase transitions.

The information is carried by the phase transitions: with a_k the differential phase index of symbol k and c_{-1} = 0 the reference, c_k = (a_k + c_{k-1}) mod M and x_k = exp(j 2 pi c_k / M), so n information symbols are sent as n + 1 unit-energy PSK symbols. The bits of a symbol are the Gray label of its transition, so the nearest wrong transitions differ by one bit. The noncoherent detector decides the transition nearest to arg(r_k conj(r_{k-1})), which a constant phase rotation of the received symbols leaves unchanged (Proakis & Salehi, Digital Communications, 5th ed., 2008, Sec. 4.5); its bit error rate over AWGN is theory_ber_dpsk(m, ebn0_db). Coherent decisions on the absolute phase followed by differential decoding spoil two differences per symbol error, hence 2p(1 - p) for DBPSK (theory_ber_dpsk(2, ebn0_db, detection="coherent"), Sec. 3.2-2).

The methods work at two levels: bits (modulate, demodulate, demodulate_coherent, and indices/bits between bits and differential indices) and phase indices (encode and decode between differential and absolute indices, detect and detect_coherent from received symbols to indices).

Parameters:

m (int) – the order, a power of two (2 for DBPSK, 4 for DQPSK).

Raises:

ValueError – for an invalid parameter (the message names it).

Examples

>>> import comnum, numpy as np
>>> dqpsk = comnum.Dpsk(4)
>>> bits = np.array([0, 1, 1, 1, 0, 0], dtype=np.uint8)
>>> x = dqpsk.map_bits(bits)
>>> x.shape
(4,)
>>> dqpsk.demap_bits(x * np.exp(1j * 1.234)).tolist()
[0, 1, 1, 1, 0, 0]
bits(indices)#

The bits of a sequence of differential phase indices (the inverse of indices).

Parameters:

indices (array_like) – differential indices, below M.

Returns:

log2 M bits per index (uint8).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum
>>> comnum.Dpsk(4).bits([0, 1, 2, 3]).tolist()
[0, 0, 0, 1, 1, 1, 1, 0]
decode(coded)#

Differential decoding: the n differences a_k = (c_k - c_{k-1}) mod M of n + 1 absolute phase indices (the inverse of encode).

Parameters:

coded (array_like) – the n + 1 absolute phase indices, below M.

Returns:

the n differential phase indices (int64).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum
>>> comnum.Dpsk(4).decode([0, 0, 1, 0, 2, 3]).tolist()
[0, 1, 3, 2, 1]
demap_bits(r)#

Noncoherent demodulation: the transition nearest to arg(r_k conj(r_{k-1})) (no carrier phase needed), as bits.

Parameters:

r (array_like) – the n + 1 received symbols.

Returns:

the n log2 M bits (uint8).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum, numpy as np
>>> d = comnum.Dpsk(2)
>>> d.demap_bits(d.map_bits([1, 0, 1]) * 1j).tolist()
[1, 0, 1]
demodulate_coherent(r)#

Coherent demodulation with differential decoding: the phase index nearest to every symbol, then the differences, as bits.

Parameters:

r (array_like) – the n + 1 received symbols (carrier phase recovered).

Returns:

the n log2 M bits (uint8).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum
>>> d = comnum.Dpsk(4)
>>> d.demodulate_coherent(d.map_bits([1, 1, 0, 1])).tolist()
[1, 1, 0, 1]
detect(r)#

Noncoherent detection: the differential phase index nearest to arg(r_k conj(r_{k-1})) for every pair of consecutive received symbols, invariant to a constant phase rotation (Proakis & Salehi, 5th ed., Sec. 4.5).

Parameters:

r (array_like) – the n + 1 received symbols.

Returns:

the n differential phase indices (int64).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum, numpy as np
>>> r = np.exp(1j * (0.3 + np.array([0.0, np.pi / 2, 0.0])))
>>> comnum.Dpsk(4).detect(r).tolist()
[1, 3]
detect_coherent(r)#

Coherent decisions: the absolute phase index nearest to every received symbol (the hard decision of M-PSK); decode then removes the differential encoding.

Parameters:

r (array_like) – the received symbols.

Returns:

one absolute phase index per symbol (int64).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum, numpy as np
>>> comnum.Dpsk(4).detect_coherent(np.exp(1j * np.pi / 2 * np.array([0, 1, 1, 2]))).tolist()
[0, 1, 1, 2]
encode(indices)#

Differential encoding of phase indices: the n + 1 absolute indices c_{-1} = 0, c_0, …, c_{n-1} with c_k = (a_k + c_{k-1}) mod M.

Parameters:

indices (array_like) – the n differential phase indices, below M.

Returns:

the n + 1 absolute phase indices (int64).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum
>>> comnum.Dpsk(4).encode([0, 1, 3, 2, 1]).tolist()
[0, 0, 1, 0, 2, 3]
indices(bits)#

The differential phase indices of a bit stream (log2 M bits per symbol, MSB first, Gray-labelled transitions).

Parameters:

bits (array_like) – the bits, a multiple of log2 M.

Returns:

one differential index per symbol (int64).

Return type:

numpy.ndarray

Raises:

ValueError – if the length is not a multiple of log2 M.

Examples

>>> import comnum
>>> comnum.Dpsk(4).indices([0, 0, 0, 1, 1, 1, 1, 0]).tolist()
[0, 1, 2, 3]
map_bits(bits)#

The unit-energy PSK symbols carrying a bit stream (the reference symbol first).

Parameters:

bits (array_like) – n bits, a multiple of log2 M.

Returns:

the n / log2 M + 1 symbols (complex128).

Return type:

numpy.ndarray

Raises:

ValueError – for an invalid parameter or input (the message names it).

Examples

>>> import comnum
>>> comnum.Dpsk(2).map_bits([1, 0]).real.round(12).tolist()
[1.0, -1.0, -1.0]
bits_per_symbol#

Bits per symbol, log2 M.

Type:

int

order#

The order M.

Type:

int