- 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¶
Michael Rice, Digital Communications: A Discrete Time Approach, Section 8.4.2.
Qasim Chaudhari, Fractional Delay Filters Using the Farrow Structure.
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"); ...:
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");
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");
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"); ....:
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"); ....:
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 state : NDArray
The filter state consisting of the previous
self.taps.shape[1] - 1
inputs.
Properties¶
- property lagrange_polys : NDArray
The Lagrange basis polynomials \(\ell_k(\mu)\).
- property taps : NDArray
The Farrow filter taps.