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).
Parameters:
mM 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.
function mpsk_constellation¶
Constellation point for Gray label g (M-PSK), unit amplitude.
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:
gGray label, masked to the low log2(M) bits.mM in {2,4,8}.
Returns:
Unit-amplitude constellation point.
function mpsk_demap¶
Hard-decide M-PSK symbols to their Gray-coded label bytes.
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:
xReceived symbols (any amplitude; phase only).x_lenNumber of symbols.outOut:x_lenGray label bytes.mM 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.
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:
xReceived symbols (any amplitude; phase only).x_lenNumber of symbols.outOut:x_lenGray label bytes.mM 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.
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:
symGray label bytes (0..M-1), one per symbol.sym_lenNumber of symbols.outOut:sym_lenconstellation points.mM 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).
function mpsk_gray_encode¶
Binary index -> Gray code (k ^ k>>1).
function mpsk_map¶
Map Gray-coded M-PSK labels to unit-amplitude constellation points.
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:
symGray label bytes (0..M-1), one per symbol.sym_lenNumber of symbols.outOut:sym_lenconstellation points.mM 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.
function mpsk_slice¶
Hard-decide y to the nearest M-PSK point; return its Gray label.
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:
yReceived symbol (any amplitude; only the phase is used).mM in {2,4,8}.ahatOut: the nearest unit-amplitude constellation point.
Returns:
Gray-coded label (0..M-1).
Macro Definition Documentation¶
define MPSK_PI¶
The documentation for this class was generated from the following file native/inc/mpsk/mpsk_core.h