Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
8 contributors

Users who have contributed to this file

@jgaeddert @brian-armstrong @gwbres @guruofquality @r4d10n @michelp @dajuro @andrewvoznytsa
8678 lines (7755 sloc) 472 KB
/*
* Copyright (c) 2007 - 2019 Joseph Gaeddert
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef __LIQUID_H__
#define __LIQUID_H__
#ifdef __cplusplus
extern "C" {
# define LIQUID_USE_COMPLEX_H 0
#else
# define LIQUID_USE_COMPLEX_H 1
#endif // __cplusplus
// common headers
#include <inttypes.h>
//
// Make sure the version and version number macros weren't defined by
// some prevoiusly included header file.
//
#ifdef LIQUID_VERSION
# undef LIQUID_VERSION
#endif
#ifdef LIQUID_VERSION_NUMBER
# undef LIQUID_VERSION_NUMBER
#endif
//
// Compile-time version numbers
//
// LIQUID_VERSION = "X.Y.Z"
// LIQUID_VERSION_NUMBER = (X*1000000 + Y*1000 + Z)
//
#define LIQUID_VERSION "1.3.2"
#define LIQUID_VERSION_NUMBER 1003002
//
// Run-time library version numbers
//
extern const char liquid_version[];
const char * liquid_libversion(void);
int liquid_libversion_number(void);
// run-time library validation
#define LIQUID_VALIDATE_LIBVERSION \
if (LIQUID_VERSION_NUMBER != liquid_libversion_number()) { \
fprintf(stderr,"%s:%u: ", __FILE__,__LINE__); \
fprintf(stderr,"error: invalid liquid runtime library\n"); \
exit(1); \
} \
#define LIQUID_CONCAT(prefix, name) prefix ## name
#define LIQUID_VALIDATE_INPUT
/*
* Compile-time complex data type definitions
*
* Default: use the C99 complex data type, otherwise
* define complex type compatible with the C++ complex standard,
* otherwise resort to defining binary compatible array.
*/
#if LIQUID_USE_COMPLEX_H==1
# include <complex.h>
# define LIQUID_DEFINE_COMPLEX(R,C) typedef R _Complex C
#elif defined _GLIBCXX_COMPLEX || defined _LIBCPP_COMPLEX
# define LIQUID_DEFINE_COMPLEX(R,C) typedef std::complex<R> C
#else
# define LIQUID_DEFINE_COMPLEX(R,C) typedef struct {R real; R imag;} C;
#endif
//# define LIQUID_DEFINE_COMPLEX(R,C) typedef R C[2]
LIQUID_DEFINE_COMPLEX(float, liquid_float_complex);
LIQUID_DEFINE_COMPLEX(double, liquid_double_complex);
//
// MODULE : agc (automatic gain control)
//
// available squelch modes
typedef enum {
LIQUID_AGC_SQUELCH_UNKNOWN=0, // unknown/unavailable squelch mode
LIQUID_AGC_SQUELCH_ENABLED, // squelch enabled but signal not detected
LIQUID_AGC_SQUELCH_RISE, // signal first hit/exceeded threshold
LIQUID_AGC_SQUELCH_SIGNALHI, // signal level high (above threshold)
LIQUID_AGC_SQUELCH_FALL, // signal first dropped below threshold
LIQUID_AGC_SQUELCH_SIGNALLO, // signal level low (below threshold)
LIQUID_AGC_SQUELCH_TIMEOUT, // signal level low (below threshold for a certain time)
LIQUID_AGC_SQUELCH_DISABLED, // squelch not enabled
} agc_squelch_mode;
#define LIQUID_AGC_MANGLE_CRCF(name) LIQUID_CONCAT(agc_crcf, name)
#define LIQUID_AGC_MANGLE_RRRF(name) LIQUID_CONCAT(agc_rrrf, name)
// large macro
// AGC : name-mangling macro
// T : primitive data type
// TC : input/output data type
#define LIQUID_AGC_DEFINE_API(AGC,T,TC) \
\
/* Automatic gain control (agc) for level correction and signal */ \
/* detection */ \
typedef struct AGC(_s) * AGC(); \
\
/* Create automatic gain control object. */ \
AGC() AGC(_create)(void); \
\
/* Destroy object, freeing all internally-allocated memory. */ \
void AGC(_destroy)(AGC() _q); \
\
/* Print object properties to stdout, including received signal */ \
/* strength indication (RSSI), loop bandwidth, lock status, and squelch */ \
/* status. */ \
void AGC(_print)(AGC() _q); \
\
/* Reset internal state of agc object, including gain estimate, input */ \
/* signal level estimate, lock status, and squelch mode */ \
/* If the squelch mode is disabled, it stays disabled, but all enabled */ \
/* modes (e.g. LIQUID_AGC_SQUELCH_TIMEOUT) resets to just */ \
/* LIQUID_AGC_SQUELCH_ENABLED. */ \
void AGC(_reset)(AGC() _q); \
\
/* Execute automatic gain control on an single input sample */ \
/* _q : automatic gain control object */ \
/* _x : input sample */ \
/* _y : output sample */ \
void AGC(_execute)(AGC() _q, \
TC _x, \
TC * _y); \
\
/* Execute automatic gain control on block of samples pointed to by _x */ \
/* and store the result in the array of the same length _y. */ \
/* _q : automatic gain control object */ \
/* _x : input data array, [size: _n x 1] */ \
/* _n : number of input, output samples */ \
/* _y : output data array, [size: _n x 1] */ \
void AGC(_execute_block)(AGC() _q, \
TC * _x, \
unsigned int _n, \
TC * _y); \
\
/* Lock agc object. When locked, the agc object still makes an estimate */ \
/* of the signal level, but the gain setting is fixed and does not */ \
/* change. */ \
/* This is useful for providing coarse input signal level correction */ \
/* and quickly detecting a packet burst but not distorting signals with */ \
/* amplitude variation due to modulation. */ \
void AGC(_lock)(AGC() _q); \
\
/* Unlock agc object, and allow amplitude correction to resume. */ \
void AGC(_unlock)(AGC() _q); \
\
/* Set loop filter bandwidth: attack/release time. */ \
/* _q : automatic gain control object */ \
/* _bt : bandwidth-time constant, _bt > 0 */ \
void AGC(_set_bandwidth)(AGC() _q, float _bt); \
\
/* Get the agc object's loop filter bandwidth. */ \
float AGC(_get_bandwidth)(AGC() _q); \
\
/* Get the input signal's estimated energy level, relative to unity. */ \
/* The result is a linear value. */ \
float AGC(_get_signal_level)(AGC() _q); \
\
/* Set the agc object's estimate of the input signal by specifying an */ \
/* explicit linear value. This is useful for initializing the agc */ \
/* object with a preliminary estimate of the signal level to help gain */ \
/* convergence. */ \
/* _q : automatic gain control object */ \
/* _x2 : signal level of input, _x2 > 0 */ \
void AGC(_set_signal_level)(AGC() _q, \
float _x2); \
\
/* Get the agc object's estimated received signal strength indication */ \
/* (RSSI) on the input signal. */ \
/* This is similar to getting the signal level (above), but returns the */ \
/* result in dB rather than on a linear scale. */ \
float AGC(_get_rssi)(AGC() _q); \
\
/* Set the agc object's estimated received signal strength indication */ \
/* (RSSI) on the input signal by specifying an explicit value in dB. */ \
/* _q : automatic gain control object */ \
/* _rssi : signal level of input [dB] */ \
void AGC(_set_rssi)(AGC() _q, float _rssi); \
\
/* Get the gain value currently being applied to the input signal */ \
/* (linear). */ \
float AGC(_get_gain)(AGC() _q); \
\
/* Set the agc object's internal gain by specifying an explicit linear */ \
/* value. */ \
/* _q : automatic gain control object */ \
/* _gain : gain to apply to input signal, _gain > 0 */ \
void AGC(_set_gain)(AGC() _q, \
float _gain); \
\
/* Get the ouput scaling applied to each sample (linear). */ \
float AGC(_get_scale)(AGC() _q); \
\
/* Set the agc object's output scaling (linear). Note that this does */ \
/* affect the response of the AGC. */ \
/* _q : automatic gain control object */ \
/* _gain : gain to apply to input signal, _gain > 0 */ \
void AGC(_set_scale)(AGC() _q, \
float _scale); \
\
/* Estimate signal level and initialize internal gain on an input */ \
/* array. */ \
/* _q : automatic gain control object */ \
/* _x : input data array, [size: _n x 1] */ \
/* _n : number of input, output samples */ \
void AGC(_init)(AGC() _q, \
TC * _x, \
unsigned int _n); \
\
/* Enable squelch mode. */ \
void AGC(_squelch_enable)(AGC() _q); \
\
/* Disable squelch mode. */ \
void AGC(_squelch_disable)(AGC() _q); \
\
/* Return flag indicating if squelch is enabled or not. */ \
int AGC(_squelch_is_enabled)(AGC() _q); \
\
/* Set threshold for enabling/disabling squelch. */ \
/* _q : automatic gain control object */ \
/* _thresh : threshold for enabling squelch [dB] */ \
void AGC(_squelch_set_threshold)(AGC() _q, \
T _thresh); \
\
/* Get squelch threshold (value in dB) */ \
T AGC(_squelch_get_threshold)(AGC() _q); \
\
/* Set timeout before enabling squelch. */ \
/* _q : automatic gain control object */ \
/* _timeout : timeout before enabling squelch [samples] */ \
void AGC(_squelch_set_timeout)(AGC() _q, \
unsigned int _timeout); \
\
/* Get squelch timeout (number of samples) */ \
unsigned int AGC(_squelch_get_timeout)(AGC() _q); \
\
/* Get squelch status (e.g. LIQUID_AGC_SQUELCH_TIMEOUT) */ \
int AGC(_squelch_get_status)(AGC() _q); \
// Define agc APIs
LIQUID_AGC_DEFINE_API(LIQUID_AGC_MANGLE_CRCF, float, liquid_float_complex)
LIQUID_AGC_DEFINE_API(LIQUID_AGC_MANGLE_RRRF, float, float)
//
// MODULE : audio
//
// CVSD: continuously variable slope delta
typedef struct cvsd_s * cvsd;
// create cvsd object
// _num_bits : number of adjacent bits to observe (4 recommended)
// _zeta : slope adjustment multiplier (1.5 recommended)
// _alpha : pre-/post-emphasis filter coefficient (0.9 recommended)
// NOTE: _alpha must be in [0,1]
cvsd cvsd_create(unsigned int _num_bits,
float _zeta,
float _alpha);
// destroy cvsd object
void cvsd_destroy(cvsd _q);
// print cvsd object parameters
void cvsd_print(cvsd _q);
// encode/decode single sample
unsigned char cvsd_encode(cvsd _q, float _audio_sample);
float cvsd_decode(cvsd _q, unsigned char _bit);
// encode/decode 8 samples at a time
void cvsd_encode8(cvsd _q, float * _audio, unsigned char * _data);
void cvsd_decode8(cvsd _q, unsigned char _data, float * _audio);
//
// MODULE : buffer
//
// circular buffer
#define LIQUID_CBUFFER_MANGLE_FLOAT(name) LIQUID_CONCAT(cbufferf, name)
#define LIQUID_CBUFFER_MANGLE_CFLOAT(name) LIQUID_CONCAT(cbuffercf, name)
// large macro
// CBUFFER : name-mangling macro
// T : data type
#define LIQUID_CBUFFER_DEFINE_API(CBUFFER,T) \
\
/* Circular buffer object for storing and retrieving samples in a */ \
/* first-in/first-out (FIFO) manner using a minimal amount of memory */ \
typedef struct CBUFFER(_s) * CBUFFER(); \
\
/* Create circular buffer object of a particular maximum storage length */ \
/* _max_size : maximum buffer size, _max_size > 0 */ \
CBUFFER() CBUFFER(_create)(unsigned int _max_size); \
\
/* Create circular buffer object of a particular maximum storage size */ \
/* and specify the maximum number of elements that can be read at any */ \
/* any given time */ \
/* _max_size : maximum buffer size, _max_size > 0 */ \
/* _max_read : maximum size that will be read from buffer */ \
CBUFFER() CBUFFER(_create_max)(unsigned int _max_size, \
unsigned int _max_read); \
\
/* Destroy cbuffer object, freeing all internal memory */ \
void CBUFFER(_destroy)(CBUFFER() _q); \
\
/* Print cbuffer object properties to stdout */ \
void CBUFFER(_print)(CBUFFER() _q); \
\
/* Print cbuffer object properties and internal state */ \
void CBUFFER(_debug_print)(CBUFFER() _q); \
\
/* Clear internal buffer */ \
void CBUFFER(_reset)(CBUFFER() _q); \
\
/* Get the number of elements currently in the buffer */ \
unsigned int CBUFFER(_size)(CBUFFER() _q); \
\
/* Get the maximum number of elements the buffer can hold */ \
unsigned int CBUFFER(_max_size)(CBUFFER() _q); \
\
/* Get the maximum number of elements you may read at once */ \
unsigned int CBUFFER(_max_read)(CBUFFER() _q); \
\
/* Get the number of available slots (max_size - size) */ \
unsigned int CBUFFER(_space_available)(CBUFFER() _q); \
\
/* Return flag indicating if the buffer is full or not */ \
int CBUFFER(_is_full)(CBUFFER() _q); \
\
/* Write a single sample into the buffer */ \
/* _q : circular buffer object */ \
/* _v : input sample */ \
void CBUFFER(_push)(CBUFFER() _q, \
T _v); \
\
/* Write a block of samples to the buffer */ \
/* _q : circular buffer object */ \
/* _v : array of samples to write to buffer */ \
/* _n : number of samples to write */ \
void CBUFFER(_write)(CBUFFER() _q, \
T * _v, \
unsigned int _n); \
\
/* Remove and return a single element from the buffer by setting the */ \
/* value of the output sample pointed to by _v */ \
/* _q : circular buffer object */ \
/* _v : pointer to sample output */ \
void CBUFFER(_pop)(CBUFFER() _q, \
T * _v); \
\
/* Read buffer contents by returning a pointer to the linearized array; */ \
/* note that the returned pointer is only valid until another operation */ \
/* is performed on the circular buffer object */ \
/* _q : circular buffer object */ \
/* _num_requested : number of elements requested */ \
/* _v : output pointer */ \
/* _num_read : number of elements referenced by _v */ \
void CBUFFER(_read)(CBUFFER() _q, \
unsigned int _num_requested, \
T ** _v, \
unsigned int * _num_read); \
\
/* Release _n samples from the buffer */ \
/* _q : circular buffer object */ \
/* _n : number of elements to release */ \
void CBUFFER(_release)(CBUFFER() _q, \
unsigned int _n); \
// Define buffer APIs
LIQUID_CBUFFER_DEFINE_API(LIQUID_CBUFFER_MANGLE_FLOAT, float)
LIQUID_CBUFFER_DEFINE_API(LIQUID_CBUFFER_MANGLE_CFLOAT, liquid_float_complex)
// Windowing functions
#define LIQUID_WINDOW_MANGLE_FLOAT(name) LIQUID_CONCAT(windowf, name)
#define LIQUID_WINDOW_MANGLE_CFLOAT(name) LIQUID_CONCAT(windowcf, name)
// large macro
// WINDOW : name-mangling macro
// T : data type
#define LIQUID_WINDOW_DEFINE_API(WINDOW,T) \
\
/* Sliding window first-in/first-out buffer with a fixed size */ \
typedef struct WINDOW(_s) * WINDOW(); \
\
/* Create window buffer object of a fixed length */ \
WINDOW() WINDOW(_create)(unsigned int _n); \
\
/* Recreate window buffer object with new length. */ \
/* This extends an existing window's size, similar to the standard C */ \
/* library's realloc() to n samples. */ \
/* If the size of the new window is larger than the old one, the newest */ \
/* values are retained at the beginning of the buffer and the oldest */ \
/* values are truncated. If the size of the new window is smaller than */ \
/* the old one, the oldest values are truncated. */ \
/* _q : old window object */ \
/* _n : new window length */ \
WINDOW() WINDOW(_recreate)(WINDOW() _q, unsigned int _n); \
\
/* Destroy window object, freeing all internally memory */ \
void WINDOW(_destroy)(WINDOW() _q); \
\
/* Print window object to stdout */ \
void WINDOW(_print)(WINDOW() _q); \
\
/* Print window object to stdout (with extra information) */ \
void WINDOW(_debug_print)(WINDOW() _q); \
\
/* Reset window object (initialize to zeros) */ \
void WINDOW(_reset)(WINDOW() _q); \
\
/* Read the contents of the window by returning a pointer to the */ \
/* aligned internal memory array. This method guarantees that the */ \
/* elements are linearized. This method should only be used for */ \
/* reading; writing values to the buffer has unspecified results. */ \
/* Note that the returned pointer is only valid until another operation */ \
/* is performed on the window buffer object */ \
/* _q : window object */ \
/* _v : output pointer (set to internal array) */ \
void WINDOW(_read)(WINDOW() _q, \
T ** _v); \
\
/* Index single element in buffer at a particular index */ \
/* This retrieves the \(i^{th}\) sample in the window, storing the */ \
/* output value in _v. */ \
/* This is equivalent to first invoking read() and then indexing on the */ \
/* resulting pointer; however the result is obtained much faster. */ \
/* Therefore setting the index to 0 returns the oldest value in the */ \
/* window. */ \
/* _q : window object */ \
/* _i : index of element to read */ \
/* _v : output value pointer */ \
void WINDOW(_index)(WINDOW() _q, \
unsigned int _i, \
T * _v); \
\
/* Shifts a single sample into the right side of the window, pushing */ \
/* the oldest (left-most) sample out of the end. Unlike stacks, the */ \
/* window object has no equivalent "pop" method, as values are retained */ \
/* in memory until they are overwritten. */ \
/* _q : window object */ \
/* _v : single input element */ \
void WINDOW(_push)(WINDOW() _q, \
T _v); \
\
/* Write array of elements onto window buffer */ \
/* Effectively, this is equivalent to pushing each sample one at a */ \
/* time, but executes much faster. */ \
/* _q : window object */ \
/* _v : input array of values to write */ \
/* _n : number of input values to write */ \
void WINDOW(_write)(WINDOW() _q, \
T * _v, \
unsigned int _n); \
// Define window APIs
LIQUID_WINDOW_DEFINE_API(LIQUID_WINDOW_MANGLE_FLOAT, float)
LIQUID_WINDOW_DEFINE_API(LIQUID_WINDOW_MANGLE_CFLOAT, liquid_float_complex)
//LIQUID_WINDOW_DEFINE_API(LIQUID_WINDOW_MANGLE_UINT, unsigned int)
// wdelay functions : windowed-delay
// Implements an efficient z^-k delay with minimal memory
#define LIQUID_WDELAY_MANGLE_FLOAT(name) LIQUID_CONCAT(wdelayf, name)
#define LIQUID_WDELAY_MANGLE_CFLOAT(name) LIQUID_CONCAT(wdelaycf, name)
//#define LIQUID_WDELAY_MANGLE_UINT(name) LIQUID_CONCAT(wdelayui, name)
// large macro
// WDELAY : name-mangling macro
// T : data type
#define LIQUID_WDELAY_DEFINE_API(WDELAY,T) \
\
/* Efficient digital delay line using a minimal amount of memory */ \
typedef struct WDELAY(_s) * WDELAY(); \
\
/* Create delay buffer object with a particular number of samples of */ \
/* delay */ \
/* _delay : number of samples of delay in the wdelay object */ \
WDELAY() WDELAY(_create)(unsigned int _delay); \
\
/* Re-create delay buffer object, adjusting the delay size, preserving */ \
/* the internal state of the object */ \
/* _q : old delay buffer object */ \
/* _delay : delay for new object */ \
WDELAY() WDELAY(_recreate)(WDELAY() _q, \
unsigned int _delay); \
\
/* Destroy delay buffer object, freeing internal memory */ \
void WDELAY(_destroy)(WDELAY() _q); \
\
/* Print delay buffer object's state to stdout */ \
void WDELAY(_print)(WDELAY() _q); \
\
/* Clear/reset state of object */ \
void WDELAY(_reset)(WDELAY() _q); \
\
/* Read delayed sample at the head of the buffer and store it to the */ \
/* output pointer */ \
/* _q : delay buffer object */ \
/* _v : value of delayed element */ \
void WDELAY(_read)(WDELAY() _q, \
T * _v); \
\
/* Push new sample into delay buffer object */ \
/* _q : delay buffer object */ \
/* _v : new value to be added to buffer */ \
void WDELAY(_push)(WDELAY() _q, \
T _v); \
// Define wdelay APIs
LIQUID_WDELAY_DEFINE_API(LIQUID_WDELAY_MANGLE_FLOAT, float)
LIQUID_WDELAY_DEFINE_API(LIQUID_WDELAY_MANGLE_CFLOAT, liquid_float_complex)
//LIQUID_WDELAY_DEFINE_API(LIQUID_WDELAY_MANGLE_UINT, unsigned int)
//
// MODULE : channel
//
#define LIQUID_CHANNEL_MANGLE_CCCF(name) LIQUID_CONCAT(channel_cccf,name)
// large macro
// CHANNEL : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_CHANNEL_DEFINE_API(CHANNEL,TO,TC,TI) \
\
/* Channel emulation */ \
typedef struct CHANNEL(_s) * CHANNEL(); \
\
/* Create channel object with default parameters */ \
CHANNEL() CHANNEL(_create)(void); \
\
/* Destroy channel object, freeing all internal memory */ \
void CHANNEL(_destroy)(CHANNEL() _q); \
\
/* Print channel object internals to standard output */ \
void CHANNEL(_print)(CHANNEL() _q); \
\
/* Include additive white Gausss noise impairment */ \
/* _q : channel object */ \
/* _N0dB : noise floor power spectral density [dB] */ \
/* _SNRdB : signal-to-noise ratio [dB] */ \
void CHANNEL(_add_awgn)(CHANNEL() _q, \
float _N0dB, \
float _SNRdB); \
\
/* Include carrier offset impairment */ \
/* _q : channel object */ \
/* _frequency : carrier frequency offset [radians/sample] */ \
/* _phase : carrier phase offset [radians] */ \
void CHANNEL(_add_carrier_offset)(CHANNEL() _q, \
float _frequency, \
float _phase); \
\
/* Include multi-path channel impairment */ \
/* _q : channel object */ \
/* _h : channel coefficients (NULL for random) */ \
/* _h_len : number of channel coefficients */ \
void CHANNEL(_add_multipath)(CHANNEL() _q, \
TC * _h, \
unsigned int _h_len); \
\
/* Include slowly-varying shadowing impairment */ \
/* _q : channel object */ \
/* _sigma : standard deviation for log-normal shadowing */ \
/* _fd : Doppler frequency, 0 <= _fd < 0.5 */ \
void CHANNEL(_add_shadowing)(CHANNEL() _q, \
float _sigma, \
float _fd); \
\
/* Apply channel impairments on single input sample */ \
/* _q : channel object */ \
/* _x : input sample */ \
/* _y : pointer to output sample */ \
void CHANNEL(_execute)(CHANNEL() _q, \
TI _x, \
TO * _y); \
\
/* Apply channel impairments on block of samples */ \
/* _q : channel object */ \
/* _x : input array, [size: _n x 1] */ \
/* _n : input array, length */ \
/* _y : output array, [size: _n x 1] */ \
void CHANNEL(_execute_block)(CHANNEL() _q, \
TI * _x, \
unsigned int _n, \
TO * _y); \
LIQUID_CHANNEL_DEFINE_API(LIQUID_CHANNEL_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
//
// time-varying multi-path channel
//
#define LIQUID_TVMPCH_MANGLE_CCCF(name) LIQUID_CONCAT(tvmpch_cccf,name)
// large macro
// TVMPCH : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_TVMPCH_DEFINE_API(TVMPCH,TO,TC,TI) \
\
/* Time-varying multipath channel emulation */ \
typedef struct TVMPCH(_s) * TVMPCH(); \
\
/* Create time-varying multi-path channel emulator object, specifying */ \
/* the number of coefficients, the standard deviation of coefficients, */ \
/* and the coherence time. The larger the standard deviation, the more */ \
/* dramatic the frequency response of the channel. The shorter the */ \
/* coeherent time, the faster the channel effects. */ \
/* _n : number of coefficients, _n > 0 */ \
/* _std : standard deviation, _std >= 0 */ \
/* _tau : normalized coherence time, 0 < _tau < 1 */ \
TVMPCH() TVMPCH(_create)(unsigned int _n, \
float _std, \
float _tau); \
\
/* Destroy channel object, freeing all internal memory */ \
void TVMPCH(_destroy)(TVMPCH() _q); \
\
/* Reset object */ \
void TVMPCH(_reset)(TVMPCH() _q); \
\
/* Print channel object internals to standard output */ \
void TVMPCH(_print)(TVMPCH() _q); \
\
/* Push sample into emulator */ \
/* _q : channel object */ \
/* _x : input sample */ \
void TVMPCH(_push)(TVMPCH() _q, \
TI _x); \
\
/* Compute output sample */ \
/* _q : channel object */ \
/* _y : output sample */ \
void TVMPCH(_execute)(TVMPCH() _q, \
TO * _y); \
\
/* Apply channel impairments on a block of samples */ \
/* _q : channel object */ \
/* _x : input array, [size: _n x 1] */ \
/* _n : input array length */ \
/* _y : output array, [size: _n x 1] */ \
void TVMPCH(_execute_block)(TVMPCH() _q, \
TI * _x, \
unsigned int _n, \
TO * _y); \
LIQUID_TVMPCH_DEFINE_API(LIQUID_TVMPCH_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
//
// MODULE : dotprod (vector dot product)
//
#define LIQUID_DOTPROD_MANGLE_RRRF(name) LIQUID_CONCAT(dotprod_rrrf,name)
#define LIQUID_DOTPROD_MANGLE_CCCF(name) LIQUID_CONCAT(dotprod_cccf,name)
#define LIQUID_DOTPROD_MANGLE_CRCF(name) LIQUID_CONCAT(dotprod_crcf,name)
// large macro
// DOTPROD : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_DOTPROD_DEFINE_API(DOTPROD,TO,TC,TI) \
\
/* Vector dot product operation */ \
typedef struct DOTPROD(_s) * DOTPROD(); \
\
/* Run dot product without creating object. This is less efficient than */ \
/* creating the object as it is an unoptimized portable implementation */ \
/* that doesn't take advantage of processor extensions. It is meant to */ \
/* provide a baseline for performance comparison and a convenient way */ \
/* to invoke a dot product operation when fast operation is not */ \
/* necessary. */ \
/* _v : coefficients array [size: _n x 1] */ \
/* _x : input array [size: _n x 1] */ \
/* _n : dotprod length, _n > 0 */ \
/* _y : output sample pointer */ \
void DOTPROD(_run)( TC * _v, \
TI * _x, \
unsigned int _n, \
TO * _y); \
\
/* This provides the same unoptimized operation as the 'run()' method */ \
/* above, but with the loop unrolled by a factor of 4. It is marginally */ \
/* faster than 'run()' without unrolling the loop. */ \
/* _v : coefficients array [size: _n x 1] */ \
/* _x : input array [size: _n x 1] */ \
/* _n : dotprod length, _n > 0 */ \
/* _y : output sample pointer */ \
void DOTPROD(_run4)( TC * _v, \
TI * _x, \
unsigned int _n, \
TO * _y); \
\
/* Create vector dot product object */ \
/* _v : coefficients array [size: _n x 1] */ \
/* _n : dotprod length, _n > 0 */ \
DOTPROD() DOTPROD(_create)(TC * _v, \
unsigned int _n); \
\
/* Re-create dot product object of potentially a different length with */ \
/* different coefficients. If the length of the dot product object does */ \
/* not change, not memory reallocation is invoked. */ \
/* _q : old dotprod object */ \
/* _v : coefficients array [size: _n x 1] */ \
/* _n : dotprod length, _n > 0 */ \
DOTPROD() DOTPROD(_recreate)(DOTPROD() _q, \
TC * _v, \
unsigned int _n); \
\
/* Destroy dotprod object, freeing all internal memory */ \
void DOTPROD(_destroy)(DOTPROD() _q); \
\
/* Print dotprod object internals to standard output */ \
void DOTPROD(_print)(DOTPROD() _q); \
\
/* Execute dot product on an input array */ \
/* _q : dotprod object */ \
/* _x : input array [size: _n x 1] */ \
/* _y : output sample pointer */ \
void DOTPROD(_execute)(DOTPROD() _q, \
TI * _x, \
TO * _y); \
LIQUID_DOTPROD_DEFINE_API(LIQUID_DOTPROD_MANGLE_RRRF,
float,
float,
float)
LIQUID_DOTPROD_DEFINE_API(LIQUID_DOTPROD_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
LIQUID_DOTPROD_DEFINE_API(LIQUID_DOTPROD_MANGLE_CRCF,
liquid_float_complex,
float,
liquid_float_complex)
//
// sum squared methods
//
float liquid_sumsqf(float * _v,
unsigned int _n);
float liquid_sumsqcf(liquid_float_complex * _v,
unsigned int _n);
//
// MODULE : equalization
//
// least mean-squares (LMS)
#define LIQUID_EQLMS_MANGLE_RRRF(name) LIQUID_CONCAT(eqlms_rrrf,name)
#define LIQUID_EQLMS_MANGLE_CCCF(name) LIQUID_CONCAT(eqlms_cccf,name)
// large macro
// EQLMS : name-mangling macro
// T : data type
#define LIQUID_EQLMS_DEFINE_API(EQLMS,T) \
\
/* Least mean-squares equalization object */ \
typedef struct EQLMS(_s) * EQLMS(); \
\
/* Create LMS EQ initialized with external coefficients */ \
/* _h : filter coefficients; set to NULL for {1,0,0...},[size: _n x 1] */ \
/* _n : filter length */ \
EQLMS() EQLMS(_create)(T * _h, \
unsigned int _n); \
\
/* Create LMS EQ initialized with square-root Nyquist prototype filter */ \
/* as initial set of coefficients. This is useful for applications */ \
/* where the baseline matched filter is a good starting point, but */ \
/* where equalization is needed to properly remove inter-symbol */ \
/* interference. */ \
/* The filter length is \(2 k m + 1\) */ \
/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \
/* _k : samples/symbol */ \
/* _m : filter delay (symbols) */ \
/* _beta : rolloff factor (0 < beta <= 1) */ \
/* _dt : fractional sample delay */ \
EQLMS() EQLMS(_create_rnyquist)(int _type, \
unsigned int _k, \
unsigned int _m, \
float _beta, \
float _dt); \
\
/* Create LMS EQ initialized with low-pass filter */ \
/* _n : filter length */ \
/* _fc : filter cut-off normalized to sample rate, 0 < _fc <= 0.5 */ \
EQLMS() EQLMS(_create_lowpass)(unsigned int _n, \
float _fc); \
\
/* Re-create EQ initialized with external coefficients */ \
/* _q : equalizer object */ \
/* _h : filter coefficients (NULL for {1,0,0...}), [size: _n x 1] */ \
/* _h_len : filter length */ \
EQLMS() EQLMS(_recreate)(EQLMS() _q, \
T * _h, \
unsigned int _h_len); \
\
/* Destroy equalizer object, freeing all internal memory */ \
void EQLMS(_destroy)(EQLMS() _q); \
\
/* Reset equalizer object, clearing internal state */ \
void EQLMS(_reset)(EQLMS() _q); \
\
/* Print equalizer internal state */ \
void EQLMS(_print)(EQLMS() _q); \
\
/* Get equalizer learning rate */ \
float EQLMS(_get_bw)(EQLMS() _q); \
\
/* Set equalizer learning rate */ \
/* _q : equalizer object */ \
/* _lambda : learning rate, _lambda > 0 */ \
void EQLMS(_set_bw)(EQLMS() _q, \
float _lambda); \
\
/* Push sample into equalizer internal buffer */ \
/* _q : equalizer object */ \
/* _x : input sample */ \
void EQLMS(_push)(EQLMS() _q, \
T _x); \
\
/* Push block of samples into internal buffer of equalizer object */ \
/* _q : equalizer object */ \
/* _x : input sample array, [size: _n x 1] */ \
/* _n : input sample array length */ \
void EQLMS(_push_block)(EQLMS() _q, \
T * _x, \
unsigned int _n); \
\
/* Execute internal dot product and return result */ \
/* _q : equalizer object */ \
/* _y : output sample */ \
void EQLMS(_execute)(EQLMS() _q, \
T * _y); \
\
/* Execute equalizer with block of samples using constant */ \
/* modulus algorithm, operating on a decimation rate of _k */ \
/* samples. */ \
/* _q : equalizer object */ \
/* _k : down-sampling rate */ \
/* _x : input sample array [size: _n x 1] */ \
/* _n : input sample array length */ \
/* _y : output sample array [size: _n x 1] */ \
void EQLMS(_execute_block)(EQLMS() _q, \
unsigned int _k, \
T * _x, \
unsigned int _n, \
T * _y); \
\
/* Step through one cycle of equalizer training */ \
/* _q : equalizer object */ \
/* _d : desired output */ \
/* _d_hat : actual output */ \
void EQLMS(_step)(EQLMS() _q, \
T _d, \
T _d_hat); \
\
/* Step through one cycle of equalizer training (blind) */ \
/* _q : equalizer object */ \
/* _d_hat : actual output */ \
void EQLMS(_step_blind)(EQLMS() _q, \
T _d_hat); \
\
/* Get equalizer's internal coefficients */ \
/* _q : equalizer object */ \
/* _w : weights, [size: _p x 1] */ \
void EQLMS(_get_weights)(EQLMS() _q, \
T * _w); \
\
/* Train equalizer object on group of samples */ \
/* _q : equalizer object */ \
/* _w : input/output weights, [size: _p x 1] */ \
/* _x : received sample vector,[size: _n x 1] */ \
/* _d : desired output vector, [size: _n x 1] */ \
/* _n : input, output vector length */ \
void EQLMS(_train)(EQLMS() _q, \
T * _w, \
T * _x, \
T * _d, \
unsigned int _n); \
LIQUID_EQLMS_DEFINE_API(LIQUID_EQLMS_MANGLE_RRRF, float)
LIQUID_EQLMS_DEFINE_API(LIQUID_EQLMS_MANGLE_CCCF, liquid_float_complex)
// recursive least-squares (RLS)
#define LIQUID_EQRLS_MANGLE_RRRF(name) LIQUID_CONCAT(eqrls_rrrf,name)
#define LIQUID_EQRLS_MANGLE_CCCF(name) LIQUID_CONCAT(eqrls_cccf,name)
// large macro
// EQRLS : name-mangling macro
// T : data type
#define LIQUID_EQRLS_DEFINE_API(EQRLS,T) \
\
/* Recursive least mean-squares equalization object */ \
typedef struct EQRLS(_s) * EQRLS(); \
\
/* Create RLS EQ initialized with external coefficients */ \
/* _h : filter coefficients; set to NULL for {1,0,0...},[size: _n x 1] */ \
/* _n : filter length */ \
EQRLS() EQRLS(_create)(T * _h, \
unsigned int _n); \
\
/* Re-create EQ initialized with external coefficients */ \
/* _q : equalizer object */ \
/* _h : filter coefficients (NULL for {1,0,0...}), [size: _n x 1] */ \
/* _n : filter length */ \
EQRLS() EQRLS(_recreate)(EQRLS() _q, \
T * _h, \
unsigned int _n); \
\
/* Destroy equalizer object, freeing all internal memory */ \
void EQRLS(_destroy)(EQRLS() _q); \
\
/* Reset equalizer object, clearing internal state */ \
void EQRLS(_reset)(EQRLS() _q); \
\
/* Print equalizer internal state */ \
void EQRLS(_print)(EQRLS() _q); \
\
/* Get equalizer learning rate */ \
float EQRLS(_get_bw)(EQRLS() _q); \
\
/* Set equalizer learning rate */ \
/* _q : equalizer object */ \
/* _mu : learning rate, _mu > 0 */ \
void EQRLS(_set_bw)(EQRLS() _q, \
float _mu); \
\
/* Push sample into equalizer internal buffer */ \
/* _q : equalizer object */ \
/* _x : input sample */ \
void EQRLS(_push)(EQRLS() _q, T _x); \
\
/* Execute internal dot product and return result */ \
/* _q : equalizer object */ \
/* _y : output sample */ \
void EQRLS(_execute)(EQRLS() _q, T * _y); \
\
/* Step through one cycle of equalizer training */ \
/* _q : equalizer object */ \
/* _d : desired output */ \
/* _d_hat : actual output */ \
void EQRLS(_step)(EQRLS() _q, T _d, T _d_hat); \
\
/* Get equalizer's internal coefficients */ \
/* _q : equalizer object */ \
/* _w : weights, [size: _p x 1] */ \
void EQRLS(_get_weights)(EQRLS() _q, \
T * _w); \
\
/* Train equalizer object on group of samples */ \
/* _q : equalizer object */ \
/* _w : input/output weights, [size: _p x 1] */ \
/* _x : received sample vector,[size: _n x 1] */ \
/* _d : desired output vector, [size: _n x 1] */ \
/* _n : input, output vector length */ \
void EQRLS(_train)(EQRLS() _q, \
T * _w, \
T * _x, \
T * _d, \
unsigned int _n); \
LIQUID_EQRLS_DEFINE_API(LIQUID_EQRLS_MANGLE_RRRF, float)
LIQUID_EQRLS_DEFINE_API(LIQUID_EQRLS_MANGLE_CCCF, liquid_float_complex)
//
// MODULE : fec (forward error-correction)
//
// soft bit values
#define LIQUID_SOFTBIT_0 (0)
#define LIQUID_SOFTBIT_1 (255)
#define LIQUID_SOFTBIT_ERASURE (127)
// available CRC schemes
#define LIQUID_CRC_NUM_SCHEMES 7
typedef enum {
LIQUID_CRC_UNKNOWN=0, // unknown/unavailable CRC scheme
LIQUID_CRC_NONE, // no error-detection
LIQUID_CRC_CHECKSUM, // 8-bit checksum
LIQUID_CRC_8, // 8-bit CRC
LIQUID_CRC_16, // 16-bit CRC
LIQUID_CRC_24, // 24-bit CRC
LIQUID_CRC_32 // 32-bit CRC
} crc_scheme;
// pretty names for crc schemes
extern const char * crc_scheme_str[LIQUID_CRC_NUM_SCHEMES][2];
// Print compact list of existing and available CRC schemes
void liquid_print_crc_schemes();
// returns crc_scheme based on input string
crc_scheme liquid_getopt_str2crc(const char * _str);
// get length of CRC (bytes)
unsigned int crc_get_length(crc_scheme _scheme);
// generate error-detection key
// _scheme : error-detection scheme
// _msg : input data message, [size: _n x 1]
// _n : input data message size
unsigned int crc_generate_key(crc_scheme _scheme,
unsigned char * _msg,
unsigned int _n);
// generate error-detection key and append to end of message
// _scheme : error-detection scheme (resulting in 'p' bytes)
// _msg : input data message, [size: _n+p x 1]
// _n : input data message size (excluding key at end)
void crc_append_key(crc_scheme _scheme,
unsigned char * _msg,
unsigned int _n);
// validate message using error-detection key
// _scheme : error-detection scheme
// _msg : input data message, [size: _n x 1]
// _n : input data message size
// _key : error-detection key
int crc_validate_message(crc_scheme _scheme,
unsigned char * _msg,
unsigned int _n,
unsigned int _key);
// check message with key appended to end of array
// _scheme : error-detection scheme (resulting in 'p' bytes)
// _msg : input data message, [size: _n+p x 1]
// _n : input data message size (excluding key at end)
int crc_check_key(crc_scheme _scheme,
unsigned char * _msg,
unsigned int _n);
// get size of key (bytes)
unsigned int crc_sizeof_key(crc_scheme _scheme);
// available FEC schemes
#define LIQUID_FEC_NUM_SCHEMES 28
typedef enum {
LIQUID_FEC_UNKNOWN=0, // unknown/unsupported scheme
LIQUID_FEC_NONE, // no error-correction
LIQUID_FEC_REP3, // simple repeat code, r1/3
LIQUID_FEC_REP5, // simple repeat code, r1/5
LIQUID_FEC_HAMMING74, // Hamming (7,4) block code, r1/2 (really 4/7)
LIQUID_FEC_HAMMING84, // Hamming (7,4) with extra parity bit, r1/2
LIQUID_FEC_HAMMING128, // Hamming (12,8) block code, r2/3
LIQUID_FEC_GOLAY2412, // Golay (24,12) block code, r1/2
LIQUID_FEC_SECDED2216, // SEC-DED (22,16) block code, r8/11
LIQUID_FEC_SECDED3932, // SEC-DED (39,32) block code
LIQUID_FEC_SECDED7264, // SEC-DED (72,64) block code, r8/9
// codecs not defined internally (see http://www.ka9q.net/code/fec/)
LIQUID_FEC_CONV_V27, // r1/2, K=7, dfree=10
LIQUID_FEC_CONV_V29, // r1/2, K=9, dfree=12
LIQUID_FEC_CONV_V39, // r1/3, K=9, dfree=18
LIQUID_FEC_CONV_V615, // r1/6, K=15, dfree<=57 (Heller 1968)
// punctured (perforated) codes
LIQUID_FEC_CONV_V27P23, // r2/3, K=7, dfree=6
LIQUID_FEC_CONV_V27P34, // r3/4, K=7, dfree=5
LIQUID_FEC_CONV_V27P45, // r4/5, K=7, dfree=4
LIQUID_FEC_CONV_V27P56, // r5/6, K=7, dfree=4
LIQUID_FEC_CONV_V27P67, // r6/7, K=7, dfree=3
LIQUID_FEC_CONV_V27P78, // r7/8, K=7, dfree=3
LIQUID_FEC_CONV_V29P23, // r2/3, K=9, dfree=7
LIQUID_FEC_CONV_V29P34, // r3/4, K=9, dfree=6
LIQUID_FEC_CONV_V29P45, // r4/5, K=9, dfree=5
LIQUID_FEC_CONV_V29P56, // r5/6, K=9, dfree=5
LIQUID_FEC_CONV_V29P67, // r6/7, K=9, dfree=4
LIQUID_FEC_CONV_V29P78, // r7/8, K=9, dfree=4
// Reed-Solomon codes
LIQUID_FEC_RS_M8 // m=8, n=255, k=223
} fec_scheme;
// pretty names for fec schemes
extern const char * fec_scheme_str[LIQUID_FEC_NUM_SCHEMES][2];
// Print compact list of existing and available FEC schemes
void liquid_print_fec_schemes();
// returns fec_scheme based on input string
fec_scheme liquid_getopt_str2fec(const char * _str);
// fec object (pointer to fec structure)
typedef struct fec_s * fec;
// return the encoded message length using a particular error-
// correction scheme (object-independent method)
// _scheme : forward error-correction scheme
// _msg_len : raw, uncoded message length
unsigned int fec_get_enc_msg_length(fec_scheme _scheme,
unsigned int _msg_len);
// get the theoretical rate of a particular forward error-
// correction scheme (object-independent method)
float fec_get_rate(fec_scheme _scheme);
// create a fec object of a particular scheme
// _scheme : error-correction scheme
// _opts : (ignored)
fec fec_create(fec_scheme _scheme,
void *_opts);
// recreate fec object
// _q : old fec object
// _scheme : new error-correction scheme
// _opts : (ignored)
fec fec_recreate(fec _q,
fec_scheme _scheme,
void *_opts);
// destroy fec object
void fec_destroy(fec _q);
// print fec object internals
void fec_print(fec _q);
// encode a block of data using a fec scheme
// _q : fec object
// _dec_msg_len : decoded message length
// _msg_dec : decoded message
// _msg_enc : encoded message
void fec_encode(fec _q,
unsigned int _dec_msg_len,
unsigned char * _msg_dec,
unsigned char * _msg_enc);
// decode a block of data using a fec scheme
// _q : fec object
// _dec_msg_len : decoded message length
// _msg_enc : encoded message
// _msg_dec : decoded message
void fec_decode(fec _q,
unsigned int _dec_msg_len,
unsigned char * _msg_enc,
unsigned char * _msg_dec);
// decode a block of data using a fec scheme (soft decision)
// _q : fec object
// _dec_msg_len : decoded message length
// _msg_enc : encoded message (soft bits)
// _msg_dec : decoded message
void fec_decode_soft(fec _q,
unsigned int _dec_msg_len,
unsigned char * _msg_enc,
unsigned char * _msg_dec);
//
// Packetizer
//
// computes the number of encoded bytes after packetizing
//
// _n : number of uncoded input bytes
// _crc : error-detecting scheme
// _fec0 : inner forward error-correction code
// _fec1 : outer forward error-correction code
unsigned int packetizer_compute_enc_msg_len(unsigned int _n,
int _crc,
int _fec0,
int _fec1);
// computes the number of decoded bytes before packetizing
//
// _k : number of encoded bytes
// _crc : error-detecting scheme
// _fec0 : inner forward error-correction code
// _fec1 : outer forward error-correction code
unsigned int packetizer_compute_dec_msg_len(unsigned int _k,
int _crc,
int _fec0,
int _fec1);
typedef struct packetizer_s * packetizer;
// create packetizer object
//
// _n : number of uncoded input bytes
// _crc : error-detecting scheme
// _fec0 : inner forward error-correction code
// _fec1 : outer forward error-correction code
packetizer packetizer_create(unsigned int _dec_msg_len,
int _crc,
int _fec0,
int _fec1);
// re-create packetizer object
//
// _p : initialz packetizer object
// _n : number of uncoded input bytes
// _crc : error-detecting scheme
// _fec0 : inner forward error-correction code
// _fec1 : outer forward error-correction code
packetizer packetizer_recreate(packetizer _p,
unsigned int _dec_msg_len,
int _crc,
int _fec0,
int _fec1);
// destroy packetizer object
void packetizer_destroy(packetizer _p);
// print packetizer object internals
void packetizer_print(packetizer _p);
// access methods
unsigned int packetizer_get_dec_msg_len(packetizer _p);
unsigned int packetizer_get_enc_msg_len(packetizer _p);
crc_scheme packetizer_get_crc (packetizer _p);
fec_scheme packetizer_get_fec0 (packetizer _p);
fec_scheme packetizer_get_fec1 (packetizer _p);
// Execute the packetizer on an input message
//
// _p : packetizer object
// _msg : input message (uncoded bytes)
// _pkt : encoded output message
void packetizer_encode(packetizer _p,
const unsigned char * _msg,
unsigned char * _pkt);
// Execute the packetizer to decode an input message, return validity
// check of resulting data
//
// _p : packetizer object
// _pkt : input message (coded bytes)
// _msg : decoded output message
int packetizer_decode(packetizer _p,
const unsigned char * _pkt,
unsigned char * _msg);
// Execute the packetizer to decode an input message, return validity
// check of resulting data
//
// _p : packetizer object
// _pkt : input message (coded soft bits)
// _msg : decoded output message
int packetizer_decode_soft(packetizer _p,
const unsigned char * _pkt,
unsigned char * _msg);
//
// interleaver
//
typedef struct interleaver_s * interleaver;
// create interleaver
// _n : number of bytes
interleaver interleaver_create(unsigned int _n);
// destroy interleaver object
void interleaver_destroy(interleaver _q);
// print interleaver object internals
void interleaver_print(interleaver _q);
// set depth (number of internal iterations)
// _q : interleaver object
// _depth : depth
void interleaver_set_depth(interleaver _q,
unsigned int _depth);
// execute forward interleaver (encoder)
// _q : interleaver object
// _msg_dec : decoded (un-interleaved) message
// _msg_enc : encoded (interleaved) message
void interleaver_encode(interleaver _q,
unsigned char * _msg_dec,
unsigned char * _msg_enc);
// execute forward interleaver (encoder) on soft bits
// _q : interleaver object
// _msg_dec : decoded (un-interleaved) message
// _msg_enc : encoded (interleaved) message
void interleaver_encode_soft(interleaver _q,
unsigned char * _msg_dec,
unsigned char * _msg_enc);
// execute reverse interleaver (decoder)
// _q : interleaver object
// _msg_enc : encoded (interleaved) message
// _msg_dec : decoded (un-interleaved) message
void interleaver_decode(interleaver _q,
unsigned char * _msg_enc,
unsigned char * _msg_dec);
// execute reverse interleaver (decoder) on soft bits
// _q : interleaver object
// _msg_enc : encoded (interleaved) message
// _msg_dec : decoded (un-interleaved) message
void interleaver_decode_soft(interleaver _q,
unsigned char * _msg_enc,
unsigned char * _msg_dec);
//
// MODULE : fft (fast Fourier transform)
//
// type of transform
typedef enum {
LIQUID_FFT_UNKNOWN = 0, // unknown transform type
// regular complex one-dimensional transforms
LIQUID_FFT_FORWARD = +1, // complex one-dimensional FFT
LIQUID_FFT_BACKWARD = -1, // complex one-dimensional inverse FFT
// discrete cosine transforms
LIQUID_FFT_REDFT00 = 10, // real one-dimensional DCT-I
LIQUID_FFT_REDFT10 = 11, // real one-dimensional DCT-II
LIQUID_FFT_REDFT01 = 12, // real one-dimensional DCT-III
LIQUID_FFT_REDFT11 = 13, // real one-dimensional DCT-IV
// discrete sine transforms
LIQUID_FFT_RODFT00 = 20, // real one-dimensional DST-I
LIQUID_FFT_RODFT10 = 21, // real one-dimensional DST-II
LIQUID_FFT_RODFT01 = 22, // real one-dimensional DST-III
LIQUID_FFT_RODFT11 = 23, // real one-dimensional DST-IV
// modified discrete cosine transform
LIQUID_FFT_MDCT = 30, // MDCT
LIQUID_FFT_IMDCT = 31, // IMDCT
} liquid_fft_type;
#define LIQUID_FFT_MANGLE_FLOAT(name) LIQUID_CONCAT(fft,name)
// Macro : FFT
// FFT : name-mangling macro
// T : primitive data type
// TC : primitive data type (complex)
#define LIQUID_FFT_DEFINE_API(FFT,T,TC) \
\
/* Fast Fourier Transform (FFT) and inverse (plan) object */ \
typedef struct FFT(plan_s) * FFT(plan); \
\
/* Create regular complex one-dimensional transform */ \
/* _n : transform size */ \
/* _x : pointer to input array [size: _n x 1] */ \
/* _y : pointer to output array [size: _n x 1] */ \
/* _dir : direction (e.g. LIQUID_FFT_FORWARD) */ \
/* _flags : options, optimization */ \
FFT(plan) FFT(_create_plan)(unsigned int _n, \
TC * _x, \
TC * _y, \
int _dir, \
int _flags); \
\
/* Create real-to-real one-dimensional transform */ \
/* _n : transform size */ \
/* _x : pointer to input array [size: _n x 1] */ \
/* _y : pointer to output array [size: _n x 1] */ \
/* _type : transform type (e.g. LIQUID_FFT_REDFT00) */ \
/* _flags : options, optimization */ \
FFT(plan) FFT(_create_plan_r2r_1d)(unsigned int _n, \
T * _x, \
T * _y, \
int _type, \
int _flags); \
\
/* Destroy transform and free all internally-allocated memory */ \
void FFT(_destroy_plan)(FFT(plan) _p); \
\
/* Print transform plan and internal strategy to stdout. This includes */ \
/* information on the strategy for computing large transforms with many */ \
/* prime factors or with large prime factors. */ \
void FFT(_print_plan)(FFT(plan) _p); \
\
/* Run the transform */ \
void FFT(_execute)(FFT(plan) _p); \
\
/* Perform n-point FFT allocating plan internally */ \
/* _nfft : fft size */ \
/* _x : input array [size: _nfft x 1] */ \
/* _y : output array [size: _nfft x 1] */ \
/* _dir : fft direction: LIQUID_FFT_{FORWARD,BACKWARD} */ \
/* _flags : fft flags */ \
void FFT(_run)(unsigned int _n, \
TC * _x, \
TC * _y, \
int _dir, \
int _flags); \
\
/* Perform n-point real one-dimensional FFT allocating plan internally */ \
/* _nfft : fft size */ \
/* _x : input array [size: _nfft x 1] */ \
/* _y : output array [size: _nfft x 1] */ \
/* _type : fft type, e.g. LIQUID_FFT_REDFT10 */ \
/* _flags : fft flags */ \
void FFT(_r2r_1d_run)(unsigned int _n, \
T * _x, \
T * _y, \
int _type, \
int _flags); \
\
/* Perform _n-point fft shift */ \
/* _x : input array [size: _n x 1] */ \
/* _n : input array size */ \
void FFT(_shift)(TC * _x, \
unsigned int _n); \
LIQUID_FFT_DEFINE_API(LIQUID_FFT_MANGLE_FLOAT,float,liquid_float_complex)
// antiquated fft methods
// FFT(plan) FFT(_create_plan_mdct)(unsigned int _n,
// T * _x,
// T * _y,
// int _kind,
// int _flags);
//
// spectral periodogram
//
#define LIQUID_SPGRAM_MANGLE_CFLOAT(name) LIQUID_CONCAT(spgramcf,name)
#define LIQUID_SPGRAM_MANGLE_FLOAT(name) LIQUID_CONCAT(spgramf, name)
// Macro : SPGRAM
// SPGRAM : name-mangling macro
// T : primitive data type
// TC : primitive data type (complex)
// TI : primitive data type (input)
#define LIQUID_SPGRAM_DEFINE_API(SPGRAM,T,TC,TI) \
\
/* Spectral periodogram object for computing power spectral density */ \
/* estimates of various signals */ \
typedef struct SPGRAM(_s) * SPGRAM(); \
\
/* Create spgram object, fully defined */ \
/* _nfft : transform (FFT) size, _nfft >= 2 */ \
/* _wtype : window type, e.g. LIQUID_WINDOW_HAMMING */ \
/* _window_len : window length, 1 <= _window_len <= _nfft */ \
/* _delay : delay between transforms, _delay > 0 */ \
SPGRAM() SPGRAM(_create)(unsigned int _nfft, \
int _wtype, \
unsigned int _window_len, \
unsigned int _delay); \
\
/* Create default spgram object of a particular transform size using */ \
/* the Kaiser-Bessel window (LIQUID_WINDOW_KAISER), a window length */ \
/* equal to _nfft/2, and a delay of _nfft/4 */ \
/* _nfft : FFT size, _nfft >= 2 */ \
SPGRAM() SPGRAM(_create_default)(unsigned int _nfft); \
\
/* Destroy spgram object, freeing all internally-allocated memory */ \
void SPGRAM(_destroy)(SPGRAM() _q); \
\
/* Clears the internal state of the object, but not the internal buffer */ \
void SPGRAM(_clear)(SPGRAM() _q); \
\
/* Reset the object to its original state completely. This effectively */ \
/* executes the clear() method and then resets the internal buffer */ \
void SPGRAM(_reset)(SPGRAM() _q); \
\
/* Print internal state of the object to stdout */ \
void SPGRAM(_print)(SPGRAM() _q); \
\
/* Set the forgetting factor (filter bandwidth) for accumulating */ \
/* independent transform squared magnitude outputs. */ \
/* This is used to compute a running time-average power spectral */ \
/* density output. */ \
/* The value of _alpha determines how the power spectral estimate is */ \
/* accumulated across transforms and can range from 0 to 1 with a */ \
/* special case of -1 to accumulate infinitely. */ \
/* Setting _alpha to 0 minimizes the bandwidth and the PSD estimate */ \
/* will never update. */ \
/* Setting _alpha to 1 forces the object to always use the most recent */ \
/* spectral estimate. */ \
/* Setting _alpha to -1 is a special case to enable infinite spectral */ \
/* accumulation. */ \
/* _q : spectral periodogram object */ \
/* _alpha : forgetting factor, set to -1 for infinite, 0<=_alpha<=1 */ \
int SPGRAM(_set_alpha)(SPGRAM() _q, \
float _alpha); \
\
/* Set the center frequency of the received signal. */ \
/* This is for display purposes only when generating the output image. */ \
/* _q : spectral periodogram object */ \
/* _freq : center frequency [Hz] */ \
int SPGRAM(_set_freq)(SPGRAM() _q, \
float _freq); \
\
/* Set the sample rate (frequency) of the received signal. */ \
/* This is for display purposes only when generating the output image. */ \
/* _q : spectral periodogram object */ \
/* _rate : sample rate [Hz] */ \
int SPGRAM(_set_rate)(SPGRAM() _q, \
float _rate); \
\
/* Get transform (FFT) size */ \
unsigned int SPGRAM(_get_nfft)(SPGRAM() _q); \
\
/* Get window length */ \
unsigned int SPGRAM(_get_window_len)(SPGRAM() _q); \
\
/* Get delay between transforms */ \
unsigned int SPGRAM(_get_delay)(SPGRAM() _q); \
\
/* Get number of samples processed since reset */ \
unsigned long long int SPGRAM(_get_num_samples)(SPGRAM() _q); \
\
/* Get number of samples processed since object was created */ \
unsigned long long int SPGRAM(_get_num_samples_total)(SPGRAM() _q); \
\
/* Get number of transforms processed since reset */ \
unsigned long long int SPGRAM(_get_num_transforms)(SPGRAM() _q); \
\
/* Get number of transforms processed since object was created */ \
unsigned long long int SPGRAM(_get_num_transforms_total)(SPGRAM() _q); \
\
/* Get forgetting factor (filter bandwidth) */ \
float SPGRAM(_get_alpha)(SPGRAM() _q); \
\
/* Push a single sample into the object, executing internal transform */ \
/* as necessary. */ \
/* _q : spgram object */ \
/* _x : input sample */ \
void SPGRAM(_push)(SPGRAM() _q, \
TI _x); \
\
/* Write a block of samples to the object, executing internal */ \
/* transform as necessary. */ \
/* _q : spgram object */ \
/* _x : input buffer [size: _n x 1] */ \
/* _n : input buffer length */ \
void SPGRAM(_write)(SPGRAM() _q, \
TI * _x, \
unsigned int _n); \
\
/* Compute spectral periodogram output (fft-shifted values in dB) from */ \
/* current buffer contents */ \
/* _q : spgram object */ \
/* _X : output spectrum (dB), [size: _nfft x 1] */ \
void SPGRAM(_get_psd)(SPGRAM() _q, \
T * _X); \
\
/* Export stand-alone gnuplot file for plotting output spectrum, */ \
/* returning 0 on sucess, anything other than 0 for failure */ \
/* _q : spgram object */ \
/* _filename : input buffer [size: _n x 1] */ \
int SPGRAM(_export_gnuplot)(SPGRAM() _q, \
const char * _filename); \
\
/* Estimate spectrum on input signal (create temporary object for */ \
/* convenience */ \
/* _nfft : FFT size */ \
/* _x : input signal [size: _n x 1] */ \
/* _n : input signal length */ \
/* _psd : output spectrum, [size: _nfft x 1] */ \
void SPGRAM(_estimate_psd)(unsigned int _nfft, \
TI * _x, \
unsigned int _n, \
T * _psd); \
LIQUID_SPGRAM_DEFINE_API(LIQUID_SPGRAM_MANGLE_CFLOAT,
float,
liquid_float_complex,
liquid_float_complex)
LIQUID_SPGRAM_DEFINE_API(LIQUID_SPGRAM_MANGLE_FLOAT,
float,
liquid_float_complex,
float)
//
// asgram : ascii spectral periodogram
//
#define LIQUID_ASGRAM_MANGLE_CFLOAT(name) LIQUID_CONCAT(asgramcf,name)
#define LIQUID_ASGRAM_MANGLE_FLOAT(name) LIQUID_CONCAT(asgramf, name)
// Macro : ASGRAM
// ASGRAM : name-mangling macro
// T : primitive data type
// TC : primitive data type (complex)
// TI : primitive data type (input)
#define LIQUID_ASGRAM_DEFINE_API(ASGRAM,T,TC,TI) \
\
/* ASCII spectral periodogram for computing and displaying an estimate */ \
/* of a signal's power spectrum with ASCII characters */ \
typedef struct ASGRAM(_s) * ASGRAM(); \
\
/* Create asgram object with size _nfft */ \
/* _nfft : size of FFT taken for each transform (character width) */ \
ASGRAM() ASGRAM(_create)(unsigned int _nfft); \
\
/* Destroy asgram object, freeing all internally-allocated memory */ \
void ASGRAM(_destroy)(ASGRAM() _q); \
\
/* Reset the internal state of the asgram object */ \
void ASGRAM(_reset)(ASGRAM() _q); \
\
/* Set the scale and offset for spectrogram in terms of dB for display */ \
/* purposes */ \
/* _q : asgram object */ \
/* _ref : signal reference level [dB] */ \
/* _div : signal division [dB] */ \
void ASGRAM(_set_scale)(ASGRAM() _q, \
float _ref, \
float _div); \
\
/* Set the display's 10 characters for output string starting from the */ \
/* weakest and ending with the strongest */ \
/* _q : asgram object */ \
/* _ascii : 10-character display, default: " .,-+*&NM#" */ \
void ASGRAM(_set_display)(ASGRAM() _q, \
const char * _ascii); \
\
/* Push a single sample into the asgram object, executing internal */ \
/* transform as necessary. */ \
/* _q : asgram object */ \
/* _x : input sample */ \
void ASGRAM(_push)(ASGRAM() _q, \
TI _x); \
\
/* Write a block of samples to the asgram object, executing internal */ \
/* transforms as necessary. */ \
/* _q : asgram object */ \
/* _x : input buffer [size: _n x 1] */ \
/* _n : input buffer length */ \
void ASGRAM(_write)(ASGRAM() _q, \
TI * _x, \
unsigned int _n); \
\
/* Compute spectral periodogram output from current buffer contents */ \
/* and return the ascii character string to display along with the peak */ \
/* value and its frequency location */ \
/* _q : asgram object */ \
/* _ascii : output ASCII string [size: _nfft x 1] */ \
/* _peakval : peak power spectral density value [dB] */ \
/* _peakfreq : peak power spectral density frequency */ \
void ASGRAM(_execute)(ASGRAM() _q, \
char * _ascii, \
float * _peakval, \
float * _peakfreq); \
\
/* Compute spectral periodogram output from current buffer contents and */ \
/* print standard format to stdout */ \
void ASGRAM(_print)(ASGRAM() _q); \
LIQUID_ASGRAM_DEFINE_API(LIQUID_ASGRAM_MANGLE_CFLOAT,
float,
liquid_float_complex,
liquid_float_complex)
LIQUID_ASGRAM_DEFINE_API(LIQUID_ASGRAM_MANGLE_FLOAT,
float,
liquid_float_complex,
float)
//
// spectral periodogram waterfall
//
#define LIQUID_SPWATERFALL_MANGLE_CFLOAT(name) LIQUID_CONCAT(spwaterfallcf,name)
#define LIQUID_SPWATERFALL_MANGLE_FLOAT(name) LIQUID_CONCAT(spwaterfallf, name)
// Macro : SPWATERFALL
// SPWATERFALL : name-mangling macro
// T : primitive data type
// TC : primitive data type (complex)
// TI : primitive data type (input)
#define LIQUID_SPWATERFALL_DEFINE_API(SPWATERFALL,T,TC,TI) \
\
/* Spectral periodogram waterfall object for computing time-varying */ \
/* power spectral density estimates */ \
typedef struct SPWATERFALL(_s) * SPWATERFALL(); \
\
/* Create spwaterfall object, fully defined */ \
/* _nfft : transform (FFT) size, _nfft >= 2 */ \
/* _wtype : window type, e.g. LIQUID_WINDOW_HAMMING */ \
/* _window_len : window length, 1 <= _window_len <= _nfft */ \
/* _delay : delay between transforms, _delay > 0 */ \
/* _time : number of aggregated transforms, _time > 0 */ \
SPWATERFALL() SPWATERFALL(_create)(unsigned int _nfft, \
int _wtype, \
unsigned int _window_len, \
unsigned int _delay, \
unsigned int _time); \
\
/* Create default spwatefall object (Kaiser-Bessel window) */ \
/* _nfft : transform size, _nfft >= 2 */ \
/* _time : delay between transforms, _delay > 0 */ \
SPWATERFALL() SPWATERFALL(_create_default)(unsigned int _nfft, \
unsigned int _time); \
\
/* Destroy spwaterfall object, freeing all internally-allocated memory */ \
void SPWATERFALL(_destroy)(SPWATERFALL() _q); \
\
/* Clears the internal state of the object, but not the internal buffer */ \
void SPWATERFALL(_clear)(SPWATERFALL() _q); \
\
/* Reset the object to its original state completely. This effectively */ \
/* executes the clear() method and then resets the internal buffer */ \
void SPWATERFALL(_reset)(SPWATERFALL() _q); \
\
/* Print internal state of the object to stdout */ \
void SPWATERFALL(_print)(SPWATERFALL() _q); \
\
/* Get number of samples processed since object was created */ \
uint64_t SPWATERFALL(_get_num_samples_total)(SPWATERFALL() _q); \
\
/* Get FFT size (columns in PSD output) */ \
unsigned int SPWATERFALL(_get_num_freq)(SPWATERFALL() _q); \
\
/* Get number of accumulated FFTs (rows in PSD output) */ \
unsigned int SPWATERFALL(_get_num_time)(SPWATERFALL() _q); \
\
/* Get power spectral density (PSD), size: nfft x time */ \
const T * SPWATERFALL(_get_psd)(SPWATERFALL() _q); \
\
/* Set the center frequency of the received signal. */ \
/* This is for display purposes only when generating the output image. */ \
/* _q : spectral periodogram waterfall object */ \
/* _freq : center frequency [Hz] */ \
int SPWATERFALL(_set_freq)(SPWATERFALL() _q, \
float _freq); \
\
/* Set the sample rate (frequency) of the received signal. */ \
/* This is for display purposes only when generating the output image. */ \
/* _q : spectral periodogram waterfall object */ \
/* _rate : sample rate [Hz] */ \
int SPWATERFALL(_set_rate)(SPWATERFALL() _q, \
float _rate); \
\
/* Set the canvas size. */ \
/* This is for display purposes only when generating the output image. */ \
/* _q : spectral periodogram waterfall object */ \
/* _width : image width [pixels] */ \
/* _height : image height [pixels] */ \
int SPWATERFALL(_set_dims)(SPWATERFALL() _q, \
unsigned int _width, \
unsigned int _height); \
\
/* Set commands for executing directly before 'plot' statement. */ \
/* _q : spectral periodogram waterfall object */ \
/* _commands : gnuplot commands separated by semicolons */ \
int SPWATERFALL(_set_commands)(SPWATERFALL() _q, \
const char * _commands); \
\
/* Push a single sample into the object, executing internal transform */ \
/* as necessary. */ \
/* _q : spwaterfall object */ \
/* _x : input sample */ \
void SPWATERFALL(_push)(SPWATERFALL() _q, \
TI _x); \
\
/* Write a block of samples to the object, executing internal */ \
/* transform as necessary. */ \
/* _q : spwaterfall object */ \
/* _x : input buffer, [size: _n x 1] */ \
/* _n : input buffer length */ \
void SPWATERFALL(_write)(SPWATERFALL() _q, \
TI * _x, \
unsigned int _n); \
\
/* Export set of files for plotting */ \
/* _q : spwaterfall object */ \
/* _base : base filename (will export .gnu, .bin, and .png files) */ \
int SPWATERFALL(_export)(SPWATERFALL() _q, \
const char * _base); \
LIQUID_SPWATERFALL_DEFINE_API(LIQUID_SPWATERFALL_MANGLE_CFLOAT,
float,
liquid_float_complex,
liquid_float_complex)
LIQUID_SPWATERFALL_DEFINE_API(LIQUID_SPWATERFALL_MANGLE_FLOAT,
float,
liquid_float_complex,
float)
//
// MODULE : filter
//
//
// firdes: finite impulse response filter design
//
// prototypes
typedef enum {
LIQUID_FIRFILT_UNKNOWN=0, // unknown filter type
// Nyquist filter prototypes
LIQUID_FIRFILT_KAISER, // Nyquist Kaiser filter
LIQUID_FIRFILT_PM, // Parks-McClellan filter
LIQUID_FIRFILT_RCOS, // raised-cosine filter
LIQUID_FIRFILT_FEXP, // flipped exponential
LIQUID_FIRFILT_FSECH, // flipped hyperbolic secant
LIQUID_FIRFILT_FARCSECH, // flipped arc-hyperbolic secant
// root-Nyquist filter prototypes
LIQUID_FIRFILT_ARKAISER, // root-Nyquist Kaiser (approximate optimum)
LIQUID_FIRFILT_RKAISER, // root-Nyquist Kaiser (true optimum)
LIQUID_FIRFILT_RRC, // root raised-cosine
LIQUID_FIRFILT_hM3, // harris-Moerder-3 filter
LIQUID_FIRFILT_GMSKTX, // GMSK transmit filter
LIQUID_FIRFILT_GMSKRX, // GMSK receive filter
LIQUID_FIRFILT_RFEXP, // flipped exponential
LIQUID_FIRFILT_RFSECH, // flipped hyperbolic secant
LIQUID_FIRFILT_RFARCSECH, // flipped arc-hyperbolic secant
} liquid_firfilt_type;
// Design (root-)Nyquist filter from prototype
// _type : filter type (e.g. LIQUID_FIRFILT_RRC)
// _k : samples/symbol, _k > 1
// _m : symbol delay, _m > 0
// _beta : excess bandwidth factor, _beta in [0,1)
// _dt : fractional sample delay, _dt in [-1,1]
// _h : output coefficient buffer (length: 2*_k*_m+1)
void liquid_firdes_prototype(liquid_firfilt_type _type,
unsigned int _k,
unsigned int _m,
float _beta,
float _dt,
float * _h);
// returns filter type based on input string
int liquid_getopt_str2firfilt(const char * _str);
// estimate required filter length given
// _df : transition bandwidth (0 < _b < 0.5)
// _As : stop-band attenuation [dB], _As > 0
unsigned int estimate_req_filter_len(float _df,
float _As);
// estimate filter stop-band attenuation given
// _df : transition bandwidth (0 < _b < 0.5)
// _N : filter length
float estimate_req_filter_As(float _df,
unsigned int _N);
// estimate filter transition bandwidth given
// _As : stop-band attenuation [dB], _As > 0
// _N : filter length
float estimate_req_filter_df(float _As,
unsigned int _N);
// returns the Kaiser window beta factor give the filter's target
// stop-band attenuation (As) [Vaidyanathan:1993]
// _As : target filter's stop-band attenuation [dB], _As > 0
float kaiser_beta_As(float _As);
// Design FIR filter using Parks-McClellan algorithm
// band type specifier
typedef enum {
LIQUID_FIRDESPM_BANDPASS=0, // regular band-pass filter
LIQUID_FIRDESPM_DIFFERENTIATOR, // differentiating filter
LIQUID_FIRDESPM_HILBERT // Hilbert transform
} liquid_firdespm_btype;
// weighting type specifier
typedef enum {
LIQUID_FIRDESPM_FLATWEIGHT=0, // flat weighting
LIQUID_FIRDESPM_EXPWEIGHT, // exponential weighting
LIQUID_FIRDESPM_LINWEIGHT, // linear weighting
} liquid_firdespm_wtype;
// run filter design (full life cycle of object)
// _h_len : length of filter (number of taps)
// _num_bands : number of frequency bands
// _bands : band edges, f in [0,0.5], [size: _num_bands x 2]
// _des : desired response [size: _num_bands x 1]
// _weights : response weighting [size: _num_bands x 1]
// _wtype : weight types (e.g. LIQUID_FIRDESPM_FLATWEIGHT) [size: _num_bands x 1]
// _btype : band type (e.g. LIQUID_FIRDESPM_BANDPASS)
// _h : output coefficients array [size: _h_len x 1]
void firdespm_run(unsigned int _h_len,
unsigned int _num_bands,
float * _bands,
float * _des,
float * _weights,
liquid_firdespm_wtype * _wtype,
liquid_firdespm_btype _btype,
float * _h);
// run filter design for basic low-pass filter
// _n : filter length, _n > 0
// _fc : cutoff frequency, 0 < _fc < 0.5
// _As : stop-band attenuation [dB], _As > 0
// _mu : fractional sample offset, -0.5 < _mu < 0.5 [ignored]
// _h : output coefficient buffer, [size: _n x 1]
void firdespm_lowpass(unsigned int _n,
float _fc,
float _As,
float _mu,
float * _h);
// firdespm response callback function
// _frequency : normalized frequency
// _userdata : pointer to userdata
// _desired : (return) desired response
// _weight : (return) weight
typedef int (*firdespm_callback)(double _frequency,
void * _userdata,
double * _desired,
double * _weight);
// structured object
typedef struct firdespm_s * firdespm;
// create firdespm object
// _h_len : length of filter (number of taps)
// _num_bands : number of frequency bands
// _bands : band edges, f in [0,0.5], [size: _num_bands x 2]
// _des : desired response [size: _num_bands x 1]
// _weights : response weighting [size: _num_bands x 1]
// _wtype : weight types (e.g. LIQUID_FIRDESPM_FLATWEIGHT) [size: _num_bands x 1]
// _btype : band type (e.g. LIQUID_FIRDESPM_BANDPASS)
firdespm firdespm_create(unsigned int _h_len,
unsigned int _num_bands,
float * _bands,
float * _des,
float * _weights,
liquid_firdespm_wtype * _wtype,
liquid_firdespm_btype _btype);
// create firdespm object with user-defined callback
// _h_len : length of filter (number of taps)
// _num_bands : number of frequency bands
// _bands : band edges, f in [0,0.5], [size: _num_bands x 2]
// _btype : band type (e.g. LIQUID_FIRDESPM_BANDPASS)
// _callback : user-defined callback for specifying desired response & weights
// _userdata : user-defined data structure for callback function
firdespm firdespm_create_callback(unsigned int _h_len,
unsigned int _num_bands,
float * _bands,
liquid_firdespm_btype _btype,
firdespm_callback _callback,
void * _userdata);
// destroy firdespm object
void firdespm_destroy(firdespm _q);
// print firdespm object internals
void firdespm_print(firdespm _q);
// execute filter design, storing result in _h
void firdespm_execute(firdespm _q, float * _h);
// Design FIR using kaiser window
// _n : filter length, _n > 0
// _fc : cutoff frequency, 0 < _fc < 0.5
// _As : stop-band attenuation [dB], _As > 0
// _mu : fractional sample offset, -0.5 < _mu < 0.5
// _h : output coefficient buffer, [size: _n x 1]
void liquid_firdes_kaiser(unsigned int _n,
float _fc,
float _As,
float _mu,
float *_h);
// Design finite impulse response notch filter
// _m : filter semi-length, m in [1,1000]
// _f0 : filter notch frequency (normalized), -0.5 <= _fc <= 0.5
// _As : stop-band attenuation [dB], _As > 0
// _h : output coefficient buffer, [size: 2*_m+1 x 1]
void liquid_firdes_notch(unsigned int _m,
float _f0,
float _As,
float * _h);
// Design FIR doppler filter
// _n : filter length
// _fd : normalized doppler frequency (0 < _fd < 0.5)
// _K : Rice fading factor (K >= 0)
// _theta : LoS component angle of arrival
// _h : output coefficient buffer
void liquid_firdes_doppler(unsigned int _n,
float _fd,
float _K,
float _theta,
float * _h);
// Design Nyquist raised-cosine filter
// _k : samples/symbol
// _m : symbol delay
// _beta : rolloff factor (0 < beta <= 1)
// _dt : fractional sample delay
// _h : output coefficient buffer (length: 2*k*m+1)
void liquid_firdes_rcos(unsigned int _k,
unsigned int _m,
float _beta,
float _dt,
float * _h);
// Design root-Nyquist raised-cosine filter
void liquid_firdes_rrcos(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Design root-Nyquist Kaiser filter
void liquid_firdes_rkaiser(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Design (approximate) root-Nyquist Kaiser filter
void liquid_firdes_arkaiser(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Design root-Nyquist harris-Moerder filter
void liquid_firdes_hM3(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Design GMSK transmit and receive filters
void liquid_firdes_gmsktx(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
void liquid_firdes_gmskrx(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Design flipped exponential Nyquist/root-Nyquist filters
void liquid_firdes_fexp( unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
void liquid_firdes_rfexp(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Design flipped hyperbolic secand Nyquist/root-Nyquist filters
void liquid_firdes_fsech( unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
void liquid_firdes_rfsech(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Design flipped arc-hyperbolic secand Nyquist/root-Nyquist filters
void liquid_firdes_farcsech( unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
void liquid_firdes_rfarcsech(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h);
// Compute group delay for an FIR filter
// _h : filter coefficients array
// _n : filter length
// _fc : frequency at which delay is evaluated (-0.5 < _fc < 0.5)
float fir_group_delay(float * _h,
unsigned int _n,
float _fc);
// Compute group delay for an IIR filter
// _b : filter numerator coefficients
// _nb : filter numerator length
// _a : filter denominator coefficients
// _na : filter denominator length
// _fc : frequency at which delay is evaluated (-0.5 < _fc < 0.5)
float iir_group_delay(float * _b,
unsigned int _nb,
float * _a,
unsigned int _na,
float _fc);
// liquid_filter_autocorr()
//
// Compute auto-correlation of filter at a specific lag.
//
// _h : filter coefficients [size: _h_len x 1]
// _h_len : filter length
// _lag : auto-correlation lag (samples)
float liquid_filter_autocorr(float * _h,
unsigned int _h_len,
int _lag);
// liquid_filter_crosscorr()
//
// Compute cross-correlation of two filters at a specific lag.
//
// _h : filter coefficients [size: _h_len]
// _h_len : filter length
// _g : filter coefficients [size: _g_len]
// _g_len : filter length
// _lag : cross-correlation lag (samples)
float liquid_filter_crosscorr(float * _h,
unsigned int _h_len,
float * _g,
unsigned int _g_len,
int _lag);
// liquid_filter_isi()
//
// Compute inter-symbol interference (ISI)--both RMS and
// maximum--for the filter _h.
//
// _h : filter coefficients [size: 2*_k*_m+1 x 1]
// _k : filter over-sampling rate (samples/symbol)
// _m : filter delay (symbols)
// _rms : output root mean-squared ISI
// _max : maximum ISI
void liquid_filter_isi(float * _h,
unsigned int _k,
unsigned int _m,
float * _rms,
float * _max);
// Compute relative out-of-band energy
//
// _h : filter coefficients [size: _h_len x 1]
// _h_len : filter length
// _fc : analysis cut-off frequency
// _nfft : fft size
float liquid_filter_energy(float * _h,
unsigned int _h_len,
float _fc,
unsigned int _nfft);
//
// IIR filter design
//
// IIR filter design filter type
typedef enum {
LIQUID_IIRDES_BUTTER=0,
LIQUID_IIRDES_CHEBY1,
LIQUID_IIRDES_CHEBY2,
LIQUID_IIRDES_ELLIP,
LIQUID_IIRDES_BESSEL
} liquid_iirdes_filtertype;
// IIR filter design band type
typedef enum {
LIQUID_IIRDES_LOWPASS=0,
LIQUID_IIRDES_HIGHPASS,
LIQUID_IIRDES_BANDPASS,
LIQUID_IIRDES_BANDSTOP
} liquid_iirdes_bandtype;
// IIR filter design coefficients format
typedef enum {
LIQUID_IIRDES_SOS=0,
LIQUID_IIRDES_TF
} liquid_iirdes_format;
// IIR filter design template
// _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER)
// _btype : band type (e.g. LIQUID_IIRDES_BANDPASS)
// _format : coefficients format (e.g. LIQUID_IIRDES_SOS)
// _n : filter order
// _fc : low-pass prototype cut-off frequency
// _f0 : center frequency (band-pass, band-stop)
// _Ap : pass-band ripple in dB
// _As : stop-band ripple in dB
// _B : numerator
// _A : denominator
void liquid_iirdes(liquid_iirdes_filtertype _ftype,
liquid_iirdes_bandtype _btype,
liquid_iirdes_format _format,
unsigned int _n,
float _fc,
float _f0,
float _Ap,
float _As,
float * _B,
float * _A);
// compute analog zeros, poles, gain for specific filter types
void butter_azpkf(unsigned int _n,
liquid_float_complex * _za,
liquid_float_complex * _pa,
liquid_float_complex * _ka);
void cheby1_azpkf(unsigned int _n,
float _ep,
liquid_float_complex * _z,
liquid_float_complex * _p,
liquid_float_complex * _k);
void cheby2_azpkf(unsigned int _n,
float _es,
liquid_float_complex * _z,
liquid_float_complex * _p,
liquid_float_complex * _k);
void ellip_azpkf(unsigned int _n,
float _ep,
float _es,
liquid_float_complex * _z,
liquid_float_complex * _p,
liquid_float_complex * _k);
void bessel_azpkf(unsigned int _n,
liquid_float_complex * _z,
liquid_float_complex * _p,
liquid_float_complex * _k);
// compute frequency pre-warping factor
float iirdes_freqprewarp(liquid_iirdes_bandtype _btype,
float _fc,
float _f0);
// convert analog z/p/k form to discrete z/p/k form (bilinear z-transform)
// _za : analog zeros [length: _nza]
// _nza : number of analog zeros
// _pa : analog poles [length: _npa]
// _npa : number of analog poles
// _m : frequency pre-warping factor
// _zd : output digital zeros [length: _npa]
// _pd : output digital poles [length: _npa]
// _kd : output digital gain (should actually be real-valued)
void bilinear_zpkf(liquid_float_complex * _za,
unsigned int _nza,
liquid_float_complex * _pa,
unsigned int _npa,
liquid_float_complex _ka,
float _m,
liquid_float_complex * _zd,
liquid_float_complex * _pd,
liquid_float_complex * _kd);
// digital z/p/k low-pass to high-pass
// _zd : digital zeros (low-pass prototype), [length: _n]
// _pd : digital poles (low-pass prototype), [length: _n]
// _n : low-pass filter order
// _zdt : output digital zeros transformed [length: _n]
// _pdt : output digital poles transformed [length: _n]
void iirdes_dzpk_lp2hp(liquid_float_complex * _zd,
liquid_float_complex * _pd,
unsigned int _n,
liquid_float_complex * _zdt,
liquid_float_complex * _pdt);
// digital z/p/k low-pass to band-pass
// _zd : digital zeros (low-pass prototype), [length: _n]
// _pd : digital poles (low-pass prototype), [length: _n]
// _n : low-pass filter order
// _f0 : center frequency
// _zdt : output digital zeros transformed [length: 2*_n]
// _pdt : output digital poles transformed [length: 2*_n]
void iirdes_dzpk_lp2bp(liquid_float_complex * _zd,
liquid_float_complex * _pd,
unsigned int _n,
float _f0,
liquid_float_complex * _zdt,
liquid_float_complex * _pdt);
// convert discrete z/p/k form to transfer function
// _zd : digital zeros [length: _n]
// _pd : digital poles [length: _n]
// _n : filter order
// _kd : digital gain
// _b : output numerator [length: _n+1]
// _a : output denominator [length: _n+1]
void iirdes_dzpk2tff(liquid_float_complex * _zd,
liquid_float_complex * _pd,
unsigned int _n,
liquid_float_complex _kd,
float * _b,
float * _a);
// convert discrete z/p/k form to second-order sections
// _zd : digital zeros [length: _n]
// _pd : digital poles [length: _n]
// _n : filter order
// _kd : digital gain
// _B : output numerator [size: 3 x L+r]
// _A : output denominator [size: 3 x L+r]
// where r = _n%2, L = (_n-r)/2
void iirdes_dzpk2sosf(liquid_float_complex * _zd,
liquid_float_complex * _pd,
unsigned int _n,
liquid_float_complex _kd,
float * _B,
float * _A);
// additional IIR filter design templates
// design 2nd-order IIR filter (active lag)
// 1 + t2 * s
// F(s) = ------------
// 1 + t1 * s
//
// _w : filter bandwidth
// _zeta : damping factor (1/sqrt(2) suggested)
// _K : loop gain (1000 suggested)
// _b : output feed-forward coefficients [size: 3 x 1]
// _a : output feed-back coefficients [size: 3 x 1]
void iirdes_pll_active_lag(float _w,
float _zeta,
float _K,
float * _b,
float * _a);
// design 2nd-order IIR filter (active PI)
// 1 + t2 * s
// F(s) = ------------
// t1 * s
//
// _w : filter bandwidth
// _zeta : damping factor (1/sqrt(2) suggested)
// _K : loop gain (1000 suggested)
// _b : output feed-forward coefficients [size: 3 x 1]
// _a : output feed-back coefficients [size: 3 x 1]
void iirdes_pll_active_PI(float _w,
float _zeta,
float _K,
float * _b,
float * _a);
// checks stability of iir filter
// _b : feed-forward coefficients [size: _n x 1]
// _a : feed-back coefficients [size: _n x 1]
// _n : number of coefficients
int iirdes_isstable(float * _b,
float * _a,
unsigned int _n);
//
// linear prediction
//
// compute the linear prediction coefficients for an input signal _x
// _x : input signal [size: _n x 1]
// _n : input signal length
// _p : prediction filter order
// _a : prediction filter [size: _p+1 x 1]
// _e : prediction error variance [size: _p+1 x 1]
void liquid_lpc(float * _x,
unsigned int _n,
unsigned int _p,
float * _a,
float * _g);
// solve the Yule-Walker equations using Levinson-Durbin recursion
// for _symmetric_ autocorrelation
// _r : autocorrelation array [size: _p+1 x 1]
// _p : filter order
// _a : output coefficients [size: _p+1 x 1]
// _e : error variance [size: _p+1 x 1]
//
// NOTES:
// By definition _a[0] = 1.0
void liquid_levinson(float * _r,
unsigned int _p,
float * _a,
float * _e);
//
// auto-correlator (delay cross-correlation)
//
#define LIQUID_AUTOCORR_MANGLE_CCCF(name) LIQUID_CONCAT(autocorr_cccf,name)
#define LIQUID_AUTOCORR_MANGLE_RRRF(name) LIQUID_CONCAT(autocorr_rrrf,name)
// Macro:
// AUTOCORR : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_AUTOCORR_DEFINE_API(AUTOCORR,TO,TC,TI) \
\
/* Computes auto-correlation with a fixed lag on input signals */ \
typedef struct AUTOCORR(_s) * AUTOCORR(); \
\
/* Create auto-correlator object with a particular window length and */ \
/* delay */ \
/* _window_size : size of the correlator window */ \
/* _delay : correlator delay [samples] */ \
AUTOCORR() AUTOCORR(_create)(unsigned int _window_size, \
unsigned int _delay); \
\
/* Destroy auto-correlator object, freeing internal memory */ \
void AUTOCORR(_destroy)(AUTOCORR() _q); \
\
/* Reset auto-correlator object's internals */ \
void AUTOCORR(_reset)(AUTOCORR() _q); \
\
/* Print auto-correlator parameters to stdout */ \
void AUTOCORR(_print)(AUTOCORR() _q); \
\
/* Push sample into auto-correlator object */ \
/* _q : auto-correlator object */ \
/* _x : single input sample */ \
void AUTOCORR(_push)(AUTOCORR() _q, \
TI _x); \
\
/* Write block of samples to auto-correlator object */ \
/* _q : auto-correlation object */ \
/* _x : input array [size: _n x 1] */ \
/* _n : number of input samples */ \
void AUTOCORR(_write)(AUTOCORR() _q, \
TI * _x, \
unsigned int _n); \
\
/* Compute single auto-correlation output */ \
/* _q : auto-correlator object */ \
/* _rxx : auto-correlated output */ \
void AUTOCORR(_execute)(AUTOCORR() _q, \
TO * _rxx); \
\
/* Compute auto-correlation on block of samples; the input and output */ \
/* arrays may have the same pointer */ \
/* _q : auto-correlation object */ \
/* _x : input array [size: _n x 1] */ \
/* _n : number of input, output samples */ \
/* _rxx : input array [size: _n x 1] */ \
void AUTOCORR(_execute_block)(AUTOCORR() _q, \
TI * _x, \
unsigned int _n, \
TO * _rxx); \
\
/* return sum of squares of buffered samples */ \
float AUTOCORR(_get_energy)(AUTOCORR() _q); \
LIQUID_AUTOCORR_DEFINE_API(LIQUID_AUTOCORR_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
LIQUID_AUTOCORR_DEFINE_API(LIQUID_AUTOCORR_MANGLE_RRRF,
float,
float,
float)
//
// Finite impulse response filter
//
#define LIQUID_FIRFILT_MANGLE_RRRF(name) LIQUID_CONCAT(firfilt_rrrf,name)
#define LIQUID_FIRFILT_MANGLE_CRCF(name) LIQUID_CONCAT(firfilt_crcf,name)
#define LIQUID_FIRFILT_MANGLE_CCCF(name) LIQUID_CONCAT(firfilt_cccf,name)
// Macro:
// FIRFILT : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_FIRFILT_DEFINE_API(FIRFILT,TO,TC,TI) \
\
/* Finite impulse response (FIR) filter */ \
typedef struct FIRFILT(_s) * FIRFILT(); \
\
/* Create a finite impulse response filter (firfilt) object by directly */ \
/* specifying the filter coefficients in an array */ \
/* _h : filter coefficients [size: _n x 1] */ \
/* _n : number of filter coefficients, _n > 0 */ \
FIRFILT() FIRFILT(_create)(TC * _h, \
unsigned int _n); \
\
/* Create object using Kaiser-Bessel windowed sinc method */ \
/* _n : filter length, _n > 0 */ \
/* _fc : filter normalized cut-off frequency, 0 < _fc < 0.5 */ \
/* _As : filter stop-band attenuation [dB], _As > 0 */ \
/* _mu : fractional sample offset, -0.5 < _mu < 0.5 */ \
FIRFILT() FIRFILT(_create_kaiser)(unsigned int _n, \
float _fc, \
float _As, \
float _mu); \
\
/* Create object from square-root Nyquist prototype. */ \
/* The filter length will be \(2 k m + 1 \) samples long with a delay */ \
/* of \( k m + 1 \) samples. */ \
/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \
/* _k : nominal samples per symbol, _k > 1 */ \
/* _m : filter delay [symbols], _m > 0 */ \
/* _beta : rolloff factor, 0 < beta <= 1 */ \
/* _mu : fractional sample offset [samples], -0.5 < _mu < 0.5 */ \
FIRFILT() FIRFILT(_create_rnyquist)(int _type, \
unsigned int _k, \
unsigned int _m, \
float _beta, \
float _mu); \
\
/* Create object from Parks-McClellan algorithm prototype */ \
/* _h_len : filter length, _h_len > 0 */ \
/* _fc : cutoff frequency, 0 < _fc < 0.5 */ \
/* _As : stop-band attenuation [dB], _As > 0 */ \
FIRFILT() FIRFILT(_create_firdespm)(unsigned int _h_len, \
float _fc, \
float _As); \
\
/* Create rectangular filter prototype; that is */ \
/* \( \vec{h} = \{ 1, 1, 1, \ldots 1 \} \) */ \
/* _n : length of filter [samples], 0 < _n <= 1024 */ \
FIRFILT() FIRFILT(_create_rect)(unsigned int _n); \
\
/* Create DC blocking filter from prototype */ \
/* _m : prototype filter semi-length such that filter length is 2*m+1 */ \
/* _As : prototype filter stop-band attenuation [dB], _As > 0 */ \
FIRFILT() FIRFILT(_create_dc_blocker)(unsigned int _m, \
float _As); \
\
/* Create notch filter from prototype */ \
/* _m : prototype filter semi-length such that filter length is 2*m+1 */ \
/* _As : prototype filter stop-band attenuation [dB], _As > 0 */ \
/* _f0 : center frequency for notch, _fc in [-0.5, 0.5] */ \
FIRFILT() FIRFILT(_create_notch)(unsigned int _m, \
float _As, \
float _f0); \
\
/* Re-create filter object of potentially a different length with */ \
/* different coefficients. If the length of the filter does not change, */ \
/* not memory reallocation is invoked. */ \
/* _q : original filter object */ \
/* _h : pointer to filter coefficients, [size: _n x 1] */ \
/* _n : filter length, _n > 0 */ \
FIRFILT() FIRFILT(_recreate)(FIRFILT() _q, \
TC * _h, \
unsigned int _n); \
\
/* Destroy filter object and free all internal memory */ \
void FIRFILT(_destroy)(FIRFILT() _q); \
\
/* Reset filter object's internal buffer */ \
void FIRFILT(_reset)(FIRFILT() _q); \
\
/* Print filter object information to stdout */ \
void FIRFILT(_print)(FIRFILT() _q); \
\
/* Set output scaling for filter */ \
/* _q : filter object */ \
/* _scale : scaling factor to apply to each output sample */ \
void FIRFILT(_set_scale)(FIRFILT() _q, \
TC _scale); \
\
/* Get output scaling for filter */ \
/* _q : filter object */ \
/* _scale : scaling factor applied to each output sample */ \
void FIRFILT(_get_scale)(FIRFILT() _q, \
TC * _scale); \
\
/* Push sample into filter object's internal buffer */ \
/* _q : filter object */ \
/* _x : single input sample */ \
void FIRFILT(_push)(FIRFILT() _q, \
TI _x); \
\
/* Write block of samples into filter object's internal buffer */ \
/* _q : filter object */ \
/* _x : buffer of input samples, [size: _n x 1] */ \
/* _n : number of input samples */ \
void FIRFILT(_write)(FIRFILT() _q, \
TI * _x, \
unsigned int _n); \
\
/* Execute vector dot product on the filter's internal buffer and */ \
/* coefficients */ \
/* _q : filter object */ \
/* _y : pointer to single output sample */ \
void FIRFILT(_execute)(FIRFILT() _q, \
TO * _y); \
\
/* Execute the filter on a block of input samples; in-place operation */ \
/* is permitted (_x and _y may point to the same place in memory) */ \
/* _q : filter object */ \
/* _x : pointer to input array, [size: _n x 1] */ \
/* _n : number of input, output samples */ \
/* _y : pointer to output array, [size: _n x 1] */ \
void FIRFILT(_execute_block)(FIRFILT() _q, \
TI * _x, \
unsigned int _n, \
TO * _y); \
\
/* Get length of filter object (number of internal coefficients) */ \
unsigned int FIRFILT(_get_length)(FIRFILT() _q); \
\
/* Compute complex frequency response of filter object */ \
/* _q : filter object */ \
/* _fc : normalized frequency for evaluation */ \
/* _H : pointer to output complex frequency response */ \
void FIRFILT(_freqresponse)(FIRFILT() _q, \
float _fc, \
liquid_float_complex * _H); \
\
/* Compute and return group delay of filter object */ \
/* _q : filter object */ \
/* _fc : frequency to evaluate */ \
float FIRFILT(_groupdelay)(FIRFILT() _q, \
float _fc); \
LIQUID_FIRFILT_DEFINE_API(LIQUID_FIRFILT_MANGLE_RRRF,
float,
float,
float)
LIQUID_FIRFILT_DEFINE_API(LIQUID_FIRFILT_MANGLE_CRCF,
liquid_float_complex,
float,
liquid_float_complex)
LIQUID_FIRFILT_DEFINE_API(LIQUID_FIRFILT_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
//
// FIR Hilbert transform
// 2:1 real-to-complex decimator
// 1:2 complex-to-real interpolator
//
#define LIQUID_FIRHILB_MANGLE_FLOAT(name) LIQUID_CONCAT(firhilbf, name)
//#define LIQUID_FIRHILB_MANGLE_DOUBLE(name) LIQUID_CONCAT(firhilb, name)
// NOTES:
// Although firhilb is a placeholder for both decimation and
// interpolation, separate objects should be used for each task.
#define LIQUID_FIRHILB_DEFINE_API(FIRHILB,T,TC) \
\
/* Finite impulse response (FIR) Hilbert transform */ \
typedef struct FIRHILB(_s) * FIRHILB(); \
\
/* 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. */ \
/* _m : filter semi-length, delay is \( 2 m + 1 \) */ \
/* _As : filter stop-band attenuation [dB] */ \
FIRHILB() FIRHILB(_create)(unsigned int _m, \
float _As); \
\
/* Destroy finite impulse response Hilbert transform, freeing all */ \
/* internally-allocted memory and objects. */ \
void FIRHILB(_destroy)(FIRHILB() _q); \
\
/* Print firhilb object internals to stdout */ \
void FIRHILB(_print)(FIRHILB() _q); \
\
/* Reset firhilb object internal state */ \
void FIRHILB(_reset)(FIRHILB() _q); \
\
/* Execute Hilbert transform (real to complex) */ \
/* _q : Hilbert transform object */ \
/* _x : real-valued input sample */ \
/* _y : complex-valued output sample */ \
void FIRHILB(_r2c_execute)(FIRHILB() _q, \
T _x, \
TC * _y); \
\
/* Execute Hilbert transform (complex to real) */ \
/* _q : Hilbert transform object */ \
/* _x : complex-valued input sample */ \
/* _y0 : real-valued output sample, lower side-band retained */ \
/* _y1 : real-valued output sample, upper side-band retained */ \
void FIRHILB(_c2r_execute)(FIRHILB() _q, \
TC _x, \
T * _y0, \
T * _y1); \
\
/* Execute Hilbert transform decimator (real to complex) */ \
/* _q : Hilbert transform object */ \
/* _x : real-valued input array, [size: 2 x 1] */ \
/* _y : complex-valued output sample */ \
void FIRHILB(_decim_execute)(FIRHILB() _q, \
T * _x, \
TC * _y); \
\
/* Execute Hilbert transform decimator (real to complex) on a block of */ \
/* samples */ \
/* _q : Hilbert transform object */ \
/* _x : real-valued input array, [size: 2*_n x 1] */ \
/* _n : number of output samples */ \
/* _y : complex-valued output array, [size: _n x 1] */ \
void FIRHILB(_decim_execute_block)(FIRHILB() _q, \
T * _x, \
unsigned int _n, \
TC * _y); \
\
/* Execute Hilbert transform interpolator (real to complex) */ \
/* _q : Hilbert transform object */ \
/* _x : complex-valued input sample */ \
/* _y : real-valued output array, [size: 2 x 1] */ \
void FIRHILB(_interp_execute)(FIRHILB() _q, \
TC _x, \
T * _y); \
\
/* Execute Hilbert transform interpolator (complex to real) on a block */ \
/* of samples */ \
/* _q : Hilbert transform object */ \
/* _x : complex-valued input array, [size: _n x 1] */ \
/* _n : number of *input* samples */ \
/* _y : real-valued output array, [size: 2*_n x 1] */ \
void FIRHILB(_interp_execute_block)(FIRHILB() _q, \
TC * _x, \
unsigned int _n, \
T * _y); \
LIQUID_FIRHILB_DEFINE_API(LIQUID_FIRHILB_MANGLE_FLOAT, float, liquid_float_complex)
//LIQUID_FIRHILB_DEFINE_API(LIQUID_FIRHILB_MANGLE_DOUBLE, double, liquid_double_complex)
//
// Infinite impulse response (IIR) Hilbert transform
// 2:1 real-to-complex decimator
// 1:2 complex-to-real interpolator
//
#define LIQUID_IIRHILB_MANGLE_FLOAT(name) LIQUID_CONCAT(iirhilbf, name)
//#define LIQUID_IIRHILB_MANGLE_DOUBLE(name) LIQUID_CONCAT(iirhilb, name)
// NOTES:
// Although iirhilb is a placeholder for both decimation and
// interpolation, separate objects should be used for each task.
#define LIQUID_IIRHILB_DEFINE_API(IIRHILB,T,TC) \
\
/* Infinite impulse response (IIR) Hilbert transform */ \
typedef struct IIRHILB(_s) * IIRHILB(); \
\
/* Create a iirhilb object with a particular filter type, order, and */ \
/* desired pass- and stop-band attenuation. */ \
/* _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) */ \
/* _n : filter order, _n > 0 */ \
/* _Ap : pass-band ripple [dB], _Ap > 0 */ \
/* _As : stop-band ripple [dB], _Ap > 0 */ \
IIRHILB() IIRHILB(_create)(liquid_iirdes_filtertype _ftype, \
unsigned int _n, \
float _Ap, \
float _As); \
\
/* Create a default iirhilb object with a particular filter order. */ \
/* _n : filter order, _n > 0 */ \
IIRHILB() IIRHILB(_create_default)(unsigned int _n); \
\
/* Destroy finite impulse response Hilbert transform, freeing all */ \
/* internally-allocted memory and objects. */ \
void IIRHILB(_destroy)(IIRHILB() _q); \
\
/* Print iirhilb object internals to stdout */ \
void IIRHILB(_print)(IIRHILB() _q); \
\
/* Reset iirhilb object internal state */ \
void IIRHILB(_reset)(IIRHILB() _q); \
\
/* Execute Hilbert transform (real to complex) */ \
/* _q : Hilbert transform object */ \
/* _x : real-valued input sample */ \
/* _y : complex-valued output sample */ \
void IIRHILB(_r2c_execute)(IIRHILB() _q, \
T _x, \
TC * _y); \
\
/* Execute Hilbert transform (complex to real) */ \
/* _q : Hilbert transform object */ \
/* _x : complex-valued input sample */ \
/* _y : real-valued output sample */ \
void IIRHILB(_c2r_execute)(IIRHILB() _q, \
TC _x, \
T * _y); \
\
/* Execute Hilbert transform decimator (real to complex) */ \
/* _q : Hilbert transform object */ \
/* _x : real-valued input array, [size: 2 x 1] */ \
/* _y : complex-valued output sample */ \
void IIRHILB(_decim_execute)(IIRHILB() _q, \
T * _x, \
TC * _y); \
\
/* Execute Hilbert transform decimator (real to complex) on a block of */ \
/* samples */ \
/* _q : Hilbert transform object */ \
/* _x : real-valued input array, [size: 2*_n x 1] */ \
/* _n : number of output samples */ \
/* _y : complex-valued output array, [size: _n x 1] */ \
void IIRHILB(_decim_execute_block)(IIRHILB() _q, \
T * _x, \
unsigned int _n, \
TC * _y); \
\
/* Execute Hilbert transform interpolator (real to complex) */ \
/* _q : Hilbert transform object */ \
/* _x : complex-valued input sample */ \
/* _y : real-valued output array, [size: 2 x 1] */ \
void IIRHILB(_interp_execute)(IIRHILB() _q, \
TC _x, \
T * _y); \
\
/* Execute Hilbert transform interpolator (complex to real) on a block */ \
/* of samples */ \
/* _q : Hilbert transform object */ \
/* _x : complex-valued input array, [size: _n x 1] */ \
/* _n : number of *input* samples */ \
/* _y : real-valued output array, [size: 2*_n x 1] */ \
void IIRHILB(_interp_execute_block)(IIRHILB() _q, \
TC * _x, \
unsigned int _n, \
T * _y); \
LIQUID_IIRHILB_DEFINE_API(LIQUID_IIRHILB_MANGLE_FLOAT, float, liquid_float_complex)
//LIQUID_IIRHILB_DEFINE_API(LIQUID_IIRHILB_MANGLE_DOUBLE, double, liquid_double_complex)
//
// FFT-based finite impulse response filter
//
#define LIQUID_FFTFILT_MANGLE_RRRF(name) LIQUID_CONCAT(fftfilt_rrrf,name)
#define LIQUID_FFTFILT_MANGLE_CRCF(name) LIQUID_CONCAT(fftfilt_crcf,name)
#define LIQUID_FFTFILT_MANGLE_CCCF(name) LIQUID_CONCAT(fftfilt_cccf,name)
// Macro:
// FFTFILT : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_FFTFILT_DEFINE_API(FFTFILT,TO,TC,TI) \
\
/* Fast Fourier transform (FFT) finite impulse response filter */ \
typedef struct FFTFILT(_s) * FFTFILT(); \
\
/* Create FFT-based FIR filter using external coefficients */ \
/* _h : filter coefficients, [size: _h_len x 1] */ \
/* _h_len : filter length, _h_len > 0 */ \
/* _n : block size = nfft/2, _n >= _h_len-1 */ \
FFTFILT() FFTFILT(_create)(TC * _h, \
unsigned int _h_len, \
unsigned int _n); \
\
/* Destroy filter object and free all internal memory */ \
void FFTFILT(_destroy)(FFTFILT() _q); \
\
/* Reset filter object's internal buffer */ \
void FFTFILT(_reset)(FFTFILT() _q); \
\
/* Print filter object information to stdout */ \
void FFTFILT(_print)(FFTFILT() _q); \
\
/* Set output scaling for filter */ \
void FFTFILT(_set_scale)(FFTFILT() _q, \
TC _scale); \
\
/* Get output scaling for filter */ \
void FFTFILT(_get_scale)(FFTFILT() _q, \
TC * _scale); \
\
/* Execute the filter on internal buffer and coefficients given a block */ \
/* of input samples; in-place operation is permitted (_x and _y may */ \
/* point to the same place in memory) */ \
/* _q : filter object */ \
/* _x : pointer to input data array, [size: _n x 1] */ \
/* _y : pointer to output data array, [size: _n x 1] */ \
void FFTFILT(_execute)(FFTFILT() _q, \
TI * _x, \
TO * _y); \
\
/* Get length of filter object's internal coefficients */ \
unsigned int FFTFILT(_get_length)(FFTFILT() _q); \
LIQUID_FFTFILT_DEFINE_API(LIQUID_FFTFILT_MANGLE_RRRF,
float,
float,
float)
LIQUID_FFTFILT_DEFINE_API(LIQUID_FFTFILT_MANGLE_CRCF,
liquid_float_complex,
float,
liquid_float_complex)
LIQUID_FFTFILT_DEFINE_API(LIQUID_FFTFILT_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
//
// Infinite impulse response filter
//
#define LIQUID_IIRFILT_MANGLE_RRRF(name) LIQUID_CONCAT(iirfilt_rrrf,name)
#define LIQUID_IIRFILT_MANGLE_CRCF(name) LIQUID_CONCAT(iirfilt_crcf,name)
#define LIQUID_IIRFILT_MANGLE_CCCF(name) LIQUID_CONCAT(iirfilt_cccf,name)
// Macro:
// IIRFILT : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_IIRFILT_DEFINE_API(IIRFILT,TO,TC,TI) \
\
/* Infinite impulse response (IIR) filter */ \
typedef struct IIRFILT(_s) * IIRFILT(); \
\
/* Create infinite impulse response filter from external coefficients. */ \
/* Note that the number of feed-forward and feed-back coefficients do */ \
/* not need to be equal, but they do need to be non-zero. */ \
/* Furthermore, the first feed-back coefficient \(a_0\) cannot be */ \
/* equal to zero, otherwise the filter will be invalid as this value is */ \
/* factored out from all coefficients. */ \
/* For stability reasons the number of coefficients should reasonably */ \
/* not exceed about 8 for single-precision floating-point. */ \
/* _b : feed-forward coefficients (numerator), [size: _nb x 1] */ \
/* _nb : number of feed-forward coefficients, _nb > 0 */ \
/* _a : feed-back coefficients (denominator), [size: _na x 1] */ \
/* _na : number of feed-back coefficients, _na > 0 */ \
IIRFILT() IIRFILT(_create)(TC * _b, \
unsigned int _nb, \
TC * _a, \
unsigned int _na); \
\
/* Create IIR filter using 2nd-order secitons from external */ \
/* coefficients. */ \
/* _B : feed-forward coefficients [size: _nsos x 3] */ \
/* _A : feed-back coefficients [size: _nsos x 3] */ \
/* _nsos : number of second-order sections (sos), _nsos > 0 */ \
IIRFILT() IIRFILT(_create_sos)(TC * _B, \
TC * _A, \
unsigned int _nsos); \
\
/* Create IIR filter from design template */ \
/* _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) */ \
/* _btype : band type (e.g. LIQUID_IIRDES_BANDPASS) */ \
/* _format : coefficients format (e.g. LIQUID_IIRDES_SOS) */ \
/* _order : filter order, _order > 0 */ \
/* _fc : low-pass prototype cut-off frequency, 0 <= _fc <= 0.5 */ \
/* _f0 : center frequency (band-pass, band-stop), 0 <= _f0 <= 0.5 */ \
/* _Ap : pass-band ripple in dB, _Ap > 0 */ \
/* _As : stop-band ripple in dB, _As > 0 */ \
IIRFILT() IIRFILT(_create_prototype)( \
liquid_iirdes_filtertype _ftype, \
liquid_iirdes_bandtype _btype, \
liquid_iirdes_format _format, \
unsigned int _order, \
float _fc, \
float _f0, \
float _Ap, \
float _As); \
\
/* Create simplified low-pass Butterworth IIR filter */ \
/* _order : filter order, _order > 0 */ \
/* _fc : low-pass prototype cut-off frequency */ \
IIRFILT() IIRFILT(_create_lowpass)(unsigned int _order, \
float _fc); \
\
/* Create 8th-order integrator filter */ \
IIRFILT() IIRFILT(_create_integrator)(void); \
\
/* Create 8th-order differentiator filter */ \
IIRFILT() IIRFILT(_create_differentiator)(void); \
\
/* Create simple first-order DC-blocking filter with transfer function */ \
/* \( H(z) = \frac{1 - z^{-1}}{1 - (1-\alpha)z^{-1}} \) */ \
/* _alpha : normalized filter bandwidth, _alpha > 0 */ \
IIRFILT() IIRFILT(_create_dc_blocker)(float _alpha); \
\
/* Create filter to operate as second-order integrating phase-locked */ \
/* loop (active lag design) */ \
/* _w : filter bandwidth, 0 < _w < 1 */ \
/* _zeta : damping factor, \( 1/\sqrt{2} \) suggested, 0 < _zeta < 1 */ \
/* _K : loop gain, 1000 suggested, _K > 0 */ \
IIRFILT() IIRFILT(_create_pll)(float _w, \
float _zeta, \
float _K); \
\
/* Destroy iirfilt object, freeing all internal memory */ \
void IIRFILT(_destroy)(IIRFILT() _q); \
\
/* Print iirfilt object properties to stdout */ \
void IIRFILT(_print)(IIRFILT() _q); \
\
/* Reset iirfilt object internals */ \
void IIRFILT(_reset)(IIRFILT() _q); \
\
/* Compute filter output given a signle input sample */ \
/* _q : iirfilt object */ \
/* _x : input sample */ \
/* _y : output sample pointer */ \
void IIRFILT(_execute)(IIRFILT() _q, \
TI _x, \
TO * _y); \
\
/* Execute the filter on a block of input samples; */ \
/* in-place operation is permitted (the input and output buffers may be */ \
/* the same) */ \
/* _q : filter object */ \
/* _x : pointer to input array, [size: _n x 1] */ \
/* _n : number of input, output samples, _n > 0 */ \
/* _y : pointer to output array, [size: _n x 1] */ \
void IIRFILT(_execute_block)(IIRFILT() _q, \
TI * _x, \
unsigned int _n, \
TO * _y); \
\
/* Return number of coefficients for iirfilt object (maximum between */ \
/* the feed-forward and feed-back coefficients). Note that the filter */ \
/* length = filter order + 1 */ \
unsigned int IIRFILT(_get_length)(IIRFILT() _q); \
\
/* Compute complex frequency response of filter object */ \
/* _q : filter object */ \
/* _fc : normalized frequency for evaluation */ \
/* _H : pointer to output complex frequency response */ \
void IIRFILT(_freqresponse)(IIRFILT() _q, \
float _fc, \
liquid_float_complex * _H); \
\
/* Compute and return group delay of filter object */ \
/* _q : filter object */ \
/* _fc : frequency to evaluate */ \
float IIRFILT(_groupdelay)(IIRFILT() _q, float _fc); \
LIQUID_IIRFILT_DEFINE_API(LIQUID_IIRFILT_MANGLE_RRRF,
float,
float,
float)
LIQUID_IIRFILT_DEFINE_API(LIQUID_IIRFILT_MANGLE_CRCF,
liquid_float_complex,
float,
liquid_float_complex)
LIQUID_IIRFILT_DEFINE_API(LIQUID_IIRFILT_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
//
// FIR Polyphase filter bank
//
#define LIQUID_FIRPFB_MANGLE_RRRF(name) LIQUID_CONCAT(firpfb_rrrf,name)
#define LIQUID_FIRPFB_MANGLE_CRCF(name) LIQUID_CONCAT(firpfb_crcf,name)
#define LIQUID_FIRPFB_MANGLE_CCCF(name) LIQUID_CONCAT(firpfb_cccf,name)
// Macro:
// FIRPFB : name-mangling macro
// TO : output data type
// TC : coefficients data type
// TI : input data type
#define LIQUID_FIRPFB_DEFINE_API(FIRPFB,TO,TC,TI) \
\
/* Finite impulse response (FIR) polyphase filter bank (PFB) */ \
typedef struct FIRPFB(_s) * FIRPFB(); \
\
/* Create firpfb object with _M sub-filter each of length _h_len/_M */ \
/* from an external array of coefficients */ \
/* _M : number of filters in the bank, _M > 1 */ \
/* _h : coefficients, [size: _h_len x 1] */ \
/* _h_len : filter length (multiple of _M), _h_len >= _M */ \
FIRPFB() FIRPFB(_create)(unsigned int _M, \
TC * _h, \
unsigned int _h_len); \
\
/* Create firpfb object using Kaiser-Bessel windowed sinc filter design */ \
/* method */ \
/* _M : number of filters in the bank, _M > 0 */ \
/* _m : filter semi-length [samples], _m > 0 */ \
/* _fc : filter normalized cut-off frequency, 0 < _fc < 0.5 */ \
/* _As : filter stop-band suppression [dB], _As > 0 */ \
FIRPFB() FIRPFB(_create_kaiser)(unsigned int _M, \
unsigned int _m, \
float _fc, \
float _As); \
\
/* Create firpfb from square-root Nyquist prototype */ \
/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \
/* _M : number of filters in the bank, _M > 0 */ \
/* _k : nominal samples/symbol, _k > 1 */ \
/* _m : filter delay [symbols], _m > 0 */ \
/* _beta : rolloff factor, 0 < _beta <= 1 */ \
FIRPFB() FIRPFB(_create_rnyquist)(int _type, \
unsigned int _M, \
unsigned int _k, \
unsigned int _m, \
float _beta); \
\
/* Create from square-root derivative Nyquist prototype */ \
/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \
/* _M : number of filters in the bank, _M > 0 */ \
/* _k : nominal samples/symbol, _k > 1 */ \
/* _m : filter delay [symbols], _m > 0 */ \
/* _beta : rolloff factor, 0 < _beta <= 1 */ \
FIRPFB() FIRPFB(_create_drnyquist)(int _type, \
unsigned int _M, \
unsigned int _k, \
unsigned int _m, \
float _beta); \
\
/* Re-create firpfb object of potentially a different length with */ \
/* different coefficients. If the length of the filter does not change, */ \
/* not memory reallocation is invoked. */ \
/* _q : original firpfb object */ \
/* _M : number of filters in the bank, _M > 1 */ \
/* _h : coefficients, [size: _h_len x 1] */ \
/* _h_len : filter length (multiple of _M), _h_len >= _M */ \
FIRPFB() FIRPFB(_recreate)(FIRPFB() _q, \
unsigned int _M, \
TC * _h, \
unsigned int _h_len); \
\
/* Destroy firpfb object, freeing all internal memory and destroying */ \
/* all internal objects */ \
void FIRPFB(_destroy)(FIRPFB() _q); \
\
/* Print firpfb object's parameters to stdout */ \
void FIRPFB(_print)(FIRPFB() _q); \
\
/* Set output scaling for filter */ \
/* _q : filter object */ \
/* _scale : scaling factor to apply to each output sample */ \
void FIRPFB(_set_scale)(FIRPFB() _q, \
TC _scale); \
\
/* Get output scaling for filter */ \
/* _q : filter object */ \
/* _scale : scaling factor applied to each output sample */ \
void FIRPFB(_get_scale)(FIRPFB() _q, \
TC * _scale); \
\
/* Reset firpfb object's internal buffer */ \
void FIRPFB(_reset)(FIRPFB() _q); \
\
/* Push sample into filter object's internal buffer */ \
/* _q : filter object */ \
/* _x : single input sample */ \
void FIRPFB(_push)(FIRPFB() _q, \
TI _x); \
\
/* Execute vector dot product on the filter's internal buffer and */ \
/* coefficients using the coefficients from sub-filter at index _i */ \
/* _q : firpfb object */ \
/* _i : index of filter to use */ \
/* _y : pointer to output sample */ \
void FIRPFB(_execute)(FIRPFB() _q, \
unsigned int _i, \
TO * _y); \
\
/* Execute the filter on a block of input samples, all using index _i. */ \
/* In-place operation is permitted (_x and _y may point to the same */ \
/* place in memory) */ \
/* _q : firpfb object */ \
/* _i : index of filter to use */ \
/* _x : pointer to input array [size: _n x 1] */ \
/* _n : number of input, output samples */ \
/* _y : pointer to output array [size: _n x 1] */ \
void FIRPFB(_execute_block)(FIRPFB() _q, \
unsigned int _i, \
TI * _x, \
unsigned int _n, \
TO * _y); \
LIQUID_FIRPFB_DEFINE_API(LIQUID_FIRPFB_MANGLE_RRRF,
float,
float,
float)
LIQUID_FIRPFB_DEFINE_API(LIQUID_FIRPFB_MANGLE_CRCF,
liquid_float_complex,
float,
liquid_float_complex)
LIQUID_FIRPFB_DEFINE_API(LIQUID_FIRPFB_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
//
// Interpolators
//
// firinterp : finite impulse response interpolator
#define LIQUID_FIRINTERP_MANGLE_RRRF(name) LIQUID_CONCAT(firinterp_rrrf,name)
#define LIQUID_FIRINTERP_MANGLE_CRCF(name) LIQUID_CONCAT(firinterp_crcf,name)
#define LIQUID_FIRINTERP_MANGLE_CCCF(name) LIQUID_CONCAT(firinterp_cccf,name)
#define LIQUID_FIRINTERP_DEFINE_API(FIRINTERP,TO,TC,TI) \
\
/* Finite impulse response (FIR) interpolator */ \
typedef struct FIRINTERP(_s) * FIRINTERP(); \
\
/* Create interpolator from external coefficients. Internally the */ \
/* interpolator creates a polyphase filter bank to efficiently realize */ \
/* resampling of the input signal. */ \
/* If the input filter length is not a multiple of the interpolation */ \
/* factor, the object internally pads the coefficients with zeros to */ \
/* compensate. */ \
/* _M : interpolation factor, _M >= 2 */ \
/* _h : filter coefficients, [size: _h_len x 1] */ \
/* _h_len : filter length, _h_len >= _M */ \
FIRINTERP() FIRINTERP(_create)(unsigned int _M, \
TC * _h, \
unsigned int _h_len); \
\
/* Create interpolator from filter prototype prototype (Kaiser-Bessel */ \
/* windowed-sinc function) */ \
/* _M : interpolation factor, _M >= 2 */ \
/* _m : filter delay [symbols], _m >= 1 */ \
/* _As : stop-band attenuation [dB], _As >= 0 */ \
FIRINTERP() FIRINTERP(_create_kaiser)(unsigned int _M, \
unsigned int _m, \
float _As); \
\
/* Create interpolator object from filter prototype */ \
/* _type : filter type (e.g. LIQUID_FIRFILT_RCOS) */ \
/* _M : interpolation factor, _M > 1 */ \
/* _m : filter delay (symbols), _m > 0 */ \
/* _beta : excess bandwidth factor, 0 <= _beta <= 1 */ \
/* _dt : fractional sample delay, -1 <= _dt <= 1 */ \
FIRINTERP() FIRINTERP(_create_prototype)(int _type, \
unsigned int _M, \
unsigned int _m, \
float _beta, \
float _dt); \
\
/* Destroy firinterp object, freeing all internal memory */ \
void FIRINTERP(_destroy)(FIRINTERP() _q); \
\
/* Print firinterp object's internal properties to stdout */ \
void FIRINTERP(_print)(FIRINTERP() _q); \
\
/* Reset internal state */ \
void FIRINTERP(_reset)(FIRINTERP() _q); \
\
/* Set output scaling for interpolator */ \
/* _q : interpolator object */ \
/* _scale : scaling factor to apply to each output sample */ \
void FIRINTERP(_set_scale)(FIRINTERP() _q, \
TC _scale); \
\
/* Get output scaling for interpolator */ \
/* _q : interpolator object */ \
/* _scale : scaling factor to apply to each output sample */ \
void FIRINTERP(_get_scale)(FIRINTERP() _q, \
TC * _scale); \
\
/* Execute interpolation on single input sample and write \(M\) output */ \
/* samples (\(M\) is the interpolation factor) */ \
/* _q : firinterp object */ \
/* _x : input sample */ \
/* _y : output sample array, [size: _M x 1] */ \
void FIRINTERP(_execute)(FIRINTERP() _q, \
TI _x, \
TO * _y); \
\
/* Execute interpolation on block of input samples */ \
/* _q : firinterp object */ \
/* _x : input array, [size: _n x 1] */ \
/* _n : size of input array */ \
/* _y : output sample array, [size: _M*_n x 1] */ \
void FIRINTERP(_execute_block)(FIRINTERP() _q, \
TI * _x, \
unsigned int _n, \
TO * _y); \
LIQUID_FIRINTERP_DEFINE_API(LIQUID_FIRINTERP_MANGLE_RRRF,
float,
float,
float)
LIQUID_FIRINTERP_DEFINE_API(LIQUID_FIRINTERP_MANGLE_CRCF,
liquid_float_complex,
float,
liquid_float_complex)
LIQUID_FIRINTERP_DEFINE_API(LIQUID_FIRINTERP_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
// iirinterp : infinite impulse response interpolator
#define LIQUID_IIRINTERP_MANGLE_RRRF(name) LIQUID_CONCAT(iirinterp_rrrf,name)
#define LIQUID_IIRINTERP_MANGLE_CRCF(name) LIQUID_CONCAT(iirinterp_crcf,name)
#define LIQUID_IIRINTERP_MANGLE_CCCF(name) LIQUID_CONCAT(iirinterp_cccf,name)
#define LIQUID_IIRINTERP_DEFINE_API(IIRINTERP,TO,TC,TI) \
\
/* Infinite impulse response (IIR) interpolator */ \
typedef struct IIRINTERP(_s) * IIRINTERP(); \
\
/* Create infinite impulse response interpolator from external */ \
/* coefficients. */ \
/* Note that the number of feed-forward and feed-back coefficients do */ \
/* not need to be equal, but they do need to be non-zero. */ \
/* Furthermore, the first feed-back coefficient \(a_0\) cannot be */ \
/* equal to zero, otherwise the filter will be invalid as this value is */ \
/* factored out from all coefficients. */ \
/* For stability reasons the number of coefficients should reasonably */ \
/* not exceed about 8 for single-precision floating-point. */ \
/* _M : interpolation factor, _M >= 2 */ \
/* _b : feed-forward coefficients (numerator), [size: _nb x 1] */ \
/* _nb : number of feed-forward coefficients, _nb > 0 */ \
/* _a : feed-back coefficients (denominator), [size: _na x 1] */ \
/* _na : number of feed-back coefficients, _na > 0 */ \
IIRINTERP() IIRINTERP(_create)(unsigned int _M, \
TC * _b, \
unsigned int _nb, \
TC * _a, \
unsigned int _na); \
\
/* Create interpolator object with default Butterworth prototype */ \
/* _M : interpolation factor, _M >= 2 */ \
/* _order : filter order, _order > 0 */ \
IIRINTERP() IIRINTERP(_create_default)(unsigned int _M, \
unsigned int _order); \
\
/* Create IIR interpolator from prototype */ \
/* _M : interpolation factor, _M >= 2 */ \
/* _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) */ \
/* _btype : band type (e.g. LIQUID_IIRDES_BANDPASS) */ \
/* _format : coefficients format (e.g. LIQUID_IIRDES_SOS) */ \
/* _order : filter order, _order > 0 */ \
/* _fc : low-pass prototype cut-off frequency, 0 <= _fc <= 0.5 */ \
/* _f0 : center frequency (band-pass, band-stop), 0 <= _f0 <= 0.5 */ \
/* _Ap : pass-band ripple in dB, _Ap > 0 */ \
/* _As : stop-band ripple in dB, _As > 0 */ \
IIRINTERP() IIRINTERP(_create_prototype)( \
unsigned int _M, \
liquid_iirdes_filtertype _ftype, \
liquid_iirdes_bandtype _btype, \
liquid_iirdes_format _format, \
unsigned int _order, \
float _fc, \
float _f0, \
float _Ap, \
float _As); \
\
/* Destroy interpolator object and free internal memory */ \
void IIRINTERP(_destroy)(IIRINTERP() _q); \
\
/* Print interpolator object internals to stdout */ \
void IIRINTERP(_print)(IIRINTERP() _q); \
\
/* Reset interpolator object */ \
void IIRINTERP(_reset)(IIRINTERP() _q); \
\
/* Execute interpolation on single input sample and write \(M\) output */ \
/* samples (\(M\) is the interpolation factor) */ \
/* _q : iirinterp object */ \
/* _x : input sample */ \
/* _y : output sample array, [size: _M x 1] */ \
void IIRINTERP(_execute)(IIRINTERP() _q, \
TI _x, \
TO * _y); \
\
/* Execute interpolation on block of input samples */ \
/* _q : iirinterp object */ \
/* _x : input array, [size: _n x 1] */ \
/* _n : size of input array */ \
/* _y : output sample array, [size: _M*_n x 1] */ \
void IIRINTERP(_execute_block)(IIRINTERP() _q, \
TI * _x, \
unsigned int _n, \
TO * _y); \
\
/* Compute and return group delay of object */ \
/* _q : filter object */ \
/* _fc : frequency to evaluate */ \
float IIRINTERP(_groupdelay)(IIRINTERP() _q, \
float _fc); \
LIQUID_IIRINTERP_DEFINE_API(LIQUID_IIRINTERP_MANGLE_RRRF,
float,
float,
float)
LIQUID_IIRINTERP_DEFINE_API(LIQUID_IIRINTERP_MANGLE_CRCF,
liquid_float_complex,
float,
liquid_float_complex)
LIQUID_IIRINTERP_DEFINE_API(LIQUID_IIRINTERP_MANGLE_CCCF,
liquid_float_complex,
liquid_float_complex,
liquid_float_complex)
//
// Decimators
//
// firdecim : finite impulse response decimator
#define LIQUID_FIRDECIM_MANGLE_RRRF(name) LIQUID_CONCAT(firdecim_rrrf,name)
#define LIQUID_FIRDECIM_MANGLE_CRCF(name) LIQUID_CONCAT(firdecim_crcf,name)
#define LIQUID_FIRDECIM_MANGLE_CCCF(name) LIQUID_CONCAT(firdecim_cccf,name)
#define LIQUID_FIRDECIM_DEFINE_API(FIRDECIM,TO,TC,TI) \
\
/* Finite impulse response (FIR) decimator */ \
typedef struct FIRDECIM(_s) * FIRDECIM(); \
\
/* Create decimator from external coefficients */ \
/* _M : decimation factor, _M >= 2 */ \
/* _h : filter coefficients, [size: _h_len x 1] */ \
/* _h_len : filter length, _h_len >= _M */ \
FIRDECIM() FIRDECIM(_create)(unsigned int _M, \
TC * _h, \
unsigned int _h_len); \
\
/* Create decimator from filter prototype prototyp