comnum.Cpm#

class comnum.Cpm(h=None, *, pulse='rec', length=1, bt=0.3, sps=4, order=2)#

A continuous-phase modulator (CPFSK, MSK, GMSK, raised-cosine CPM) and its detectors.

An M-ary symbol sequence a_k ∈ {±1, ±3, …, ±(M-1)} is carried by the phase of a unit-envelope signal s(t) = exp(jφ(t)), φ(t) = 2πh Σ_k a_k q(t - kT), where h = p/q is the modulation index and q(t) the integral of a frequency pulse of L symbols and area 1/2 (Proakis & Salehi, Digital Communications, 5th ed., Sec. 3.3-2). The pulses: "rec" (rectangular; 1REC is CPFSK and MSK is 1REC with h = 1/2), "rc" (raised cosine) and "gmsk" (the Gaussian pulse of Murota & Hirade, IEEE Trans. Commun. 29(7), 1981, of normalised bandwidth bt, truncated to L symbols). The trellis has 2q M^(L-1) states.

Symbols are indices i ∈ {0, …, M-1}, the level being a = 2i - (M-1) (see levels()): for binary CPM the bits themselves are the indices, the bit 1 sending the level +1. With MSK, msk_precode() turns information bits into the indices whose signal is OQPSK with half-sinusoid pulses, and msk_bits() reads them back from the phase states of ml_detect_path(), with the BER of BPSK.

Parameters:
  • h (str | tuple[int, int]) – modulation index, "p/q" or (p, q) with 0 < p < q ≤ 64 ("1/2" by default).

  • pulse (str) – "rec", "rc" or "gmsk".

  • length (int) – pulse length L in symbols (1 to 8).

  • bt (float) – bandwidth–time product of the Gaussian pulse.

  • sps (int) – samples per symbol (at least 2).

  • order (int) – alphabet size M, 2 or 4.

Raises:

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

Examples

>>> import comnum, numpy as np
>>> msk = comnum.Cpm("1/2", pulse="rec", sps=4)
>>> a = np.array([1, 0, 0, 1, 1, 0])
>>> s = msk.modulate(a)
>>> np.allclose(np.abs(s), 1.0)
True
>>> msk.ml_detect(s).tolist()
[1, 0, 0, 1, 1, 0]
>>> comnum.Cpm.gmsk(0.3, length=3, sps=8).n_states
16
static gmsk(bt=0.3, *, length=3, sps=4)#

GMSK: binary Gaussian pulse of bandwidth bt over length symbols, h = 1/2.

Parameters:
  • bt (float) – bandwidth–time product BT of the Gaussian filter.

  • length (int) – pulse length L in symbols.

  • sps (int) – samples per symbol.

Returns:

the modulator.

Return type:

Cpm

Raises:

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

static msk(sps=4)#

MSK: binary 1REC with h = 1/2.

Parameters:

sps (int) – samples per symbol.

Returns:

the modulator.

Return type:

Cpm

Raises:

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

differential_detect(samples, *, bt=None)#

The one-symbol differential phase detector of binary CPM: after an optional Gaussian low-pass predetection filter of normalised 3 dB bandwidth bt, the sign of Im(r_n r_{n-1}*) sampled once per symbol decides a_n (Simon & Wang, IEEE Trans. Veh. Technol. 33(4), 1984). Exact for MSK without noise; the partial response of GMSK closes the eye.

Parameters:
  • samples (array_like) – a whole number of symbols of received samples.

  • bt (float, optional) – receive bandwidth–time product (no filter by default; about 0.5 is the best choice for MSK over AWGN).

Returns:

the symbol indices (int64; for binary CPM, the bits).

Return type:

numpy.ndarray

Raises:

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

levels(indices)#

The symbol levels a = 2i - (M-1) of symbol indices (for binary CPM, the bits b give the levels 2b - 1).

Parameters:

indices (array_like) – symbol indices below M.

Returns:

the levels ±1, ±3, … (float64).

Return type:

numpy.ndarray

Raises:

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

min_distance2(*, max_len=3)#

The normalised minimum squared Euclidean distance over the pairs of sequences that diverge at t = 0 and merge within max_len symbols (the bound d_B² of Anderson, Aulin & Sundberg, Digital Phase Modulation, 1986, ch. 3; Aulin & Sundberg, IEEE Trans. Commun. 29(3), 1981, for full response): antipodal signalling and MSK have d² = 2, and the asymptotic gain over BPSK is 10 log10(d²/2) dB.

Parameters:

max_len (int) – the longest merge considered (the search is exhaustive, (2M-1)^max_len sequences).

Returns:

d² (infinite if no sequence merges within max_len symbols).

Return type:

float

Raises:

ValueError – if (2M-1)^max_len exceeds 2^20 sequences.

ml_detect(samples)#

Coherent maximum-likelihood sequence detection (Viterbi algorithm over the phase trellis), from the initial state of the modulator to any state.

Parameters:

samples (array_like) – a whole number of symbols of received samples.

Returns:

the symbol indices of the most likely sequence (int64).

Return type:

numpy.ndarray

Raises:

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

ml_detect_path(samples)#

The maximum-likelihood sequence with its terminal phase states.

Parameters:

samples (array_like) – a whole number of symbols of received samples.

Returns:

the symbol indices, and the terminal phase states in units of π/q (modulo 2q) at the start of every interval and after the last one (one more value than symbols).

Return type:

tuple[numpy.ndarray, numpy.ndarray]

Raises:

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

modulate(symbols)#

The unit-envelope signal exp(jφ(t)) at sps samples per symbol.

Parameters:

symbols (array_like) – symbol indices below M (for binary CPM, the bits).

Returns:

sps · len(symbols) complex samples.

Return type:

numpy.ndarray

Raises:

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

msk_detect(samples)#

The quadrature detector of MSK: the boundary bits d_n of the equivalent OQPSK signal (the information bits of msk_precode()), each decided from the correlation with a half-sinusoid over two symbols, with the BER of antipodal signalling over AWGN (Proakis & Salehi, Sec. 3.3-2).

Parameters:

samples (array_like) – a whole number of symbols of an MSK signal.

Returns:

the bits (uint8).

Return type:

numpy.ndarray

Raises:

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

phase(symbols)#

The phase φ(t) at every sample, the terminal phase reduced modulo 2π.

Parameters:

symbols (array_like) – symbol indices below M.

Returns:

sps phase samples (rad) per symbol.

Return type:

numpy.ndarray

Raises:

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

bt#

The bandwidth–time product of the Gaussian pulse (None for another pulse).

Type:

float | None

h#

The modulation index h = p/q (in lowest terms).

Type:

float

h_fraction#

The numerator and the denominator (p, q) of the modulation index.

Type:

tuple[int, int]

length#

The pulse length L in symbols.

Type:

int

n_phase_states#

The number of terminal phases, 2q (the trellis visits q of them when p is even).

Type:

int

n_states#

The number of trellis states, 2q M^(L-1).

Type:

int

order#

The alphabet size M.

Type:

int

phase_response#

The tabulated phase response q(kT/sps) for k < L·sps.

Type:

numpy.ndarray

phase_states#

The terminal phases of the trellis (rad), kπ/q for k < 2q.

Type:

numpy.ndarray

pulse#

The pulse name, "rec", "rc" or "gmsk".

Type:

str

sps#

Samples per symbol.

Type:

int