Source code for sigkit.modem.psk

"""Phase Shift Keying Module."""

import numpy as np

from sigkit.core.base import SigKitError, Signal
from sigkit.modem.base import Modem


[docs] class PSK(Modem): """PSK Modem for modulating and demodulating bits.""" def __init__( self, sample_rate: int, symbol_rate: int, n_components, cf: float = 0.0 ): """N-PSK Modem. Args: sample_rate: Sampling rate of the waveform symbol_rate: Symbol rate, used to calculate samples per symbol n_components: Number of PSK points (e.g. 2, 4, 8, 16..) cf: Carrier frequency """ super().__init__(sample_rate, symbol_rate, n_components, cf) bin_indicies = np.arange(n_components) indicies = bin_indicies ^ (bin_indicies >> 1) phases = 2 * np.pi * indicies / n_components self.constellation = np.exp(1j * phases)
[docs] def modulate(self, bits: np.ndarray) -> Signal: """Modulate bits with PSK. Args: bits: 1D array of 0 | 1, length multiple of log2(n_components) Returns: Signal: containing complex64 samples """ if bits.ndim != 1 or bits.size % self.bits_per_symbol != 0: raise SigKitError( f"Number of bits must be a multiple of {self.bits_per_symbol}" ) bits = bits.reshape(-1, self.bits_per_symbol) weights = 1 << np.arange(self.bits_per_symbol - 1, -1, -1) bin_indicies = bits.dot(weights) indicies = bin_indicies ^ (bin_indicies >> 1) baseband = self.constellation[indicies] samples = np.repeat(baseband, self.sps) if self.cf != 0.0: t = np.arange(samples.size) / self.sample_rate samples *= np.exp(1j * 2 * np.pi * self.cf * t) return Signal( samples=samples.astype(np.complex64), sample_rate=self.sample_rate, carrier_frequency=self.cf, )
[docs] def demodulate(self, signal: Signal | np.ndarray) -> np.ndarray: """Map received PSK samples to bits. Args: signal: Signal containing modulated complex samples. Returns: 1D array of bits. """ x = signal.samples if isinstance(signal, Signal) else signal if not x.dtype == np.complex64: raise SigKitError("Demodulate expects samples to be of type np.complex64.") indices = self.extract_symbols(x) bin_indices = indices.copy() shift = indices >> 1 while np.any(shift): bin_indices ^= shift shift >>= 1 bits_matrix = ( (bin_indices[:, None] & (1 << np.arange(self.bits_per_symbol - 1, -1, -1))) > 0 ).astype(np.uint8) return bits_matrix.ravel()