class sdr.FarrowResampler(sdr.FarrowFractionalDelay)

Implements a piecewise polynomial Farrow arbitrary resampler.

Notes

See FarrowFractionalDelay for a detailed description of the Farrow structure and Farrow filter.

The Farrow resampler is a special implementation of the Farrow filter that is used for arbitrary resampling. The resampling rate \(r\) is a real-valued number that can be any positive value. The resampling rate \(r\) is defined as the ratio of the output sample rate to the input sample rate. The resampling rate is used to compute the basepoint sample indices \(m(k)\) and the fractional sample advance \(\mu(k)\).

\[\mu(k + 1) = \mu(k) + \frac{1}{r}\]

If \(\mu(k + 1) > 1\), then the basepoint sample index \(m(k + 1) = m(k) + 1\) and \(\mu(k + 1) = \mu(k + 1) - 1\). Otherwise, the basepoint sample index \(m(k + 1) = m(k)\).

References

Examples

In [1]: d = np.zeros(10, dtype=float); \
   ...: d[d.size // 2] = 1
   ...: 

In [2]: t = np.linspace(-2, 2, 1000); \
   ...: sinc = np.sinc(t)
   ...: 

In [3]: plt.figure(); \
   ...: sdr.plot.time_domain(t, sinc, color="k", linestyle="--", label="Ideal Sinc");
   ...: 

In [4]: for order in range(1, 5 + 1, 2):
   ...:     rate = 100
   ...:     farrow = sdr.FarrowResampler(order)
   ...:     h = farrow(d, rate, mode="full")
   ...:     sdr.plot.time_domain(h, sample_rate=rate, offset=-d.size // 2 - farrow.delay, label=order)
   ...: 

In [5]: plt.legend(title="Farrow Order"); \
   ...: plt.xlim(-2, 2); \
   ...: plt.xlabel("Samples, $n$"); \
   ...: plt.title("Farrow Resampler Impulse Responses");
   ...: 
../../_images/sdr_FarrowResampler_1.svg

Create a sine wave with angular frequency \(\omega = 2 \pi / 5.179\). Interpolate the signal by \(r = \pi\) using Farrow piecewise polynomial Farrow resamplers.

In [6]: x = np.cos(2 * np.pi / 5.179 * np.arange(11))

In [7]: rate = np.pi

Compare the outputs of the Farrow resamplers with varying polynomial order. The convolution mode is set to "rate", which means that the output signal \(y[k]\) is aligned with the input signal.

In [8]: plt.figure(); \
   ...: sdr.plot.time_domain(x, sample_rate=1, marker="o", label="Input");
   ...: 

In [9]: for order in range(1, 5 + 1, 2):
   ...:     farrow = sdr.FarrowResampler(order)
   ...:     y = farrow(x, rate, mode="rate")
   ...:     sdr.plot.time_domain(y, sample_rate=rate, marker=".", label=f"Output {order}")
   ...: 

In [10]: plt.title("Farrow Resampler Outputs");
../../_images/sdr_FarrowResampler_2.svg

Compare the outputs of the Farrow resamplers with varying polynomial order. The convolution mode is set to "full", which means that the output signal \(y[k]\) is delayed by the filter delay \(D\). The plot is offset by the delay for easier viewing.

In [11]: plt.figure(); \
   ....: sdr.plot.time_domain(x, sample_rate=1, marker="o", label="Input");
   ....: 

In [12]: for order in range(1, 5 + 1, 2):
   ....:     farrow = sdr.FarrowResampler(order)
   ....:     y = farrow(x, rate, mode="full")
   ....:     sdr.plot.time_domain(y, sample_rate=rate, offset=-farrow.delay, marker=".", label=f"Output {order}")
   ....: 

In [13]: plt.title("Farrow Resampler Outputs");
../../_images/sdr_FarrowResampler_3.svg

Run a Farrow resampler with cubic polynomial order in streaming mode.

In [14]: x = np.cos(2 * np.pi / 5.179 * np.arange(40))

In [15]: farrow = sdr.FarrowResampler(3, streaming=True)

In [16]: y = []

In [17]: for i in range(0, x.size, 10):
   ....:     yi = farrow(x[i : i + 10], rate, mode="rate")
   ....:     y.append(yi)
   ....: 

In [18]: y = np.concatenate(y)

In [19]: plt.figure(); \
   ....: sdr.plot.time_domain(x, sample_rate=1, marker="o", label="Input"); \
   ....: sdr.plot.time_domain(y, sample_rate=rate, marker=".", label="Cubic concatenated"); \
   ....: plt.title("Cubic Farrow Resampler Concatenated Outputs");
   ....: 
../../_images/sdr_FarrowResampler_4.svg

Run a Farrow resampler with cubic polynomial order in streaming mode by clocking 10 outputs at a time. Clocking a specific number of output samples is useful in a demodulator when a specific number of samples per symbol are requested at a given resampling rate.

In [20]: farrow = sdr.FarrowResampler(3, streaming=True)

In [21]: n_outputs = 10; \
   ....: i = 0; \
   ....: y = []; \
   ....: lengths = [];
   ....: 

In [22]: while i < x.size - n_outputs / rate:
   ....:     yi, n_inputs = farrow.clock_outputs(x[i:], rate, n_outputs, mode="rate")
   ....:     i += n_inputs
   ....:     y.append(yi)
   ....:     lengths.append(yi.size)
   ....: 

In [23]: y = np.concatenate(y); \
   ....: print(lengths)
   ....: 
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]

In [24]: plt.figure(); \
   ....: sdr.plot.time_domain(x, sample_rate=1, marker="o", label="Input"); \
   ....: sdr.plot.time_domain(y, sample_rate=rate, marker=".", label="Cubic concatenated"); \
   ....: plt.title("Cubic Farrow Resampler Concatenated Outputs");
   ....: 
../../_images/sdr_FarrowResampler_5.svg

See the Farrow arbitrary resampler example.

Constructors

FarrowResampler(order: int, alpha: float = 0.5, ...)

Creates a new Farrow arbitrary resampler.

Special methods

__call__(x: ArrayLike, rate: ArrayLike, ...) NDArray

Resamples the input signal \(x[n]\) by the given arbitrary rate \(r\).

Methods

clock_outputs(...) tuple[TypeAliasForwardRef('~numpy.typing.NDArray'), int]

Resamples the input signal \(x[n]\) by the given arbitrary rate \(r\).

Streaming mode only

reset()

Resets the filter state.

property streaming : bool

Indicates whether the filter is in streaming mode.

property state : NDArray

The filter state consisting of the previous self.taps.shape[1] - 1 inputs.

Properties

property delay : int

The delay \(D\) of the Farrow FIR filters in samples.

property order : int

The order \(p\) of the Lagrange interpolating polynomial.

property lagrange_polys : NDArray

The Lagrange basis polynomials \(\ell_k(\mu)\).

property taps : NDArray

The Farrow filter taps.

property lookahead : int

The number of samples needed before the current input sample.