Finite Impulse Response Hilbert Transform (firhilb)

The firhilbf object in liquid implements a finite impulse response Hilbert transform which converts between real and complex time series. The interpolator takes a complex time series and produces real-valued samples at twice the sample rate. The decimator reverses the process by halving the sample rate of a real-valued time series to a complex-valued one.

Typical trade-offs between filter length, side-lobe suppression, and transition bandwidth apply. The firhilbf object uses a half-band filter to implement the transform as efficiently as possible. While any filter length can be accepted, the firhilbf object internally forces the length to be of the form \(n=4m+1\) to reduce the computational load. A half-band filter of this length has \(2m\) zeros and \(2m+1\) non-zero coefficients. Of these non-zero coefficients, the center is exactly \(1\) while the other \(2m\) are even symmetric, and therefore only \(m\) computations are needed.

../_images/firhilb-0.png

Figure 3 firhilbf (Hilbert transform) decimator demonstration. The small signal at \(f=0.13\) is due to aliasing as a result of imperfect image rejection.

A graphical example of the Hilbert decimator can be seen in [fig-filter-firhilb] where a real-valued input sinusoid is converted into a complex sinusoid with half the number of samples. An example code listing is given below. Although firhilbf is a placeholder for both decimation (real to complex) and interpolation (complex to real), separate objects should be used for each task.

#include <liquid/liquid.h>

int main() {
    unsigned int m=5;           // filter semi-length
    float slsl=60.0f;           // filter sidelobe suppression level

    // create Hilbert transform objects
    firhilbf q0 = firhilbf_create(m,slsl);
    firhilbf q1 = firhilbf_create(m,slsl);

    float complex x;    // interpolator input
    float y[2];         // interpolator output
    float complex z;    // decimator output

    // ...

    // execute transforms
    firhilbf_interp_execute(q0, x, y);   // interpolator
    firhilbf_decim_execute(q1, y, &z);   // decimator

    // clean up allocated memory
    firhilbf_destroy(q0);
    firhilbf_destroy(q1);
}

For more detailed examples on Hilbert transforms in liquid, refer to the files examples/firhilb_decim_example.c and examples/firhilb_interp_example.c located within the main liquid project directory. _See also:_ resamp2 ([section-filter-resamp2]), FIR filter design ([section-filter-firdes]).

Interface

Listed below is the full interface to the firhilb family of objects.

create()

Create a firhilb object with a particular filter semi-length and desired stop-band attenuation. Internally the object designs a half-band filter based on applying a Kaiser-Bessel window to a sinc function to guarantee zeros at all off-center odd indexed samples.

firhilbf = firhilbf_create(unsigned int _m, float _as);
  • unsigned int _m: filter semi-length, delay is ( 2 m + 1 )

  • float _as: filter stop-band attenuation

  • returns new firhilbf object

copy()

Copy object including all internal objects and state

firhilbf = firhilbf_copy(firhilbf _q);
  • firhilbf _q:

  • returns new firhilbf object

destroy()

Destroy finite impulse response Hilbert transform, freeing all internally-allocted memory and objects.

int = firhilbf_destroy(firhilbf _q);
  • firhilbf _q:

  • returns standard error code

print()

Print firhilb object internals to stdout

int = firhilbf_print(firhilbf _q);
  • firhilbf _q:

  • returns standard error code

reset()

Reset firhilb object internal state

int = firhilbf_reset(firhilbf _q);
  • firhilbf _q:

  • returns standard error code

r2c_execute()

Execute Hilbert transform (real to complex)

int = firhilbf_r2c_execute(firhilbf _q, float _x, float complex * _y);
  • firhilbf _q: Hilbert transform object

  • float _x: real-valued input sample

  • float complex * _y: complex-valued output sample

  • returns standard error code

c2r_execute()

Execute Hilbert transform (complex to real)

int = firhilbf_c2r_execute(firhilbf _q, float complex _x, float * _y0, float * _y1);
  • firhilbf _q: Hilbert transform object

  • float complex _x: complex-valued input sample

  • float * _y0: real-valued output sample, lower side-band retained

  • float * _y1: real-valued output sample, upper side-band retained

  • returns standard error code

decim_execute()

Execute Hilbert transform decimator (real to complex)

int = firhilbf_decim_execute(firhilbf _q, float * _x, float complex * _y);
  • firhilbf _q: Hilbert transform object

  • float * _x: real-valued input array

  • float complex * _y: complex-valued output sample

  • returns standard error code

decim_execute_block()

Execute Hilbert transform decimator (real to complex) on a block of samples

int = firhilbf_decim_execute_block(firhilbf _q, float * _x, unsigned int _n, float complex * _y);
  • firhilbf _q: Hilbert transform object

  • float * _x: real-valued input array

  • unsigned int _n: number of output samples

  • float complex * _y: complex-valued output array

  • returns standard error code

interp_execute()

Execute Hilbert transform interpolator (real to complex)

int = firhilbf_interp_execute(firhilbf _q, float complex _x, float * _y);
  • firhilbf _q: Hilbert transform object

  • float complex _x: complex-valued input sample

  • float * _y: real-valued output array

  • returns standard error code

interp_execute_block()

Execute Hilbert transform interpolator (complex to real) on a block of samples

int = firhilbf_interp_execute_block(firhilbf _q, float complex * _x, unsigned int _n, float * _y);
  • firhilbf _q: Hilbert transform object

  • float complex * _x: complex-valued input array

  • unsigned int _n: number of input samples

  • float * _y: real-valued output array

  • returns standard error code