/*
 * Copyright (c) 2007 - 2017 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.0"
#define LIQUID_VERSION_NUMBER   1003000

//
// 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)                         \
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                        */  \
void AGC(_print)(AGC() _q);                                     \
                                                                \
/* reset object's internal state                            */  \
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       */  \
/*  _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/unlock gain control */                                  \
void AGC(_lock)(  AGC() _q);                                    \
void AGC(_unlock)(AGC() _q);                                    \
                                                                \
/* get/set loop filter bandwidth; attack/release time       */  \
float AGC(_get_bandwidth)(AGC() _q);                            \
void  AGC(_set_bandwidth)(AGC() _q, float _bt);                 \
                                                                \
/* get/set signal level (linear) relative to unity energy   */  \
float AGC(_get_signal_level)(AGC() _q);                         \
void  AGC(_set_signal_level)(AGC() _q, float _signal_level);    \
                                                                \
/* get/set signal level (dB) relative to unity energy       */  \
float AGC(_get_rssi)(AGC() _q);                                 \
void  AGC(_set_rssi)(AGC() _q, float _rssi);                    \
                                                                \
/* get/set gain value (linear) relative to unity energy     */  \
float AGC(_get_gain)(AGC() _q);                                 \
void  AGC(_set_gain)(AGC() _q, float _gain);                    \
                                                                \
/* initialize internal gain on 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);                           \
                                                                \
/* is squelch enabled?                                      */  \
int  AGC(_squelch_is_enabled)(AGC() _q);                        \
                                                                \
/* set squelch threshold                                    */  \
/*  _q          :   automatic gain control object           */  \
/*  _thresh_dB  :   threshold for enabling squelch [dB]     */  \
void AGC(_squelch_set_threshold)(AGC() _q,                      \
                                 T     _threshold);             \
                                                                \
/* get squelch threshold [dB]                               */  \
T    AGC(_squelch_get_threshold)(AGC() _q);                     \
                                                                \
/* set squelch timeout                                      */  \
/*  _q       : automatic gain control object                */  \
/*  _timeout : timeout before enabling squelch [samples]    */  \
void AGC(_squelch_set_timeout)(AGC()        _q,                 \
                               unsigned int _timeout);          \
                                                                \
/* get squelch timeout [samples]                            */  \
unsigned int AGC(_squelch_get_timeout)(AGC() _q);               \
                                                                \
/* get squelch status                                       */  \
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)                    \
typedef struct CBUFFER(_s) * CBUFFER();                         \
                                                                \
/* create circular buffer object of a particular size       */  \
/*  _max_size  : maximum buffer size, _max_size > 0         */  \
CBUFFER() CBUFFER(_create)(unsigned int _max_size);             \
                                                                \
/* create circular buffer object of a particular size and   */  \
/* specify the maximum number of elements that can be read  */  \
/* at 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                          */  \
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);           \
                                                                \
/* is buffer full?                                          */  \
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 samples to the buffer                              */  \
/*  _q  : circular buffer object                            */  \
/*  _v  : output array                                      */  \
/*  _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       */  \
/*  _q  : circular buffer object                            */  \
/*  _v  : pointer to sample output                          */  \
void CBUFFER(_pop)(CBUFFER() _q,                                \
                   T *       _v);                               \
                                                                \
/* read buffer contents                                     */  \
/*  _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)                      \
                                                                \
typedef struct WINDOW(_s) * WINDOW();                           \
                                                                \
/* create window buffer object of length _n                 */  \
WINDOW() WINDOW(_create)(unsigned int _n);                      \
                                                                \
/* recreate window buffer object with new length            */  \
/*  _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 window buffer contents                              */  \
/*  _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     */  \
/*  _q      : window object                                 */  \
/*  _i      : index of element to read                      */  \
/*  _v      : output value pointer                          */  \
void WINDOW(_index)(WINDOW()     _q,                            \
                    unsigned int _i,                            \
                    T *          _v);                           \
                                                                \
/* push single element onto window buffer                   */  \
/*  _q      : window object                                 */  \
/*  _v      : single input element                          */  \
void WINDOW(_push)(WINDOW() _q,                                 \
                   T        _v);                                \
                                                                \
/* write array of elements onto window buffer               */  \
/*  _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)                      \
typedef struct WDELAY(_s) * WDELAY();                           \
                                                                \
/* create delay buffer object with '_delay' samples         */  \
WDELAY() WDELAY(_create)(unsigned int _delay);                  \
                                                                \
/* re-create delay buffer object with '_delay' samples      */  \
/*  _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 from delay buffer object             */  \
/*  _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)             \
                                                                \
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);                             \
                                                                \
/* apply 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);                      \
                                                                \
/* apply 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);            \
                                                                \
/* apply 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);              \
                                                                \
/* apply slowly-varying shadowing impairment                */  \
/*  _q          : channel object                            */  \
/*  _sigma      : std. deviation for log-normal shadowing   */  \
/*  _fd         : Doppler frequency, _fd in (0,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)               \
                                                                \
typedef struct TVMPCH(_s) * TVMPCH();                           \
                                                                \
/* create channel object with default parameters            */  \
/* create time-varying multi-path channel emulator object   */  \
/*  _n      :   number of coefficients, _n > 0              */  \
/*  _std    :   standard deviation                          */  \
/*  _tau    :   coherence time                              */  \
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: _nx x 1]                   */  \
/*  _nx     : input array length                            */  \
/*  _y      : output array                                  */  \
void TVMPCH(_execute_block)(TVMPCH()     _q,                    \
                            TI *         _x,                    \
                            unsigned int _nx,                   \
                            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)             \
                                                                \
/* run dot product without creating object [unrolled 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(_run)( TC *_v, TI *_x, unsigned int _n, TO *_y);   \
void DOTPROD(_run4)(TC *_v, TI *_x, unsigned int _n, TO *_y);   \
                                                                \
typedef struct DOTPROD(_s) * DOTPROD();                         \
                                                                \
/* create 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                             */  \
/*  _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                                      */  \
/*  _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)                        \
typedef struct EQLMS(_s) * EQLMS();                             \
                                                                \
/* create LMS EQ initialized with external coefficients     */  \
/*  _h      : filter coefficients (NULL for {1,0,0...})     */  \
/*  _h_len  : filter length                                 */  \
EQLMS() EQLMS(_create)(T *          _h,                         \
                       unsigned int _h_len);                    \
                                                                \
/* create LMS EQ initialized with square-root Nyquist       */  \
/*  _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           */  \
/*  _h_len  : filter length                                 */  \
/*  _fc     : filter cut-off, _fc in (0,0.5]                */  \
EQLMS() EQLMS(_create_lowpass)(unsigned int _h_len,             \
                               float        _fc);               \
                                                                \
/* re-create EQ initialized with external coefficients      */  \
/*  _q      :   equalizer object                            */  \
/*  _h      :   filter coefficients (NULL for {1,0,0...})   */  \
/*  _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/set equalizer learning rate                          */  \
float EQLMS(_get_bw)(EQLMS() _q);                               \
void  EQLMS(_set_bw)(EQLMS() _q,                                \
                     float   _lambda);                          \
                                                                \
/* push sample into equalizer internal buffer               */  \
void EQLMS(_push)(EQLMS() _q,                                   \
                  T       _x);                                  \
                                                                \
/* push sample into equalizer internal buffer as block      */  \
/*  _q      :   equalizer object                            */  \
/*  _x      :   input sample array                          */  \
/*  _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)                        \
typedef struct EQRLS(_s) * EQRLS();                             \
                                                                \
/* create RLS EQ initialized with external coefficients     */  \
/*  _h  : filter coefficients (NULL for {1,0,0...})         */  \
/*  _p  : filter length                                     */  \
EQRLS() EQRLS(_create)(T *          _h,                         \
                       unsigned int _p);                        \
                                                                \
/* re-create RLS EQ initialized with external coefficients  */  \
/*  _q  : initial equalizer object                          */  \
/*  _h  : filter coefficients (NULL for {1,0,0...})         */  \
/*  _p  : filter length                                     */  \
EQRLS() EQRLS(_recreate)(EQRLS()      _q,                       \
                         T *          _h,                       \
                         unsigned int _p);                      \
                                                                \
/* destroy equalizer object, freeing all internal memory    */  \
void EQRLS(_destroy)(EQRLS() _q);                               \
                                                                \
/* print equalizer internal state                           */  \
void EQRLS(_print)(EQRLS() _q);                                 \
                                                                \
/* reset equalizer object, clearing internal state          */  \
void EQRLS(_reset)(EQRLS() _q);                                 \
                                                                \
/* get/set equalizer learning rate                          */  \
float EQRLS(_get_bw)(EQRLS() _q);                               \
void  EQRLS(_set_bw)(EQRLS() _q,                                \
                     float   _mu);                              \
                                                                \
/* push sample into equalizer internal buffer               */  \
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);                  \
                                                                \
/* retrieve internal filter 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)                         \
                                                                \
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 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                                        */  \
void FFT(_destroy_plan)(FFT(plan) _p);                          \
                                                                \
/* print transform plan and internal strategy               */  \
void FFT(_print_plan)(FFT(plan) _p);                            \
                                                                \
/* run the transform                                        */  \
void FFT(_execute)(FFT(plan) _p);                               \
                                                                \
/* object-independent methods */                                \
                                                                \
/* 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 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                               */  \
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)                \
                                                                \
typedef struct SPGRAM(_s) * SPGRAM();                           \
                                                                \
/* create spgram object                                     */  \
/*  _nfft       : FFT size                                  */  \
/*  _wtype      : window type, e.g. LIQUID_WINDOW_HAMMING   */  \
/*  _window_len : window length, _window_len in [1,_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 (Kaiser-Bessel window)      */  \
SPGRAM() SPGRAM(_create_default)(unsigned int _nfft);           \
                                                                \
/* destroy spgram object                                    */  \
void SPGRAM(_destroy)(SPGRAM() _q);                             \
                                                                \
/* clears the internal state of the spgram object, but not  */  \
/* the internal buffer                                      */  \
void SPGRAM(_clear)(SPGRAM() _q);                               \
                                                                \
/* reset the spgram object to its original state completely */  \
void SPGRAM(_reset)(SPGRAM() _q);                               \
                                                                \
/* print internal state of the spgram object                */  \
void SPGRAM(_print)(SPGRAM() _q);                               \
                                                                \
/* set methods                                              */  \
int          SPGRAM(_set_alpha)(SPGRAM() _q, float _alpha);     \
                                                                \
/* access methods                                           */  \
unsigned int SPGRAM(_get_nfft)                (SPGRAM() _q);    \
unsigned int SPGRAM(_get_window_len)          (SPGRAM() _q);    \
unsigned int SPGRAM(_get_delay)               (SPGRAM() _q);    \
uint64_t     SPGRAM(_get_num_samples)         (SPGRAM() _q);    \
uint64_t     SPGRAM(_get_num_samples_total)   (SPGRAM() _q);    \
uint64_t     SPGRAM(_get_num_transforms)      (SPGRAM() _q);    \
uint64_t     SPGRAM(_get_num_transforms_total)(SPGRAM() _q);    \
float        SPGRAM(_get_alpha)               (SPGRAM() _q);    \
                                                                \
/* push a single sample into the spgram object              */  \
/*  _q      :   spgram object                               */  \
/*  _x      :   input sample                                */  \
void SPGRAM(_push)(SPGRAM() _q,                                 \
                   TI       _x);                                \
                                                                \
/* write a block of samples to the spgram object            */  \
/*  _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 gnuplot file                                      */  \
/*  _q        : spgram object                               */  \
/*  _filename : input buffer [size: _n x 1]                 */  \
int SPGRAM(_export_gnuplot)(SPGRAM()     _q,                    \
                            const char * _filename);            \
                                                                \
/* object-independent methods */                                \
                                                                \
/* estimate spectrum on input signal                        */  \
/*  _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)                \
                                                                \
typedef struct ASGRAM(_s) * ASGRAM();                           \
                                                                \
/* create asgram object with size _nfft                     */  \
ASGRAM() ASGRAM(_create)(unsigned int _nfft);                   \
                                                                \
/* destroy asgram object                                    */  \
void ASGRAM(_destroy)(ASGRAM() _q);                             \
                                                                \
/* resets the internal state of the asgram object           */  \
void ASGRAM(_reset)(ASGRAM() _q);                               \
                                                                \
/* set scale and offset for spectrogram                     */  \
/*  _q      :   asgram object                               */  \
/*  _ref    :   signal reference level [dB]                 */  \
/*  _div    :   signal division [dB]                        */  \
void ASGRAM(_set_scale)(ASGRAM() _q,                            \
                        float    _ref,                          \
                        float    _div);                         \
                                                                \
/* set display characters for output string                 */  \
/*  _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              */  \
/*  _q      :   asgram object                               */  \
/*  _x      :   input sample                                */  \
void ASGRAM(_push)(ASGRAM() _q,                                 \
                   TI       _x);                                \
                                                                \
/* write a block of samples to the asgram object            */  \
/*  _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                                                 */  \
/*  _q          :   spgram 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)      \
                                                                \
typedef struct SPWATERFALL(_s) * SPWATERFALL();                 \
                                                                \
/* create spgram object                                     */  \
/*  _nfft       : FFT size                                  */  \
/*  _wtype      : window type, e.g. LIQUID_WINDOW_HAMMING   */  \
/*  _window_len : window length, _window_len in [1,_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 spgram object (Kaiser-Bessel window)      */  \
SPWATERFALL() SPWATERFALL(_create_default)(unsigned int _nfft,  \
                                           unsigned int _time); \
                                                                \
/* destroy spgram object                                    */  \
void SPWATERFALL(_destroy)(SPWATERFALL() _q);                   \
                                                                \
/* clears the internal state of the spgram object, but not  */  \
/* the internal buffer                                      */  \
void SPWATERFALL(_clear)(SPWATERFALL() _q);                     \
                                                                \
/* reset the spgram object to its original state completely */  \
void SPWATERFALL(_reset)(SPWATERFALL() _q);                     \
                                                                \
/* print internal state of the spgram object                */  \
void SPWATERFALL(_print)(SPWATERFALL() _q);                     \
                                                                \
/* push a single sample into the spgram object              */  \
/*  _q      :   spgram object                               */  \
/*  _x      :   input sample                                */  \
void SPWATERFALL(_push)(SPWATERFALL() _q,                       \
                        TI            _x);                      \
                                                                \
/* write a block of samples to the spgram object            */  \
/*  _q      :   spgram object                               */  \
/*  _x      :   input buffer [size: _n x 1]                 */  \
/*  _n      :   input buffer length                         */  \
void SPWATERFALL(_write)(SPWATERFALL() _q,                      \
                         TI *          _x,                      \
                         unsigned int  _n);                     \
                                                                \
/* export files for plotting                                */  \
/*  _q             : spgram object                          */  \
/*  _filename_base : base filename (will export files with  */  \
/*                   .gnu, .bin, and .png extensions)       */  \
int SPWATERFALL(_export)(SPWATERFALL() _q,                      \
                         const char *  _filename_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 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)           \
                                                                \
typedef struct AUTOCORR(_s) * AUTOCORR();                       \
                                                                \
/* create auto-correlator object                            */  \
/*  _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                  */  \
void AUTOCORR(_push)(AUTOCORR() _q,                             \
                     TI         _x);                            \
                                                                \
/* compute single auto-correlation 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)             \
typedef struct FIRFILT(_s) * FIRFILT();                         \
                                                                \
FIRFILT() FIRFILT(_create)(TC * _h, unsigned int _n);           \
                                                                \
/* create using Kaiser-Bessel windowed sinc method          */  \
/*  _n      : filter length, _n > 0                         */  \
/*  _fc     : filter 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 from square-root Nyquist prototype                */  \
/*  _type   : filter type (e.g. LIQUID_FIRFILT_RRC)         */  \
/*  _k      : nominal samples/symbol, _k > 1                */  \
/*  _m      : filter delay [symbols], _m > 0                */  \
/*  _beta   : rolloff factor, 0 < beta <= 1                 */  \
/*  _mu     : fractional sample offset,-0.5 < _mu < 0.5     */  \
FIRFILT() FIRFILT(_create_rnyquist)(int          _type,         \
                                    unsigned int _k,            \
                                    unsigned int _m,            \
                                    float        _beta,         \
                                    float        _mu);          \
                                                                \
/* create rectangular filter prototype                      */  \
FIRFILT() FIRFILT(_create_rect)(unsigned int _n);               \
                                                                \
/* re-create filter                                         */  \
/*  _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                          */  \
void FIRFILT(_print)(FIRFILT() _q);                             \
                                                                \
/* set output scaling for filter                            */  \
void FIRFILT(_set_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);                              \
                                                                \
/* execute the filter on 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; 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               */  \
/*  _y      : pointer to output array [size: _n x 1]        */  \
void FIRFILT(_execute_block)(FIRFILT()    _q,                   \
                             TI *         _x,                   \
                             unsigned int _n,                   \
                             TO *         _y);                  \
                                                                \
/* return length of filter object                           */  \
unsigned int FIRFILT(_get_length)(FIRFILT() _q);                \
                                                                \
/* compute complex frequency response of filter object      */  \
/*  _q      : filter object                                 */  \
/*  _fc     : frequency to evaluate                         */  \
/*  _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)                 \
typedef struct FIRHILB(_s) * FIRHILB();                         \
                                                                \
/* create finite impulse reponse Hilbert transform          */  \
/*  _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 reponse Hilbert transform         */  \
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                 */  \
/*  _y      :   real-valued output sample                   */  \
void FIRHILB(_c2r_execute)(FIRHILB() _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 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)


//
// 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)             \
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, at least _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                          */  \
void FFTFILT(_print)(FFTFILT() _q);                             \
                                                                \
/* set output scaling for filter                            */  \
void FFTFILT(_set_scale)(FFTFILT() _q,                          \
                         TC        _scale);                     \
                                                                \
/* execute the filter on internal buffer and coefficients   */  \
/*  _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);                           \
                                                                \
/* return 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)             \
                                                                \
typedef struct IIRFILT(_s) * IIRFILT();                         \
                                                                \
/* create infinite impulse reponse filter                   */  \
/*  _b      : feed-forward coefficients [size: _nb x 1]     */  \
/*  _nb     : number of feed-forward coefficients           */  \
/*  _a      : feed-back coefficients [size: _na x 1]        */  \
/*  _na     : number of feed-back coefficients              */  \
IIRFILT() IIRFILT(_create)(TC *         _b,                     \
                           unsigned int _nb,                    \
                           TC *         _a,                     \
                           unsigned int _na);                   \
                                                                \
/* create IIR filter using 2nd-order secitons               */  \
/*  _B      : feed-forward coefficients [size: _nsos x 3]   */  \
/*  _A      : feed-back coefficients    [size: _nsos x 3]   */  \
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                                  */  \
/*  _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                        */  \
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                                  */  \
/*  _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)();                        \
                                                                \
/* create 8th-order differentiator filter                   */  \
IIRFILT() IIRFILT(_create_differentiator)();                    \
                                                                \
/* create simple DC-blocking filter                         */  \
IIRFILT() IIRFILT(_create_dc_blocker)(float _alpha);            \
                                                                \
/* create phase-locked loop iirfilt object                  */  \
/*  _w      : filter bandwidth                              */  \
/*  _zeta   : damping factor (1/sqrt(2) suggested)          */  \
/*  _K      : loop gain (1000 suggested)                    */  \
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);                             \
                                                                \
/* clear/reset iirfilt object internals                     */  \
void IIRFILT(_reset)(IIRFILT() _q);                             \
                                                                \
/* compute filter output                                    */  \
/*  _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; 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               */  \
/*  _y      : pointer to output array [size: _n x 1]        */  \
void IIRFILT(_execute_block)(IIRFILT()    _q,                   \
                             TI *         _x,                   \
                             unsigned int _n,                   \
                             TO *         _y);                  \
                                                                \
/* return iirfilt object's filter length (order + 1)        */  \
unsigned int IIRFILT(_get_length)(IIRFILT() _q);                \
                                                                \
/* compute complex frequency response of filter object      */  \
/*  _q      : filter object                                 */  \
/*  _fc     : frequency to evaluate                         */  \
/*  _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)               \
                                                                \
typedef struct FIRPFB(_s) * FIRPFB();                           \
                                                                \
/* create firpfb from external coefficients                 */  \
/*  _M      : number of filters in the bank                 */  \
/*  _h      : coefficients [size: _M*_h_len x 1]            */  \
/*  _h_len  : filter delay (symbols)                        */  \
FIRPFB() FIRPFB(_create)(unsigned int _M,                       \
                         TC *         _h,                       \
                         unsigned int _h_len);                  \
                                                                \
/* create firpfb from external coefficients                 */  \
/*  _M      : number of filters in the bank                 */  \
/*  _m      : filter semi-length [samples]                  */  \
/*  _fc     : filter cut-off frequency 0 < _fc < 0.5        */  \
/*  _As     : filter stop-band suppression [dB]             */  \
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)         */  \
/*  _npfb   : number of filters in the bank                 */  \
/*  _k      : nominal samples/symbol                        */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _beta   : rolloff factor (0 < beta <= 1)                */  \
FIRPFB() FIRPFB(_create_rnyquist)(int          _type,           \
                                  unsigned int _npfb,           \
                                  unsigned int _k,              \
                                  unsigned int _m,              \
                                  float        _beta);          \
                                                                \
/* create from square-root derivative Nyquist prototype     */  \
/*  _type   : filter type (e.g. LIQUID_FIRFILT_RRC)         */  \
/*  _npfb   : number of filters in the bank                 */  \
/*  _k      : nominal samples/symbol                        */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _beta   : rolloff factor (0 < beta <= 1)                */  \
FIRPFB() FIRPFB(_create_drnyquist)(int          _type,          \
                                   unsigned int _npfb,          \
                                   unsigned int _k,             \
                                   unsigned int _m,             \
                                   float        _beta);         \
                                                                \
/* re-create filterbank object                              */  \
/*  _q      : original firpfb object                        */  \
/*  _M      : number of filters in the bank                 */  \
/*  _h      : coefficients [size: _M x _h_len]              */  \
/*  _h_len  : length of each filter                         */  \
FIRPFB() FIRPFB(_recreate)(FIRPFB()     _q,                     \
                           unsigned int _M,                     \
                           TC *         _h,                     \
                           unsigned int _h_len);                \
                                                                \
/* destroy firpfb object, freeing all internal memory       */  \
void FIRPFB(_destroy)(FIRPFB() _q);                             \
                                                                \
/* print firpfb object's parameters                         */  \
void FIRPFB(_print)(FIRPFB() _q);                               \
                                                                \
/* set output scaling for filter                            */  \
void FIRPFB(_set_scale)(FIRPFB() _q,                            \
                        TC       _g);                           \
                                                                \
/* clear/reset firpfb object internal state                 */  \
void FIRPFB(_reset)(FIRPFB() _q);                               \
                                                                \
/* push sample into firpfb internal buffer                  */  \
void FIRPFB(_push)(FIRPFB() _q, TI _x);                         \
                                                                \
/* execute the filter on internal buffer and coefficients   */  \
/*  _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; the      */  \
/* input and output buffers may be the same                 */  \
/*  _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)         \
                                                                \
typedef struct FIRINTERP(_s) * FIRINTERP();                     \
                                                                \
/* create interpolator from external coefficients           */  \
/*  _M      : interpolation factor                          */  \
/*  _h      : filter coefficients [size: _h_len x 1]        */  \
/*  _h_len  : filter length                                 */  \
FIRINTERP() FIRINTERP(_create)(unsigned int _M,                 \
                               TC *         _h,                 \
                               unsigned int _h_len);            \
                                                                \
/* create interpolator from Kaiser prototype                */  \
/*  _M      : interpolation factor                          */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _As     : stop-band attenuation [dB]                    */  \
FIRINTERP() FIRINTERP(_create_kaiser)(unsigned int _M,          \
                                      unsigned int _m,          \
                                      float        _As);        \
                                                                \
/* create prorotype (root-)Nyquist interpolator             */  \
/*  _type   : filter type (e.g. LIQUID_FIRFILT_RCOS)        */  \
/*  _k      :   samples/symbol,          _k > 1             */  \
/*  _m      :   filter delay (symbols),  _m > 0             */  \
/*  _beta   :   excess bandwidth factor, _beta < 1          */  \
/*  _dt     :   fractional sample delay, _dt in (-1, 1)     */  \
FIRINTERP() FIRINTERP(_create_prototype)(int          _type,    \
                                         unsigned int _k,       \
                                         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);                         \
                                                                \
/* execute interpolation on single input sample             */  \
/*  _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)         \
typedef struct IIRINTERP(_s) * IIRINTERP();                     \
                                                                \
/* create interpolator from external coefficients           */  \
/*  _M      : interpolation factor                          */  \
/*  _b      : feed-back coefficients [size: _nb x 1]        */  \
/*  _nb     : feed-back coefficients length                 */  \
/*  _a      : feed-forward coefficients [size: _na x 1]     */  \
/*  _na     : feed-forward coefficients length              */  \
IIRINTERP() IIRINTERP(_create)(unsigned int _M,                 \
                               TC *         _b,                 \
                               unsigned int _nb,                \
                               TC *         _a,                 \
                               unsigned int _na);               \
                                                                \
/* create decimator with default Butterworth prototype      */  \
/*  _M      : decimation factor                             */  \
/*  _order  : filter order                                  */  \
IIRINTERP() IIRINTERP(_create_default)(unsigned int _M,         \
                                       unsigned int _order);    \
                                                                \
/* create interpolator from prototype                       */  \
/*  _M      : interpolation factor                          */  \
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                      */  \
void IIRINTERP(_print)(IIRINTERP() _q);                         \
                                                                \
/* reset interpolator object                                */  \
void IIRINTERP(_reset)(IIRINTERP() _q);                         \
                                                                \
/* execute interpolation on single input sample             */  \
/*  _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);                \
                                                                \
/* get system group delay at frequency _fc                  */  \
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)           \
typedef struct FIRDECIM(_s) * FIRDECIM();                       \
                                                                \
/* create decimator from external coefficients              */  \
/*  _M      : decimation factor                             */  \
/*  _h      : filter coefficients [size: _h_len x 1]        */  \
/*  _h_len  : filter coefficients length                    */  \
FIRDECIM() FIRDECIM(_create)(unsigned int _M,                   \
                             TC *         _h,                   \
                             unsigned int _h_len);              \
                                                                \
/* create decimator from Kaiser prototype                   */  \
/*  _M      : decimation factor                             */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _As     : stop-band attenuation [dB]                    */  \
FIRDECIM() FIRDECIM(_create_kaiser)(unsigned int _M,            \
                                    unsigned int _m,            \
                                    float        _As);          \
                                                                \
/* create square-root Nyquist decimator                     */  \
/*  _type   : filter type (e.g. LIQUID_FIRFILT_RRC)         */  \
/*  _M      : samples/symbol (decimation factor)            */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _beta   : rolloff factor (0 < beta <= 1)                */  \
/*  _dt     : fractional sample delay                       */  \
FIRDECIM() FIRDECIM(_create_prototype)(int          _type,      \
                                       unsigned int _M,         \
                                       unsigned int _m,         \
                                       float        _beta,      \
                                       float        _dt);       \
                                                                \
/* destroy decimator object                                 */  \
void FIRDECIM(_destroy)(FIRDECIM() _q);                         \
                                                                \
/* print decimator object propreties to stdout              */  \
void FIRDECIM(_print)(FIRDECIM() _q);                           \
                                                                \
/* reset decimator object internal state                    */  \
void FIRDECIM(_reset)(FIRDECIM() _q);                           \
                                                                \
/* execute decimator on _M input samples                    */  \
/*  _q      : decimator object                              */  \
/*  _x      : input samples [size: _M x 1]                  */  \
/*  _y      : output sample pointer                         */  \
void FIRDECIM(_execute)(FIRDECIM() _q,                          \
                        TI *       _x,                          \
                        TO *       _y);                         \
                                                                \
/* execute decimator on block of _n*_M input samples        */  \
/*  _q      : decimator object                              */  \
/*  _x      : input array [size: _n*_M x 1]                 */  \
/*  _n      : number of _output_ samples                    */  \
/*  _y      : output array [_size: _n x 1]                  */  \
void FIRDECIM(_execute_block)(FIRDECIM()   _q,                  \
                              TI *         _x,                  \
                              unsigned int _n,                  \
                              TO *         _y);                 \

LIQUID_FIRDECIM_DEFINE_API(LIQUID_FIRDECIM_MANGLE_RRRF,
                           float,
                           float,
                           float)

LIQUID_FIRDECIM_DEFINE_API(LIQUID_FIRDECIM_MANGLE_CRCF,
                           liquid_float_complex,
                           float,
                           liquid_float_complex)

LIQUID_FIRDECIM_DEFINE_API(LIQUID_FIRDECIM_MANGLE_CCCF,
                           liquid_float_complex,
                           liquid_float_complex,
                           liquid_float_complex)


// iirdecim : infinite impulse response decimator
#define LIQUID_IIRDECIM_MANGLE_RRRF(name) LIQUID_CONCAT(iirdecim_rrrf,name)
#define LIQUID_IIRDECIM_MANGLE_CRCF(name) LIQUID_CONCAT(iirdecim_crcf,name)
#define LIQUID_IIRDECIM_MANGLE_CCCF(name) LIQUID_CONCAT(iirdecim_cccf,name)

#define LIQUID_IIRDECIM_DEFINE_API(IIRDECIM,TO,TC,TI)           \
typedef struct IIRDECIM(_s) * IIRDECIM();                       \
                                                                \
/* create decimator from external coefficients              */  \
/*  _M      : decimation factor                             */  \
/*  _b      : feed-back coefficients [size: _nb x 1]        */  \
/*  _nb     : feed-back coefficients length                 */  \
/*  _a      : feed-forward coefficients [size: _na x 1]     */  \
/*  _na     : feed-forward coefficients length              */  \
IIRDECIM() IIRDECIM(_create)(unsigned int _M,                   \
                             TC *         _b,                   \
                             unsigned int _nb,                  \
                             TC *         _a,                   \
                             unsigned int _na);                 \
                                                                \
/* create decimator with default Butterworth prototype      */  \
/*  _M      : decimation factor                             */  \
/*  _order  : filter order                                  */  \
IIRDECIM() IIRDECIM(_create_default)(unsigned int _M,           \
                                     unsigned int _order);      \
                                                                \
/* create decimator from prototype                          */  \
/*  _M      : decimation factor                             */  \
/*  _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                        */  \
IIRDECIM() IIRDECIM(_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 decimator object and free internal memory        */  \
void IIRDECIM(_destroy)(IIRDECIM() _q);                         \
                                                                \
/* print decimator object internals                         */  \
void IIRDECIM(_print)(IIRDECIM() _q);                           \
                                                                \
/* reset decimator object                                   */  \
void IIRDECIM(_reset)(IIRDECIM() _q);                           \
                                                                \
/* execute decimator on _M input samples                    */  \
/*  _q      : decimator object                              */  \
/*  _x      : input samples [size: _M x 1]                  */  \
/*  _y      : output sample pointer                         */  \
void IIRDECIM(_execute)(IIRDECIM() _q,                          \
                        TI *       _x,                          \
                        TO *       _y);                         \
                                                                \
/* execute decimator on block of _n*_M input samples        */  \
/*  _q      : decimator object                              */  \
/*  _x      : input array [size: _n*_M x 1]                 */  \
/*  _n      : number of _output_ samples                    */  \
/*  _y      : output array [_sze: _n x 1]                   */  \
void IIRDECIM(_execute_block)(IIRDECIM()   _q,                  \
                              TI *         _x,                  \
                              unsigned int _n,                  \
                              TO *         _y);                 \
                                                                \
/* get system group delay at frequency _fc                  */  \
float IIRDECIM(_groupdelay)(IIRDECIM() _q, float _fc);          \

LIQUID_IIRDECIM_DEFINE_API(LIQUID_IIRDECIM_MANGLE_RRRF,
                           float,
                           float,
                           float)

LIQUID_IIRDECIM_DEFINE_API(LIQUID_IIRDECIM_MANGLE_CRCF,
                           liquid_float_complex,
                           float,
                           liquid_float_complex)

LIQUID_IIRDECIM_DEFINE_API(LIQUID_IIRDECIM_MANGLE_CCCF,
                           liquid_float_complex,
                           liquid_float_complex,
                           liquid_float_complex)



// 
// Half-band resampler
//
#define LIQUID_RESAMP2_MANGLE_RRRF(name) LIQUID_CONCAT(resamp2_rrrf,name)
#define LIQUID_RESAMP2_MANGLE_CRCF(name) LIQUID_CONCAT(resamp2_crcf,name)
#define LIQUID_RESAMP2_MANGLE_CCCF(name) LIQUID_CONCAT(resamp2_cccf,name)

#define LIQUID_RESAMP2_DEFINE_API(RESAMP2,TO,TC,TI)             \
typedef struct RESAMP2(_s) * RESAMP2();                         \
                                                                \
/* create half-band resampler                               */  \
/*  _m      :   filter semi-length (h_len = 4*m+1)          */  \
/*  _f0     :   filter center frequency                     */  \
/*  _As     :   stop-band attenuation [dB]                  */  \
RESAMP2() RESAMP2(_create)(unsigned int _m,                     \
                           float        _f0,                    \
                           float        _As);                   \
                                                                \
/* re-create half-band resampler with new properties        */  \
/*  _q      :   original half-band resampler object         */  \
/*  _m      :   filter semi-length (h_len = 4*m+1)          */  \
/*  _f0     :   filter center frequency                     */  \
/*  _As     :   stop-band attenuation [dB]                  */  \
RESAMP2() RESAMP2(_recreate)(RESAMP2()    _q,                   \
                             unsigned int _m,                   \
                             float        _f0,                  \
                             float        _As);                 \
                                                                \
/* destroy half-band resampler                              */  \
void RESAMP2(_destroy)(RESAMP2() _q);                           \
                                                                \
/* print resamp2 object's internals                         */  \
void RESAMP2(_print)(RESAMP2() _q);                             \
                                                                \
/* reset internal buffer                                    */  \
void RESAMP2(_reset)(RESAMP2() _q);                             \
                                                                \
/* get resampler filter delay (semi-length m)               */  \
unsigned int RESAMP2(_get_delay)(RESAMP2() _q);                 \
                                                                \
/* execute resamp2 as half-band filter                      */  \
/*  _q      :   resamp2 object                              */  \
/*  _x      :   input sample                                */  \
/*  _y0     :   output sample pointer (low frequency)       */  \
/*  _y1     :   output sample pointer (high frequency)      */  \
void RESAMP2(_filter_execute)(RESAMP2() _q,                     \
                              TI        _x,                     \
                              TO *      _y0,                    \
                              TO *      _y1);                   \
                                                                \
/* execute resamp2 as half-band analysis filterbank         */  \
/*  _q      :   resamp2 object                              */  \
/*  _x      :   input array  [size: 2 x 1]                  */  \
/*  _y      :   output array [size: 2 x 1]                  */  \
void RESAMP2(_analyzer_execute)(RESAMP2() _q,                   \
                                TI *      _x,                   \
                                TO *      _y);                  \
                                                                \
/* execute resamp2 as half-band synthesis filterbank        */  \
/*  _q      :   resamp2 object                              */  \
/*  _x      :   input array  [size: 2 x 1]                  */  \
/*  _y      :   output array [size: 2 x 1]                  */  \
void RESAMP2(_synthesizer_execute)(RESAMP2() _q,                \
                                   TI *      _x,                \
                                   TO *      _y);               \
                                                                \
/* execute resamp2 as half-band decimator                   */  \
/*  _q      :   resamp2 object                              */  \
/*  _x      :   input array  [size: 2 x 1]                  */  \
/*  _y      :   output sample pointer                       */  \
void RESAMP2(_decim_execute)(RESAMP2() _q,                      \
                             TI *      _x,                      \
                             TO *      _y);                     \
                                                                \
/* execute resamp2 as half-band interpolator                */  \
/*  _q      :   resamp2 object                              */  \
/*  _x      :   input sample                                */  \
/*  _y      :   output array [size: 2 x 1]                  */  \
void RESAMP2(_interp_execute)(RESAMP2() _q,                     \
                              TI        _x,                     \
                              TO *      _y);                    \

LIQUID_RESAMP2_DEFINE_API(LIQUID_RESAMP2_MANGLE_RRRF,
                          float,
                          float,
                          float)

LIQUID_RESAMP2_DEFINE_API(LIQUID_RESAMP2_MANGLE_CRCF,
                          liquid_float_complex,
                          float,
                          liquid_float_complex)

LIQUID_RESAMP2_DEFINE_API(LIQUID_RESAMP2_MANGLE_CCCF,
                          liquid_float_complex,
                          liquid_float_complex,
                          liquid_float_complex)


// 
// Arbitrary resampler
//
#define LIQUID_RESAMP_MANGLE_RRRF(name) LIQUID_CONCAT(resamp_rrrf,name)
#define LIQUID_RESAMP_MANGLE_CRCF(name) LIQUID_CONCAT(resamp_crcf,name)
#define LIQUID_RESAMP_MANGLE_CCCF(name) LIQUID_CONCAT(resamp_cccf,name)

#define LIQUID_RESAMP_DEFINE_API(RESAMP,TO,TC,TI)               \
typedef struct RESAMP(_s) * RESAMP();                           \
                                                                \
/* create arbitrary resampler object                        */  \
/*  _rate   : arbitrary resampling rate                     */  \
/*  _m      : filter semi-length (delay)                    */  \
/*  _fc     : filter cutoff frequency, 0 < _fc < 0.5        */  \
/*  _As     : filter stop-band attenuation [dB]             */  \
/*  _npfb   : number of filters in the bank                 */  \
RESAMP() RESAMP(_create)(float        _rate,                    \
                         unsigned int _m,                       \
                         float        _fc,                      \
                         float        _As,                      \
                         unsigned int _npfb);                   \
                                                                \
/* create arbitrary resampler object with a specified input */  \
/* resampling rate and default parameters                   */  \
/*  m    : (filter semi-length) = 7                         */  \
/*  fc   : (filter cutoff frequency) = 0.25                 */  \
/*  As   : (filter stop-band attenuation) = 60 dB           */  \
/*  npfb : (number of filters in the bank) = 64             */  \
RESAMP() RESAMP(_create_default)(float _rate);                  \
                                                                \
/* destroy arbitrary resampler object                       */  \
void RESAMP(_destroy)(RESAMP() _q);                             \
                                                                \
/* print resamp object internals to stdout                  */  \
void RESAMP(_print)(RESAMP() _q);                               \
                                                                \
/* reset resamp object internals                            */  \
void RESAMP(_reset)(RESAMP() _q);                               \
                                                                \
/* get resampler delay (output samples)                     */  \
unsigned int RESAMP(_get_delay)(RESAMP() _q);                   \
                                                                \
/* set rate of arbitrary resampler                          */  \
/*  _q      : resampling object                             */  \
/*  _rate   : new sampling rate, _rate > 0                  */  \
void RESAMP(_set_rate)(RESAMP() _q,                             \
                       float    _rate);                         \
                                                                \
/* adjust rate of arbitrary resampler                       */  \
/*  _q      : resampling object                             */  \
/*  _delta  : rate adjustment; _rate <- _rate + _delta      */  \
void RESAMP(_adjust_rate)(RESAMP() _q,                          \
                          float    _delta);                     \
                                                                \
/* set resampling timing phase                              */  \
/*  _q      : resampling object                             */  \
/*  _tau    : sample timing                                 */  \
void RESAMP(_set_timing_phase)(RESAMP() _q,                     \
                               float    _tau);                  \
                                                                \
/* adjust resampling timing phase                           */  \
/*  _q      : resampling object                             */  \
/*  _delta  : sample timing adjustment                      */  \
void RESAMP(_adjust_timing_phase)(RESAMP() _q,                  \
                                  float    _delta);             \
                                                                \
/* execute arbitrary resampler                              */  \
/*  _q              :   resamp object                       */  \
/*  _x              :   single input sample                 */  \
/*  _y              :   output sample array (pointer)       */  \
/*  _num_written    :   number of samples written to _y     */  \
void RESAMP(_execute)(RESAMP()       _q,                        \
                      TI             _x,                        \
                      TO *           _y,                        \
                      unsigned int * _num_written);             \
                                                                \
/* execute arbitrary resampler on a block of samples        */  \
/*  _q              :   resamp object                       */  \
/*  _x              :   input buffer [size: _nx x 1]        */  \
/*  _nx             :   input buffer                        */  \
/*  _y              :   output sample array (pointer)       */  \
/*  _ny             :   number of samples written to _y     */  \
void RESAMP(_execute_block)(RESAMP()       _q,                  \
                            TI *           _x,                  \
                            unsigned int   _nx,                 \
                            TO *           _y,                  \
                            unsigned int * _ny);                \

LIQUID_RESAMP_DEFINE_API(LIQUID_RESAMP_MANGLE_RRRF,
                         float,
                         float,
                         float)

LIQUID_RESAMP_DEFINE_API(LIQUID_RESAMP_MANGLE_CRCF,
                         liquid_float_complex,
                         float,
                         liquid_float_complex)

LIQUID_RESAMP_DEFINE_API(LIQUID_RESAMP_MANGLE_CCCF,
                         liquid_float_complex,
                         liquid_float_complex,
                         liquid_float_complex)


// 
// Multi-stage half-band resampler
//

// resampling type (interpolator/decimator)
typedef enum {
    LIQUID_RESAMP_INTERP=0, // interpolator
    LIQUID_RESAMP_DECIM,    // decimator
} liquid_resamp_type;

#define LIQUID_MSRESAMP2_MANGLE_RRRF(name) LIQUID_CONCAT(msresamp2_rrrf,name)
#define LIQUID_MSRESAMP2_MANGLE_CRCF(name) LIQUID_CONCAT(msresamp2_crcf,name)
#define LIQUID_MSRESAMP2_MANGLE_CCCF(name) LIQUID_CONCAT(msresamp2_cccf,name)

#define LIQUID_MSRESAMP2_DEFINE_API(MSRESAMP2,TO,TC,TI)         \
typedef struct MSRESAMP2(_s) * MSRESAMP2();                     \
                                                                \
/* create multi-stage half-band resampler                   */  \
/*  _type       : resampler type (e.g. LIQUID_RESAMP_DECIM) */  \
/*  _num_stages : number of resampling stages               */  \
/*  _fc         : filter cut-off frequency 0 < _fc < 0.5    */  \
/*  _f0         : filter center frequency                   */  \
/*  _As         : stop-band attenuation [dB]                */  \
MSRESAMP2() MSRESAMP2(_create)(int          _type,              \
                               unsigned int _num_stages,        \
                               float        _fc,                \
                               float        _f0,                \
                               float        _As);               \
                                                                \
/* destroy multi-stage half-bandresampler                   */  \
void MSRESAMP2(_destroy)(MSRESAMP2() _q);                       \
                                                                \
/* print msresamp object internals to stdout                */  \
void MSRESAMP2(_print)(MSRESAMP2() _q);                         \
                                                                \
/* reset msresamp object internal state                     */  \
void MSRESAMP2(_reset)(MSRESAMP2() _q);                         \
                                                                \
/* get group delay (number of output samples)               */  \
float MSRESAMP2(_get_delay)(MSRESAMP2() _q);                    \
                                                                \
/* execute multi-stage resampler, M = 2^num_stages          */  \
/*  LIQUID_RESAMP_INTERP:   input: 1,   output: M           */  \
/*  LIQUID_RESAMP_DECIM:    input: M,   output: 1           */  \
/*  _q      : msresamp object                               */  \
/*  _x      : input sample array                            */  \
/*  _y      : output sample array                           */  \
void MSRESAMP2(_execute)(MSRESAMP2() _q,                        \
                         TI *        _x,                        \
                         TO *        _y);                       \

LIQUID_MSRESAMP2_DEFINE_API(LIQUID_MSRESAMP2_MANGLE_RRRF,
                            float,
                            float,
                            float)

LIQUID_MSRESAMP2_DEFINE_API(LIQUID_MSRESAMP2_MANGLE_CRCF,
                            liquid_float_complex,
                            float,
                            liquid_float_complex)

LIQUID_MSRESAMP2_DEFINE_API(LIQUID_MSRESAMP2_MANGLE_CCCF,
                            liquid_float_complex,
                            liquid_float_complex,
                            liquid_float_complex)


// 
// Multi-stage arbitrary resampler
//
#define LIQUID_MSRESAMP_MANGLE_RRRF(name) LIQUID_CONCAT(msresamp_rrrf,name)
#define LIQUID_MSRESAMP_MANGLE_CRCF(name) LIQUID_CONCAT(msresamp_crcf,name)
#define LIQUID_MSRESAMP_MANGLE_CCCF(name) LIQUID_CONCAT(msresamp_cccf,name)

#define LIQUID_MSRESAMP_DEFINE_API(MSRESAMP,TO,TC,TI)           \
typedef struct MSRESAMP(_s) * MSRESAMP();                       \
                                                                \
/* create multi-stage arbitrary resampler                   */  \
/*  _r      :   resampling rate [output/input]              */  \
/*  _As     :   stop-band attenuation [dB]                  */  \
MSRESAMP() MSRESAMP(_create)(float _r,                          \
                             float _As);                        \
                                                                \
/* destroy multi-stage arbitrary resampler                  */  \
void MSRESAMP(_destroy)(MSRESAMP() _q);                         \
                                                                \
/* print msresamp object internals to stdout                */  \
void MSRESAMP(_print)(MSRESAMP() _q);                           \
                                                                \
/* reset msresamp object internal state                     */  \
void MSRESAMP(_reset)(MSRESAMP() _q);                           \
                                                                \
/* get filter delay (output samples)                        */  \
float MSRESAMP(_get_delay)(MSRESAMP() _q);                      \
                                                                \
/* execute multi-stage resampler                            */  \
/*  _q      :   msresamp object                             */  \
/*  _x      :   input sample array  [size: _nx x 1]         */  \
/*  _nx     :   input sample array size                     */  \
/*  _y      :   output sample array [size: variable]        */  \
/*  _ny     :   number of samples written to _y             */  \
void MSRESAMP(_execute)(MSRESAMP()     _q,                      \
                        TI *           _x,                      \
                        unsigned int   _nx,                     \
                        TO *           _y,                      \
                        unsigned int * _ny);                    \

LIQUID_MSRESAMP_DEFINE_API(LIQUID_MSRESAMP_MANGLE_RRRF,
                           float,
                           float,
                           float)

LIQUID_MSRESAMP_DEFINE_API(LIQUID_MSRESAMP_MANGLE_CRCF,
                           liquid_float_complex,
                           float,
                           liquid_float_complex)

LIQUID_MSRESAMP_DEFINE_API(LIQUID_MSRESAMP_MANGLE_CCCF,
                           liquid_float_complex,
                           liquid_float_complex,
                           liquid_float_complex)


// 
// Symbol timing recovery (symbol synchronizer)
//
#define LIQUID_SYMSYNC_MANGLE_RRRF(name) LIQUID_CONCAT(symsync_rrrf,name)
#define LIQUID_SYMSYNC_MANGLE_CRCF(name) LIQUID_CONCAT(symsync_crcf,name)

#define LIQUID_SYMSYNC_DEFINE_API(SYMSYNC,TO,TC,TI)             \
                                                                \
typedef struct SYMSYNC(_s) * SYMSYNC();                         \
                                                                \
/* create synchronizer object from external coefficients    */  \
/*  _k      : samples per symbol                            */  \
/*  _M      : number of filters in the bank                 */  \
/*  _h      : matched filter coefficients [size:            */  \
/*  _h_len  : length of matched filter                      */  \
SYMSYNC() SYMSYNC(_create)(unsigned int _k,                     \
                           unsigned int _M,                     \
                           TC *         _h,                     \
                           unsigned int _h_len);                \
                                                                \
/* create square-root Nyquist symbol synchronizer           */  \
/*  _type   : filter type (e.g. LIQUID_FIRFILT_RRC)         */  \
/*  _k      : samples/symbol                                */  \
/*  _m      : symbol delay                                  */  \
/*  _beta   : rolloff factor, beta in (0,1]                 */  \
/*  _M      : number of filters in the bank                 */  \
SYMSYNC() SYMSYNC(_create_rnyquist)(int          _type,         \
                                    unsigned int _k,            \
                                    unsigned int _m,            \
                                    float        _beta,         \
                                    unsigned int _M);           \
                                                                \
/* create symsync using Kaiser filter interpolator; useful  */  \
/* when the input signal has matched filter applied already */  \
/*  _k      : input samples/symbol                          */  \
/*  _m      : symbol delay                                  */  \
/*  _beta   : rolloff factor, beta in (0,1]                 */  \
/*  _M      : number of filters in the bank                 */  \
SYMSYNC() SYMSYNC(_create_kaiser)(unsigned int _k,              \
                                  unsigned int _m,              \
                                  float        _beta,           \
                                  unsigned int _M);             \
                                                                \
/* destroy symsync object, freeing all internal memory      */  \
void SYMSYNC(_destroy)(SYMSYNC() _q);                           \
                                                                \
/* print symsync object's parameters                        */  \
void SYMSYNC(_print)(SYMSYNC() _q);                             \
                                                                \
/* reset symsync internal state                             */  \
void SYMSYNC(_reset)(SYMSYNC() _q);                             \
                                                                \
/* lock/unlock loop control                                 */  \
void SYMSYNC(_lock)(  SYMSYNC() _q);                            \
void SYMSYNC(_unlock)(SYMSYNC() _q);                            \
                                                                \
/* set synchronizer output rate (samples/symbol)            */  \
/*  _q      : synchronizer object                           */  \
/*  _k_out  : output samples/symbol                         */  \
void SYMSYNC(_set_output_rate)(SYMSYNC()    _q,                 \
                               unsigned int _k_out);            \
                                                                \
/* set loop-filter bandwidth                                */  \
/*  _q      : synchronizer object                           */  \
/*  _bt     : loop bandwidth                                */  \
void SYMSYNC(_set_lf_bw)(SYMSYNC() _q,                          \
                         float     _bt);                        \
                                                                \
/* return instantaneous fractional timing offset estimate   */  \
float SYMSYNC(_get_tau)(SYMSYNC() _q);                          \
                                                                \
/* execute synchronizer on input data array                 */  \
/*  _q      : synchronizer object                           */  \
/*  _x      : input data array                              */  \
/*  _nx     : number of input samples                       */  \
/*  _y      : output data array                             */  \
/*  _ny     : number of samples written to output buffer    */  \
void SYMSYNC(_execute)(SYMSYNC()      _q,                       \
                       TI *           _x,                       \
                       unsigned int   _nx,                      \
                       TO *           _y,                       \
                       unsigned int * _ny);                     \

LIQUID_SYMSYNC_DEFINE_API(LIQUID_SYMSYNC_MANGLE_RRRF,
                          float,
                          float,
                          float)

LIQUID_SYMSYNC_DEFINE_API(LIQUID_SYMSYNC_MANGLE_CRCF,
                          liquid_float_complex,
                          float,
                          liquid_float_complex)


//
// Finite impulse response Farrow filter
//

#define LIQUID_FIRFARROW_MANGLE_RRRF(name) LIQUID_CONCAT(firfarrow_rrrf,name)
#define LIQUID_FIRFARROW_MANGLE_CRCF(name) LIQUID_CONCAT(firfarrow_crcf,name)
//#define LIQUID_FIRFARROW_MANGLE_CCCF(name) LIQUID_CONCAT(firfarrow_cccf,name)

// Macro:
//   FIRFARROW  : name-mangling macro
//   TO         : output data type
//   TC         : coefficients data type
//   TI         : input data type
#define LIQUID_FIRFARROW_DEFINE_API(FIRFARROW,TO,TC,TI)         \
                                                                \
typedef struct FIRFARROW(_s) * FIRFARROW();                     \
                                                                \
/* create firfarrow object                                  */  \
/*  _h_len      : filter length                             */  \
/*  _p          : polynomial order                          */  \
/*  _fc         : filter cutoff frequency                   */  \
/*  _As         : stopband attenuation [dB]                 */  \
FIRFARROW() FIRFARROW(_create)(unsigned int _h_len,             \
                               unsigned int _p,                 \
                               float        _fc,                \
                               float        _As);               \
                                                                \
/* destroy firfarrow object, freeing all internal memory    */  \
void FIRFARROW(_destroy)(FIRFARROW() _q);                       \
                                                                \
/* print firfarrow object's internal properties             */  \
void FIRFARROW(_print)(FIRFARROW() _q);                         \
                                                                \
/* reset firfarrow object's internal state                  */  \
void FIRFARROW(_reset)(FIRFARROW() _q);                         \
                                                                \
/* push sample into firfarrow object                        */  \
/*  _q      : firfarrow object                              */  \
/*  _x      : input sample                                  */  \
void FIRFARROW(_push)(FIRFARROW() _q,                           \
                      TI          _x);                          \
                                                                \
/* set fractional delay of firfarrow object                 */  \
/*  _q      : firfarrow object                              */  \
/*  _mu     : fractional sample delay                       */  \
void FIRFARROW(_set_delay)(FIRFARROW() _q,                      \
                           float       _mu);                    \
                                                                \
/* execute firfarrow internal dot product                   */  \
/*  _q      : firfarrow object                              */  \
/*  _y      : output sample pointer                         */  \
void FIRFARROW(_execute)(FIRFARROW() _q,                        \
                         TO *        _y);                       \
                                                                \
/* compute firfarrow filter on block of samples; the input  */  \
/* and output arrays may have the same pointer              */  \
/*  _q      : firfarrow object                              */  \
/*  _x      : input array [size: _n x 1]                    */  \
/*  _n      : input, output array size                      */  \
/*  _y      : output array [size: _n x 1]                   */  \
void FIRFARROW(_execute_block)(FIRFARROW()  _q,                 \
                               TI *         _x,                 \
                               unsigned int _n,                 \
                               TO *         _y);                \
                                                                \
/* get length of firfarrow object (number of filter taps)   */  \
unsigned int FIRFARROW(_get_length)(FIRFARROW() _q);            \
                                                                \
/* get coefficients of firfarrow object                     */  \
/*  _q      : firfarrow object                              */  \
/*  _h      : output coefficients pointer                   */  \
void FIRFARROW(_get_coefficients)(FIRFARROW() _q,               \
                                  float *     _h);              \
                                                                \
/* compute complex frequency response                       */  \
/*  _q      : filter object                                 */  \
/*  _fc     : frequency                                     */  \
/*  _H      : output frequency response                     */  \
void FIRFARROW(_freqresponse)(FIRFARROW()            _q,        \
                              float                  _fc,       \
                              liquid_float_complex * _H);       \
                                                                \
/* compute group delay [samples]                            */  \
/*  _q      :   filter object                               */  \
/*  _fc     :   frequency                                   */  \
float FIRFARROW(_groupdelay)(FIRFARROW() _q,                    \
                             float       _fc);                  \

LIQUID_FIRFARROW_DEFINE_API(LIQUID_FIRFARROW_MANGLE_RRRF,
                            float,
                            float,
                            float)

LIQUID_FIRFARROW_DEFINE_API(LIQUID_FIRFARROW_MANGLE_CRCF,
                            liquid_float_complex,
                            float,
                            liquid_float_complex)



//
// MODULE : framing
//

// framesyncstats : generic frame synchronizer statistic structure

typedef struct {
    // signal quality
    float evm;      // error vector magnitude [dB]
    float rssi;     // received signal strength indicator [dB]
    float cfo;      // carrier frequency offset (f/Fs)

    // demodulated frame symbols
    liquid_float_complex * framesyms;   // pointer to array [size: framesyms x 1]
    unsigned int num_framesyms;         // length of framesyms

    // modulation/coding scheme etc.
    unsigned int mod_scheme;    // modulation scheme
    unsigned int mod_bps;       // modulation depth (bits/symbol)
    unsigned int check;         // data validity check (crc, checksum)
    unsigned int fec0;          // forward error-correction (inner)
    unsigned int fec1;          // forward error-correction (outer)
} framesyncstats_s;

// external framesyncstats default object
extern framesyncstats_s framesyncstats_default;

// initialize framesyncstats object on default
void framesyncstats_init_default(framesyncstats_s * _stats);

// print framesyncstats object
void framesyncstats_print(framesyncstats_s * _stats);


// framedatastats : gather frame data
typedef struct {
    unsigned int      num_frames_detected;
    unsigned int      num_headers_valid;
    unsigned int      num_payloads_valid;
    unsigned long int num_bytes_received;
} framedatastats_s;

// reset framedatastats object
void framedatastats_reset(framedatastats_s * _stats);

// print framedatastats object
void framedatastats_print(framedatastats_s * _stats);


// Generic frame synchronizer callback function type
//  _header         :   header data [size: 8 bytes]
//  _header_valid   :   is header valid? (0:no, 1:yes)
//  _payload        :   payload data [size: _payload_len]
//  _payload_len    :   length of payload (bytes)
//  _payload_valid  :   is payload valid? (0:no, 1:yes)
//  _stats          :   frame statistics object
//  _userdata       :   pointer to userdata
typedef int (*framesync_callback)(unsigned char *  _header,
                                  int              _header_valid,
                                  unsigned char *  _payload,
                                  unsigned int     _payload_len,
                                  int              _payload_valid,
                                  framesyncstats_s _stats,
                                  void *           _userdata);

// framesync csma callback functions invoked when signal levels is high or low
//  _userdata       :   user-defined data pointer
typedef void (*framesync_csma_callback)(void * _userdata);

//
// packet encoder/decoder
//

typedef struct qpacketmodem_s * qpacketmodem;

// create packet encoder
qpacketmodem qpacketmodem_create ();
void         qpacketmodem_destroy(qpacketmodem _q);
void         qpacketmodem_reset  (qpacketmodem _q);
void         qpacketmodem_print  (qpacketmodem _q);

int qpacketmodem_configure(qpacketmodem _q,
                           unsigned int _payload_len,
                           crc_scheme   _check,
                           fec_scheme   _fec0,
                           fec_scheme   _fec1,
                           int          _ms);

// get length of encoded frame in symbols
unsigned int qpacketmodem_get_frame_len(qpacketmodem _q);

// get unencoded/decoded payload length (bytes)
unsigned int qpacketmodem_get_payload_len(qpacketmodem _q);

// regular access methods
unsigned int qpacketmodem_get_crc      (qpacketmodem _q);
unsigned int qpacketmodem_get_fec0     (qpacketmodem _q);
unsigned int qpacketmodem_get_fec1     (qpacketmodem _q);
unsigned int qpacketmodem_get_modscheme(qpacketmodem _q);

// encode packet into un-modulated frame symbol indices
//  _q          :   qpacketmodem object
//  _payload    :   unencoded payload bytes
//  _syms       :   encoded but un-modulated payload symbol indices
void qpacketmodem_encode_syms(qpacketmodem          _q,
                              const unsigned char * _payload,
                              unsigned char *       _syms);

// decode packet from demodulated frame symbol indices (hard-decision decoding)
//  _q          :   qpacketmodem object
//  _syms       :   received hard-decision symbol indices [size: frame_len x 1]
//  _payload    :   recovered decoded payload bytes
int qpacketmodem_decode_syms(qpacketmodem    _q,
                             unsigned char * _syms,
                             unsigned char * _payload);

// decode packet from demodulated frame bits (soft-decision decoding)
//  _q          :   qpacketmodem object
//  _bits       :   received soft-decision bits, [size: bps*frame_len x 1]
//  _payload    :   recovered decoded payload bytes
int qpacketmodem_decode_bits(qpacketmodem    _q,
                             unsigned char * _bits,
                             unsigned char * _payload);

// encode and modulate packet into modulated frame samples
//  _q          :   qpacketmodem object
//  _payload    :   unencoded payload bytes
//  _frame      :   encoded/modulated payload symbols
void qpacketmodem_encode(qpacketmodem           _q,
                         const unsigned char *  _payload,
                         liquid_float_complex * _frame);

// decode packet from modulated frame samples, returning flag if CRC passed
// NOTE: hard-decision decoding
//  _q          :   qpacketmodem object
//  _frame      :   encoded/modulated payload symbols
//  _payload    :   recovered decoded payload bytes
int qpacketmodem_decode(qpacketmodem           _q,
                        liquid_float_complex * _frame,
                        unsigned char *        _payload);

// decode packet from modulated frame samples, returning flag if CRC passed
// NOTE: soft-decision decoding
//  _q          :   qpacketmodem object
//  _frame      :   encoded/modulated payload symbols
//  _payload    :   recovered decoded payload bytes
int qpacketmodem_decode_soft(qpacketmodem           _q,
                             liquid_float_complex * _frame,
                             unsigned char *        _payload);

//
// pilot generator for streaming applications
//
typedef struct qpilotgen_s * qpilotgen;

// create packet encoder
qpilotgen qpilotgen_create(unsigned int _payload_len,
                           unsigned int _pilot_spacing);

qpilotgen qpilotgen_recreate(qpilotgen    _q,
                             unsigned int _payload_len,
                             unsigned int _pilot_spacing);

void qpilotgen_destroy(qpilotgen _q);
void qpilotgen_reset(  qpilotgen _q);
void qpilotgen_print(  qpilotgen _q);

unsigned int qpilotgen_get_frame_len(qpilotgen _q);

// insert pilot symbols
void qpilotgen_execute(qpilotgen              _q,
                       liquid_float_complex * _payload,
                       liquid_float_complex * _frame);

//
// pilot synchronizer for streaming applications
//
typedef struct qpilotsync_s * qpilotsync;

// create packet encoder
qpilotsync qpilotsync_create(unsigned int _payload_len,
                             unsigned int _pilot_spacing);

qpilotsync qpilotsync_recreate(qpilotsync   _q,
                               unsigned int _payload_len,
                               unsigned int _pilot_spacing);

void qpilotsync_destroy(qpilotsync _q);
void qpilotsync_reset(  qpilotsync _q);
void qpilotsync_print(  qpilotsync _q);

unsigned int qpilotsync_get_frame_len(qpilotsync _q);

// recover frame symbols from received frame
void qpilotsync_execute(qpilotsync             _q,
                        liquid_float_complex * _frame,
                        liquid_float_complex * _payload);

// get estimates
float qpilotsync_get_dphi(qpilotsync _q);
float qpilotsync_get_phi (qpilotsync _q);
float qpilotsync_get_gain(qpilotsync _q);


//
// Basic frame generator (64 bytes data payload)
//

// frame length in samples
#define LIQUID_FRAME64_LEN (1440)

typedef struct framegen64_s * framegen64;

// create frame generator
framegen64 framegen64_create();

// destroy frame generator
void framegen64_destroy(framegen64 _q);

// print frame generator internal properties
void framegen64_print(framegen64 _q);

// generate frame
//  _q          :   frame generator object
//  _header     :   8-byte header data
//  _payload    :   64-byte payload data
//  _frame      :   output frame samples [size: LIQUID_FRAME64_LEN x 1]
void framegen64_execute(framegen64             _q,
                        unsigned char *        _header,
                        unsigned char *        _payload,
                        liquid_float_complex * _frame);

typedef struct framesync64_s * framesync64;

// create framesync64 object
//  _callback   :   callback function
//  _userdata   :   user data pointer passed to callback function
framesync64 framesync64_create(framesync_callback _callback,
                               void *             _userdata);

// destroy frame synchronizer
void framesync64_destroy(framesync64 _q);

// print frame synchronizer internal properties
void framesync64_print(framesync64 _q);

// reset frame synchronizer internal state
void framesync64_reset(framesync64 _q);

// push samples through frame synchronizer
//  _q      :   frame synchronizer object
//  _x      :   input samples [size: _n x 1]
//  _n      :   number of input samples
void framesync64_execute(framesync64            _q,
                         liquid_float_complex * _x,
                         unsigned int           _n);

// enable/disable debugging
void framesync64_debug_enable(framesync64 _q);
void framesync64_debug_disable(framesync64 _q);
void framesync64_debug_print(framesync64 _q, const char * _filename);

#if 0
// advanced modes
void framesync64_set_csma_callbacks(framesync64             _q,
                                    framesync_csma_callback _csma_lock,
                                    framesync_csma_callback _csma_unlock,
                                    void *                  _csma_userdata);
#endif

//
// Flexible frame : adjustable payload, mod scheme, etc., but bring
//                  your own error correction, redundancy check
//

// frame generator
typedef struct {
    unsigned int check;         // data validity check
    unsigned int fec0;          // forward error-correction scheme (inner)
    unsigned int fec1;          // forward error-correction scheme (outer)
    unsigned int mod_scheme;    // modulation scheme
} flexframegenprops_s;

void flexframegenprops_init_default(flexframegenprops_s * _fgprops);

typedef struct flexframegen_s * flexframegen;

// create flexframegen object
//  _props  :   frame properties (modulation scheme, etc.)
flexframegen flexframegen_create(flexframegenprops_s * _props);

// destroy flexframegen object
void flexframegen_destroy(flexframegen _q);

// print flexframegen object internals
void flexframegen_print(flexframegen _q);

// reset flexframegen object internals
void flexframegen_reset(flexframegen _q);

// is frame assembled?
int flexframegen_is_assembled(flexframegen _q);

// get frame properties
void flexframegen_getprops(flexframegen _q, flexframegenprops_s * _props);

// set frame properties
int flexframegen_setprops(flexframegen _q, flexframegenprops_s * _props);

// get length of assembled frame (samples)
unsigned int flexframegen_getframelen(flexframegen _q);

// assemble a frame from an array of data
//  _q              :   frame generator object
//  _header         :   frame header
//  _payload        :   payload data [size: _payload_len x 1]
//  _payload_len    :   payload data length
void flexframegen_assemble(flexframegen          _q,
                           const unsigned char * _header,
                           const unsigned char * _payload,
                           unsigned int          _payload_len);

// write samples of assembled frame, two samples at a time, returning
// '1' when frame is complete, '0' otherwise. Zeros will be written
// to the buffer if the frame is not assembled
//  _q          :   frame generator object
//  _buffer     :   output buffer [size: _buffer_len x 1]
//  _buffer_len :   output buffer length
int flexframegen_write_samples(flexframegen           _q,
                               liquid_float_complex * _buffer,
                               unsigned int           _buffer_len);

// frame synchronizer

typedef struct flexframesync_s * flexframesync;

// create flexframesync object
//  _callback   :   callback function
//  _userdata   :   user data pointer passed to callback function
flexframesync flexframesync_create(framesync_callback _callback,
                                   void *             _userdata);

// destroy frame synchronizer
void flexframesync_destroy(flexframesync _q);

// print frame synchronizer internal properties
void flexframesync_print(flexframesync _q);

// reset frame synchronizer internal state
void flexframesync_reset(flexframesync _q);

// has frame been detected?
int flexframesync_is_frame_open(flexframesync _q);

// push samples through frame synchronizer
//  _q      :   frame synchronizer object
//  _x      :   input samples [size: _n x 1]
//  _n      :   number of input samples
void flexframesync_execute(flexframesync          _q,
                           liquid_float_complex * _x,
                           unsigned int           _n);

// frame data statistics
void             flexframesync_reset_framedatastats(flexframesync _q);
framedatastats_s flexframesync_get_framedatastats  (flexframesync _q);

// enable/disable debugging
void flexframesync_debug_enable(flexframesync _q);
void flexframesync_debug_disable(flexframesync _q);
void flexframesync_debug_print(flexframesync _q,
                               const char *  _filename);

//
// bpacket : binary packet suitable for data streaming
//

// 
// bpacket generator/encoder
//
typedef struct bpacketgen_s * bpacketgen;

// create bpacketgen object
//  _m              :   p/n sequence length (ignored)
//  _dec_msg_len    :   decoded message length (original uncoded data)
//  _crc            :   data validity check (e.g. cyclic redundancy check)
//  _fec0           :   inner forward error-correction code scheme
//  _fec1           :   outer forward error-correction code scheme
bpacketgen bpacketgen_create(unsigned int _m,
                             unsigned int _dec_msg_len,
                             int _crc,
                             int _fec0,
                             int _fec1);

// re-create bpacketgen object from old object
//  _q              :   old bpacketgen object
//  _m              :   p/n sequence length (ignored)
//  _dec_msg_len    :   decoded message length (original uncoded data)
//  _crc            :   data validity check (e.g. cyclic redundancy check)
//  _fec0           :   inner forward error-correction code scheme
//  _fec1           :   outer forward error-correction code scheme
bpacketgen bpacketgen_recreate(bpacketgen _q,
                               unsigned int _m,
                               unsigned int _dec_msg_len,
                               int _crc,
                               int _fec0,
                               int _fec1);

// destroy bpacketgen object, freeing all internally-allocated memory
void bpacketgen_destroy(bpacketgen _q);

// print bpacketgen internals
void bpacketgen_print(bpacketgen _q);

// return length of full packet
unsigned int bpacketgen_get_packet_len(bpacketgen _q);

// encode packet
void bpacketgen_encode(bpacketgen _q,
                       unsigned char * _msg_dec,
                       unsigned char * _packet);

// 
// bpacket synchronizer/decoder
//
typedef struct bpacketsync_s * bpacketsync;
typedef int (*bpacketsync_callback)(unsigned char *  _payload,
                                    int              _payload_valid,
                                    unsigned int     _payload_len,
                                    framesyncstats_s _stats,
                                    void *           _userdata);
bpacketsync bpacketsync_create(unsigned int _m,
                               bpacketsync_callback _callback,
                               void * _userdata);
void bpacketsync_destroy(bpacketsync _q);
void bpacketsync_print(bpacketsync _q);
void bpacketsync_reset(bpacketsync _q);

// run synchronizer on array of input bytes
//  _q      :   bpacketsync object
//  _bytes  :   input data array [size: _n x 1]
//  _n      :   input array size
void bpacketsync_execute(bpacketsync _q,
                         unsigned char * _bytes,
                         unsigned int _n);

// run synchronizer on input byte
//  _q      :   bpacketsync object
//  _byte   :   input byte
void bpacketsync_execute_byte(bpacketsync _q,
                              unsigned char _byte);

// run synchronizer on input symbol
//  _q      :   bpacketsync object
//  _sym    :   input symbol with _bps significant bits
//  _bps    :   number of bits in input symbol
void bpacketsync_execute_sym(bpacketsync _q,
                             unsigned char _sym,
                             unsigned int _bps);

// execute one bit at a time
void bpacketsync_execute_bit(bpacketsync _q,
                             unsigned char _bit);

//
// GMSK frame generator
//

typedef struct gmskframegen_s * gmskframegen;

// create GMSK frame generator
gmskframegen gmskframegen_create();
void gmskframegen_destroy       (gmskframegen _q);
int  gmskframegen_is_assembled  (gmskframegen _q);
void gmskframegen_print         (gmskframegen _q);
void gmskframegen_reset         (gmskframegen _q);
void gmskframegen_assemble      (gmskframegen          _q,
                                 const unsigned char * _header,
                                 const unsigned char * _payload,
                                 unsigned int          _payload_len,
                                 crc_scheme            _check,
                                 fec_scheme            _fec0,
                                 fec_scheme            _fec1);
unsigned int gmskframegen_getframelen(gmskframegen _q);
int gmskframegen_write_samples(gmskframegen _q,
                               liquid_float_complex * _y);


//
// GMSK frame synchronizer
//

typedef struct gmskframesync_s * gmskframesync;

// create GMSK frame synchronizer
//  _callback   :   callback function
//  _userdata   :   user data pointer passed to callback function
gmskframesync gmskframesync_create(framesync_callback _callback,
                                   void *             _userdata);
void gmskframesync_destroy(gmskframesync _q);
void gmskframesync_print(gmskframesync _q);
void gmskframesync_reset(gmskframesync _q);
int  gmskframesync_is_frame_open(gmskframesync _q);
void gmskframesync_execute(gmskframesync _q,
                           liquid_float_complex * _x,
                           unsigned int _n);

// debugging
void gmskframesync_debug_enable(gmskframesync _q);
void gmskframesync_debug_disable(gmskframesync _q);
void gmskframesync_debug_print(gmskframesync _q, const char * _filename);



// 
// OFDM flexframe generator
//

// ofdm frame generator properties
typedef struct {
    unsigned int check;         // data validity check
    unsigned int fec0;          // forward error-correction scheme (inner)
    unsigned int fec1;          // forward error-correction scheme (outer)
    unsigned int mod_scheme;    // modulation scheme
    //unsigned int block_size;  // framing block size
} ofdmflexframegenprops_s;
void ofdmflexframegenprops_init_default(ofdmflexframegenprops_s * _props);

typedef struct ofdmflexframegen_s * ofdmflexframegen;

// create OFDM flexible framing generator object
//  _M          :   number of subcarriers, >10 typical
//  _cp_len     :   cyclic prefix length
//  _taper_len  :   taper length (OFDM symbol overlap)
//  _p          :   subcarrier allocation (null, pilot, data), [size: _M x 1]
//  _fgprops    :   frame properties (modulation scheme, etc.)
ofdmflexframegen ofdmflexframegen_create(unsigned int              _M,
                                         unsigned int              _cp_len,
                                         unsigned int              _taper_len,
                                         unsigned char *           _p,
                                         ofdmflexframegenprops_s * _fgprops);

// destroy ofdmflexframegen object
void ofdmflexframegen_destroy(ofdmflexframegen _q);

// print parameters, properties, etc.
void ofdmflexframegen_print(ofdmflexframegen _q);

// reset ofdmflexframegen object internals
void ofdmflexframegen_reset(ofdmflexframegen _q);

// is frame assembled?
int ofdmflexframegen_is_assembled(ofdmflexframegen _q);

// get properties
void ofdmflexframegen_getprops(ofdmflexframegen _q,
                               ofdmflexframegenprops_s * _props);

// set properties
void ofdmflexframegen_setprops(ofdmflexframegen _q,
                               ofdmflexframegenprops_s * _props);

// get length of frame (symbols)
//  _q              :   OFDM frame generator object
unsigned int ofdmflexframegen_getframelen(ofdmflexframegen _q);

// assemble a frame from an array of data (NULL pointers will use random data)
//  _q              :   OFDM frame generator object
//  _header         :   frame header [8 bytes]
//  _payload        :   payload data [size: _payload_len x 1]
//  _payload_len    :   payload data length
void ofdmflexframegen_assemble(ofdmflexframegen      _q,
                               const unsigned char * _header,
                               const unsigned char * _payload,
                               unsigned int          _payload_len);

// write samples of assembled frame
//  _q              :   OFDM frame generator object
//  _buf            :   output buffer [size: _buf_len x 1]
//  _buf_len        :   output buffer length
int ofdmflexframegen_write(ofdmflexframegen       _q,
                          liquid_float_complex * _buf,
                          unsigned int           _buf_len);

// 
// OFDM flex frame synchronizer
//

typedef struct ofdmflexframesync_s * ofdmflexframesync;

// create OFDM flexible framing synchronizer object
//  _M          :   number of subcarriers
//  _cp_len     :   cyclic prefix length
//  _taper_len  :   taper length (OFDM symbol overlap)
//  _p          :   subcarrier allocation (null, pilot, data), [size: _M x 1]
//  _callback   :   user-defined callback function
//  _userdata   :   user-defined data pointer
ofdmflexframesync ofdmflexframesync_create(unsigned int       _M,
                                           unsigned int       _cp_len,
                                           unsigned int       _taper_len,
                                           unsigned char *    _p,
                                           framesync_callback _callback,
                                           void *             _userdata);

void ofdmflexframesync_destroy(ofdmflexframesync _q);
void ofdmflexframesync_print(ofdmflexframesync _q);
void ofdmflexframesync_reset(ofdmflexframesync _q);
int  ofdmflexframesync_is_frame_open(ofdmflexframesync _q);
void ofdmflexframesync_execute(ofdmflexframesync _q,
                               liquid_float_complex * _x,
                               unsigned int _n);

// query the received signal strength indication
float ofdmflexframesync_get_rssi(ofdmflexframesync _q);

// query the received carrier offset estimate
float ofdmflexframesync_get_cfo(ofdmflexframesync _q);

// enable/disable debugging
void ofdmflexframesync_debug_enable(ofdmflexframesync _q);
void ofdmflexframesync_debug_disable(ofdmflexframesync _q);
void ofdmflexframesync_debug_print(ofdmflexframesync _q,
                                   const char *      _filename);



//
// Binary P/N synchronizer
//
#define LIQUID_BSYNC_MANGLE_RRRF(name) LIQUID_CONCAT(bsync_rrrf,name)
#define LIQUID_BSYNC_MANGLE_CRCF(name) LIQUID_CONCAT(bsync_crcf,name)
#define LIQUID_BSYNC_MANGLE_CCCF(name) LIQUID_CONCAT(bsync_cccf,name)

// Macro:
//   BSYNC  : name-mangling macro
//   TO     : output data type
//   TC     : coefficients data type
//   TI     : input data type
#define LIQUID_BSYNC_DEFINE_API(BSYNC,TO,TC,TI)                 \
typedef struct BSYNC(_s) * BSYNC();                             \
                                                                \
BSYNC() BSYNC(_create)(unsigned int _n, TC * _v);               \
                                                                \
/* create binary synchronizer from m-sequence               */  \
/*  _g      :   m-sequence generator polynomial             */  \
/*  _k      :   samples/symbol (over-sampling factor)       */  \
BSYNC() BSYNC(_create_msequence)(unsigned int _g,               \
                                 unsigned int _k);              \
void BSYNC(_destroy)(BSYNC() _fs);                              \
void BSYNC(_print)(BSYNC() _fs);                                \
void BSYNC(_correlate)(BSYNC() _fs, TI _sym, TO * _y);

LIQUID_BSYNC_DEFINE_API(LIQUID_BSYNC_MANGLE_RRRF,
                        float,
                        float,
                        float)

LIQUID_BSYNC_DEFINE_API(LIQUID_BSYNC_MANGLE_CRCF,
                        liquid_float_complex,
                        float,
                        liquid_float_complex)

LIQUID_BSYNC_DEFINE_API(LIQUID_BSYNC_MANGLE_CCCF,
                        liquid_float_complex,
                        liquid_float_complex,
                        liquid_float_complex)


//
// Pre-demodulation synchronizers (binary and otherwise)
//
#define  LIQUID_PRESYNC_MANGLE_CCCF(name) LIQUID_CONCAT( presync_cccf,name)
#define LIQUID_BPRESYNC_MANGLE_CCCF(name) LIQUID_CONCAT(bpresync_cccf,name)

// Macro:
//   PRESYNC   : name-mangling macro
//   TO         : output data type
//   TC         : coefficients data type
//   TI         : input data type
#define LIQUID_PRESYNC_DEFINE_API(PRESYNC,TO,TC,TI)             \
typedef struct PRESYNC(_s) * PRESYNC();                         \
                                                                \
/* create pre-demod synchronizer                            */  \
/*  _v          :   baseband sequence                       */  \
/*  _n          :   baseband sequence length                */  \
/*  _dphi_max   :   maximum absolute frequency deviation    */  \
/*  _m          :   number of correlators                   */  \
PRESYNC() PRESYNC(_create)(TC *         _v,                     \
                           unsigned int _n,                     \
                           float        _dphi_max,              \
                           unsigned int _m);                    \
                                                                \
/* destroy pre-demod synchronizer                           */  \
void PRESYNC(_destroy)(PRESYNC() _q);                           \
                                                                \
/* print pre-demod synchronizer internal state              */  \
void PRESYNC(_print)(PRESYNC() _q);                             \
                                                                \
/* reset pre-demod synchronizer internal state              */  \
void PRESYNC(_reset)(PRESYNC() _q);                             \
                                                                \
/* push input sample into pre-demod synchronizer            */  \
/*  _q          :   pre-demod synchronizer object           */  \
/*  _x          :   input sample                            */  \
void PRESYNC(_push)(PRESYNC() _q,                               \
                    TI        _x);                              \
                                                                \
/* correlate input sequence                                 */  \
/*  _q          :   pre-demod synchronizer object           */  \
/*  _rxy        :   output cross correlation                */  \
/*  _dphi_hat   :   output frequency offset estiamte        */  \
void PRESYNC(_correlate)(PRESYNC() _q,                          \
                         TO *      _rxy,                        \
                         float *   _dphi_hat);                  \

// non-binary pre-demodulation synchronizer
LIQUID_PRESYNC_DEFINE_API(LIQUID_PRESYNC_MANGLE_CCCF,
                          liquid_float_complex,
                          liquid_float_complex,
                          liquid_float_complex)

// binary pre-demodulation synchronizer
LIQUID_PRESYNC_DEFINE_API(LIQUID_BPRESYNC_MANGLE_CCCF,
                          liquid_float_complex,
                          liquid_float_complex,
                          liquid_float_complex)

//
// Frame detector
//

typedef struct qdetector_cccf_s * qdetector_cccf;

// create detector with generic sequence
//  _s      :   sample sequence
//  _s_len  :   length of sample sequence
qdetector_cccf qdetector_cccf_create(liquid_float_complex * _s,
                                     unsigned int           _s_len);

// create detector from sequence of symbols using internal linear interpolator
//  _sequence       :   symbol sequence
//  _sequence_len   :   length of symbol sequence
//  _ftype          :   filter prototype (e.g. LIQUID_FIRFILT_RRC)
//  _k              :   samples/symbol
//  _m              :   filter delay
//  _beta           :   excess bandwidth factor
qdetector_cccf qdetector_cccf_create_linear(liquid_float_complex * _sequence,
                                            unsigned int           _sequence_len,
                                            int                    _ftype,
                                            unsigned int           _k,
                                            unsigned int           _m,
                                            float                  _beta);

// create detector from sequence of GMSK symbols
//  _sequence       :   bit sequence
//  _sequence_len   :   length of bit sequence
//  _k              :   samples/symbol
//  _m              :   filter delay
//  _beta           :   excess bandwidth factor
qdetector_cccf qdetector_cccf_create_gmsk(unsigned char * _sequence,
                                          unsigned int    _sequence_len,
                                          unsigned int    _k,
                                          unsigned int    _m,
                                          float           _beta);

void qdetector_cccf_destroy(qdetector_cccf _q);
void qdetector_cccf_print  (qdetector_cccf _q);
void qdetector_cccf_reset  (qdetector_cccf _q);

// run detector, looking for sequence; return pointer to aligned, buffered samples
void * qdetector_cccf_execute(qdetector_cccf       _q,
                              liquid_float_complex _x);

// set detection threshold (should be between 0 and 1, good starting point is 0.5)
void qdetector_cccf_set_threshold(qdetector_cccf _q,
                                  float          _threshold);

// set carrier offset search range
void qdetector_cccf_set_range(qdetector_cccf _q,
                              float          _dphi_max);

// access methods
unsigned int qdetector_cccf_get_seq_len (qdetector_cccf _q); // sequence length
const void * qdetector_cccf_get_sequence(qdetector_cccf _q); // pointer to sequence
unsigned int qdetector_cccf_get_buf_len (qdetector_cccf _q); // buffer length
float        qdetector_cccf_get_tau     (qdetector_cccf _q); // fractional timing offset estimate
float        qdetector_cccf_get_gamma   (qdetector_cccf _q); // channel gain
float        qdetector_cccf_get_dphi    (qdetector_cccf _q); // carrier frequency offset estimate
float        qdetector_cccf_get_phi     (qdetector_cccf _q); // carrier phase offset estimate

//
// Pre-demodulation detector
//

typedef struct detector_cccf_s * detector_cccf;

// create pre-demod detector
//  _s          :   sequence
//  _n          :   sequence length
//  _threshold  :   detection threshold (default: 0.7)
//  _dphi_max   :   maximum carrier offset
detector_cccf detector_cccf_create(liquid_float_complex * _s,
                                   unsigned int           _n,
                                   float                  _threshold,
                                   float                  _dphi_max);

// destroy pre-demo detector object
void detector_cccf_destroy(detector_cccf _q);

// print pre-demod detector internal state
void detector_cccf_print(detector_cccf _q);

// reset pre-demod detector internal state
void detector_cccf_reset(detector_cccf _q);

// Run sample through pre-demod detector's correlator.
// Returns '1' if signal was detected, '0' otherwise
//  _q          :   pre-demod detector
//  _x          :   input sample
//  _tau_hat    :   fractional sample offset estimate (set when detected)
//  _dphi_hat   :   carrier frequency offset estimate (set when detected)
//  _gamma_hat  :   channel gain estimate (set when detected)
int detector_cccf_correlate(detector_cccf        _q,
                            liquid_float_complex _x,
                            float *              _tau_hat,
                            float *              _dphi_hat,
                            float *              _gamma_hat);


// 
// symbol streaming for testing (no meaningful data, just symbols)
//
#define LIQUID_SYMSTREAM_MANGLE_CFLOAT(name) LIQUID_CONCAT(symstreamcf,name)

#define LIQUID_SYMSTREAM_DEFINE_API(SYMSTREAM,TO)               \
                                                                \
typedef struct SYMSTREAM(_s) * SYMSTREAM();                     \
                                                                \
/* create default symstream object                          */  \
/* (LIQUID_RNYQUIST_ARKAISER, k=2, m=7, beta=0.3, QPSK)     */  \
SYMSTREAM() SYMSTREAM(_create)(void);                           \
                                                                \
/* create symstream object with linear modulation           */  \
/*  _ftype  : filter type (e.g. LIQUID_RNYQUIST_RRC)        */  \
/*  _k      : samples per symbol                            */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _beta   : filter excess bandwidth                       */  \
/*  _ms     : modulation scheme (e.g. LIQUID_MODEM_QPSK)    */  \
SYMSTREAM() SYMSTREAM(_create_linear)(int          _ftype,      \
                                      unsigned int _k,          \
                                      unsigned int _m,          \
                                      float        _beta,       \
                                      int          _ms);        \
                                                                \
/* destroy symstream object, freeing all internal memory    */  \
void SYMSTREAM(_destroy)(SYMSTREAM() _q);                       \
                                                                \
/* print symstream object's parameters                      */  \
void SYMSTREAM(_print)(SYMSTREAM() _q);                         \
                                                                \
/* reset symstream internal state                           */  \
void SYMSTREAM(_reset)(SYMSTREAM() _q);                         \
                                                                \
/* write block of samples to output buffer                  */  \
/*  _q      : synchronizer object                           */  \
/*  _buf    : output buffer [size: _buf_len x 1]            */  \
/*  _buf_len: output buffer size                            */  \
void SYMSTREAM(_write_samples)(SYMSTREAM()  _q,                 \
                               TO *         _buf,               \
                               unsigned int _buf_len);          \
    
LIQUID_SYMSTREAM_DEFINE_API(LIQUID_SYMSTREAM_MANGLE_CFLOAT, liquid_float_complex)



//
// multi-signal source for testing (no meaningful data, just signals)
//
#define LIQUID_MSOURCE_MANGLE_CFLOAT(name) LIQUID_CONCAT(msourcecf,name)

#define LIQUID_MSOURCE_DEFINE_API(MSOURCE,TO)                   \
                                                                \
typedef struct MSOURCE(_s) * MSOURCE();                         \
                                                                \
/* create default msource object                            */  \
MSOURCE() MSOURCE(_create)(void);                               \
                                                                \
/* destroy msource object                                   */  \
void MSOURCE(_destroy)(MSOURCE() _q);                           \
                                                                \
/* print msrouce object                                     */  \
void MSOURCE(_print)(MSOURCE() _q);                             \
                                                                \
/* reset msrouce object                                     */  \
void MSOURCE(_reset)(MSOURCE() _q);                             \
                                                                \
/* add signal sources                                       */  \
int MSOURCE(_add_tone) (MSOURCE() _q);                          \
int MSOURCE(_add_noise)(MSOURCE() _q, float _bandwidth);        \
int MSOURCE(_add_modem)(MSOURCE()    _q,                        \
                        int          _ms,                       \
                        unsigned int _k,                        \
                        unsigned int _m,                        \
                        float        _beta);                    \
                                                                \
/* remove signal                                            */  \
void MSOURCE(_remove)(MSOURCE() _q, int _id);                   \
                                                                \
/* enable/disable signal                                    */  \
void MSOURCE(_enable) (MSOURCE() _q, int _id);                  \
void MSOURCE(_disable)(MSOURCE() _q, int _id);                  \
                                                                \
/* set signal gain                                          */  \
/*  _q      :   msource object                              */  \
/*  _id     :   source id                                   */  \
/*  _gain   :   signal gain                                 */  \
void MSOURCE(_set_gain)(MSOURCE() _q,                           \
                        int       _id,                          \
                        float     _gain_dB);                    \
                                                                \
/* set carrier offset to signal                             */  \
/*  _q      :   msource object                              */  \
/*  _id     :   source id                                   */  \
/*  _fc     :   carrier offset, fc in [-0.5,0.5]            */  \
void MSOURCE(_set_frequency)(MSOURCE() _q,                      \
                             int       _id,                     \
                             float     _dphi);                  \
                                                                \
/* write block of samples to output buffer                  */  \
/*  _q      : synchronizer object                           */  \
/*  _buf    : output buffer [size: _buf_len x 1]            */  \
/*  _buf_len: output buffer size                            */  \
void MSOURCE(_write_samples)(MSOURCE()    _q,                   \
                             TO *         _buf,                 \
                             unsigned int _buf_len);            \
    
LIQUID_MSOURCE_DEFINE_API(LIQUID_MSOURCE_MANGLE_CFLOAT, liquid_float_complex)




// 
// Symbol tracking: AGC > symsync > EQ > carrier recovery
//
#define LIQUID_SYMTRACK_MANGLE_RRRF(name) LIQUID_CONCAT(symtrack_rrrf,name)
#define LIQUID_SYMTRACK_MANGLE_CCCF(name) LIQUID_CONCAT(symtrack_cccf,name)

// large macro
//   SYMTRACK   : name-mangling macro
//   T          : data type, primitive
//   TO         : data type, output
//   TC         : data type, coefficients
//   TI         : data type, input
#define LIQUID_SYMTRACK_DEFINE_API(SYMTRACK,T,TO,TC,TI)         \
                                                                \
typedef struct SYMTRACK(_s) * SYMTRACK();                       \
                                                                \
/* create symtrack object with default parameters           */  \
/*  _ftype  : filter type (e.g. LIQUID_RNYQUIST_RRC)        */  \
/*  _k      : samples per symbol                            */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _beta   : filter excess bandwidth                       */  \
/*  _ms     : modulation scheme (e.g. LIQUID_MODEM_QPSK)    */  \
SYMTRACK() SYMTRACK(_create)(int          _ftype,               \
                             unsigned int _k,                   \
                             unsigned int _m,                   \
                             float        _beta,                \
                             int          _ms);                 \
                                                                \
/* create symtrack object using default parameters          */  \
SYMTRACK() SYMTRACK(_create_default)();                         \
                                                                \
/* destroy symtrack object, freeing all internal memory     */  \
void SYMTRACK(_destroy)(SYMTRACK() _q);                         \
                                                                \
/* print symtrack object's parameters                       */  \
void SYMTRACK(_print)(SYMTRACK() _q);                           \
                                                                \
/* reset symtrack internal state                            */  \
void SYMTRACK(_reset)(SYMTRACK() _q);                           \
                                                                \
/* set symtrack modulation scheme                           */  \
void SYMTRACK(_set_modscheme)(SYMTRACK() _q, int _ms);          \
                                                                \
/* set symtrack internal bandwidth                          */  \
void SYMTRACK(_set_bandwidth)(SYMTRACK() _q, float _bw);        \
                                                                \
/* adjust internal nco by requested phase                   */  \
void SYMTRACK(_adjust_phase)(SYMTRACK() _q, T _dphi);           \
                                                                \
/* execute synchronizer on single input sample              */  \
/*  _q      : synchronizer object                           */  \
/*  _x      : input data sample                             */  \
/*  _y      : output data array                             */  \
/*  _ny     : number of samples written to output buffer    */  \
void SYMTRACK(_execute)(SYMTRACK()     _q,                      \
                        TI             _x,                      \
                        TO *           _y,                      \
                        unsigned int * _ny);                    \
                                                                \
/* execute synchronizer on input data array                 */  \
/*  _q      : synchronizer object                           */  \
/*  _x      : input data array                              */  \
/*  _nx     : number of input samples                       */  \
/*  _y      : output data array                             */  \
/*  _ny     : number of samples written to output buffer    */  \
void SYMTRACK(_execute_block)(SYMTRACK()     _q,                \
                              TI *           _x,                \
                              unsigned int   _nx,               \
                              TO *           _y,                \
                              unsigned int * _ny);              \
    
LIQUID_SYMTRACK_DEFINE_API(LIQUID_SYMTRACK_MANGLE_RRRF,
                           float,
                           float,
                           float,
                           float)

LIQUID_SYMTRACK_DEFINE_API(LIQUID_SYMTRACK_MANGLE_CCCF,
                           float,
                           liquid_float_complex,
                           liquid_float_complex,
                           liquid_float_complex)



//
// MODULE : math
//

// ln( Gamma(z) )
float liquid_lngammaf(float _z);

// Gamma(z)
float liquid_gammaf(float _z);

// ln( gamma(z,alpha) ) : lower incomplete gamma function
float liquid_lnlowergammaf(float _z, float _alpha);

// ln( Gamma(z,alpha) ) : upper incomplete gamma function
float liquid_lnuppergammaf(float _z, float _alpha);

// gamma(z,alpha) : lower incomplete gamma function
float liquid_lowergammaf(float _z, float _alpha);

// Gamma(z,alpha) : upper incomplete gamma function
float liquid_uppergammaf(float _z, float _alpha);

// n!
float liquid_factorialf(unsigned int _n);



// ln(I_v(z)) : log Modified Bessel function of the first kind
float liquid_lnbesselif(float _nu, float _z);

// I_v(z) : Modified Bessel function of the first kind
float liquid_besselif(float _nu, float _z);

// I_0(z) : Modified Bessel function of the first kind (order zero)
float liquid_besseli0f(float _z);

// J_v(z) : Bessel function of the first kind
float liquid_besseljf(float _nu, float _z);

// J_0(z) : Bessel function of the first kind (order zero)
float liquid_besselj0f(float _z);


// Q function
float liquid_Qf(float _z);

// Marcum Q-function
float liquid_MarcumQf(int _M,
                      float _alpha,
                      float _beta);

// Marcum Q-function (M=1)
float liquid_MarcumQ1f(float _alpha,
                       float _beta);

// sin(pi x) / (pi x)
float sincf(float _x);

// next power of 2 : y = ceil(log2(_x))
unsigned int liquid_nextpow2(unsigned int _x);

// (n choose k) = n! / ( k! (n-k)! )
float liquid_nchoosek(unsigned int _n, unsigned int _k);

// 
// Windowing functions
//

// Modulation schemes available
#define LIQUID_WINDOW_NUM_FUNCTIONS (10)

// prototypes
typedef enum {
    LIQUID_WINDOW_UNKNOWN=0,        // unknown/unsupported scheme

    LIQUID_WINDOW_HAMMING,          // Hamming
    LIQUID_WINDOW_HANN,             // Hann
    LIQUID_WINDOW_BLACKMANHARRIS,   // Blackman-harris (4-term)
    LIQUID_WINDOW_BLACKMANHARRIS7,  // Blackman-harris (7-term)
    LIQUID_WINDOW_KAISER,           // Kaiser (beta factor unspecified)
    LIQUID_WINDOW_FLATTOP,          // flat top (includes negative values)
    LIQUID_WINDOW_TRIANGULAR,       // triangular
    LIQUID_WINDOW_RCOSTAPER,        // raised-cosine taper (taper size unspecified)
    LIQUID_WINDOW_KBD,              // Kaiser-Bessel derived window (beta factor unspecified)
} liquid_window_type;

// pretty names for window
extern const char * liquid_window_str[LIQUID_WINDOW_NUM_FUNCTIONS][2];

// Print compact list of existing and available windowing functions
void liquid_print_windows();

// returns modulation_scheme based on input string
liquid_window_type liquid_getopt_str2window(const char * _str);

// Kaiser-Bessel derived window (single sample)
//  _n      :   index (0 <= _n < _N)
//  _N      :   length of filter (must be even)
//  _beta   :   Kaiser window parameter (_beta > 0)
float liquid_kbd(unsigned int _n, unsigned int _N, float _beta);

// Kaiser-Bessel derived window (full window)
//  _n      :   length of filter (must be even)
//  _beta   :   Kaiser window parameter (_beta > 0)
//  _w      :   resulting window
void liquid_kbd_window(unsigned int _n, float _beta, float * _w);

// Kaiser window
//  _n      :   window index
//  _N      :   full window length
//  _beta   :   Kaiser-Bessel window shape parameter
//  _dt     :   fractional sample offset
float kaiser(unsigned int _n,
             unsigned int _N,
             float        _beta,
             float        _dt);

// Hamming window
//  _n      :   window index
//  _N      :   full window length
float hamming(unsigned int _n,
              unsigned int _N);

// Hann window
//  _n      :   window index
//  _N      :   full window length
float hann(unsigned int _n,
           unsigned int _N);

// Blackman-harris window
//  _n      :   window index
//  _N      :   full window length
float blackmanharris(unsigned int _n,
                     unsigned int _N);

// 7th order Blackman-harris window
// _n			:	window index
// _N			:	full window length
float blackmanharris7(unsigned int _n,
                      unsigned int _N);

// Flat-top window
// _n			:	window index
// _N			:	full window length
float flattop(unsigned int _n,
              unsigned int _N);

// Triangular window
// _n			:	window index
// _N			:	full window length
// _L			:	triangle length, _L in {_N, _N+1, _N-1}
float triangular(unsigned int _n,
                 unsigned int _N,
                 unsigned int _L);

// raised-cosine tapering window
//  _n      :   window index
//  _t      :   taper length
//  _N      :   full window length
float liquid_rcostaper_windowf(unsigned int _n,
                               unsigned int _t,
                               unsigned int _N);


// polynomials


#define LIQUID_POLY_MANGLE_DOUBLE(name)  LIQUID_CONCAT(poly,   name)
#define LIQUID_POLY_MANGLE_FLOAT(name)   LIQUID_CONCAT(polyf,  name)

#define LIQUID_POLY_MANGLE_CDOUBLE(name) LIQUID_CONCAT(polyc,  name)
#define LIQUID_POLY_MANGLE_CFLOAT(name)  LIQUID_CONCAT(polycf, name)

// large macro
//   POLY   : name-mangling macro
//   T      : data type
//   TC     : data type (complex)
#define LIQUID_POLY_DEFINE_API(POLY,T,TC)                       \
/* evaluate polynomial _p (order _k-1) at value _x  */          \
T POLY(_val)(T * _p, unsigned int _k, T _x);                    \
                                                                \
/* least-squares polynomial fit (order _k-1) */                 \
void POLY(_fit)(T * _x,                                         \
                T * _y,                                         \
                unsigned int _n,                                \
                T * _p,                                         \
                unsigned int _k);                               \
                                                                \
/* Lagrange polynomial exact fit (order _n-1) */                \
void POLY(_fit_lagrange)(T * _x,                                \
                         T * _y,                                \
                         unsigned int _n,                       \
                         T * _p);                               \
                                                                \
/* Lagrange polynomial interpolation */                         \
T POLY(_interp_lagrange)(T * _x,                                \
                         T * _y,                                \
                         unsigned int _n,                       \
                         T   _x0);                              \
                                                                \
/* Lagrange polynomial fit (barycentric form) */                \
void POLY(_fit_lagrange_barycentric)(T * _x,                    \
                                     unsigned int _n,           \
                                     T * _w);                   \
                                                                \
/* Lagrange polynomial interpolation (barycentric form) */      \
T POLY(_val_lagrange_barycentric)(T * _x,                       \
                                  T * _y,                       \
                                  T * _w,                       \
                                  T   _x0,                      \
                                  unsigned int _n);             \
                                                                \
/* expands the polynomial:                                      \
 *  P_n(x) = (1+x)^n                                            \
 * as                                                           \
 *  P_n(x) = p[0] + p[1]*x + p[2]*x^2 + ... + p[n]x^n           \
 * NOTE: _p has order n=m+k (array is length n+1)               \
 */                                                             \
void POLY(_expandbinomial)(unsigned int _n,                     \
                           T * _p);                             \
                                                                \
/* expands the polynomial:                                      \
 *  P_n(x) = (1+x)^m * (1-x)^k                                  \
 * as                                                           \
 *  P_n(x) = p[0] + p[1]*x + p[2]*x^2 + ... + p[n]x^n           \
 * NOTE: _p has order n=m+k (array is length n+1)               \
 */                                                             \
void POLY(_expandbinomial_pm)(unsigned int _m,                  \
                              unsigned int _k,                  \
                              T * _p);                          \
                                                                \
/* expands the polynomial:                                      \
 *  P_n(x) = (x-r[0]) * (x-r[1]) * ... * (x-r[n-1])             \
 * as                                                           \
 *  P_n(x) = c[0] + c[1]*x + ... + c[n]*x^n                     \
 * where r[0],r[1],...,r[n-1] are the roots of P_n(x)           \
 * NOTE: _c has order _n (array is length _n+1)                 \
 */                                                             \
void POLY(_expandroots)(T * _a,                                 \
                        unsigned int _n,                        \
                        T * _c);                                \
                                                                \
/* expands the polynomial:                                      \
 *  P_n(x) =                                                    \
 *    (x*b[0]-a[0]) * (x*b[1]-a[1]) * ... * (x*b[n-1]-a[n-1])   \
 * as                                                           \
 *  P_n(x) = c[0] + c[1]*x + ... + c[n]*x^n                     \
 * NOTE: _c has order _n (array is length _n+1)                 \
 */                                                             \
void POLY(_expandroots2)(T * _a,                                \
                         T * _b,                                \
                         unsigned int _n,                       \
                         T * _c);                               \
                                                                \
/* find roots of the polynomial (complex)                   */  \
/*  _poly   : poly array, ascending powers [size: _k x 1]   */  \
/*  _k      : poly length (poly order = _k - 1)             */  \
/*  _roots  : resulting complex roots [size: _k-1 x 1]      */  \
void POLY(_findroots)(T *          _poly,                       \
                      unsigned int _n,                          \
                      TC *         _roots);                     \
                                                                \
/* find the complex roots of the polynomial using the       */  \
/* Durand-Kerner method                                     */  \
void POLY(_findroots_durandkerner)(T *          _poly,          \
                                   unsigned int _k,             \
                                   TC *         _roots);        \
                                                                \
/* find the complex roots of the polynomial using           */  \
/* Bairstow's method                                        */  \
void POLY(_findroots_bairstow)(T *          _poly,              \
                               unsigned int _k,                 \
                               TC *         _roots);            \
                                                                \
/* expands the multiplication of two polynomials */             \
void POLY(_mul)(T *          _a,                                \
                unsigned int _order_a,                          \
                T *          _b,                                \
                unsigned int _order_b,                          \
                T *          _c);                               \

LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_DOUBLE,
                       double,
                       liquid_double_complex)

LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_FLOAT,
                       float,
                       liquid_float_complex)

LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_CDOUBLE,
                       liquid_double_complex,
                       liquid_double_complex)

LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_CFLOAT,
                       liquid_float_complex,
                       liquid_float_complex)

#if 0
// expands the polynomial: (1+x)^n
void poly_binomial_expand(unsigned int _n, int * _c);

// expands the polynomial: (1+x)^k * (1-x)^(n-k)
void poly_binomial_expand_pm(unsigned int _n,
                             unsigned int _k,
                             int * _c);
#endif

// 
// modular arithmetic, etc.
//

// maximum number of factors
#define LIQUID_MAX_FACTORS (40)

// is number prime?
int liquid_is_prime(unsigned int _n);

// compute number's prime factors
//  _n          :   number to factor
//  _factors    :   pre-allocated array of factors [size: LIQUID_MAX_FACTORS x 1]
//  _num_factors:   number of factors found, sorted ascending
void liquid_factor(unsigned int   _n,
                   unsigned int * _factors,
                   unsigned int * _num_factors);

// compute number's unique prime factors
//  _n          :   number to factor
//  _factors    :   pre-allocated array of factors [size: LIQUID_MAX_FACTORS x 1]
//  _num_factors:   number of unique factors found, sorted ascending
void liquid_unique_factor(unsigned int   _n,
                          unsigned int * _factors,
                          unsigned int * _num_factors);

// compute c = base^exp (mod n)
unsigned int liquid_modpow(unsigned int _base,
                           unsigned int _exp,
                           unsigned int _n);

// find smallest primitive root of _n
unsigned int liquid_primitive_root(unsigned int _n);

// find smallest primitive root of _n, assuming _n is prime
unsigned int liquid_primitive_root_prime(unsigned int _n);

// Euler's totient function
unsigned int liquid_totient(unsigned int _n);


//
// MODULE : matrix
//

#define LIQUID_MATRIX_MANGLE_DOUBLE(name)  LIQUID_CONCAT(matrix,   name)
#define LIQUID_MATRIX_MANGLE_FLOAT(name)   LIQUID_CONCAT(matrixf,  name)

#define LIQUID_MATRIX_MANGLE_CDOUBLE(name) LIQUID_CONCAT(matrixc,  name)
#define LIQUID_MATRIX_MANGLE_CFLOAT(name)  LIQUID_CONCAT(matrixcf, name)

// large macro
//   MATRIX : name-mangling macro
//   T      : data type
#define LIQUID_MATRIX_DEFINE_API(MATRIX,T)                      \
                                                                \
/* print array as matrix                                    */  \
/*  _x      : input matrix [size: _r x _c]                  */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_print)(T *          _x,                            \
                    unsigned int _r,                            \
                    unsigned int _c);                           \
                                                                \
/* add two matrices _x and _y saving the result in _z       */  \
/*  _x      : input matrix  [size: _r x _c]                 */  \
/*  _y      : input matrix  [size: _r x _c]                 */  \
/*  _z      : output matrix [size: _r x _c]                 */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_add)(T *          _x,                              \
                  T *          _y,                              \
                  T *          _z,                              \
                  unsigned int _r,                              \
                  unsigned int _c);                             \
                                                                \
/* subtract two matrices _x and _y saving the result in _z  */  \
/*  _x      : input matrix  [size: _r x _c]                 */  \
/*  _y      : input matrix  [size: _r x _c]                 */  \
/*  _z      : output matrix [size: _r x _c]                 */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_sub)(T *          _x,                              \
                  T *          _y,                              \
                  T *          _z,                              \
                  unsigned int _r,                              \
                  unsigned int _c);                             \
                                                                \
/* perform point-wise multiplication of two matrices _x     */  \
/* and _y saving the result in _z                           */  \
/*  _x      : input matrix  [size: _r x _c]                 */  \
/*  _y      : input matrix  [size: _r x _c]                 */  \
/*  _z      : output matrix [size: _r x _c]                 */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_pmul)(T *          _x,                             \
                   T *          _y,                             \
                   T *          _z,                             \
                   unsigned int _r,                             \
                   unsigned int _c);                            \
                                                                \
/* perform point-wise division of two matrices _x and _y    */  \
/* saving the result in _z                                  */  \
/*  _x      : input matrix  [size: _r x _c]                 */  \
/*  _y      : input matrix  [size: _r x _c]                 */  \
/*  _z      : output matrix [size: _r x _c]                 */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_pdiv)(T *          _x,                             \
                   T *          _y,                             \
                   T *          _z,                             \
                   unsigned int _r,                             \
                   unsigned int _c);                            \
                                                                \
/* multiply two matrices _x and _y storing the result in _z */  \
/* NOTE: _rz = _rx, _cz = _cy, and _cx = _ry                */  \
/*  _x      : input matrix  [size: _rx x _cx]               */  \
/*  _y      : input matrix  [size: _ry x _cy]               */  \
/*  _z      : output matrix [size: _rz x _cz]               */  \
void MATRIX(_mul)(T * _x, unsigned int _rx, unsigned int _cx,   \
                  T * _y, unsigned int _ry, unsigned int _cy,   \
                  T * _z, unsigned int _rz, unsigned int _cz);  \
                                                                \
/* solve _x = _y*_z for _z for square matrices of size _n   */  \
/*  _x      : input matrix  [size: _n x _n]                 */  \
/*  _y      : input matrix  [size: _n x _n]                 */  \
/*  _z      : output matrix [size: _n x _n]                 */  \
void MATRIX(_div)(T *          _x,                              \
                  T *          _y,                              \
                  T *          _z,                              \
                  unsigned int _n);                             \
                                                                \
/* compute the determinant of a square matrix _x            */  \
/*  _x      : input matrix [size: _r x _c]                  */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
T MATRIX(_det)(T *          _x,                                 \
               unsigned int _r,                                 \
               unsigned int _c);                                \
                                                                \
/* compute the in-place transpose of the matrix _x          */  \
/*  _x      : input matrix [size: _r x _c]                  */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_trans)(T *          _x,                            \
                    unsigned int _r,                            \
                    unsigned int _c);                           \
                                                                \
/* compute the in-place Hermitian transpose of _x           */  \
/*  _x      : input matrix [size: _r x _c]                  */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_hermitian)(T *          _x,                        \
                        unsigned int _r,                        \
                        unsigned int _c);                       \
                                                                \
/* compute x*x' on [m x n] matrix, result: [m x m]          */  \
/*  _x      : input matrix [size: _m x _n]                  */  \
/*  _m      : input rows                                    */  \
/*  _n      : input columns                                 */  \
/*  _xxT    : output matrix [size: _m x _m]                 */  \
void MATRIX(_mul_transpose)(T *          _x,                    \
                            unsigned int _m,                    \
                            unsigned int _n,                    \
                            T *          _xxT);                 \
                                                                \
/* compute x'*x on [m x n] matrix, result: [n x n]          */  \
/*  _x      : input matrix [size: _m x _n]                  */  \
/*  _m      : input rows                                    */  \
/*  _n      : input columns                                 */  \
/*  _xTx    : output matrix [size: _n x _n]                 */  \
void MATRIX(_transpose_mul)(T *          _x,                    \
                            unsigned int _m,                    \
                            unsigned int _n,                    \
                            T *          _xTx);                 \
                                                                \
/* compute x*x.' on [m x n] matrix, result: [m x m]         */  \
/*  _x      : input matrix [size: _m x _n]                  */  \
/*  _m      : input rows                                    */  \
/*  _n      : input columns                                 */  \
/*  _xxH    : output matrix [size: _m x _m]                 */  \
void MATRIX(_mul_hermitian)(T *          _x,                    \
                            unsigned int _m,                    \
                            unsigned int _n,                    \
                            T *          _xxH);                 \
                                                                \
/* compute x.'*x on [m x n] matrix, result: [n x n]         */  \
/*  _x      : input matrix [size: _m x _n]                  */  \
/*  _m      : input rows                                    */  \
/*  _n      : input columns                                 */  \
/*  _xHx    : output matrix [size: _n x _n]                 */  \
void MATRIX(_hermitian_mul)(T *          _x,                    \
                            unsigned int _m,                    \
                            unsigned int _n,                    \
                            T *          _xHx);                 \
                                                                \
                                                                \
/* augment two matrices _x and _y storing the result in _z  */  \
/* NOTE: _rz = _rx = _ry, _rx = _ry, and _cz = _cx + _cy    */  \
/*  _x      : input matrix  [size: _rx x _cx]               */  \
/*  _y      : input matrix  [size: _ry x _cy]               */  \
/*  _z      : output matrix [size: _rz x _cz]               */  \
void MATRIX(_aug)(T * _x, unsigned int _rx, unsigned int _cx,   \
                  T * _y, unsigned int _ry, unsigned int _cy,   \
                  T * _z, unsigned int _rz, unsigned int _cz);  \
                                                                \
/* compute the inverse of a square matrix _x                */  \
/*  _x      : input/output matrix [size: _r x _c]           */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_inv)(T *          _x,                              \
                  unsigned int _r,                              \
                  unsigned int _c);                             \
                                                                \
/* generate the identity square matrix of size _n           */  \
/*  _x      : output matrix [size: _n x _n]                 */  \
/*  _n      : dimensions of _x                              */  \
void MATRIX(_eye)(T *          _x,                              \
                  unsigned int _n);                             \
                                                                \
/* generate the all-ones matrix of size _n                  */  \
/*  _x      : output matrix [size: _r x _c]                 */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_ones)(T *          _n,                             \
                   unsigned int _r,                             \
                   unsigned int _c);                            \
                                                                \
/* generate the all-zeros matrix of size _n                 */  \
/*  _x      : output matrix [size: _r x _c]                 */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_zeros)(T *          _x,                            \
                    unsigned int _r,                            \
                    unsigned int _c);                           \
                                                                \
/* perform Gauss-Jordan elimination on matrix _x            */  \
/*  _x      : input/output matrix [size: _r x _c]           */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
void MATRIX(_gjelim)(T *          _x,                           \
                     unsigned int _r,                           \
                     unsigned int _c);                          \
                                                                \
/* pivot on element _x[_r,_c]                               */  \
/*  _x      : output matrix [size: _rx x _cx]               */  \
/*  _rx     : rows of _x                                    */  \
/*  _cx     : columns of _x                                 */  \
/*  _r      : pivot row                                     */  \
/*  _c      : pivot column                                  */  \
void MATRIX(_pivot)(T *          _x,                            \
                    unsigned int _rx,                           \
                    unsigned int _cx,                           \
                    unsigned int _r,                            \
                    unsigned int _c);                           \
                                                                \
/* swap rows _r1 and _r2 of matrix _x                       */  \
/*  _x      : input/output matrix [size: _rx x _cx]         */  \
/*  _rx     : rows of _x                                    */  \
/*  _cx     : columns of _x                                 */  \
/*  _r1     : first row to swap                             */  \
/*  _r2     : second row to swap                            */  \
void MATRIX(_swaprows)(T *          _x,                         \
                       unsigned int _rx,                        \
                       unsigned int _cx,                        \
                       unsigned int _r1,                        \
                       unsigned int _r2);                       \
                                                                \
/* solve linear system of _n equations: _A*_x = _b          */  \
/*  _A      :   system matrix [size: _n x _n]               */  \
/*  _n      :   system size                                 */  \
/*  _b      :   equality vector [size: _n x 1]              */  \
/*  _x      :   solution vector [size: _n x 1]              */  \
/*  _opts   :   options (ignored for now)                   */  \
void MATRIX(_linsolve)(T *          _A,                         \
                       unsigned int _n,                         \
                       T *          _b,                         \
                       T *          _x,                         \
                       void *       _opts);                     \
                                                                \
/* solve linear system of equations using conjugate         */  \
/* gradient method                                          */  \
/*  _A      :   symmetric positive definite square matrix   */  \
/*  _n      :   system dimension                            */  \
/*  _b      :   equality [size: _n x 1]                     */  \
/*  _x      :   solution estimate [size: _n x 1]            */  \
/*  _opts   :   options (ignored for now)                   */  \
void MATRIX(_cgsolve)(T *          _A,                          \
                      unsigned int _n,                          \
                      T *          _b,                          \
                      T *          _x,                          \
                      void *       _opts);                      \
                                                                \
/* L/U/P decomposition, Crout's method                      */  \
/*  _x      : input/output matrix [size: _rx x _cx]         */  \
/*  _rx     : rows of _x                                    */  \
/*  _cx     : columns of _x                                 */  \
/*  _L      : first row to swap                             */  \
/*  _U      : first row to swap                             */  \
/*  _P      : first row to swap                             */  \
void MATRIX(_ludecomp_crout)(T *          _x,                   \
                             unsigned int _rx,                  \
                             unsigned int _cx,                  \
                             T *          _L,                   \
                             T *          _U,                   \
                             T *          _P);                  \
                                                                \
/* L/U/P decomposition, Doolittle's method                  */  \
/*  _x      : input/output matrix [size: _rx x _cx]         */  \
/*  _rx     : rows of _x                                    */  \
/*  _cx     : columns of _x                                 */  \
/*  _L      : first row to swap                             */  \
/*  _U      : first row to swap                             */  \
/*  _P      : first row to swap                             */  \
void MATRIX(_ludecomp_doolittle)(T *          _x,               \
                                 unsigned int _rx,              \
                                 unsigned int _cx,              \
                                 T *          _L,               \
                                 T *          _U,               \
                                 T *          _P);              \
                                                                \
/* Orthnormalization using the Gram-Schmidt algorithm       */  \
/*  _A      : input matrix [size: _r x _c]                  */  \
/*  _r      : rows                                          */  \
/*  _c      : columns                                       */  \
/*  _v      : output matrix                                 */  \
void MATRIX(_gramschmidt)(T *          _A,                      \
                          unsigned int _r,                      \
                          unsigned int _c,                      \
                          T *          _v);                     \
                                                                \
/* Q/R decomposition using the Gram-Schmidt algorithm such  */  \
/* that _A = _Q*_R and _Q^T * _Q = _In and _R is a diagonal */  \
/* matrix                                                   */  \
/* NOTE: all matrices are square                            */  \
/*  _A      : input matrix [size: _m x _m]                  */  \
/*  _m      : rows                                          */  \
/*  _n      : columns (same as cols)                        */  \
/*  _Q      : output matrix [size: _m x _m]                 */  \
/*  _R      : output matrix [size: _m x _m]                 */  \
void MATRIX(_qrdecomp_gramschmidt)(T *          _A,             \
                                   unsigned int _m,             \
                                   unsigned int _n,             \
                                   T *          _Q,             \
                                   T *          _R);            \
                                                                \
/* Compute Cholesky decomposition of a symmetric/Hermitian  */  \
/* positive-definite matrix as A = L * L^T                  */  \
/*  _A      :   input square matrix [size: _n x _n]         */  \
/*  _n      :   input matrix dimension                      */  \
/*  _L      :   output lower-triangular matrix              */  \
void MATRIX(_chol)(T *          _A,                             \
                   unsigned int _n,                             \
                   T *          _L);                            \

#define matrix_access(X,R,C,r,c) ((X)[(r)*(C)+(c)])

#define matrixc_access(X,R,C,r,c)   matrix_access(X,R,C,r,c)
#define matrixf_access(X,R,C,r,c)   matrix_access(X,R,C,r,c)
#define matrixcf_access(X,R,C,r,c)  matrix_access(X,R,C,r,c)

LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_FLOAT,   float)
LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_DOUBLE,  double)

LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_CFLOAT,  liquid_float_complex)
LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_CDOUBLE, liquid_double_complex)


#define LIQUID_SMATRIX_MANGLE_BOOL(name)  LIQUID_CONCAT(smatrixb,  name)
#define LIQUID_SMATRIX_MANGLE_FLOAT(name) LIQUID_CONCAT(smatrixf,  name)
#define LIQUID_SMATRIX_MANGLE_INT(name)   LIQUID_CONCAT(smatrixi,  name)

// sparse 'alist' matrix type (similar to MacKay, Davey Lafferty convention)
// large macro
//   SMATRIX    : name-mangling macro
//   T          : primitive data type
#define LIQUID_SMATRIX_DEFINE_API(SMATRIX,T)                    \
typedef struct SMATRIX(_s) * SMATRIX();                         \
                                                                \
/* create _M x _N matrix, initialized with zeros */             \
SMATRIX() SMATRIX(_create)(unsigned int _M,                     \
                           unsigned int _N);                    \
                                                                \
/* create _M x _N matrix, initialized on array */               \
SMATRIX() SMATRIX(_create_array)(T *          _x,               \
                                 unsigned int _m,               \
                                 unsigned int _n);              \
                                                                \
/* destroy object */                                            \
void SMATRIX(_destroy)(SMATRIX() _q);                           \
                                                                \
/* print compact form */                                        \
void SMATRIX(_print)(SMATRIX() _q);                             \
                                                                \
/* print expanded form */                                       \
void SMATRIX(_print_expanded)(SMATRIX() _q);                    \
                                                                \
/* query properties methods */                                  \
void SMATRIX(_size)(SMATRIX()      _q,                          \
                    unsigned int * _m,                          \
                    unsigned int * _n);                         \
                                                                \
/* zero all elements and keep memory                        */  \
void SMATRIX(_clear)(SMATRIX() _q);                             \
                                                                \
/* zero all elements and clear memory                       */  \
void SMATRIX(_reset)(SMATRIX() _q);                             \
                                                                \
/* determine if value has been set (allocated memory) */        \
int SMATRIX(_isset)(SMATRIX()    _q,                            \
                    unsigned int _m,                            \
                    unsigned int _n);                           \
                                                                \
/* inserts/deletes element at index (memory allocation) */      \
void SMATRIX(_insert)(SMATRIX()    _q,                          \
                      unsigned int _m,                          \
                      unsigned int _n,                          \
                      T            _v);                         \
void SMATRIX(_delete)(SMATRIX()    _q,                          \
                      unsigned int _m,                          \
                      unsigned int _n);                         \
                                                                \
/* sets/gets the value (with memory allocation if needed) */    \
void SMATRIX(_set)(SMATRIX()    _q,                             \
                   unsigned int _m,                             \
                   unsigned int _n,                             \
                   T            _v);                            \
T SMATRIX(_get)(SMATRIX()    _q,                                \
                unsigned int _m,                                \
                unsigned int _n);                               \
                                                                \
/* initialize to identity matrix */                             \
void SMATRIX(_eye)(SMATRIX() _q);                               \
                                                                \
/* multiply two sparse binary matrices */                       \
void SMATRIX(_mul)(SMATRIX() _x,                                \
                   SMATRIX() _y,                                \
                   SMATRIX() _z);                               \
                                                                \
/* multiply sparse matrix by vector         */                  \
/*  _q  :   sparse matrix                   */                  \
/*  _x  :   input vector [size: _N x 1]     */                  \
/*  _y  :   output vector [size: _M x 1]    */                  \
void SMATRIX(_vmul)(SMATRIX() _q,                               \
                    T *       _x,                               \
                    T *       _y);                              \

LIQUID_SMATRIX_DEFINE_API(LIQUID_SMATRIX_MANGLE_BOOL,  unsigned char)
LIQUID_SMATRIX_DEFINE_API(LIQUID_SMATRIX_MANGLE_FLOAT, float)
LIQUID_SMATRIX_DEFINE_API(LIQUID_SMATRIX_MANGLE_INT,   short int)

// 
// smatrix cross methods
//

// multiply sparse binary matrix by floating-point matrix
//  _q  :   sparse matrix [size: A->M x A->N]
//  _x  :   input vector  [size:  mx  x  nx ]
//  _y  :   output vector [size:  my  x  ny ]
void smatrixb_mulf(smatrixb     _A,
                   float *      _x,
                   unsigned int _mx,
                   unsigned int _nx,
                   float *      _y,
                   unsigned int _my,
                   unsigned int _ny);

// multiply sparse binary matrix by floating-point vector
//  _q  :   sparse matrix
//  _x  :   input vector [size: _N x 1]
//  _y  :   output vector [size: _M x 1]
void smatrixb_vmulf(smatrixb _q,
                    float *  _x,
                    float *  _y);


//
// MODULE : modem (modulator/demodulator)
//

// Maximum number of allowed bits per symbol
#define MAX_MOD_BITS_PER_SYMBOL 8

// Modulation schemes available
#define LIQUID_MODEM_NUM_SCHEMES      (52)

typedef enum {
    LIQUID_MODEM_UNKNOWN=0, // Unknown modulation scheme

    // Phase-shift keying (PSK)
    LIQUID_MODEM_PSK2,      LIQUID_MODEM_PSK4,
    LIQUID_MODEM_PSK8,      LIQUID_MODEM_PSK16,
    LIQUID_MODEM_PSK32,     LIQUID_MODEM_PSK64,
    LIQUID_MODEM_PSK128,    LIQUID_MODEM_PSK256,

    // Differential phase-shift keying (DPSK)
    LIQUID_MODEM_DPSK2,     LIQUID_MODEM_DPSK4,
    LIQUID_MODEM_DPSK8,     LIQUID_MODEM_DPSK16,
    LIQUID_MODEM_DPSK32,    LIQUID_MODEM_DPSK64,
    LIQUID_MODEM_DPSK128,   LIQUID_MODEM_DPSK256,

    // amplitude-shift keying
    LIQUID_MODEM_ASK2,      LIQUID_MODEM_ASK4,
    LIQUID_MODEM_ASK8,      LIQUID_MODEM_ASK16,
    LIQUID_MODEM_ASK32,     LIQUID_MODEM_ASK64,
    LIQUID_MODEM_ASK128,    LIQUID_MODEM_ASK256,

    // rectangular quadrature amplitude-shift keying (QAM)
    LIQUID_MODEM_QAM4,
    LIQUID_MODEM_QAM8,      LIQUID_MODEM_QAM16,
    LIQUID_MODEM_QAM32,     LIQUID_MODEM_QAM64,
    LIQUID_MODEM_QAM128,    LIQUID_MODEM_QAM256,

    // amplitude phase-shift keying (APSK)
    LIQUID_MODEM_APSK4,
    LIQUID_MODEM_APSK8,     LIQUID_MODEM_APSK16,
    LIQUID_MODEM_APSK32,    LIQUID_MODEM_APSK64,
    LIQUID_MODEM_APSK128,   LIQUID_MODEM_APSK256,

    // specific modem types
    LIQUID_MODEM_BPSK,      // Specific: binary PSK
    LIQUID_MODEM_QPSK,      // specific: quaternary PSK
    LIQUID_MODEM_OOK,       // Specific: on/off keying
    LIQUID_MODEM_SQAM32,    // 'square' 32-QAM
    LIQUID_MODEM_SQAM128,   // 'square' 128-QAM
    LIQUID_MODEM_V29,       // V.29 star constellation
    LIQUID_MODEM_ARB16OPT,  // optimal 16-QAM
    LIQUID_MODEM_ARB32OPT,  // optimal 32-QAM
    LIQUID_MODEM_ARB64OPT,  // optimal 64-QAM
    LIQUID_MODEM_ARB128OPT, // optimal 128-QAM
    LIQUID_MODEM_ARB256OPT, // optimal 256-QAM
    LIQUID_MODEM_ARB64VT,   // Virginia Tech logo

    // arbitrary modem type
    LIQUID_MODEM_ARB        // arbitrary QAM
} modulation_scheme;

// structure for holding full modulation type descriptor
struct modulation_type_s {
    const char * name;          // short name (e.g. 'bpsk')
    const char * fullname;      // full name (e.g. 'binary phase-shift keying')
    modulation_scheme scheme;   // modulation scheme (e.g. LIQUID_MODEM_BPSK)
    unsigned int bps;           // modulation depth (e.g. 1)
};

// full modulation type descriptor
extern const struct modulation_type_s modulation_types[LIQUID_MODEM_NUM_SCHEMES];

// Print compact list of existing and available modulation schemes
void liquid_print_modulation_schemes();

// returns modulation_scheme based on input string
modulation_scheme liquid_getopt_str2mod(const char * _str);

// query basic modulation types
int liquid_modem_is_psk(modulation_scheme _ms);
int liquid_modem_is_dpsk(modulation_scheme _ms);
int liquid_modem_is_ask(modulation_scheme _ms);
int liquid_modem_is_qam(modulation_scheme _ms);
int liquid_modem_is_apsk(modulation_scheme _ms);

// useful functions

// counts the number of different bits between two symbols
unsigned int count_bit_errors(unsigned int _s1, unsigned int _s2);

// counts the number of different bits between two arrays of symbols
//  _msg0   :   original message [size: _n x 1]
//  _msg1   :   copy of original message [size: _n x 1]
//  _n      :   message size
unsigned int count_bit_errors_array(unsigned char * _msg0,
                                    unsigned char * _msg1,
                                    unsigned int _n);

// converts binary-coded decimal (BCD) to gray, ensuring successive values
// differ by exactly one bit
unsigned int gray_encode(unsigned int symbol_in);

// converts a gray-encoded symbol to binary-coded decimal (BCD)
unsigned int gray_decode(unsigned int symbol_in);

// pack soft bits into symbol
//  _soft_bits  :   soft input bits [size: _bps x 1]
//  _bps        :   bits per symbol
//  _sym_out    :   output symbol, value in [0,2^_bps)
void liquid_pack_soft_bits(unsigned char * _soft_bits,
                           unsigned int _bps,
                           unsigned int * _sym_out);

// unpack soft bits into symbol
//  _sym_in     :   input symbol, value in [0,2^_bps)
//  _bps        :   bits per symbol
//  _soft_bits  :   soft output bits [size: _bps x 1]
void liquid_unpack_soft_bits(unsigned int _sym_in,
                             unsigned int _bps,
                             unsigned char * _soft_bits);


//
// Linear modem
//

#define LIQUID_MODEM_MANGLE_FLOAT(name) LIQUID_CONCAT(modem,name)

// Macro    :   MODEM
//  MODEM   :   name-mangling macro
//  T       :   primitive data type
//  TC      :   primitive data type (complex)
#define LIQUID_MODEM_DEFINE_API(MODEM,T,TC)                     \
typedef struct MODEM(_s) * MODEM();                             \
                                                                \
/* create digital modem object                              */  \
MODEM() MODEM(_create)(modulation_scheme _scheme);              \
                                                                \
/* create arbitrary digital modem object                    */  \
/*  _table  :   array of complex constellation points       */  \
/*  _M      :   modulation order and table size             */  \
MODEM() MODEM(_create_arbitrary)(TC *         _table,           \
                                 unsigned int _M);              \
                                                                \
/* recreate modulation scheme, re-allocating memory as      */  \
/* necessary                                                */  \
MODEM() MODEM(_recreate)(MODEM()           _q,                  \
                         modulation_scheme _scheme);            \
                                                                \
void MODEM(_destroy)(MODEM() _q);                               \
void MODEM(_print)(  MODEM() _q);                               \
void MODEM(_reset)(  MODEM() _q);                               \
                                                                \
/* generate random symbol                                   */  \
unsigned int MODEM(_gen_rand_sym)(MODEM() _q);                  \
                                                                \
/* Accessor functions */                                        \
unsigned int      MODEM(_get_bps)   (MODEM() _q);               \
modulation_scheme MODEM(_get_scheme)(MODEM() _q);               \
                                                                \
/* generic modulate function; simply queries modem scheme   */  \
/* and calls appropriate subroutine                         */  \
/*  _q  :   modem object                                    */  \
/*  _s  :   input symbol                                    */  \
/*  _x  :   output sample                                   */  \
void MODEM(_modulate)(MODEM()      _q,                          \
                      unsigned int _s,                          \
                      TC *         _y);                         \
                                                                \
/* generic hard-decision demodulation function              */  \
/*  _q  :   modem object                                    */  \
/*  _x  :   input sample                                    */  \
/*  _s  :   output symbol                                   */  \
void MODEM(_demodulate)(MODEM()        _q,                      \
                        TC             _x,                      \
                        unsigned int * _s);                     \
                                                                \
/* generic soft-decision demodulation function              */  \
/*  _q          :   modem object                            */  \
/*  _x          :   input sample                            */  \
/*  _s          :   output hard symbol                      */  \
/*  _soft_bits  :   output soft bits                        */  \
void MODEM(_demodulate_soft)(MODEM()         _q,                \
                             TC              _x,                \
                             unsigned int  * _s,                \
                             unsigned char * _soft_bits);       \
                                                                \
/* get demodulator's estimated transmit sample */               \
void MODEM(_get_demodulator_sample)(MODEM() _q,                 \
                                    TC *    _x_hat);            \
                                                                \
/* get demodulator phase error */                               \
float MODEM(_get_demodulator_phase_error)(MODEM() _q);          \
                                                                \
/* get demodulator error vector magnitude */                    \
float MODEM(_get_demodulator_evm)(MODEM() _q);                  \

// define modem APIs
LIQUID_MODEM_DEFINE_API(LIQUID_MODEM_MANGLE_FLOAT,float,liquid_float_complex)


//
// continuous-phase modulation
//

// gmskmod : GMSK modulator
typedef struct gmskmod_s * gmskmod;

// create gmskmod object
//  _k      :   samples/symbol
//  _m      :   filter delay (symbols)
//  _BT     :   excess bandwidth factor
gmskmod gmskmod_create(unsigned int _k,
                       unsigned int _m,
                       float        _BT);
void gmskmod_destroy(gmskmod _q);
void gmskmod_print(gmskmod _q);
void gmskmod_reset(gmskmod _q);
void gmskmod_modulate(gmskmod _q,
                      unsigned int _sym,
                      liquid_float_complex * _y);


// gmskdem : GMSK demodulator
typedef struct gmskdem_s * gmskdem;

// create gmskdem object
//  _k      :   samples/symbol
//  _m      :   filter delay (symbols)
//  _BT     :   excess bandwidth factor
gmskdem gmskdem_create(unsigned int _k,
                       unsigned int _m,
                       float        _BT);
void gmskdem_destroy(gmskdem _q);
void gmskdem_print(gmskdem _q);
void gmskdem_reset(gmskdem _q);
void gmskdem_set_eq_bw(gmskdem _q, float _bw);
void gmskdem_demodulate(gmskdem _q,
                        liquid_float_complex * _y,
                        unsigned int * _sym);

//
// continuous phase frequency-shift keying (CP-FSK) modems
//

// CP-FSK filter prototypes
typedef enum {
    LIQUID_CPFSK_SQUARE=0,      // square pulse
    LIQUID_CPFSK_RCOS_FULL,     // raised-cosine (full response)
    LIQUID_CPFSK_RCOS_PARTIAL,  // raised-cosine (partial response)
    LIQUID_CPFSK_GMSK,          // Gauss minimum-shift keying pulse
} liquid_cpfsk_filter;

// CP-FSK modulator
typedef struct cpfskmod_s * cpfskmod;

// create cpfskmod object (frequency modulator)
//  _bps    :   bits per symbol, _bps > 0
//  _h      :   modulation index, _h > 0
//  _k      :   samples/symbol, _k > 1, _k even
//  _m      :   filter delay (symbols), _m > 0
//  _beta   :   filter bandwidth parameter, _beta > 0
//  _type   :   filter type (e.g. LIQUID_CPFSK_SQUARE)
cpfskmod cpfskmod_create(unsigned int _bps,
                         float        _h,
                         unsigned int _k,
                         unsigned int _m,
                         float        _beta,
                         int          _type);
//cpfskmod cpfskmod_create_msk(unsigned int _k);
//cpfskmod cpfskmod_create_gmsk(unsigned int _k, float _BT);

// destroy cpfskmod object
void cpfskmod_destroy(cpfskmod _q);

// print cpfskmod object internals
void cpfskmod_print(cpfskmod _q);

// reset state
void cpfskmod_reset(cpfskmod _q);

// get transmit delay [symbols]
unsigned int cpfskmod_get_delay(cpfskmod _q);

// modulate sample
//  _q      :   frequency modulator object
//  _s      :   input symbol
//  _y      :   output sample array [size: _k x 1]
void cpfskmod_modulate(cpfskmod               _q,
                       unsigned int           _s,
                       liquid_float_complex * _y);



// CP-FSK demodulator
typedef struct cpfskdem_s * cpfskdem;

// create cpfskdem object (frequency modulator)
//  _bps    :   bits per symbol, _bps > 0
//  _h      :   modulation index, _h > 0
//  _k      :   samples/symbol, _k > 1, _k even
//  _m      :   filter delay (symbols), _m > 0
//  _beta   :   filter bandwidth parameter, _beta > 0
//  _type   :   filter type (e.g. LIQUID_CPFSK_SQUARE)
cpfskdem cpfskdem_create(unsigned int _bps,
                         float        _h,
                         unsigned int _k,
                         unsigned int _m,
                         float        _beta,
                         int          _type);
//cpfskdem cpfskdem_create_msk(unsigned int _k);
//cpfskdem cpfskdem_create_gmsk(unsigned int _k, float _BT);

// destroy cpfskdem object
void cpfskdem_destroy(cpfskdem _q);

// print cpfskdem object internals
void cpfskdem_print(cpfskdem _q);

// reset state
void cpfskdem_reset(cpfskdem _q);

// get receive delay [symbols]
unsigned int cpfskdem_get_delay(cpfskdem _q);

#if 0
// demodulate array of samples
//  _q      :   continuous-phase frequency demodulator object
//  _y      :   input sample array [size: _n x 1]
//  _n      :   input sample array length
//  _s      :   output symbol array
//  _nw     :   number of output symbols written
void cpfskdem_demodulate(cpfskdem               _q,
                         liquid_float_complex * _y,
                         unsigned int           _n,
                         unsigned int         * _s,
                         unsigned int         * _nw);
#else
// demodulate array of samples, assuming perfect timing
//  _q      :   continuous-phase frequency demodulator object
//  _y      :   input sample array [size: _k x 1]
unsigned int cpfskdem_demodulate(cpfskdem               _q,
                                 liquid_float_complex * _y);
#endif



//
// M-ary frequency-shift keying (MFSK) modems
//

// FSK modulator
typedef struct fskmod_s * fskmod;

// create fskmod object (frequency modulator)
//  _m          :   bits per symbol, _bps > 0
//  _k          :   samples/symbol, _k >= 2^_m
//  _bandwidth  :   total signal bandwidth, (0,0.5)
fskmod fskmod_create(unsigned int _m,
                     unsigned int _k,
                     float        _bandwidth);

// destroy fskmod object
void fskmod_destroy(fskmod _q);

// print fskmod object internals
void fskmod_print(fskmod _q);

// reset state
void fskmod_reset(fskmod _q);

// modulate sample
//  _q      :   frequency modulator object
//  _s      :   input symbol
//  _y      :   output sample array [size: _k x 1]
void fskmod_modulate(fskmod                 _q,
                     unsigned int           _s,
                     liquid_float_complex * _y);



// FSK demodulator
typedef struct fskdem_s * fskdem;

// create fskdem object (frequency demodulator)
//  _m          :   bits per symbol, _bps > 0
//  _k          :   samples/symbol, _k >= 2^_m
//  _bandwidth  :   total signal bandwidth, (0,0.5)
fskdem fskdem_create(unsigned int _m,
                     unsigned int _k,
                     float        _bandwidth);

// destroy fskdem object
void fskdem_destroy(fskdem _q);

// print fskdem object internals
void fskdem_print(fskdem _q);

// reset state
void fskdem_reset(fskdem _q);

// demodulate symbol, assuming perfect symbol timing
//  _q      :   fskdem object
//  _y      :   input sample array [size: _k x 1]
unsigned int fskdem_demodulate(fskdem                 _q,
                               liquid_float_complex * _y);

// get demodulator frequency error
float fskdem_get_frequency_error(fskdem _q);


// 
// Analog frequency modulator
//
#define LIQUID_FREQMOD_MANGLE_FLOAT(name) LIQUID_CONCAT(freqmod,name)

// Macro    :   FREQMOD (analog frequency modulator)
//  FREQMOD :   name-mangling macro
//  T       :   primitive data type
//  TC      :   primitive data type (complex)
#define LIQUID_FREQMOD_DEFINE_API(FREQMOD,T,TC)                 \
typedef struct FREQMOD(_s) * FREQMOD();                         \
                                                                \
/* create freqmod object (frequency modulator)              */  \
/*  _kf     :   modulation factor                           */  \
FREQMOD() FREQMOD(_create)(float _kf);                          \
                                                                \
/* destroy freqmod object                                   */  \
void FREQMOD(_destroy)(FREQMOD() _q);                           \
                                                                \
/* print freqmod object internals                           */  \
void FREQMOD(_print)(FREQMOD() _q);                             \
                                                                \
/* reset state                                              */  \
void FREQMOD(_reset)(FREQMOD() _q);                             \
                                                                \
/* modulate single sample                                   */  \
/*  _q      :   frequency modulator object                  */  \
/*  _m      :   message signal m(t)                         */  \
/*  _s      :   complex baseband signal s(t)                */  \
void FREQMOD(_modulate)(FREQMOD() _q,                           \
                        T         _m,                           \
                        TC *      _s);                          \
                                                                \
/* modulate block of samples                                */  \
/*  _q      :   frequency modulator object                  */  \
/*  _m      :   message signal m(t), [size: _n x 1]         */  \
/*  _n      :   number of input, output samples             */  \
/*  _s      :   complex baseband signal s(t) [size: _n x 1] */  \
void FREQMOD(_modulate_block)(FREQMOD()    _q,                  \
                              T *          _m,                  \
                              unsigned int _n,                  \
                              TC *         _s);                 \

// define freqmod APIs
LIQUID_FREQMOD_DEFINE_API(LIQUID_FREQMOD_MANGLE_FLOAT,float,liquid_float_complex)

// 
// Analog frequency demodulator
//

#define LIQUID_FREQDEM_MANGLE_FLOAT(name) LIQUID_CONCAT(freqdem,name)

// Macro    :   FREQDEM (analog frequency modulator)
//  FREQDEM :   name-mangling macro
//  T       :   primitive data type
//  TC      :   primitive data type (complex)
#define LIQUID_FREQDEM_DEFINE_API(FREQDEM,T,TC)                 \
typedef struct FREQDEM(_s) * FREQDEM();                         \
                                                                \
/* create freqdem object (frequency modulator)              */  \
/*  _kf      :   modulation factor                          */  \
FREQDEM() FREQDEM(_create)(float _kf);                          \
                                                                \
/* destroy freqdem object                                   */  \
void FREQDEM(_destroy)(FREQDEM() _q);                           \
                                                                \
/* print freqdem object internals                           */  \
void FREQDEM(_print)(FREQDEM() _q);                             \
                                                                \
/* reset state                                              */  \
void FREQDEM(_reset)(FREQDEM() _q);                             \
                                                                \
/* demodulate sample                                        */  \
/*  _q      :   frequency modulator object                  */  \
/*  _r      :   received signal r(t)                        */  \
/*  _m      :   output message signal m(t)                  */  \
void FREQDEM(_demodulate)(FREQDEM() _q,                         \
                          TC        _r,                         \
                          T *       _m);                        \
                                                                \
/* demodulate block of samples                              */  \
/*  _q      :   frequency demodulator object                */  \
/*  _r      :   received signal r(t) [size: _n x 1]         */  \
/*  _n      :   number of input, output samples             */  \
/*  _m      :   message signal m(t), [size: _n x 1]         */  \
void FREQDEM(_demodulate_block)(FREQDEM()    _q,                \
                                TC *         _r,                \
                                unsigned int _n,                \
                                T *          _m);               \

// define freqdem APIs
LIQUID_FREQDEM_DEFINE_API(LIQUID_FREQDEM_MANGLE_FLOAT,float,liquid_float_complex)



// amplitude modulation types
typedef enum {
    LIQUID_AMPMODEM_DSB=0,  // double side-band
    LIQUID_AMPMODEM_USB,    // single side-band (upper)
    LIQUID_AMPMODEM_LSB     // single side-band (lower)
} liquid_ampmodem_type;

typedef struct ampmodem_s * ampmodem;

// create ampmodem object
//  _m                  :   modulation index
//  _fc                 :   carrier frequency, range: [-0.5,0.5]
//  _type               :   AM type (e.g. LIQUID_AMPMODEM_DSB)
//  _suppressed_carrier :   carrier suppression flag
ampmodem ampmodem_create(float _m,
                         float _fc,
                         liquid_ampmodem_type _type,
                         int _suppressed_carrier);

// destroy ampmodem object
void ampmodem_destroy(ampmodem _fm);

// print ampmodem object internals
void ampmodem_print(ampmodem _fm);

// reset ampmodem object state
void ampmodem_reset(ampmodem _fm);

// modulate sample
void ampmodem_modulate(ampmodem _fm,
                       float _x,
                       liquid_float_complex *_y);

void ampmodem_modulate_block(ampmodem _q,
                             float * _m,
                             unsigned int _n,
                             liquid_float_complex *_s);

// demodulate sample
void ampmodem_demodulate(ampmodem _fm,
                         liquid_float_complex _y,
                         float *_x);

void ampmodem_demodulate_block(ampmodem _q,
                               liquid_float_complex * _r,
                               unsigned int _n,
                               float * _m);

//
// MODULE : multichannel
//


#define FIRPFBCH_NYQUIST        0
#define FIRPFBCH_ROOTNYQUIST    1

#define LIQUID_ANALYZER         0
#define LIQUID_SYNTHESIZER      1


//
// Finite impulse response polyphase filterbank channelizer
//

#define LIQUID_FIRPFBCH_MANGLE_CRCF(name) LIQUID_CONCAT(firpfbch_crcf,name)
#define LIQUID_FIRPFBCH_MANGLE_CCCF(name) LIQUID_CONCAT(firpfbch_cccf,name)

// Macro:
//   FIRPFBCH   : name-mangling macro
//   TO         : output data type
//   TC         : coefficients data type
//   TI         : input data type
#define LIQUID_FIRPFBCH_DEFINE_API(FIRPFBCH,TO,TC,TI)           \
typedef struct FIRPFBCH(_s) * FIRPFBCH();                       \
                                                                \
/* create finite impulse response polyphase filter-bank     */  \
/* channelizer object from external coefficients            */  \
/*  _type   : channelizer type, e.g. LIQUID_ANALYZER        */  \
/*  _M      : number of channels                            */  \
/*  _p      : number of coefficients for each channel       */  \
/*  _h      : coefficients [size: _M*_p x 1]                */  \
FIRPFBCH() FIRPFBCH(_create)(int          _type,                \
                             unsigned int _M,                   \
                             unsigned int _p,                   \
                             TC *         _h);                  \
                                                                \
/* create FIR polyphase filterbank channelizer object with  */  \
/* prototype filter based on windowed Kaiser design         */  \
/*  _type   : type (LIQUID_ANALYZER | LIQUID_SYNTHESIZER)   */  \
/*  _M      : number of channels                            */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _As     : stop-band attentuation [dB]                   */  \
FIRPFBCH() FIRPFBCH(_create_kaiser)(int          _type,         \
                                    unsigned int _M,            \
                                    unsigned int _m,            \
                                    float        _As);          \
                                                                \
/* create FIR polyphase filterbank channelizer object with  */  \
/* prototype root-Nyquist filter                            */  \
/*  _type   : type (LIQUID_ANALYZER | LIQUID_SYNTHESIZER)   */  \
/*  _M      : number of channels                            */  \
/*  _m      : filter delay (symbols)                        */  \
/*  _beta   : filter excess bandwidth factor, in [0,1]      */  \
/*  _ftype  : filter prototype (rrcos, rkaiser, etc.)       */  \
FIRPFBCH() FIRPFBCH(_create_rnyquist)(int          _type,       \
                                      unsigned int _M,          \
                                      unsigned int _m,          \
                                      float        _beta,       \
                                      int          _ftype);     \
                                                                \
/* destroy firpfbch object                                  */  \
void FIRPFBCH(_destroy)(FIRPFBCH() _q);                         \
                                                                \
/* clear/reset firpfbch internal state                      */  \
void FIRPFBCH(_reset)(FIRPFBCH() _q);                           \
                                                                \
/* print firpfbch internal parameters to stdout             */  \
void FIRPFBCH(_print)(FIRPFBCH() _q);                           \
                                                                \
/* execute filterbank as synthesizer on block of samples    */  \
/*  _q      : filterbank channelizer object                 */  \
/*  _x      : channelized input, [size: num_channels x 1]   */  \
/*  _y      : output time series, [size: num_channels x 1]  */  \
void FIRPFBCH(_synthesizer_execute)(FIRPFBCH() _q,              \
                                    TI *       _x,              \
                                    TO *       _y);             \
                                                                \
/* execute filterbank as analyzer on block of samples       */  \
/*  _q      : filterbank channelizer object                 */  \
/*  _x      : input time series, [size: num_channels x 1]   */  \
/*  _y      : channelized output, [size: num_channels x 1]  */  \
void FIRPFBCH(_analyzer_execute)(FIRPFBCH() _q,                 \
                                 TI *       _x,                 \
                                 TO *       _y);                \


LIQUID_FIRPFBCH_DEFINE_API(LIQUID_FIRPFBCH_MANGLE_CRCF,
                           liquid_float_complex,
                           float,
                           liquid_float_complex)

LIQUID_FIRPFBCH_DEFINE_API(LIQUID_FIRPFBCH_MANGLE_CCCF,
                           liquid_float_complex,
                           liquid_float_complex,
                           liquid_float_complex)


//
// Finite impulse response polyphase filterbank channelizer
// with output rate 2 Fs / M
//

#define LIQUID_FIRPFBCH2_MANGLE_CRCF(name) LIQUID_CONCAT(firpfbch2_crcf,name)

// Macro:
//   FIRPFBCH2  : name-mangling macro
//   TO         : output data type
//   TC         : coefficients data type
//   TI         : input data type
#define LIQUID_FIRPFBCH2_DEFINE_API(FIRPFBCH2,TO,TC,TI)         \
typedef struct FIRPFBCH2(_s) * FIRPFBCH2();                     \
                                                                \
/* create firpfbch2 object                                  */  \
/*  _type   :   channelizer type (e.g. LIQUID_ANALYZER)     */  \
/*  _M      :   number of channels (must be even)           */  \
/*  _m      :   prototype filter semi-lenth, length=2*M*m   */  \
/*  _h      :   prototype filter coefficient array          */  \
FIRPFBCH2() FIRPFBCH2(_create)(int          _type,              \
                               unsigned int _M,                 \
                               unsigned int _m,                 \
                               TC *         _h);                \
                                                                \
/* create firpfbch2 object using Kaiser window prototype    */  \
/*  _type   :   channelizer type (e.g. LIQUID_ANALYZER)     */  \
/*  _M      :   number of channels (must be even)           */  \
/*  _m      :   prototype filter semi-lenth, length=2*M*m+1 */  \
/*  _As     :   filter stop-band attenuation [dB]           */  \
FIRPFBCH2() FIRPFBCH2(_create_kaiser)(int          _type,       \
                                      unsigned int _M,          \
                                      unsigned int _m,          \
                                      float        _As);        \
                                                                \
/* destroy firpfbch2 object, freeing internal memory        */  \
void FIRPFBCH2(_destroy)(FIRPFBCH2() _q);                       \
                                                                \
/* reset firpfbch2 object internals                         */  \
void FIRPFBCH2(_reset)(FIRPFBCH2() _q);                         \
                                                                \
/* print firpfbch2 object internals                         */  \
void FIRPFBCH2(_print)(FIRPFBCH2() _q);                         \
                                                                \
/* execute filterbank channelizer                           */  \
/* LIQUID_ANALYZER:     input: M/2, output: M               */  \
/* LIQUID_SYNTHESIZER:  input: M,   output: M/2             */  \
/*  _x      :   channelizer input                           */  \
/*  _y      :   channelizer output                          */  \
void FIRPFBCH2(_execute)(FIRPFBCH2() _q,                        \
                         TI *        _x,                        \
                         TO *        _y);                       \


LIQUID_FIRPFBCH2_DEFINE_API(LIQUID_FIRPFBCH2_MANGLE_CRCF,
                            liquid_float_complex,
                            float,
                            liquid_float_complex)



#define OFDMFRAME_SCTYPE_NULL   0
#define OFDMFRAME_SCTYPE_PILOT  1
#define OFDMFRAME_SCTYPE_DATA   2

// initialize default subcarrier allocation
//  _M      :   number of subcarriers
//  _p      :   output subcarrier allocation array, [size: _M x 1]
void ofdmframe_init_default_sctype(unsigned int    _M,
                                   unsigned char * _p);

// initialize default subcarrier allocation
//  _M      :   number of subcarriers
//  _f0     :   lower frequency band, _f0 in [-0.5,0.5]
//  _f1     :   upper frequency band, _f1 in [-0.5,0.5]
//  _p      :   output subcarrier allocation array, [size: _M x 1]
void ofdmframe_init_sctype_range(unsigned int    _M,
                                 float           _f0,
                                 float           _f1,
                                 unsigned char * _p);

// validate subcarrier type (count number of null, pilot, and data
// subcarriers in the allocation)
//  _p          :   subcarrier allocation array, [size: _M x 1]
//  _M          :   number of subcarriers
//  _M_null     :   output number of null subcarriers
//  _M_pilot    :   output number of pilot subcarriers
//  _M_data     :   output number of data subcarriers
void ofdmframe_validate_sctype(unsigned char * _p,
                               unsigned int _M,
                               unsigned int * _M_null,
                               unsigned int * _M_pilot,
                               unsigned int * _M_data);

// print subcarrier allocation to screen
//  _p      :   output subcarrier allocation array, [size: _M x 1]
//  _M      :   number of subcarriers
void ofdmframe_print_sctype(unsigned char * _p,
                            unsigned int    _M);


// 
// OFDM frame (symbol) generator
//
typedef struct ofdmframegen_s * ofdmframegen;

// create OFDM framing generator object
//  _M          :   number of subcarriers, >10 typical
//  _cp_len     :   cyclic prefix length
//  _taper_len  :   taper length (OFDM symbol overlap)
//  _p          :   subcarrier allocation (null, pilot, data), [size: _M x 1]
ofdmframegen ofdmframegen_create(unsigned int    _M,
                                 unsigned int    _cp_len,
                                 unsigned int    _taper_len,
                                 unsigned char * _p);

void ofdmframegen_destroy(ofdmframegen _q);

void ofdmframegen_print(ofdmframegen _q);

void ofdmframegen_reset(ofdmframegen _q);

// write first S0 symbol
void ofdmframegen_write_S0a(ofdmframegen _q,
                            liquid_float_complex *_y);

// write second S0 symbol
void ofdmframegen_write_S0b(ofdmframegen _q,
                            liquid_float_complex *_y);

// write S1 symbol
void ofdmframegen_write_S1(ofdmframegen _q,
                           liquid_float_complex *_y);

// write data symbol
void ofdmframegen_writesymbol(ofdmframegen _q,
                              liquid_float_complex * _x,
                              liquid_float_complex *_y);

// write tail
void ofdmframegen_writetail(ofdmframegen _q,
                            liquid_float_complex * _x);

// 
// OFDM frame (symbol) synchronizer
//
typedef int (*ofdmframesync_callback)(liquid_float_complex * _y,
                                      unsigned char * _p,
                                      unsigned int _M,
                                      void * _userdata);
typedef struct ofdmframesync_s * ofdmframesync;

// create OFDM framing synchronizer object
//  _M          :   number of subcarriers, >10 typical
//  _cp_len     :   cyclic prefix length
//  _taper_len  :   taper length (OFDM symbol overlap)
//  _p          :   subcarrier allocation (null, pilot, data), [size: _M x 1]
//  _callback   :   user-defined callback function
//  _userdata   :   user-defined data pointer
ofdmframesync ofdmframesync_create(unsigned int           _M,
                                   unsigned int           _cp_len,
                                   unsigned int           _taper_len,
                                   unsigned char *        _p,
                                   ofdmframesync_callback _callback,
                                   void *                 _userdata);
void ofdmframesync_destroy(ofdmframesync _q);
void ofdmframesync_print(ofdmframesync _q);
void ofdmframesync_reset(ofdmframesync _q);
int  ofdmframesync_is_frame_open(ofdmframesync _q);
void ofdmframesync_execute(ofdmframesync _q,
                           liquid_float_complex * _x,
                           unsigned int _n);

// query methods
float ofdmframesync_get_rssi(ofdmframesync _q); // received signal strength indication
float ofdmframesync_get_cfo(ofdmframesync _q);  // carrier offset estimate

// debugging
void ofdmframesync_debug_enable(ofdmframesync _q);
void ofdmframesync_debug_disable(ofdmframesync _q);
void ofdmframesync_debug_print(ofdmframesync _q, const char * _filename);


// 
// MODULE : nco (numerically-controlled oscillator)
//

// oscillator type
//  LIQUID_NCO  :   numerically-controlled oscillator (fast)
//  LIQUID_VCO  :   "voltage"-controlled oscillator (precise)
typedef enum {
    LIQUID_NCO=0,
    LIQUID_VCO
} liquid_ncotype;

#define LIQUID_NCO_MANGLE_FLOAT(name) LIQUID_CONCAT(nco_crcf, name)

// large macro
//   NCO    : name-mangling macro
//   T      : primitive data type
//   TC     : input/output data type
#define LIQUID_NCO_DEFINE_API(NCO,T,TC)                         \
typedef struct NCO(_s) * NCO();                                 \
                                                                \
NCO() NCO(_create)(liquid_ncotype _type);                       \
void NCO(_destroy)(NCO() _q);                                   \
void NCO(_print)(NCO() _q);                                     \
                                                                \
/* set phase/frequency to zero, reset pll filter        */      \
void NCO(_reset)(NCO() _q);                                     \
                                                                \
/* get/set/adjust internal frequency/phase              */      \
T    NCO(_get_frequency)(   NCO() _q);                          \
void NCO(_set_frequency)(   NCO() _q, T _f);                    \
void NCO(_adjust_frequency)(NCO() _q, T _df);                   \
T    NCO(_get_phase)(       NCO() _q);                          \
void NCO(_set_phase)(       NCO() _q, T _phi);                  \
void NCO(_adjust_phase)(    NCO() _q, T _dphi);                 \
                                                                \
/* increment phase by internal phase step (frequency)   */      \
void NCO(_step)(NCO() _q);                                      \
                                                                \
/* compute trigonometric functions                      */      \
T NCO(_sin)(NCO() _q);                                          \
T NCO(_cos)(NCO() _q);                                          \
void NCO(_sincos)(NCO() _q, T* _s, T* _c);                      \
void NCO(_cexpf)(NCO() _q, TC * _y);                            \
                                                                \
/* pll : phase-locked loop                              */      \
void NCO(_pll_set_bandwidth)(NCO() _q, T _bandwidth);           \
void NCO(_pll_step)(NCO() _q, T _dphi);                         \
                                                                \
/* Rotate input sample up by NCO angle (no stepping)    */      \
void NCO(_mix_up)(NCO() _q, TC _x, TC *_y);                     \
                                                                \
/* Rotate input sample down by NCO angle (no stepping)  */      \
void NCO(_mix_down)(NCO() _q, TC _x, TC *_y);                   \
                                                                \
/* Rotate input vector up by NCO angle (stepping)       */      \
/*  _q      :   nco object                              */      \
/*  _x      :   input vector [size: _N x 1]             */      \
/*  _y      :   output vector [size: _N x 1]            */      \
/*  _N      :   vector size                             */      \
void NCO(_mix_block_up)(NCO() _q,                               \
                        TC *_x,                                 \
                        TC *_y,                                 \
                        unsigned int _N);                       \
                                                                \
/* Rotate input vector down by NCO angle (stepping)     */      \
/*  _q      :   nco object                              */      \
/*  _x      :   input vector [size: _N x 1]             */      \
/*  _y      :   output vector [size: _N x 1]            */      \
/*  _N      :   vector size                             */      \
void NCO(_mix_block_down)(NCO() _q,                             \
                          TC *_x,                               \
                          TC *_y,                               \
                          unsigned int _N);                     \

// Define nco APIs
LIQUID_NCO_DEFINE_API(LIQUID_NCO_MANGLE_FLOAT, float, liquid_float_complex)


// nco utilities

// unwrap phase of array (basic)
void liquid_unwrap_phase(float * _theta, unsigned int _n);

// unwrap phase of array (advanced)
void liquid_unwrap_phase2(float * _theta, unsigned int _n);



//
// MODULE : optimization
//

// utility function pointer definition
typedef float (*utility_function)(void *       _userdata,
                                  float *      _v,
                                  unsigned int _n);

// n-dimensional Rosenbrock utility function (minimum at _v = {1,1,1...}
//  _userdata   :   user-defined data structure (convenience)
//  _v          :   input vector [size: _n x 1]
//  _n          :   input vector size
float liquid_rosenbrock(void *       _userdata,
                        float *      _v,
                        unsigned int _n);

// n-dimensional inverse Gauss utility function (minimum at _v = {0,0,0...}
//  _userdata   :   user-defined data structure (convenience)
//  _v          :   input vector [size: _n x 1]
//  _n          :   input vector size
float liquid_invgauss(void *       _userdata,
                      float *      _v,
                      unsigned int _n);

// n-dimensional multimodal utility function (minimum at _v = {0,0,0...}
//  _userdata   :   user-defined data structure (convenience)
//  _v          :   input vector [size: _n x 1]
//  _n          :   input vector size
float liquid_multimodal(void *       _userdata,
                        float *      _v,
                        unsigned int _n);

// n-dimensional spiral utility function (minimum at _v = {0,0,0...}
//  _userdata   :   user-defined data structure (convenience)
//  _v          :   input vector [size: _n x 1]
//  _n          :   input vector size
float liquid_spiral(void *       _userdata,
                    float *      _v,
                    unsigned int _n);


//
// Gradient search
//

#define LIQUID_OPTIM_MINIMIZE (0)
#define LIQUID_OPTIM_MAXIMIZE (1)

typedef struct gradsearch_s * gradsearch;

// Create a gradient search object
//   _userdata          :   user data object pointer
//   _v                 :   array of parameters to optimize
//   _num_parameters    :   array length (number of parameters to optimize)
//   _u                 :   utility function pointer
//   _direction         :   search direction (e.g. LIQUID_OPTIM_MAXIMIZE)
gradsearch gradsearch_create(void *           _userdata,
                             float *          _v,
                             unsigned int     _num_parameters,
                             utility_function _utility,
                             int              _direction);

// Destroy a gradsearch object
void gradsearch_destroy(gradsearch _q);

// Prints current status of search
void gradsearch_print(gradsearch _q);

// Iterate once
float gradsearch_step(gradsearch _q);

// Execute the search
float gradsearch_execute(gradsearch   _q,
                         unsigned int _max_iterations,
                         float        _target_utility);


// quasi-Newton search
typedef struct qnsearch_s * qnsearch;

// Create a simple qnsearch object; parameters are specified internally
//   _userdata          :   userdata
//   _v                 :   array of parameters to optimize
//   _num_parameters    :   array length
//   _get_utility       :   utility function pointer
//   _direction         :   search direction (e.g. LIQUID_OPTIM_MAXIMIZE)
qnsearch qnsearch_create(void *           _userdata,
                         float *          _v,
                         unsigned int     _num_parameters,
                         utility_function _u,
                         int              _direction);

// Destroy a qnsearch object
void qnsearch_destroy(qnsearch _g);

// Prints current status of search
void qnsearch_print(qnsearch _g);

// Resets internal state
void qnsearch_reset(qnsearch _g);

// Iterate once
void qnsearch_step(qnsearch _g);

// Execute the search
float qnsearch_execute(qnsearch _g,
                       unsigned int _max_iterations,
                       float _target_utility);

// 
// chromosome (for genetic algorithm search)
//
typedef struct chromosome_s * chromosome;

// create a chromosome object, variable bits/trait
chromosome chromosome_create(unsigned int * _bits_per_trait,
                             unsigned int _num_traits);

// create a chromosome object, all traits same resolution
chromosome chromosome_create_basic(unsigned int _num_traits,
                                   unsigned int _bits_per_trait);

// create a chromosome object, cloning a parent
chromosome chromosome_create_clone(chromosome _parent);

// copy existing chromosomes' internal traits (all other internal
// parameters must be equal)
void chromosome_copy(chromosome _parent, chromosome _child);

// Destroy a chromosome object
void chromosome_destroy(chromosome _c);

// get number of traits in chromosome
unsigned int chromosome_get_num_traits(chromosome _c);

// Print chromosome values to screen (binary representation)
void chromosome_print(chromosome _c);

// Print chromosome values to screen (floating-point representation)
void chromosome_printf(chromosome _c);

// clear chromosome (set traits to zero)
void chromosome_reset(chromosome _c);

// initialize chromosome on integer values
void chromosome_init(chromosome _c,
                     unsigned int * _v);

// initialize chromosome on floating-point values
void chromosome_initf(chromosome _c,
                      float * _v);

// Mutates chromosome _c at _index
void chromosome_mutate(chromosome _c, unsigned int _index);

// Resulting chromosome _c is a crossover of parents _p1 and _p2 at _threshold
void chromosome_crossover(chromosome _p1,
                          chromosome _p2,
                          chromosome _c,
                          unsigned int _threshold);

// Initializes chromosome to random value
void chromosome_init_random(chromosome _c);

// Returns integer representation of chromosome
unsigned int chromosome_value(chromosome _c,
                              unsigned int _index);

// Returns floating-point representation of chromosome
float chromosome_valuef(chromosome _c,
                        unsigned int _index);

// 
// genetic algorithm search
//
typedef struct gasearch_s * gasearch;

typedef float (*gasearch_utility)(void * _userdata, chromosome _c);

// Create a simple gasearch object; parameters are specified internally
//  _utility            :   chromosome fitness utility function
//  _userdata           :   user data, void pointer passed to _get_utility() callback
//  _parent             :   initial population parent chromosome, governs precision, etc.
//  _minmax             :   search direction
gasearch gasearch_create(gasearch_utility _u,
                         void * _userdata,
                         chromosome _parent,
                         int _minmax);

// Create a gasearch object, specifying search parameters
//  _utility            :   chromosome fitness utility function
//  _userdata           :   user data, void pointer passed to _get_utility() callback
//  _parent             :   initial population parent chromosome, governs precision, etc.
//  _minmax             :   search direction
//  _population_size    :   number of chromosomes in population
//  _mutation_rate      :   probability of mutating chromosomes
gasearch gasearch_create_advanced(gasearch_utility _utility,
                                  void * _userdata,
                                  chromosome _parent,
                                  int _minmax,
                                  unsigned int _population_size,
                                  float _mutation_rate);


// Destroy a gasearch object
void gasearch_destroy(gasearch _q);

// print search parameter internals
void gasearch_print(gasearch _q);

// set mutation rate
void gasearch_set_mutation_rate(gasearch _q,
                                float _mutation_rate);

// set population/selection size
//  _q                  :   ga search object
//  _population_size    :   new population size (number of chromosomes)
//  _selection_size     :   selection size (number of parents for new generation)
void gasearch_set_population_size(gasearch _q,
                                  unsigned int _population_size,
                                  unsigned int _selection_size);

// Execute the search
//  _q              :   ga search object
//  _max_iterations :   maximum number of iterations to run before bailing
//  _target_utility :   target utility
float gasearch_run(gasearch _q,
                    unsigned int _max_iterations,
                    float _target_utility);

// iterate over one evolution of the search algorithm
void gasearch_evolve(gasearch _q);

// get optimal chromosome
//  _q              :   ga search object
//  _c              :   output optimal chromosome
//  _utility_opt    :   fitness of _c
void gasearch_getopt(gasearch _q,
                     chromosome _c,
                     float * _utility_opt);

//
// MODULE : quantization
//

float compress_mulaw(float _x, float _mu);
float expand_mulaw(float _x, float _mu);

void compress_cf_mulaw(liquid_float_complex _x, float _mu, liquid_float_complex * _y);
void expand_cf_mulaw(liquid_float_complex _y, float _mu, liquid_float_complex * _x);

//float compress_alaw(float _x, float _a);
//float expand_alaw(float _x, float _a);

// inline quantizer: 'analog' signal in [-1, 1]
unsigned int quantize_adc(float _x, unsigned int _num_bits);
float quantize_dac(unsigned int _s, unsigned int _num_bits);

// structured quantizer

typedef enum {
    LIQUID_COMPANDER_NONE=0,
    LIQUID_COMPANDER_LINEAR,
    LIQUID_COMPANDER_MULAW,
    LIQUID_COMPANDER_ALAW
} liquid_compander_type;

#define LIQUID_QUANTIZER_MANGLE_FLOAT(name)  LIQUID_CONCAT(quantizerf,  name)
#define LIQUID_QUANTIZER_MANGLE_CFLOAT(name) LIQUID_CONCAT(quantizercf, name)

// large macro
//   QUANTIZER  : name-mangling macro
//   T          : data type
#define LIQUID_QUANTIZER_DEFINE_API(QUANTIZER,T)                \
typedef struct QUANTIZER(_s) * QUANTIZER();                     \
QUANTIZER() QUANTIZER(_create)(liquid_compander_type _ctype,    \
                               float _range,                    \
                               unsigned int _num_bits);         \
void QUANTIZER(_destroy)(QUANTIZER() _q);                       \
void QUANTIZER(_print)(QUANTIZER() _q);                         \
void QUANTIZER(_execute_adc)(QUANTIZER() _q,                    \
                             T _x,                              \
                             unsigned int * _sample);           \
void QUANTIZER(_execute_dac)(QUANTIZER() _q,                    \
                             unsigned int _sample,              \
                             T * _x);                           \

LIQUID_QUANTIZER_DEFINE_API(LIQUID_QUANTIZER_MANGLE_FLOAT,  float)
LIQUID_QUANTIZER_DEFINE_API(LIQUID_QUANTIZER_MANGLE_CFLOAT, liquid_float_complex)


//
// MODULE : random (number generators)
//


// Uniform random number generator, (0,1]
float randf();
float randf_pdf(float _x);
float randf_cdf(float _x);

// Gauss random number generator, N(0,1)
//   f(x) = 1/sqrt(2*pi*sigma^2) * exp{-(x-eta)^2/(2*sigma^2)}
//
//   where
//     eta   = mean
//     sigma = standard deviation
//
float randnf();
void awgn(float *_x, float _nstd);
void crandnf(liquid_float_complex *_y);
void cawgn(liquid_float_complex *_x, float _nstd);
float randnf_pdf(float _x, float _eta, float _sig);
float randnf_cdf(float _x, float _eta, float _sig);

// Exponential
//  f(x) = lambda exp{ -lambda x }
// where
//  lambda = spread parameter, lambda > 0
//  x >= 0
float randexpf(float _lambda);
float randexpf_pdf(float _x, float _lambda);
float randexpf_cdf(float _x, float _lambda);

// Weibull
//   f(x) = (a/b) (x/b)^(a-1) exp{ -(x/b)^a }
//   where
//     a = alpha : shape parameter
//     b = beta  : scaling parameter
//     g = gamma : location (threshold) parameter
//
float randweibf(float _alpha, float _beta, float _gamma);
float randweibf_pdf(float _x, float _a, float _b, float _g);
float randweibf_cdf(float _x, float _a, float _b, float _g);

// Gamma
//          x^(a-1) exp(-x/b)
//  f(x) = -------------------
//            Gamma(a) b^a
//  where
//      a = alpha : shape parameter, a > 0
//      b = beta  : scale parameter, b > 0
//      Gamma(z) = regular gamma function
//      x >= 0
float randgammaf(float _alpha, float _beta);
float randgammaf_pdf(float _x, float _alpha, float _beta);
float randgammaf_cdf(float _x, float _alpha, float _beta);

// Nakagami-m
//  f(x) = (2/Gamma(m)) (m/omega)^m x^(2m-1) exp{-(m/omega)x^2}
// where
//      m       : shape parameter, m >= 0.5
//      omega   : spread parameter, omega > 0
//      Gamma(z): regular complete gamma function
//      x >= 0
float randnakmf(float _m, float _omega);
float randnakmf_pdf(float _x, float _m, float _omega);
float randnakmf_cdf(float _x, float _m, float _omega);

// Rice-K
//  f(x) = (x/sigma^2) exp{ -(x^2+s^2)/(2sigma^2) } I0( x s / sigma^2 )
// where
//  s     = sqrt( omega*K/(K+1) )
//  sigma = sqrt(0.5 omega/(K+1))
// and
//  K     = shape parameter
//  omega = spread parameter
//  I0    = modified Bessel function of the first kind
//  x >= 0
float randricekf(float _K, float _omega);
float randricekf_cdf(float _x, float _K, float _omega);
float randricekf_pdf(float _x, float _K, float _omega);


// Data scrambler : whiten data sequence
void scramble_data(unsigned char * _x, unsigned int _len);
void unscramble_data(unsigned char * _x, unsigned int _len);
void unscramble_data_soft(unsigned char * _x, unsigned int _len);

//
// MODULE : sequence
//

// Binary sequence (generic)

typedef struct bsequence_s * bsequence;

// Create a binary sequence of a specific length (number of bits)
bsequence bsequence_create(unsigned int num_bits);

// Free memory in a binary sequence
void bsequence_destroy(bsequence _bs);

// Clear binary sequence (set to 0's)
void bsequence_reset(bsequence _bs);

// initialize sequence on external array
void bsequence_init(bsequence _bs,
                    unsigned char * _v);

// Print sequence to the screen
void bsequence_print(bsequence _bs);

// Push bit into to back of a binary sequence
void bsequence_push(bsequence _bs,
                    unsigned int _bit);

// circular shift (left)
void bsequence_circshift(bsequence _bs);

// Correlate two binary sequences together
int bsequence_correlate(bsequence _bs1, bsequence _bs2);

// compute the binary addition of two bit sequences
void bsequence_add(bsequence _bs1, bsequence _bs2, bsequence _bs3);

// compute the binary multiplication of two bit sequences
void bsequence_mul(bsequence _bs1, bsequence _bs2, bsequence _bs3);

// accumulate the 1's in a binary sequence
unsigned int bsequence_accumulate(bsequence _bs);

// accessor functions
unsigned int bsequence_get_length(bsequence _bs);
unsigned int bsequence_index(bsequence _bs, unsigned int _i);

// Complementary codes

// intialize two sequences to complementary codes.  sequences must
// be of length at least 8 and a power of 2 (e.g. 8, 16, 32, 64,...)
//  _a      :   sequence 'a' (bsequence object)
//  _b      :   sequence 'b' (bsequence object)
void bsequence_create_ccodes(bsequence _a,
                             bsequence _b);


// M-Sequence

#define LIQUID_MAX_MSEQUENCE_LENGTH   32767

// default m-sequence generators:       g (hex)     m       n   g (oct)       g (binary)
#define LIQUID_MSEQUENCE_GENPOLY_M2     0x0007  //  2       3        7               111
#define LIQUID_MSEQUENCE_GENPOLY_M3     0x000B  //  3       7       13              1011
#define LIQUID_MSEQUENCE_GENPOLY_M4     0x0013  //  4      15       23             10011
#define LIQUID_MSEQUENCE_GENPOLY_M5     0x0025  //  5      31       45            100101
#define LIQUID_MSEQUENCE_GENPOLY_M6     0x0043  //  6      63      103           1000011
#define LIQUID_MSEQUENCE_GENPOLY_M7     0x0089  //  7     127      211          10001001
#define LIQUID_MSEQUENCE_GENPOLY_M8     0x011D  //  8     255      435         100101101
#define LIQUID_MSEQUENCE_GENPOLY_M9     0x0211  //  9     511     1021        1000010001
#define LIQUID_MSEQUENCE_GENPOLY_M10    0x0409  // 10    1023     2011       10000001001
#define LIQUID_MSEQUENCE_GENPOLY_M11    0x0805  // 11    2047     4005      100000000101
#define LIQUID_MSEQUENCE_GENPOLY_M12    0x1053  // 12    4095    10123     1000001010011
#define LIQUID_MSEQUENCE_GENPOLY_M13    0x201b  // 13    8191    20033    10000000011011
#define LIQUID_MSEQUENCE_GENPOLY_M14    0x402b  // 14   16383    40053   100000000101011
#define LIQUID_MSEQUENCE_GENPOLY_M15    0x8003  // 15   32767   100003  1000000000000011
   
typedef struct msequence_s * msequence;

// create a maximal-length sequence (m-sequence) object with
// an internal shift register length of _m bits.
//  _m      :   generator polynomial length, sequence length is (2^m)-1
//  _g      :   generator polynomial, starting with most-significant bit
//  _a      :   initial shift register state, default: 000...001
msequence msequence_create(unsigned int _m,
                           unsigned int _g,
                           unsigned int _a);

// create a maximal-length sequence (m-sequence) object from a generator polynomial
msequence msequence_create_genpoly(unsigned int _g);

// creates a default maximal-length sequence
msequence msequence_create_default(unsigned int _m);

// destroy an msequence object, freeing all internal memory
void msequence_destroy(msequence _m);

// prints the sequence's internal state to the screen
void msequence_print(msequence _m);

// advance msequence on shift register, returning output bit
unsigned int msequence_advance(msequence _ms);

// generate pseudo-random symbol from shift register by
// advancing _bps bits and returning compacted symbol
//  _ms     :   m-sequence object
//  _bps    :   bits per symbol of output
unsigned int msequence_generate_symbol(msequence _ms,
                                       unsigned int _bps);

// reset msequence shift register to original state, typically '1'
void msequence_reset(msequence _ms);

// initialize a bsequence object on an msequence object
//  _bs     :   bsequence object
//  _ms     :   msequence object
void bsequence_init_msequence(bsequence _bs,
                              msequence _ms);

// get the length of the sequence
unsigned int msequence_get_length(msequence _ms);

// get the internal state of the sequence
unsigned int msequence_get_state(msequence _ms);

// set the internal state of the sequence
void msequence_set_state(msequence    _ms,
                         unsigned int _a);


// 
// MODULE : utility
//

// pack binary array with symbol(s)
//  _src        :   source array [size: _n x 1]
//  _n          :   input source array length
//  _k          :   bit index to write in _src
//  _b          :   number of bits in input symbol
//  _sym_in     :   input symbol
void liquid_pack_array(unsigned char * _src,
                       unsigned int _n,
                       unsigned int _k,
                       unsigned int _b,
                       unsigned char _sym_in);

// unpack symbols from binary array
//  _src        :   source array [size: _n x 1]
//  _n          :   input source array length
//  _k          :   bit index to write in _src
//  _b          :   number of bits in output symbol
//  _sym_out    :   output symbol
void liquid_unpack_array(unsigned char * _src,
                         unsigned int _n,
                         unsigned int _k,
                         unsigned int _b,
                         unsigned char * _sym_out);

// pack one-bit symbols into bytes (8-bit symbols)
//  _sym_in             :   input symbols array [size: _sym_in_len x 1]
//  _sym_in_len         :   number of input symbols
//  _sym_out            :   output symbols
//  _sym_out_len        :   number of bytes allocated to output symbols array
//  _num_written        :   number of output symbols actually written
void liquid_pack_bytes(unsigned char * _sym_in,
                       unsigned int _sym_in_len,
                       unsigned char * _sym_out,
                       unsigned int _sym_out_len,
                       unsigned int * _num_written);

// unpack 8-bit symbols (full bytes) into one-bit symbols
//  _sym_in             :   input symbols array [size: _sym_in_len x 1]
//  _sym_in_len         :   number of input symbols
//  _sym_out            :   output symbols array
//  _sym_out_len        :   number of bytes allocated to output symbols array
//  _num_written        :   number of output symbols actually written
void liquid_unpack_bytes(unsigned char * _sym_in,
                         unsigned int _sym_in_len,
                         unsigned char * _sym_out,
                         unsigned int _sym_out_len,
                         unsigned int * _num_written);

// repack bytes with arbitrary symbol sizes
//  _sym_in             :   input symbols array [size: _sym_in_len x 1]
//  _sym_in_bps         :   number of bits per input symbol
//  _sym_in_len         :   number of input symbols
//  _sym_out            :   output symbols array
//  _sym_out_bps        :   number of bits per output symbol
//  _sym_out_len        :   number of bytes allocated to output symbols array
//  _num_written        :   number of output symbols actually written
void liquid_repack_bytes(unsigned char * _sym_in,
                         unsigned int _sym_in_bps,
                         unsigned int _sym_in_len,
                         unsigned char * _sym_out,
                         unsigned int _sym_out_bps,
                         unsigned int _sym_out_len,
                         unsigned int * _num_written);
 
// shift array to the left _b bits, filling in zeros
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bits to shift
void liquid_lbshift(unsigned char * _src,
                    unsigned int _n,
                    unsigned int _b);
 
// shift array to the right _b bits, filling in zeros
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bits to shift
void liquid_rbshift(unsigned char * _src,
                    unsigned int _n,
                    unsigned int _b);
 
// circularly shift array to the left _b bits
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bits to shift
void liquid_lbcircshift(unsigned char * _src,
                        unsigned int _n,
                        unsigned int _b);
 
// circularly shift array to the right _b bits
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bits to shift
void liquid_rbcircshift(unsigned char * _src,
                        unsigned int _n,
                        unsigned int _b);
 



// shift array to the left _b bytes, filling in zeros
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bytes to shift
void liquid_lshift(unsigned char * _src,
                   unsigned int _n,
                   unsigned int _b);
 
// shift array to the right _b bytes, filling in zeros
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bytes to shift
void liquid_rshift(unsigned char * _src,
                   unsigned int _n,
                   unsigned int _b);
 
// circular shift array to the left _b bytes
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bytes to shift
void liquid_lcircshift(unsigned char * _src,
                       unsigned int _n,
                       unsigned int _b);
 
// circular shift array to the right _b bytes
//  _src        :   source address [size: _n x 1]
//  _n          :   input data array size
//  _b          :   number of bytes to shift
void liquid_rcircshift(unsigned char * _src,
                       unsigned int _n,
                       unsigned int _b);
 
// Count the number of ones in an integer
unsigned int liquid_count_ones(unsigned int _x); 

// count number of ones in an integer, modulo 2
unsigned int liquid_count_ones_mod2(unsigned int _x);

// compute bindary dot-product between two integers
unsigned int liquid_bdotprod(unsigned int _x,
                             unsigned int _y);

// Count leading zeros in an integer
unsigned int liquid_count_leading_zeros(unsigned int _x); 

// Most-significant bit index
unsigned int liquid_msb_index(unsigned int _x);

// Print string of bits to stdout
void liquid_print_bitstring(unsigned int _x,
                            unsigned int _n);

// reverse byte, word, etc.
unsigned char liquid_reverse_byte(  unsigned char _x);
unsigned int  liquid_reverse_uint16(unsigned int  _x);
unsigned int  liquid_reverse_uint24(unsigned int  _x);
unsigned int  liquid_reverse_uint32(unsigned int  _x);

// 
// MODULE : vector
//

#define LIQUID_VECTOR_MANGLE_RF(name) LIQUID_CONCAT(liquid_vectorf, name)
#define LIQUID_VECTOR_MANGLE_CF(name) LIQUID_CONCAT(liquid_vectorcf,name)

// large macro
//   VECTOR     : name-mangling macro
//   T          : data type
//   TP         : data type (primitive)
#define LIQUID_VECTOR_DEFINE_API(VECTOR,T,TP)                   \
                                                                \
/* initialize vector with scalar: x[i] = c (scalar)         */  \
void VECTOR(_init)(T            _c,                             \
                   T *          _x,                             \
                   unsigned int _n);                            \
                                                                \
/* add each element: z[i] = x[i] + y[i]                     */  \
void VECTOR(_add)(T *          _x,                              \
                  T *          _y,                              \
                  unsigned int _n,                              \
                  T *          _z);                             \
/* add scalar to each element: y[i] = x[i] + c              */  \
void VECTOR(_addscalar)(T *          _x,                        \
                        unsigned int _n,                        \
                        T            _c,                        \
                        T *          _y);                       \
                                                                \
/* multiply each element: z[i] = x[i] * y[i]                */  \
void VECTOR(_mul)(T *          _x,                              \
                  T *          _y,                              \
                  unsigned int _n,                              \
                  T *          _z);                             \
/* multiply each element with scalar: y[i] = x[i] * c       */  \
void VECTOR(_mulscalar)(T *          _x,                        \
                        unsigned int _n,                        \
                        T            _c,                        \
                        T *          _y);                       \
                                                                \
/* compute complex phase rotation: x[i] = exp{j theta[i]}   */  \
void VECTOR(_cexpj)(TP *         _theta,                        \
                    unsigned int _n,                            \
                    T *          _x);                           \
/* compute angle of each element: theta[i] = arg{ x[i] }    */  \
void VECTOR(_carg)(T *          _x,                             \
                   unsigned int _n,                             \
                   TP *         _theta);                        \
/* compute absolute value of each element: y[i] = |x[i]|    */  \
void VECTOR(_abs)(T *          _x,                              \
                  unsigned int _n,                              \
                  TP *         _y);                             \
                                                                \
/* compute sum of squares: sum{ |x|^2 }                     */  \
TP VECTOR(_sumsq)(T *          _x,                              \
                  unsigned int _n);                             \
                                                                \
/* compute l-2 norm: sqrt{ sum{ |x|^2 } }                   */  \
TP VECTOR(_norm)(T *          _x,                               \
                 unsigned int _n);                              \
                                                                \
/* compute l-p norm: { sum{ |x|^p } }^(1/p)                 */  \
TP VECTOR(_pnorm)(T *          _x,                              \
                  unsigned int _n,                              \
                  TP           _p);                             \
                                                                \
/* scale vector elements by l-2 norm: y[i] = x[i]/norm(x)   */  \
void VECTOR(_normalize)(T *          _x,                        \
                        unsigned int _n,                        \
                        T *          _y);                       \

LIQUID_VECTOR_DEFINE_API(LIQUID_VECTOR_MANGLE_RF, float,                float)
LIQUID_VECTOR_DEFINE_API(LIQUID_VECTOR_MANGLE_CF, liquid_float_complex, float)

// 
// mixed types
//
#if 0
void liquid_vectorf_add(float *      _a,
                        float *      _b,
                        unsigned int _n,
                        float *      _c);
#endif

#ifdef __cplusplus
} //extern "C"
#endif // __cplusplus

#endif // __LIQUID_H__

