- class sdr.MSK(sdr.OQPSK)
Implements minimum-shift keying (MSK) modulation and demodulation.
Notes¶
MSK is a linear phase modulation scheme similar to OQPSK. One key distinction is that the pulse shape is a half sine wave. This results in a constant envelope signal, which results in a lower peak-to-average power ratio (PAPR).
MSK can also be consider as continuous-phase frequency-shift keying (CPFSK) with the frequency separation equaling half the bit period.
Examples¶
Create a MSK modem.
In [1]: msk = sdr.MSK(); msk Out[1]: sdr.MSK(phase_offset=45, symbol_labels='gray') In [2]: plt.figure(figsize=(8, 4)); \ ...: sdr.plot.symbol_map(msk); ...:
Generate a random bit stream, convert to 2-bit symbols, and map to complex symbols.
In [3]: bits = np.random.randint(0, 2, 1000); bits[0:8] Out[3]: array([0, 1, 1, 0, 1, 1, 1, 1]) In [4]: symbols = sdr.pack(bits, msk.bps); symbols[0:4] Out[4]: array([1, 2, 3, 3], dtype=uint8) In [5]: complex_symbols = msk.map_symbols(symbols); complex_symbols[0:4] Out[5]: array([-0.70710678+0.j , -0.70710678+0.70710678j, 0.70710678+0.70710678j, 0.70710678-0.70710678j]) In [6]: plt.figure(figsize=(8, 4)); \ ...: sdr.plot.constellation(complex_symbols, linestyle="-"); ...:
Modulate and pulse shape the symbols to a complex baseband signal.
In [7]: tx_samples = msk.modulate(symbols) In [8]: plt.figure(figsize=(8, 4)); \ ...: sdr.plot.time_domain(tx_samples[0:50*msk.sps], sample_rate=msk.sps); ...:
MSK, like OQPSK, has I and Q channels that are offset by half a symbol period.
In [9]: plt.figure(figsize=(8, 6)); \ ...: plt.subplot(2, 1, 1); \ ...: sdr.plot.eye(tx_samples[msk.sps : -msk.sps].real, msk.sps); \ ...: plt.title("In-phase channel, $I$"); \ ...: plt.subplot(2, 1, 2); \ ...: sdr.plot.eye(tx_samples[msk.sps : -msk.sps].imag, msk.sps); \ ...: plt.title("Quadrature channel, $Q$"); \ ...: plt.tight_layout(); ...:
The phase trajectory of MSK is linear and continuous. Although, it should be noted that the phase is not differentiable at the symbol boundaries. This leads to lower spectral efficiency than, for instance, GMSK.
In [10]: plt.figure(figsize=(8, 4)); \ ....: sdr.plot.phase_tree(tx_samples[msk.sps:], msk.sps); ....:
Add AWGN noise such that \(E_b/N_0 = 20\) dB.
In [11]: ebn0 = 20; \ ....: snr = sdr.ebn0_to_snr(ebn0, bps=msk.bps, sps=msk.sps); \ ....: rx_samples = sdr.awgn(tx_samples, snr=snr) ....: In [12]: plt.figure(figsize=(8, 4)); \ ....: sdr.plot.time_domain(rx_samples[0:50*msk.sps], sample_rate=msk.sps); ....:
Matched filter and demodulate. Note, the first symbol has \(Q = 0\) and the last symbol has \(I = 0\).
In [13]: rx_symbols, rx_complex_symbols = msk.demodulate(rx_samples) # The symbol decisions are error-free In [14]: np.array_equal(symbols, rx_symbols) Out[14]: True In [15]: plt.figure(figsize=(8, 4)); \ ....: sdr.plot.constellation(rx_complex_symbols); ....:
See the Phase-shift keying example.
Constructors¶
-
MSK(phase_offset: float =
45
, ...) Creates a new MSK object.
String representation¶
Methods¶
-
ber(ebn0: ArrayLike, diff_encoded: bool =
False
) NDArray[float_] Computes the bit error rate (BER) at the provided \(E_b/N_0\) values.
-
ser(esn0: ArrayLike, diff_encoded: bool =
False
) NDArray[float_] Computes the symbol error rate (SER) at the provided \(E_s/N_0\) values.
- map_symbols(s: ArrayLike) NDArray[complex_]
Converts the decimal symbols \(s[k]\) to complex symbols \(a[k]\).
- decide_symbols(a_hat: ArrayLike) NDArray[int_]
Converts the received complex symbols \(\hat{a}[k]\) into decimal symbol decisions \(\hat{s}[k]\) using maximum-likelihood estimation (MLE).
- modulate(s: ArrayLike) NDArray[complex_]
Modulates the decimal symbols \(s[k]\) into pulse-shaped complex samples \(x[n]\).
- demodulate(x_hat) tuple[NDArray[int_], NDArray[complex_]]
Demodulates the pulse-shaped complex samples \(\hat{x}[n]\) into decimal symbol decisions \(\hat{s}[k]\) using matched filtering and maximum-likelihood estimation.
Properties¶
- property phase_offset : float
The phase offset \(\phi\) in degrees.
- property symbol_map : NDArray[np.complex_]
The symbol map \(\{0, \dots, M-1\} \mapsto \mathbb{C}\). This maps decimal symbols from \(0\) to \(M-1\) to complex symbols.
- property pulse_shape : NDArray[np.float_]
The pulse shape \(h[n]\) of the modulated signal.
- property tx_filter : Interpolator
The transmit interpolating pulse shaping filter. The filter coefficients are the pulse shape \(h[n]\).
-
MSK(phase_offset: float =