diff --git a/config b/config index 029d463..7d66a2d 100644 --- a/config +++ b/config @@ -32,4 +32,5 @@ source "scripts/paths" # Project-specific variables below. [ -z "${CC}" ] && CC="gcc" +[ -z "${CXX}" ] && CXX="g++" [ -z "${CFLAGS}" ] && CFLAGS="-g -O2 -W -Wall" diff --git a/src/libfir++/000_TopHeader.h b/src/libfir++/000_TopHeader.h index ee124af..5291fec 100644 --- a/src/libfir++/000_TopHeader.h +++ b/src/libfir++/000_TopHeader.h @@ -9,6 +9,16 @@ #define HEADER_libfirpp // standard includes, or includes needed for type declarations +#include + +/*! \defgroup libfirpp C++ library */ + +/*! \brief All %FIR library functions and classes. + +\ingroup libfirpp + +*/ +namespace FIR { /* options for text editors vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen diff --git a/src/libfir++/000_TopSource.cc b/src/libfir++/000_TopSource.cc new file mode 100644 index 0000000..ca7c76e --- /dev/null +++ b/src/libfir++/000_TopSource.cc @@ -0,0 +1,23 @@ +/* libfir/src/libfir++/000_TopSource.cc + * + * Copyright: ©2014, Laurence Withers. + * Author: Laurence Withers + * License: GPLv3 + */ + +// "fir.h" includes , which uses a preprocessor define on the +// symbol complex. We need to include it first so that it gets its required +// define, and then clear it up before including anything that pulls in the +// C++ header . +#include "fir.h" +#undef complex + +#include "fir++.h" + +// Below are all the includes used throughout the library. + +namespace FIR { + +/* options for text editors +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/libfir++/100_fir.h b/src/libfir++/100_fir.h new file mode 100644 index 0000000..8189b5d --- /dev/null +++ b/src/libfir++/100_fir.h @@ -0,0 +1,47 @@ +/* libfir/src/libfir++/100_fir.h + * + * Copyright: ©2014, Laurence Withers. + * Author: Laurence Withers + * License: GPLv3 + */ + + + +/*! \defgroup libfirpp_fir Basic FIR filtering + +\ingroup libfirpp + +\todo need our own symmetry enum +\todo forward declares +\todo coefficient query + +*/ + + + +/*! \brief Basic FIR filter object. + +\ingroup libfirpp_fir + +*/ +class Filter { +public: + Filter(int ncoeff, double* coeff, + enum fir_symmetry symm = fir_symmetry_none); + Filter(const Filter& other); + Filter(const std::vector& coeff, + enum fir_symmetry symm = fir_symmetry_none); + virtual ~Filter(); + +private: + struct fir_filter_t* fi_; + + // disallow assignment + Filter& operator=(const Filter& other); +}; + + + +/* options for text editors +vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen +*/ diff --git a/src/libfir++/999_BottomHeader.h b/src/libfir++/999_BottomHeader.h index c1e8dcc..28d9774 100644 --- a/src/libfir++/999_BottomHeader.h +++ b/src/libfir++/999_BottomHeader.h @@ -5,6 +5,8 @@ * License: GPLv3 */ +} // end namespace FIR + #endif /* options for text editors diff --git a/src/libfir++/000_TopSource.cpp b/src/libfir++/999_BottomSource.cc similarity index 59% rename from src/libfir++/000_TopSource.cpp rename to src/libfir++/999_BottomSource.cc index dfa8aba..6632108 100644 --- a/src/libfir++/000_TopSource.cpp +++ b/src/libfir++/999_BottomSource.cc @@ -1,13 +1,11 @@ -/* libfir/src/libfir++/TopSource.cpp +/* libfir/src/libfir++/999_BottomSource.cc * * Copyright: ©2014, Laurence Withers. * Author: Laurence Withers * License: GPLv3 */ -#include "fir++.h" - -// Below are all the includes used throughout the library. +} // end namespace FIR /* options for text editors vim: expandtab:ts=4:sw=4 diff --git a/src/libfir++/build.lib b/src/libfir++/build.lib index 73d8739..7269bb7 100644 --- a/src/libfir++/build.lib +++ b/src/libfir++/build.lib @@ -6,6 +6,8 @@ # libfirpp_DEP_CFLAGS # libfirpp_DEP_LIBS +build_target libfir + if [ -z ${libfirpp_BUILT} ] then libfirpp_BASE=libfir++ @@ -15,7 +17,7 @@ then libfirpp_DEP_CFLAGS="" # @TODO@ cflags libfirpp_DEP_LIBS="" # @TODO@ libs SO_EXTRA="${libfirpp_DEP_CFLAGS} ${libfirpp_DEP_LIBS} -lstdc++ -lc \ - -D_GNU_SOURCE" + ${libfir} -D_GNU_SOURCE" echo "Building library ${libfirpp}..." diff --git a/src/libfir++/build.monolithic b/src/libfir++/build.monolithic index c9a9bac..5feaedf 100644 --- a/src/libfir++/build.monolithic +++ b/src/libfir++/build.monolithic @@ -11,7 +11,7 @@ then MONOLITHIC_SOURCE="$(find src/libfir++/ -name '*.h' | sort)" make_monolithic ${HDR} Ch || return 1 - MONOLITHIC_SOURCE="$(find src/libfir++/ -name '*.cpp' | sort)" + MONOLITHIC_SOURCE="$(find src/libfir++/ -name '*.cc' | sort)" make_monolithic ${SRC} C || return 1 libfirpp_MONOLITHIC=1 diff --git a/src/libfir/000_TopHeader.h b/src/libfir/000_TopHeader.h index c7ef957..b0ad494 100644 --- a/src/libfir/000_TopHeader.h +++ b/src/libfir/000_TopHeader.h @@ -12,6 +12,8 @@ extern "C" { #endif +/*! \defgroup libfir C library */ + /* standard includes, or includes needed for type declarations */ /* options for text editors diff --git a/src/libfir/000_TopSource.c b/src/libfir/000_TopSource.c index 2f6be6c..a1fc33c 100644 --- a/src/libfir/000_TopSource.c +++ b/src/libfir/000_TopSource.c @@ -9,6 +9,7 @@ /* Below are all the includes used throughout the library. */ #include +#include #include #include #include diff --git a/src/libfir/100_fir.c b/src/libfir/100_fir.c index 7ebab7c..c407761 100644 --- a/src/libfir/100_fir.c +++ b/src/libfir/100_fir.c @@ -48,8 +48,14 @@ symm_setup(struct fir_filter_t* fi, int delay) struct fir_filter_t* fir_filter_new(const double* coeff, int ncoeff, enum fir_symmetry symm) { - struct fir_filter_t* fi = malloc(sizeof(*fi)); + struct fir_filter_t* fi; + if(ncoeff < 1) { + errno = EINVAL; + return 0; + } + + fi = malloc(sizeof(*fi)); fi->coeff = malloc(sizeof(double) * ncoeff); memcpy(fi->coeff, coeff, sizeof(double) * ncoeff); fi->ncoeff = ncoeff; diff --git a/src/libfir/100_fir.h b/src/libfir/100_fir.h index b3f28e8..f9513da 100644 --- a/src/libfir/100_fir.h +++ b/src/libfir/100_fir.h @@ -7,29 +7,112 @@ +/*! \defgroup libfir_fir Basic FIR filtering + +\ingroup libfir + +Routines in this section provide the basic building blocks for %FIR filter use: +allocation/build from coefficients, cloning, and the filter operation itself. + +*/ +/*!@{*/ + + + +/* opaque structure */ struct fir_filter_t; +/*! \brief %FIR filter symmetry. + +The library extensively takes advantage of the symmetry that is often present +in %FIR filter coefficient sets. + +*/ enum fir_symmetry { + /*! \brief No symmetry. */ fir_symmetry_none, + + /*! \brief Odd symmetry (odd total number of points). */ fir_symmetry_odd, + + /*! \brief Even symmetry (even total number of points). */ fir_symmetry_even, }; +/*! \brief Allocate %FIR filter instance. + +\param coeff Pointer to array of coefficients. +\param ncoeff Number of coefficients in \a coeff (must be ≥ 1). +\param symm Whether there is any symmetry. +\returns Newly-allocated filter object. +\retval 0 on error (and see \a errno). + +Allocates a new %FIR filter based on the given set of coefficients \a coeff. +If the coefficient set is known to have odd symmetry of \a N points, then +pass \a ncoeff as \a (N+1)/2 (and this is the number of entries in \a coeff +that are read). If the coefficient set is known to have even symmetry of \a N +points, then pass \a ncoeff as \a N/2. + +The contents of \a coeff are copied so it is not necessary to keep the original +array in order to run the filter. + +*/ struct fir_filter_t* fir_filter_new(const double* coeff, int ncoeff, enum fir_symmetry symm); + + +/*! \brief Free %FIR filter instance. + +\param fi Filter object (may be 0). +\returns Newly-allocated filter object. + +Frees all memory associated with the filter object \a fi. Does nothing if +passed a null pointer. + +*/ void fir_filter_free(struct fir_filter_t* fi); + + +/*! \brief Clone %FIR filter instance. + +\param fi Filter object to clone. + +Clones the %FIR filter object \a fi, returning a new %FIR filter object that +is ready to use. The new copy will have its own copy of the required +coefficients and does not hold any reference to the original. + +Note that filter state (i.e. previous samples) is not copied; the new filter +will be fresh. + +*/ struct fir_filter_t* fir_filter_clone(const struct fir_filter_t* fi); + + +/*! \brief Run %FIR filter. + +\param fi Filter object. +\param x Input sample. +\returns Output sample. + +This function runs the filter for the given input sample \a x, returning an +output sample \a y. If this is the very first sample after creation (either via +\ref fir_filter_new() or \ref fir_filter_clone()), then this sample will be +considered as the steady-state value and the previous-sample values stored in +\a fi will be initialised to \a x. + +*/ double fir_filter(struct fir_filter_t* fi, double x); +/*!@}*/ /* options for text editors -vim: expandtab:ts=4:sw=4 +vim: expandtab:ts=4:sw=4:syntax=ch.doxygen */ diff --git a/src/libfir/200_common_filters.c b/src/libfir/200_common_filters.c index 9916aba..de9546d 100644 --- a/src/libfir/200_common_filters.c +++ b/src/libfir/200_common_filters.c @@ -8,11 +8,16 @@ struct fir_filter_t* -fir_sinc_lowpass(int npoints, double corner) +fir_sinc_lowpass(int npoints, double corner, double gain) { - double* coeff, t; + double* coeff, t, sum = 0; int i, spoints; + if(npoints < 1) { + errno = EINVAL; + return 0; + } + /* find number of points after taking symmetry into account */ spoints = (npoints + 1) / 2; coeff = alloca(sizeof(double) * spoints); @@ -21,13 +26,21 @@ fir_sinc_lowpass(int npoints, double corner) for(i = 0; i < spoints; ++i) { if((i * 2 + 1) == npoints) { coeff[i] = 1; + sum += 1; /* not 2, as we don't repeat this coeff */ } else { t = i + 0.5 - npoints * 0.5; t *= corner * 2; coeff[i] = sin(t) / t; + sum += coeff[i] * 2; } } + /* normalise gain */ + gain /= sum; + for(i = 0; i < spoints; ++i) { + coeff[i] *= gain; + } + return fir_filter_new(coeff, spoints, (npoints & 1) ? fir_symmetry_odd : fir_symmetry_even); } diff --git a/src/libfir/200_common_filters.h b/src/libfir/200_common_filters.h index cfd996e..3adfc2c 100644 --- a/src/libfir/200_common_filters.h +++ b/src/libfir/200_common_filters.h @@ -7,10 +7,41 @@ -struct fir_filter_t* fir_sinc_lowpass(int npoints, double corner); +/*! \defgroup libfir_common Common filter types +\ingroup libfir +Routines in this section instantiate %FIR filters using algorithms to build +commonly-used types of filter, such as brickwall low-pass etc. -/* options for text editors -vim: expandtab:ts=4:sw=4 +*/ +/*!@{*/ + + + +/*! \brief Brickwall low-pass linear phase: sinc filter. + +\param npoints Number of points required (≥1). +\param corner Corner frequency as fraction of sample rate. +\param gain Gain of filter. +\returns Newly-allocated %FIR filter. + +Builds a truncated-sinc filter which has a low-pass, brickwall effect (cutoff +at \a corner, which is a fraction of sampling rate, i.e. 0 < \a corner < +0.5). + +The number of points \a npoints is a trade-off between accuracy of filter (an +ideal sinc filter would have an infinite number of points) and computation time. +\a npoints may be odd or even; symmetry will be exploited in either case. + +The DC gain of the filter is normalised to \a gain, which is often 1.0. + +*/ +struct fir_filter_t* fir_sinc_lowpass(int npoints, double corner, double gain); + + + +/*!@}*/ +/* options for text editors +vim: expandtab:ts=4:sw=4:syntax=ch.doxygen */