Source code for baseband_tasks.conversion

"""Module for signal-processing of baseband signals."""

import numpy as np
from .base import TaskBase
from .fourier import fft_maker

__all__ = ['Real2Complex']


[docs]class Real2Complex(TaskBase): """ Convert a real baseband signal to a complex baseband signal. This task computes the analytic representation of the input signal via a Hilbert transform, throwing away negative frequency components. Then, the signal is shifted in frequency domain by -B/2 where B is the bandwidth of the signal. Finally, the signal is decimated by a factor of 2, which results in the complex baseband representation of the input signal. Parameters ---------- ih : task or `baseband` stream reader Input data stream, with time as the first axis. samples_per_frame : int, optional Number of complete output samples per frame (see Notes). Default: Half the number of samples per frame in the input stream. See Also -------- baseband_tasks.fourier.fft_maker : to select the FFT package used. Raises ------ ValueError If ``ih`` has complex data. Notes ----- This function assumes the input signal is a causal signal. References ---------- .. https://dsp.stackexchange.com/q/43278/17721 """ def __init__(self, ih, samples_per_frame=None): if ih.complex_data: raise ValueError("Stream should be real.") if samples_per_frame is None: assert ih.samples_per_frame % 2 == 0, \ "need even number of input samples" samples_per_frame = ih.samples_per_frame // 2 dtype = np.dtype('c{}'.format(ih.dtype.itemsize * 2)) self._fft = fft_maker((samples_per_frame * 2, ) + ih.sample_shape, dtype, sample_rate=ih.sample_rate, axis=0) self._ifft = self._fft.inverse() frequency = getattr(ih, 'frequency', None) sideband = getattr(ih, 'sideband', None) if frequency is not None: frequency = frequency + ih.sample_rate / 2 * sideband super().__init__(ih, samples_per_frame=samples_per_frame, sample_rate=ih.sample_rate / 2, frequency=frequency, sideband=sideband, dtype=dtype)
[docs] def task(self, data): z = data.astype(self.dtype) N = z.shape[0] # Hilbert transform z = self._fft(z) h = np.zeros(N) if N % 2 == 0: h[0] = h[N // 2] = 1 h[1:N // 2] = 2 else: h[0] = 1 h[1:(N + 1) // 2] = 2 z = self._ifft(z * h) # Frequency shift signal by -B/2 h = np.exp(-1j * np.pi / 2 * np.arange(N)) z *= h z = z[::2] return z
def _repr_item(self, key, default, value=None): if key == 'samples_per_frame' and default is None: default = self.ih.samples_per_frame // 2 return super()._repr_item(key, default=default, value=value)