Skip to content

File farrow_core.h

FileList > farrow > farrow_core.h

Go to the source code of this file

Farrow fractional-delay interpolator — linear / parabolic / cubic. More...

  • #include "clib_common.h"
  • #include "jm_perf.h"
  • #include "dp_state.h"
  • #include <complex.h>

Classes

Type Name
struct farrow_state_t
Farrow interpolator state (4-tap delay line + order).

Public Types

Type Name
enum farrow__core_8h_1a06fc87d81c62e9abb8790b6e5713c55b

Public Functions

Type Name
farrow_state_t * farrow_create (int order)
Create a Farrow interpolator.
size_t farrow_delay (farrow_state_t * state, const float _Complex * x, size_t x_len, double mu, float _Complex * out, size_t max_out)
Apply a constant fractional delay of mu samples to a CF32 block.
size_t farrow_delay_max_out (farrow_state_t * state)
void farrow_destroy (farrow_state_t * state)
Destroy a Farrow interpolator.
JM_FORCEINLINE JM_HOT float _Complex farrow_eval (const farrow_state_t * s, float mu)
Interpolate at fractional offset mu [0,1) betweend[1] andd[2] .
size_t farrow_get_group_delay (const farrow_state_t * state)
void farrow_get_state (const farrow_state_t * state, void * blob)
JM_FORCEINLINE void farrow_init (farrow_state_t * s, int order)
Initialise in place: set order, clear the delay line.
JM_FORCEINLINE JM_HOT void farrow_push (farrow_state_t * s, float _Complex x)
Push one input sample into the delay line (oldest drops out).
void farrow_reset (farrow_state_t * state)
Clear the interpolator delay line; keep the order.
int farrow_set_state (farrow_state_t * state, const void * blob)
size_t farrow_state_bytes (const farrow_state_t * state)

Macros

Type Name
define FARROW_GROUP_DELAY 2u
define FARROW_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('F', 'R', 'R', 'W')
define FARROW_STATE_VERSION 1u

Detailed Description

A selectable-order Lagrange interpolator in Farrow (Horner-in-µ) form — the lean alternative to a full polyphase resampler when all you need is a fractional-delay tap for a timing loop. All three orders share one 4-tap delay line and interpolate at the SAME point — between the two middle taps — so the group delay is 2 samples regardless of order, which keeps a driving symbol-timing loop order-agnostic. Push input samples with farrow_push(); evaluate the output at a fractional offset µ ∈ [0,1) with farrow_eval(). The fractional offset is meant to come from an integer timing NCO (the post-wrap accumulator value), so the timing stays drift-free while only the interpolation itself is floating point.

order: 0 = linear (2-tap Lagrange), 1 = parabolic (4-tap symmetric piecewise-parabolic Farrow, α = 0.5), 2 = cubic (4-tap cubic Lagrange). All three are symmetric about the interpolation point, so the phase (delay) response is linear — no timing bias. Linear and cubic are exact for degree 1 and 3 polynomials; the piecewise-parabolic trades exactness for a flatter magnitude response than linear at no delay cost.

Lifecycle: farrow_create -> (push / eval / reset)* -> farrow_destroy, or embed by value with farrow_init().

farrow_state_t f;
farrow_init(&f, FARROW_CUBIC);
for (size_t i = 0; i < n; i++) farrow_push(&f, x[i]);
float _Complex y = farrow_eval(&f, 0.3f);   // x interpolated 0.3 past tap[1]

Public Types Documentation

enum farrow__core_8h_1a06fc87d81c62e9abb8790b6e5713c55b

enum farrow__core_8h_1a06fc87d81c62e9abb8790b6e5713c55b {
    FARROW_LINEAR = 0,
    FARROW_PARABOLIC = 1,
    FARROW_CUBIC = 2
};

Public Functions Documentation

function farrow_create

Create a Farrow interpolator.

farrow_state_t * farrow_create (
    int order
) 

Parameters:

  • order 0 = linear, 1 = parabolic, 2 = cubic.

Returns:

Heap-allocated state, or NULL on allocation failure.

Note:

Caller must call farrow_destroy() when done.


function farrow_delay

Apply a constant fractional delay of mu samples to a CF32 block.

size_t farrow_delay (
    farrow_state_t * state,
    const float _Complex * x,
    size_t x_len,
    double mu,
    float _Complex * out,
    size_t max_out
) 

Pushes each input sample through the delay line and evaluates the interpolator at the same fixed offset, so the whole block is delayed by a constant, non-integer amount. Output sample i is the input interpolated at i - group_delay + mu, i.e. the stream shifted later by group_delay - mu samples; the first group_delay outputs are the delay-line filling transient and should be discarded. Because the offset is held constant this is the open-loop use of the interpolator — a timing loop instead steers mu per sample via farrow_push()/farrow_eval().

Parameters:

  • state Pointer to a valid farrow_state_t.
  • x CF32 input samples.
  • x_len Number of input samples.
  • mu Fractional delay in samples; the offset in [0,1) into the interpolation interval (values outside extrapolate).
  • out Output buffer; one output per input sample.
  • max_out Capacity of out in samples.

Returns:

CF32 output array, same length as x, each sample delayed by group_delay - mu.

>>> from doppler.resample import Farrow
>>> import numpy as np
>>> f = Farrow(order="cubic")
>>> x = np.arange(8, dtype=np.complex64)   # a ramp: exact interp
>>> y = f.delay(x, 0.5)                  # delay group_delay - 0.5
>>> [round(float(v.real), 4) for v in y]  # first 2 are transient
[0.0, -0.0625, 0.4375, 1.5, 2.5, 3.5, 4.5, 5.5]

function farrow_delay_max_out

size_t farrow_delay_max_out (
    farrow_state_t * state
) 

function farrow_destroy

Destroy a Farrow interpolator.

void farrow_destroy (
    farrow_state_t * state
) 

Parameters:

  • state May be NULL.

function farrow_eval

Interpolate at fractional offset mu [0,1) betweend[1] andd[2] .

JM_FORCEINLINE  JM_HOT float _Complex farrow_eval (
    const farrow_state_t * s,
    float mu
) 

Horner-in-µ evaluation of the order's Lagrange polynomial. µ = 0 returns d[1] (= input at i - 2); µ → 1 returns d[2].

Parameters:

  • s State. Must be non-NULL.
  • mu Fractional offset in [0,1).

Returns:

The interpolated sample.


function farrow_get_group_delay

size_t farrow_get_group_delay (
    const farrow_state_t * state
) 

function farrow_get_state

void farrow_get_state (
    const farrow_state_t * state,
    void * blob
) 

function farrow_init

Initialise in place: set order, clear the delay line.

JM_FORCEINLINE void farrow_init (
    farrow_state_t * s,
    int order
) 


function farrow_push

Push one input sample into the delay line (oldest drops out).

JM_FORCEINLINE  JM_HOT void farrow_push (
    farrow_state_t * s,
    float _Complex x
) 


function farrow_reset

Clear the interpolator delay line; keep the order.

void farrow_reset (
    farrow_state_t * state
) 

Zeroes the 4-tap delay line so the next block starts from a filling transient again, exactly as a freshly created interpolator would. The order (linear / parabolic / cubic) is preserved, so the same object can be reused across independent bursts without rebuilding the polynomial. Call it between unrelated signal segments to stop the tail of one leaking into the head of the next.

Parameters:

  • state Must be non-NULL.
>>> from doppler.resample import Farrow
>>> import numpy as np
>>> f = Farrow(order="cubic")
>>> _ = f.delay(np.ones(8, dtype=np.complex64), 0.25)  # leaves state
>>> f.reset()                                 # back to pristine
>>> x = np.arange(8, dtype=np.complex64)
>>> f.delay(x, 0.5)[3:].real.tolist()   # steady part: ramp - 1.5
[1.5, 2.5, 3.5, 4.5, 5.5]

function farrow_set_state

int farrow_set_state (
    farrow_state_t * state,
    const void * blob
) 

function farrow_state_bytes

size_t farrow_state_bytes (
    const farrow_state_t * state
) 

Macro Definition Documentation

define FARROW_GROUP_DELAY

#define FARROW_GROUP_DELAY `2u`

define FARROW_STATE_MAGIC

#define FARROW_STATE_MAGIC `DP_FOURCC ('F', 'R', 'R', 'W')`

define FARROW_STATE_VERSION

#define FARROW_STATE_VERSION `1u`


The documentation for this class was generated from the following file native/inc/farrow/farrow_core.h