class sdr.Interpolator(sdr.PolyphaseFIR)

Implements a polyphase interpolating FIR filter.

Notes

The polyphase interpolating filter is equivalent to first upsampling the input signal \(x[n]\) by \(P\) (by inserting \(P-1\) zeros between each sample) and then filtering the upsampled signal with the prototype FIR filter with feedforward coefficients \(h[n]\).

Instead, the polyphase interpolating filter first decomposes the prototype FIR filter into \(P\) polyphase filters with feedforward coefficients \(h_i[n]\). The polyphase filters are then applied to the input signal \(x[n]\) in parallel. The output of the polyphase filters are then commutated to produce the output signal \(y[n]\). This prevents the need to multiply with zeros in the upsampled input, as is needed in the first case.

Polyphase 3x Interpolating FIR Filter Block Diagram
                       +------------------------+
                   +-->| h[0], h[3], h[6], h[9] |--> ..., y[3], y[0]
                   |   +------------------------+
                   |   +------------------------+
 ..., x[1], x[0] --+-->| h[1], h[4], h[7], 0    |--> ..., y[4], y[1]
                   |   +------------------------+
                   |   +------------------------+
                   +-->| h[2], h[5], h[8], 0    |--> ..., y[5], y[2]
                       +------------------------+

 Input Hold                                          Output Commutator
                                                     (top-to-bottom)

 x[n] = Input signal with sample rate fs
 y[n] = Output signal with sample rate fs * P
 h[n] = Prototype FIR filter

The polyphase feedforward taps \(h_i[n]\) are related to the prototype feedforward taps \(h[n]\) by

\[h_i[j] = h[i + j P] .\]

References

  • fred harris, Multirate Signal Processing for Communication Systems, Chapter 7: Resampling Filters.

Examples

Create an input signal to interpolate.

In [1]: x = np.cos(np.pi / 4 * np.arange(40))

Create a polyphase filter that interpolates by 7 using the Kaiser window method.

In [2]: fir = sdr.Interpolator(7); fir
Out[2]: sdr.Interpolator(7, 'kaiser', streaming=False)

In [3]: y = fir(x)

In [4]: plt.figure(); \
   ...: sdr.plot.time_domain(x, marker="o", label="Input"); \
   ...: sdr.plot.time_domain(y, sample_rate=fir.rate, marker=".", label="Interpolated"); \
   ...: plt.title("Interpolation by 7 with the Kaiser window method");
   ...: 
../../_images/sdr_Interpolator_1.png

Create a streaming polyphase filter that interpolates by 7 using the Kaiser window method. This filter preserves state between calls.

In [5]: fir = sdr.Interpolator(7, streaming=True); fir
Out[5]: sdr.Interpolator(7, 'kaiser', streaming=True)

In [6]: y1 = fir(x[0:10]); \
   ...: y2 = fir(x[10:20]); \
   ...: y3 = fir(x[20:30]); \
   ...: y4 = fir(x[30:40]); \
   ...: y5 = fir.flush()
   ...: 

In [7]: plt.figure(); \
   ...: sdr.plot.time_domain(x, marker="o", label="Input"); \
   ...: sdr.plot.time_domain(y1, sample_rate=fir.rate, offset=-fir.delay/fir.rate + 0, marker=".", label="Interpolated $y_1[n]$"); \
   ...: sdr.plot.time_domain(y2, sample_rate=fir.rate, offset=-fir.delay/fir.rate + 10, marker=".", label="Interpolated $y_2[n]$"); \
   ...: sdr.plot.time_domain(y3, sample_rate=fir.rate, offset=-fir.delay/fir.rate + 20, marker=".", label="Interpolated $y_3[n]$"); \
   ...: sdr.plot.time_domain(y4, sample_rate=fir.rate, offset=-fir.delay/fir.rate + 30, marker=".", label="Interpolated $y_4[n]$"); \
   ...: sdr.plot.time_domain(y5, sample_rate=fir.rate, offset=-fir.delay/fir.rate + 40, marker=".", label="Interpolated $y_5[n]$"); \
   ...: plt.title("Streaming interpolation by 7 with the Kaiser window method");
   ...: 
../../_images/sdr_Interpolator_2.png

Create a polyphase filter that interpolates by 7 using linear method.

In [8]: fir = sdr.Interpolator(7, "linear"); fir
Out[8]: sdr.Interpolator(7, 'linear', streaming=False)

In [9]: y = fir(x)

In [10]: plt.figure(); \
   ....: sdr.plot.time_domain(x, marker="o", label="Input"); \
   ....: sdr.plot.time_domain(y, sample_rate=fir.rate, marker=".", label="Interpolated"); \
   ....: plt.title("Interpolation by 7 with the linear method");
   ....: 
../../_images/sdr_Interpolator_3.png

Create a polyphase filter that interpolates by 7 using the zero-order hold method. It is recommended to use the "full" convolution mode. This way the first upsampled symbol has \(r\) samples.

In [11]: fir = sdr.Interpolator(7, "zoh"); fir
Out[11]: sdr.Interpolator(7, 'zoh', streaming=False)

In [12]: y = fir(x, mode="full")

In [13]: plt.figure(); \
   ....: sdr.plot.time_domain(x, marker="o", label="Input"); \
   ....: sdr.plot.time_domain(y, sample_rate=fir.rate, offset=-fir.delay/fir.rate, marker=".", label="Interpolated"); \
   ....: plt.title("Interpolation by 7 with the zero-order hold method");
   ....: 
../../_images/sdr_Interpolator_4.png

Constructors

Interpolator(interpolation: int, ...)

Creates a polyphase FIR interpolating filter.

Special methods

__call__(x: ArrayLike, mode: 'rate' | 'full' = 'rate') → NDArray

Filters the input signal \(x[n]\) with the polyphase FIR filter.

__len__() → int

Returns the filter length \(N + 1\).

Streaming mode only

reset()

Resets the filter state. Only useful when using streaming mode.

flush() → NDArray

Flushes the filter state by passing zeros through the filter. Only useful when using streaming mode.

property streaming : bool

Indicates whether the filter is in streaming mode.

property state : NDArray

The filter state consisting of the previous \(N\) inputs.

Methods

impulse_response(N: int | None = None) → NDArray

Returns the impulse response \(h[n]\) of the FIR filter.

step_response(N: int | None = None) → NDArray

Returns the step response \(s[n]\) of the FIR filter.

frequency_response(...) → tuple[ndarray[Any, dtype[float64]], ndarray[Any, dtype[complex128]]]
frequency_response(freqs: float, ...) → complex
frequency_response(freqs, ...) → ndarray[Any, dtype[complex128]]

Returns the frequency response \(H(\omega)\) of the FIR filter.

group_delay(...) → tuple[NDArray, NDArray]

Returns the group delay \(\tau_g(\omega)\) of the FIR filter.

phase_delay(...) → tuple[NDArray, NDArray]

Returns the phase delay \(\tau_{\phi}(\omega)\) of the FIR filter.

Properties

property method : 'kaiser' | 'linear' | 'linear-matlab' | 'zoh' | 'custom'

The method used to design the polyphase interpolating filter.

property branches : int

The number of polyphase branches \(B\).

property taps : NDArray

The prototype feedforward taps \(h[n]\).

property polyphase_taps : NDArray

The polyphase feedforward taps \(h_i[n]\).

property order : int

The order \(N = (M + 1)B - 1\) of the FIR prototype filter \(h[n]\).

property polyphase_order : int

The order \(M = (N + 1)/B - 1\) of each FIR polyphase filter \(h_i[n]\).

property input : 'hold' | 'top-to-bottom' | 'bottom-to-top'

The input connection method.

property output : 'sum' | 'top-to-bottom' | 'bottom-to-top' | 'all'

The output connection method.

property interpolation : int

The integer interpolation rate \(P\).

property decimation : int

The integer decimation rate \(Q\).

property rate : float

The fractional resampling rate \(r = P/Q\). The output sample rate is \(f_{s,out} = f_{s,in} \cdot r\).

property delay : int

The delay of polyphase FIR filter in samples.