Skip to content

File mpsk_core.h

FileList > inc > mpsk > mpsk_core.h

Go to the source code of this file

M-PSK constellation: Gray-coded map / demap for BPSK, QPSK, 8PSK. More...

  • #include "clib_common.h"
  • #include "jm_perf.h"
  • #include <complex.h>
  • #include <math.h>

Public Functions

Type Name
int mpsk_bits_per_symbol (int m)
Bits per M-PSK symbol = log2(M).
JM_FORCEINLINE int mpsk_bps (int m)
Bits per M-PSK symbol = log2(M); M in {2,4,8} -> {1,2,3}, else 0.
JM_FORCEINLINE float complex mpsk_constellation (unsigned g, int m)
Constellation point for Gray label g (M-PSK), unit amplitude.
void mpsk_demap (const float complex * x, size_t x_len, uint8_t * out, int m)
Hard-decide M-PSK symbols to their Gray-coded label bytes.
void mpsk_diff_demap (const float complex * x, size_t x_len, uint8_t * out, int m)
Differential M-PSK demap: decide from the phase DIFFERENCE.
void mpsk_diff_map (const uint8_t * sym, size_t sym_len, float complex * out, int m)
Differential M-PSK map: the label selects a phase INCREMENT.
JM_FORCEINLINE unsigned mpsk_gray_decode (unsigned g)
Gray code -> binary index (inverse of mpsk_gray_encode).
JM_FORCEINLINE unsigned mpsk_gray_encode (unsigned k)
Binary index -> Gray code (k ^ k>>1).
void mpsk_map (const uint8_t * sym, size_t sym_len, float complex * out, int m)
Map Gray-coded M-PSK labels to unit-amplitude constellation points.
JM_FORCEINLINE double mpsk_phi0 (int m)
Constellation phase offset (radians): pi/4 for QPSK, else 0.
JM_FORCEINLINE unsigned mpsk_slice (float complex y, int m, float complex * ahat)
Hard-decide y to the nearest M-PSK point; return its Gray label.

Macros

Type Name
define MPSK_PI 3.14159265358979323846

Detailed Description

The receive-side decision primitive (and its transmit inverse) for M-ary phase-shift keying. A symbol carries log2(M) bits packed LSB-first into one byte (0..M-1); the byte IS the Gray-coded label, so a slip to an adjacent constellation point flips exactly one bit. Unit amplitude; constellation:

  • BPSK (M=2): {+1, -1} (phi0 = 0)
  • QPSK (M=4): (+-1 +- j)/sqrt(2) (phi0 = pi/4, axis-separable I/Q)
  • 8PSK (M=8): exp(j*k*pi/4) (phi0 = 0)

The inline helpers (mpsk_constellation, mpsk_slice) are the C composition API a carrier loop / receiver inlines per symbol; the module free functions (mpsk_map/mpsk_demap and the differential variants) are the array Python face. Memoryless functions are element-wise (one byte <-> one cf32); the differential variants carry phase state across the array (info on phase differences, which removes the M-fold carrier ambiguity).

// C: round-trip one QPSK symbol through the inline slicer
float complex ahat;
unsigned g = mpsk_slice((1.0f + 1.0f*I) * 0.70710678f, 4, &ahat); // -> 0

Public Functions Documentation

function mpsk_bits_per_symbol

Bits per M-PSK symbol = log2(M).

int mpsk_bits_per_symbol (
    int m
) 

Parameters:

  • m M in {2,4,8}.

Returns:

1, 2, or 3 (0 for an unsupported M).

>>> from doppler.mpsk import mpsk_bits_per_symbol
>>> [mpsk_bits_per_symbol(m) for m in (2, 4, 8)]
[1, 2, 3]

function mpsk_bps

Bits per M-PSK symbol = log2(M); M in {2,4,8} -> {1,2,3}, else 0.

JM_FORCEINLINE int mpsk_bps (
    int m
) 


function mpsk_constellation

Constellation point for Gray label g (M-PSK), unit amplitude.

JM_FORCEINLINE float complex mpsk_constellation (
    unsigned g,
    int m
) 

Maps the Gray-coded label byte (0..M-1) to its complex point exp(j*(2*pi*k/M + phi0)), where k = gray_decode(g) is the constellation index. Inverse of mpsk_slice()'s returned label.

Parameters:

  • g Gray label, masked to the low log2(M) bits.
  • m M in {2,4,8}.

Returns:

Unit-amplitude constellation point.


function mpsk_demap

Hard-decide M-PSK symbols to their Gray-coded label bytes.

void mpsk_demap (
    const float complex * x,
    size_t x_len,
    uint8_t * out,
    int m
) 

Element-wise inverse of mpsk_map(): each cf32 symbol is sliced to the nearest constellation point and its Gray label (0..M-1) is written out. A slip to an adjacent point flips exactly one bit (Gray). out must hold x_len bytes.

Parameters:

  • x Received symbols (any amplitude; phase only).
  • x_len Number of symbols.
  • out Out: x_len Gray label bytes.
  • m M in {2,4,8}.
>>> import numpy as np
>>> from doppler.mpsk import mpsk_demap
>>> x = np.array([1+0j, 1j, -1+0j, -1j], dtype=np.complex64)   # 8PSK points
>>> mpsk_demap(x, 8).tolist()   # Gray labels of indices 0, 2, 4, 6
[0, 3, 6, 5]

function mpsk_diff_demap

Differential M-PSK demap: decide from the phase DIFFERENCE.

void mpsk_diff_demap (
    const float complex * x,
    size_t x_len,
    uint8_t * out,
    int m
) 

Inverse of mpsk_diff_map(): the Gray label of each symbol is decided from the phase difference between consecutive sliced indices (the first references an implicit zero-phase start). Invariant to an unknown constant carrier phase.

Parameters:

  • x Received symbols (any amplitude; phase only).
  • x_len Number of symbols.
  • out Out: x_len Gray label bytes.
  • m M in {2,4,8}.
>>> import numpy as np
>>> from doppler.mpsk import mpsk_diff_demap, mpsk_diff_map
>>> sym = np.array([2, 2, 1, 0], dtype=np.uint8)
>>> np.array_equal(mpsk_diff_demap(mpsk_diff_map(sym, 8), 8), sym)
True

function mpsk_diff_map

Differential M-PSK map: the label selects a phase INCREMENT.

void mpsk_diff_map (
    const uint8_t * sym,
    size_t sym_len,
    float complex * out,
    int m
) 

Information rides on phase differences: the running constellation index accumulates gray_decode(label) each symbol (starting from an implicit zero-phase reference), so an unknown constant carrier phase cancels at the receiver (mpsk_diff_demap) — resolving the M-fold ambiguity, at ~2x the symbol-error rate of coherent map(). Sequential over the array.

Parameters:

  • sym Gray label bytes (0..M-1), one per symbol.
  • sym_len Number of symbols.
  • out Out: sym_len constellation points.
  • m M in {2,4,8}.
>>> import numpy as np
>>> from doppler.mpsk import mpsk_diff_map, mpsk_diff_demap
>>> sym = np.array([1, 0, 3, 2, 1], dtype=np.uint8)
>>> pts = mpsk_diff_map(sym, 4)
>>> np.array_equal(mpsk_diff_demap(pts, 4), sym)   # exact round-trip
True
>>> rot = (pts * np.exp(1j * np.pi / 2)).astype(np.complex64)  # 90 deg slip
>>> np.array_equal(mpsk_diff_demap(rot, 4)[1:], sym[1:])   # rotation-invariant
True

function mpsk_gray_decode

Gray code -> binary index (inverse of mpsk_gray_encode).

JM_FORCEINLINE unsigned mpsk_gray_decode (
    unsigned g
) 


function mpsk_gray_encode

Binary index -> Gray code (k ^ k>>1).

JM_FORCEINLINE unsigned mpsk_gray_encode (
    unsigned k
) 


function mpsk_map

Map Gray-coded M-PSK labels to unit-amplitude constellation points.

void mpsk_map (
    const uint8_t * sym,
    size_t sym_len,
    float complex * out,
    int m
) 

Element-wise inverse of mpsk_demap(): each input byte is one symbol's log2(M) Gray-coded bits (0..M-1), each output is its cf32 point. Memoryless (absolute phase). out must hold sym_len points.

Parameters:

  • sym Gray label bytes (0..M-1), one per symbol.
  • sym_len Number of symbols.
  • out Out: sym_len constellation points.
  • m M in {2,4,8}.
>>> import numpy as np
>>> from doppler.mpsk import mpsk_map, mpsk_demap
>>> sym = np.array([0, 1, 2, 3], dtype=np.uint8)   # QPSK labels
>>> pts = mpsk_map(sym, 4)
>>> np.round(np.abs(pts), 5)
array([1., 1., 1., 1.], dtype=float32)
>>> np.array_equal(mpsk_demap(pts, 4), sym)
True

function mpsk_phi0

Constellation phase offset (radians): pi/4 for QPSK, else 0.

JM_FORCEINLINE double mpsk_phi0 (
    int m
) 


function mpsk_slice

Hard-decide y to the nearest M-PSK point; return its Gray label.

JM_FORCEINLINE unsigned mpsk_slice (
    float complex y,
    int m,
    float complex * ahat
) 

Picks the constellation index nearest in phase, writes that unit-amplitude point to ahat (the decision, for a decision-directed carrier error Im(y * conj(ahat))), and returns the Gray-coded label byte. Inverse of mpsk_constellation(). One atan2 per call (symbol-rate, not the sample loop).

Parameters:

  • y Received symbol (any amplitude; only the phase is used).
  • m M in {2,4,8}.
  • ahat Out: the nearest unit-amplitude constellation point.

Returns:

Gray-coded label (0..M-1).


Macro Definition Documentation

define MPSK_PI

#define MPSK_PI `3.14159265358979323846`


The documentation for this class was generated from the following file native/inc/mpsk/mpsk_core.h