Python M-PSK Constellation API¶
The doppler.mpsk module is the M-ary PSK constellation layer over the C
mpsk core: Gray-coded map / demap for BPSK, QPSK, and 8PSK. It is the decision
primitive (and its transmit inverse) that the
track.Carrier.Mpsk carrier loop and the MPSK receiver
compose; the inline mpsk_slice() / mpsk_constellation() helpers in
mpsk_core.h are the C composition API those loops inline per symbol.
A symbol carries log2(M) bits packed LSB-first into one uint8 (0..M−1); that
byte is the Gray-coded label, so a slip to an adjacent constellation point
flips exactly one bit. Constellations are unit amplitude: BPSK {+1, −1}, QPSK
(±1 ± j)/√2 (axis-separable, at π/4), 8PSK exp(j·k·π/4). m defaults to QPSK
and is keyword-capable.
See the M-PSK gallery page for the constellations and the BER-vs-Eb/N0 validation against theory.
Memoryless map / demap¶
mpsk_map and mpsk_demap are element-wise (one label byte ↔ one cf32 point),
absolute-phase. mpsk_demap is a hard decision (nearest point by phase;
amplitude-invariant).
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:
| Name | Type | Description | Default |
|---|---|---|---|
sym
|
NDArray[uint8]
|
Gray label bytes (0..M-1), one per symbol. |
required |
m
|
int
|
M in {2,4,8}. |
4
|
Returns:
| Type | Description |
|---|---|
NDArray[complex64]
|
Output. |
Examples:
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:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
Received symbols (any amplitude; phase only). |
required |
m
|
int
|
M in {2,4,8}. |
4
|
Returns:
| Type | Description |
|---|---|
NDArray[uint8]
|
Output. |
Examples:
Differential map / demap¶
The differential variants carry phase state across the array — information rides on phase differences, so an unknown constant carrier rotation cancels at the receiver. This resolves the M-fold phase ambiguity a decision-directed carrier loop leaves, at ~2× the symbol-error rate. Every symbol after the implicit zero-phase reference (the first) is rotation-invariant.
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:
| Name | Type | Description | Default |
|---|---|---|---|
sym
|
NDArray[uint8]
|
Gray label bytes (0..M-1), one per symbol. |
required |
m
|
int
|
M in {2,4,8}. |
4
|
Returns:
| Type | Description |
|---|---|
NDArray[complex64]
|
Output. |
Examples:
>>> 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
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:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
Received symbols (any amplitude; phase only). |
required |
m
|
int
|
M in {2,4,8}. |
4
|
Returns:
| Type | Description |
|---|---|
NDArray[uint8]
|
Output. |
Examples: