From f64b971f34ab813f70b0680ec7ae02744343ef3c Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 1 May 2012 22:10:17 -0400 Subject: filter: work on creating gr-filter top-level component. Builds but does not bring in libgnuradio-fft.so symbols. --- CMakeLists.txt | 1 + gr-filter/CMakeLists.txt | 109 +++++++++++++++++ gr-filter/doc/CMakeLists.txt | 23 ++++ gr-filter/doc/README.filter | 13 ++ gr-filter/doc/filter.dox | 27 +++++ gr-filter/gnuradio-filter.pc.in | 11 ++ gr-filter/grc/CMakeLists.txt | 24 ++++ gr-filter/grc/filter_block_tree.xml | 34 ++++++ gr-filter/include/filter/CMakeLists.txt | 82 +++++++++++++ gr-filter/include/filter/api.h | 33 +++++ gr-filter/include/filter/fft.h | 195 ++++++++++++++++++++++++++++++ gr-filter/include/filter/fft_vcc.h | 57 +++++++++ gr-filter/include/filter/fir_filter_fff.h | 53 ++++++++ gr-filter/lib/CMakeLists.txt | 60 +++++++++ gr-filter/lib/fir_filter_fff_impl.cc | 110 +++++++++++++++++ gr-filter/lib/fir_filter_fff_impl.h | 56 +++++++++ gr-filter/python/CMakeLists.txt | 47 +++++++ gr-filter/python/__init__.py | 28 +++++ gr-filter/python/qa_fir_filter.py | 46 +++++++ gr-filter/swig/CMakeLists.txt | 53 ++++++++ gr-filter/swig/filter_swig.i | 36 ++++++ 21 files changed, 1098 insertions(+) create mode 100644 gr-filter/CMakeLists.txt create mode 100644 gr-filter/doc/CMakeLists.txt create mode 100644 gr-filter/doc/README.filter create mode 100644 gr-filter/doc/filter.dox create mode 100644 gr-filter/gnuradio-filter.pc.in create mode 100644 gr-filter/grc/CMakeLists.txt create mode 100644 gr-filter/grc/filter_block_tree.xml create mode 100644 gr-filter/include/filter/CMakeLists.txt create mode 100644 gr-filter/include/filter/api.h create mode 100644 gr-filter/include/filter/fft.h create mode 100644 gr-filter/include/filter/fft_vcc.h create mode 100644 gr-filter/include/filter/fir_filter_fff.h create mode 100644 gr-filter/lib/CMakeLists.txt create mode 100644 gr-filter/lib/fir_filter_fff_impl.cc create mode 100644 gr-filter/lib/fir_filter_fff_impl.h create mode 100644 gr-filter/python/CMakeLists.txt create mode 100644 gr-filter/python/__init__.py create mode 100755 gr-filter/python/qa_fir_filter.py create mode 100644 gr-filter/swig/CMakeLists.txt create mode 100644 gr-filter/swig/filter_swig.i diff --git a/CMakeLists.txt b/CMakeLists.txt index dad398e8c..9be8217ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -229,6 +229,7 @@ add_subdirectory(gnuradio-core) add_subdirectory(grc) add_subdirectory(gr-fft) +add_subdirectory(gr-filter) add_subdirectory(gr-atsc) add_subdirectory(gr-audio) add_subdirectory(gr-comedi) diff --git a/gr-filter/CMakeLists.txt b/gr-filter/CMakeLists.txt new file mode 100644 index 000000000..a4d25fd13 --- /dev/null +++ b/gr-filter/CMakeLists.txt @@ -0,0 +1,109 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +######################################################################## +# Setup dependencies +######################################################################## +include(GrBoost) + +######################################################################## +# Register component +######################################################################## +include(GrComponent) + +GR_REGISTER_COMPONENT("gr-filter" ENABLE_GR_FILTER + ENABLE_GRUEL + ENABLE_VOLK + Boost_FOUND + ENABLE_GR_CORE + ENABLE_GR_FFT +) + +GR_SET_GLOBAL(FILTER_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/lib + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +######################################################################## +# Begin conditional configuration +######################################################################## +if(ENABLE_GR_FILTER) + +######################################################################## +# Setup CPack components +######################################################################## +include(GrPackage) +CPACK_SET(CPACK_COMPONENT_GROUP_FILTER_DESCRIPTION "GNU Radio Filter Blocks") + +CPACK_COMPONENT("filter_runtime" + GROUP "Filter" + DISPLAY_NAME "Runtime" + DESCRIPTION "Runtime" + DEPENDS "core_runtime" +) + +CPACK_COMPONENT("filter_devel" + GROUP "Filter" + DISPLAY_NAME "Development" + DESCRIPTION "C++ headers, package config, import libraries" + DEPENDS "core_devel" +) + +CPACK_COMPONENT("filter_python" + GROUP "Filter" + DISPLAY_NAME "Python" + DESCRIPTION "Python modules for runtime; GRC xml files" + DEPENDS "core_python;filter_runtime" +) + +CPACK_COMPONENT("filter_swig" + GROUP "Filter" + DISPLAY_NAME "SWIG" + DESCRIPTION "SWIG development .i files" + DEPENDS "core_swig;filter_python;filter_devel" +) + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(include/filter) +add_subdirectory(lib) +if(ENABLE_PYTHON) + add_subdirectory(swig) + add_subdirectory(python) + add_subdirectory(grc) +endif(ENABLE_PYTHON) +#add_subdirectory(examples) +add_subdirectory(doc) + +######################################################################## +# Create Pkg Config File +######################################################################## +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/gnuradio-filter.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/gnuradio-filter.pc +@ONLY) + +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/gnuradio-filter.pc + DESTINATION ${GR_LIBRARY_DIR}/pkgconfig + COMPONENT "filter_devel" +) + +endif(ENABLE_GR_FILTER) diff --git a/gr-filter/doc/CMakeLists.txt b/gr-filter/doc/CMakeLists.txt new file mode 100644 index 000000000..63447ed2c --- /dev/null +++ b/gr-filter/doc/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +install( + FILES README.filter + DESTINATION ${GR_PKG_DOC_DIR} +) diff --git a/gr-filter/doc/README.filter b/gr-filter/doc/README.filter new file mode 100644 index 000000000..776acd46c --- /dev/null +++ b/gr-filter/doc/README.filter @@ -0,0 +1,13 @@ +This is the gr-filter package. It contains signal processing blocks to +perform filtering operations. + +The Python namespace is in gnuradio.filter, which would be normally +imported as: + + from gnuradio import filter + +See the Doxygen documentation for details about the blocks available +in this package. A quick listing of the details can be found in Python +after importing by using: + + help(filter) diff --git a/gr-filter/doc/filter.dox b/gr-filter/doc/filter.dox new file mode 100644 index 000000000..fc3b52e91 --- /dev/null +++ b/gr-filter/doc/filter.dox @@ -0,0 +1,27 @@ +/*! \page page_filter filter Signal Processing Blocks + +\section Introduction + +This is the gr-filter package. It contains signal processing blocks to +perform filtering operations. + +The Python namespace is in gnuradio.filter, which would be normally +imported as: + +\code + from gnuradio import filter +\endcode + +See the Doxygen documentation for details about the blocks available +in this package. A quick listing of the details can be found in Python +after importing by using: + +\code + help(filter) +\endcode + +\section Dependencies + +The filter blocks depend on \ref page_fft. + +*/ diff --git a/gr-filter/gnuradio-filter.pc.in b/gr-filter/gnuradio-filter.pc.in new file mode 100644 index 000000000..e4e83b350 --- /dev/null +++ b/gr-filter/gnuradio-filter.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: gnuradio-filter +Description: GNU Radio's filter signal processing blocks +Requires: gnuradio-core gnuradio-fft +Version: @LIBVER@ +Libs: -L${libdir} -lgnuradio-filter +Cflags: -I${includedir} diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt new file mode 100644 index 000000000..79496183f --- /dev/null +++ b/gr-filter/grc/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +install(FILES + filter_block_tree.xml + DESTINATION ${GRC_BLOCKS_DIR} + COMPONENT "filter_python" +) diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml new file mode 100644 index 000000000..a97620db9 --- /dev/null +++ b/gr-filter/grc/filter_block_tree.xml @@ -0,0 +1,34 @@ + + + + + + + + + Filters + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt new file mode 100644 index 000000000..a3c83f3dc --- /dev/null +++ b/gr-filter/include/filter/CMakeLists.txt @@ -0,0 +1,82 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +######################################################################## +# generate helper scripts to expand templated files +######################################################################## +include(GrPython) + +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py " +#!${PYTHON_EXECUTABLE} + +import sys, os, re +sys.path.append('${GR_CORE_PYTHONPATH}') +os.environ['srcdir'] = '${CMAKE_CURRENT_SOURCE_DIR}' +os.chdir('${CMAKE_CURRENT_BINARY_DIR}') + +if __name__ == '__main__': + import build_utils + root, inp = sys.argv[1:3] + for sig in sys.argv[3:]: + name = re.sub ('X+', sig, root) + d = build_utils.standard_dict2(name, sig, 'filter') + build_utils.expand_template(d, inp) + +") + +macro(expand_h root) + #make a list of all the generated files + unset(expanded_files_h) + foreach(sig ${ARGN}) + string(REGEX REPLACE "X+" ${sig} name ${root}) + list(APPEND expanded_files_h ${CMAKE_CURRENT_BINARY_DIR}/${name}.h) + endforeach(sig) + + #create a command to generate the files + add_custom_command( + OUTPUT ${expanded_files_h} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}.h.t + COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} + ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py + ${root} ${root}.h.t ${ARGN} + ) + + #install rules for the generated h files + list(APPEND generated_includes ${expanded_files_h}) +endmacro(expand_h) + +######################################################################## +# Invoke macro to generate various sources +####################################################################### +#expand_h(fir_filter_XXX fff ccc ccf fcc fsf scc) + +#add_custom_target(filter_generated_includes DEPENDS +# ${generated_includes} +#) + +######################################################################## +# Install header files +######################################################################## +install(FILES + api.h + fir_filter_fff.h + DESTINATION ${GR_INCLUDE_DIR}/gnuradio/fft + COMPONENT "fft_devel" +) + diff --git a/gr-filter/include/filter/api.h b/gr-filter/include/filter/api.h new file mode 100644 index 000000000..025b0bd4a --- /dev/null +++ b/gr-filter/include/filter/api.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_API_H +#define INCLUDED_FILTER_API_H + +#include + +#ifdef gnuradio_filter_EXPORTS +# define FILTER_API __GR_ATTR_EXPORT +#else +# define FILTER_API __GR_ATTR_IMPORT +#endif + +#endif /* INCLUDED_FILTER_API_H */ diff --git a/gr-filter/include/filter/fft.h b/gr-filter/include/filter/fft.h new file mode 100644 index 000000000..5cc2e21e8 --- /dev/null +++ b/gr-filter/include/filter/fft.h @@ -0,0 +1,195 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2008,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _FFT_FFT_H_ +#define _FFT_FFT_H_ + +/* + * Wrappers for FFTW single precision 1d dft + */ + +#include +#include +#include + +namespace gr { + namespace fft { + + + /*! \brief Helper function for allocating complex fft buffers + */ + gr_complex* malloc_complex(int size); + + /*! \brief Helper function for allocating float fft buffers + */ + float* malloc_float(int size); + + /*! \brief Helper function for freeing fft buffers + */ + void free(void *b); + + /*! + * \brief Export reference to planner mutex for those apps that + * want to use FFTW w/o using the fft_impl_fftw* classes. + */ + class FFT_API planner { + public: + typedef boost::mutex::scoped_lock scoped_lock; + /*! + * Return reference to planner mutex + */ + static boost::mutex &mutex(); + }; + + /*! + * \brief FFT: complex in, complex out + * \ingroup misc + */ + class FFT_API fft_complex { + int d_fft_size; + int d_nthreads; + gr_complex *d_inbuf; + gr_complex *d_outbuf; + void *d_plan; + + public: + fft_complex(int fft_size, bool forward = true, int nthreads=1); + virtual ~fft_complex(); + + /* + * These return pointers to buffers owned by fft_impl_fft_complex + * into which input and output take place. It's done this way in + * order to ensure optimal alignment for SIMD instructions. + */ + gr_complex *get_inbuf() const { return d_inbuf; } + gr_complex *get_outbuf() const { return d_outbuf; } + + int inbuf_length() const { return d_fft_size; } + int outbuf_length() const { return d_fft_size; } + + /*! + * Set the number of threads to use for caclulation. + */ + void set_nthreads(int n); + + /*! + * Get the number of threads being used by FFTW + */ + int nthreads() const { return d_nthreads; } + + /*! + * compute FFT. The input comes from inbuf, the output is placed in + * outbuf. + */ + void execute(); + }; + + /*! + * \brief FFT: real in, complex out + * \ingroup misc + */ + class FFT_API fft_real_fwd { + int d_fft_size; + int d_nthreads; + float *d_inbuf; + gr_complex *d_outbuf; + void *d_plan; + + public: + fft_real_fwd (int fft_size, int nthreads=1); + virtual ~fft_real_fwd (); + + /* + * These return pointers to buffers owned by fft_impl_fft_real_fwd + * into which input and output take place. It's done this way in + * order to ensure optimal alignment for SIMD instructions. + */ + float *get_inbuf() const { return d_inbuf; } + gr_complex *get_outbuf() const { return d_outbuf; } + + int inbuf_length() const { return d_fft_size; } + int outbuf_length() const { return d_fft_size / 2 + 1; } + + /*! + * Set the number of threads to use for caclulation. + */ + void set_nthreads(int n); + + /*! + * Get the number of threads being used by FFTW + */ + int nthreads() const { return d_nthreads; } + + /*! + * compute FFT. The input comes from inbuf, the output is placed in + * outbuf. + */ + void execute(); + }; + + /*! + * \brief FFT: complex in, float out + * \ingroup misc + */ + class FFT_API fft_real_rev { + int d_fft_size; + int d_nthreads; + gr_complex *d_inbuf; + float *d_outbuf; + void *d_plan; + + public: + fft_real_rev(int fft_size, int nthreads=1); + virtual ~fft_real_rev(); + + /* + * These return pointers to buffers owned by fft_impl_fft_real_rev + * into which input and output take place. It's done this way in + * order to ensure optimal alignment for SIMD instructions. + */ + gr_complex *get_inbuf() const { return d_inbuf; } + float *get_outbuf() const { return d_outbuf; } + + int inbuf_length() const { return d_fft_size / 2 + 1; } + int outbuf_length() const { return d_fft_size; } + + /*! + * Set the number of threads to use for caclulation. + */ + void set_nthreads(int n); + + /*! + * Get the number of threads being used by FFTW + */ + int nthreads() const { return d_nthreads; } + + /*! + * compute FFT. The input comes from inbuf, the output is placed in + * outbuf. + */ + void execute(); + }; + + } /* namespace fft */ +} /*namespace gr */ + +#endif /* _FFT_FFT_H_ */ diff --git a/gr-filter/include/filter/fft_vcc.h b/gr-filter/include/filter/fft_vcc.h new file mode 100644 index 000000000..561ae858d --- /dev/null +++ b/gr-filter/include/filter/fft_vcc.h @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2008,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FFT_FFT_VCC_H +#define INCLUDED_FFT_FFT_VCC_H + +#include +#include + +namespace gr { + namespace fft { + + class FFT_API fft_vcc : virtual public gr_sync_block + { + public: + + // gr::fft::fft_vcc::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief Compute forward or reverse FFT. complex vector in / complex vector out. + * \ingroup dft_blk + */ + static FFT_API sptr make(int fft_size, bool forward, + const std::vector &window, + bool shift=false, int nthreads=1); + + virtual void set_nthreads(int n) = 0; + + virtual int nthreads() const = 0; + + virtual bool set_window(const std::vector &window) = 0; + }; + + } /* namespace fft */ +} /* namespace gr */ + +#endif /* INCLUDED_FFT_FFT_VCC_H */ diff --git a/gr-filter/include/filter/fir_filter_fff.h b/gr-filter/include/filter/fir_filter_fff.h new file mode 100644 index 000000000..5b6d19b34 --- /dev/null +++ b/gr-filter/include/filter/fir_filter_fff.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef FILTER_FIR_FILTER_FFF_H +#define FILTER_FIR_FILTER_FFF_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fir_filter_fff : virtual public gr_sync_decimator + { + public: + + // gr::filter::fir_filter_fff::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief FIR filter with float input, float output, and float taps + * \ingroup filter_blk + */ + static FILTER_API sptr make(int decimation, + const std::vector &taps); + + virtual void set_taps (const std::vector &taps) = 0; + virtual std::vector taps () const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* FILTER_FIR_FILTER_FFF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt new file mode 100644 index 000000000..0ffdb75fc --- /dev/null +++ b/gr-filter/lib/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +######################################################################## +# Setup the include and linker paths +######################################################################## +include_directories( + ${GNURADIO_CORE_INCLUDE_DIRS} + ${VOLK_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +include_directories(${FILTER_INCLUDE_DIRS}) +link_directories(${FILTER_LIBRARY_DIRS}) + +include_directories(${FFT_INCLUDE_DIRS}) +link_directories(${FFT_LIBRARY_DIRS}) + +include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) + +include_directories(${FFTW3F_INCLUDE_DIRS}) +link_directories(${FFTW3F_LIBRARY_DIRS}) + +######################################################################## +# Setup library +######################################################################## +list(APPEND filter_sources + fir_filter_fff_impl.cc +) + +list(APPEND filter_libs + gnuradio-core + gnuradio-fft + ${Boost_LIBRARIES} + ${FFTW3F_LIBRARIES} + ${VOLK_LIBRARIES} +) + +add_library(gnuradio-filter SHARED ${filter_sources}) +target_link_libraries(gnuradio-filter ${filter_libs}) +GR_LIBRARY_FOO(gnuradio-filter RUNTIME_COMPONENT "filter_runtime" DEVEL_COMPONENT "filter_devel") +add_dependencies(gnuradio-filter gnuradio-fft filter_generated_includes filter_generated_swigs) diff --git a/gr-filter/lib/fir_filter_fff_impl.cc b/gr-filter/lib/fir_filter_fff_impl.cc new file mode 100644 index 000000000..4a1233a24 --- /dev/null +++ b/gr-filter/lib/fir_filter_fff_impl.cc @@ -0,0 +1,110 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fir_filter_fff_impl.h" +#include +#include +#include +#include + +namespace gr { + namespace filter { + + fir_filter_fff::sptr + fir_filter_fff::make(int decimation, const std::vector &taps) + { + return gnuradio::get_initial_sptr(new fir_filter_fff_impl + (decimation, taps)); + } + + + fir_filter_fff_impl::fir_filter_fff_impl(int decimation, + const std::vector &taps) + : gr_sync_decimator("fir_filter_fff", + gr_make_io_signature (1, 1, sizeof(float)), + gr_make_io_signature (1, 1, sizeof(float)), + decimation) + { + set_taps(taps); + d_updated = false; + set_history(d_ntaps+1); + } + + fir_filter_fff_impl::~fir_filter_fff_impl() + { + } + + void + fir_filter_fff_impl::set_taps(const std::vector &taps) + { + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_float(d_ntaps); + for(int i = 0; i < d_ntaps; i++) { + d_taps[i] = taps[i]; + } + d_updated = true; + } + + std::vector + fir_filter_fff_impl::taps() const + { + std::vector t; + for(int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[i]); + return t; + } + + int + fir_filter_fff_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *in = (const float*)input_items[0]; + float *out = (float*)output_items[0]; + + if (d_updated) { + set_history((unsigned int)d_ntaps+1); + d_updated = false; + return 0; // history requirements may have changed. + } + + if (decimation() == 1) { + for(int i = 0; i < noutput_items; i++) { + volk_32f_x2_dot_prod_32f_u(&out[i], &in[i], d_taps, d_ntaps); + } + + //d_fir->filterN(out, in, noutput_items); + } + else { + //d_fir->filterNdec(out, in, noutput_items, decimation()); + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ + diff --git a/gr-filter/lib/fir_filter_fff_impl.h b/gr-filter/lib/fir_filter_fff_impl.h new file mode 100644 index 000000000..962781466 --- /dev/null +++ b/gr-filter/lib/fir_filter_fff_impl.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef FILTER_FIR_FILTER_FFF_IMPL_H +#define FILTER_FIR_FILTER_FFF_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fir_filter_fff_impl : public fir_filter_fff + { + private: + float *d_taps; + int d_ntaps; + bool d_updated; + + public: + fir_filter_fff_impl(int decimation, + const std::vector &taps); + + ~fir_filter_fff_impl(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* FILTER_FIR_FILTER_FFF_IMPL_H */ diff --git a/gr-filter/python/CMakeLists.txt b/gr-filter/python/CMakeLists.txt new file mode 100644 index 000000000..e52cac759 --- /dev/null +++ b/gr-filter/python/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +######################################################################## +include(GrPython) + +GR_PYTHON_INSTALL( + FILES + __init__.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/filter + COMPONENT "filter_python" +) + +######################################################################## +# Handle the unit tests +######################################################################## +if(ENABLE_TESTING) +include(GrTest) +file(GLOB py_qa_test_files "qa_*.py") +foreach(py_qa_test_file ${py_qa_test_files}) + get_filename_component(py_qa_test_name ${py_qa_test_file} NAME_WE) + set(GR_TEST_PYTHON_DIRS + ${CMAKE_BINARY_DIR}/gnuradio-core/src/python + ${CMAKE_BINARY_DIR}/gnuradio-core/src/lib/swig + ${CMAKE_BINARY_DIR}/gr-filter/python + ${CMAKE_BINARY_DIR}/gr-filter/swig + ) + set(GR_TEST_TARGET_DEPS gruel gnuradio-core gnuradio-filter) + GR_ADD_TEST(${py_qa_test_name} ${PYTHON_EXECUTABLE} ${py_qa_test_file}) +endforeach(py_qa_test_file) +endif(ENABLE_TESTING) diff --git a/gr-filter/python/__init__.py b/gr-filter/python/__init__.py new file mode 100644 index 000000000..56dd2dc5a --- /dev/null +++ b/gr-filter/python/__init__.py @@ -0,0 +1,28 @@ +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +''' +This is the gr-filter package. This package provides GNU Radio +processing blocks for FILTER and related functions. +''' + +from filter_swig import * + diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py new file mode 100755 index 000000000..928f17aa3 --- /dev/null +++ b/gr-filter/python/qa_fir_filter.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# +# Copyright 2008,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter + +class test_filter(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block () + + def tearDown(self): + self.tb = None + + def test_fir_filter_fff_001(self): + src_data = [1, 2, 3, 4] + expected_data = [0, 0.5, 1.5, 2.5] + src = gr.vector_source_f(src_data) + op = filter.fir_filter_fff(1, [0.5, 0.5]) + dst = gr.vector_sink_f() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_data, result_data, 5) + +if __name__ == '__main__': + gr_unittest.run(test_filter, "test_filter.xml") + diff --git a/gr-filter/swig/CMakeLists.txt b/gr-filter/swig/CMakeLists.txt new file mode 100644 index 000000000..5dff9f631 --- /dev/null +++ b/gr-filter/swig/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +######################################################################## +# Setup swig generation +######################################################################## +include(GrPython) +include(GrSwig) + +set(GR_SWIG_INCLUDE_DIRS + ${FILTER_INCLUDE_DIRS} + ${GNURADIO_CORE_SWIG_INCLUDE_DIRS} + ${FFT_INCLUDE_DIRS} + ${FFTW3F_INCLUDE_DIRS} +) + +# FIXME: rename to filter_swig_doc.i when gnuradio-core is updated +set(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/gr_filter_swig_doc.i) +set(GR_SWIG_DOC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../lib) + +set(GR_SWIG_LIBRARIES gnuradio-filter gnuradio-fft) + +GR_SWIG_MAKE(filter_swig filter_swig.i) + +GR_SWIG_INSTALL( + TARGETS filter_swig + DESTINATION ${GR_PYTHON_DIR}/gnuradio/filter + COMPONENT "filter_python" +) + +install( + FILES + filter_swig.i + ${CMAKE_CURRENT_BINARY_DIR}/gr_filter_swig_doc.i + DESTINATION ${GR_INCLUDE_DIR}/gnuradio/swig + COMPONENT "filter_swig" +) diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i new file mode 100644 index 000000000..55ee3c56c --- /dev/null +++ b/gr-filter/swig/filter_swig.i @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#define FILTER_API + +%include "gnuradio.i" + +//load generated python docstrings +%include "gr_filter_swig_doc.i" + +%{ +#include "filter/fir_filter_fff.h" +%} + +%include "filter/fir_filter_fff.h" + +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); -- cgit From 7f8331808f99a9747bed39e55bbee98bc58c6038 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 11:45:29 -0400 Subject: build: reworking cmake structure for include and lib directories. I think this is cleaner. We also probably don't need the link_directories (according to the cmake guys, this is no longer necessary). --- gr-fft/lib/CMakeLists.txt | 11 ++++------- gr-filter/lib/CMakeLists.txt | 16 ++++++---------- gr-wavelet/lib/CMakeLists.txt | 9 +++------ 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/gr-fft/lib/CMakeLists.txt b/gr-fft/lib/CMakeLists.txt index e2f17a183..ea1248fba 100644 --- a/gr-fft/lib/CMakeLists.txt +++ b/gr-fft/lib/CMakeLists.txt @@ -21,19 +21,16 @@ # Setup the include and linker paths ######################################################################## include_directories( - ${GNURADIO_CORE_INCLUDE_DIRS} - ${GR_FFT_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} + ${GNURADIO_CORE_INCLUDE_DIRS} + ${GR_FFT_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} + ${FFTW3F_INCLUDE_DIRS} ) -include_directories(${FFT_INCLUDE_DIRS}) link_directories(${FFT_LIBRARY_DIRS}) - -include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) - -include_directories(${FFTW3F_INCLUDE_DIRS}) link_directories(${FFTW3F_LIBRARY_DIRS}) ######################################################################## diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 0ffdb75fc..2850b6925 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -21,22 +21,18 @@ # Setup the include and linker paths ######################################################################## include_directories( - ${GNURADIO_CORE_INCLUDE_DIRS} - ${VOLK_INCLUDE_DIRS} + ${GR_FILTER_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} + ${VOLK_INCLUDE_DIRS} + ${GNURADIO_CORE_INCLUDE_DIRS} + ${GR_FFT_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} + ${FFTW3F_INCLUDE_DIRS} ) -include_directories(${FILTER_INCLUDE_DIRS}) -link_directories(${FILTER_LIBRARY_DIRS}) - -include_directories(${FFT_INCLUDE_DIRS}) link_directories(${FFT_LIBRARY_DIRS}) - -include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) - -include_directories(${FFTW3F_INCLUDE_DIRS}) link_directories(${FFTW3F_LIBRARY_DIRS}) ######################################################################## diff --git a/gr-wavelet/lib/CMakeLists.txt b/gr-wavelet/lib/CMakeLists.txt index f0d9068b7..3247c17b5 100644 --- a/gr-wavelet/lib/CMakeLists.txt +++ b/gr-wavelet/lib/CMakeLists.txt @@ -21,19 +21,16 @@ # Setup the include and linker paths ######################################################################## include_directories( - ${GNURADIO_CORE_INCLUDE_DIRS} ${GR_WAVELET_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} + ${GNURADIO_CORE_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} + ${GSL_INCLUDE_DIRS} ) -include_directories(${WAVELET_INCLUDE_DIRS}) link_directories(${WAVELET_LIBRARY_DIRS}) - -include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) - -include_directories(${GSL_INCLUDE_DIRS}) link_directories(${GSL_LIBRARY_DIRS}) ######################################################################## -- cgit From 38120d6ba5b805f522e74c0e440e487f673cb82e Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 11:59:25 -0400 Subject: fft: export the FFTW malloc and free helper functions. --- gr-fft/include/fft/fft.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gr-fft/include/fft/fft.h b/gr-fft/include/fft/fft.h index 5cc2e21e8..e3fd3f278 100644 --- a/gr-fft/include/fft/fft.h +++ b/gr-fft/include/fft/fft.h @@ -37,15 +37,15 @@ namespace gr { /*! \brief Helper function for allocating complex fft buffers */ - gr_complex* malloc_complex(int size); + FFT_API gr_complex* malloc_complex(int size); /*! \brief Helper function for allocating float fft buffers */ - float* malloc_float(int size); + FFT_API float* malloc_float(int size); /*! \brief Helper function for freeing fft buffers */ - void free(void *b); + FFT_API void free(void *b); /*! * \brief Export reference to planner mutex for those apps that -- cgit From 417337cd4a6cef95bbec7c5d04bbacdab0eb9f47 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 12:43:18 -0400 Subject: filter: fixing up fir_filter_fff to handle taps appropriately, also does decimation. --- gr-filter/lib/fir_filter_fff_impl.cc | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/gr-filter/lib/fir_filter_fff_impl.cc b/gr-filter/lib/fir_filter_fff_impl.cc index 4a1233a24..15ccb4d07 100644 --- a/gr-filter/lib/fir_filter_fff_impl.cc +++ b/gr-filter/lib/fir_filter_fff_impl.cc @@ -48,6 +48,7 @@ namespace gr { gr_make_io_signature (1, 1, sizeof(float)), decimation) { + d_taps = NULL; set_taps(taps); d_updated = false; set_history(d_ntaps+1); @@ -55,11 +56,21 @@ namespace gr { fir_filter_fff_impl::~fir_filter_fff_impl() { + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } } void fir_filter_fff_impl::set_taps(const std::vector &taps) { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + d_ntaps = (int)taps.size(); d_taps = fft::malloc_float(d_ntaps); for(int i = 0; i < d_ntaps; i++) { @@ -95,11 +106,13 @@ namespace gr { for(int i = 0; i < noutput_items; i++) { volk_32f_x2_dot_prod_32f_u(&out[i], &in[i], d_taps, d_ntaps); } - - //d_fir->filterN(out, in, noutput_items); } else { - //d_fir->filterNdec(out, in, noutput_items, decimation()); + int j = 0; + for(int i = 0; i < noutput_items; i++) { + volk_32f_x2_dot_prod_32f_u(&out[i], &in[j], d_taps, d_ntaps); + j += decimation(); + } } return noutput_items; -- cgit From a247889d7ad212cf3a69df8ec95dc2436e4cc400 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 12:43:43 -0400 Subject: filter: adding examples folder. First example compares old filter in gr to new one in filter. --- gr-filter/examples/CMakeLists.txt | 27 +++++++++++ gr-filter/examples/fir_filter_fff.py | 93 ++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 gr-filter/examples/CMakeLists.txt create mode 100755 gr-filter/examples/fir_filter_fff.py diff --git a/gr-filter/examples/CMakeLists.txt b/gr-filter/examples/CMakeLists.txt new file mode 100644 index 000000000..c798a8403 --- /dev/null +++ b/gr-filter/examples/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +include(GrPython) + +# Base stuff +GR_PYTHON_INSTALL(PROGRAMS + fir_filter_fff.py + DESTINATION ${GR_PKG_DIGITAL_EXAMPLES_DIR} + COMPONENT "filter_python" +) diff --git a/gr-filter/examples/fir_filter_fff.py b/gr-filter/examples/fir_filter_fff.py new file mode 100755 index 000000000..538aded44 --- /dev/null +++ b/gr-filter/examples/fir_filter_fff.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +from gnuradio import gr, filter +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +class example_fir_filter_fff(gr.top_block): + def __init__(self, N, fs, bw, tw, atten, D): + gr.top_block.__init__(self) + + self._nsamps = N + self._fs = fs + self._bw = bw + self._tw = tw + self._at = atten + self._decim = D + taps = gr.firdes.low_pass_2(1, self._fs, self._bw, self._tw, self._at) + print "Num. Taps: ", len(taps) + + self.src = gr.noise_source_f(gr.GR_GAUSSIAN, 1) + self.head = gr.head(gr.sizeof_float, self._nsamps) + + self.filt0 = filter.fir_filter_fff(self._decim, taps) + self.filt1 = gr.fir_filter_fff(self._decim, taps) + + self.vsnk_src = gr.vector_sink_f() + self.vsnk_out = gr.vector_sink_f() + self.vsnk_gr = gr.vector_sink_f() + + self.connect(self.src, self.head, self.vsnk_src) + self.connect(self.head, self.filt0, self.vsnk_out) + self.connect(self.head, self.filt1, self.vsnk_gr) + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=10000, + help="Number of samples to process [default=%default]") + parser.add_option("-s", "--samplerate", type="eng_float", default=8000, + help="System sample rate [default=%default]") + parser.add_option("-B", "--bandwidth", type="eng_float", default=1000, + help="Filter bandwidth [default=%default]") + parser.add_option("-T", "--transition", type="eng_float", default=100, + help="Transition band [default=%default]") + parser.add_option("-A", "--attenuation", type="eng_float", default=80, + help="Stopband attenuation [default=%default]") + parser.add_option("-D", "--decimation", type="int", default=1, + help="Decmation factor [default=%default]") + (options, args) = parser.parse_args () + + put = example_fir_filter_fff(options.nsamples, + options.samplerate, + options.bandwidth, + options.transition, + options.attenuation, + options.decimation) + put.run() + + data_src = scipy.array(put.vsnk_src.data()) + data_snk = scipy.array(put.vsnk_out.data()) + data_gr = scipy.array(put.vsnk_gr.data()) + + # Plot the signals PSDs + nfft = 1024 + f1 = pylab.figure(1, figsize=(12,10)) + s1 = f1.add_subplot(1,1,1) + s1.psd(data_src, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + -- cgit From 32f807a8c8f1bcadfd8f8ad4c5a46c1b099f8c8f Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 16:14:09 -0400 Subject: filter: Reworking filter to have a set of basic implementation classes for filters of different kinds. The GR blocks are templated now and call from fir_filters for the volk-specific implemenation. Note the modification to build_utils.py to accomodate these changes. --- gnuradio-core/src/python/build_utils.py | 32 +++- gr-filter/CMakeLists.txt | 4 +- gr-filter/include/filter/CMakeLists.txt | 15 +- gr-filter/include/filter/fft.h | 195 -------------------- gr-filter/include/filter/fft_vcc.h | 57 ------ gr-filter/include/filter/fir_filter.h | 112 ++++++++++++ gr-filter/include/filter/fir_filter_XXX.h.t | 55 ++++++ gr-filter/include/filter/fir_filter_fff.h | 53 ------ gr-filter/lib/CMakeLists.txt | 69 ++++++- gr-filter/lib/fir_filter.cc | 268 ++++++++++++++++++++++++++++ gr-filter/lib/fir_filter_XXX_impl.cc.t | 98 ++++++++++ gr-filter/lib/fir_filter_XXX_impl.h.t | 57 ++++++ gr-filter/lib/fir_filter_fff_impl.cc | 123 ------------- gr-filter/lib/fir_filter_fff_impl.h | 56 ------ gr-filter/python/qa_fir_filter.py | 22 +++ gr-filter/swig/CMakeLists.txt | 2 +- gr-filter/swig/filter_swig.i | 6 + 17 files changed, 729 insertions(+), 495 deletions(-) delete mode 100644 gr-filter/include/filter/fft.h delete mode 100644 gr-filter/include/filter/fft_vcc.h create mode 100644 gr-filter/include/filter/fir_filter.h create mode 100644 gr-filter/include/filter/fir_filter_XXX.h.t delete mode 100644 gr-filter/include/filter/fir_filter_fff.h create mode 100644 gr-filter/lib/fir_filter.cc create mode 100644 gr-filter/lib/fir_filter_XXX_impl.cc.t create mode 100644 gr-filter/lib/fir_filter_XXX_impl.h.t delete mode 100644 gr-filter/lib/fir_filter_fff_impl.cc delete mode 100644 gr-filter/lib/fir_filter_fff_impl.h diff --git a/gnuradio-core/src/python/build_utils.py b/gnuradio-core/src/python/build_utils.py index 0660941d5..0898f46c4 100644 --- a/gnuradio-core/src/python/build_utils.py +++ b/gnuradio-core/src/python/build_utils.py @@ -182,7 +182,37 @@ def standard_dict (name, code3): d['GUARD_NAME'] = 'INCLUDED_%s_H' % name.upper () d['BASE_NAME'] = re.sub ('^gr_', '', name) d['SPTR_NAME'] = '%s_sptr' % name - d['WARNING'] = 'WARNING: this file is machine generated. Edits will be over written' + d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' + d['COPYRIGHT'] = copyright + d['TYPE'] = i_type (code3) + d['I_TYPE'] = i_type (code3) + d['O_TYPE'] = o_type (code3) + d['TAP_TYPE'] = tap_type (code3) + d['IS_COMPLEX'] = is_complex (code3) + return d + + +def standard_dict2 (name, code3, package): + d = {} + d['NAME'] = name + d['BASE_NAME'] = name + d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) + d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' + d['COPYRIGHT'] = copyright + d['TYPE'] = i_type (code3) + d['I_TYPE'] = i_type (code3) + d['O_TYPE'] = o_type (code3) + d['TAP_TYPE'] = tap_type (code3) + d['IS_COMPLEX'] = is_complex (code3) + return d + +def standard_impl_dict2 (name, code3, package): + d = {} + d['NAME'] = name + d['IMPL_NAME'] = name + d['BASE_NAME'] = name.rstrip("_impl") + d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) + d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' d['COPYRIGHT'] = copyright d['TYPE'] = i_type (code3) d['I_TYPE'] = i_type (code3) diff --git a/gr-filter/CMakeLists.txt b/gr-filter/CMakeLists.txt index a4d25fd13..02b8f23fe 100644 --- a/gr-filter/CMakeLists.txt +++ b/gr-filter/CMakeLists.txt @@ -35,9 +35,11 @@ GR_REGISTER_COMPONENT("gr-filter" ENABLE_GR_FILTER ENABLE_GR_FFT ) -GR_SET_GLOBAL(FILTER_INCLUDE_DIRS +GR_SET_GLOBAL(GR_FILTER_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/lib + ${CMAKE_CURRENT_BINARY_DIR}/include ) ######################################################################## diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index a3c83f3dc..4889312da 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -32,7 +32,7 @@ os.chdir('${CMAKE_CURRENT_BINARY_DIR}') if __name__ == '__main__': import build_utils - root, inp = sys.argv[1:3] + root, inp = sys.argv[1:3] for sig in sys.argv[3:]: name = re.sub ('X+', sig, root) d = build_utils.standard_dict2(name, sig, 'filter') @@ -64,19 +64,20 @@ endmacro(expand_h) ######################################################################## # Invoke macro to generate various sources ####################################################################### -#expand_h(fir_filter_XXX fff ccc ccf fcc fsf scc) +expand_h(fir_filter_XXX fff ccf ccc) -#add_custom_target(filter_generated_includes DEPENDS -# ${generated_includes} -#) +add_custom_target(filter_generated_includes DEPENDS + ${generated_includes} +) ######################################################################## # Install header files ######################################################################## install(FILES api.h - fir_filter_fff.h - DESTINATION ${GR_INCLUDE_DIR}/gnuradio/fft + fir_filter.h + ${generated_includes} + DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "fft_devel" ) diff --git a/gr-filter/include/filter/fft.h b/gr-filter/include/filter/fft.h deleted file mode 100644 index 5cc2e21e8..000000000 --- a/gr-filter/include/filter/fft.h +++ /dev/null @@ -1,195 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2008,2012 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _FFT_FFT_H_ -#define _FFT_FFT_H_ - -/* - * Wrappers for FFTW single precision 1d dft - */ - -#include -#include -#include - -namespace gr { - namespace fft { - - - /*! \brief Helper function for allocating complex fft buffers - */ - gr_complex* malloc_complex(int size); - - /*! \brief Helper function for allocating float fft buffers - */ - float* malloc_float(int size); - - /*! \brief Helper function for freeing fft buffers - */ - void free(void *b); - - /*! - * \brief Export reference to planner mutex for those apps that - * want to use FFTW w/o using the fft_impl_fftw* classes. - */ - class FFT_API planner { - public: - typedef boost::mutex::scoped_lock scoped_lock; - /*! - * Return reference to planner mutex - */ - static boost::mutex &mutex(); - }; - - /*! - * \brief FFT: complex in, complex out - * \ingroup misc - */ - class FFT_API fft_complex { - int d_fft_size; - int d_nthreads; - gr_complex *d_inbuf; - gr_complex *d_outbuf; - void *d_plan; - - public: - fft_complex(int fft_size, bool forward = true, int nthreads=1); - virtual ~fft_complex(); - - /* - * These return pointers to buffers owned by fft_impl_fft_complex - * into which input and output take place. It's done this way in - * order to ensure optimal alignment for SIMD instructions. - */ - gr_complex *get_inbuf() const { return d_inbuf; } - gr_complex *get_outbuf() const { return d_outbuf; } - - int inbuf_length() const { return d_fft_size; } - int outbuf_length() const { return d_fft_size; } - - /*! - * Set the number of threads to use for caclulation. - */ - void set_nthreads(int n); - - /*! - * Get the number of threads being used by FFTW - */ - int nthreads() const { return d_nthreads; } - - /*! - * compute FFT. The input comes from inbuf, the output is placed in - * outbuf. - */ - void execute(); - }; - - /*! - * \brief FFT: real in, complex out - * \ingroup misc - */ - class FFT_API fft_real_fwd { - int d_fft_size; - int d_nthreads; - float *d_inbuf; - gr_complex *d_outbuf; - void *d_plan; - - public: - fft_real_fwd (int fft_size, int nthreads=1); - virtual ~fft_real_fwd (); - - /* - * These return pointers to buffers owned by fft_impl_fft_real_fwd - * into which input and output take place. It's done this way in - * order to ensure optimal alignment for SIMD instructions. - */ - float *get_inbuf() const { return d_inbuf; } - gr_complex *get_outbuf() const { return d_outbuf; } - - int inbuf_length() const { return d_fft_size; } - int outbuf_length() const { return d_fft_size / 2 + 1; } - - /*! - * Set the number of threads to use for caclulation. - */ - void set_nthreads(int n); - - /*! - * Get the number of threads being used by FFTW - */ - int nthreads() const { return d_nthreads; } - - /*! - * compute FFT. The input comes from inbuf, the output is placed in - * outbuf. - */ - void execute(); - }; - - /*! - * \brief FFT: complex in, float out - * \ingroup misc - */ - class FFT_API fft_real_rev { - int d_fft_size; - int d_nthreads; - gr_complex *d_inbuf; - float *d_outbuf; - void *d_plan; - - public: - fft_real_rev(int fft_size, int nthreads=1); - virtual ~fft_real_rev(); - - /* - * These return pointers to buffers owned by fft_impl_fft_real_rev - * into which input and output take place. It's done this way in - * order to ensure optimal alignment for SIMD instructions. - */ - gr_complex *get_inbuf() const { return d_inbuf; } - float *get_outbuf() const { return d_outbuf; } - - int inbuf_length() const { return d_fft_size / 2 + 1; } - int outbuf_length() const { return d_fft_size; } - - /*! - * Set the number of threads to use for caclulation. - */ - void set_nthreads(int n); - - /*! - * Get the number of threads being used by FFTW - */ - int nthreads() const { return d_nthreads; } - - /*! - * compute FFT. The input comes from inbuf, the output is placed in - * outbuf. - */ - void execute(); - }; - - } /* namespace fft */ -} /*namespace gr */ - -#endif /* _FFT_FFT_H_ */ diff --git a/gr-filter/include/filter/fft_vcc.h b/gr-filter/include/filter/fft_vcc.h deleted file mode 100644 index 561ae858d..000000000 --- a/gr-filter/include/filter/fft_vcc.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004,2007,2008,2012 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef INCLUDED_FFT_FFT_VCC_H -#define INCLUDED_FFT_FFT_VCC_H - -#include -#include - -namespace gr { - namespace fft { - - class FFT_API fft_vcc : virtual public gr_sync_block - { - public: - - // gr::fft::fft_vcc::sptr - typedef boost::shared_ptr sptr; - - /*! - * \brief Compute forward or reverse FFT. complex vector in / complex vector out. - * \ingroup dft_blk - */ - static FFT_API sptr make(int fft_size, bool forward, - const std::vector &window, - bool shift=false, int nthreads=1); - - virtual void set_nthreads(int n) = 0; - - virtual int nthreads() const = 0; - - virtual bool set_window(const std::vector &window) = 0; - }; - - } /* namespace fft */ -} /* namespace gr */ - -#endif /* INCLUDED_FFT_FFT_VCC_H */ diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h new file mode 100644 index 000000000..c307fea46 --- /dev/null +++ b/gr-filter/include/filter/fir_filter.h @@ -0,0 +1,112 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +namespace gr { + namespace filter { + namespace impl { + + class FILTER_API fir_filter_fff + { + public: + fir_filter_fff(int decimation, + const std::vector &taps); + ~fir_filter_fff(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + unsigned int ntaps() const; + + float filter(const float input[]); + void filterN(float output[], + const float input[], + unsigned long n); + void filterNdec(float output[], + const float input[], + unsigned long n, + unsigned int decimate); + + private: + unsigned int d_ntaps; + float *d_taps; + }; + + /**************************************************************/ + + class FILTER_API fir_filter_ccf + { + public: + fir_filter_ccf(int decimation, + const std::vector &taps); + ~fir_filter_ccf(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + unsigned int ntaps() const; + + gr_complex filter(const gr_complex input[]); + void filterN(gr_complex output[], + const gr_complex input[], + unsigned long n); + void filterNdec(gr_complex output[], + const gr_complex input[], + unsigned long n, + unsigned int decimate); + + private: + unsigned int d_ntaps; + gr_complex *d_taps; + }; + + /**************************************************************/ + + class FILTER_API fir_filter_ccc + { + public: + fir_filter_ccc(int decimation, + const std::vector &taps); + ~fir_filter_ccc(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + unsigned int ntaps() const; + + gr_complex filter(const gr_complex input[]); + void filterN(gr_complex output[], + const gr_complex input[], + unsigned long n); + void filterNdec(gr_complex output[], + const gr_complex input[], + unsigned long n, + unsigned int decimate); + + private: + unsigned int d_ntaps; + gr_complex *d_taps; + }; + + } /* namespace impl */ + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/include/filter/fir_filter_XXX.h.t b/gr-filter/include/filter/fir_filter_XXX.h.t new file mode 100644 index 000000000..ef6b8bfc9 --- /dev/null +++ b/gr-filter/include/filter/fir_filter_XXX.h.t @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API @BASE_NAME@ : virtual public gr_sync_decimator + { + public: + + // gr::filter::@BASE_NAME@::sptr + typedef boost::shared_ptr<@BASE_NAME@> sptr; + + /*! + * \brief FIR filter with @I_TYPE@ input, @O_TYPE@ output, and @TAP_TYPE@ taps + * \ingroup filter_blk + */ + static FILTER_API sptr make(int decimation, + const std::vector<@TAP_TYPE@> &taps); + + virtual void set_taps (const std::vector<@TAP_TYPE@> &taps) = 0; + virtual std::vector<@TAP_TYPE@> taps () const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/include/filter/fir_filter_fff.h b/gr-filter/include/filter/fir_filter_fff.h deleted file mode 100644 index 5b6d19b34..000000000 --- a/gr-filter/include/filter/fir_filter_fff.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004,2012 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef FILTER_FIR_FILTER_FFF_H -#define FILTER_FIR_FILTER_FFF_H - -#include -#include - -namespace gr { - namespace filter { - - class FILTER_API fir_filter_fff : virtual public gr_sync_decimator - { - public: - - // gr::filter::fir_filter_fff::sptr - typedef boost::shared_ptr sptr; - - /*! - * \brief FIR filter with float input, float output, and float taps - * \ingroup filter_blk - */ - static FILTER_API sptr make(int decimation, - const std::vector &taps); - - virtual void set_taps (const std::vector &taps) = 0; - virtual std::vector taps () const = 0; - }; - - } /* namespace filter */ -} /* namespace gr */ - -#endif /* FILTER_FIR_FILTER_FFF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 2850b6925..784a3c109 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -17,6 +17,72 @@ # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. +######################################################################## +# generate helper scripts to expand templated files +######################################################################## +include(GrPython) + +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py " +#!${PYTHON_EXECUTABLE} + +import sys, os, re +sys.path.append('${GR_CORE_PYTHONPATH}') +os.environ['srcdir'] = '${CMAKE_CURRENT_SOURCE_DIR}' +os.chdir('${CMAKE_CURRENT_BINARY_DIR}') + +if __name__ == '__main__': + import build_utils + root, inp = sys.argv[1:3] + for sig in sys.argv[3:]: + name = re.sub ('X+', sig, root) + d = build_utils.standard_impl_dict2(name, sig, 'filter') + build_utils.expand_template(d, inp) +") + +macro(expand_cc root) + #make a list of all the generated files + unset(expanded_files_cc) + unset(expanded_files_h) + foreach(sig ${ARGN}) + string(REGEX REPLACE "X+" ${sig} name ${root}) + list(APPEND expanded_files_cc ${CMAKE_CURRENT_BINARY_DIR}/${name}.cc) + list(APPEND expanded_files_h ${CMAKE_CURRENT_BINARY_DIR}/${name}.h) + endforeach(sig) + + #create a command to generate the source files + add_custom_command( + OUTPUT ${expanded_files_cc} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}.cc.t + COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} + ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py + ${root} ${root}.cc.t ${ARGN} + ) + + #create a command to generate the header file + add_custom_command( + OUTPUT ${expanded_files_h} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}.h.t + COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} + ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py + ${root} ${root}.h.t ${ARGN} + ) + + #make source files depends on headers to force generation + set_source_files_properties(${expanded_files_cc} + PROPERTIES OBJECT_DEPENDS "${expanded_files_h}" + ) + + #install rules for the generated cc files + list(APPEND generated_sources ${expanded_files_cc}) + list(APPEND generated_headers ${expanded_files_h}) +endmacro(expand_cc) + +######################################################################## +# Invoke macro to generate various sources +######################################################################## +expand_cc(fir_filter_XXX_impl fff ccf ccc) + + ######################################################################## # Setup the include and linker paths ######################################################################## @@ -39,7 +105,8 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) # Setup library ######################################################################## list(APPEND filter_sources - fir_filter_fff_impl.cc + fir_filter.cc + ${generated_sources} ) list(APPEND filter_libs diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc new file mode 100644 index 000000000..5f0e0912d --- /dev/null +++ b/gr-filter/lib/fir_filter.cc @@ -0,0 +1,268 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +namespace gr { + namespace filter { + namespace impl { + + fir_filter_fff::fir_filter_fff(int decimation, + const std::vector &taps) + { + d_taps = NULL; + set_taps(taps); + } + + fir_filter_fff::~fir_filter_fff() + { + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + } + + void + fir_filter_fff::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_float(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[i] = taps[i]; + } + } + + std::vector + fir_filter_fff::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[i]); + return t; + } + + unsigned int + fir_filter_fff::ntaps() const + { + return d_ntaps; + } + + float + fir_filter_fff::filter(const float input[]) + { + float output; + volk_32f_x2_dot_prod_32f_u(&output, input, d_taps, d_ntaps); + return output; + } + + void + fir_filter_fff::filterN(float output[], + const float input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) + output[i] = filter(&input[i]); + } + + + void + fir_filter_fff::filterNdec(float output[], + const float input[], + unsigned long n, + unsigned int decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++){ + output[i] = filter(&input[j]); + j += decimate; + } + } + + /**************************************************************/ + + fir_filter_ccf::fir_filter_ccf(int decimation, + const std::vector &taps) + { + d_taps = NULL; + set_taps(taps); + } + + fir_filter_ccf::~fir_filter_ccf() + { + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + } + + void + fir_filter_ccf::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_complex(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[i] = gr_complex(taps[i],0); + } + } + + std::vector + fir_filter_ccf::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[i].real()); + return t; + } + + unsigned int + fir_filter_ccf::ntaps() const + { + return d_ntaps; + } + + gr_complex + fir_filter_ccf::filter(const gr_complex input[]) + { + gr_complex output; + volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); + return output; + } + + void + fir_filter_ccf::filterN(gr_complex output[], + const gr_complex input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) + output[i] = filter(&input[i]); + } + + + void + fir_filter_ccf::filterNdec(gr_complex output[], + const gr_complex input[], + unsigned long n, + unsigned int decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++){ + output[i] = filter(&input[j]); + j += decimate; + } + } + + /**************************************************************/ + + fir_filter_ccc::fir_filter_ccc(int decimation, + const std::vector &taps) + { + d_taps = NULL; + set_taps(taps); + } + + fir_filter_ccc::~fir_filter_ccc() + { + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + } + + void + fir_filter_ccc::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_complex(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[i] = taps[i]; + } + } + + std::vector + fir_filter_ccc::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[i]); + return t; + } + + unsigned int + fir_filter_ccc::ntaps() const + { + return d_ntaps; + } + + gr_complex + fir_filter_ccc::filter(const gr_complex input[]) + { + gr_complex output; + volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); + return output; + } + + void + fir_filter_ccc::filterN(gr_complex output[], + const gr_complex input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) + output[i] = filter(&input[i]); + } + + + void + fir_filter_ccc::filterNdec(gr_complex output[], + const gr_complex input[], + unsigned long n, + unsigned int decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++){ + output[i] = filter(&input[j]); + j += decimate; + } + } + + } /* namespace impl */ + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fir_filter_XXX_impl.cc.t b/gr-filter/lib/fir_filter_XXX_impl.cc.t new file mode 100644 index 000000000..70c3fba3f --- /dev/null +++ b/gr-filter/lib/fir_filter_XXX_impl.cc.t @@ -0,0 +1,98 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "@IMPL_NAME@.h" +#include +#include +#include + +namespace gr { + namespace filter { + + @BASE_NAME@::sptr + @BASE_NAME@::make(int decimation, const std::vector<@TAP_TYPE@> &taps) + { + return gnuradio::get_initial_sptr(new @IMPL_NAME@ + (decimation, taps)); + } + + + @IMPL_NAME@::@IMPL_NAME@(int decimation, const std::vector<@TAP_TYPE@> &taps) + : gr_sync_decimator("fir_filter_fff", + gr_make_io_signature(1, 1, sizeof(@I_TYPE@)), + gr_make_io_signature(1, 1, sizeof(@O_TYPE@)), + decimation) + { + d_fir = new impl::@BASE_NAME@(decimation, taps); + d_updated = false; + set_history(d_fir->ntaps()+1); + } + + @IMPL_NAME@::~@IMPL_NAME@() + { + delete d_fir; + } + + void + @IMPL_NAME@::set_taps(const std::vector<@TAP_TYPE@> &taps) + { + d_fir->set_taps(taps); + d_updated = true; + } + + std::vector<@TAP_TYPE@> + @IMPL_NAME@::taps() const + { + return d_fir->taps(); + } + + int + @IMPL_NAME@::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const @I_TYPE@ *in = (const @I_TYPE@*)input_items[0]; + @O_TYPE@ *out = (@O_TYPE@*)output_items[0]; + + if (d_updated) { + set_history(d_fir->ntaps()+1); + d_updated = false; + return 0; // history requirements may have changed. + } + + if (decimation() == 1) { + d_fir->filterN(out, in, noutput_items); + } + else { + d_fir->filterNdec(out, in, noutput_items, decimation()); + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ + diff --git a/gr-filter/lib/fir_filter_XXX_impl.h.t b/gr-filter/lib/fir_filter_XXX_impl.h.t new file mode 100644 index 000000000..a40c49bf2 --- /dev/null +++ b/gr-filter/lib/fir_filter_XXX_impl.h.t @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API @IMPL_NAME@ : public @BASE_NAME@ + { + private: + impl::@BASE_NAME@ *d_fir; + bool d_updated; + + public: + @IMPL_NAME@(int decimation, const std::vector<@TAP_TYPE@> &taps); + + ~@IMPL_NAME@(); + + void set_taps(const std::vector<@TAP_TYPE@> &taps); + std::vector<@TAP_TYPE@> taps() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/lib/fir_filter_fff_impl.cc b/gr-filter/lib/fir_filter_fff_impl.cc deleted file mode 100644 index 15ccb4d07..000000000 --- a/gr-filter/lib/fir_filter_fff_impl.cc +++ /dev/null @@ -1,123 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004,2010,2012 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "fir_filter_fff_impl.h" -#include -#include -#include -#include - -namespace gr { - namespace filter { - - fir_filter_fff::sptr - fir_filter_fff::make(int decimation, const std::vector &taps) - { - return gnuradio::get_initial_sptr(new fir_filter_fff_impl - (decimation, taps)); - } - - - fir_filter_fff_impl::fir_filter_fff_impl(int decimation, - const std::vector &taps) - : gr_sync_decimator("fir_filter_fff", - gr_make_io_signature (1, 1, sizeof(float)), - gr_make_io_signature (1, 1, sizeof(float)), - decimation) - { - d_taps = NULL; - set_taps(taps); - d_updated = false; - set_history(d_ntaps+1); - } - - fir_filter_fff_impl::~fir_filter_fff_impl() - { - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - } - - void - fir_filter_fff_impl::set_taps(const std::vector &taps) - { - // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - - d_ntaps = (int)taps.size(); - d_taps = fft::malloc_float(d_ntaps); - for(int i = 0; i < d_ntaps; i++) { - d_taps[i] = taps[i]; - } - d_updated = true; - } - - std::vector - fir_filter_fff_impl::taps() const - { - std::vector t; - for(int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[i]); - return t; - } - - int - fir_filter_fff_impl::work(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) - { - const float *in = (const float*)input_items[0]; - float *out = (float*)output_items[0]; - - if (d_updated) { - set_history((unsigned int)d_ntaps+1); - d_updated = false; - return 0; // history requirements may have changed. - } - - if (decimation() == 1) { - for(int i = 0; i < noutput_items; i++) { - volk_32f_x2_dot_prod_32f_u(&out[i], &in[i], d_taps, d_ntaps); - } - } - else { - int j = 0; - for(int i = 0; i < noutput_items; i++) { - volk_32f_x2_dot_prod_32f_u(&out[i], &in[j], d_taps, d_ntaps); - j += decimation(); - } - } - - return noutput_items; - } - - } /* namespace filter */ -} /* namespace gr */ - diff --git a/gr-filter/lib/fir_filter_fff_impl.h b/gr-filter/lib/fir_filter_fff_impl.h deleted file mode 100644 index 962781466..000000000 --- a/gr-filter/lib/fir_filter_fff_impl.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004,2012 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef FILTER_FIR_FILTER_FFF_IMPL_H -#define FILTER_FIR_FILTER_FFF_IMPL_H - -#include -#include - -namespace gr { - namespace filter { - - class FILTER_API fir_filter_fff_impl : public fir_filter_fff - { - private: - float *d_taps; - int d_ntaps; - bool d_updated; - - public: - fir_filter_fff_impl(int decimation, - const std::vector &taps); - - ~fir_filter_fff_impl(); - - void set_taps(const std::vector &taps); - std::vector taps() const; - - int work(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - }; - - } /* namespace filter */ -} /* namespace gr */ - -#endif /* FILTER_FIR_FILTER_FFF_IMPL_H */ diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py index 928f17aa3..2c88e7830 100755 --- a/gr-filter/python/qa_fir_filter.py +++ b/gr-filter/python/qa_fir_filter.py @@ -41,6 +41,28 @@ class test_filter(gr_unittest.TestCase): result_data = dst.data() self.assertFloatTuplesAlmostEqual(expected_data, result_data, 5) + def test_fir_filter_ccf_001(self): + src_data = [1+1j, 2+2j, 3+3j, 4+4j] + expected_data = [0+0j, 0.5+0.5j, 1.5+1.5j, 2.5+2.5j] + src = gr.vector_source_c(src_data) + op = filter.fir_filter_ccf(1, [0.5, 0.5]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + def test_fir_filter_ccc_001(self): + src_data = [1+1j, 2+2j, 3+3j, 4+4j] + expected_data = [0+0j, -0.5+1.5j, -1.5+4.5j, -2.5+7.5j] + src = gr.vector_source_c(src_data) + op = filter.fir_filter_ccc(1, [0.5+1j, 0.5+1j]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + if __name__ == '__main__': gr_unittest.run(test_filter, "test_filter.xml") diff --git a/gr-filter/swig/CMakeLists.txt b/gr-filter/swig/CMakeLists.txt index 5dff9f631..3eee3a1ee 100644 --- a/gr-filter/swig/CMakeLists.txt +++ b/gr-filter/swig/CMakeLists.txt @@ -24,7 +24,7 @@ include(GrPython) include(GrSwig) set(GR_SWIG_INCLUDE_DIRS - ${FILTER_INCLUDE_DIRS} + ${GR_FILTER_INCLUDE_DIRS} ${GNURADIO_CORE_SWIG_INCLUDE_DIRS} ${FFT_INCLUDE_DIRS} ${FFTW3F_INCLUDE_DIRS} diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 55ee3c56c..28268528a 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -29,8 +29,14 @@ %{ #include "filter/fir_filter_fff.h" +#include "filter/fir_filter_ccf.h" +#include "filter/fir_filter_ccc.h" %} %include "filter/fir_filter_fff.h" +%include "filter/fir_filter_ccf.h" +%include "filter/fir_filter_ccc.h" GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); -- cgit From 26531c2da601a1c21c50e3644350c604c27f8658 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 18:59:23 -0400 Subject: filter: fixed FIR filter taps and added complex FFT filter. --- gr-filter/examples/fft_filter_ccc.py | 107 +++++++++ gr-filter/examples/fir_filter_ccc.py | 101 ++++++++ gr-filter/examples/fir_filter_fff.py | 7 + gr-filter/include/filter/CMakeLists.txt | 2 + gr-filter/include/filter/fft_filter.h | 105 +++++++++ gr-filter/include/filter/fft_filter_ccc.h | 67 ++++++ gr-filter/include/filter/fft_filter_fff.h | 88 +++++++ gr-filter/include/filter/fir_filter.h | 7 +- gr-filter/lib/CMakeLists.txt | 2 + gr-filter/lib/fft_filter.cc | 174 ++++++++++++++ gr-filter/lib/fft_filter_ccc_impl.cc | 119 ++++++++++ gr-filter/lib/fft_filter_ccc_impl.h | 61 +++++ gr-filter/lib/fft_filter_fff_impl.cc | 123 ++++++++++ gr-filter/lib/fft_filter_fff_impl.h | 88 +++++++ gr-filter/lib/fir_filter.cc | 14 +- gr-filter/lib/fir_filter_XXX_impl.cc.t | 8 +- gr-filter/lib/fir_filter_XXX_impl.h.t | 2 +- gr-filter/python/qa_fft_filter.py | 380 ++++++++++++++++++++++++++++++ gr-filter/python/qa_fir_filter.py | 22 +- gr-filter/swig/filter_swig.i | 3 + 20 files changed, 1463 insertions(+), 17 deletions(-) create mode 100755 gr-filter/examples/fft_filter_ccc.py create mode 100755 gr-filter/examples/fir_filter_ccc.py create mode 100644 gr-filter/include/filter/fft_filter.h create mode 100644 gr-filter/include/filter/fft_filter_ccc.h create mode 100644 gr-filter/include/filter/fft_filter_fff.h create mode 100644 gr-filter/lib/fft_filter.cc create mode 100644 gr-filter/lib/fft_filter_ccc_impl.cc create mode 100644 gr-filter/lib/fft_filter_ccc_impl.h create mode 100644 gr-filter/lib/fft_filter_fff_impl.cc create mode 100644 gr-filter/lib/fft_filter_fff_impl.h create mode 100755 gr-filter/python/qa_fft_filter.py diff --git a/gr-filter/examples/fft_filter_ccc.py b/gr-filter/examples/fft_filter_ccc.py new file mode 100755 index 000000000..0844c88ef --- /dev/null +++ b/gr-filter/examples/fft_filter_ccc.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +from gnuradio import gr, filter +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +class example_fft_filter_ccc(gr.top_block): + def __init__(self, N, fs, bw0, bw1, tw, atten, D): + gr.top_block.__init__(self) + + self._nsamps = N + self._fs = fs + self._bw0 = bw0 + self._bw1 = bw1 + self._tw = tw + self._at = atten + self._decim = D + taps = gr.firdes.complex_band_pass_2(1, self._fs, + self._bw0, self._bw1, + self._tw, self._at) + print "Num. Taps: ", len(taps) + + self.src = gr.noise_source_c(gr.GR_GAUSSIAN, 1) + self.head = gr.head(gr.sizeof_gr_complex, self._nsamps) + + self.filt0 = filter.fft_filter_ccc(self._decim, taps) + self.filt1 = gr.fft_filter_ccc(self._decim, taps) + #self.filt1 = filter.fft_filter_ccc(self._decim, taps) + + self.vsnk_src = gr.vector_sink_c() + self.vsnk_out = gr.vector_sink_c() + self.vsnk_gr = gr.vector_sink_c() + + self.connect(self.src, self.head, self.vsnk_src) + self.connect(self.head, self.filt0, self.vsnk_out) + self.connect(self.head, self.filt1, self.vsnk_gr) + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=10000, + help="Number of samples to process [default=%default]") + parser.add_option("-s", "--samplerate", type="eng_float", default=8000, + help="System sample rate [default=%default]") + parser.add_option("-S", "--start-pass", type="eng_float", default=1000, + help="Start of Passband [default=%default]") + parser.add_option("-E", "--end-pass", type="eng_float", default=2000, + help="End of Passband [default=%default]") + parser.add_option("-T", "--transition", type="eng_float", default=100, + help="Transition band [default=%default]") + parser.add_option("-A", "--attenuation", type="eng_float", default=80, + help="Stopband attenuation [default=%default]") + parser.add_option("-D", "--decimation", type="int", default=1, + help="Decmation factor [default=%default]") + (options, args) = parser.parse_args () + + put = example_fft_filter_ccc(options.nsamples, + options.samplerate, + options.start_pass, + options.end_pass, + options.transition, + options.attenuation, + options.decimation) + put.run() + + data_src = scipy.array(put.vsnk_src.data()) + data_snk = scipy.array(put.vsnk_out.data()) + data_gr = scipy.array(put.vsnk_gr.data()) + + # Plot the signals PSDs + nfft = 1024 + f1 = pylab.figure(1, figsize=(12,10)) + s1 = f1.add_subplot(1,1,1) + s1.psd(data_src, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + + + f2 = pylab.figure(2, figsize=(12,10)) + s2 = f2.add_subplot(1,1,1) + s2.plot(data_src) + s2.plot(data_snk.real, 'g') + s2.plot(data_gr.real, 'r--') + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/fir_filter_ccc.py b/gr-filter/examples/fir_filter_ccc.py new file mode 100755 index 000000000..57df868f7 --- /dev/null +++ b/gr-filter/examples/fir_filter_ccc.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +from gnuradio import gr, filter +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +try: + import scipy +except ImportError: + print "Error: could not import scipy (http://www.scipy.org/)" + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: could not import pylab (http://matplotlib.sourceforge.net/)" + sys.exit(1) + +class example_fir_filter_ccc(gr.top_block): + def __init__(self, N, fs, bw, tw, atten, D): + gr.top_block.__init__(self) + + self._nsamps = N + self._fs = fs + self._bw = bw + self._tw = tw + self._at = atten + self._decim = D + taps = gr.firdes.low_pass_2(1, self._fs, self._bw, self._tw, self._at) + print "Num. Taps: ", len(taps) + + self.src = gr.noise_source_c(gr.GR_GAUSSIAN, 1) + self.head = gr.head(gr.sizeof_gr_complex, self._nsamps) + + self.filt0 = filter.fir_filter_ccc(self._decim, taps) + self.filt1 = gr.fir_filter_ccc(self._decim, taps) + #self.filt1 = filter.fft_filter_ccc(self._decim, taps) + + self.vsnk_src = gr.vector_sink_c() + self.vsnk_out = gr.vector_sink_c() + self.vsnk_gr = gr.vector_sink_c() + + self.connect(self.src, self.head, self.vsnk_src) + self.connect(self.head, self.filt0, self.vsnk_out) + self.connect(self.head, self.filt1, self.vsnk_gr) + +def main(): + parser = OptionParser(option_class=eng_option, conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=10000, + help="Number of samples to process [default=%default]") + parser.add_option("-s", "--samplerate", type="eng_float", default=8000, + help="System sample rate [default=%default]") + parser.add_option("-B", "--bandwidth", type="eng_float", default=1000, + help="Filter bandwidth [default=%default]") + parser.add_option("-T", "--transition", type="eng_float", default=100, + help="Transition band [default=%default]") + parser.add_option("-A", "--attenuation", type="eng_float", default=80, + help="Stopband attenuation [default=%default]") + parser.add_option("-D", "--decimation", type="int", default=1, + help="Decmation factor [default=%default]") + (options, args) = parser.parse_args () + + put = example_fir_filter_ccc(options.nsamples, + options.samplerate, + options.bandwidth, + options.transition, + options.attenuation, + options.decimation) + put.run() + + data_src = scipy.array(put.vsnk_src.data()) + data_snk = scipy.array(put.vsnk_out.data()) + data_gr = scipy.array(put.vsnk_gr.data()) + + # Plot the signals PSDs + nfft = 1024 + f1 = pylab.figure(1, figsize=(12,10)) + s1 = f1.add_subplot(1,1,1) + s1.psd(data_src, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, + Fs=options.samplerate) + + + f2 = pylab.figure(2, figsize=(12,10)) + s2 = f2.add_subplot(1,1,1) + s2.plot(data_src) + s2.plot(data_snk.real, 'g') + s2.plot(data_gr.real, 'r--') + + pylab.show() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/fir_filter_fff.py b/gr-filter/examples/fir_filter_fff.py index 538aded44..1ed889fcf 100755 --- a/gr-filter/examples/fir_filter_fff.py +++ b/gr-filter/examples/fir_filter_fff.py @@ -83,6 +83,13 @@ def main(): s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, Fs=options.samplerate) + + f2 = pylab.figure(2, figsize=(12,10)) + s2 = f2.add_subplot(1,1,1) + s2.plot(data_src) + s2.plot(data_snk.real, 'g') + s2.plot(data_gr.real, 'r--') + pylab.show() if __name__ == "__main__": diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 4889312da..f96128805 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -76,7 +76,9 @@ add_custom_target(filter_generated_includes DEPENDS install(FILES api.h fir_filter.h + fft_filter.h ${generated_includes} + fft_filter_ccc.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "fft_devel" ) diff --git a/gr-filter/include/filter/fft_filter.h b/gr-filter/include/filter/fft_filter.h new file mode 100644 index 000000000..fccea595f --- /dev/null +++ b/gr-filter/include/filter/fft_filter.h @@ -0,0 +1,105 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_FFT_FILTER_H +#define INCLUDED_FILTER_FFT_FILTER_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + /*! + * \brief Fast FFT filter with gr_complex input, gr_complex output and gr_complex taps + * \ingroup filter_blk + */ + class FILTER_API fft_filter_ccc + { + private: + int d_ntaps; + int d_nsamples; + int d_fftsize; // fftsize = ntaps + nsamples - 1 + int d_decimation; + fft::fft_complex *d_fwdfft; // forward "plan" + fft::fft_complex *d_invfft; // inverse "plan" + int d_nthreads; // number of FFTW threads to use + std::vector d_tail; // state carried between blocks for overlap-add + std::vector d_new_taps; + gr_complex *d_xformed_taps; // Fourier xformed taps + + void compute_sizes(int ntaps); + int tailsize() const { return d_ntaps - 1; } + + public: + /*! + * \brief Construct an FFT filter for complex vectors with the given taps and decimation rate. + * + * This is the basic implementation for performing FFT filter for fast convolution + * in other blocks for complex vectors (such as gr_fft_filter_ccc). + * + * \param decimation The decimation rate of the filter (int) + * \param taps The filter taps (complex) + * \param nthreads The number of threads for the FFT to use (int) + */ + fft_filter_ccc(int decimation, + const std::vector &taps, + int nthreads=1); + + ~fft_filter_ccc(); + + /*! + * \brief Set new taps for the filter. + * + * Sets new taps and resets the class properties to handle different sizes + * \param taps The filter taps (complex) + */ + int set_taps(const std::vector &taps); + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + /*! + * \brief Perform the filter operation + * + * \param nitems The number of items to produce + * \param input The input vector to be filtered + * \param output The result of the filter operation + */ + int filter(int nitems, const gr_complex *input, gr_complex *output); + }; + + } /* namespace impl */ + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_H */ diff --git a/gr-filter/include/filter/fft_filter_ccc.h b/gr-filter/include/filter/fft_filter_ccc.h new file mode 100644 index 000000000..a309ffc22 --- /dev/null +++ b/gr-filter/include/filter/fft_filter_ccc.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_FFT_FILTER_CCC_H +#define INCLUDED_FILTER_FFT_FILTER_CCC_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fft_filter_ccc : virtual public gr_sync_decimator + { + public: + // gr::filter::fft_filter::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief Fast FFT filter with gr_complex input, gr_complex output and gr_complex taps + * \ingroup filter_blk + * + * \param decimation >= 1 + * \param taps complex filter taps + * \param nthreads number of threads for the FFT to use + */ + static FILTER_API sptr make(int decimation, + const std::vector &taps, + int nthreads=1); + + virtual void set_taps(const std::vector &taps) = 0; + virtual std::vector taps() const = 0; + + /*! + * \brief Set number of threads to use. + */ + virtual void set_nthreads(int n) = 0; + + /*! + * \brief Get number of threads being used. + */ + virtual int nthreads() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_CCC_H */ diff --git a/gr-filter/include/filter/fft_filter_fff.h b/gr-filter/include/filter/fft_filter_fff.h new file mode 100644 index 000000000..309a55135 --- /dev/null +++ b/gr-filter/include/filter/fft_filter_fff.h @@ -0,0 +1,88 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_FFT_FILTER_FFF_H +#define INCLUDED_GR_FFT_FILTER_FFF_H + +#include +#include + +class gr_fft_filter_fff; +typedef boost::shared_ptr gr_fft_filter_fff_sptr; +GR_CORE_API gr_fft_filter_fff_sptr +gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + +class gri_fft_filter_fff_generic; +//class gri_fft_filter_fff_sse; + +/*! + * \brief Fast FFT filter with float input, float output and float taps + * \ingroup filter_blk + */ +class GR_CORE_API gr_fft_filter_fff : public gr_sync_decimator +{ + private: + friend GR_CORE_API gr_fft_filter_fff_sptr + gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads); + + int d_nsamples; + bool d_updated; +#if 1 // don't enable the sse version until handling it is worked out + gri_fft_filter_fff_generic *d_filter; +#else + gri_fft_filter_fff_sse *d_filter; +#endif + std::vector d_new_taps; + + /*! + * Construct a FFT filter with the given taps + * + * \param decimation >= 1 + * \param taps float filter taps + * \param nthreads number of threads for the FFT to use + */ + gr_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + + public: + ~gr_fft_filter_fff (); + + void set_taps (const std::vector &taps); + std::vector taps () const; + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GR_FFT_FILTER_FFF_H */ diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index c307fea46..525532fe6 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -20,13 +20,16 @@ * Boston, MA 02110-1301, USA. */ +#ifndef INCLUDED_FILTER_FIR_FILTER_H +#define INCLUDED_FILTER_FIR_FILTER_H + #include #include #include namespace gr { namespace filter { - namespace impl { + namespace kernel { class FILTER_API fir_filter_fff { @@ -110,3 +113,5 @@ namespace gr { } /* namespace impl */ } /* namespace filter */ } /* namespace gr */ + +#endif /* INCLUDED_FILTER_FIR_FILTER_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 784a3c109..aa03c4615 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -106,7 +106,9 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) ######################################################################## list(APPEND filter_sources fir_filter.cc + fft_filter.cc ${generated_sources} + fft_filter_ccc_impl.cc ) list(APPEND filter_libs diff --git a/gr-filter/lib/fft_filter.cc b/gr-filter/lib/fft_filter.cc new file mode 100644 index 000000000..86b2a2fdb --- /dev/null +++ b/gr-filter/lib/fft_filter.cc @@ -0,0 +1,174 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + #define VERBOSE 0 + + fft_filter_ccc::fft_filter_ccc(int decimation, + const std::vector &taps, + int nthreads) + : d_fftsize(-1), d_decimation(decimation), d_fwdfft(0), + d_invfft(0), d_nthreads(nthreads) + { + set_taps(taps); + } + + fft_filter_ccc::~fft_filter_ccc() + { + delete d_fwdfft; + delete d_invfft; + fft::free(d_xformed_taps); + } + + /* + * determines d_ntaps, d_nsamples, d_fftsize, d_xformed_taps + */ + int + fft_filter_ccc::set_taps(const std::vector &taps) + { + int i = 0; + compute_sizes(taps.size()); + + d_tail.resize(tailsize()); + for(i = 0; i < tailsize(); i++) + d_tail[i] = 0; + + gr_complex *in = d_fwdfft->get_inbuf(); + gr_complex *out = d_fwdfft->get_outbuf(); + + float scale = 1.0 / d_fftsize; + + // Compute forward xform of taps. + // Copy taps into first ntaps slots, then pad with zeros + for(i = 0; i < d_ntaps; i++) + in[i] = taps[i] * scale; + + for(; i < d_fftsize; i++) + in[i] = 0; + + d_fwdfft->execute(); // do the xform + + // now copy output to d_xformed_taps + for(i = 0; i < d_fftsize; i++) + d_xformed_taps[i] = out[i]; + + return d_nsamples; + } + + // determine and set d_ntaps, d_nsamples, d_fftsize + void + fft_filter_ccc::compute_sizes(int ntaps) + { + int old_fftsize = d_fftsize; + d_ntaps = ntaps; + d_fftsize = (int) (2 * pow(2.0, ceil(log(double(ntaps)) / log(2.0)))); + d_nsamples = d_fftsize - d_ntaps + 1; + + if(VERBOSE) { + std::cerr << "fft_filter_ccc: ntaps = " << d_ntaps + << " fftsize = " << d_fftsize + << " nsamples = " << d_nsamples << std::endl; + } + + // compute new plans + if(d_fftsize != old_fftsize) { + delete d_fwdfft; + delete d_invfft; + d_fwdfft = new fft::fft_complex(d_fftsize, true, d_nthreads); + d_invfft = new fft::fft_complex(d_fftsize, false, d_nthreads); + d_xformed_taps = fft::malloc_complex(d_fftsize); + } + } + + void + fft_filter_ccc::set_nthreads(int n) + { + d_nthreads = n; + if(d_fwdfft) + d_fwdfft->set_nthreads(n); + if(d_invfft) + d_invfft->set_nthreads(n); + } + + int + fft_filter_ccc::nthreads() const + { + return d_nthreads; + } + + int + fft_filter_ccc::filter (int nitems, const gr_complex *input, gr_complex *output) + { + int dec_ctr = 0; + int j = 0; + int ninput_items = nitems * d_decimation; + + for(int i = 0; i < ninput_items; i += d_nsamples) { + memcpy(d_fwdfft->get_inbuf(), &input[i], d_nsamples * sizeof(gr_complex)); + + for(j = d_nsamples; j < d_fftsize; j++) + d_fwdfft->get_inbuf()[j] = 0; + + d_fwdfft->execute(); // compute fwd xform + + gr_complex *a = d_fwdfft->get_outbuf(); + gr_complex *b = d_xformed_taps; + gr_complex *c = d_invfft->get_inbuf(); + + volk_32fc_x2_multiply_32fc_a(c, a, b, d_fftsize); + + d_invfft->execute(); // compute inv xform + + // add in the overlapping tail + + for(j = 0; j < tailsize(); j++) + d_invfft->get_outbuf()[j] += d_tail[j]; + + // copy nsamples to output + j = dec_ctr; + while(j < d_nsamples) { + *output++ = d_invfft->get_outbuf()[j]; + j += d_decimation; + } + dec_ctr = (j - d_nsamples); + + // stash the tail + memcpy(&d_tail[0], d_invfft->get_outbuf() + d_nsamples, + tailsize() * sizeof(gr_complex)); + } + + return nitems; + } + } /* namespace impl */ + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_ccc_impl.cc b/gr-filter/lib/fft_filter_ccc_impl.cc new file mode 100644 index 000000000..2721f128e --- /dev/null +++ b/gr-filter/lib/fft_filter_ccc_impl.cc @@ -0,0 +1,119 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fft_filter_ccc_impl.h" +#include + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + fft_filter_ccc::sptr fft_filter_ccc::make(int decimation, + const std::vector &taps, + int nthreads) + { + return gnuradio::get_initial_sptr(new fft_filter_ccc_impl + (decimation, taps, nthreads)); + } + + fft_filter_ccc_impl::fft_filter_ccc_impl(int decimation, + const std::vector &taps, + int nthreads) + : gr_sync_decimator("fft_filter_ccc", + gr_make_io_signature (1, 1, sizeof(gr_complex)), + gr_make_io_signature (1, 1, sizeof(gr_complex)), + decimation), + d_updated(false) + { + set_history(1); + + d_filter = new kernel::fft_filter_ccc(decimation, taps, nthreads); + + d_new_taps = taps; + d_nsamples = d_filter->set_taps(taps); + set_output_multiple(d_nsamples); + } + + fft_filter_ccc_impl::~fft_filter_ccc_impl() + { + delete d_filter; + } + + void + fft_filter_ccc_impl::set_taps(const std::vector &taps) + { + d_new_taps = taps; + d_updated = true; + } + + std::vector + fft_filter_ccc_impl::taps() const + { + return d_new_taps; + } + + void + fft_filter_ccc_impl::set_nthreads(int n) + { + if(d_filter) + d_filter->set_nthreads(n); + } + + int + fft_filter_ccc_impl::nthreads() const + { + if(d_filter) + return d_filter->nthreads(); + else + return 0; + } + + int + fft_filter_ccc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + if (d_updated){ + d_nsamples = d_filter->set_taps(d_new_taps); + d_updated = false; + set_output_multiple(d_nsamples); + return 0; // output multiple may have changed + } + + d_filter->filter(noutput_items, in, out); + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_ccc_impl.h b/gr-filter/lib/fft_filter_ccc_impl.h new file mode 100644 index 000000000..2d8d61c5e --- /dev/null +++ b/gr-filter/lib/fft_filter_ccc_impl.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_FILTER_FFT_FILTER_CCC_IMPL_H +#define INCLUDED_FILTER_FFT_FILTER_CCC_IMPL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fft_filter_ccc_impl : public fft_filter_ccc + { + private: + int d_nsamples; + bool d_updated; + kernel::fft_filter_ccc *d_filter; + std::vector d_new_taps; + + public: + fft_filter_ccc_impl(int decimation, + const std::vector &taps, + int nthreads=1); + + ~fft_filter_ccc_impl(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + + void set_nthreads(int n); + int nthreads() const; + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_CCC_IMPL_H */ diff --git a/gr-filter/lib/fft_filter_fff_impl.cc b/gr-filter/lib/fft_filter_fff_impl.cc new file mode 100644 index 000000000..a09feb7f1 --- /dev/null +++ b/gr-filter/lib/fft_filter_fff_impl.cc @@ -0,0 +1,123 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +gr_fft_filter_fff_sptr gr_make_fft_filter_fff (int decimation, + const std::vector &taps, + int nthreads) +{ + return gnuradio::get_initial_sptr(new gr_fft_filter_fff (decimation, taps, nthreads)); +} + + +gr_fft_filter_fff::gr_fft_filter_fff (int decimation, + const std::vector &taps, + int nthreads) + : gr_sync_decimator ("fft_filter_fff", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (float)), + decimation), + d_updated(false) +{ + set_history(1); + +#if 1 // don't enable the sse version until handling it is worked out + d_filter = new gri_fft_filter_fff_generic(decimation, taps, nthreads); +#else + d_filter = new gri_fft_filter_fff_sse(decimation, taps); +#endif + + d_new_taps = taps; + d_nsamples = d_filter->set_taps(taps); + set_output_multiple(d_nsamples); +} + +gr_fft_filter_fff::~gr_fft_filter_fff () +{ + delete d_filter; +} + +void +gr_fft_filter_fff::set_taps (const std::vector &taps) +{ + d_new_taps = taps; + d_updated = true; +} + +std::vector +gr_fft_filter_fff::taps () const +{ + return d_new_taps; +} + +void +gr_fft_filter_fff::set_nthreads(int n) +{ + if(d_filter) + d_filter->set_nthreads(n); +} + +int +gr_fft_filter_fff::nthreads() const +{ + if(d_filter) + return d_filter->nthreads(); + else + return 0; +} + +int +gr_fft_filter_fff::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + float *out = (float *) output_items[0]; + + if (d_updated){ + d_nsamples = d_filter->set_taps(d_new_taps); + d_updated = false; + set_output_multiple(d_nsamples); + return 0; // output multiple may have changed + } + + assert(noutput_items % d_nsamples == 0); + + d_filter->filter(noutput_items, in, out); + + //assert((out - (float *) output_items[0]) == noutput_items); + + return noutput_items; +} diff --git a/gr-filter/lib/fft_filter_fff_impl.h b/gr-filter/lib/fft_filter_fff_impl.h new file mode 100644 index 000000000..309a55135 --- /dev/null +++ b/gr-filter/lib/fft_filter_fff_impl.h @@ -0,0 +1,88 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_GR_FFT_FILTER_FFF_H +#define INCLUDED_GR_FFT_FILTER_FFF_H + +#include +#include + +class gr_fft_filter_fff; +typedef boost::shared_ptr gr_fft_filter_fff_sptr; +GR_CORE_API gr_fft_filter_fff_sptr +gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + +class gri_fft_filter_fff_generic; +//class gri_fft_filter_fff_sse; + +/*! + * \brief Fast FFT filter with float input, float output and float taps + * \ingroup filter_blk + */ +class GR_CORE_API gr_fft_filter_fff : public gr_sync_decimator +{ + private: + friend GR_CORE_API gr_fft_filter_fff_sptr + gr_make_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads); + + int d_nsamples; + bool d_updated; +#if 1 // don't enable the sse version until handling it is worked out + gri_fft_filter_fff_generic *d_filter; +#else + gri_fft_filter_fff_sse *d_filter; +#endif + std::vector d_new_taps; + + /*! + * Construct a FFT filter with the given taps + * + * \param decimation >= 1 + * \param taps float filter taps + * \param nthreads number of threads for the FFT to use + */ + gr_fft_filter_fff (int decimation, const std::vector &taps, + int nthreads=1); + + public: + ~gr_fft_filter_fff (); + + void set_taps (const std::vector &taps); + std::vector taps () const; + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GR_FFT_FILTER_FFF_H */ diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 5f0e0912d..321f6981e 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -26,7 +26,7 @@ namespace gr { namespace filter { - namespace impl { + namespace kernel { fir_filter_fff::fir_filter_fff(int decimation, const std::vector &taps) @@ -55,7 +55,7 @@ namespace gr { d_ntaps = (int)taps.size(); d_taps = fft::malloc_float(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[i] = taps[i]; + d_taps[d_ntaps-i-1] = taps[i]; } } @@ -64,7 +64,7 @@ namespace gr { { std::vector t; for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[i]); + t.push_back(d_taps[d_ntaps-i-1]); return t; } @@ -134,7 +134,7 @@ namespace gr { d_ntaps = (int)taps.size(); d_taps = fft::malloc_complex(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[i] = gr_complex(taps[i],0); + d_taps[d_ntaps-i-1] = gr_complex(taps[i],0); } } @@ -143,7 +143,7 @@ namespace gr { { std::vector t; for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[i].real()); + t.push_back(d_taps[d_ntaps-i-1].real()); return t; } @@ -213,7 +213,7 @@ namespace gr { d_ntaps = (int)taps.size(); d_taps = fft::malloc_complex(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[i] = taps[i]; + d_taps[d_ntaps-i-1] = taps[i]; } } @@ -222,7 +222,7 @@ namespace gr { { std::vector t; for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[i]); + t.push_back(d_taps[d_ntaps-i-1]); return t; } diff --git a/gr-filter/lib/fir_filter_XXX_impl.cc.t b/gr-filter/lib/fir_filter_XXX_impl.cc.t index 70c3fba3f..c3637042d 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.cc.t +++ b/gr-filter/lib/fir_filter_XXX_impl.cc.t @@ -26,8 +26,6 @@ #include "@IMPL_NAME@.h" #include -#include -#include namespace gr { namespace filter { @@ -46,9 +44,9 @@ namespace gr { gr_make_io_signature(1, 1, sizeof(@O_TYPE@)), decimation) { - d_fir = new impl::@BASE_NAME@(decimation, taps); + d_fir = new kernel::@BASE_NAME@(decimation, taps); d_updated = false; - set_history(d_fir->ntaps()+1); + set_history(d_fir->ntaps()); } @IMPL_NAME@::~@IMPL_NAME@() @@ -78,7 +76,7 @@ namespace gr { @O_TYPE@ *out = (@O_TYPE@*)output_items[0]; if (d_updated) { - set_history(d_fir->ntaps()+1); + set_history(d_fir->ntaps()); d_updated = false; return 0; // history requirements may have changed. } diff --git a/gr-filter/lib/fir_filter_XXX_impl.h.t b/gr-filter/lib/fir_filter_XXX_impl.h.t index a40c49bf2..d5bf7104d 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.h.t +++ b/gr-filter/lib/fir_filter_XXX_impl.h.t @@ -35,7 +35,7 @@ namespace gr { class FILTER_API @IMPL_NAME@ : public @BASE_NAME@ { private: - impl::@BASE_NAME@ *d_fir; + kernel::@BASE_NAME@ *d_fir; bool d_updated; public: diff --git a/gr-filter/python/qa_fft_filter.py b/gr-filter/python/qa_fft_filter.py new file mode 100755 index 000000000..33d3d870b --- /dev/null +++ b/gr-filter/python/qa_fft_filter.py @@ -0,0 +1,380 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005,2007,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import sys +import random + +def make_random_complex_tuple(L): + result = [] + for x in range(L): + result.append(complex(random.uniform(-1000,1000), + random.uniform(-1000,1000))) + return tuple(result) + +def make_random_float_tuple(L): + result = [] + for x in range(L): + result.append(float(int(random.uniform(-1000,1000)))) + return tuple(result) + + +def reference_filter_ccc(dec, taps, input): + """ + compute result using conventional fir filter + """ + tb = gr.top_block() + #src = gr.vector_source_c(((0,) * (len(taps) - 1)) + input) + src = gr.vector_source_c(input) + op = filter.fir_filter_ccc(dec, taps) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + return dst.data() + +def reference_filter_fff(dec, taps, input): + """ + compute result using conventional fir filter + """ + tb = gr.top_block() + #src = gr.vector_source_f(((0,) * (len(taps) - 1)) + input) + src = gr.vector_source_f(input) + op = filter.fir_filter_fff(dec, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + return dst.data() + + +def print_complex(x): + for i in x: + i = complex(i) + sys.stdout.write("(%6.3f,%6.3fj), " % (i.real, i.imag)) + sys.stdout.write('\n') + + +class test_fft_filter(gr_unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def assert_fft_ok2(self, expected_result, result_data): + expected_result = expected_result[:len(result_data)] + self.assertComplexTuplesAlmostEqual2 (expected_result, result_data, + abs_eps=1e-9, rel_eps=4e-4) + + def assert_fft_float_ok2(self, expected_result, result_data, abs_eps=1e-9, rel_eps=4e-4): + expected_result = expected_result[:len(result_data)] + self.assertFloatTuplesAlmostEqual2 (expected_result, result_data, + abs_eps, rel_eps) + + def test_ccc_001(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (1,) + expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_ccc_002(self): + # Test nthreads + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + nthreads = 2 + expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps, nthreads) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + def test_ccc_003(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_ccc_004(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(1, taps, src_data) + + src = gr.vector_source_c(src_data) + op = filter.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + del tb + self.assert_fft_ok2(expected_result, result_data) + + def test_ccc_005(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(dec, taps, src_data) + + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(dec, taps) + dst = gr.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + del tb + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + def test_ccc_006(self): + # Test decimating with nthreads=2 + random.seed(0) + nthreads = 2 + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(dec, taps, src_data) + + src = gr.vector_source_c(src_data) + op = filter.fft_filter_ccc(dec, taps, nthreads) + dst = gr.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + del tb + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + # ---------------------------------------------------------------- + # test _fff version + # ---------------------------------------------------------------- + + def test_fff_001(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (1,) + expected_result = tuple([float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_fff_002(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + def test_fff_003(self): + # Test 02 with nthreads + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + nthreads = 2 + expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps, nthreads) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + def xtest_fff_004(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4096 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(1, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + #print "src_len =", src_len, " ntaps =", ntaps + try: + self.assert_fft_float_ok2(expected_result, result_data, abs_eps=1.0) + except: + expected = open('expected', 'w') + for x in expected_result: + expected.write(`x` + '\n') + actual = open('actual', 'w') + for x in result_data: + actual.write(`x` + '\n') + raise + + def xtest_fff_005(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(1, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data, abs_eps=2.0) + + def xtest_fff_006(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(dec, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(dec, taps) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data) + + def xtest_fff_007(self): + # test decimation with nthreads + random.seed(0) + nthreads = 2 + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(dec, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(dec, taps, nthreads) + dst = gr.vector_sink_f() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data) + + def test_fff_get0(self): + random.seed(0) + for i in xrange(25): + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + + op = gr.fft_filter_fff(1, taps) + result_data = op.taps() + #print result_data + + self.assertEqual(taps, result_data) + + def test_ccc_get0(self): + random.seed(0) + for i in xrange(25): + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + + op = gr.fft_filter_ccc(1, taps) + result_data = op.taps() + #print result_data + + self.assertComplexTuplesAlmostEqual(taps, result_data, 4) + + +if __name__ == '__main__': + gr_unittest.run(test_fft_filter, "test_fft_filter.xml") + diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py index 2c88e7830..f0f08afca 100755 --- a/gr-filter/python/qa_fir_filter.py +++ b/gr-filter/python/qa_fir_filter.py @@ -32,7 +32,7 @@ class test_filter(gr_unittest.TestCase): def test_fir_filter_fff_001(self): src_data = [1, 2, 3, 4] - expected_data = [0, 0.5, 1.5, 2.5] + expected_data = [0.5, 1.5, 2.5, 3.5] src = gr.vector_source_f(src_data) op = filter.fir_filter_fff(1, [0.5, 0.5]) dst = gr.vector_sink_f() @@ -43,7 +43,7 @@ class test_filter(gr_unittest.TestCase): def test_fir_filter_ccf_001(self): src_data = [1+1j, 2+2j, 3+3j, 4+4j] - expected_data = [0+0j, 0.5+0.5j, 1.5+1.5j, 2.5+2.5j] + expected_data = [0.5+0.5j, 1.5+1.5j, 2.5+2.5j, 3.5+3.5j] src = gr.vector_source_c(src_data) op = filter.fir_filter_ccf(1, [0.5, 0.5]) dst = gr.vector_sink_c() @@ -54,7 +54,7 @@ class test_filter(gr_unittest.TestCase): def test_fir_filter_ccc_001(self): src_data = [1+1j, 2+2j, 3+3j, 4+4j] - expected_data = [0+0j, -0.5+1.5j, -1.5+4.5j, -2.5+7.5j] + expected_data = [-0.5+1.5j, -1.5+4.5j, -2.5+7.5j, -3.5+10.5j] src = gr.vector_source_c(src_data) op = filter.fir_filter_ccc(1, [0.5+1j, 0.5+1j]) dst = gr.vector_sink_c() @@ -63,6 +63,22 @@ class test_filter(gr_unittest.TestCase): result_data = dst.data() self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + def test_fir_filter_ccc_002(self): + src_data = 10*[1+1j, 2+2j, 3+3j, 4+4j] + + # results derived from original gr.fir_filter_ccc + expected_data = ((7.537424837948042e-20+7.537424837948042e-20j), (9.131923434324563e-05+9.131923434324563e-05j), (0.0003317668742965907+0.0003317668742965907j), (0.0007230418268591166+0.0007230418268591166j), (0.0012087896466255188+0.0012087896466255188j), (0.0013292605290189385+0.0013292605290189385j), (0.001120875240303576+0.001120875240303576j), (0.000744672492146492+0.000744672492146492j), (0.000429437990533188+0.000429437990533188j), (2.283908543176949e-05+2.283908543176949e-05j), (-0.0002245186478830874-0.0002245186478830874j), (-0.0001157080550910905-0.0001157080550910905j), (0.00041409023106098175+0.00041409023106098175j), (0.0009017843985930085+0.0009017843985930085j), (0.0012520025484263897+0.0012520025484263897j), (0.0014116164529696107+0.0014116164529696107j), (0.001393353333696723+0.001393353333696723j), (0.000912194955162704+0.000912194955162704j), (0.00022649182938039303+0.00022649182938039303j), (-0.00031363096786662936-0.00031363096786662936j), (-0.0003966730728279799-0.0003966730728279799j), (-0.00023757052258588374-0.00023757052258588374j), (0.00021952332463115454+0.00021952332463115454j), (0.0009092430118471384+0.0009092430118471384j), (0.001662317430600524+0.001662317430600524j), (0.0019024648936465383+0.0019024648936465383j), (0.0015955769922584295+0.0015955769922584295j), (0.0009144138311967254+0.0009144138311967254j), (0.0001872836146503687+0.0001872836146503687j), (-0.000581968342885375-0.000581968342885375j), (-0.0009886166080832481-0.0009886166080832481j), (-0.0007480768254026771-0.0007480768254026771j), (0.00018211957649327815+0.00018211957649327815j), (0.0012042406015098095+0.0012042406015098095j), (0.0020200139842927456+0.0020200139842927456j), (0.0023816542234271765+0.0023816542234271765j), (0.002195809967815876+0.002195809967815876j), (0.0012113333214074373+0.0012113333214074373j), (-0.00014088614261709154-0.00014088614261709154j), (-0.0012574587017297745-0.0012574587017297745j)) + + taps = gr.firdes.low_pass(1, 1, 0.1, 0.01) + src = gr.vector_source_c(src_data) + op = filter.fir_filter_ccc(1, taps) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + if __name__ == '__main__': gr_unittest.run(test_filter, "test_filter.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 28268528a..94ad9cd64 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -31,12 +31,15 @@ #include "filter/fir_filter_fff.h" #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" +#include "filter/fft_filter_ccc.h" %} %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" +%include "filter/fft_filter_ccc.h" GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); -- cgit From b63e3b8a6923cf358d42064dca16d262a33c2918 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 19:59:43 -0400 Subject: filter: adding fff version of FFT filter. --- gr-filter/include/filter/fft_filter.h | 70 ++++++++++- gr-filter/include/filter/fft_filter_ccc.h | 2 +- gr-filter/include/filter/fft_filter_fff.h | 95 ++++++--------- gr-filter/include/filter/fir_filter.h | 2 +- gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/fft_filter.cc | 145 ++++++++++++++++++++++- gr-filter/lib/fft_filter_ccc_impl.h | 10 +- gr-filter/lib/fft_filter_fff_impl.cc | 187 +++++++++++++++--------------- gr-filter/lib/fft_filter_fff_impl.h | 109 +++++++---------- gr-filter/lib/fir_filter.cc | 2 +- gr-filter/python/qa_fft_filter.py | 18 +-- gr-filter/swig/filter_swig.i | 3 + 12 files changed, 402 insertions(+), 242 deletions(-) diff --git a/gr-filter/include/filter/fft_filter.h b/gr-filter/include/filter/fft_filter.h index fccea595f..8c7d6cf78 100644 --- a/gr-filter/include/filter/fft_filter.h +++ b/gr-filter/include/filter/fft_filter.h @@ -31,6 +31,72 @@ namespace gr { namespace filter { namespace kernel { + /*! + * \brief Fast FFT filter with float input, float output and float taps + * \ingroup filter_blk + */ + class FILTER_API fft_filter_fff + { + private: + int d_ntaps; + int d_nsamples; + int d_fftsize; // fftsize = ntaps + nsamples - 1 + int d_decimation; + fft::fft_real_fwd *d_fwdfft; // forward "plan" + fft::fft_real_rev *d_invfft; // inverse "plan" + int d_nthreads; // number of FFTW threads to use + std::vector d_tail; // state carried between blocks for overlap-add + std::vector d_new_taps; + gr_complex *d_xformed_taps; // Fourier xformed taps + + void compute_sizes(int ntaps); + int tailsize() const { return d_ntaps - 1; } + + public: + /*! + * \brief Construct an FFT filter for float vectors with the given taps and decimation rate. + * + * This is the basic implementation for performing FFT filter for fast convolution + * in other blocks for complex vectors (such as fft_filter_ccc). + * + * \param decimation The decimation rate of the filter (int) + * \param taps The filter taps (complex) + * \param nthreads The number of threads for the FFT to use (int) + */ + fft_filter_fff(int decimation, + const std::vector &taps, + int nthreads=1); + + ~fft_filter_fff(); + + /*! + * \brief Set new taps for the filter. + * + * Sets new taps and resets the class properties to handle different sizes + * \param taps The filter taps (complex) + */ + int set_taps(const std::vector &taps); + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + /*! + * \brief Perform the filter operation + * + * \param nitems The number of items to produce + * \param input The input vector to be filtered + * \param output The result of the filter operation + */ + int filter(int nitems, const float *input, float *output); + }; + /*! * \brief Fast FFT filter with gr_complex input, gr_complex output and gr_complex taps @@ -58,7 +124,7 @@ namespace gr { * \brief Construct an FFT filter for complex vectors with the given taps and decimation rate. * * This is the basic implementation for performing FFT filter for fast convolution - * in other blocks for complex vectors (such as gr_fft_filter_ccc). + * in other blocks for complex vectors (such as fft_filter_ccc). * * \param decimation The decimation rate of the filter (int) * \param taps The filter taps (complex) @@ -98,7 +164,7 @@ namespace gr { int filter(int nitems, const gr_complex *input, gr_complex *output); }; - } /* namespace impl */ + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/include/filter/fft_filter_ccc.h b/gr-filter/include/filter/fft_filter_ccc.h index a309ffc22..29fd54713 100644 --- a/gr-filter/include/filter/fft_filter_ccc.h +++ b/gr-filter/include/filter/fft_filter_ccc.h @@ -32,7 +32,7 @@ namespace gr { class FILTER_API fft_filter_ccc : virtual public gr_sync_decimator { public: - // gr::filter::fft_filter::sptr + // gr::filter::fft_filter_ccc::sptr typedef boost::shared_ptr sptr; /*! diff --git a/gr-filter/include/filter/fft_filter_fff.h b/gr-filter/include/filter/fft_filter_fff.h index 309a55135..8eb0a1c83 100644 --- a/gr-filter/include/filter/fft_filter_fff.h +++ b/gr-filter/include/filter/fft_filter_fff.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2005 Free Software Foundation, Inc. + * Copyright 2005,2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -19,70 +19,49 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ -#ifndef INCLUDED_GR_FFT_FILTER_FFF_H -#define INCLUDED_GR_FFT_FILTER_FFF_H -#include -#include - -class gr_fft_filter_fff; -typedef boost::shared_ptr gr_fft_filter_fff_sptr; -GR_CORE_API gr_fft_filter_fff_sptr -gr_make_fft_filter_fff (int decimation, const std::vector &taps, - int nthreads=1); - -class gri_fft_filter_fff_generic; -//class gri_fft_filter_fff_sse; +#ifndef INCLUDED_FILTER_FFT_FILTER_FFF_H +#define INCLUDED_FILTER_FFT_FILTER_FFF_H -/*! - * \brief Fast FFT filter with float input, float output and float taps - * \ingroup filter_blk - */ -class GR_CORE_API gr_fft_filter_fff : public gr_sync_decimator -{ - private: - friend GR_CORE_API gr_fft_filter_fff_sptr - gr_make_fft_filter_fff (int decimation, const std::vector &taps, - int nthreads); +#include +#include - int d_nsamples; - bool d_updated; -#if 1 // don't enable the sse version until handling it is worked out - gri_fft_filter_fff_generic *d_filter; -#else - gri_fft_filter_fff_sse *d_filter; -#endif - std::vector d_new_taps; +namespace gr { + namespace filter { - /*! - * Construct a FFT filter with the given taps - * - * \param decimation >= 1 - * \param taps float filter taps - * \param nthreads number of threads for the FFT to use - */ - gr_fft_filter_fff (int decimation, const std::vector &taps, - int nthreads=1); + class FILTER_API fft_filter_fff : virtual public gr_sync_decimator + { + public: + // gr::filter::fft_filter_fff::sptr + typedef boost::shared_ptr sptr; - public: - ~gr_fft_filter_fff (); + /*! + * \brief Fast FFT filter with float input, float output and float taps + * \ingroup filter_blk + * + * \param decimation >= 1 + * \param taps float filter taps + * \param nthreads number of threads for the FFT to use + */ + static FILTER_API sptr make(int decimation, + const std::vector &taps, + int nthreads=1); - void set_taps (const std::vector &taps); - std::vector taps () const; + virtual void set_taps(const std::vector &taps) = 0; + virtual std::vector taps() const = 0; - /*! - * \brief Set number of threads to use. - */ - void set_nthreads(int n); + /*! + * \brief Set number of threads to use. + */ + virtual void set_nthreads(int n) = 0; - /*! - * \brief Get number of threads being used. - */ - int nthreads() const; + /*! + * \brief Get number of threads being used. + */ + virtual int nthreads() const = 0; + }; - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; + } /* namespace filter */ +} /* namespace gr */ -#endif /* INCLUDED_GR_FFT_FILTER_FFF_H */ +#endif /* INCLUDED_FILTER_FFT_FILTER_FFF_H */ diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 525532fe6..76b00cae9 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -110,7 +110,7 @@ namespace gr { gr_complex *d_taps; }; - } /* namespace impl */ + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index aa03c4615..e0af76700 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -109,6 +109,7 @@ list(APPEND filter_sources fft_filter.cc ${generated_sources} fft_filter_ccc_impl.cc + fft_filter_fff_impl.cc ) list(APPEND filter_libs diff --git a/gr-filter/lib/fft_filter.cc b/gr-filter/lib/fft_filter.cc index 86b2a2fdb..130886d7f 100644 --- a/gr-filter/lib/fft_filter.cc +++ b/gr-filter/lib/fft_filter.cc @@ -33,6 +33,146 @@ namespace gr { namespace kernel { #define VERBOSE 0 + + fft_filter_fff::fft_filter_fff(int decimation, + const std::vector &taps, + int nthreads) + : d_fftsize(-1), d_decimation(decimation), d_fwdfft(0), + d_invfft(0), d_nthreads(nthreads) + { + set_taps(taps); + } + + fft_filter_fff::~fft_filter_fff() + { + delete d_fwdfft; + delete d_invfft; + fft::free(d_xformed_taps); + } + + /* + * determines d_ntaps, d_nsamples, d_fftsize, d_xformed_taps + */ + int + fft_filter_fff::set_taps(const std::vector &taps) + { + int i = 0; + compute_sizes(taps.size()); + + d_tail.resize(tailsize()); + for(i = 0; i < tailsize(); i++) + d_tail[i] = 0; + + float *in = d_fwdfft->get_inbuf(); + gr_complex *out = d_fwdfft->get_outbuf(); + + float scale = 1.0 / d_fftsize; + + // Compute forward xform of taps. + // Copy taps into first ntaps slots, then pad with zeros + for (i = 0; i < d_ntaps; i++) + in[i] = taps[i] * scale; + + for (; i < d_fftsize; i++) + in[i] = 0; + + d_fwdfft->execute(); // do the xform + + // now copy output to d_xformed_taps + for (i = 0; i < d_fftsize/2+1; i++) + d_xformed_taps[i] = out[i]; + + return d_nsamples; + } + + // determine and set d_ntaps, d_nsamples, d_fftsize + void + fft_filter_fff::compute_sizes(int ntaps) + { + int old_fftsize = d_fftsize; + d_ntaps = ntaps; + d_fftsize = (int) (2 * pow(2.0, ceil(log(double(ntaps)) / log(2.0)))); + d_nsamples = d_fftsize - d_ntaps + 1; + + if(VERBOSE) { + std::cerr << "fft_filter_fff: ntaps = " << d_ntaps + << " fftsize = " << d_fftsize + << " nsamples = " << d_nsamples << std::endl; + } + + // compute new plans + if(d_fftsize != old_fftsize) { + delete d_fwdfft; + delete d_invfft; + d_fwdfft = new fft::fft_real_fwd(d_fftsize); + d_invfft = new fft::fft_real_rev(d_fftsize); + d_xformed_taps = fft::malloc_complex(d_fftsize/2+1); + } + } + + void + fft_filter_fff::set_nthreads(int n) + { + d_nthreads = n; + if(d_fwdfft) + d_fwdfft->set_nthreads(n); + if(d_invfft) + d_invfft->set_nthreads(n); + } + + int + fft_filter_fff::nthreads() const + { + return d_nthreads; + } + + int + fft_filter_fff::filter(int nitems, const float *input, float *output) + { + int dec_ctr = 0; + int j = 0; + int ninput_items = nitems * d_decimation; + + for (int i = 0; i < ninput_items; i += d_nsamples){ + + memcpy(d_fwdfft->get_inbuf(), &input[i], d_nsamples * sizeof(float)); + + for (j = d_nsamples; j < d_fftsize; j++) + d_fwdfft->get_inbuf()[j] = 0; + + d_fwdfft->execute(); // compute fwd xform + + gr_complex *a = d_fwdfft->get_outbuf(); + gr_complex *b = d_xformed_taps; + gr_complex *c = d_invfft->get_inbuf(); + + volk_32fc_x2_multiply_32fc_a(c, a, b, d_fftsize/2+1); + + d_invfft->execute(); // compute inv xform + + // add in the overlapping tail + for (j = 0; j < tailsize(); j++) + d_invfft->get_outbuf()[j] += d_tail[j]; + + // copy nsamples to output + j = dec_ctr; + while (j < d_nsamples) { + *output++ = d_invfft->get_outbuf()[j]; + j += d_decimation; + } + dec_ctr = (j - d_nsamples); + + // stash the tail + memcpy(&d_tail[0], d_invfft->get_outbuf() + d_nsamples, + tailsize() * sizeof(float)); + } + + return nitems; + } + + + /**************************************************************/ + fft_filter_ccc::fft_filter_ccc(int decimation, const std::vector &taps, @@ -127,7 +267,7 @@ namespace gr { } int - fft_filter_ccc::filter (int nitems, const gr_complex *input, gr_complex *output) + fft_filter_ccc::filter(int nitems, const gr_complex *input, gr_complex *output) { int dec_ctr = 0; int j = 0; @@ -169,6 +309,7 @@ namespace gr { return nitems; } - } /* namespace impl */ + + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_ccc_impl.h b/gr-filter/lib/fft_filter_ccc_impl.h index 2d8d61c5e..82be00915 100644 --- a/gr-filter/lib/fft_filter_ccc_impl.h +++ b/gr-filter/lib/fft_filter_ccc_impl.h @@ -39,8 +39,8 @@ namespace gr { public: fft_filter_ccc_impl(int decimation, - const std::vector &taps, - int nthreads=1); + const std::vector &taps, + int nthreads=1); ~fft_filter_ccc_impl(); @@ -50,9 +50,9 @@ namespace gr { void set_nthreads(int n); int nthreads() const; - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); }; } /* namespace filter */ diff --git a/gr-filter/lib/fft_filter_fff_impl.cc b/gr-filter/lib/fft_filter_fff_impl.cc index a09feb7f1..95b82aae5 100644 --- a/gr-filter/lib/fft_filter_fff_impl.cc +++ b/gr-filter/lib/fft_filter_fff_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2005,2010 Free Software Foundation, Inc. + * Copyright 2005,2010,2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -24,100 +24,97 @@ #include "config.h" #endif -#include -#include +#include "fft_filter_fff_impl.h" #include + +#include #include #include - -#include -#include -#include - -gr_fft_filter_fff_sptr gr_make_fft_filter_fff (int decimation, - const std::vector &taps, - int nthreads) -{ - return gnuradio::get_initial_sptr(new gr_fft_filter_fff (decimation, taps, nthreads)); -} - - -gr_fft_filter_fff::gr_fft_filter_fff (int decimation, - const std::vector &taps, - int nthreads) - : gr_sync_decimator ("fft_filter_fff", - gr_make_io_signature (1, 1, sizeof (float)), - gr_make_io_signature (1, 1, sizeof (float)), - decimation), - d_updated(false) -{ - set_history(1); - -#if 1 // don't enable the sse version until handling it is worked out - d_filter = new gri_fft_filter_fff_generic(decimation, taps, nthreads); -#else - d_filter = new gri_fft_filter_fff_sse(decimation, taps); -#endif - - d_new_taps = taps; - d_nsamples = d_filter->set_taps(taps); - set_output_multiple(d_nsamples); -} - -gr_fft_filter_fff::~gr_fft_filter_fff () -{ - delete d_filter; -} - -void -gr_fft_filter_fff::set_taps (const std::vector &taps) -{ - d_new_taps = taps; - d_updated = true; -} - -std::vector -gr_fft_filter_fff::taps () const -{ - return d_new_taps; -} - -void -gr_fft_filter_fff::set_nthreads(int n) -{ - if(d_filter) - d_filter->set_nthreads(n); -} - -int -gr_fft_filter_fff::nthreads() const -{ - if(d_filter) - return d_filter->nthreads(); - else - return 0; -} - -int -gr_fft_filter_fff::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - const float *in = (const float *) input_items[0]; - float *out = (float *) output_items[0]; - - if (d_updated){ - d_nsamples = d_filter->set_taps(d_new_taps); - d_updated = false; - set_output_multiple(d_nsamples); - return 0; // output multiple may have changed - } - - assert(noutput_items % d_nsamples == 0); - - d_filter->filter(noutput_items, in, out); - - //assert((out - (float *) output_items[0]) == noutput_items); - - return noutput_items; -} +#include + +namespace gr { + namespace filter { + + fft_filter_fff::sptr fft_filter_fff::make(int decimation, + const std::vector &taps, + int nthreads) + { + return gnuradio::get_initial_sptr(new fft_filter_fff_impl + (decimation, taps, nthreads)); + } + + + fft_filter_fff_impl::fft_filter_fff_impl(int decimation, + const std::vector &taps, + int nthreads) + : gr_sync_decimator("fft_filter_fff", + gr_make_io_signature (1, 1, sizeof(float)), + gr_make_io_signature (1, 1, sizeof(float)), + decimation), + d_updated(false) + { + set_history(1); + + d_filter = new kernel::fft_filter_fff(decimation, taps, nthreads); + + d_new_taps = taps; + d_nsamples = d_filter->set_taps(taps); + set_output_multiple(d_nsamples); + } + + fft_filter_fff_impl::~fft_filter_fff_impl() + { + delete d_filter; + } + + void + fft_filter_fff_impl::set_taps(const std::vector &taps) + { + d_new_taps = taps; + d_updated = true; + } + + std::vector + fft_filter_fff_impl::taps() const + { + return d_new_taps; + } + + void + fft_filter_fff_impl::set_nthreads(int n) + { + if(d_filter) + d_filter->set_nthreads(n); + } + + int + fft_filter_fff_impl::nthreads() const + { + if(d_filter) + return d_filter->nthreads(); + else + return 0; + } + + int + fft_filter_fff_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *in = (const float *)input_items[0]; + float *out = (float *)output_items[0]; + + if (d_updated){ + d_nsamples = d_filter->set_taps(d_new_taps); + d_updated = false; + set_output_multiple(d_nsamples); + return 0; // output multiple may have changed + } + + d_filter->filter(noutput_items, in, out); + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_fff_impl.h b/gr-filter/lib/fft_filter_fff_impl.h index 309a55135..df35d3df7 100644 --- a/gr-filter/lib/fft_filter_fff_impl.h +++ b/gr-filter/lib/fft_filter_fff_impl.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2005 Free Software Foundation, Inc. + * Copyright 2005,2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -19,70 +19,43 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ -#ifndef INCLUDED_GR_FFT_FILTER_FFF_H -#define INCLUDED_GR_FFT_FILTER_FFF_H - -#include -#include - -class gr_fft_filter_fff; -typedef boost::shared_ptr gr_fft_filter_fff_sptr; -GR_CORE_API gr_fft_filter_fff_sptr -gr_make_fft_filter_fff (int decimation, const std::vector &taps, - int nthreads=1); - -class gri_fft_filter_fff_generic; -//class gri_fft_filter_fff_sse; - -/*! - * \brief Fast FFT filter with float input, float output and float taps - * \ingroup filter_blk - */ -class GR_CORE_API gr_fft_filter_fff : public gr_sync_decimator -{ - private: - friend GR_CORE_API gr_fft_filter_fff_sptr - gr_make_fft_filter_fff (int decimation, const std::vector &taps, - int nthreads); - - int d_nsamples; - bool d_updated; -#if 1 // don't enable the sse version until handling it is worked out - gri_fft_filter_fff_generic *d_filter; -#else - gri_fft_filter_fff_sse *d_filter; -#endif - std::vector d_new_taps; - - /*! - * Construct a FFT filter with the given taps - * - * \param decimation >= 1 - * \param taps float filter taps - * \param nthreads number of threads for the FFT to use - */ - gr_fft_filter_fff (int decimation, const std::vector &taps, - int nthreads=1); - - public: - ~gr_fft_filter_fff (); - - void set_taps (const std::vector &taps); - std::vector taps () const; - - /*! - * \brief Set number of threads to use. - */ - void set_nthreads(int n); - - /*! - * \brief Get number of threads being used. - */ - int nthreads() const; - - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; - -#endif /* INCLUDED_GR_FFT_FILTER_FFF_H */ +#ifndef INCLUDED_FILTER_FFT_FILTER_FFF_IMPL_H +#define INCLUDED_FILTER_FFT_FILTER_FFF_IMPL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fft_filter_fff_impl : public fft_filter_fff + { + private: + int d_nsamples; + bool d_updated; + kernel::fft_filter_fff *d_filter; + std::vector d_new_taps; + + public: + fft_filter_fff_impl(int decimation, + const std::vector &taps, + int nthreads=1); + + ~fft_filter_fff_impl(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + + void set_nthreads(int n); + int nthreads() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_FFF_IMPL_H */ diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 321f6981e..b45209aa7 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -263,6 +263,6 @@ namespace gr { } } - } /* namespace impl */ + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/python/qa_fft_filter.py b/gr-filter/python/qa_fft_filter.py index 33d3d870b..87d64df36 100755 --- a/gr-filter/python/qa_fft_filter.py +++ b/gr-filter/python/qa_fft_filter.py @@ -216,7 +216,7 @@ class test_fft_filter(gr_unittest.TestCase): taps = (1,) expected_result = tuple([float(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_f(src_data) - op = gr.fft_filter_fff(1, taps) + op = filter.fft_filter_fff(1, taps) dst = gr.vector_sink_f() tb.connect(src, op, dst) tb.run() @@ -232,7 +232,7 @@ class test_fft_filter(gr_unittest.TestCase): taps = (2,) expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_f(src_data) - op = gr.fft_filter_fff(1, taps) + op = filter.fft_filter_fff(1, taps) dst = gr.vector_sink_f() tb.connect(src, op, dst) tb.run() @@ -249,7 +249,7 @@ class test_fft_filter(gr_unittest.TestCase): nthreads = 2 expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_f(src_data) - op = gr.fft_filter_fff(1, taps, nthreads) + op = filter.fft_filter_fff(1, taps, nthreads) dst = gr.vector_sink_f() tb.connect(src, op, dst) tb.run() @@ -267,7 +267,7 @@ class test_fft_filter(gr_unittest.TestCase): expected_result = reference_filter_fff(1, taps, src_data) src = gr.vector_source_f(src_data) - op = gr.fft_filter_fff(1, taps) + op = filter.fft_filter_fff(1, taps) dst = gr.vector_sink_f() tb = gr.top_block() tb.connect(src, op, dst) @@ -297,7 +297,7 @@ class test_fft_filter(gr_unittest.TestCase): expected_result = reference_filter_fff(1, taps, src_data) src = gr.vector_source_f(src_data) - op = gr.fft_filter_fff(1, taps) + op = filter.fft_filter_fff(1, taps) dst = gr.vector_sink_f() tb = gr.top_block() tb.connect(src, op, dst) @@ -318,7 +318,7 @@ class test_fft_filter(gr_unittest.TestCase): expected_result = reference_filter_fff(dec, taps, src_data) src = gr.vector_source_f(src_data) - op = gr.fft_filter_fff(dec, taps) + op = filter.fft_filter_fff(dec, taps) dst = gr.vector_sink_f() tb = gr.top_block() tb.connect(src, op, dst) @@ -341,7 +341,7 @@ class test_fft_filter(gr_unittest.TestCase): expected_result = reference_filter_fff(dec, taps, src_data) src = gr.vector_source_f(src_data) - op = gr.fft_filter_fff(dec, taps, nthreads) + op = filter.fft_filter_fff(dec, taps, nthreads) dst = gr.vector_sink_f() tb = gr.top_block() tb.connect(src, op, dst) @@ -356,7 +356,7 @@ class test_fft_filter(gr_unittest.TestCase): ntaps = int(random.uniform(2, 100)) taps = make_random_float_tuple(ntaps) - op = gr.fft_filter_fff(1, taps) + op = filter.fft_filter_fff(1, taps) result_data = op.taps() #print result_data @@ -368,7 +368,7 @@ class test_fft_filter(gr_unittest.TestCase): ntaps = int(random.uniform(2, 100)) taps = make_random_complex_tuple(ntaps) - op = gr.fft_filter_ccc(1, taps) + op = filter.fft_filter_ccc(1, taps) result_data = op.taps() #print result_data diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 94ad9cd64..6010455b7 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -32,14 +32,17 @@ #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" #include "filter/fft_filter_ccc.h" +#include "filter/fft_filter_fff.h" %} %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" %include "filter/fft_filter_ccc.h" +%include "filter/fft_filter_fff.h" GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); -- cgit From 42eeb8736a50f13a36cc5b40e9465af03b64b2af Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 2 May 2012 20:03:41 -0400 Subject: filter: fixed fft_filter tests. --- gr-filter/python/qa_fft_filter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gr-filter/python/qa_fft_filter.py b/gr-filter/python/qa_fft_filter.py index 87d64df36..eaef3156d 100755 --- a/gr-filter/python/qa_fft_filter.py +++ b/gr-filter/python/qa_fft_filter.py @@ -97,7 +97,7 @@ class test_fft_filter(gr_unittest.TestCase): taps = (1,) expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_c(src_data) - op = gr.fft_filter_ccc(1, taps) + op = filter.fft_filter_ccc(1, taps) dst = gr.vector_sink_c() tb.connect(src, op, dst) tb.run() @@ -115,7 +115,7 @@ class test_fft_filter(gr_unittest.TestCase): nthreads = 2 expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_c(src_data) - op = gr.fft_filter_ccc(1, taps, nthreads) + op = filter.fft_filter_ccc(1, taps, nthreads) dst = gr.vector_sink_c() tb.connect(src, op, dst) tb.run() @@ -130,7 +130,7 @@ class test_fft_filter(gr_unittest.TestCase): taps = (2,) expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_c(src_data) - op = gr.fft_filter_ccc(1, taps) + op = filter.fft_filter_ccc(1, taps) dst = gr.vector_sink_c() tb.connect(src, op, dst) tb.run() @@ -172,7 +172,7 @@ class test_fft_filter(gr_unittest.TestCase): expected_result = reference_filter_ccc(dec, taps, src_data) src = gr.vector_source_c(src_data) - op = gr.fft_filter_ccc(dec, taps) + op = filter.fft_filter_ccc(dec, taps) dst = gr.vector_sink_c() tb = gr.top_block() tb.connect(src, op, dst) -- cgit From 9968eb116b89f2a38602d590207fbe0ea514c018 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 3 May 2012 12:17:35 -0400 Subject: filter: preparing to handle alignment better in FIR filters. --- gr-filter/include/filter/fir_filter.h | 1 + gr-filter/lib/fir_filter.cc | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 76b00cae9..fba4641bf 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -54,6 +54,7 @@ namespace gr { private: unsigned int d_ntaps; float *d_taps; + float *d_aligned_taps[4]; }; /**************************************************************/ diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index b45209aa7..3abcda53e 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -57,6 +57,12 @@ namespace gr { for(unsigned int i = 0; i < d_ntaps; i++) { d_taps[d_ntaps-i-1] = taps[i]; } + + for(int i = 0; i < 4; i++) { + d_aligned_taps[i] = fft::malloc_float(d_ntaps+3); + memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+3)); + memcpy(&d_aligned_taps[i][i], d_taps, sizeof(float)*(d_ntaps)); + } } std::vector @@ -78,6 +84,9 @@ namespace gr { fir_filter_fff::filter(const float input[]) { float output; + + //const float *ar = (float*)((unsigned long)input & ~15); + volk_32f_x2_dot_prod_32f_u(&output, input, d_taps, d_ntaps); return output; } -- cgit From 2c80be2c6e3aadcc96046c2e99891c54f61b027c Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 3 May 2012 13:33:54 -0400 Subject: filter: improved documentation for fft_filters. --- gr-filter/include/filter/fft_filter_ccc.h | 19 +++++++++++++++++++ gr-filter/include/filter/fft_filter_fff.h | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/gr-filter/include/filter/fft_filter_ccc.h b/gr-filter/include/filter/fft_filter_ccc.h index 29fd54713..c018629a9 100644 --- a/gr-filter/include/filter/fft_filter_ccc.h +++ b/gr-filter/include/filter/fft_filter_ccc.h @@ -39,6 +39,25 @@ namespace gr { * \brief Fast FFT filter with gr_complex input, gr_complex output and gr_complex taps * \ingroup filter_blk * + * This block implements a complex decimating filter using the + * fast convolution method via an FFT. The decimation factor is + * an interger that is greater than or equal to 1. + * + * The filter takes a set of complex (or real) taps to use in + * the filtering operation. These taps can be defined as + * anything that satisfies the user's filtering needs. For + * standard filters such as lowpass, highpass, bandpass, etc., + * the filter.firdes and filter.optfir classes provide + * convenient generating methods. + * + * This filter is implemented by using the FFTW package to + * perform the required FFTs. An optional argument, nthreads, + * may be passed to the constructor (or set using the + * set_nthreads member function) to split the FFT among N number + * of threads. This can improve performance on very large FFTs + * (that is, if the number of taps used is very large) if you + * have enough threads/cores to support it. + * * \param decimation >= 1 * \param taps complex filter taps * \param nthreads number of threads for the FFT to use diff --git a/gr-filter/include/filter/fft_filter_fff.h b/gr-filter/include/filter/fft_filter_fff.h index 8eb0a1c83..78540666c 100644 --- a/gr-filter/include/filter/fft_filter_fff.h +++ b/gr-filter/include/filter/fft_filter_fff.h @@ -39,6 +39,25 @@ namespace gr { * \brief Fast FFT filter with float input, float output and float taps * \ingroup filter_blk * + * This block implements a real-value decimating filter using + * the fast convolution method via an FFT. The decimation factor + * is an interger that is greater than or equal to 1. + * + * The filter takes a set of real-valued taps to use in + * the filtering operation. These taps can be defined as + * anything that satisfies the user's filtering needs. For + * standard filters such as lowpass, highpass, bandpass, etc., + * the filter.firdes and filter.optfir classes provide + * convenient generating methods. + * + * This filter is implemented by using the FFTW package to + * perform the required FFTs. An optional argument, nthreads, + * may be passed to the constructor (or set using the + * set_nthreads member function) to split the FFT among N number + * of threads. This can improve performance on very large FFTs + * (that is, if the number of taps used is very large) if you + * have enough threads/cores to support it. + * * \param decimation >= 1 * \param taps float filter taps * \param nthreads number of threads for the FFT to use -- cgit From 1e18d093dad92f798796f75136943adf088cfe60 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 3 May 2012 14:05:21 -0400 Subject: filter: added GRC block for FFT filters. Fixed example install. --- gr-filter/CMakeLists.txt | 4 ++- gr-filter/examples/CMakeLists.txt | 4 ++- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/fft_filter_xxx.xml | 59 +++++++++++++++++++++++++++++++++++++ gr-filter/grc/filter_block_tree.xml | 1 + 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 gr-filter/grc/fft_filter_xxx.xml diff --git a/gr-filter/CMakeLists.txt b/gr-filter/CMakeLists.txt index 02b8f23fe..b77a3d9d4 100644 --- a/gr-filter/CMakeLists.txt +++ b/gr-filter/CMakeLists.txt @@ -42,6 +42,8 @@ GR_SET_GLOBAL(GR_FILTER_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include ) +SET(GR_PKG_FILTER_EXAMPLES_DIR ${GR_PKG_DATA_DIR}/examples/filter) + ######################################################################## # Begin conditional configuration ######################################################################## @@ -91,7 +93,7 @@ if(ENABLE_PYTHON) add_subdirectory(python) add_subdirectory(grc) endif(ENABLE_PYTHON) -#add_subdirectory(examples) +add_subdirectory(examples) add_subdirectory(doc) ######################################################################## diff --git a/gr-filter/examples/CMakeLists.txt b/gr-filter/examples/CMakeLists.txt index c798a8403..12a3c42e0 100644 --- a/gr-filter/examples/CMakeLists.txt +++ b/gr-filter/examples/CMakeLists.txt @@ -22,6 +22,8 @@ include(GrPython) # Base stuff GR_PYTHON_INSTALL(PROGRAMS fir_filter_fff.py - DESTINATION ${GR_PKG_DIGITAL_EXAMPLES_DIR} + fir_filter_ccc.py + fft_filter_ccc.py + DESTINATION ${GR_PKG_FILTER_EXAMPLES_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 79496183f..9b40395b8 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -19,6 +19,7 @@ install(FILES filter_block_tree.xml + fft_filter_xxx.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/grc/fft_filter_xxx.xml b/gr-filter/grc/fft_filter_xxx.xml new file mode 100644 index 000000000..29612dffd --- /dev/null +++ b/gr-filter/grc/fft_filter_xxx.xml @@ -0,0 +1,59 @@ + + + + FFT Filter + fft_filter_xxx + from gnuradio import filter + from gnuradio.gr import firdes + filter.fft_filter_$(type)($decim, $taps, $nthreads) + set_taps($taps) + set_nthreads($nthreads) + + Type + type + enum + + + + + Decimation + decim + 1 + int + + + Taps + taps + $type.taps + + + Num. Threads + nthreads + 1 + int + + + in + $type.input + + + out + $type.output + + diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index a97620db9..7d0559857 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -30,5 +30,6 @@ Filters + fft_filter_xxx -- cgit From 906fe742d1c6a6ca93b47261b0ee615d4779081b Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sat, 5 May 2012 13:34:56 -0400 Subject: filter: added firdes for generating FIR filters to gr-filter. Addes QA code in both Python and C++. --- gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/firdes.h | 378 ++++++++++++++ gr-filter/lib/CMakeLists.txt | 22 + gr-filter/lib/firdes.cc | 855 ++++++++++++++++++++++++++++++++ gr-filter/lib/qa_filter.cc | 39 ++ gr-filter/lib/qa_filter.h | 38 ++ gr-filter/lib/qa_firdes.cc | 621 +++++++++++++++++++++++ gr-filter/lib/qa_firdes.h | 56 +++ gr-filter/lib/test_gr_filter.cc | 42 ++ gr-filter/python/qa_fir_filter.py | 2 +- gr-filter/python/qa_firdes.py | 202 ++++++++ gr-filter/swig/filter_swig.i | 2 + 12 files changed, 2257 insertions(+), 1 deletion(-) create mode 100644 gr-filter/include/filter/firdes.h create mode 100644 gr-filter/lib/firdes.cc create mode 100644 gr-filter/lib/qa_filter.cc create mode 100644 gr-filter/lib/qa_filter.h create mode 100644 gr-filter/lib/qa_firdes.cc create mode 100644 gr-filter/lib/qa_firdes.h create mode 100644 gr-filter/lib/test_gr_filter.cc create mode 100755 gr-filter/python/qa_firdes.py diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index f96128805..108509e9a 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -75,6 +75,7 @@ add_custom_target(filter_generated_includes DEPENDS ######################################################################## install(FILES api.h + firdes.h fir_filter.h fft_filter.h ${generated_includes} diff --git a/gr-filter/include/filter/firdes.h b/gr-filter/include/filter/firdes.h new file mode 100644 index 000000000..172563ea0 --- /dev/null +++ b/gr-filter/include/filter/firdes.h @@ -0,0 +1,378 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2008,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _FILTER_FIRDES_H_ +#define _FILTER_FIRDES_H_ + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief Finite Impulse Response (FIR) filter design functions. + * \ingroup filter_design + */ + + class FILTER_API firdes { + public: + + enum win_type { + WIN_HAMMING = 0, // max attenuation 53 dB + WIN_HANN = 1, // max attenuation 44 dB + WIN_BLACKMAN = 2, // max attenuation 74 dB + WIN_RECTANGULAR = 3, + WIN_KAISER = 4, // max attenuation a function of beta, google it + WIN_BLACKMAN_hARRIS = 5, + WIN_BLACKMAN_HARRIS = 5, // alias for capitalization consistency + }; + + + // ... class methods ... + + /*! + * \brief use "window method" to design a low-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * The normalized width of the transition + * band is what sets the number of taps + * required. Narrow --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + static std::vector + low_pass(double gain, + double sampling_freq, + double cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a low-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * \p attenuation_dB required stopband attenuation + * The normalized width of the transition + * band and the required stop band + * attenuation is what sets the number of taps + * required. Narrow --> more taps + * More attenuatin --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + + static std::vector + low_pass_2(double gain, + double sampling_freq, + double cutoff_freq, // Hz beginning transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // out of band attenuation dB + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a high-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * The normalized width of the transition + * band is what sets the number of taps + * required. Narrow --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + + static std::vector + high_pass(double gain, + double sampling_freq, + double cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a high-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * \p attenuation_dB out of band attenuation + * The normalized width of the transition + * band and the required stop band + * attenuation is what sets the number of taps + * required. Narrow --> more taps + * More attenuation --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + + static std::vector + high_pass_2(double gain, + double sampling_freq, + double cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // out of band attenuation dB + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a band-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p low_cutoff_freq: center of transition band (Hz) + * \p high_cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * The normalized width of the transition + * band is what sets the number of taps + * required. Narrow --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + static std::vector + band_pass(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a band-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p low_cutoff_freq: center of transition band (Hz) + * \p high_cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * \p attenuation_dB out of band attenuation + * The normalized width of the transition + * band and the required stop band + * attenuation is what sets the number of taps + * required. Narrow --> more taps + * More attenuation --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + + static std::vector + band_pass_2(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz beginning transition band + double high_cutoff_freq, // Hz beginning transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // out of band attenuation dB + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a complex band-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p low_cutoff_freq: center of transition band (Hz) + * \p high_cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * The normalized width of the transition + * band is what sets the number of taps + * required. Narrow --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + static std::vector + complex_band_pass(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a complex band-pass FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p low_cutoff_freq: center of transition band (Hz) + * \p high_cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * \p attenuation_dB out of band attenuation + * The normalized width of the transition + * band and the required stop band + * attenuation is what sets the number of taps + * required. Narrow --> more taps + * More attenuation --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + + static std::vector + complex_band_pass_2(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz beginning transition band + double high_cutoff_freq, // Hz beginning transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // out of band attenuation dB + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a band-reject FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p low_cutoff_freq: center of transition band (Hz) + * \p high_cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * The normalized width of the transition + * band is what sets the number of taps + * required. Narrow --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + + static std::vector + band_reject(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*! + * \brief use "window method" to design a band-reject FIR filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p low_cutoff_freq: center of transition band (Hz) + * \p high_cutoff_freq: center of transition band (Hz) + * \p transition_width: width of transition band (Hz). + * \p attenuation_dB out of band attenuation + * The normalized width of the transition + * band and the required stop band + * attenuation is what sets the number of taps + * required. Narrow --> more taps + * More attenuation --> more taps + * \p window_type: What kind of window to use. Determines + * maximum attenuation and passband ripple. + * \p beta: parameter for Kaiser window + */ + + static std::vector + band_reject_2(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz beginning transition band + double high_cutoff_freq, // Hz beginning transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // out of band attenuation dB + win_type window = WIN_HAMMING, + double beta = 6.76); // used only with Kaiser + + /*!\brief design a Hilbert Transform Filter + * + * \p ntaps: Number of taps, must be odd + * \p window_type: What kind of window to use + * \p beta: Only used for Kaiser + */ + static std::vector + hilbert(unsigned int ntaps = 19, + win_type windowtype = WIN_RECTANGULAR, + double beta = 6.76); + + /*! + * \brief design a Root Cosine FIR Filter (do we need a window?) + * + * \p gain: overall gain of filter (typically 1.0) + * \p sampling_freq: sampling freq (Hz) + * \p symbol rate: symbol rate, must be a factor of sample rate + * \p alpha: excess bandwidth factor + * \p ntaps: number of taps + */ + static std::vector + root_raised_cosine(double gain, + double sampling_freq, + double symbol_rate, // Symbol rate, NOT bitrate (unless BPSK) + double alpha, // Excess Bandwidth Factor + int ntaps); + + /*! + * \brief design a Gaussian filter + * + * \p gain: overall gain of filter (typically 1.0) + * \p symbols per bit: symbol rate, must be a factor of sample rate + * \p ntaps: number of taps + */ + static std::vector + gaussian(double gain, + double spb, + double bt, // Bandwidth to bitrate ratio + int ntaps); + + // window functions ... + static std::vector window (win_type type, int ntaps, double beta); + + private: + static double bessi0(double x); + static void sanity_check_1f(double sampling_freq, double f1, + double transition_width); + static void sanity_check_2f(double sampling_freq, double f1, double f2, + double transition_width); + static void sanity_check_2f_c(double sampling_freq, double f1, double f2, + double transition_width); + + static int compute_ntaps(double sampling_freq, + double transition_width, + win_type window_type, double beta); + + static int compute_ntaps_windes(double sampling_freq, + double transition_width, + double attenuation_dB); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* _FILTER_FIRDES_H_ */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index e0af76700..f085987de 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -107,6 +107,7 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) list(APPEND filter_sources fir_filter.cc fft_filter.cc + firdes_impl.cc ${generated_sources} fft_filter_ccc_impl.cc fft_filter_fff_impl.cc @@ -124,3 +125,24 @@ add_library(gnuradio-filter SHARED ${filter_sources}) target_link_libraries(gnuradio-filter ${filter_libs}) GR_LIBRARY_FOO(gnuradio-filter RUNTIME_COMPONENT "filter_runtime" DEVEL_COMPONENT "filter_devel") add_dependencies(gnuradio-filter gnuradio-fft filter_generated_includes filter_generated_swigs) + + +######################################################################## +# QA C++ Code for gr-filter +######################################################################## +list(APPEND test_gr_filter_sources + ${CMAKE_CURRENT_SOURCE_DIR}/test_gr_filter.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_filter.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_firdes.cc +) + +add_executable(test-gr-filter ${test_gr_filter_sources}) +target_link_libraries( + test-gr-filter + gnuradio-core + gnuradio-filter + ${CPPUNIT_LIBRARIES} + ${Boost_LIBRARIES} +) + +GR_ADD_TEST(test_gr_filter) diff --git a/gr-filter/lib/firdes.cc b/gr-filter/lib/firdes.cc new file mode 100644 index 000000000..5c3320d71 --- /dev/null +++ b/gr-filter/lib/firdes.cc @@ -0,0 +1,855 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2007,2008,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +using std::vector; + +namespace gr { + namespace filter { + +#define IzeroEPSILON 1E-21 /* Max error acceptable in Izero */ + + static double Izero(double x) + { + double sum, u, halfx, temp; + int n; + + sum = u = n = 1; + halfx = x/2.0; + do { + temp = halfx/(double)n; + n += 1; + temp *= temp; + u *= temp; + sum += u; + } while (u >= IzeroEPSILON*sum); + return(sum); + } + + + // + // === Low Pass === + // + + vector + firdes::low_pass_2(double gain, + double sampling_freq, // Hz + double cutoff_freq, // Hz BEGINNING of transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // attenuation dB + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_1f(sampling_freq, cutoff_freq, transition_width); + + int ntaps = compute_ntaps_windes(sampling_freq, + transition_width, + attenuation_dB); + + // construct the truncated ideal impulse response + // [sin(x)/x for the low pass case] + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq; + for(int n = -M; n <= M; n++) { + if (n == 0) + taps[n + M] = fwT0 / M_PI * w[n + M]; + else { + // a little algebra gets this into the more familiar sin(x)/x form + taps[n + M] = sin(n * fwT0) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For low-pass, gain @ zero freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M]; + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + vector + firdes::low_pass(double gain, + double sampling_freq, + double cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_1f(sampling_freq, cutoff_freq, transition_width); + + int ntaps = compute_ntaps(sampling_freq, + transition_width, + window_type, beta); + + // construct the truncated ideal impulse response + // [sin(x)/x for the low pass case] + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq; + + for(int n = -M; n <= M; n++) { + if(n == 0) + taps[n + M] = fwT0 / M_PI * w[n + M]; + else { + // a little algebra gets this into the more familiar sin(x)/x form + taps[n + M] = sin(n * fwT0) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For low-pass, gain @ zero freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M]; + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + + // + // === High Pass === + // + + vector + firdes::high_pass_2(double gain, + double sampling_freq, + double cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // attenuation dB + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_1f(sampling_freq, cutoff_freq, transition_width); + + int ntaps = compute_ntaps_windes(sampling_freq, + transition_width, + attenuation_dB); + + // construct the truncated ideal impulse response times the window function + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq; + + for(int n = -M; n <= M; n++) { + if(n == 0) + taps[n + M] = (1 - (fwT0 / M_PI)) * w[n + M]; + else { + // a little algebra gets this into the more familiar sin(x)/x form + taps[n + M] = -sin(n * fwT0) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For high-pass, gain @ fs/2 freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M] * cos(n * M_PI); + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + + vector + firdes::high_pass(double gain, + double sampling_freq, + double cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_1f(sampling_freq, cutoff_freq, transition_width); + + int ntaps = compute_ntaps(sampling_freq, + transition_width, + window_type, beta); + + // construct the truncated ideal impulse response times the window function + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq; + + for(int n = -M; n <= M; n++) { + if(n == 0) + taps[n + M] = (1 - (fwT0 / M_PI)) * w[n + M]; + else { + // a little algebra gets this into the more familiar sin(x)/x form + taps[n + M] = -sin(n * fwT0) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For high-pass, gain @ fs/2 freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M] * cos(n * M_PI); + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + // + // === Band Pass === + // + + vector + firdes::band_pass_2(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // attenuation dB + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_2f(sampling_freq, + low_cutoff_freq, + high_cutoff_freq, transition_width); + + int ntaps = compute_ntaps_windes(sampling_freq, + transition_width, + attenuation_dB); + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq; + double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq; + + for(int n = -M; n <= M; n++) { + if (n == 0) + taps[n + M] = (fwT1 - fwT0) / M_PI * w[n + M]; + else { + taps[n + M] = (sin(n * fwT1) - sin(n * fwT0)) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For band-pass, gain @ center freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M] * cos(n * (fwT0 + fwT1) * 0.5); + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + + vector + firdes::band_pass(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_2f(sampling_freq, + low_cutoff_freq, + high_cutoff_freq, + transition_width); + + int ntaps = compute_ntaps(sampling_freq, + transition_width, + window_type, beta); + + // construct the truncated ideal impulse response times the window function + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq; + double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq; + + for(int n = -M; n <= M; n++) { + if (n == 0) + taps[n + M] = (fwT1 - fwT0) / M_PI * w[n + M]; + else { + taps[n + M] = (sin(n * fwT1) - sin(n * fwT0)) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For band-pass, gain @ center freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M] * cos(n * (fwT0 + fwT1) * 0.5); + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + // + // === Complex Band Pass === + // + + vector + firdes::complex_band_pass_2(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // attenuation dB + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_2f_c(sampling_freq, + low_cutoff_freq, + high_cutoff_freq, + transition_width); + + int ntaps = compute_ntaps_windes(sampling_freq, + transition_width, + attenuation_dB); + + vector taps(ntaps); + vector lptaps(ntaps); + vector w = window(window_type, ntaps, beta); + + lptaps = low_pass_2(gain, sampling_freq, + (high_cutoff_freq - low_cutoff_freq)/2, + transition_width, attenuation_dB, + window_type, beta); + + gr_complex *optr = &taps[0]; + float *iptr = &lptaps[0]; + float freq = M_PI * (high_cutoff_freq + low_cutoff_freq)/sampling_freq; + float phase = 0; + if (lptaps.size() & 01) { + phase = - freq * ( lptaps.size() >> 1 ); + } + else + phase = - freq/2.0 * ((1 + 2*lptaps.size()) >> 1); + + for(unsigned int i = 0; i < lptaps.size(); i++) { + *optr++ = gr_complex(*iptr * cos(phase), *iptr * sin(phase)); + iptr++, phase += freq; + } + + return taps; + } + + + vector + firdes::complex_band_pass(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_2f_c (sampling_freq, + low_cutoff_freq, + high_cutoff_freq, + transition_width); + + int ntaps = compute_ntaps(sampling_freq, + transition_width, + window_type, beta); + + // construct the truncated ideal impulse response times the window function + + vector taps(ntaps); + vector lptaps(ntaps); + vector w = window(window_type, ntaps, beta); + + lptaps = low_pass(gain, sampling_freq, + (high_cutoff_freq - low_cutoff_freq)/2, + transition_width, window_type, beta); + + gr_complex *optr = &taps[0]; + float *iptr = &lptaps[0]; + float freq = M_PI * (high_cutoff_freq + low_cutoff_freq)/sampling_freq; + float phase = 0; + if(lptaps.size() & 01) { + phase = - freq * ( lptaps.size() >> 1 ); + } + else + phase = - freq/2.0 * ((1 + 2*lptaps.size()) >> 1); + + for(unsigned int i=0;i + firdes::band_reject_2(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + double attenuation_dB, // attenuation dB + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_2f(sampling_freq, + low_cutoff_freq, + high_cutoff_freq, + transition_width); + + int ntaps = compute_ntaps_windes(sampling_freq, + transition_width, + attenuation_dB); + + // construct the truncated ideal impulse response times the window function + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq; + double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq; + + for(int n = -M; n <= M; n++) { + if (n == 0) + taps[n + M] = 1.0 + ((fwT0 - fwT1) / M_PI * w[n + M]); + else { + taps[n + M] = (sin(n * fwT0) - sin(n * fwT1)) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For band-reject, gain @ zero freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M]; + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + vector + firdes::band_reject(double gain, + double sampling_freq, + double low_cutoff_freq, // Hz center of transition band + double high_cutoff_freq, // Hz center of transition band + double transition_width, // Hz width of transition band + win_type window_type, + double beta) // used only with Kaiser + { + sanity_check_2f(sampling_freq, + low_cutoff_freq, + high_cutoff_freq, + transition_width); + + int ntaps = compute_ntaps(sampling_freq, + transition_width, + window_type, beta); + + // construct the truncated ideal impulse response times the window function + + vector taps(ntaps); + vector w = window(window_type, ntaps, beta); + + int M = (ntaps - 1) / 2; + double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq; + double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq; + + for(int n = -M; n <= M; n++) { + if (n == 0) + taps[n + M] = 1.0 + ((fwT0 - fwT1) / M_PI * w[n + M]); + else { + taps[n + M] = (sin(n * fwT0) - sin(n * fwT1)) / (n * M_PI) * w[n + M]; + } + } + + // find the factor to normalize the gain, fmax. + // For band-reject, gain @ zero freq = 1.0 + + double fmax = taps[0 + M]; + for(int n = 1; n <= M; n++) + fmax += 2 * taps[n + M]; + + gain /= fmax; // normalize + + for(int i = 0; i < ntaps; i++) + taps[i] *= gain; + + return taps; + } + + // + // Hilbert Transform + // + + vector + firdes::hilbert(unsigned int ntaps, + win_type windowtype, + double beta) + { + if(!(ntaps & 1)) + throw std::out_of_range("Hilbert: Must have odd number of taps"); + + vector taps(ntaps); + vector w = window (windowtype, ntaps, beta); + unsigned int h = (ntaps-1)/2; + float gain=0; + for(unsigned int i = 1; i <= h; i++) { + if(i & 1) { + float x = 1/(float)i; + taps[h+i] = x * w[h+i]; + taps[h-i] = -x * w[h-i]; + gain = taps[h+i] - gain; + } + else + taps[h+i] = taps[h-i] = 0; + } + + gain = 2 * fabs(gain); + for(unsigned int i = 0; i < ntaps; i++) + taps[i] /= gain; + return taps; + } + + // + // Gaussian + // + + vector + firdes::gaussian(double gain, + double spb, + double bt, + int ntaps) + { + vector taps(ntaps); + double scale = 0; + double dt = 1.0/spb; + double s = 1.0/(sqrt(log(2.0)) / (2*M_PI*bt)); + double t0 = -0.5 * ntaps; + double ts; + for(int i=0;i + firdes::root_raised_cosine(double gain, + double sampling_freq, + double symbol_rate, + double alpha, + int ntaps) + { + ntaps |= 1; // ensure that ntaps is odd + + double spb = sampling_freq/symbol_rate; // samples per bit/symbol + vector taps(ntaps); + double scale = 0; + for(int i = 0; i < ntaps; i++) { + double x1,x2,x3,num,den; + double xindx = i - ntaps/2; + x1 = M_PI * xindx/spb; + x2 = 4 * alpha * xindx / spb; + x3 = x2*x2 - 1; + + if(fabs(x3) >= 0.000001) { // Avoid Rounding errors... + if(i != ntaps/2) + num = cos((1+alpha)*x1) + sin((1-alpha)*x1)/(4*alpha*xindx/spb); + else + num = cos((1+alpha)*x1) + (1-alpha) * M_PI / (4*alpha); + den = x3 * M_PI; + } + else { + if(alpha==1) { + taps[i] = -1; + continue; + } + x3 = (1-alpha)*x1; + x2 = (1+alpha)*x1; + num = (sin(x2)*(1+alpha)*M_PI + - cos(x3)*((1-alpha)*M_PI*spb)/(4*alpha*xindx) + + sin(x3)*spb*spb/(4*alpha*xindx*xindx)); + den = -32 * M_PI * alpha * alpha * xindx/spb; + } + taps[i] = 4 * alpha * num / den; + scale += taps[i]; + } + + for(int i = 0; i < ntaps; i++) + taps[i] = taps[i] * gain / scale; + + return taps; + } + + // + // === Utilities === + // + + // delta_f / width_factor gives number of taps required. + static const float width_factor[5] = { // indexed by win_type + 3.3, // WIN_HAMMING + 3.1, // WIN_HANN + 5.5, // WIN_BLACKMAN + 2.0, // WIN_RECTANGULAR + //5.0 // WIN_KAISER (guesstimate compromise) + //2.0 // WIN_KAISER (guesstimate compromise) + 10.0 // WIN_KAISER + }; + + int + firdes::compute_ntaps_windes(double sampling_freq, + double transition_width, // this is frequency, not relative frequency + double attenuation_dB) + { + // Based on formula from Multirate Signal Processing for + // Communications Systems, fredric j harris + int ntaps = (int)(attenuation_dB*sampling_freq/(22.0*transition_width)); + if ((ntaps & 1) == 0) // if even... + ntaps++; // ...make odd + return ntaps; + } + + int + firdes::compute_ntaps(double sampling_freq, + double transition_width, + win_type window_type, + double beta) + { + // normalized transition width + double delta_f = transition_width / sampling_freq; + + // compute number of taps required for given transition width + int ntaps = (int)(width_factor[window_type] / delta_f + 0.5); + if((ntaps & 1) == 0) // if even... + ntaps++; // ...make odd + + return ntaps; + } + + double + firdes::bessi0(double x) + { + double ax,ans; + double y; + + ax=fabs(x); + if (ax < 3.75) { + y=x/3.75; + y*=y; + ans=1.0+y*(3.5156229+y*(3.0899424+y*(1.2067492 + +y*(0.2659732+y*(0.360768e-1+y*0.45813e-2))))); + } + else { + y=3.75/ax; + ans=(exp(ax)/sqrt(ax))*(0.39894228+y*(0.1328592e-1 + +y*(0.225319e-2+y*(-0.157565e-2+y*(0.916281e-2 + +y*(-0.2057706e-1+y*(0.2635537e-1+y*(-0.1647633e-1 + +y*0.392377e-2)))))))); + } + return ans; + } + + vector + firdes::window (win_type type, int ntaps, double beta) + { + vector taps(ntaps); + int M = ntaps - 1; // filter order + + switch (type) { + case WIN_RECTANGULAR: + for(int n = 0; n < ntaps; n++) + taps[n] = 1; + + case WIN_HAMMING: + for(int n = 0; n < ntaps; n++) + taps[n] = 0.54 - 0.46 * cos((2 * M_PI * n) / M); + break; + + case WIN_HANN: + for(int n = 0; n < ntaps; n++) + taps[n] = 0.5 - 0.5 * cos((2 * M_PI * n) / M); + break; + + case WIN_BLACKMAN: + for(int n = 0; n < ntaps; n++) + taps[n] = 0.42 - 0.50 * cos((2*M_PI * n) / (M-1)) + - 0.08 * cos((4*M_PI * n) / (M-1)); + break; + + case WIN_BLACKMAN_hARRIS: + for(int n = -ntaps/2; n < ntaps/2; n++) + taps[n+ntaps/2] = 0.35875 + 0.48829*cos((2*M_PI * n) / (float)M) + + 0.14128*cos((4*M_PI * n) / (float)M) + 0.01168*cos((6*M_PI * n) / (float)M); + break; + + case WIN_KAISER: + { + double IBeta = 1.0/Izero(beta); + double inm1 = 1.0/((double)(ntaps)); + double temp; + //fprintf(stderr, "IBeta = %g; inm1 = %g\n", IBeta, inm1); + + for(int i=0; i 0"); + + if(fa <= 0.0 || fa > sampling_freq / 2) + throw std::out_of_range("firdes check failed: 0 < fa <= sampling_freq / 2"); + + if(transition_width <= 0) + throw std::out_of_range("gr_dirdes check failed: transition_width > 0"); + } + + void + firdes::sanity_check_2f(double sampling_freq, + double fa, // first cutoff freq + double fb, // second cutoff freq + double transition_width) + { + if (sampling_freq <= 0.0) + throw std::out_of_range("firdes check failed: sampling_freq > 0"); + + if (fa <= 0.0 || fa > sampling_freq / 2) + throw std::out_of_range("firdes check failed: 0 < fa <= sampling_freq / 2"); + + if (fb <= 0.0 || fb > sampling_freq / 2) + throw std::out_of_range("firdes check failed: 0 < fb <= sampling_freq / 2"); + + if (fa > fb) + throw std::out_of_range("firdes check failed: fa <= fb"); + + if (transition_width <= 0) + throw std::out_of_range("firdes check failed: transition_width > 0"); + } + + void + firdes::sanity_check_2f_c(double sampling_freq, + double fa, // first cutoff freq + double fb, // second cutoff freq + double transition_width) + { + if(sampling_freq <= 0.0) + throw std::out_of_range("firdes check failed: sampling_freq > 0"); + + if(fa < -sampling_freq / 2 || fa > sampling_freq / 2) + throw std::out_of_range("firdes check failed: 0 < fa <= sampling_freq / 2"); + + if(fb < -sampling_freq / 2 || fb > sampling_freq / 2) + throw std::out_of_range("firdes check failed: 0 < fb <= sampling_freq / 2"); + + if(fa > fb) + throw std::out_of_range("firdes check failed: fa <= fb"); + + if(transition_width <= 0) + throw std::out_of_range("firdes check failed: transition_width > 0"); + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/qa_filter.cc b/gr-filter/lib/qa_filter.cc new file mode 100644 index 000000000..86d4860de --- /dev/null +++ b/gr-filter/lib/qa_filter.cc @@ -0,0 +1,39 @@ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * This class gathers together all the test cases for the gr-filter + * directory into a single test suite. As you create new test cases, + * add them here. + */ + +#include +#include + +CppUnit::TestSuite * +qa_gr_filter::suite () +{ + CppUnit::TestSuite *s = new CppUnit::TestSuite ("gr-filter"); + + s->addTest(gr::filter::qa_firdes::suite ()); + + return s; +} diff --git a/gr-filter/lib/qa_filter.h b/gr-filter/lib/qa_filter.h new file mode 100644 index 000000000..427e7f9f6 --- /dev/null +++ b/gr-filter/lib/qa_filter.h @@ -0,0 +1,38 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _QA_GR_FILTER_H_ +#define _QA_GR_FILTER_H_ + +#include +#include + +//! collect all the tests for the gr-filter directory + +class __GR_ATTR_EXPORT qa_gr_filter { + public: + //! return suite of tests for all of gr-filter directory + static CppUnit::TestSuite *suite (); +}; + + +#endif /* _QA_GR_FILTER_H_ */ diff --git a/gr-filter/lib/qa_firdes.cc b/gr-filter/lib/qa_firdes.cc new file mode 100644 index 000000000..c2fe399d5 --- /dev/null +++ b/gr-filter/lib/qa_firdes.cc @@ -0,0 +1,621 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gr { + namespace filter { + +#define NELEM(x) (sizeof(x) / sizeof(x[0])) + + using std::vector; + +#if 0 + static void + print_taps(std::ostream &s, vector &v) + { + + for(unsigned int i = 0; i < v.size(); i++) { + printf("tap[%2d] = %16.7e\n", i, v[i]); + } + } +#endif + + static void + check_symmetry(vector &v) + { + int n = v.size(); + int m = n / 2; + + for(int i = 0; i < m; i++) + CPPUNIT_ASSERT_DOUBLES_EQUAL(v[i], v[n - i - 1], 1e-9); + } + + const static float t1_exp[53] = { + -9.0525491e-04, + 2.0713841e-04, + 1.2388536e-03, + 2.9683491e-04, + -1.7744775e-03, + -1.3599906e-03, + 2.2031884e-03, + 3.2744040e-03, + -1.8868084e-03, + -5.9935520e-03, + 6.4301129e-18, + 8.9516686e-03, + 4.2178580e-03, + -1.0998557e-02, + -1.1173409e-02, + 1.0455756e-02, + 2.0686293e-02, + -5.2032238e-03, + -3.1896964e-02, + -7.4998410e-03, + 4.3362070e-02, + 3.2502845e-02, + -5.3328082e-02, + -8.5621715e-02, + 6.0117975e-02, + 3.1128189e-01, + 4.3769023e-01, + 3.1128189e-01, + 6.0117975e-02, + -8.5621715e-02, + -5.3328082e-02, + 3.2502845e-02, + 4.3362070e-02, + -7.4998410e-03, + -3.1896964e-02, + -5.2032238e-03, + 2.0686293e-02, + 1.0455756e-02, + -1.1173409e-02, + -1.0998557e-02, + 4.2178580e-03, + 8.9516686e-03, + 6.4301129e-18, + -5.9935520e-03, + -1.8868084e-03, + 3.2744040e-03, + 2.2031884e-03, + -1.3599906e-03, + -1.7744775e-03, + 2.9683491e-04, + 1.2388536e-03, + 2.0713841e-04, + -9.0525491e-04 + }; + + const static float t2_exp[53] = { + 9.0380036e-04, + -2.0680559e-04, + -1.2368630e-03, + -2.9635796e-04, + 1.7716263e-03, + 1.3578053e-03, + -2.1996482e-03, + -3.2691427e-03, + 1.8837767e-03, + 5.9839217e-03, + -6.4197810e-18, + -8.9372853e-03, + -4.2110807e-03, + 1.0980885e-02, + 1.1155456e-02, + -1.0438956e-02, + -2.0653054e-02, + 5.1948633e-03, + 3.1845711e-02, + 7.4877902e-03, + -4.3292396e-02, + -3.2450620e-02, + 5.3242393e-02, + 8.5484132e-02, + -6.0021374e-02, + -3.1078172e-01, + 5.6184036e-01, + -3.1078172e-01, + -6.0021374e-02, + 8.5484132e-02, + 5.3242393e-02, + -3.2450620e-02, + -4.3292396e-02, + 7.4877902e-03, + 3.1845711e-02, + 5.1948633e-03, + -2.0653054e-02, + -1.0438956e-02, + 1.1155456e-02, + 1.0980885e-02, + -4.2110807e-03, + -8.9372853e-03, + -6.4197810e-18, + 5.9839217e-03, + 1.8837767e-03, + -3.2691427e-03, + -2.1996482e-03, + 1.3578053e-03, + 1.7716263e-03, + -2.9635796e-04, + -1.2368630e-03, + -2.0680559e-04, + 9.0380036e-04 + }; + + const static float t3_exp[107] = { + -1.8970841e-06, + -7.1057165e-04, + 5.4005696e-04, + 4.6233178e-04, + 2.0572044e-04, + 3.5209916e-04, + -1.4098573e-03, + 1.1279077e-04, + -6.2994129e-04, + 1.1450432e-03, + 1.3637283e-03, + -6.4360141e-04, + 3.6509900e-04, + -3.2864159e-03, + 7.0192874e-04, + 3.7524730e-04, + 2.0256115e-03, + 3.0641893e-03, + -3.6618244e-03, + 7.5592739e-05, + -5.5586505e-03, + 2.3849572e-03, + 4.0114378e-03, + 1.6636450e-03, + 4.7835698e-03, + -1.0191196e-02, + -3.8158931e-04, + -5.5551580e-03, + 5.3901658e-03, + 1.1366769e-02, + -3.0000482e-03, + 4.9341680e-03, + -2.0093076e-02, + 5.5752542e-17, + 1.2093617e-03, + 8.6089745e-03, + 2.2382140e-02, + -1.6854567e-02, + 1.6913920e-03, + -3.1222520e-02, + 3.2711059e-03, + 2.2604836e-02, + 8.1451107e-03, + 3.7583180e-02, + -5.2293688e-02, + -8.0551542e-03, + -4.0092729e-02, + 1.5582236e-02, + 9.7452506e-02, + -1.6183170e-02, + 8.3281815e-02, + -2.8196752e-01, + -1.0965768e-01, + 5.2867508e-01, + -1.0965768e-01, + -2.8196752e-01, + 8.3281815e-02, + -1.6183170e-02, + 9.7452506e-02, + 1.5582236e-02, + -4.0092729e-02, + -8.0551542e-03, + -5.2293688e-02, + 3.7583180e-02, + 8.1451107e-03, + 2.2604836e-02, + 3.2711059e-03, + -3.1222520e-02, + 1.6913920e-03, + -1.6854567e-02, + 2.2382140e-02, + 8.6089745e-03, + 1.2093617e-03, + 5.5752542e-17, + -2.0093076e-02, + 4.9341680e-03, + -3.0000482e-03, + 1.1366769e-02, + 5.3901658e-03, + -5.5551580e-03, + -3.8158931e-04, + -1.0191196e-02, + 4.7835698e-03, + 1.6636450e-03, + 4.0114378e-03, + 2.3849572e-03, + -5.5586505e-03, + 7.5592739e-05, + -3.6618244e-03, + 3.0641893e-03, + 2.0256115e-03, + 3.7524730e-04, + 7.0192874e-04, + -3.2864159e-03, + 3.6509900e-04, + -6.4360141e-04, + 1.3637283e-03, + 1.1450432e-03, + -6.2994129e-04, + 1.1279077e-04, + -1.4098573e-03, + 3.5209916e-04, + 2.0572044e-04, + 4.6233178e-04, + 5.4005696e-04, + -7.1057165e-04, + -1.8970841e-06 + }; + + const static float t4_exp[] = { // low pass + 0.001059958362, + 0.0002263929928, + -0.001277606934, + -0.0009675776237, + 0.001592264394, + 0.00243603508, + -0.001451682881, + -0.004769335967, + 5.281541594e-18, + 0.007567512803, + 0.003658855334, + -0.009761494584, + -0.01011830103, + 0.009636915289, + 0.0193619132, + -0.004935568199, + -0.03060629964, + -0.007267376408, + 0.04236677289, + 0.03197422624, + -0.05274848267, + -0.0850463286, + 0.05989059806, + 0.31065014, + 0.4370569289, + 0.31065014, + 0.05989059806, + -0.0850463286, + -0.05274848267, + 0.03197422624, + 0.04236677289, + -0.007267376408, + -0.03060629964, + -0.004935568199, + 0.0193619132, + 0.009636915289, + -0.01011830103, + -0.009761494584, + 0.003658855334, + 0.007567512803, + 5.281541594e-18, + -0.004769335967, + -0.001451682881, + 0.00243603508, + 0.001592264394, + -0.0009675776237, + -0.001277606934, + 0.0002263929928, + 0.001059958362, + }; + + const static float t5_exp[] = { //high pass + -0.001062123571, + -0.0002268554381, + 0.001280216733, + 0.000969554123, + -0.001595516922, + -0.002441011136, + 0.001454648213, + 0.004779078532, + -5.292330097e-18, + -0.007582970895, + -0.00366632943, + 0.009781434201, + 0.01013896987, + -0.009656600654, + -0.01940146461, + 0.004945650231, + 0.03066881932, + 0.00728222169, + -0.04245331511, + -0.03203954175, + 0.05285623297, + 0.08522006124, + -0.06001294032, + -0.3112847209, + 0.5630782247, + -0.3112847209, + -0.06001294032, + 0.08522006124, + 0.05285623297, + -0.03203954175, + -0.04245331511, + 0.00728222169, + 0.03066881932, + 0.004945650231, + -0.01940146461, + -0.009656600654, + 0.01013896987, + 0.009781434201, + -0.00366632943, + -0.007582970895, + -5.292330097e-18, + 0.004779078532, + 0.001454648213, + -0.002441011136, + -0.001595516922, + 0.000969554123, + 0.001280216733, + -0.0002268554381, + -0.001062123571, + }; + + const static float t6_exp[] = { // bandpass + 0.0002809273137, + -0.001047327649, + 7.936541806e-05, + -0.0004270860809, + 0.0007595835486, + 0.0008966081077, + -0.0004236323002, + 0.0002423936094, + -0.002212299034, + 0.0004807534278, + 0.0002620361629, + 0.001443728455, + 0.002229931997, + -0.002720607212, + 5.731141573e-05, + -0.004297634587, + 0.001878833398, + 0.003217151389, + 0.001357055153, + 0.003965090029, + -0.008576190099, + -0.0003257228818, + -0.004805727862, + 0.004721920472, + 0.01007549558, + -0.002688719891, + 0.004467967432, + -0.01837076992, + 5.119658377e-17, + 0.001125075156, + 0.008071650751, + 0.02113764361, + -0.01602453552, + 0.001618095324, + -0.03004053794, + 0.003163811285, + 0.0219683405, + 0.007950295694, + 0.03682873398, + -0.05142467469, + -0.00794606097, + -0.03965795785, + 0.01544955093, + 0.09681399167, + -0.01610304788, + 0.08297294378, + -0.2811714709, + -0.1094062924, + 0.5275565982, + -0.1094062924, + -0.2811714709, + 0.08297294378, + -0.01610304788, + 0.09681399167, + 0.01544955093, + -0.03965795785, + -0.00794606097, + -0.05142467469, + 0.03682873398, + 0.007950295694, + 0.0219683405, + 0.003163811285, + -0.03004053794, + 0.001618095324, + -0.01602453552, + 0.02113764361, + 0.008071650751, + 0.001125075156, + 5.119658377e-17, + -0.01837076992, + 0.004467967432, + -0.002688719891, + 0.01007549558, + 0.004721920472, + -0.004805727862, + -0.0003257228818, + -0.008576190099, + 0.003965090029, + 0.001357055153, + 0.003217151389, + 0.001878833398, + -0.004297634587, + 5.731141573e-05, + -0.002720607212, + 0.002229931997, + 0.001443728455, + 0.0002620361629, + 0.0004807534278, + -0.002212299034, + 0.0002423936094, + -0.0004236323002, + 0.0008966081077, + 0.0007595835486, + -0.0004270860809, + 7.936541806e-05, + -0.001047327649, + 0.0002809273137, + }; + + void + qa_firdes::t1() + { + vector taps = + firdes::low_pass(1.0, + 8000, + 1750, + 500, + firdes::WIN_HAMMING); + + // cout << "ntaps: " << taps.size() << endl; + // print_taps(cout, taps); + + CPPUNIT_ASSERT_EQUAL(NELEM(t1_exp), taps.size()); + for(unsigned int i = 0; i < taps.size(); i++) + CPPUNIT_ASSERT_DOUBLES_EQUAL(t1_exp[i], taps[i], 1e-9); + + check_symmetry(taps); +} + + void + qa_firdes::t2() + { + vector taps = + firdes::high_pass(1.0, + 8000, + 1750, + 500, + firdes::WIN_HAMMING); + + // cout << "ntaps: " << taps.size() << endl; + // print_taps(cout, taps); + + CPPUNIT_ASSERT_EQUAL(NELEM(t2_exp), taps.size()); + + for(unsigned int i = 0; i < taps.size(); i++) + CPPUNIT_ASSERT_DOUBLES_EQUAL(t2_exp[i], taps[i], 1e-9); + + check_symmetry(taps); + } + + void + qa_firdes::t3() + { + vector taps = + firdes::band_pass(1.0, + 20e6, + 5.75e6 - (5.28e6/2), + 5.75e6 + (5.28e6/2), + 0.62e6, + firdes::WIN_HAMMING); + + // cout << "ntaps: " << taps.size() << endl; + // print_taps(cout, taps); + + CPPUNIT_ASSERT_EQUAL(NELEM(t3_exp), taps.size()); + + for(unsigned int i = 0; i < taps.size(); i++) + CPPUNIT_ASSERT_DOUBLES_EQUAL(t3_exp[i], taps[i], 1e-7); + + check_symmetry(taps); + } + + void + qa_firdes::t4() + { + vector taps = + firdes::low_pass_2(1.0, + 8000, + 1750, + 500, + 66, + firdes::WIN_HAMMING); + + // std::cout << "ntaps: " << taps.size() << std::endl; + // print_taps(std::cout, taps); + + CPPUNIT_ASSERT_EQUAL(NELEM(t4_exp), taps.size()); + for(unsigned int i = 0; i < taps.size(); i++) + CPPUNIT_ASSERT_DOUBLES_EQUAL(t4_exp[i], taps[i], 1e-9); + + check_symmetry(taps); + } + + void + qa_firdes::t5() + { + vector taps = + firdes::high_pass_2(1.0, + 8000, + 1750, + 500, + 66, + firdes::WIN_HAMMING); + + // std::cout << "ntaps: " << taps.size() << std::endl; + // print_taps(std::cout, taps); + + CPPUNIT_ASSERT_EQUAL(NELEM(t5_exp), taps.size()); + + for(unsigned int i = 0; i < taps.size(); i++) + CPPUNIT_ASSERT_DOUBLES_EQUAL(t5_exp[i], taps[i], 1e-9); + + check_symmetry(taps); +} + + void + qa_firdes::t6() + { + vector taps = + firdes::band_pass_2(1.0, + 20e6, + 5.75e6 - (5.28e6/2), + 5.75e6 + (5.28e6/2), + 0.62e6, + 66, + firdes::WIN_HAMMING); + + // std::cout << "ntaps: " << taps.size() << std::endl; + // print_taps(std::cout, taps); + + CPPUNIT_ASSERT_EQUAL(NELEM(t6_exp), taps.size()); + + for(unsigned int i = 0; i < taps.size(); i++) + CPPUNIT_ASSERT_DOUBLES_EQUAL(t6_exp[i], taps[i], 1e-7); + + check_symmetry(taps); + } + + void + qa_firdes::t7() + { + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/qa_firdes.h b/gr-filter/lib/qa_firdes.h new file mode 100644 index 000000000..b27cf78b9 --- /dev/null +++ b/gr-filter/lib/qa_firdes.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef _QA_FILTER_FIRDES_H_ +#define _QA_FILTER_FIRDES_H_ + +#include +#include + +namespace gr { + namespace filter { + + class qa_firdes : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_firdes); + CPPUNIT_TEST(t1); + CPPUNIT_TEST(t2); + CPPUNIT_TEST(t3); + CPPUNIT_TEST(t4); + CPPUNIT_TEST(t5); + CPPUNIT_TEST(t6); + CPPUNIT_TEST(t7); + CPPUNIT_TEST_SUITE_END(); + + private: + void t1(); + void t2(); + void t3(); + void t4(); + void t5(); + void t6(); + void t7(); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* _QA_FILTER_FIRDES_H_ */ diff --git a/gr-filter/lib/test_gr_filter.cc b/gr-filter/lib/test_gr_filter.cc new file mode 100644 index 000000000..9027b7a99 --- /dev/null +++ b/gr-filter/lib/test_gr_filter.cc @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include +#include + +int +main (int argc, char **argv) +{ + CppUnit::TextTestRunner runner; + std::ofstream xmlfile(get_unittest_path("gr_filter.xml").c_str()); + CppUnit::XmlOutputter *xmlout = new CppUnit::XmlOutputter(&runner.result(), xmlfile); + + runner.addTest(qa_gr_filter::suite()); + runner.setOutputter(xmlout); + + bool was_successful = runner.run("", false); + + return was_successful ? 0 : 1; +} diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py index f0f08afca..38bfd9ea5 100755 --- a/gr-filter/python/qa_fir_filter.py +++ b/gr-filter/python/qa_fir_filter.py @@ -70,7 +70,7 @@ class test_filter(gr_unittest.TestCase): # results derived from original gr.fir_filter_ccc expected_data = ((7.537424837948042e-20+7.537424837948042e-20j), (9.131923434324563e-05+9.131923434324563e-05j), (0.0003317668742965907+0.0003317668742965907j), (0.0007230418268591166+0.0007230418268591166j), (0.0012087896466255188+0.0012087896466255188j), (0.0013292605290189385+0.0013292605290189385j), (0.001120875240303576+0.001120875240303576j), (0.000744672492146492+0.000744672492146492j), (0.000429437990533188+0.000429437990533188j), (2.283908543176949e-05+2.283908543176949e-05j), (-0.0002245186478830874-0.0002245186478830874j), (-0.0001157080550910905-0.0001157080550910905j), (0.00041409023106098175+0.00041409023106098175j), (0.0009017843985930085+0.0009017843985930085j), (0.0012520025484263897+0.0012520025484263897j), (0.0014116164529696107+0.0014116164529696107j), (0.001393353333696723+0.001393353333696723j), (0.000912194955162704+0.000912194955162704j), (0.00022649182938039303+0.00022649182938039303j), (-0.00031363096786662936-0.00031363096786662936j), (-0.0003966730728279799-0.0003966730728279799j), (-0.00023757052258588374-0.00023757052258588374j), (0.00021952332463115454+0.00021952332463115454j), (0.0009092430118471384+0.0009092430118471384j), (0.001662317430600524+0.001662317430600524j), (0.0019024648936465383+0.0019024648936465383j), (0.0015955769922584295+0.0015955769922584295j), (0.0009144138311967254+0.0009144138311967254j), (0.0001872836146503687+0.0001872836146503687j), (-0.000581968342885375-0.000581968342885375j), (-0.0009886166080832481-0.0009886166080832481j), (-0.0007480768254026771-0.0007480768254026771j), (0.00018211957649327815+0.00018211957649327815j), (0.0012042406015098095+0.0012042406015098095j), (0.0020200139842927456+0.0020200139842927456j), (0.0023816542234271765+0.0023816542234271765j), (0.002195809967815876+0.002195809967815876j), (0.0012113333214074373+0.0012113333214074373j), (-0.00014088614261709154-0.00014088614261709154j), (-0.0012574587017297745-0.0012574587017297745j)) - taps = gr.firdes.low_pass(1, 1, 0.1, 0.01) + taps = filter.firdes.low_pass(1, 1, 0.1, 0.01) src = gr.vector_source_c(src_data) op = filter.fir_filter_ccc(1, taps) dst = gr.vector_sink_c() diff --git a/gr-filter/python/qa_firdes.py b/gr-filter/python/qa_firdes.py new file mode 100755 index 000000000..cfd10435f --- /dev/null +++ b/gr-filter/python/qa_firdes.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import sys + +class test_firdes(gr_unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_low_pass(self): + known_taps = (0.0030193300917744637, -0.004960992839187384, + 0.006678304169327021, -1.132049690556083e-17, + -0.0251916591078043, 0.07206480950117111, + -0.13062666356563568, 0.18007083237171173, + 0.7978920936584473, 0.18007083237171173, + -0.13062666356563568, 0.07206480950117111, + -0.0251916591078043, -1.132049690556083e-17, + 0.006678304169327021, -0.004960992839187384, + 0.0030193300917744637) + new_taps = filter.firdes.low_pass(1, 1, 0.4, 0.2) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_low_pass_2(self): + known_taps = (0.0024871660862118006, -4.403502608370943e-18, + -0.014456653036177158, 0.0543283149600029, + -0.116202212870121, 0.17504146695137024, + 0.7976038455963135, 0.17504146695137024, + -0.116202212870121, 0.0543283149600029, + -0.014456653036177158, -4.403502608370943e-18, + 0.0024871660862118006) + new_taps = filter.firdes.low_pass_2(1, 1, 0.4, 0.2, 60) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_high_pass(self): + known_taps = (-0.003062003292143345, 0.005031108390539885, + -0.0067726909182965755, 1.1480492661182674e-17, + 0.025547700002789497, -0.0730833187699318, + 0.13247284293174744, -0.18261581659317017, + 0.20229223370552063, -0.18261581659317017, + 0.13247284293174744, -0.0730833187699318, + 0.025547700002789497, 1.1480492661182674e-17, + -0.0067726909182965755, 0.005031108390539885, + -0.003062003292143345) + new_taps = filter.firdes.high_pass(1, 1, 0.4, 0.2) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_high_pass_2(self): + known_taps = (-0.0027197482995688915, 4.815287179370254e-18, + 0.01580853760242462, -0.05940871313214302, + 0.1270686239004135, -0.1914101094007492, + 0.21804752945899963, -0.1914101094007492, + 0.1270686239004135, -0.05940871313214302, + 0.01580853760242462, 4.815287179370254e-18, + -0.0027197482995688915) + new_taps = filter.firdes.high_pass_2(1, 1, 0.4, 0.2, 60) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_band_pass(self): + known_taps = (0.004961997736245394, -0.008152946829795837, + -0.004192151129245758, -5.749020235348687e-18, + 0.01581347920000553, 0.11843203753232956, + -0.21467317640781403, -0.11303528398275375, + 0.40520283579826355, -0.11303528398275375, + -0.21467317640781403, 0.11843203753232956, + 0.01581347920000553, -5.749020235348687e-18, + -0.004192151129245758, -0.008152946829795837, + 0.004961997736245394) + new_taps = filter.firdes.band_pass(1, 1, 0.2, 0.4, 0.2) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_band_pass_2(self): + known_taps = (-0.001676854444667697, -2.4018533253972557e-18, + 0.009746716357767582, 0.09589414298534393, + -0.20510689914226532, -0.11801345646381378, + 0.4350462853908539, -0.11801345646381378, + -0.20510689914226532, 0.09589414298534393, + 0.009746716357767582, -2.4018533253972557e-18, + -0.001676854444667697) + new_taps = filter.firdes.band_pass_2(1, 1, 0.2, 0.4, 0.2, 60) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_complex_band_pass(self): + known_taps = ((0.0024772135075181723+0.0017997993854805827j), + (-0.004070250317454338+0.002957213670015335j), + (-0.0020928815938532352-0.006441210396587849j), + (-2.8701231652956686e-18+2.805614574993832e-24j), + (0.007894645445048809-0.024297315627336502j), + (0.05912570655345917+0.04295721650123596j), + (-0.10717268288135529+0.07786571979522705j), + (-0.0564316064119339-0.17367789149284363j), + (0.20229223370552063-2.4115112751132983e-07j), + (-0.05643119290471077+0.17367802560329437j), + (-0.10717286914587021-0.07786546647548676j), + (0.05912560224533081-0.0429573580622673j), + (0.007894691079854965+0.024297300726175308j), + (-2.8701231652956686e-18+2.6687109203363464e-24j), + (-0.0020928694866597652+0.006441214121878147j), + (-0.004070255905389786-0.0029572059866040945j), + (0.0024772100150585175-0.0017998040420934558j)) + new_taps = filter.firdes.complex_band_pass(1, 1, 0.2, 0.4, 0.2) + self.assertComplexTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_complex_band_pass_2(self): + known_taps = ((-0.0008404505206272006-0.0025866336654871702j), + (-1.2038217948425635e-18+1.1767648157397848e-24j), + (0.0048850891180336475-0.015034818090498447j), + (0.048062704503536224+0.03491950035095215j), + (-0.10280057787895203+0.07468919456005096j), + (-0.05914920195937157-0.18204176425933838j), + (0.21804752945899963-2.5993290364567656e-07j), + (-0.059148769825696945+0.18204189836978912j), + (-0.10280075669288635-0.07468894869089127j), + (0.04806262254714966-0.0349196158349514j), + (0.004885117989033461+0.015034808777272701j), + (-1.2038217948425635e-18+1.1193430388030685e-24j), + (-0.000840445572976023+0.002586635295301676j)) + new_taps = filter.firdes.complex_band_pass_2(1, 1, 0.2, 0.4, 0.2, 60) + self.assertComplexTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_band_reject(self): + known_taps = (-0.004915320314466953, 0.008076251484453678, + 0.00415271520614624, 5.694938753309664e-18, + -0.01566472090780735, -0.11731793731451035, + 0.2126537412405014, 0.11197195947170258, + 0.6020866632461548, 0.11197195947170258, + 0.2126537412405014, -0.11731793731451035, + -0.01566472090780735, 5.694938753309664e-18, + 0.00415271520614624, 0.008076251484453678, + -0.004915320314466953) + new_taps = filter.firdes.band_reject(1, 1, 0.2, 0.4, 0.2) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_band_reject_2(self): + known_taps = (0.0015371545450761914, 2.201753372137003e-18, + -0.00893471110612154, -0.08790513873100281, + 0.1880193054676056, 0.1081816703081131, + 0.5982034206390381, 0.1081816703081131, + 0.1880193054676056, -0.08790513873100281, + -0.00893471110612154, 2.201753372137003e-18, + 0.0015371545450761914) + new_taps = filter.firdes.band_reject_2(1, 1, 0.2, 0.4, 0.2, 60) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_hilbert(self): + known_taps = (-0.010056184604763985, 0.0, + -0.08335155993700027, 0.0, + -0.5732954144477844, 0.0, + 0.5732954144477844, 0.0, + 0.08335155993700027, 0.0, + 0.010056184604763985) + new_taps = filter.firdes.hilbert(11) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_root_raised_cosine(self): + known_taps = (-0.04609205573797226, -0.02069387212395668, + 0.050548505038022995, 0.14850808680057526, + 0.23387153446674347, 0.2677156329154968, + 0.23387153446674347, 0.14850808680057526, + 0.050548505038022995, -0.02069387212395668, + -0.04609205573797226) + new_taps = filter.firdes.root_raised_cosine(1, 4, 1, 0.35, 11) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + + def test_gaussian(self): + known_taps = (0.0003600157215259969, 0.0031858310103416443, + 0.0182281993329525, 0.06743486225605011, + 0.16130395233631134, 0.24947398900985718, + 0.24947398900985718, 0.16130395233631134, + 0.06743486225605011, 0.0182281993329525, + 0.0031858310103416443, 0.0003600157215259969, + 2.630509879963938e-05) + new_taps = filter.firdes.gaussian(1, 4, 0.35, 13) + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + +if __name__ == '__main__': + gr_unittest.run(test_firdes, "test_firdes.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 6010455b7..a44131931 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -28,6 +28,7 @@ %include "gr_filter_swig_doc.i" %{ +#include "filter/firdes.h" #include "filter/fir_filter_fff.h" #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" @@ -35,6 +36,7 @@ #include "filter/fft_filter_fff.h" %} +%include "filter/firdes.h" %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" -- cgit From 77d5097c7df9494ee7e215d9dbf29d185ffbe5ed Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sat, 5 May 2012 13:35:40 -0400 Subject: filter: cleanup and moving calls from gr.firdes to filter.firdes. --- gr-filter/examples/fft_filter_ccc.py | 6 +++--- gr-filter/examples/fir_filter_ccc.py | 2 +- gr-filter/examples/fir_filter_fff.py | 2 +- gr-filter/lib/fft_filter_ccc_impl.cc | 1 - gr-filter/lib/fft_filter_fff_impl.cc | 1 - 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/gr-filter/examples/fft_filter_ccc.py b/gr-filter/examples/fft_filter_ccc.py index 0844c88ef..cc70e2b15 100755 --- a/gr-filter/examples/fft_filter_ccc.py +++ b/gr-filter/examples/fft_filter_ccc.py @@ -28,9 +28,9 @@ class example_fft_filter_ccc(gr.top_block): self._tw = tw self._at = atten self._decim = D - taps = gr.firdes.complex_band_pass_2(1, self._fs, - self._bw0, self._bw1, - self._tw, self._at) + taps = filter.firdes.complex_band_pass_2(1, self._fs, + self._bw0, self._bw1, + self._tw, self._at) print "Num. Taps: ", len(taps) self.src = gr.noise_source_c(gr.GR_GAUSSIAN, 1) diff --git a/gr-filter/examples/fir_filter_ccc.py b/gr-filter/examples/fir_filter_ccc.py index 57df868f7..b5a461d2c 100755 --- a/gr-filter/examples/fir_filter_ccc.py +++ b/gr-filter/examples/fir_filter_ccc.py @@ -27,7 +27,7 @@ class example_fir_filter_ccc(gr.top_block): self._tw = tw self._at = atten self._decim = D - taps = gr.firdes.low_pass_2(1, self._fs, self._bw, self._tw, self._at) + taps = filter.firdes.low_pass_2(1, self._fs, self._bw, self._tw, self._at) print "Num. Taps: ", len(taps) self.src = gr.noise_source_c(gr.GR_GAUSSIAN, 1) diff --git a/gr-filter/examples/fir_filter_fff.py b/gr-filter/examples/fir_filter_fff.py index 1ed889fcf..983088622 100755 --- a/gr-filter/examples/fir_filter_fff.py +++ b/gr-filter/examples/fir_filter_fff.py @@ -27,7 +27,7 @@ class example_fir_filter_fff(gr.top_block): self._tw = tw self._at = atten self._decim = D - taps = gr.firdes.low_pass_2(1, self._fs, self._bw, self._tw, self._at) + taps = filter.firdes.low_pass_2(1, self._fs, self._bw, self._tw, self._at) print "Num. Taps: ", len(taps) self.src = gr.noise_source_f(gr.GR_GAUSSIAN, 1) diff --git a/gr-filter/lib/fft_filter_ccc_impl.cc b/gr-filter/lib/fft_filter_ccc_impl.cc index 2721f128e..0a2002991 100644 --- a/gr-filter/lib/fft_filter_ccc_impl.cc +++ b/gr-filter/lib/fft_filter_ccc_impl.cc @@ -30,7 +30,6 @@ #include #include #include -#include namespace gr { namespace filter { diff --git a/gr-filter/lib/fft_filter_fff_impl.cc b/gr-filter/lib/fft_filter_fff_impl.cc index 95b82aae5..1d6eb02db 100644 --- a/gr-filter/lib/fft_filter_fff_impl.cc +++ b/gr-filter/lib/fft_filter_fff_impl.cc @@ -30,7 +30,6 @@ #include #include #include -#include namespace gr { namespace filter { -- cgit From e832b09166547e380972f48b2317d96a25d3d0e5 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sat, 5 May 2012 14:41:03 -0400 Subject: filter: added Parks-McClellen algorithm. Renamed gr_remez to pm_remez here. Added QA code and fixed coding style. --- gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/pm_remez.h | 72 +++ gr-filter/lib/CMakeLists.txt | 3 +- gr-filter/lib/pm_remez.cc | 834 ++++++++++++++++++++++++++++++++ gr-filter/lib/test_gr_filter.cc | 1 + gr-filter/python/qa_pm_remez.py | 188 +++++++ gr-filter/swig/filter_swig.i | 2 + 7 files changed, 1100 insertions(+), 1 deletion(-) create mode 100644 gr-filter/include/filter/pm_remez.h create mode 100644 gr-filter/lib/pm_remez.cc create mode 100755 gr-filter/python/qa_pm_remez.py diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 108509e9a..cdb07b2f9 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -76,6 +76,7 @@ add_custom_target(filter_generated_includes DEPENDS install(FILES api.h firdes.h + pm_remez.h fir_filter.h fft_filter.h ${generated_includes} diff --git a/gr-filter/include/filter/pm_remez.h b/gr-filter/include/filter/pm_remez.h new file mode 100644 index 000000000..a57e9e276 --- /dev/null +++ b/gr-filter/include/filter/pm_remez.h @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_PM_REMEZ_H +#define INCLUDED_FILTER_PM_REMEZ_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + /*! + * \brief Parks-McClellan FIR filter design using Remez algorithm. + * + * \ingroup filter_design + * + * Calculates the optimal (in the Chebyshev/minimax sense) FIR + * filter inpulse reponse given a set of band edges, the desired + * reponse on those bands, and the weight given to the error in + * those bands. + * + * \param order filter order (number of taps in the returned filter - 1) + * \param bands frequency at the band edges [ b1 e1 b2 e2 b3 e3 ...] + * \param ampl desired amplitude at the band edges [ a(b1) a(e1) a(b2) a(e2) ...] + * \param error_weight weighting applied to each band (usually 1) + * \param filter_type one of "bandpass", "hilbert" or "differentiator" + * \param grid_density determines how accurately the filter will be constructed. \ + * The minimum value is 16; higher values are slower to compute. + * + * Frequency is in the range [0, 1], with 1 being the Nyquist + * frequency (Fs/2) + * + * \returns vector of computed taps + * + * \throws std::runtime_error if args are invalid or calculation + * fails to converge. + */ + + FILTER_API std::vector + pm_remez(int order, + const std::vector &bands, + const std::vector &l, + const std::vector &error_weight, + const std::string filter_type = "bandpass", + int grid_density = 16 + ) throw (std::runtime_error); + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_PM_REMEZ_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index f085987de..247d0604c 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -107,7 +107,8 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) list(APPEND filter_sources fir_filter.cc fft_filter.cc - firdes_impl.cc + firdes.cc + pm_remez.cc ${generated_sources} fft_filter_ccc_impl.cc fft_filter_fff_impl.cc diff --git a/gr-filter/lib/pm_remez.cc b/gr-filter/lib/pm_remez.cc new file mode 100644 index 000000000..e7136bd83 --- /dev/null +++ b/gr-filter/lib/pm_remez.cc @@ -0,0 +1,834 @@ +/************************************************************************** + * Parks-McClellan algorithm for FIR filter design (C version) + *------------------------------------------------- + * Copyright (c) 1995,1998 Jake Janovetz (janovetz@uiuc.edu) + * Copyright (c) 2004 Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA + * + * + * Sep 1999 - Paul Kienzle (pkienzle@cs.indiana.edu) + * Modified for use in octave as a replacement for the matlab function + * remez.mex. In particular, magnitude responses are required for all + * band edges rather than one per band, griddensity is a parameter, + * and errors are returned rather than printed directly. + * Mar 2000 - Kai Habel (kahacjde@linux.zrz.tu-berlin.de) + * Change: ColumnVector x=arg(i).vector_value(); + * to: ColumnVector x(arg(i).vector_value()); + * There appear to be some problems with the routine search. See comments + * therein [search for PAK:]. I haven't looked closely at the rest + * of the code---it may also have some problems. + *************************************************************************/ + +/* + * This code was extracted from octave.sf.net, and wrapped with + * GNU Radio glue. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#ifndef LOCAL_BUFFER +#include +#define LOCAL_BUFFER(T, buf, size) \ + std::vector buf ## _vector (size); \ + T *buf = &(buf ## _vector[0]) +#endif + +namespace gr { + namespace filter { + +#define CONST const +#define BANDPASS 1 +#define DIFFERENTIATOR 2 +#define HILBERT 3 + +#define NEGATIVE 0 +#define POSITIVE 1 + +#define Pi 3.14159265358979323846 +#define Pi2 (2*Pi) + +#define GRIDDENSITY 16 +#define MAXITERATIONS 40 + + /******************* + * create_dense_grid + *================= + * + * Creates the dense grid of frequencies from the specified bands. + * Also creates the Desired Frequency Response function (D[]) and + * the Weight function (W[]) on that dense grid + * + * + * INPUT: + * ------ + * int r - 1/2 the number of filter coefficients + * int numtaps - Number of taps in the resulting filter + * int numband - Number of bands in user specification + * double bands[] - User-specified band edges [2*numband] + * double des[] - Desired response per band [2*numband] + * double weight[] - Weight per band [numband] + * int symmetry - Symmetry of filter - used for grid check + * int griddensity + * + * OUTPUT: + * ------- + * int gridsize - Number of elements in the dense frequency grid + * double Grid[] - Frequencies (0 to 0.5) on the dense grid [gridsize] + * double D[] - Desired response on the dense grid [gridsize] + * double W[] - Weight function on the dense grid [gridsize] + *******************/ + + static void + create_dense_grid(int r, int numtaps, int numband, const double bands[], + const double des[], const double weight[], int gridsize, + double Grid[], double D[], double W[], + int symmetry, int griddensity) + { + int i, j, k, band; + double delf, lowf, highf, grid0; + + delf = 0.5/(griddensity*r); + + /* + * For differentiator, hilbert, + * symmetry is odd and Grid[0] = max(delf, bands[0]) + */ + grid0 = (symmetry == NEGATIVE) && (delf > bands[0]) ? delf : bands[0]; + + j=0; + for(band=0; band < numband; band++) { + lowf = (band==0 ? grid0 : bands[2*band]); + highf = bands[2*band + 1]; + k = (int)((highf - lowf)/delf + 0.5); /* .5 for rounding */ + for(i=0; i (0.5 - delf)) && + (numtaps % 2)) + { + Grid[gridsize-1] = 0.5-delf; + } + } + + + /******************** + * initial_guess + *============== + * Places Extremal Frequencies evenly throughout the dense grid. + * + * + * INPUT: + * ------ + * int r - 1/2 the number of filter coefficients + * int gridsize - Number of elements in the dense frequency grid + * + * OUTPUT: + * ------- + * int ext[] - Extremal indexes to dense frequency grid [r+1] + ********************/ + + static void + initial_guess(int r, int Ext[], int gridsize) + { + int i; + + for(i=0; i<=r; i++) + Ext[i] = i * (gridsize-1) / r; + } + + + /*********************** + * calc_parms + *=========== + * + * + * INPUT: + * ------ + * int r - 1/2 the number of filter coefficients + * int Ext[] - Extremal indexes to dense frequency grid [r+1] + * double Grid[] - Frequencies (0 to 0.5) on the dense grid [gridsize] + * double D[] - Desired response on the dense grid [gridsize] + * double W[] - Weight function on the dense grid [gridsize] + * + * OUTPUT: + * ------- + * double ad[] - 'b' in Oppenheim & Schafer [r+1] + * double x[] - [r+1] + * double y[] - 'C' in Oppenheim & Schafer [r+1] + ***********************/ + + static void + calc_parms(int r, int Ext[], double Grid[], double D[], double W[], + double ad[], double x[], double y[]) + { + int i, j, k, ld; + double sign, xi, delta, denom, numer; + + /* + * Find x[] + */ + for(i = 0; i <= r; i++) + x[i] = cos(Pi2 * Grid[Ext[i]]); + + /* + * Calculate ad[] - Oppenheim & Schafer eq 7.132 + */ + ld = (r-1)/15 + 1; /* Skips around to avoid round errors */ + for(i = 0; i <= r; i++) { + denom = 1.0; + xi = x[i]; + for(j = 0; j < ld; j++) { + for(k = j; k <= r; k += ld) + if(k != i) + denom *= 2.0*(xi - x[k]); + } + if(fabs(denom) < 0.00001) + denom = 0.00001; + ad[i] = 1.0/denom; + } + + /* + * Calculate delta - Oppenheim & Schafer eq 7.131 + */ + numer = denom = 0; + sign = 1; + for(i = 0; i <= r; i++) { + numer += ad[i] * D[Ext[i]]; + denom += sign * ad[i]/W[Ext[i]]; + sign = -sign; + } + delta = numer/denom; + sign = 1; + + /* + * Calculate y[] - Oppenheim & Schafer eq 7.133b + */ + for(i = 0; i <= r; i++) { + y[i] = D[Ext[i]] - sign * delta/W[Ext[i]]; + sign = -sign; + } + } + + + /********************* + * compute_A + *========== + * Using values calculated in calc_parms, compute_A calculates the + * actual filter response at a given frequency (freq). Uses + * eq 7.133a from Oppenheim & Schafer. + * + * + * INPUT: + * ------ + * double freq - Frequency (0 to 0.5) at which to calculate A + * int r - 1/2 the number of filter coefficients + * double ad[] - 'b' in Oppenheim & Schafer [r+1] + * double x[] - [r+1] + * double y[] - 'C' in Oppenheim & Schafer [r+1] + * + * OUTPUT: + * ------- + * Returns double value of A[freq] + *********************/ + + static double + compute_A(double freq, int r, double ad[], double x[], double y[]) + { + int i; + double xc, c, denom, numer; + + denom = numer = 0; + xc = cos(Pi2 * freq); + for(i = 0; i <= r; i++) { + c = xc - x[i]; + if(fabs(c) < 1.0e-7) { + numer = y[i]; + denom = 1; + break; + } + c = ad[i]/c; + denom += c; + numer += c*y[i]; + } + return numer/denom; + } + + + /************************ + * calc_error + *=========== + * Calculates the Error function from the desired frequency response + * on the dense grid (D[]), the weight function on the dense grid (W[]), + * and the present response calculation (A[]) + * + * + * INPUT: + * ------ + * int r - 1/2 the number of filter coefficients + * double ad[] - [r+1] + * double x[] - [r+1] + * double y[] - [r+1] + * int gridsize - Number of elements in the dense frequency grid + * double Grid[] - Frequencies on the dense grid [gridsize] + * double D[] - Desired response on the dense grid [gridsize] + * double W[] - Weight function on the desnse grid [gridsize] + * + * OUTPUT: + * ------- + * double E[] - Error function on dense grid [gridsize] + ************************/ + + static void + calc_error(int r, double ad[], double x[], double y[], + int gridsize, double Grid[], + double D[], double W[], double E[]) + { + int i; + double A; + + for(i = 0; i < gridsize; i++) { + A = compute_A(Grid[i], r, ad, x, y); + E[i] = W[i] * (D[i] - A); + } + } + + /************************ + * search + *======== + * Searches for the maxima/minima of the error curve. If more than + * r+1 extrema are found, it uses the following heuristic (thanks + * Chris Hanson): + * 1) Adjacent non-alternating extrema deleted first. + * 2) If there are more than one excess extrema, delete the + * one with the smallest error. This will create a non-alternation + * condition that is fixed by 1). + * 3) If there is exactly one excess extremum, delete the smaller + * of the first/last extremum + * + * + * INPUT: + * ------ + * int r - 1/2 the number of filter coefficients + * int Ext[] - Indexes to Grid[] of extremal frequencies [r+1] + * int gridsize - Number of elements in the dense frequency grid + * double E[] - Array of error values. [gridsize] + * OUTPUT: + * ------- + * int Ext[] - New indexes to extremal frequencies [r+1] + ************************/ + static int + search(int r, int Ext[], + int gridsize, double E[]) + { + int i, j, k, l, extra; /* Counters */ + int up, alt; + int *foundExt; /* Array of found extremals */ + + /* + * Allocate enough space for found extremals. + */ + foundExt = (int *)malloc((2*r) * sizeof(int)); + k = 0; + + /* + * Check for extremum at 0. + */ + if(((E[0] > 0.0) && (E[0] > E[1])) || + ((E[0] < 0.0) && (E[0] < E[1]))) + foundExt[k++] = 0; + + /* + * Check for extrema inside dense grid + */ + for(i = 1; i < gridsize-1; i++) { + if(((E[i] >= E[i-1]) && (E[i] > E[i+1]) && (E[i] > 0.0)) || + ((E[i] <= E[i-1]) && (E[i] < E[i+1]) && (E[i] < 0.0))) { + // PAK: we sometimes get too many extremal frequencies + if(k >= 2*r) + return -3; + foundExt[k++] = i; + } + } + + /* + * Check for extremum at 0.5 + */ + j = gridsize-1; + if(((E[j] > 0.0) && (E[j] > E[j-1])) || + ((E[j] < 0.0) && (E[j] < E[j-1]))) { + if(k >= 2*r) + return -3; + foundExt[k++] = j; + } + + // PAK: we sometimes get not enough extremal frequencies + if(k < r+1) + return -2; + + /* + * Remove extra extremals + */ + extra = k - (r+1); + assert(extra >= 0); + + while(extra > 0) { + if(E[foundExt[0]] > 0.0) + up = 1; /* first one is a maxima */ + else + up = 0; /* first one is a minima */ + + l=0; + alt = 1; + for(j = 1; j < k; j++) { + if(fabs(E[foundExt[j]]) < fabs(E[foundExt[l]])) + l = j; /* new smallest error. */ + if((up) && (E[foundExt[j]] < 0.0)) + up = 0; /* switch to a minima */ + else if((!up) && (E[foundExt[j]] > 0.0)) + up = 1; /* switch to a maxima */ + else { + alt = 0; + // PAK: break now and you will delete the smallest overall + // extremal. If you want to delete the smallest of the + // pair of non-alternating extremals, then you must do: + // + // if(fabs(E[foundExt[j]]) < fabs(E[foundExt[j-1]])) l=j; + // else l=j-1; + break; /* Ooops, found two non-alternating */ + } /* extrema. Delete smallest of them */ + } /* if the loop finishes, all extrema are alternating */ + + /* + * If there's only one extremal and all are alternating, + * delete the smallest of the first/last extremals. + */ + if((alt) && (extra == 1)) { + if(fabs(E[foundExt[k-1]]) < fabs(E[foundExt[0]])) + /* Delete last extremal */ + l = k-1; + // PAK: changed from l = foundExt[k-1]; + else + /* Delete first extremal */ + l = 0; + // PAK: changed from l = foundExt[0]; + } + + for(j = l; j < k-1; j++) { /* Loop that does the deletion */ + foundExt[j] = foundExt[j+1]; + assert(foundExt[j] max) + max = current; + } + return(((max-min)/max) < 0.0001); + } + + /******************** + * remez + *======= + * Calculates the optimal (in the Chebyshev/minimax sense) + * FIR filter impulse response given a set of band edges, + * the desired reponse on those bands, and the weight given to + * the error in those bands. + * + * INPUT: + * ------ + * int numtaps - Number of filter coefficients + * int numband - Number of bands in filter specification + * double bands[] - User-specified band edges [2 * numband] + * double des[] - User-specified band responses [2 * numband] + * double weight[] - User-specified error weights [numband] + * int type - Type of filter + * + * OUTPUT: + * ------- + * double h[] - Impulse response of final filter [numtaps] + * returns - true on success, false on failure to converge + ********************/ + + static int + remez(double h[], int numtaps, + int numband, const double bands[], + const double des[], const double weight[], + int type, int griddensity) + { + double *Grid, *W, *D, *E; + int i, iter, gridsize, r, *Ext; + double *taps, c; + double *x, *y, *ad; + int symmetry; + + if(type == BANDPASS) + symmetry = POSITIVE; + else + symmetry = NEGATIVE; + + r = numtaps/2; /* number of extrema */ + if((numtaps % 2) && (symmetry == POSITIVE)) + r++; + + /* + * Predict dense grid size in advance for memory allocation + * .5 is so we round up, not truncate + */ + gridsize = 0; + for(i = 0; i < numband; i++) { + gridsize +=(int)(2*r*griddensity*(bands[2*i+1] - bands[2*i]) + .5); + } + if(symmetry == NEGATIVE) { + gridsize--; + } + + /* + * Dynamically allocate memory for arrays with proper sizes + */ + Grid = (double *)malloc(gridsize * sizeof(double)); + D = (double *)malloc(gridsize * sizeof(double)); + W = (double *)malloc(gridsize * sizeof(double)); + E = (double *)malloc(gridsize * sizeof(double)); + Ext = (int *)malloc((r+1) * sizeof(int)); + taps = (double *)malloc((r+1) * sizeof(double)); + x = (double *)malloc((r+1) * sizeof(double)); + y = (double *)malloc((r+1) * sizeof(double)); + ad = (double *)malloc((r+1) * sizeof(double)); + + /* + * Create dense frequency grid + */ + create_dense_grid(r, numtaps, numband, bands, des, weight, + gridsize, Grid, D, W, symmetry, griddensity); + initial_guess(r, Ext, gridsize); + + /* + * For Differentiator: (fix grid) + */ + if(type == DIFFERENTIATOR) { + for(i = 0; i < gridsize; i++) { + /* D[i] = D[i]*Grid[i]; */ + if(D[i] > 0.0001) + W[i] = W[i]/Grid[i]; + } + } + + /* + * For odd or Negative symmetry filters, alter the + * D[] and W[] according to Parks McClellan + */ + if(symmetry == POSITIVE) { + if(numtaps % 2 == 0) { + for(i = 0; i < gridsize; i++) { + c = cos(Pi * Grid[i]); + D[i] /= c; + W[i] *= c; + } + } + } + else { + if(numtaps % 2) { + for(i = 0; i < gridsize; i++) { + c = sin(Pi2 * Grid[i]); + D[i] /= c; + W[i] *= c; + } + } + else { + for(i = 0; i < gridsize; i++) { + c = sin(Pi * Grid[i]); + D[i] /= c; + W[i] *= c; + } + } + } + + /* + * Perform the Remez Exchange algorithm + */ + for(iter = 0; iter < MAXITERATIONS; iter++) { + calc_parms(r, Ext, Grid, D, W, ad, x, y); + calc_error(r, ad, x, y, gridsize, Grid, D, W, E); + int err = search(r, Ext, gridsize, E); + if(err) + return err; + for(int i = 0; i <= r; i++) + assert(Ext[i] < gridsize); + if(is_done(r, Ext, E)) + break; + } + + calc_parms(r, Ext, Grid, D, W, ad, x, y); + + /* + * Find the 'taps' of the filter for use with Frequency + * Sampling. If odd or Negative symmetry, fix the taps + * according to Parks McClellan + */ + for(i = 0; i <= numtaps/2; i++) { + if(symmetry == POSITIVE) { + if(numtaps % 2) + c = 1; + else + c = cos(Pi * (double)i/numtaps); + } + else { + if(numtaps % 2) + c = sin(Pi2 * (double)i/numtaps); + else + c = sin(Pi * (double)i/numtaps); + } + taps[i] = compute_A((double)i/numtaps, r, ad, x, y)*c; + } + + /* + * Frequency sampling design with calculated taps + */ + freq_sample(numtaps, taps, h, symmetry); + + /* + * Delete allocated memory + */ + free(Grid); + free(W); + free(D); + free(E); + free(Ext); + free(x); + free(y); + free(ad); + + return iter + pm_remez(int order, + const std::vector &arg_bands, + const std::vector &arg_response, + const std::vector &arg_weight, + const std::string filter_type, + int grid_density + ) throw (std::runtime_error) + { + int numtaps = order + 1; + if(numtaps < 4) + punt("gr_remez: number of taps must be >= 3"); + + int numbands = arg_bands.size() / 2; + LOCAL_BUFFER(double, bands, numbands * 2); + if(numbands < 1 || arg_bands.size() % 2 == 1) + punt("gr_remez: must have an even number of band edges"); + + for(unsigned int i = 1; i < arg_bands.size(); i++){ + if(arg_bands[i] < arg_bands[i-1]) + punt("gr_remez: band edges must be nondecreasing"); + } + + if(arg_bands[0] < 0 || arg_bands[arg_bands.size() - 1] > 1) + punt("gr_remez: band edges must be in the range [0,1]"); + + // Divide by 2 to fit with the implementation that uses a + // sample rate of [0, 0.5] instead of [0, 1.0] + for(int i = 0; i < 2 * numbands; i++) + bands[i] = arg_bands[i] / 2; + + LOCAL_BUFFER(double, response, numbands * 2); + if(arg_response.size() != arg_bands.size()) + punt("gr_remez: must have one response magnitude for each band edge"); + + for(int i = 0; i < 2 * numbands; i++) + response[i] = arg_response[i]; + + LOCAL_BUFFER(double, weight, numbands); + for(int i = 0; i < numbands; i++) + weight[i] = 1.0; + + if(arg_weight.size() != 0) { + if((int) arg_weight.size() != numbands) + punt("gr_remez: need one weight for each band [=length(band)/2]"); + for(int i = 0; i < numbands; i++) + weight[i] = arg_weight [i]; + } + + int itype = 0; + if(filter_type == "bandpass") + itype = BANDPASS; + else if(filter_type == "differentiator") + itype = DIFFERENTIATOR; + else if(filter_type == "hilbert") + itype = HILBERT; + else + punt("gr_remez: unknown ftype '" + filter_type + "'"); + + if(grid_density < 16) + punt("gr_remez: grid_density is too low; must be >= 16"); + + LOCAL_BUFFER(double, coeff, numtaps + 5); // FIXME why + 5? + int err = remez(coeff, numtaps, numbands, + bands, response, weight, itype, grid_density); + + if(err == -1) + punt("gr_remez: failed to converge"); + + if(err == -2) + punt("gr_remez: insufficient extremals -- cannot continue"); + + if(err == -3) + punt("gr_remez: too many extremals -- cannot continue"); + + return std::vector(&coeff[0], &coeff[numtaps]); + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/test_gr_filter.cc b/gr-filter/lib/test_gr_filter.cc index 9027b7a99..915b6286b 100644 --- a/gr-filter/lib/test_gr_filter.cc +++ b/gr-filter/lib/test_gr_filter.cc @@ -25,6 +25,7 @@ #include #include +#include int main (int argc, char **argv) diff --git a/gr-filter/python/qa_pm_remez.py b/gr-filter/python/qa_pm_remez.py new file mode 100755 index 000000000..765e2ea6a --- /dev/null +++ b/gr-filter/python/qa_pm_remez.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import sys, math + +# ---------------------------------------------------------------- +# See optfir for an explanation of these. + +def stopband_atten_to_dev (atten_db): + """Convert a stopband attenuation in dB to an absolute value""" + return 10**(-atten_db/20) + +def passband_ripple_to_dev (ripple_db): + """Convert passband ripple spec expressed in dB to an absolute value""" + return (10**(ripple_db/20)-1)/(10**(ripple_db/20)+1) + +# ---------------------------------------------------------------- + +def remezord (fcuts, mags, devs, fsamp = 2): + ''' + FIR order estimator (lowpass, highpass, bandpass, mulitiband). + ''' + + # get local copies + fcuts = fcuts[:] + mags = mags[:] + devs = devs[:] + + for i in range (len (fcuts)): + fcuts[i] = float (fcuts[i]) / fsamp + + nf = len (fcuts) + nm = len (mags) + nd = len (devs) + nbands = nm + + if nm != nd: + raise ValueError, "Length of mags and devs must be equal" + + if nf != 2 * (nbands - 1): + raise ValueError, "Length of f must be 2 * len (mags) - 2" + + for i in range (len (mags)): + if mags[i] != 0: # if not stopband, get relative deviation + devs[i] = devs[i] / mags[i] + + # separate the passband and stopband edges + f1 = fcuts[0::2] + f2 = fcuts[1::2] + + n = 0 + min_delta = 2 + for i in range (len (f1)): + if f2[i] - f1[i] < min_delta: + n = i + min_delta = f2[i] - f1[i] + + if nbands == 2: + # lowpass or highpass case (use formula) + l = lporder (f1[n], f2[n], devs[0], devs[1]) + else: + # bandpass or multipass case + # try different lowpasses and take the worst one that + # goes through the BP specs + l = 0 + for i in range (1, nbands-1): + l1 = lporder (f1[i-1], f2[i-1], devs[i], devs[i-1]) + l2 = lporder (f1[i], f2[i], devs[i], devs[i+1]) + l = max (l, l1, l2) + + n = int (math.ceil (l)) - 1 # need order, not length for remez + + # cook up remez compatible result + ff = [0] + fcuts + [1] + for i in range (1, len (ff) - 1): + ff[i] *= 2 + + aa = [] + for a in mags: + aa = aa + [a, a] + + max_dev = max (devs) + wts = [1] * len(devs) + for i in range (len (wts)): + wts[i] = max_dev / devs[i] + + return (n, ff, aa, wts) + + +def lporder (freq1, freq2, delta_p, delta_s): + ''' + FIR lowpass filter length estimator. + ''' + df = abs (freq2 - freq1) + ddp = math.log10 (delta_p) + dds = math.log10 (delta_s) + + a1 = 5.309e-3 + a2 = 7.114e-2 + a3 = -4.761e-1 + a4 = -2.66e-3 + a5 = -5.941e-1 + a6 = -4.278e-1 + + b1 = 11.01217 + b2 = 0.5124401 + + t1 = a1 * ddp * ddp + t2 = a2 * ddp + t3 = a4 * ddp * ddp + t4 = a5 * ddp + + dinf=((t1 + t2 + a3) * dds) + (t3 + t4 + a6) + ff = b1 + b2 * (ddp - dds) + n = dinf / df - ff * df + 1 + return n + +# ---------------------------------------------------------------- + +class test_pm_remez(gr_unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_low_pass(self): + gain = 1 + Fs = 1 + freq1 = 0.1 + freq2 = 0.2 + passband_ripple_db = 0.01 + stopband_atten_db = 60 + + passband_dev = passband_ripple_to_dev(passband_ripple_db) + stopband_dev = stopband_atten_to_dev(stopband_atten_db) + desired_ampls = (gain, 0) + (n, fo, ao, w) = remezord([freq1, freq2], desired_ampls, + [passband_dev, stopband_dev], Fs) + new_taps = gr.remez(n + 2, fo, ao, w, "bandpass") + + known_taps = (-0.0008370135734511828, -0.0006622211673134374, + 0.0008501079576365787, 0.003059609130249229, + 0.003202235537205373, -0.001000899296974219, + -0.007589728680590891, -0.009790921118281865, + -0.001524210202628562, 0.014373535837200111, + 0.02392881326993834, 0.011798133085019008, + -0.021954446348997188, -0.05293436740264934, + -0.04375787096766848, 0.028038890498420392, + 0.14612655590172896, 0.25738578419108626, + 0.302967004188747, 0.25738578419108626, + 0.14612655590172896, 0.028038890498420392, + -0.04375787096766848, -0.05293436740264934, + -0.021954446348997188, 0.011798133085019008, + 0.02392881326993834, 0.014373535837200111, + -0.001524210202628562, -0.009790921118281865, + -0.007589728680590891, -0.001000899296974219, + 0.003202235537205373, 0.003059609130249229, + 0.0008501079576365787, -0.0006622211673134374, + -0.0008370135734511828) + + self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5) + +if __name__ == '__main__': + gr_unittest.run(test_pm_remez, "test_pm_remez.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index a44131931..fd9aaff68 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -29,6 +29,7 @@ %{ #include "filter/firdes.h" +#include "filter/pm_remez.h" #include "filter/fir_filter_fff.h" #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" @@ -37,6 +38,7 @@ %} %include "filter/firdes.h" +%include "filter/pm_remez.h" %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" -- cgit From 53f57b6730fa7579d34bf6387d50f894a46030d4 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sat, 5 May 2012 14:42:18 -0400 Subject: filter: actually run the C++ unit tests. --- gr-filter/lib/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 247d0604c..e8a2a04a7 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -146,4 +146,4 @@ target_link_libraries( ${Boost_LIBRARIES} ) -GR_ADD_TEST(test_gr_filter) +GR_ADD_TEST(test_gr_filter test-gr-filter) -- cgit From 897a60220a29fc509802e5e30e645e7ee154e184 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sat, 5 May 2012 17:52:28 -0400 Subject: filter: added hilbert xform block with QA and GRC block. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/hilbert_fc.xml | 26 +++++++ gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/hilbert_fc.h | 53 +++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/hilbert_fc_impl.cc | 70 +++++++++++++++++++ gr-filter/lib/hilbert_fc_impl.h | 53 +++++++++++++++ gr-filter/python/qa_hilbert.py | 117 ++++++++++++++++++++++++++++++++ gr-filter/swig/filter_swig.i | 3 + 10 files changed, 326 insertions(+) create mode 100644 gr-filter/grc/hilbert_fc.xml create mode 100644 gr-filter/include/filter/hilbert_fc.h create mode 100644 gr-filter/lib/hilbert_fc_impl.cc create mode 100644 gr-filter/lib/hilbert_fc_impl.h create mode 100755 gr-filter/python/qa_hilbert.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 9b40395b8..00171e6ff 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -20,6 +20,7 @@ install(FILES filter_block_tree.xml fft_filter_xxx.xml + hilbert_fc.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 7d0559857..c0c2e96e2 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -31,5 +31,6 @@ Filters fft_filter_xxx + hilbert_fc diff --git a/gr-filter/grc/hilbert_fc.xml b/gr-filter/grc/hilbert_fc.xml new file mode 100644 index 000000000..dd4c94b83 --- /dev/null +++ b/gr-filter/grc/hilbert_fc.xml @@ -0,0 +1,26 @@ + + + + Hilbert + hilbert_fc + from gnuradio import filter + filter.hilbert_fc($num_taps) + + Num Taps + num_taps + 64 + int + + + in + float + + + out + complex + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index cdb07b2f9..b0b2a7e91 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -81,6 +81,7 @@ install(FILES fft_filter.h ${generated_includes} fft_filter_ccc.h + hilbert_fc.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "fft_devel" ) diff --git a/gr-filter/include/filter/hilbert_fc.h b/gr-filter/include/filter/hilbert_fc.h new file mode 100644 index 000000000..d29f8a726 --- /dev/null +++ b/gr-filter/include/filter/hilbert_fc.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_HILBERT_FC_H +#define INCLUDED_FILTER_HILBERT_FC_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API hilbert_fc : virtual public gr_sync_block + { + public: + // gr::filter::hilbert_fc::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief Hilbert transformer. + * \ingroup filter_blk + * + * real output is input appropriately delayed. + * imaginary output is hilbert filtered (90 degree phase shift) + * version of input. + */ + static FILTER_API sptr make(unsigned int ntaps); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_HILBERT_FC_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index e8a2a04a7..6b33d547b 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -112,6 +112,7 @@ list(APPEND filter_sources ${generated_sources} fft_filter_ccc_impl.cc fft_filter_fff_impl.cc + hilbert_fc_impl.cc ) list(APPEND filter_libs diff --git a/gr-filter/lib/hilbert_fc_impl.cc b/gr-filter/lib/hilbert_fc_impl.cc new file mode 100644 index 000000000..946b47e4a --- /dev/null +++ b/gr-filter/lib/hilbert_fc_impl.cc @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "hilbert_fc_impl.h" +#include +#include + +namespace gr { + namespace filter { + + hilbert_fc::sptr hilbert_fc::make(unsigned int ntaps) + { + return gnuradio::get_initial_sptr(new hilbert_fc_impl(ntaps)); + } + + hilbert_fc_impl::hilbert_fc_impl(unsigned int ntaps) + : gr_sync_block ("hilbert_fc", + gr_make_io_signature (1, 1, sizeof(float)), + gr_make_io_signature (1, 1, sizeof(gr_complex))), + d_ntaps(ntaps | 0x1) // ensure ntaps is odd + { + d_hilb = new kernel::fir_filter_fff(1, firdes::hilbert(d_ntaps)); + set_history (d_ntaps); + } + + hilbert_fc_impl::~hilbert_fc_impl() + { + delete d_hilb; + } + + int + hilbert_fc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + float *in = (float *)input_items[0]; + gr_complex *out = (gr_complex *)output_items[0]; + + for(int i = 0; i < noutput_items; i++) + out[i] = gr_complex (in[i + d_ntaps/2], + d_hilb->filter (&in[i])); + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/hilbert_fc_impl.h b/gr-filter/lib/hilbert_fc_impl.h new file mode 100644 index 000000000..de4d754b4 --- /dev/null +++ b/gr-filter/lib/hilbert_fc_impl.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_HILBERT_FC_IMPL_H +#define INCLUDED_FILTER_HILBERT_FC_IMPL_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API hilbert_fc_impl : public hilbert_fc + { + private: + unsigned int d_ntaps; + kernel::fir_filter_fff *d_hilb; + + public: + hilbert_fc_impl(unsigned int ntaps); + + ~hilbert_fc_impl(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_HILBERT_FC_IMPL_H */ diff --git a/gr-filter/python/qa_hilbert.py b/gr-filter/python/qa_hilbert.py new file mode 100755 index 000000000..b460b6438 --- /dev/null +++ b/gr-filter/python/qa_hilbert.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_hilbert(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block () + + def tearDown(self): + self.tb = None + + def test_hilbert(self): + tb = self.tb + ntaps = 51 + sampling_freq = 100 + + expected_result = ( -1.4678005338941702e-11j, + -0.0011950774351134896j, + -0.0019336787518113852j, + -0.0034673355985432863j, + -0.0036765895783901215j, + -0.004916108213365078j, + -0.0042778430506587029j, + -0.006028641015291214j, + -0.005476709920912981j, + -0.0092810001224279404j, + -0.0095402700826525688j, + -0.016060983762145042j, + -0.016446959227323532j, + -0.02523401565849781j, + -0.024382550269365311j, + -0.035477779805660248j, + -0.033021725714206696j, + -0.048487484455108643j, + -0.04543270543217659j, + -0.069477587938308716j, + -0.066984444856643677j, + -0.10703597217798233j, + -0.10620346665382385j, + -0.1852707713842392j, + -0.19357112050056458j, + (7.2191945754696007e-09 -0.50004088878631592j), + (0.58778399229049683 -0.6155126690864563j), + (0.95105588436126709 -0.12377222627401352j), + (0.95105588436126709 +0.41524654626846313j), + (0.5877838134765625 +0.91611981391906738j), + (5.8516356205018383e-09 +1.0670661926269531j), + (-0.5877840518951416 +0.87856143712997437j), + (-0.95105588436126709 +0.35447561740875244j), + (-0.95105588436126709 -0.26055556535720825j), + (-0.5877838134765625 -0.77606213092803955j), + (-8.7774534307527574e-09 -0.96460390090942383j), + (0.58778399229049683 -0.78470128774642944j), + (0.95105588436126709 -0.28380891680717468j), + (0.95105588436126709 +0.32548999786376953j), + (0.5877838134765625 +0.82514488697052002j), + (1.4629089051254596e-08 +1.0096219778060913j), + (-0.5877840518951416 +0.81836479902267456j), + (-0.95105588436126709 +0.31451958417892456j), + (-0.95105588436126709 -0.3030143678188324j), + (-0.5877838134765625 -0.80480599403381348j), + (-1.7554906861505515e-08 -0.99516552686691284j), + (0.58778399229049683 -0.80540722608566284j), + (0.95105582475662231 -0.30557557940483093j), + (0.95105588436126709 +0.31097668409347534j), + (0.5877838134765625 +0.81027895212173462j), + (2.3406542482007353e-08 +1.0000816583633423j), + (-0.5877840518951416 +0.80908381938934326j), + (-0.95105588436126709 +0.30904293060302734j), + (-0.95105588436126709 -0.30904296040534973j), + (-0.5877838134765625 -0.80908387899398804j), + (-2.6332360292258272e-08 -1.0000815391540527j), + (0.58778399229049683 -0.80908381938934326j), + (0.95105582475662231 -0.30904299020767212j), + (0.95105588436126709 +0.30904293060302734j), + (0.5877838134765625 +0.80908381938934326j), + (3.218399768911695e-08 +1.0000815391540527j)) + + + src1 = gr.sig_source_f(sampling_freq, gr.GR_SIN_WAVE, + sampling_freq * 0.10, 1.0) + + head = gr.head(gr.sizeof_float, int (ntaps + sampling_freq * 0.10)) + hilb = filter.hilbert_fc(ntaps) + dst1 = gr.vector_sink_c() + tb.connect(src1, head) + tb.connect(head, hilb) + tb.connect(hilb, dst1) + tb.run() + dst_data = dst1.data() + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + +if __name__ == '__main__': + gr_unittest.run(test_hilbert, "test_hilbert.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index fd9aaff68..558565fc4 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -35,6 +35,7 @@ #include "filter/fir_filter_ccc.h" #include "filter/fft_filter_ccc.h" #include "filter/fft_filter_fff.h" +#include "filter/hilbert_fc.h" %} %include "filter/firdes.h" @@ -44,9 +45,11 @@ %include "filter/fir_filter_ccc.h" %include "filter/fft_filter_ccc.h" %include "filter/fft_filter_fff.h" +%include "filter/hilbert_fc.h" GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); +GR_SWIG_BLOCK_MAGIC2(filter, hilbert_fc); -- cgit From 137cd17199b62409591acff84476605f64e15ff8 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 6 May 2012 16:24:43 -0400 Subject: filter: moved dc_blocker to gr-filter with QA and GRC blocks. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/dc_blocker_xx.xml | 51 +++++++++++ gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/include/filter/CMakeLists.txt | 3 + gr-filter/include/filter/dc_blocker_cc.h | 75 ++++++++++++++++ gr-filter/include/filter/dc_blocker_ff.h | 75 ++++++++++++++++ gr-filter/lib/CMakeLists.txt | 2 + gr-filter/lib/dc_blocker_cc_impl.cc | 144 +++++++++++++++++++++++++++++++ gr-filter/lib/dc_blocker_cc_impl.h | 76 ++++++++++++++++ gr-filter/lib/dc_blocker_ff_impl.cc | 142 ++++++++++++++++++++++++++++++ gr-filter/lib/dc_blocker_ff_impl.h | 76 ++++++++++++++++ gr-filter/python/qa_dc_blocker.py | 109 +++++++++++++++++++++++ gr-filter/swig/filter_swig.i | 6 ++ 13 files changed, 761 insertions(+) create mode 100644 gr-filter/grc/dc_blocker_xx.xml create mode 100644 gr-filter/include/filter/dc_blocker_cc.h create mode 100644 gr-filter/include/filter/dc_blocker_ff.h create mode 100644 gr-filter/lib/dc_blocker_cc_impl.cc create mode 100644 gr-filter/lib/dc_blocker_cc_impl.h create mode 100644 gr-filter/lib/dc_blocker_ff_impl.cc create mode 100644 gr-filter/lib/dc_blocker_ff_impl.h create mode 100755 gr-filter/python/qa_dc_blocker.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 00171e6ff..5241680e8 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -19,6 +19,7 @@ install(FILES filter_block_tree.xml + dc_blocker_xx.xml fft_filter_xxx.xml hilbert_fc.xml DESTINATION ${GRC_BLOCKS_DIR} diff --git a/gr-filter/grc/dc_blocker_xx.xml b/gr-filter/grc/dc_blocker_xx.xml new file mode 100644 index 000000000..9bce5e980 --- /dev/null +++ b/gr-filter/grc/dc_blocker_xx.xml @@ -0,0 +1,51 @@ + + + + DC Blocker + dc_blocker_xx + from gnuradio import filter + filter.dc_blocker_$(type)($length, $long_form) + + + Type + type + enum + + + + + Length + length + 32 + int + + + Long Form + long_form + True + bool + + + + in + $type.input + + + out + $type.output + + diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index c0c2e96e2..709aa89aa 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -30,6 +30,7 @@ Filters + dc_blocker_xx fft_filter_xxx hilbert_fc diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index b0b2a7e91..402dd599a 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -80,7 +80,10 @@ install(FILES fir_filter.h fft_filter.h ${generated_includes} + dc_blocker_cc.h + dc_blocker_ff.h fft_filter_ccc.h + fft_filter_fff.h hilbert_fc.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "fft_devel" diff --git a/gr-filter/include/filter/dc_blocker_cc.h b/gr-filter/include/filter/dc_blocker_cc.h new file mode 100644 index 000000000..64d7ebffc --- /dev/null +++ b/gr-filter/include/filter/dc_blocker_cc.h @@ -0,0 +1,75 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_DC_BLOCKER_CC_H +#define INCLUDED_FILTER_DC_BLOCKER_CC_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API dc_blocker_cc : virtual public gr_sync_block + { + public: + + // gr::filter::dc_blocker_cc::sptr + typedef boost::shared_ptr sptr; + + /*! + * \class gr_dc_blocker_cc + * \brief a computationally efficient controllable DC blocker + * + * \ingroup filter_blk + * + * This block implements a computationally efficient DC blocker that produces + * a tighter notch filter around DC for a smaller group delay than an + * equivalent FIR filter or using a single pole IIR filter (though the IIR + * filter is computationally cheaper). + * + * The block defaults to using a delay line of length 32 and the long form + * of the filter. Optionally, the delay line length can be changed to alter + * the width of the DC notch (longer lines will decrease the width). + * + * The long form of the filter produces a nearly flat response outside of + * the notch but at the cost of a group delay of 2D-2. + * + * The short form of the filter does not have as flat a response in the + * passband but has a group delay of only D-1 and is cheaper to compute. + * + * The theory behind this block can be found in the paper: + * + * R. Yates, "DC Blocker Algorithms," IEEE Signal Processing Magazine, + * Mar. 2008, pp 132-134. + * + * \param D (int) the length of the delay line + * \param long_form (bool) whether to use long (true, default) or short form + */ + static FILTER_API sptr make(int D, bool long_form); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_DC_BLOCKER_CC_H */ diff --git a/gr-filter/include/filter/dc_blocker_ff.h b/gr-filter/include/filter/dc_blocker_ff.h new file mode 100644 index 000000000..d1c7e7819 --- /dev/null +++ b/gr-filter/include/filter/dc_blocker_ff.h @@ -0,0 +1,75 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_DC_BLOCKER_FF_H +#define INCLUDED_FILTER_DC_BLOCKER_FF_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API dc_blocker_ff : virtual public gr_sync_block + { + public: + + // gr::filter::dc_blocker_ff::sptr + typedef boost::shared_ptr sptr; + + /*! + * \class gr_dc_blocker_ff + * \brief a computationally efficient controllable DC blocker + * + * \ingroup filter_blk + * + * This block implements a computationally efficient DC blocker that produces + * a tighter notch filter around DC for a smaller group delay than an + * equivalent FIR filter or using a single pole IIR filter (though the IIR + * filter is computationally cheaper). + * + * The block defaults to using a delay line of length 32 and the long form + * of the filter. Optionally, the delay line length can be changed to alter + * the width of the DC notch (longer lines will decrease the width). + * + * The long form of the filter produces a nearly flat response outside of + * the notch but at the cost of a group delay of 2D-2. + * + * The short form of the filter does not have as flat a response in the + * passband but has a group delay of only D-1 and is cheaper to compute. + * + * The theory behind this block can be found in the paper: + * + * R. Yates, "DC Blocker Algorithms," IEEE Signal Processing Magazine, + * Mar. 2008, pp 132-134. + * + * \param D (int) the length of the delay line + * \param long_form (bool) whether to use long (true, default) or short form + */ + static FILTER_API sptr make(int D, bool long_form=true); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_DC_BLOCKER_FF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 6b33d547b..ab49cf7ae 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -110,6 +110,8 @@ list(APPEND filter_sources firdes.cc pm_remez.cc ${generated_sources} + dc_blocker_cc_impl.cc + dc_blocker_ff_impl.cc fft_filter_ccc_impl.cc fft_filter_fff_impl.cc hilbert_fc_impl.cc diff --git a/gr-filter/lib/dc_blocker_cc_impl.cc b/gr-filter/lib/dc_blocker_cc_impl.cc new file mode 100644 index 000000000..11974e5ae --- /dev/null +++ b/gr-filter/lib/dc_blocker_cc_impl.cc @@ -0,0 +1,144 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dc_blocker_cc_impl.h" +#include +#include + +namespace gr { + namespace filter { + + moving_averager_c::moving_averager_c(int D) + : d_length(D), d_out(0), d_out_d1(0), d_out_d2(0) + { + d_delay_line = std::deque(d_length-1, gr_complex(0,0)); + } + + moving_averager_c::~moving_averager_c() + { + } + + gr_complex + moving_averager_c::filter(gr_complex x) + { + d_out_d1 = d_out; + d_delay_line.push_back(x); + d_out = d_delay_line[0]; + d_delay_line.pop_front(); + + gr_complex y = x - d_out_d1 + d_out_d2; + d_out_d2 = y; + + return (y / (float)(d_length)); + } + + + + dc_blocker_cc::sptr dc_blocker_cc::make(int D, bool long_form) + { + return gnuradio::get_initial_sptr(new dc_blocker_cc_impl(D, long_form)); + } + + + dc_blocker_cc_impl::dc_blocker_cc_impl(int D, bool long_form) + : gr_sync_block ("dc_blocker_cc", + gr_make_io_signature (1, 1, sizeof(gr_complex)), + gr_make_io_signature (1, 1, sizeof(gr_complex))), + d_length(D), d_long_form(long_form) + { + if(d_long_form) { + d_ma_0 = new moving_averager_c(D); + d_ma_1 = new moving_averager_c(D); + d_ma_2 = new moving_averager_c(D); + d_ma_3 = new moving_averager_c(D); + d_delay_line = std::deque(d_length-1, gr_complex(0,0)); + } + else { + d_ma_0 = new moving_averager_c(D); + d_ma_1 = new moving_averager_c(D); + } + } + + dc_blocker_cc_impl::~dc_blocker_cc_impl() + { + if(d_long_form) { + delete d_ma_0; + delete d_ma_1; + delete d_ma_2; + delete d_ma_3; + } + else { + delete d_ma_0; + delete d_ma_1; + } + } + + int + dc_blocker_cc_impl::get_group_delay() + { + if(d_long_form) + return (2*d_length-2); + else + return d_length - 1; + } + + int + dc_blocker_cc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + + if(d_long_form) { + gr_complex y1, y2, y3, y4, d; + for(int i = 0; i < noutput_items; i++) { + y1 = d_ma_0->filter(in[i]); + y2 = d_ma_1->filter(y1); + y3 = d_ma_2->filter(y2); + y4 = d_ma_3->filter(y3); + + d_delay_line.push_back(d_ma_0->delayed_sig()); + d = d_delay_line[0]; + d_delay_line.pop_front(); + + out[i] = d - y4; + } + } + else { + gr_complex y1, y2; + for(int i = 0; i < noutput_items; i++) { + y1 = d_ma_0->filter(in[i]); + y2 = d_ma_1->filter(y1); + out[i] = d_ma_0->delayed_sig() - y2; + } + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/dc_blocker_cc_impl.h b/gr-filter/lib/dc_blocker_cc_impl.h new file mode 100644 index 000000000..7c8d1d93d --- /dev/null +++ b/gr-filter/lib/dc_blocker_cc_impl.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_FILTER_DC_BLOCKER_CC_IMPL_H +#define INCLUDED_FILTER_DC_BLOCKER_CC_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class moving_averager_c + { + public: + moving_averager_c(int D); + ~moving_averager_c(); + + gr_complex filter(gr_complex x); + gr_complex delayed_sig() { return d_out; } + + private: + int d_length; + gr_complex d_out, d_out_d1, d_out_d2; + std::deque d_delay_line; + }; + + class FILTER_API dc_blocker_cc_impl : public dc_blocker_cc + { + private: + int d_length; + bool d_long_form; + moving_averager_c *d_ma_0; + moving_averager_c *d_ma_1; + moving_averager_c *d_ma_2; + moving_averager_c *d_ma_3; + std::deque d_delay_line; + + public: + dc_blocker_cc_impl(int D, bool long_form); + + ~dc_blocker_cc_impl(); + + int get_group_delay(); + + //int set_length(int D); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_DC_BLOCKER_CC_IMPL_H */ diff --git a/gr-filter/lib/dc_blocker_ff_impl.cc b/gr-filter/lib/dc_blocker_ff_impl.cc new file mode 100644 index 000000000..86d2f2f7a --- /dev/null +++ b/gr-filter/lib/dc_blocker_ff_impl.cc @@ -0,0 +1,142 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dc_blocker_ff_impl.h" +#include +#include + +namespace gr { + namespace filter { + + moving_averager_f::moving_averager_f(int D) + : d_length(D), d_out(0), d_out_d1(0), d_out_d2(0) + { + d_delay_line = std::deque(d_length-1, 0); + } + + moving_averager_f::~moving_averager_f() + { + } + + float + moving_averager_f::filter(float x) + { + d_out_d1 = d_out; + d_delay_line.push_back(x); + d_out = d_delay_line[0]; + d_delay_line.pop_front(); + + float y = x - d_out_d1 + d_out_d2; + d_out_d2 = y; + + return (y / (float)(d_length)); + } + + + dc_blocker_ff::sptr dc_blocker_ff::make(int D, bool long_form) + { + return gnuradio::get_initial_sptr(new dc_blocker_ff_impl(D, long_form)); + } + + dc_blocker_ff_impl::dc_blocker_ff_impl (int D, bool long_form) + : gr_sync_block ("dc_blocker_ff", + gr_make_io_signature (1, 1, sizeof(float)), + gr_make_io_signature (1, 1, sizeof(float))), + d_length(D), d_long_form(long_form) + { + if(d_long_form) { + d_ma_0 = new moving_averager_f(D); + d_ma_1 = new moving_averager_f(D); + d_ma_2 = new moving_averager_f(D); + d_ma_3 = new moving_averager_f(D); + d_delay_line = std::deque(d_length-1, 0); + } + else { + d_ma_0 = new moving_averager_f(D); + d_ma_1 = new moving_averager_f(D); + } + } + + dc_blocker_ff_impl::~dc_blocker_ff_impl() + { + if(d_long_form) { + delete d_ma_0; + delete d_ma_1; + delete d_ma_2; + delete d_ma_3; + } + else { + delete d_ma_0; + delete d_ma_1; + } + } + + int + dc_blocker_ff_impl::get_group_delay() + { + if(d_long_form) + return (2*d_length-2); + else + return d_length - 1; + } + + int + dc_blocker_ff_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *in = (const float*)input_items[0]; + float *out = (float*)output_items[0]; + + if(d_long_form) { + float y1, y2, y3, y4, d; + for(int i = 0; i < noutput_items; i++) { + y1 = d_ma_0->filter(in[i]); + y2 = d_ma_1->filter(y1); + y3 = d_ma_2->filter(y2); + y4 = d_ma_3->filter(y3); + + d_delay_line.push_back(d_ma_0->delayed_sig()); + d = d_delay_line[0]; + d_delay_line.pop_front(); + + out[i] = d - y4; + } + } + else { + float y1, y2; + for(int i = 0; i < noutput_items; i++) { + y1 = d_ma_0->filter(in[i]); + y2 = d_ma_1->filter(y1); + out[i] = d_ma_0->delayed_sig() - y2; + } + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/dc_blocker_ff_impl.h b/gr-filter/lib/dc_blocker_ff_impl.h new file mode 100644 index 000000000..868e1d433 --- /dev/null +++ b/gr-filter/lib/dc_blocker_ff_impl.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_FILTER_DC_BLOCKER_FF_IMPL_H +#define INCLUDED_FILTER_DC_BLOCKER_FF_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class moving_averager_f + { + public: + moving_averager_f(int D); + ~moving_averager_f(); + + float filter(float x); + float delayed_sig() { return d_out; } + + private: + int d_length; + float d_out, d_out_d1, d_out_d2; + std::deque d_delay_line; + }; + + class FILTER_API dc_blocker_ff_impl : public dc_blocker_ff + { + private: + int d_length; + bool d_long_form; + moving_averager_f *d_ma_0; + moving_averager_f *d_ma_1; + moving_averager_f *d_ma_2; + moving_averager_f *d_ma_3; + std::deque d_delay_line; + + public: + dc_blocker_ff_impl(int D, bool long_form); + + ~dc_blocker_ff_impl(); + + int get_group_delay(); + + //int set_length(int D); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_DC_BLOCKER_FF_IMPL_H */ diff --git a/gr-filter/python/qa_dc_blocker.py b/gr-filter/python/qa_dc_blocker.py new file mode 100755 index 000000000..b9df06b45 --- /dev/null +++ b/gr-filter/python/qa_dc_blocker.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# +# Copyright 2011,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter + +class test_dc_blocker(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_001(self): + ''' Test impulse response - long form, cc ''' + src_data = [1,] + 100*[0,] + expected_result = ((-0.02072429656982422+0j), (-0.02081298828125+0j), + (0.979156494140625+0j), (-0.02081298828125+0j), + (-0.02072429656982422+0j)) + + src = gr.vector_source_c(src_data) + op = filter.dc_blocker_cc(32, True) + dst = gr.vector_sink_c() + + self.tb.connect (src, op, dst) + self.tb.run() + + # only test samples around 2D-2 + result_data = dst.data()[60:65] + self.assertComplexTuplesAlmostEqual (expected_result, result_data) + + def test_002(self): + ''' Test impulse response - short form, cc ''' + src_data = [1,] + 100*[0,] + expected_result = ((-0.029296875+0j), (-0.0302734375+0j), + (0.96875+0j), (-0.0302734375+0j), + (-0.029296875+0j)) + + src = gr.vector_source_c(src_data) + op = filter.dc_blocker_cc(32, False) + dst = gr.vector_sink_c() + + self.tb.connect (src, op, dst) + self.tb.run() + + # only test samples around D-1 + result_data = dst.data()[29:34] + self.assertComplexTuplesAlmostEqual (expected_result, result_data) + + + def test_003(self): + ''' Test impulse response - long form, ff ''' + src_data = [1,] + 100*[0,] + expected_result = ((-0.02072429656982422), (-0.02081298828125), + (0.979156494140625), (-0.02081298828125), + (-0.02072429656982422)) + + src = gr.vector_source_f(src_data) + op = filter.dc_blocker_ff(32, True) + dst = gr.vector_sink_f() + + self.tb.connect (src, op, dst) + self.tb.run() + + # only test samples around 2D-2 + result_data = dst.data()[60:65] + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_004(self): + ''' Test impulse response - short form, ff ''' + src_data = [1,] + 100*[0,] + expected_result = ((-0.029296875), (-0.0302734375), + (0.96875), (-0.0302734375), + (-0.029296875)) + + src = gr.vector_source_f(src_data) + op = filter.dc_blocker_ff(32, False) + dst = gr.vector_sink_f() + + self.tb.connect (src, op, dst) + self.tb.run() + + # only test samples around D-1 + result_data = dst.data()[29:34] + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + +if __name__ == '__main__': + gr_unittest.run(test_dc_blocker, "test_dc_blocker.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 558565fc4..3eba5f4c8 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -30,6 +30,8 @@ %{ #include "filter/firdes.h" #include "filter/pm_remez.h" +#include "filter/dc_blocker_cc.h" +#include "filter/dc_blocker_ff.h" #include "filter/fir_filter_fff.h" #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" @@ -40,6 +42,8 @@ %include "filter/firdes.h" %include "filter/pm_remez.h" +%include "filter/dc_blocker_cc.h" +%include "filter/dc_blocker_ff.h" %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" @@ -47,6 +51,8 @@ %include "filter/fft_filter_fff.h" %include "filter/hilbert_fc.h" +GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_cc); +GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_ff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); -- cgit From b3987add5662344ed88be910afdb55de1097ed15 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 6 May 2012 17:11:05 -0400 Subject: filter: some cleanup with the dc_blocker code. --- gr-filter/include/filter/dc_blocker_cc.h | 5 +++-- gr-filter/include/filter/dc_blocker_ff.h | 5 +++-- gr-filter/lib/dc_blocker_cc_impl.cc | 8 ++++---- gr-filter/lib/dc_blocker_cc_impl.h | 2 +- gr-filter/lib/dc_blocker_ff_impl.cc | 10 +++++----- gr-filter/lib/dc_blocker_ff_impl.h | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/gr-filter/include/filter/dc_blocker_cc.h b/gr-filter/include/filter/dc_blocker_cc.h index 64d7ebffc..b0a38ffcb 100644 --- a/gr-filter/include/filter/dc_blocker_cc.h +++ b/gr-filter/include/filter/dc_blocker_cc.h @@ -25,7 +25,6 @@ #include #include -#include namespace gr { namespace filter { @@ -38,7 +37,7 @@ namespace gr { typedef boost::shared_ptr sptr; /*! - * \class gr_dc_blocker_cc + * \class dc_blocker_cc * \brief a computationally efficient controllable DC blocker * * \ingroup filter_blk @@ -67,6 +66,8 @@ namespace gr { * \param long_form (bool) whether to use long (true, default) or short form */ static FILTER_API sptr make(int D, bool long_form); + + virtual int group_delay() = 0; }; } /* namespace filter */ diff --git a/gr-filter/include/filter/dc_blocker_ff.h b/gr-filter/include/filter/dc_blocker_ff.h index d1c7e7819..18c729768 100644 --- a/gr-filter/include/filter/dc_blocker_ff.h +++ b/gr-filter/include/filter/dc_blocker_ff.h @@ -25,7 +25,6 @@ #include #include -#include namespace gr { namespace filter { @@ -38,7 +37,7 @@ namespace gr { typedef boost::shared_ptr sptr; /*! - * \class gr_dc_blocker_ff + * \class dc_blocker_ff * \brief a computationally efficient controllable DC blocker * * \ingroup filter_blk @@ -67,6 +66,8 @@ namespace gr { * \param long_form (bool) whether to use long (true, default) or short form */ static FILTER_API sptr make(int D, bool long_form=true); + + virtual int group_delay() = 0; }; } /* namespace filter */ diff --git a/gr-filter/lib/dc_blocker_cc_impl.cc b/gr-filter/lib/dc_blocker_cc_impl.cc index 11974e5ae..663ba94f1 100644 --- a/gr-filter/lib/dc_blocker_cc_impl.cc +++ b/gr-filter/lib/dc_blocker_cc_impl.cc @@ -64,9 +64,9 @@ namespace gr { dc_blocker_cc_impl::dc_blocker_cc_impl(int D, bool long_form) - : gr_sync_block ("dc_blocker_cc", - gr_make_io_signature (1, 1, sizeof(gr_complex)), - gr_make_io_signature (1, 1, sizeof(gr_complex))), + : gr_sync_block("dc_blocker_cc", + gr_make_io_signature (1, 1, sizeof(gr_complex)), + gr_make_io_signature (1, 1, sizeof(gr_complex))), d_length(D), d_long_form(long_form) { if(d_long_form) { @@ -97,7 +97,7 @@ namespace gr { } int - dc_blocker_cc_impl::get_group_delay() + dc_blocker_cc_impl::group_delay() { if(d_long_form) return (2*d_length-2); diff --git a/gr-filter/lib/dc_blocker_cc_impl.h b/gr-filter/lib/dc_blocker_cc_impl.h index 7c8d1d93d..6f8bc16c7 100644 --- a/gr-filter/lib/dc_blocker_cc_impl.h +++ b/gr-filter/lib/dc_blocker_cc_impl.h @@ -61,7 +61,7 @@ namespace gr { ~dc_blocker_cc_impl(); - int get_group_delay(); + int group_delay(); //int set_length(int D); diff --git a/gr-filter/lib/dc_blocker_ff_impl.cc b/gr-filter/lib/dc_blocker_ff_impl.cc index 86d2f2f7a..22822d147 100644 --- a/gr-filter/lib/dc_blocker_ff_impl.cc +++ b/gr-filter/lib/dc_blocker_ff_impl.cc @@ -61,10 +61,10 @@ namespace gr { return gnuradio::get_initial_sptr(new dc_blocker_ff_impl(D, long_form)); } - dc_blocker_ff_impl::dc_blocker_ff_impl (int D, bool long_form) - : gr_sync_block ("dc_blocker_ff", - gr_make_io_signature (1, 1, sizeof(float)), - gr_make_io_signature (1, 1, sizeof(float))), + dc_blocker_ff_impl::dc_blocker_ff_impl(int D, bool long_form) + : gr_sync_block("dc_blocker_ff", + gr_make_io_signature (1, 1, sizeof(float)), + gr_make_io_signature (1, 1, sizeof(float))), d_length(D), d_long_form(long_form) { if(d_long_form) { @@ -95,7 +95,7 @@ namespace gr { } int - dc_blocker_ff_impl::get_group_delay() + dc_blocker_ff_impl::group_delay() { if(d_long_form) return (2*d_length-2); diff --git a/gr-filter/lib/dc_blocker_ff_impl.h b/gr-filter/lib/dc_blocker_ff_impl.h index 868e1d433..5ae60e2e4 100644 --- a/gr-filter/lib/dc_blocker_ff_impl.h +++ b/gr-filter/lib/dc_blocker_ff_impl.h @@ -61,7 +61,7 @@ namespace gr { ~dc_blocker_ff_impl(); - int get_group_delay(); + int group_delay(); //int set_length(int D); -- cgit From fc98a2a9775ab8e091413a195ce2584e5b76de99 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 6 May 2012 19:36:03 -0400 Subject: filter: added filter_delay_fc to gr-filter with QA and GRC files. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/filter_delay_fc.xml | 31 +++ gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/filter_delay_fc.h | 65 ++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/filter_delay_fc_impl.cc | 107 ++++++++++ gr-filter/lib/filter_delay_fc_impl.h | 56 +++++ gr-filter/python/qa_filter_delay_fc.py | 318 +++++++++++++++++++++++++++++ gr-filter/swig/filter_swig.i | 3 + 10 files changed, 584 insertions(+) create mode 100644 gr-filter/grc/filter_delay_fc.xml create mode 100644 gr-filter/include/filter/filter_delay_fc.h create mode 100644 gr-filter/lib/filter_delay_fc_impl.cc create mode 100644 gr-filter/lib/filter_delay_fc_impl.h create mode 100755 gr-filter/python/qa_filter_delay_fc.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 5241680e8..6ace8a8b4 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -21,6 +21,7 @@ install(FILES filter_block_tree.xml dc_blocker_xx.xml fft_filter_xxx.xml + filter_delay_fc.xml hilbert_fc.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 709aa89aa..fbc4e7cb5 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -32,6 +32,7 @@ Filters dc_blocker_xx fft_filter_xxx + filter_delay_fc hilbert_fc diff --git a/gr-filter/grc/filter_delay_fc.xml b/gr-filter/grc/filter_delay_fc.xml new file mode 100644 index 000000000..7b77c2caf --- /dev/null +++ b/gr-filter/grc/filter_delay_fc.xml @@ -0,0 +1,31 @@ + + + + Filter Delay + filter_delay_fc + from gnuradio import filter + from gnuradio.filter import firdes + filter.filter_delay_fc($taps) + + Taps + taps + real_vector + + + in + float + + + in + float + 1 + + + out + complex + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 402dd599a..75d44cb66 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -82,6 +82,7 @@ install(FILES ${generated_includes} dc_blocker_cc.h dc_blocker_ff.h + filter_delay_fc.h fft_filter_ccc.h fft_filter_fff.h hilbert_fc.h diff --git a/gr-filter/include/filter/filter_delay_fc.h b/gr-filter/include/filter/filter_delay_fc.h new file mode 100644 index 000000000..b772fe736 --- /dev/null +++ b/gr-filter/include/filter/filter_delay_fc.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_FILTER_DELAY_FC_H +#define INCLUDED_FILTER_FILTER_DELAY_FC_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API filter_delay_fc : virtual public gr_sync_block + { + public: + + // gr::foo::filter_delay_fc::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief Filter-Delay Combination Block. + * \ingroup filter_blk + * + * The block takes one or two float stream and outputs a complex + * stream. + * + * If only one float stream is input, the real output is a + * delayed version of this input and the imaginary output is the + * filtered output. + * + * If two floats are connected to the input, then the real + * output is the delayed version of the first input, and the + * imaginary output is the filtered output. + * + * The delay in the real path accounts for the group delay + * introduced by the filter in the imaginary path. The filter + * taps needs to be calculated before initializing this block. + * + */ + static FILTER_API sptr make(const std::vector &taps); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FILTER_DELAY_FC_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index ab49cf7ae..c8e7b7e18 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -112,6 +112,7 @@ list(APPEND filter_sources ${generated_sources} dc_blocker_cc_impl.cc dc_blocker_ff_impl.cc + filter_delay_fc_impl.cc fft_filter_ccc_impl.cc fft_filter_fff_impl.cc hilbert_fc_impl.cc diff --git a/gr-filter/lib/filter_delay_fc_impl.cc b/gr-filter/lib/filter_delay_fc_impl.cc new file mode 100644 index 000000000..ce320c557 --- /dev/null +++ b/gr-filter/lib/filter_delay_fc_impl.cc @@ -0,0 +1,107 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "filter_delay_fc_impl.h" + +namespace gr { + namespace filter { + + filter_delay_fc::sptr filter_delay_fc::make(const std::vector &taps) + { + return gnuradio::get_initial_sptr(new filter_delay_fc_impl(taps)); + } + + filter_delay_fc_impl::filter_delay_fc_impl(const std::vector &taps) + : gr_sync_block("filter_delay_fc", + gr_make_io_signature(1, 2, sizeof(float)), + gr_make_io_signature(1, 1, sizeof(gr_complex))), + d_update(false) + { + d_taps = taps; + d_fir = new kernel::fir_filter_fff(1, taps); + d_delay = d_fir->ntaps() / 2; + set_history(d_fir->ntaps()); + } + + filter_delay_fc_impl::~filter_delay_fc_impl() + { + delete d_fir; + } + + std::vector + filter_delay_fc_impl::taps() + { + return d_fir->taps(); + } + + void + filter_delay_fc_impl::set_taps(const std::vector &taps) + { + // don't reset taps in case history changes + d_taps = taps; + d_update = true; + } + + int + filter_delay_fc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + float *in0 = (float *)input_items[0]; + float *in1 = (float *)input_items[1]; + gr_complex *out = (gr_complex *)output_items[0]; + + if(d_update) { + d_fir->set_taps(d_taps); + d_delay = d_fir->ntaps() / 2; + set_history(d_fir->ntaps()); + return 0; + } + + switch(input_items.size ()) { + case 1: + for(int i = 0; i < noutput_items; i++) { + out[i] = gr_complex (in0[i + d_delay], + d_fir->filter (&in0[i])); + } + break; + + case 2: + for(int j = 0; j < noutput_items; j++) { + out[j] = gr_complex (in0[j + d_delay], + d_fir->filter (&in1[j])); + } + break; + + default: + assert(0); + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/filter_delay_fc_impl.h b/gr-filter/lib/filter_delay_fc_impl.h new file mode 100644 index 000000000..5157d6e02 --- /dev/null +++ b/gr-filter/lib/filter_delay_fc_impl.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_FILTER_DELAY_FC_IMPL_H +#define INCLUDED_FILTER_FILTER_DELAY_FC_IMPL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API filter_delay_fc_impl : public filter_delay_fc + { + private: + unsigned int d_delay; + kernel::fir_filter_fff *d_fir; + std::vector d_taps; + bool d_update; + + public: + filter_delay_fc_impl(const std::vector &taps); + ~filter_delay_fc_impl(); + + std::vector taps(); + void set_taps(const std::vector &taps); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FILTER_DELAY_FC_IMPL_H */ diff --git a/gr-filter/python/qa_filter_delay_fc.py b/gr-filter/python/qa_filter_delay_fc.py new file mode 100755 index 000000000..57b063e7b --- /dev/null +++ b/gr-filter/python/qa_filter_delay_fc.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_filter_delay_fc(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001_filter_delay_one_input(self): + + # expected result + expected_result = ( -1.4678005338941702e-11j, + -0.0011950774351134896j, + -0.0019336787518113852j, + -0.0034673355985432863j, + -0.0036765895783901215j, + -0.004916108213365078j, + -0.0042778430506587029j, + -0.006028641015291214j, + -0.005476709920912981j, + -0.0092810001224279404j, + -0.0095402700826525688j, + -0.016060983762145042j, + -0.016446959227323532j, + -0.02523401565849781j, + -0.024382550269365311j, + -0.035477779805660248j, + -0.033021725714206696j, + -0.048487484455108643j, + -0.04543270543217659j, + -0.069477587938308716j, + -0.066984444856643677j, + -0.10703597217798233j, + -0.10620346665382385j, + -0.1852707713842392j, + -0.19357112050056458j, + (7.2191945754696007e-09 -0.50004088878631592j), + (0.58778399229049683 -0.6155126690864563j), + (0.95105588436126709 -0.12377222627401352j), + (0.95105588436126709 +0.41524654626846313j), + (0.5877838134765625 +0.91611981391906738j), + (5.8516356205018383e-09 +1.0670661926269531j), + (-0.5877840518951416 +0.87856143712997437j), + (-0.95105588436126709 +0.35447561740875244j), + (-0.95105588436126709 -0.26055556535720825j), + (-0.5877838134765625 -0.77606213092803955j), + (-8.7774534307527574e-09 -0.96460390090942383j), + (0.58778399229049683 -0.78470128774642944j), + (0.95105588436126709 -0.28380891680717468j), + (0.95105588436126709 +0.32548999786376953j), + (0.5877838134765625 +0.82514488697052002j), + (1.4629089051254596e-08 +1.0096219778060913j), + (-0.5877840518951416 +0.81836479902267456j), + (-0.95105588436126709 +0.31451958417892456j), + (-0.95105588436126709 -0.3030143678188324j), + (-0.5877838134765625 -0.80480599403381348j), + (-1.7554906861505515e-08 -0.99516552686691284j), + (0.58778399229049683 -0.80540722608566284j), + (0.95105582475662231 -0.30557557940483093j), + (0.95105588436126709 +0.31097668409347534j), + (0.5877838134765625 +0.81027895212173462j), + (2.3406542482007353e-08 +1.0000816583633423j), + (-0.5877840518951416 +0.80908381938934326j), + (-0.95105588436126709 +0.30904293060302734j), + (-0.95105588436126709 -0.30904296040534973j), + (-0.5877838134765625 -0.80908387899398804j), + (-2.6332360292258272e-08 -1.0000815391540527j), + (0.58778399229049683 -0.80908381938934326j), + (0.95105582475662231 -0.30904299020767212j), + (0.95105588436126709 +0.30904293060302734j), + (0.5877838134765625 +0.80908381938934326j), + (3.218399768911695e-08 +1.0000815391540527j)) + + tb = self.tb + + sampling_freq = 100 + + ntaps = 51 + src1 = gr.sig_source_f(sampling_freq, gr.GR_SIN_WAVE, + sampling_freq * 0.10, 1.0) + head = gr.head(gr.sizeof_float, int(ntaps + sampling_freq * 0.10)) + dst2 = gr.vector_sink_c() + + # calculate taps + taps = filter.firdes_hilbert(ntaps) + hd = filter.filter_delay_fc(taps) + + tb.connect(src1, head) + tb.connect(head, hd) + tb.connect(hd,dst2) + + tb.run() + + # get output + result_data = dst2.data() + self.assertComplexTuplesAlmostEqual(expected_result, result_data, 5) + + def test_002_filter_delay_two_inputs(self): + + # giving the same signal to both the inputs should fetch the same results + # as above + + # expected result + expected_result = ( -1.4678005338941702e-11j, + -0.0011950774351134896j, + -0.0019336787518113852j, + -0.0034673355985432863j, + -0.0036765895783901215j, + -0.004916108213365078j, + -0.0042778430506587029j, + -0.006028641015291214j, + -0.005476709920912981j, + -0.0092810001224279404j, + -0.0095402700826525688j, + -0.016060983762145042j, + -0.016446959227323532j, + -0.02523401565849781j, + -0.024382550269365311j, + -0.035477779805660248j, + -0.033021725714206696j, + -0.048487484455108643j, + -0.04543270543217659j, + -0.069477587938308716j, + -0.066984444856643677j, + -0.10703597217798233j, + -0.10620346665382385j, + -0.1852707713842392j, + -0.19357112050056458j, + (7.2191945754696007e-09 -0.50004088878631592j), + (0.58778399229049683 -0.6155126690864563j), + (0.95105588436126709 -0.12377222627401352j), + (0.95105588436126709 +0.41524654626846313j), + (0.5877838134765625 +0.91611981391906738j), + (5.8516356205018383e-09 +1.0670661926269531j), + (-0.5877840518951416 +0.87856143712997437j), + (-0.95105588436126709 +0.35447561740875244j), + (-0.95105588436126709 -0.26055556535720825j), + (-0.5877838134765625 -0.77606213092803955j), + (-8.7774534307527574e-09 -0.96460390090942383j), + (0.58778399229049683 -0.78470128774642944j), + (0.95105588436126709 -0.28380891680717468j), + (0.95105588436126709 +0.32548999786376953j), + (0.5877838134765625 +0.82514488697052002j), + (1.4629089051254596e-08 +1.0096219778060913j), + (-0.5877840518951416 +0.81836479902267456j), + (-0.95105588436126709 +0.31451958417892456j), + (-0.95105588436126709 -0.3030143678188324j), + (-0.5877838134765625 -0.80480599403381348j), + (-1.7554906861505515e-08 -0.99516552686691284j), + (0.58778399229049683 -0.80540722608566284j), + (0.95105582475662231 -0.30557557940483093j), + (0.95105588436126709 +0.31097668409347534j), + (0.5877838134765625 +0.81027895212173462j), + (2.3406542482007353e-08 +1.0000816583633423j), + (-0.5877840518951416 +0.80908381938934326j), + (-0.95105588436126709 +0.30904293060302734j), + (-0.95105588436126709 -0.30904296040534973j), + (-0.5877838134765625 -0.80908387899398804j), + (-2.6332360292258272e-08 -1.0000815391540527j), + (0.58778399229049683 -0.80908381938934326j), + (0.95105582475662231 -0.30904299020767212j), + (0.95105588436126709 +0.30904293060302734j), + (0.5877838134765625 +0.80908381938934326j), + (3.218399768911695e-08 +1.0000815391540527j)) + + + tb = self.tb + + sampling_freq = 100 + ntaps = 51 + src1 = gr.sig_source_f(sampling_freq, gr.GR_SIN_WAVE, + sampling_freq * 0.10, 1.0) + head = gr.head(gr.sizeof_float, int(ntaps + sampling_freq * 0.10)) + dst2 = gr.vector_sink_c() + + + # calculate taps + taps = filter.firdes_hilbert(ntaps) + hd = filter.filter_delay_fc(taps) + + tb.connect(src1, head) + tb.connect(head, (hd,0)) + tb.connect(head, (hd,1)) + tb.connect(hd,dst2) + tb.run() + + # get output + result_data = dst2.data() + + self.assertComplexTuplesAlmostEqual(expected_result, result_data, 5) + + + def test_003_filter_delay_two_inputs(self): + + # give two different inputs + + # expected result + expected_result = ( -0.0020331963896751404j, + -0.0016448829555884004j, + -0.0032375147566199303j, + -0.0014826074475422502j, + -0.0033034090884029865j, + -0.00051144487224519253j, + -0.0043686260469257832j, + -0.0010198024101555347j, + -0.0082517862319946289j, + -0.003456643782556057j, + -0.014193611219525337j, + -0.005875137634575367j, + -0.020293503999710083j, + -0.0067503536120057106j, + -0.026798896491527557j, + -0.0073488112539052963j, + -0.037041611969470978j, + -0.010557252913713455j, + -0.055669989436864853j, + -0.018332764506340027j, + -0.089904911816120148j, + -0.033361352980136871j, + -0.16902604699134827j, + -0.074318811297416687j, + -0.58429563045501709j, + (7.2191945754696007e-09 -0.35892376303672791j), + (0.58778399229049683 +0.63660913705825806j), + (0.95105588436126709 +0.87681591510772705j), + (0.95105588436126709 +0.98705857992172241j), + (0.5877838134765625 +0.55447429418563843j), + (5.8516356205018383e-09 +0.026006083935499191j), + (-0.5877840518951416 -0.60616838932037354j), + (-0.95105588436126709 -0.9311758279800415j), + (-0.95105588436126709 -0.96169203519821167j), + (-0.5877838134765625 -0.57292771339416504j), + (-8.7774534307527574e-09 -0.0073488391935825348j), + (0.58778399229049683 +0.59720659255981445j), + (0.95105588436126709 +0.94438445568084717j), + (0.95105588436126709 +0.95582199096679688j), + (0.5877838134765625 +0.58196049928665161j), + (1.4629089051254596e-08 +0.0026587247848510742j), + (-0.5877840518951416 -0.59129220247268677j), + (-0.95105588436126709 -0.94841635227203369j), + (-0.95105588436126709 -0.95215457677841187j), + (-0.5877838134765625 -0.58535969257354736j), + (-1.7554906861505515e-08 -0.00051158666610717773j), + (0.58778399229049683 +0.58867418766021729j), + (0.95105582475662231 +0.94965213537216187j), + (0.95105588436126709 +0.95050644874572754j), + (0.5877838134765625 +0.58619076013565063j), + (2.3406542482007353e-08 +1.1920928955078125e-07j), + (-0.5877840518951416 -0.58783555030822754j), + (-0.95105588436126709 -0.95113480091094971j), + (-0.95105588436126709 -0.95113474130630493j), + (-0.5877838134765625 -0.58783555030822754j), + (-2.6332360292258272e-08 -8.1956386566162109e-08j), + (0.58778399229049683 +0.58783555030822754j), + (0.95105582475662231 +0.95113474130630493j), + (0.95105588436126709 +0.95113474130630493j), + (0.5877838134765625 +0.58783560991287231j), + (3.218399768911695e-08 +1.1920928955078125e-07j)) + + tb = self.tb + + sampling_freq = 100 + ntaps = 51 + + src1 = gr.sig_source_f(sampling_freq, gr.GR_SIN_WAVE,sampling_freq * 0.10, 1.0) + src2 = gr.sig_source_f(sampling_freq, gr.GR_COS_WAVE,sampling_freq * 0.10, 1.0) + + head1 = gr.head(gr.sizeof_float, int(ntaps + sampling_freq * 0.10)) + head2 = gr.head(gr.sizeof_float, int(ntaps + sampling_freq * 0.10)) + + taps = filter.firdes_hilbert(ntaps) + hd = filter.filter_delay_fc(taps) + + dst2 = gr.vector_sink_c() + + tb.connect(src1, head1) + tb.connect(src2, head2) + + tb.connect(head1, (hd,0)) + tb.connect(head2, (hd,1)) + tb.connect(hd, dst2) + + tb.run() + + # get output + result_data = dst2.data() + + self.assertComplexTuplesAlmostEqual(expected_result, result_data, 5) + + +if __name__ == '__main__': + gr_unittest.run(test_filter_delay_fc, "test_filter_delay_fc.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 3eba5f4c8..4f0caa59c 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -32,6 +32,7 @@ #include "filter/pm_remez.h" #include "filter/dc_blocker_cc.h" #include "filter/dc_blocker_ff.h" +#include "filter/filter_delay_fc.h" #include "filter/fir_filter_fff.h" #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" @@ -44,6 +45,7 @@ %include "filter/pm_remez.h" %include "filter/dc_blocker_cc.h" %include "filter/dc_blocker_ff.h" +%include "filter/filter_delay_fc.h" %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" @@ -53,6 +55,7 @@ GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_cc); GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_ff); +GR_SWIG_BLOCK_MAGIC2(filter, filter_delay_fc); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); -- cgit From 0f6af7cf0ed1cad636a185fb951f026b85b49064 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 6 May 2012 22:42:22 -0400 Subject: filter: added fir_filter_with_buffer to gr-filter. Only fff and ccc versions so far. Needs work on Volk to be followed from fir_filter. --- gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/fir_filter_with_buffer.h | 218 ++++++++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/fir_filter_with_buffer.cc | 236 ++++++++++++++++++++++ 4 files changed, 456 insertions(+) create mode 100644 gr-filter/include/filter/fir_filter_with_buffer.h create mode 100644 gr-filter/lib/fir_filter_with_buffer.cc diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 75d44cb66..d5452a130 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -78,6 +78,7 @@ install(FILES firdes.h pm_remez.h fir_filter.h + fir_filter_with_buffer.h fft_filter.h ${generated_includes} dc_blocker_cc.h diff --git a/gr-filter/include/filter/fir_filter_with_buffer.h b/gr-filter/include/filter/fir_filter_with_buffer.h new file mode 100644 index 000000000..feebb382f --- /dev/null +++ b/gr-filter/include/filter/fir_filter_with_buffer.h @@ -0,0 +1,218 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_FIR_FILTER_WITH_BUFFER_H +#define INCLUDED_FILTER_FIR_FILTER_WITH_BUFFER_H + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + /*! + * \brief FIR with internal buffer for float input, float output and float taps. + * \ingroup filter + */ + class FILTER_API fir_filter_with_buffer_fff + { + private: + float *d_taps; + float *d_buffer; + unsigned int d_idx; + unsigned int d_ntaps; + + public: + + // CONSTRUCTORS + + /*! + * \brief construct new FIR with given taps. + * + * Note that taps must be in forward order, e.g., coefficient 0 is + * stored in new_taps[0], coefficient 1 is stored in + * new_taps[1], etc. + */ + fir_filter_with_buffer_fff(const std::vector &taps); + + ~fir_filter_with_buffer_fff(); + + // MANIPULATORS + + /*! + * \brief compute a single output value. + * + * \p input is a single input value of the filter type + * + * \returns the filtered input value. + */ + float filter(float input); + + /*! + * \brief compute a single output value; designed for decimating filters. + * + * \p input is a single input value of the filter type. The value of dec is the + * decimating value of the filter, so input[] must have dec valid values. + * The filter pushes dec number of items onto the circ. buffer before computing + * a single output. + * + * \returns the filtered input value. + */ + float filter(const float input[], unsigned long dec); + + /*! + * \brief compute an array of N output values. + * + * \p input must have (n - 1 + ntaps()) valid entries. + * input[0] .. input[n - 1 + ntaps() - 1] are referenced to compute the output values. + */ + void filterN(float output[], + const float input[], + unsigned long n); + + /*! + * \brief compute an array of N output values, decimating the input + * + * \p input must have (decimate * (n - 1) + ntaps()) valid entries. + * input[0] .. input[decimate * (n - 1) + ntaps() - 1] are referenced to + * compute the output values. + */ + void filterNdec(float output[], const float input[], + unsigned long n, unsigned long decimate); + + // ACCESSORS + + /*! + * \return number of taps in filter. + */ + unsigned int ntaps() const { return d_ntaps; } + + /*! + * \brief install \p new_taps as the current taps. + */ + void set_taps(const std::vector &taps); + + /*! + * \return current taps + */ + std::vector taps() const; + }; + + + /**************************************************************/ + + + /*! + * \brief FIR with internal buffer for gr_complex input, gr_complex output and gr_complex taps. + * \ingroup filter + */ + class FILTER_API fir_filter_with_buffer_ccc + { + private: + gr_complex *d_taps; + gr_complex *d_buffer; + unsigned int d_idx; + unsigned int d_ntaps; + + public: + + // CONSTRUCTORS + + /*! + * \brief construct new FIR with given taps. + * + * Note that taps must be in forward order, e.g., coefficient 0 is + * stored in new_taps[0], coefficient 1 is stored in + * new_taps[1], etc. + */ + fir_filter_with_buffer_ccc(const std::vector &taps); + + ~fir_filter_with_buffer_ccc(); + + // MANIPULATORS + + /*! + * \brief compute a single output value. + * + * \p input is a single input value of the filter type + * + * \returns the filtered input value. + */ + gr_complex filter(gr_complex input); + + /*! + * \brief compute a single output value; designed for decimating filters. + * + * \p input is a single input value of the filter type. The value of dec is the + * decimating value of the filter, so input[] must have dec valid values. + * The filter pushes dec number of items onto the circ. buffer before computing + * a single output. + * + * \returns the filtered input value. + */ + gr_complex filter(const gr_complex input[], unsigned long dec); + + /*! + * \brief compute an array of N output values. + * + * \p input must have (n - 1 + ntaps()) valid entries. + * input[0] .. input[n - 1 + ntaps() - 1] are referenced to compute the output values. + */ + void filterN(gr_complex output[], + const gr_complex input[], + unsigned long n); + + /*! + * \brief compute an array of N output values, decimating the input + * + * \p input must have (decimate * (n - 1) + ntaps()) valid entries. + * input[0] .. input[decimate * (n - 1) + ntaps() - 1] are referenced to + * compute the output values. + */ + void filterNdec(gr_complex output[], const gr_complex input[], + unsigned long n, unsigned long decimate); + + // ACCESSORS + + /*! + * \return number of taps in filter. + */ + unsigned int ntaps() const { return d_ntaps; } + + /*! + * \brief install \p new_taps as the current taps. + */ + void set_taps(const std::vector &taps); + + /*! + * \return current taps + */ + std::vector taps() const; + }; + + } /* namespace kernel */ + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FIR_FILTER_WITH_BUFFER_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index c8e7b7e18..e1fae56a3 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -106,6 +106,7 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) ######################################################################## list(APPEND filter_sources fir_filter.cc + fir_filter_with_buffer.cc fft_filter.cc firdes.cc pm_remez.cc diff --git a/gr-filter/lib/fir_filter_with_buffer.cc b/gr-filter/lib/fir_filter_with_buffer.cc new file mode 100644 index 000000000..a2b804a08 --- /dev/null +++ b/gr-filter/lib/fir_filter_with_buffer.cc @@ -0,0 +1,236 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + fir_filter_with_buffer_fff::fir_filter_with_buffer_fff(const std::vector &taps) + { + d_buffer = NULL; + set_taps(taps); + } + + fir_filter_with_buffer_fff::~fir_filter_with_buffer_fff() + { + if(d_buffer != NULL) + fft::free(d_buffer); + } + + void + fir_filter_with_buffer_fff::set_taps(const std::vector &taps) + { + if(d_buffer != NULL) { + fft::free(d_buffer); + d_buffer = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_float(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[d_ntaps-i-1] = taps[i]; + } + d_idx = 0; + } + + std::vector + fir_filter_with_buffer_fff::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[d_ntaps-i-1]); + return t; + } + + float + fir_filter_with_buffer_fff::filter(float input) + { + d_buffer[d_idx] = input; + d_buffer[d_idx+ntaps()] = input; + + d_idx++; + if(d_idx >= ntaps()) + d_idx = 0; + + float output = 0; + volk_32f_x2_dot_prod_32f_u(&output, &d_buffer[d_idx], + d_taps, d_ntaps); + + return output; + } + + float + fir_filter_with_buffer_fff::filter(const float input[], + unsigned long dec) + { + unsigned int i; + + for(i = 0; i < dec; i++) { + d_buffer[d_idx] = input[i]; + d_buffer[d_idx+ntaps()] = input[i]; + d_idx++; + if(d_idx >= ntaps()) + d_idx = 0; + } + + float output = 0; + volk_32f_x2_dot_prod_32f_u(&output, &d_buffer[d_idx], + d_taps, d_ntaps); + return output; + } + + void + fir_filter_with_buffer_fff::filterN(float output[], + const float input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(input[i]); + } + } + + void + fir_filter_with_buffer_fff::filterNdec(float output[], + const float input[], + unsigned long n, + unsigned long decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(&input[j], decimate); + j += decimate; + } + } + + + /**************************************************************/ + + + fir_filter_with_buffer_ccc::fir_filter_with_buffer_ccc(const std::vector &taps) + { + d_buffer = NULL; + set_taps(taps); + } + + fir_filter_with_buffer_ccc::~fir_filter_with_buffer_ccc() + { + if(d_buffer != NULL) + fft::free(d_buffer); + } + + void + fir_filter_with_buffer_ccc::set_taps(const std::vector &taps) + { + if(d_buffer != NULL) { + fft::free(d_buffer); + d_buffer = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_complex(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[d_ntaps-i-1] = taps[i]; + } + d_idx = 0; + } + + std::vector + fir_filter_with_buffer_ccc::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[d_ntaps-i-1]); + return t; + } + + gr_complex + fir_filter_with_buffer_ccc::filter(gr_complex input) + { + d_buffer[d_idx] = input; + d_buffer[d_idx+ntaps()] = input; + + d_idx++; + if(d_idx >= ntaps()) + d_idx = 0; + + gr_complex output = 0; + volk_32fc_x2_dot_prod_32fc_u(&output, &d_buffer[d_idx], + d_taps, d_ntaps); + + return output; + } + + gr_complex + fir_filter_with_buffer_ccc::filter(const gr_complex input[], + unsigned long dec) + { + unsigned int i; + + for(i = 0; i < dec; i++) { + d_buffer[d_idx] = input[i]; + d_buffer[d_idx+ntaps()] = input[i]; + d_idx++; + if(d_idx >= ntaps()) + d_idx = 0; + } + + gr_complex output = 0; + volk_32fc_x2_dot_prod_32fc_u(&output, &d_buffer[d_idx], + d_taps, d_ntaps); + return output; + } + + void + fir_filter_with_buffer_ccc::filterN(gr_complex output[], + const gr_complex input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(input[i]); + } + } + + void + fir_filter_with_buffer_ccc::filterNdec(gr_complex output[], + const gr_complex input[], + unsigned long n, + unsigned long decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(&input[j], decimate); + j += decimate; + } + } + + + } /* namespace kernel */ + } /* namespace filter */ +} /* namespace gr */ -- cgit From df09c431d1ab1f9fd0645b6d5187d5eaec4699b8 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Mon, 7 May 2012 20:05:29 -0400 Subject: filter: fixed typo. --- gr-filter/include/filter/filter_delay_fc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gr-filter/include/filter/filter_delay_fc.h b/gr-filter/include/filter/filter_delay_fc.h index b772fe736..299a65519 100644 --- a/gr-filter/include/filter/filter_delay_fc.h +++ b/gr-filter/include/filter/filter_delay_fc.h @@ -33,7 +33,7 @@ namespace gr { { public: - // gr::foo::filter_delay_fc::sptr + // gr::filter::filter_delay_fc::sptr typedef boost::shared_ptr sptr; /*! -- cgit From 4f08cb7eb6e3ac4b7315b4b78dbc7d812b6c3dd1 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 8 May 2012 22:07:05 -0400 Subject: filter: copied over pfb_channelizer to gr-filter with QA and GRC. --- gr-filter/examples/CMakeLists.txt | 2 + gr-filter/examples/channelize.py | 195 ++++++++++++++++++++ gr-filter/examples/chirp_channelize.py | 204 +++++++++++++++++++++ gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/pfb_channelizer.xml | 62 +++++++ gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/pfb_channelizer_ccf.h | 205 +++++++++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/hilbert_fc_impl.h | 1 - gr-filter/lib/pfb_channelizer_ccf_impl.cc | 240 +++++++++++++++++++++++++ gr-filter/lib/pfb_channelizer_ccf_impl.h | 72 ++++++++ gr-filter/python/CMakeLists.txt | 1 + gr-filter/python/__init__.py | 2 +- gr-filter/python/pfb.py | 74 ++++++++ gr-filter/python/qa_pfb_channelizer.py | 104 +++++++++++ gr-filter/swig/filter_swig.i | 3 + 17 files changed, 1167 insertions(+), 2 deletions(-) create mode 100755 gr-filter/examples/channelize.py create mode 100755 gr-filter/examples/chirp_channelize.py create mode 100644 gr-filter/grc/pfb_channelizer.xml create mode 100644 gr-filter/include/filter/pfb_channelizer_ccf.h create mode 100644 gr-filter/lib/pfb_channelizer_ccf_impl.cc create mode 100644 gr-filter/lib/pfb_channelizer_ccf_impl.h create mode 100644 gr-filter/python/pfb.py create mode 100644 gr-filter/python/qa_pfb_channelizer.py diff --git a/gr-filter/examples/CMakeLists.txt b/gr-filter/examples/CMakeLists.txt index 12a3c42e0..2eca31726 100644 --- a/gr-filter/examples/CMakeLists.txt +++ b/gr-filter/examples/CMakeLists.txt @@ -24,6 +24,8 @@ GR_PYTHON_INSTALL(PROGRAMS fir_filter_fff.py fir_filter_ccc.py fft_filter_ccc.py + channelize.py + chirp_channelize.py DESTINATION ${GR_PKG_FILTER_EXAMPLES_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/examples/channelize.py b/gr-filter/examples/channelize.py new file mode 100755 index 000000000..affce7b57 --- /dev/null +++ b/gr-filter/examples/channelize.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# Copyright 2009,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blks2 +from gnuradio import filter +import sys, time + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab + from pylab import mlab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +class pfb_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self._N = 2000000 # number of samples to use + self._fs = 1000 # initial sampling rate + self._M = M = 9 # Number of channels to channelize + self._ifs = M*self._fs # initial sampling rate + + # Create a set of taps for the PFB channelizer + self._taps = gr.firdes.low_pass_2(1, self._ifs, 475.50, 50, + attenuation_dB=100, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + # Calculate the number of taps per channel for our own information + tpc = scipy.ceil(float(len(self._taps)) / float(self._M)) + print "Number of taps: ", len(self._taps) + print "Number of channels: ", self._M + print "Taps per channel: ", tpc + + # Create a set of signals at different frequencies + # freqs lists the frequencies of the signals that get stored + # in the list "signals", which then get summed together + self.signals = list() + self.add = gr.add_cc() + freqs = [-70, -50, -30, -10, 10, 20, 40, 60, 80] + for i in xrange(len(freqs)): + f = freqs[i] + (M/2-M+i+1)*self._fs + self.signals.append(gr.sig_source_c(self._ifs, gr.GR_SIN_WAVE, f, 1)) + self.connect(self.signals[i], (self.add,i)) + + self.head = gr.head(gr.sizeof_gr_complex, self._N) + + # Construct the channelizer filter + self.pfb = filter.pfb.channelizer_ccf(self._M, self._taps, 1) + + # Construct a vector sink for the input signal to the channelizer + self.snk_i = gr.vector_sink_c() + + # Connect the blocks + self.connect(self.add, self.head, self.pfb) + self.connect(self.add, self.snk_i) + + # Use this to play with the channel mapping + #self.pfb.set_channel_map([5,6,7,8,0,1,2,3,4]) + + # Create a vector sink for each of M output channels of the filter and connect it + self.snks = list() + for i in xrange(self._M): + self.snks.append(gr.vector_sink_c()) + self.connect((self.pfb, i), self.snks[i]) + + +def main(): + tstart = time.time() + + tb = pfb_top_block() + tb.run() + + tend = time.time() + print "Run time: %f" % (tend - tstart) + + if 1: + fig_in = pylab.figure(1, figsize=(16,9), facecolor="w") + fig1 = pylab.figure(2, figsize=(16,9), facecolor="w") + fig2 = pylab.figure(3, figsize=(16,9), facecolor="w") + + Ns = 1000 + Ne = 10000 + + fftlen = 8192 + winfunc = scipy.blackman + fs = tb._ifs + + # Plot the input signal on its own figure + d = tb.snk_i.data()[Ns:Ne] + spin_f = fig_in.add_subplot(2, 1, 1) + + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_in = 10.0*scipy.log10(abs(X)) + f_in = scipy.arange(-fs/2.0, fs/2.0, fs/float(X_in.size)) + pin_f = spin_f.plot(f_in, X_in, "b") + spin_f.set_xlim([min(f_in), max(f_in)+1]) + spin_f.set_ylim([-200.0, 50.0]) + + spin_f.set_title("Input Signal", weight="bold") + spin_f.set_xlabel("Frequency (Hz)") + spin_f.set_ylabel("Power (dBW)") + + + Ts = 1.0/fs + Tmax = len(d)*Ts + + t_in = scipy.arange(0, Tmax, Ts) + x_in = scipy.array(d) + spin_t = fig_in.add_subplot(2, 1, 2) + pin_t = spin_t.plot(t_in, x_in.real, "b") + pin_t = spin_t.plot(t_in, x_in.imag, "r") + + spin_t.set_xlabel("Time (s)") + spin_t.set_ylabel("Amplitude") + + Ncols = int(scipy.floor(scipy.sqrt(tb._M))) + Nrows = int(scipy.floor(tb._M / Ncols)) + if(tb._M % Ncols != 0): + Nrows += 1 + + # Plot each of the channels outputs. Frequencies on Figure 2 and + # time signals on Figure 3 + fs_o = tb._fs + Ts_o = 1.0/fs_o + Tmax_o = len(d)*Ts_o + for i in xrange(len(tb.snks)): + # remove issues with the transients at the beginning + # also remove some corruption at the end of the stream + # this is a bug, probably due to the corner cases + d = tb.snks[i].data()[Ns:Ne] + + sp1_f = fig1.add_subplot(Nrows, Ncols, 1+i) + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs_o, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_o = 10.0*scipy.log10(abs(X)) + f_o = scipy.arange(-fs_o/2.0, fs_o/2.0, fs_o/float(X_o.size)) + p2_f = sp1_f.plot(f_o, X_o, "b") + sp1_f.set_xlim([min(f_o), max(f_o)+1]) + sp1_f.set_ylim([-200.0, 50.0]) + + sp1_f.set_title(("Channel %d" % i), weight="bold") + sp1_f.set_xlabel("Frequency (Hz)") + sp1_f.set_ylabel("Power (dBW)") + + x_o = scipy.array(d) + t_o = scipy.arange(0, Tmax_o, Ts_o) + sp2_o = fig2.add_subplot(Nrows, Ncols, 1+i) + p2_o = sp2_o.plot(t_o, x_o.real, "b") + p2_o = sp2_o.plot(t_o, x_o.imag, "r") + sp2_o.set_xlim([min(t_o), max(t_o)+1]) + sp2_o.set_ylim([-2, 2]) + + sp2_o.set_title(("Channel %d" % i), weight="bold") + sp2_o.set_xlabel("Time (s)") + sp2_o.set_ylabel("Amplitude") + + pylab.show() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/chirp_channelize.py b/gr-filter/examples/chirp_channelize.py new file mode 100755 index 000000000..99bf3b6e2 --- /dev/null +++ b/gr-filter/examples/chirp_channelize.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# +# Copyright 2009,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blks2 +from gnuradio import filter +import sys, time + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab + from pylab import mlab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +class pfb_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self._N = 200000 # number of samples to use + self._fs = 9000 # initial sampling rate + self._M = 9 # Number of channels to channelize + + # Create a set of taps for the PFB channelizer + self._taps = gr.firdes.low_pass_2(1, self._fs, 500, 20, + attenuation_dB=10, window=gr.firdes.WIN_BLACKMAN_hARRIS) + + # Calculate the number of taps per channel for our own information + tpc = scipy.ceil(float(len(self._taps)) / float(self._M)) + print "Number of taps: ", len(self._taps) + print "Number of channels: ", self._M + print "Taps per channel: ", tpc + + repeated = True + if(repeated): + self.vco_input = gr.sig_source_f(self._fs, gr.GR_SIN_WAVE, 0.25, 110) + else: + amp = 100 + data = scipy.arange(0, amp, amp/float(self._N)) + self.vco_input = gr.vector_source_f(data, False) + + # Build a VCO controlled by either the sinusoid or single chirp tone + # Then convert this to a complex signal + self.vco = gr.vco_f(self._fs, 225, 1) + self.f2c = gr.float_to_complex() + + self.head = gr.head(gr.sizeof_gr_complex, self._N) + + # Construct the channelizer filter + self.pfb = filter.pfb.channelizer_ccf(self._M, self._taps) + + # Construct a vector sink for the input signal to the channelizer + self.snk_i = gr.vector_sink_c() + + # Connect the blocks + self.connect(self.vco_input, self.vco, self.f2c) + self.connect(self.f2c, self.head, self.pfb) + self.connect(self.f2c, self.snk_i) + + # Create a vector sink for each of M output channels of the filter and connect it + self.snks = list() + for i in xrange(self._M): + self.snks.append(gr.vector_sink_c()) + self.connect((self.pfb, i), self.snks[i]) + + +def main(): + tstart = time.time() + + tb = pfb_top_block() + tb.run() + + tend = time.time() + print "Run time: %f" % (tend - tstart) + + if 1: + fig_in = pylab.figure(1, figsize=(16,9), facecolor="w") + fig1 = pylab.figure(2, figsize=(16,9), facecolor="w") + fig2 = pylab.figure(3, figsize=(16,9), facecolor="w") + fig3 = pylab.figure(4, figsize=(16,9), facecolor="w") + + Ns = 650 + Ne = 20000 + + fftlen = 8192 + winfunc = scipy.blackman + fs = tb._fs + + # Plot the input signal on its own figure + d = tb.snk_i.data()[Ns:Ne] + spin_f = fig_in.add_subplot(2, 1, 1) + + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_in = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_in = scipy.arange(-fs/2.0, fs/2.0, fs/float(X_in.size)) + pin_f = spin_f.plot(f_in, X_in, "b") + spin_f.set_xlim([min(f_in), max(f_in)+1]) + spin_f.set_ylim([-200.0, 50.0]) + + spin_f.set_title("Input Signal", weight="bold") + spin_f.set_xlabel("Frequency (Hz)") + spin_f.set_ylabel("Power (dBW)") + + + Ts = 1.0/fs + Tmax = len(d)*Ts + + t_in = scipy.arange(0, Tmax, Ts) + x_in = scipy.array(d) + spin_t = fig_in.add_subplot(2, 1, 2) + pin_t = spin_t.plot(t_in, x_in.real, "b") + pin_t = spin_t.plot(t_in, x_in.imag, "r") + + spin_t.set_xlabel("Time (s)") + spin_t.set_ylabel("Amplitude") + + Ncols = int(scipy.floor(scipy.sqrt(tb._M))) + Nrows = int(scipy.floor(tb._M / Ncols)) + if(tb._M % Ncols != 0): + Nrows += 1 + + # Plot each of the channels outputs. Frequencies on Figure 2 and + # time signals on Figure 3 + fs_o = tb._fs / tb._M + Ts_o = 1.0/fs_o + Tmax_o = len(d)*Ts_o + for i in xrange(len(tb.snks)): + # remove issues with the transients at the beginning + # also remove some corruption at the end of the stream + # this is a bug, probably due to the corner cases + d = tb.snks[i].data()[Ns:Ne] + + sp1_f = fig1.add_subplot(Nrows, Ncols, 1+i) + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs_o, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_o = 10.0*scipy.log10(abs(X)) + f_o = freq + p2_f = sp1_f.plot(f_o, X_o, "b") + sp1_f.set_xlim([min(f_o), max(f_o)+1]) + sp1_f.set_ylim([-200.0, 50.0]) + + sp1_f.set_title(("Channel %d" % i), weight="bold") + sp1_f.set_xlabel("Frequency (Hz)") + sp1_f.set_ylabel("Power (dBW)") + + x_o = scipy.array(d) + t_o = scipy.arange(0, Tmax_o, Ts_o) + sp2_o = fig2.add_subplot(Nrows, Ncols, 1+i) + p2_o = sp2_o.plot(t_o, x_o.real, "b") + p2_o = sp2_o.plot(t_o, x_o.imag, "r") + sp2_o.set_xlim([min(t_o), max(t_o)+1]) + sp2_o.set_ylim([-2, 2]) + + sp2_o.set_title(("Channel %d" % i), weight="bold") + sp2_o.set_xlabel("Time (s)") + sp2_o.set_ylabel("Amplitude") + + + sp3 = fig3.add_subplot(1,1,1) + p3 = sp3.plot(t_o, x_o.real) + sp3.set_xlim([min(t_o), max(t_o)+1]) + sp3.set_ylim([-2, 2]) + + sp3.set_title("All Channels") + sp3.set_xlabel("Time (s)") + sp3.set_ylabel("Amplitude") + + pylab.show() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 6ace8a8b4..1c69e47c3 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -23,6 +23,7 @@ install(FILES fft_filter_xxx.xml filter_delay_fc.xml hilbert_fc.xml + pfb_channelizer.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index fbc4e7cb5..0c685d9f6 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -34,5 +34,6 @@ fft_filter_xxx filter_delay_fc hilbert_fc + pfb_channelizer_ccf diff --git a/gr-filter/grc/pfb_channelizer.xml b/gr-filter/grc/pfb_channelizer.xml new file mode 100644 index 000000000..114abc0f0 --- /dev/null +++ b/gr-filter/grc/pfb_channelizer.xml @@ -0,0 +1,62 @@ + + + + Polyphase Channelizer + pfb_channelizer_ccf + from gnuradio import filter + from gnuradio.filter import firdes + filter.pfb.channelizer_ccf( + $nchans, + $taps, + $osr, + $atten) +self.$(id).set_channel_map($ch_map) + + + set_channel_map($ch_map) + + + Channels + nchans + int + + + Taps + taps + None + real_vector + + + Over Sample Ratio + osr + 1.0 + real + + + Attenuation + atten + 100 + real + + + Channel Map + ch_map + [] + int_vector + + + in + complex + + + out + complex + $nchans + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index d5452a130..c64178696 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -87,6 +87,7 @@ install(FILES fft_filter_ccc.h fft_filter_fff.h hilbert_fc.h + pfb_channelizer_ccf.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "fft_devel" ) diff --git a/gr-filter/include/filter/pfb_channelizer_ccf.h b/gr-filter/include/filter/pfb_channelizer_ccf.h new file mode 100644 index 000000000..ab79f7036 --- /dev/null +++ b/gr-filter/include/filter/pfb_channelizer_ccf.h @@ -0,0 +1,205 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_FILTER_PFB_CHANNELIZER_CCF_H +#define INCLUDED_FILTER_PFB_CHANNELIZER_CCF_H + +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \class gr_pfb_channelizer_ccf + * + * \brief Polyphase filterbank channelizer with + * gr_complex input, gr_complex output and float taps + * + * \ingroup filter_blk + * \ingroup pfb_blk + * + * This block takes in complex inputs and channelizes it to M + * channels of equal bandwidth. Each of the resulting channels is + * decimated to the new rate that is the input sampling rate + * fs divided by the number of channels, M. + * + * The PFB channelizer code takes the taps generated above and builds + * a set of filters. The set contains M number of filters + * and each filter contains ceil(taps.size()/decim) number of taps. + * Each tap from the filter prototype is sequentially inserted into + * the next filter. When all of the input taps are used, the remaining + * filters in the filterbank are filled out with 0's to make sure each + * filter has the same number of taps. + * + * Each filter operates using the gr_fir filter classs of GNU Radio, + * which takes the input stream at i and performs the inner + * product calculation to i+(n-1) where n is the + * number of filter taps. To efficiently handle this in the GNU Radio + * structure, each filter input must come from its own input + * stream. So the channelizer must be provided with M streams + * where the input stream has been deinterleaved. This is most easily + * done using the gr_stream_to_streams block. + * + * The output is then produced as a vector, where index i in + * the vector is the next sample from the ith channel. This + * is most easily handled by sending the output to a + * gr_vector_to_streams block to handle the conversion and passing + * M streams out. + * + * The input and output formatting is done using a hier_block2 called + * pfb_channelizer_ccf. This can take in a single stream and outputs + * M streams based on the behavior described above. + * + * The filter's taps should be based on the input sampling rate. + * + * For example, using the GNU Radio's firdes utility to building + * filters, we build a low-pass filter with a sampling rate of + * fs, a 3-dB bandwidth of BW and a transition + * bandwidth of TB. We can also specify the out-of-band + * attenuation to use, ATT, and the filter window + * function (a Blackman-harris window in this case). The first input + * is the gain of the filter, which we specify here as unity. + * + * self._taps = gr.firdes.low_pass_2(1, fs, BW, TB, + * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * + * The filter output can also be overs ampled. The over sampling rate + * is the ratio of the the actual output sampling rate to the normal + * output sampling rate. It must be rationally related to the number + * of channels as N/i for i in [1,N], which gives an outputsample rate + * of [fs/N, fs] where fs is the input sample rate and N is the number + * of channels. + * + * For example, for 6 channels with fs = 6000 Hz, the normal rate is + * 6000/6 = 1000 Hz. Allowable oversampling rates are 6/6, 6/5, 6/4, + * 6/3, 6/2, and 6/1 where the output sample rate of a 6/1 oversample + * ratio is 6000 Hz, or 6 times the normal 1000 Hz. A rate of 6/5 = 1.2, + * so the output rate would be 1200 Hz. + * + * The theory behind this block can be found in Chapter 6 of + * the following book. + * + * f. harris, "Multirate Signal Processing for Communication + * Systems," Upper Saddle River, NJ: Prentice Hall, Inc. 2004. + * + */ + + class FILTER_API pfb_channelizer_ccf : virtual public gr_block + { + public: + // gr::filter::pfb_channelizer_ccf::sptr + typedef boost::shared_ptr sptr; + + /*! + * Build the polyphase filterbank decimator. + * \param numchans (unsigned integer) Specifies the number of + * channels M + * \param taps (vector/list of floats) The prototype filter to + * populate the filterbank. + * \param oversample_rate (float) The over sampling rate is the + * ratio of the the actual output + * sampling rate to the normal + * output sampling rate. It must + * be rationally related to the + * number of channels as N/i for + * i in [1,N], which gives an + * outputsample rate of [fs/N, + * fs] where fs is the input + * sample rate and N is the + * number of channels. + * + * For example, for 6 channels + * with fs = 6000 Hz, the normal + * rateis 6000/6 = 1000 + * Hz. Allowable oversampling + * rates are 6/6, 6/5, 6/4, 6/3, + * 6/2, and 6/1 where the output + * sample rate of a 6/1 + * oversample ratio is 6000 Hz, + * or 6 times the normal 1000 Hz. + */ + static FILTER_API sptr make(unsigned int numchans, + const std::vector &taps, + float oversample_rate); + + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + */ + virtual void set_taps(const std::vector &taps) = 0; + + /*! + * Print all of the filterbank taps to screen. + */ + virtual void print_taps() = 0; + + /*! + * Return a vector> of the filterbank taps + */ + virtual std::vector > taps() const = 0; + + /*! + * Set the channel map. Channels are numbers as: + * + * N/2+1 | ... | N-1 | 0 | 1 | 2 | ... | N/2 + * <------------------- 0 --------------------> + * freq + * + * So output stream 0 comes from channel 0, etc. Setting a new + * channel map allows the user to specify which channel in frequency + * he/she wants to got to which output stream. + * + * The map should have the same number of elements as the number + * of output connections from the block. The minimum value of + * the map is 0 (for the 0th channel) and the maximum number is + * N-1 where N is the number of channels. + * + * We specify M as the number of output connections made where M + * <= N, so only M out of N channels are driven to an output + * stream. The number of items in the channel map should be at + * least M long. If there are more channels specified, any value + * in the map over M-1 will be ignored. If the size of the map + * is less than M the behavior is unknown (we don't wish to + * check every entry into the work function). + * + * This means that if the channelizer is splitting the signal up + * into N channels but only M channels are specified in the map + * (where M <= N), then M output streams must be connected and + * the map and the channel numbers used must be less than + * N-1. Output channel number can be reused, too. By default, + * the map is [0...M-1] with M = N. + */ + virtual void set_channel_map(const std::vector &map) = 0; + + /*! + * Gets the current channel map. + */ + virtual std::vector channel_map() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_PFB_CHANNELIZER_CCF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index e1fae56a3..5930ec9bb 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -117,6 +117,7 @@ list(APPEND filter_sources fft_filter_ccc_impl.cc fft_filter_fff_impl.cc hilbert_fc_impl.cc + pfb_channelizer_ccf_impl.cc ) list(APPEND filter_libs diff --git a/gr-filter/lib/hilbert_fc_impl.h b/gr-filter/lib/hilbert_fc_impl.h index de4d754b4..d2b41b573 100644 --- a/gr-filter/lib/hilbert_fc_impl.h +++ b/gr-filter/lib/hilbert_fc_impl.h @@ -25,7 +25,6 @@ #include #include -#include #include namespace gr { diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.cc b/gr-filter/lib/pfb_channelizer_ccf_impl.cc new file mode 100644 index 000000000..4006db789 --- /dev/null +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.cc @@ -0,0 +1,240 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pfb_channelizer_ccf_impl.h" +#include +#include + +namespace gr { + namespace filter { + + pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int numchans, + const std::vector &taps, + float oversample_rate) + { + return gnuradio::get_initial_sptr(new pfb_channelizer_ccf_impl(numchans, taps, + oversample_rate)); + } + + pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int numchans, + const std::vector &taps, + float oversample_rate) + : gr_block("pfb_channelizer_ccf", + gr_make_io_signature(numchans, numchans, sizeof(gr_complex)), + gr_make_io_signature(1, numchans, sizeof(gr_complex))), + d_updated(false), d_numchans(numchans), d_oversample_rate(oversample_rate) + { + // The over sampling rate must be rationally related to the number of channels + // in that it must be N/i for i in [1,N], which gives an outputsample rate + // of [fs/N, fs] where fs is the input sample rate. + // This tests the specified input sample rate to see if it conforms to this + // requirement within a few significant figures. + double intp = 0; + double fltp = modf(numchans / oversample_rate, &intp); + if(fltp != 0.0) + throw std::invalid_argument("pfb_channelizer: oversample rate must be N/i for i in [1, N]"); + + set_relative_rate(1.0/intp); + + d_filters = std::vector(d_numchans); + d_channel_map.resize(d_numchans); + + // Create an FIR filter for each channel and zero out the taps + std::vector vtaps(0, d_numchans); + for(unsigned int i = 0; i < d_numchans; i++) { + d_filters[i] = new kernel::fir_filter_ccf(1, vtaps); + d_channel_map[i] = i; + } + + // Now, actually set the filters' taps + set_taps(taps); + + // Create the FFT to handle the output de-spinning of the channels + d_fft = new fft::fft_complex(d_numchans, false); + + // Although the filters change, we use this look up table + // to set the index of the FFT input buffer, which equivalently + // performs the FFT shift operation on every other turn. + d_rate_ratio = (int)rintf(d_numchans / d_oversample_rate); + d_idxlut = new int[d_numchans]; + for(unsigned int i = 0; i < d_numchans; i++) { + d_idxlut[i] = d_numchans - ((i + d_rate_ratio) % d_numchans) - 1; + } + + // Calculate the number of filtering rounds to do to evenly + // align the input vectors with the output channels + d_output_multiple = 1; + while((d_output_multiple * d_rate_ratio) % d_numchans != 0) + d_output_multiple++; + set_output_multiple(d_output_multiple); + } + + pfb_channelizer_ccf_impl::~pfb_channelizer_ccf_impl() + { + delete [] d_idxlut; + delete d_fft; + + for(unsigned int i = 0; i < d_numchans; i++) { + delete d_filters[i]; + } + } + + void + pfb_channelizer_ccf_impl::set_taps (const std::vector &taps) + { + gruel::scoped_lock guard(d_mutex); + unsigned int i,j; + + unsigned int ntaps = taps.size(); + d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_numchans); + + // Create d_numchan vectors to store each channel's taps + d_taps.resize(d_numchans); + + // Make a vector of the taps plus fill it out with 0's to fill + // each polyphase filter with exactly d_taps_per_filter + std::vector tmp_taps; + tmp_taps = taps; + while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) { + tmp_taps.push_back(0.0); + } + + // Partition the filter + for(i = 0; i < d_numchans; i++) { + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + d_taps[i] = std::vector(d_taps_per_filter, 0); + for(j = 0; j < d_taps_per_filter; j++) { + d_taps[i][j] = tmp_taps[i + j*d_numchans]; // add taps to channels in reverse order + } + + // Build a filter for each channel and add it's taps to it + d_filters[i]->set_taps(d_taps[i]); + } + + // Set the history to ensure enough input items for each filter + set_history (d_taps_per_filter+1); + + d_updated = true; + } + + void + pfb_channelizer_ccf_impl::print_taps() + { + unsigned int i, j; + for(i = 0; i < d_numchans; i++) { + printf("filter[%d]: [", i); + for(j = 0; j < d_taps_per_filter; j++) { + printf(" %.4e", d_taps[i][j]); + } + printf("]\n\n"); + } + } + + std::vector< std::vector > + pfb_channelizer_ccf_impl::taps() const + { + return d_taps; + } + + void + pfb_channelizer_ccf_impl::set_channel_map(const std::vector &map) + { + gruel::scoped_lock guard(d_mutex); + + if(map.size() > 0) { + unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end()); + unsigned int min = (unsigned int)*std::min_element(map.begin(), map.end()); + if((max >= d_numchans) || (min < 0)) { + throw std::invalid_argument("pfb_channelizer_ccf_impl::set_channel_map: map range out of bounds.\n"); + } + d_channel_map = map; + } + } + + std::vector + pfb_channelizer_ccf_impl::channel_map() const + { + return d_channel_map; + } + + int + pfb_channelizer_ccf_impl::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gruel::scoped_lock guard(d_mutex); + + gr_complex *in = (gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + if (d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + size_t noutputs = output_items.size(); + + int n=1, i=-1, j=0, oo=0, last; + int toconsume = (int)rintf(noutput_items/d_oversample_rate); + while(n <= toconsume) { + j = 0; + i = (i + d_rate_ratio) % d_numchans; + last = i; + while(i >= 0) { + in = (gr_complex*)input_items[j]; + d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n]); + j++; + i--; + } + + i = d_numchans-1; + while(i > last) { + in = (gr_complex*)input_items[j]; + d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n-1]); + j++; + i--; + } + + n += (i+d_rate_ratio) >= (int)d_numchans; + + // despin through FFT + d_fft->execute(); + + // Send to output channels + for(unsigned int nn = 0; nn < noutputs; nn++) { + out = (gr_complex*)output_items[nn]; + out[oo] = d_fft->get_outbuf()[d_channel_map[nn]]; + } + oo++; + } + + consume_each(toconsume); + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.h b/gr-filter/lib/pfb_channelizer_ccf_impl.h new file mode 100644 index 000000000..ee9327663 --- /dev/null +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.h @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_PFB_CHANNELIZER_CCF_IMPL_H +#define INCLUDED_FILTER_PFB_CHANNELIZER_CCF_IMPL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf + { + private: + bool d_updated; + unsigned int d_numchans; + float d_oversample_rate; + std::vector d_filters; + std::vector< std::vector > d_taps; + unsigned int d_taps_per_filter; + fft::fft_complex *d_fft; + int *d_idxlut; + int d_rate_ratio; + int d_output_multiple; + std::vector d_channel_map; + gruel::mutex d_mutex; // mutex to protect set/work access + + public: + pfb_channelizer_ccf_impl(unsigned int numchans, + const std::vector &taps, + float oversample_rate); + + ~pfb_channelizer_ccf_impl(); + + void set_taps(const std::vector &taps); + void print_taps(); + std::vector > taps() const; + + void set_channel_map(const std::vector &map); + std::vector channel_map() const; + + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif diff --git a/gr-filter/python/CMakeLists.txt b/gr-filter/python/CMakeLists.txt index e52cac759..99a1aa3a9 100644 --- a/gr-filter/python/CMakeLists.txt +++ b/gr-filter/python/CMakeLists.txt @@ -23,6 +23,7 @@ include(GrPython) GR_PYTHON_INSTALL( FILES __init__.py + pfb.py DESTINATION ${GR_PYTHON_DIR}/gnuradio/filter COMPONENT "filter_python" ) diff --git a/gr-filter/python/__init__.py b/gr-filter/python/__init__.py index 56dd2dc5a..90b5ce0a4 100644 --- a/gr-filter/python/__init__.py +++ b/gr-filter/python/__init__.py @@ -25,4 +25,4 @@ processing blocks for FILTER and related functions. ''' from filter_swig import * - +import pfb diff --git a/gr-filter/python/pfb.py b/gr-filter/python/pfb.py new file mode 100644 index 000000000..9c7e18e31 --- /dev/null +++ b/gr-filter/python/pfb.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright 2009,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, optfir +import filter_swig as filter + +class channelizer_ccf(gr.hier_block2): + ''' + Make a Polyphase Filter channelizer (complex in, complex out, floating-point taps) + + This simplifies the interface by allowing a single input stream to connect to this block. + It will then output a stream for each channel. + ''' + def __init__(self, numchans, taps=None, oversample_rate=1, atten=100): + gr.hier_block2.__init__(self, "pfb_channelizer_ccf", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(numchans, numchans, gr.sizeof_gr_complex)) # Output signature + + self._nchans = numchans + self._oversample_rate = oversample_rate + + if taps is not None: + self._taps = taps + else: + # Create a filter that covers the full bandwidth of the input signal + bw = 0.4 + tb = 0.2 + ripple = 0.1 + made = False + while not made: + try: + self._taps = optfir.low_pass(1, self._nchans, bw, bw+tb, ripple, atten) + made = True + except RuntimeError: + ripple += 0.01 + made = False + print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple)) + + # Build in an exit strategy; if we've come this far, it ain't working. + if(ripple >= 1.0): + raise RuntimeError("optfir could not generate an appropriate filter.") + + self.s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, self._nchans) + self.pfb = filter.pfb_channelizer_ccf(self._nchans, self._taps, + self._oversample_rate) + self.connect(self, self.s2ss) + + for i in xrange(self._nchans): + self.connect((self.s2ss,i), (self.pfb,i)) + self.connect((self.pfb,i), (self,i)) + + def set_channel_map(self, newmap): + self.pfb.set_channel_map(newmap) + + diff --git a/gr-filter/python/qa_pfb_channelizer.py b/gr-filter/python/qa_pfb_channelizer.py new file mode 100644 index 000000000..b52c80e8b --- /dev/null +++ b/gr-filter/python/qa_pfb_channelizer.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_pfb_channelizer(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_000(self): + N = 1000 # number of samples to use + M = 5 # Number of channels to channelize + fs = 1000 # baseband sampling rate + ifs = M*fs # input samp rate to channelizer + + taps = gr.firdes.low_pass_2(1, ifs, 500, 50, + attenuation_dB=80, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + signals = list() + add = gr.add_cc() + freqs = [-200, -100, 0, 100, 200] + for i in xrange(len(freqs)): + f = freqs[i] + (M/2-M+i+1)*fs + signals.append(gr.sig_source_c(ifs, gr.GR_SIN_WAVE, f, 1)) + self.tb.connect(signals[i], (add,i)) + + head = gr.head(gr.sizeof_gr_complex, N) + s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, M) + pfb = filter.pfb_channelizer_ccf(M, taps, 1) + + self.tb.connect(add, head, s2ss) + + snks = list() + for i in xrange(M): + snks.append(gr.vector_sink_c()) + self.tb.connect((s2ss,i), (pfb,i)) + self.tb.connect((pfb, i), snks[i]) + + self.tb.run() + + Ntest = 50 + L = len(snks[0].data()) + t = map(lambda x: float(x)/fs, xrange(L)) + + # Adjusted phase rotations for data + p0 = 0 + p1 = 1.6335486 + p2 = -3.01609 + p3 = 3.01609 + p4 = -1.6335486 + + # Create known data as complex sinusoids at the different baseband freqs + # the different channel numbering is due to channelizer output order. + expected0_data = map(lambda x: math.cos(2.*math.pi*freqs[2]*x+p0) + \ + 1j*math.sin(2.*math.pi*freqs[2]*x+p0), t) + expected1_data = map(lambda x: math.cos(2.*math.pi*freqs[3]*x+p1) + \ + 1j*math.sin(2.*math.pi*freqs[3]*x+p1), t) + expected2_data = map(lambda x: math.cos(2.*math.pi*freqs[4]*x+p2) + \ + 1j*math.sin(2.*math.pi*freqs[4]*x+p2), t) + expected3_data = map(lambda x: math.cos(2.*math.pi*freqs[0]*x+p3) + \ + 1j*math.sin(2.*math.pi*freqs[0]*x+p3), t) + expected4_data = map(lambda x: math.cos(2.*math.pi*freqs[1]*x+p4) + \ + 1j*math.sin(2.*math.pi*freqs[1]*x+p4), t) + + dst0_data = snks[0].data() + dst1_data = snks[1].data() + dst2_data = snks[2].data() + dst3_data = snks[3].data() + dst4_data = snks[4].data() + + self.assertComplexTuplesAlmostEqual(expected0_data[-Ntest:], dst0_data[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected1_data[-Ntest:], dst1_data[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected2_data[-Ntest:], dst2_data[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected3_data[-Ntest:], dst3_data[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected4_data[-Ntest:], dst4_data[-Ntest:], 4) + +if __name__ == '__main__': + gr_unittest.run(test_pfb_channelizer, "test_pfb_channelizer.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 4f0caa59c..cc15b5722 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -39,6 +39,7 @@ #include "filter/fft_filter_ccc.h" #include "filter/fft_filter_fff.h" #include "filter/hilbert_fc.h" +#include "filter/pfb_channelizer_ccf.h" %} %include "filter/firdes.h" @@ -52,6 +53,7 @@ %include "filter/fft_filter_ccc.h" %include "filter/fft_filter_fff.h" %include "filter/hilbert_fc.h" +%include "filter/pfb_channelizer_ccf.h" GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_cc); GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_ff); @@ -62,3 +64,4 @@ GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, hilbert_fc); +GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); -- cgit From 274bc141208ac8209c6b3342ff5ddc3755b321de Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 8 May 2012 22:43:48 -0400 Subject: filter: created polyphase_filterbank parent class to handle some initialization and setting taps of PFB blocks. Converted pfb_channelizer_ccf over to new parent class. Passes QA. --- gr-filter/include/filter/CMakeLists.txt | 3 +- gr-filter/include/filter/polyphase_filterbank.h | 148 ++++++++++++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/pfb_channelizer_ccf_impl.cc | 102 +++++----------- gr-filter/lib/pfb_channelizer_ccf_impl.h | 24 ++-- gr-filter/lib/polyphase_filterbank.cc | 114 ++++++++++++++++++ 6 files changed, 302 insertions(+), 90 deletions(-) create mode 100644 gr-filter/include/filter/polyphase_filterbank.h create mode 100644 gr-filter/lib/polyphase_filterbank.cc diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index c64178696..0c067fcba 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -76,10 +76,11 @@ add_custom_target(filter_generated_includes DEPENDS install(FILES api.h firdes.h - pm_remez.h fir_filter.h fir_filter_with_buffer.h fft_filter.h + pm_remez.h + polyphase_filterbank.h ${generated_includes} dc_blocker_cc.h dc_blocker_ff.h diff --git a/gr-filter/include/filter/polyphase_filterbank.h b/gr-filter/include/filter/polyphase_filterbank.h new file mode 100644 index 000000000..50d087340 --- /dev/null +++ b/gr-filter/include/filter/polyphase_filterbank.h @@ -0,0 +1,148 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_FILTER_POLYPHASE_FILTERBANK_H +#define INCLUDED_FILTER_POLYPHASE_FILTERBANK_H + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + /*! + * \class polyphase_filterbank + * + * \brief Polyphase filterbank parent class + * + * \ingroup filter_blk + * \ingroup pfb_blk + * + * This block takes in complex inputs and channelizes it to + * M channels of equal bandwidth. Each of the resulting + * channels is decimated to the new rate that is the input + * sampling rate fs divided by the number of channels, + * M. + * + * The PFB channelizer code takes the taps generated above and + * builds a set of filters. The set contains M number + * of filters and each filter contains ceil(taps.size()/decim) + * number of taps. Each tap from the filter prototype is + * sequentially inserted into the next filter. When all of the + * input taps are used, the remaining filters in the filterbank + * are filled out with 0's to make sure each filter has the same + * number of taps. + * + * Each filter operates using the gr_fir filter classs of GNU + * Radio, which takes the input stream at i and + * performs the inner product calculation to i+(n-1) + * where n is the number of filter taps. To efficiently + * handle this in the GNU Radio structure, each filter input + * must come from its own input stream. So the channelizer must + * be provided with M streams where the input stream + * has been deinterleaved. This is most easily done using the + * gr_stream_to_streams block. + * + * The output is then produced as a vector, where index + * i in the vector is the next sample from the + * ith channel. This is most easily handled by sending + * the output to a gr_vector_to_streams block to handle the + * conversion and passing M streams out. + * + * The input and output formatting is done using a hier_block2 + * called pfb_channelizer_ccf. This can take in a single stream + * and outputs M streams based on the behavior + * described above. + * + * The filter's taps should be based on the input sampling rate. + * + * For example, using the GNU Radio's firdes utility to building + * filters, we build a low-pass filter with a sampling rate of + * fs, a 3-dB bandwidth of BW and a transition + * bandwidth of TB. We can also specify the out-of-band + * attenuation to use, ATT, and the filter window + * function (a Blackman-harris window in this case). The first + * input is the gain of the filter, which we specify here as + * unity. + * + * self._taps = gr.firdes.low_pass_2(1, fs, BW, TB, + * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * + * More on the theory of polyphase filterbanks can be found in + * the following book. + * + * f. harris, "Multirate Signal Processing for + * Communication Systems," Upper Saddle River, NJ: + * Prentice Hall, Inc. 2004. + * + */ + + class FILTER_API polyphase_filterbank + { + protected: + unsigned int d_nfilts; + std::vector d_filters; + std::vector< std::vector > d_taps; + unsigned int d_taps_per_filter; + fft::fft_complex *d_fft; + + public: + /*! + * Build the polyphase filterbank decimator. + * \param nfilts (unsigned integer) Specifies the number of + * channels M + * \param taps (vector/list of floats) The prototype filter to + * populate the filterbank. + */ + polyphase_filterbank(unsigned int nfilts, + const std::vector &taps); + + ~polyphase_filterbank(); + + /*! + * Update the filterbank's filter taps from a prototype + * filter. + * + * \param taps (vector/list of floats) The prototype filter to + * populate the filterbank. + */ + void set_taps(const std::vector &taps); + + /*! + * Print all of the filterbank taps to screen. + */ + void print_taps(); + + /*! + * Return a vector> of the filterbank taps + */ + std::vector > taps() const; + }; + + } /* namespace kernel */ + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_POLYPHASE_FILTERBANK_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 5930ec9bb..b7577ad5e 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -110,6 +110,7 @@ list(APPEND filter_sources fft_filter.cc firdes.cc pm_remez.cc + polyphase_filterbank.cc ${generated_sources} dc_blocker_cc_impl.cc dc_blocker_ff_impl.cc diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.cc b/gr-filter/lib/pfb_channelizer_ccf_impl.cc index 4006db789..4e06cbc5f 100644 --- a/gr-filter/lib/pfb_channelizer_ccf_impl.cc +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.cc @@ -26,26 +26,26 @@ #include "pfb_channelizer_ccf_impl.h" #include -#include namespace gr { namespace filter { - pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int numchans, + pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int nfilts, const std::vector &taps, float oversample_rate) { - return gnuradio::get_initial_sptr(new pfb_channelizer_ccf_impl(numchans, taps, + return gnuradio::get_initial_sptr(new pfb_channelizer_ccf_impl(nfilts, taps, oversample_rate)); } - pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int numchans, + pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int nfilts, const std::vector &taps, float oversample_rate) : gr_block("pfb_channelizer_ccf", - gr_make_io_signature(numchans, numchans, sizeof(gr_complex)), - gr_make_io_signature(1, numchans, sizeof(gr_complex))), - d_updated(false), d_numchans(numchans), d_oversample_rate(oversample_rate) + gr_make_io_signature(nfilts, nfilts, sizeof(gr_complex)), + gr_make_io_signature(1, nfilts, sizeof(gr_complex))), + polyphase_filterbank(nfilts, taps), + d_updated(false), d_oversample_rate(oversample_rate) { // The over sampling rate must be rationally related to the number of channels // in that it must be N/i for i in [1,N], which gives an outputsample rate @@ -53,110 +53,62 @@ namespace gr { // This tests the specified input sample rate to see if it conforms to this // requirement within a few significant figures. double intp = 0; - double fltp = modf(numchans / oversample_rate, &intp); + double fltp = modf(nfilts / oversample_rate, &intp); if(fltp != 0.0) throw std::invalid_argument("pfb_channelizer: oversample rate must be N/i for i in [1, N]"); set_relative_rate(1.0/intp); - d_filters = std::vector(d_numchans); - d_channel_map.resize(d_numchans); - - // Create an FIR filter for each channel and zero out the taps - std::vector vtaps(0, d_numchans); - for(unsigned int i = 0; i < d_numchans; i++) { - d_filters[i] = new kernel::fir_filter_ccf(1, vtaps); + // Default channel map + d_channel_map.resize(d_nfilts); + for(unsigned int i = 0; i < d_nfilts; i++) { d_channel_map[i] = i; } - // Now, actually set the filters' taps - set_taps(taps); - - // Create the FFT to handle the output de-spinning of the channels - d_fft = new fft::fft_complex(d_numchans, false); - // Although the filters change, we use this look up table // to set the index of the FFT input buffer, which equivalently // performs the FFT shift operation on every other turn. - d_rate_ratio = (int)rintf(d_numchans / d_oversample_rate); - d_idxlut = new int[d_numchans]; - for(unsigned int i = 0; i < d_numchans; i++) { - d_idxlut[i] = d_numchans - ((i + d_rate_ratio) % d_numchans) - 1; + d_rate_ratio = (int)rintf(d_nfilts / d_oversample_rate); + d_idxlut = new int[d_nfilts]; + for(unsigned int i = 0; i < d_nfilts; i++) { + d_idxlut[i] = d_nfilts - ((i + d_rate_ratio) % d_nfilts) - 1; } // Calculate the number of filtering rounds to do to evenly // align the input vectors with the output channels d_output_multiple = 1; - while((d_output_multiple * d_rate_ratio) % d_numchans != 0) + while((d_output_multiple * d_rate_ratio) % d_nfilts != 0) d_output_multiple++; set_output_multiple(d_output_multiple); + + set_history (d_taps_per_filter+1); } pfb_channelizer_ccf_impl::~pfb_channelizer_ccf_impl() { delete [] d_idxlut; - delete d_fft; - - for(unsigned int i = 0; i < d_numchans; i++) { - delete d_filters[i]; - } } void - pfb_channelizer_ccf_impl::set_taps (const std::vector &taps) + pfb_channelizer_ccf_impl::set_taps(const std::vector &taps) { gruel::scoped_lock guard(d_mutex); - unsigned int i,j; - - unsigned int ntaps = taps.size(); - d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_numchans); - - // Create d_numchan vectors to store each channel's taps - d_taps.resize(d_numchans); - // Make a vector of the taps plus fill it out with 0's to fill - // each polyphase filter with exactly d_taps_per_filter - std::vector tmp_taps; - tmp_taps = taps; - while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) { - tmp_taps.push_back(0.0); - } - - // Partition the filter - for(i = 0; i < d_numchans; i++) { - // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out - d_taps[i] = std::vector(d_taps_per_filter, 0); - for(j = 0; j < d_taps_per_filter; j++) { - d_taps[i][j] = tmp_taps[i + j*d_numchans]; // add taps to channels in reverse order - } - - // Build a filter for each channel and add it's taps to it - d_filters[i]->set_taps(d_taps[i]); - } - - // Set the history to ensure enough input items for each filter + polyphase_filterbank::set_taps(taps); set_history (d_taps_per_filter+1); - d_updated = true; } void pfb_channelizer_ccf_impl::print_taps() { - unsigned int i, j; - for(i = 0; i < d_numchans; i++) { - printf("filter[%d]: [", i); - for(j = 0; j < d_taps_per_filter; j++) { - printf(" %.4e", d_taps[i][j]); - } - printf("]\n\n"); - } + polyphase_filterbank::print_taps(); } - std::vector< std::vector > + std::vector > pfb_channelizer_ccf_impl::taps() const { - return d_taps; + return polyphase_filterbank::taps(); } void @@ -167,7 +119,7 @@ namespace gr { if(map.size() > 0) { unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end()); unsigned int min = (unsigned int)*std::min_element(map.begin(), map.end()); - if((max >= d_numchans) || (min < 0)) { + if((max >= d_nfilts) || (min < 0)) { throw std::invalid_argument("pfb_channelizer_ccf_impl::set_channel_map: map range out of bounds.\n"); } d_channel_map = map; @@ -202,7 +154,7 @@ namespace gr { int toconsume = (int)rintf(noutput_items/d_oversample_rate); while(n <= toconsume) { j = 0; - i = (i + d_rate_ratio) % d_numchans; + i = (i + d_rate_ratio) % d_nfilts; last = i; while(i >= 0) { in = (gr_complex*)input_items[j]; @@ -211,7 +163,7 @@ namespace gr { i--; } - i = d_numchans-1; + i = d_nfilts-1; while(i > last) { in = (gr_complex*)input_items[j]; d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n-1]); @@ -219,7 +171,7 @@ namespace gr { i--; } - n += (i+d_rate_ratio) >= (int)d_numchans; + n += (i+d_rate_ratio) >= (int)d_nfilts; // despin through FFT d_fft->execute(); diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.h b/gr-filter/lib/pfb_channelizer_ccf_impl.h index ee9327663..8cabc4e34 100644 --- a/gr-filter/lib/pfb_channelizer_ccf_impl.h +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.h @@ -24,30 +24,26 @@ #define INCLUDED_FILTER_PFB_CHANNELIZER_CCF_IMPL_H #include +#include #include #include namespace gr { namespace filter { - class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf + class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf, kernel::polyphase_filterbank { private: - bool d_updated; - unsigned int d_numchans; - float d_oversample_rate; - std::vector d_filters; - std::vector< std::vector > d_taps; - unsigned int d_taps_per_filter; - fft::fft_complex *d_fft; - int *d_idxlut; - int d_rate_ratio; - int d_output_multiple; - std::vector d_channel_map; - gruel::mutex d_mutex; // mutex to protect set/work access + bool d_updated; + float d_oversample_rate; + int *d_idxlut; + int d_rate_ratio; + int d_output_multiple; + std::vector d_channel_map; + gruel::mutex d_mutex; // mutex to protect set/work access public: - pfb_channelizer_ccf_impl(unsigned int numchans, + pfb_channelizer_ccf_impl(unsigned int nfilts, const std::vector &taps, float oversample_rate); diff --git a/gr-filter/lib/polyphase_filterbank.cc b/gr-filter/lib/polyphase_filterbank.cc new file mode 100644 index 000000000..9d881d76e --- /dev/null +++ b/gr-filter/lib/polyphase_filterbank.cc @@ -0,0 +1,114 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + polyphase_filterbank::polyphase_filterbank(unsigned int nfilts, + const std::vector &taps) + : d_nfilts(nfilts) + { + d_filters = std::vector(d_nfilts); + + // Create an FIR filter for each channel and zero out the taps + std::vector vtaps(0, d_nfilts); + for(unsigned int i = 0; i < d_nfilts; i++) { + d_filters[i] = new kernel::fir_filter_ccf(1, vtaps); + } + + // Now, actually set the filters' taps + set_taps(taps); + + // Create the FFT to handle the output de-spinning of the channels + d_fft = new fft::fft_complex(d_nfilts, false); + } + + polyphase_filterbank::~polyphase_filterbank() + { + delete d_fft; + for(unsigned int i = 0; i < d_nfilts; i++) { + delete d_filters[i]; + } + } + + void + polyphase_filterbank::set_taps (const std::vector &taps) + { + unsigned int i,j; + + unsigned int ntaps = taps.size(); + d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_nfilts); + + // Create d_numchan vectors to store each channel's taps + d_taps.resize(d_nfilts); + + // Make a vector of the taps plus fill it out with 0's to fill + // each polyphase filter with exactly d_taps_per_filter + std::vector tmp_taps; + tmp_taps = taps; + while((float)(tmp_taps.size()) < d_nfilts*d_taps_per_filter) { + tmp_taps.push_back(0.0); + } + + // Partition the filter + for(i = 0; i < d_nfilts; i++) { + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + d_taps[i] = std::vector(d_taps_per_filter, 0); + for(j = 0; j < d_taps_per_filter; j++) { + d_taps[i][j] = tmp_taps[i + j*d_nfilts]; // add taps to channels in reverse order + } + + // Build a filter for each channel and add it's taps to it + d_filters[i]->set_taps(d_taps[i]); + } + } + + void + polyphase_filterbank::print_taps() + { + unsigned int i, j; + for(i = 0; i < d_nfilts; i++) { + printf("filter[%d]: [", i); + for(j = 0; j < d_taps_per_filter; j++) { + printf(" %.4e", d_taps[i][j]); + } + printf("]\n\n"); + } + } + + std::vector< std::vector > + polyphase_filterbank::taps() const + { + return d_taps; + } + + } /* namespace kernel */ + } /* namespace filter */ +} /* namespace gr */ -- cgit From 511f351466e77fb49cc26d21fca3396f2213a44c Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 9 May 2012 20:17:39 -0400 Subject: filter: wip: working on adaptive FIR filter. Moved ccc and created parent class that is not a gr_block. --- gr-filter/include/filter/CMakeLists.txt | 2 + gr-filter/include/filter/adaptive_fir.h | 69 ++++++++++++++++++ gr-filter/include/filter/adaptive_fir_ccc.h | 53 ++++++++++++++ gr-filter/lib/CMakeLists.txt | 2 + gr-filter/lib/adaptive_fir.cc | 109 ++++++++++++++++++++++++++++ gr-filter/lib/adaptive_fir_ccc_impl.cc | 94 ++++++++++++++++++++++++ gr-filter/lib/adaptive_fir_ccc_impl.h | 58 +++++++++++++++ 7 files changed, 387 insertions(+) create mode 100644 gr-filter/include/filter/adaptive_fir.h create mode 100644 gr-filter/include/filter/adaptive_fir_ccc.h create mode 100644 gr-filter/lib/adaptive_fir.cc create mode 100644 gr-filter/lib/adaptive_fir_ccc_impl.cc create mode 100644 gr-filter/lib/adaptive_fir_ccc_impl.h diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 0c067fcba..98cd2161e 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -75,6 +75,7 @@ add_custom_target(filter_generated_includes DEPENDS ######################################################################## install(FILES api.h + adaptive_fir.h firdes.h fir_filter.h fir_filter_with_buffer.h @@ -82,6 +83,7 @@ install(FILES pm_remez.h polyphase_filterbank.h ${generated_includes} + adaptive_fir_ccc.h dc_blocker_cc.h dc_blocker_ff.h filter_delay_fc.h diff --git a/gr-filter/include/filter/adaptive_fir.h b/gr-filter/include/filter/adaptive_fir.h new file mode 100644 index 000000000..7468e6a05 --- /dev/null +++ b/gr-filter/include/filter/adaptive_fir.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_ADAPTIVE_FIR_H +#define INCLUDED_FILTER_ADAPTIVE_FIR_H + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + class FILTER_API adaptive_fir_ccc + { + private: + bool d_updated; + int d_decim; + + protected: + unsigned int d_ntaps; + gr_complex d_error; + gr_complex *d_taps; + + // Override to calculate error signal per output + virtual gr_complex error(const gr_complex &out) = 0; + + // Override to calculate new weight from old, corresponding input + virtual void update_tap(gr_complex &tap, const gr_complex &in) = 0; + + public: + adaptive_fir_ccc(int decimation, + const std::vector &taps); + + void set_taps(const std::vector &taps); + std::vector taps() const; + + int decimation() const; + unsigned int ntaps() const; + + gr_complex filter(gr_complex *input); + void filterN(gr_complex *out, gr_complex *in, int nitems); + }; + + } /* namespace kernel */ + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_ADAPTIVE_FIR_H */ diff --git a/gr-filter/include/filter/adaptive_fir_ccc.h b/gr-filter/include/filter/adaptive_fir_ccc.h new file mode 100644 index 000000000..14e9f6f53 --- /dev/null +++ b/gr-filter/include/filter/adaptive_fir_ccc.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_ADAPTIVE_FIR_CCC_H +#define INCLUDED_FILTER_ADAPTIVE_FIR_CCC_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API adaptive_fir_ccc : virtual public gr_sync_decimator + { + public: + // gr::filter::adaptive_fir_ccc::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief Adaptive FIR filter with gr_complex input, gr_complex output and float taps + * \ingroup filter_blk + */ + static FILTER_API sptr make(const char *name, int decimation, + const std::vector &taps); + + void set_taps(const std::vector &taps); + std::vector taps(); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_ADAPTIVE_FIR_CCC_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index b7577ad5e..6f3e80d82 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -105,6 +105,7 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) # Setup library ######################################################################## list(APPEND filter_sources + adaptive_fir.cc fir_filter.cc fir_filter_with_buffer.cc fft_filter.cc @@ -112,6 +113,7 @@ list(APPEND filter_sources pm_remez.cc polyphase_filterbank.cc ${generated_sources} + adaptive_fir_ccc_impl.cc dc_blocker_cc_impl.cc dc_blocker_ff_impl.cc filter_delay_fc_impl.cc diff --git a/gr-filter/lib/adaptive_fir.cc b/gr-filter/lib/adaptive_fir.cc new file mode 100644 index 000000000..413133941 --- /dev/null +++ b/gr-filter/lib/adaptive_fir.cc @@ -0,0 +1,109 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + adaptive_fir_ccc::adaptive_fir_ccc(int decimation, + const std::vector &taps) + { + d_taps = NULL; + d_decim = decimation; + set_taps(taps); + } + + void + adaptive_fir_ccc::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_complex(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[d_ntaps-i-1] = taps[i]; + } + } + + std::vector + adaptive_fir_ccc::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[d_ntaps-i-1]); + return t; + } + + int + adaptive_fir_ccc::decimation() const + { + return d_decim; + } + + unsigned int + adaptive_fir_ccc::ntaps() const + { + return d_ntaps; + } + + gr_complex + adaptive_fir_ccc::filter(gr_complex *input) + { + gr_complex output; + volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); + return output; + } + + void + adaptive_fir_ccc::filterN(gr_complex *out, gr_complex *in, + int nitems) + { + int j = 0; + unsigned int k; + for(int i = 0; i < nitems; i++) { + out[i] = filter(&in[j]); + + // Adjust taps + d_error = error(out[i]); + for(k = 0; k < d_ntaps; k++) { + update_tap(d_taps[d_ntaps-k-1], in[j+k]); + } + + j += decimation(); + } + } + + } /* namespace kernel */ + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/adaptive_fir_ccc_impl.cc b/gr-filter/lib/adaptive_fir_ccc_impl.cc new file mode 100644 index 000000000..06736ae6f --- /dev/null +++ b/gr-filter/lib/adaptive_fir_ccc_impl.cc @@ -0,0 +1,94 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "adaptive_fir_ccc_impl.h" +#include + +namespace gr { + namespace filter { + + adaptive_fir_ccc::sptr adaptive_fir_ccc::make(const char *name, int decimation, + const std::vector &taps) + { + return gnuradio::get_initial_sptr(new adaptive_fir_ccc_impl + (name, decimation, taps)); + } + + adaptive_fir_ccc_impl::adaptive_fir_ccc_impl(const char *name, int decimation, + const std::vector &taps) + : gr_sync_decimator(name, + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex)), + decimation), + kernel::adaptive_fir_ccc(decimation, taps), + d_updated(false) + { + set_history(d_ntaps); + } + + void + adaptive_fir_ccc_impl::set_taps(const std::vector &taps) + { + d_new_taps = taps; + d_updated = true; + } + + gr_complex + adaptive_fir_ccc_impl::error(const gr_complex &out) + { + return 0; + } + + void + adaptive_fir_ccc_impl::update_tap(gr_complex &tap, const gr_complex &in) + { + tap = tap; + } + + int + adaptive_fir_ccc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in = (gr_complex *)input_items[0]; + gr_complex *out = (gr_complex *)output_items[0]; + + if (d_updated) { + kernel::adaptive_fir_ccc::set_taps(d_new_taps); + set_history(d_ntaps); + d_updated = false; + return 0; // history requirements may have changed. + } + + // Call base class filtering function that uses + // overloaded error and update_tap functions. + filterN(out, in, noutput_items); + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/adaptive_fir_ccc_impl.h b/gr-filter/lib/adaptive_fir_ccc_impl.h new file mode 100644 index 000000000..f145ceeaa --- /dev/null +++ b/gr-filter/lib/adaptive_fir_ccc_impl.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_ADAPTIVE_FIR_CCC_IMPL_H +#define INCLUDED_FILTER_ADAPTIVE_FIR_CCC_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API adaptive_fir_ccc_impl : public adaptive_fir_ccc, public kernel::adaptive_fir_ccc + { + private: + std::vector d_new_taps; + bool d_updated; + + // Override to calculate error signal per output + gr_complex error(const gr_complex &out); + + // Override to calculate new weight from old, corresponding input + void update_tap(gr_complex &tap, const gr_complex &in); + + public: + void set_taps(const std::vector &taps); + + adaptive_fir_ccc_impl(const char *name, int decimation, + const std::vector &taps); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_ADAPTIVE_FIR_CCC_IMPL_H */ -- cgit From 14532d8da0f40f2b58595bd7e217004bdbfc90e3 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 10 Jun 2012 19:33:52 -0400 Subject: filter: adding ccf version for adaptive filter. --- gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/adaptive_fir.h | 37 ++++++++++-- gr-filter/include/filter/adaptive_fir_ccf.h | 53 ++++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/adaptive_fir.cc | 76 +++++++++++++++++++++++ gr-filter/lib/adaptive_fir_ccf_impl.cc | 94 +++++++++++++++++++++++++++++ gr-filter/lib/adaptive_fir_ccf_impl.h | 58 ++++++++++++++++++ 7 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 gr-filter/include/filter/adaptive_fir_ccf.h create mode 100644 gr-filter/lib/adaptive_fir_ccf_impl.cc create mode 100644 gr-filter/lib/adaptive_fir_ccf_impl.h diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 98cd2161e..5b209873c 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -84,6 +84,7 @@ install(FILES polyphase_filterbank.h ${generated_includes} adaptive_fir_ccc.h + adaptive_fir_ccf.h dc_blocker_cc.h dc_blocker_ff.h filter_delay_fc.h diff --git a/gr-filter/include/filter/adaptive_fir.h b/gr-filter/include/filter/adaptive_fir.h index 7468e6a05..75a5f57db 100644 --- a/gr-filter/include/filter/adaptive_fir.h +++ b/gr-filter/include/filter/adaptive_fir.h @@ -33,11 +33,8 @@ namespace gr { class FILTER_API adaptive_fir_ccc { - private: - bool d_updated; - int d_decim; - protected: + int d_decim; unsigned int d_ntaps; gr_complex d_error; gr_complex *d_taps; @@ -62,6 +59,38 @@ namespace gr { void filterN(gr_complex *out, gr_complex *in, int nitems); }; + + /**************************************************************/ + + + class FILTER_API adaptive_fir_ccf + { + protected: + int d_decim; + unsigned int d_ntaps; + float d_error; + gr_complex *d_taps; + + // Override to calculate error signal per output + virtual float error(const gr_complex &out) = 0; + + // Override to calculate new weight from old, corresponding input + virtual void update_tap(gr_complex &tap, const gr_complex &in) = 0; + + public: + adaptive_fir_ccf(int decimation, + const std::vector &taps); + + void set_taps(const std::vector &taps); + std::vector taps() const; + + int decimation() const; + unsigned int ntaps() const; + + gr_complex filter(gr_complex *input); + void filterN(gr_complex *out, gr_complex *in, int nitems); + }; + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/include/filter/adaptive_fir_ccf.h b/gr-filter/include/filter/adaptive_fir_ccf.h new file mode 100644 index 000000000..8eb01c877 --- /dev/null +++ b/gr-filter/include/filter/adaptive_fir_ccf.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_ADAPTIVE_FIR_CCF_H +#define INCLUDED_FILTER_ADAPTIVE_FIR_CCF_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API adaptive_fir_ccf : virtual public gr_sync_decimator + { + public: + // gr::filter::adaptive_fir_ccf::sptr + typedef boost::shared_ptr sptr; + + /*! + * \brief Adaptive FIR filter with gr_complex input, gr_complex output and float taps + * \ingroup filter_blk + */ + static FILTER_API sptr make(const char *name, int decimation, + const std::vector &taps); + + void set_taps(const std::vector &taps); + std::vector taps(); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_ADAPTIVE_FIR_CCF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 6f3e80d82..b51a23bab 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -114,6 +114,7 @@ list(APPEND filter_sources polyphase_filterbank.cc ${generated_sources} adaptive_fir_ccc_impl.cc + adaptive_fir_ccf_impl.cc dc_blocker_cc_impl.cc dc_blocker_ff_impl.cc filter_delay_fc_impl.cc diff --git a/gr-filter/lib/adaptive_fir.cc b/gr-filter/lib/adaptive_fir.cc index 413133941..9098e86ca 100644 --- a/gr-filter/lib/adaptive_fir.cc +++ b/gr-filter/lib/adaptive_fir.cc @@ -104,6 +104,82 @@ namespace gr { } } + + /**************************************************************/ + + + adaptive_fir_ccf::adaptive_fir_ccf(int decimation, + const std::vector &taps) + { + d_taps = NULL; + d_decim = decimation; + set_taps(taps); + } + + void + adaptive_fir_ccf::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_complex(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[d_ntaps-i-1] = taps[i]; + } + } + + std::vector + adaptive_fir_ccf::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[d_ntaps-i-1].real()); + return t; + } + + int + adaptive_fir_ccf::decimation() const + { + return d_decim; + } + + unsigned int + adaptive_fir_ccf::ntaps() const + { + return d_ntaps; + } + + gr_complex + adaptive_fir_ccf::filter(gr_complex *input) + { + gr_complex output; + volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); + return output; + } + + void + adaptive_fir_ccf::filterN(gr_complex *out, gr_complex *in, + int nitems) + { + int j = 0; + unsigned int k; + for(int i = 0; i < nitems; i++) { + out[i] = filter(&in[j]); + + // Adjust taps + d_error = error(out[i]); + for(k = 0; k < d_ntaps; k++) { + update_tap(d_taps[d_ntaps-k-1], in[j+k]); + } + + j += decimation(); + } + } + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/lib/adaptive_fir_ccf_impl.cc b/gr-filter/lib/adaptive_fir_ccf_impl.cc new file mode 100644 index 000000000..053facdc2 --- /dev/null +++ b/gr-filter/lib/adaptive_fir_ccf_impl.cc @@ -0,0 +1,94 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "adaptive_fir_ccf_impl.h" +#include + +namespace gr { + namespace filter { + + adaptive_fir_ccf::sptr adaptive_fir_ccf::make(const char *name, int decimation, + const std::vector &taps) + { + return gnuradio::get_initial_sptr(new adaptive_fir_ccf_impl + (name, decimation, taps)); + } + + adaptive_fir_ccf_impl::adaptive_fir_ccf_impl(const char *name, int decimation, + const std::vector &taps) + : gr_sync_decimator(name, + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex)), + decimation), + kernel::adaptive_fir_ccf(decimation, taps), + d_updated(false) + { + set_history(d_ntaps); + } + + void + adaptive_fir_ccf_impl::set_taps(const std::vector &taps) + { + d_new_taps = taps; + d_updated = true; + } + + float + adaptive_fir_ccf_impl::error(const gr_complex &out) + { + return 0; + } + + void + adaptive_fir_ccf_impl::update_tap(gr_complex &tap, const gr_complex &in) + { + tap = tap; + } + + int + adaptive_fir_ccf_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in = (gr_complex *)input_items[0]; + gr_complex *out = (gr_complex *)output_items[0]; + + if (d_updated) { + kernel::adaptive_fir_ccf::set_taps(d_new_taps); + set_history(d_ntaps); + d_updated = false; + return 0; // history requirements may have changed. + } + + // Call base class filtering function that uses + // overloaded error and update_tap functions. + filterN(out, in, noutput_items); + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/adaptive_fir_ccf_impl.h b/gr-filter/lib/adaptive_fir_ccf_impl.h new file mode 100644 index 000000000..fa9a42189 --- /dev/null +++ b/gr-filter/lib/adaptive_fir_ccf_impl.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_ADAPTIVE_FIR_CCF_IMPL_H +#define INCLUDED_FILTER_ADAPTIVE_FIR_CCF_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API adaptive_fir_ccf_impl : public adaptive_fir_ccf, public kernel::adaptive_fir_ccf + { + private: + std::vector d_new_taps; + bool d_updated; + + // Override to calculate error signal per output + float error(const gr_complex &out); + + // Override to calculate new weight from old, corresponding input + void update_tap(gr_complex &tap, const gr_complex &in); + + public: + void set_taps(const std::vector &taps); + + adaptive_fir_ccf_impl(const char *name, int decimation, + const std::vector &taps); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_ADAPTIVE_FIR_CCF_IMPL_H */ -- cgit From 227448e247ba720b87b99e1b9382cd3737241ab3 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 10 Jun 2012 19:35:55 -0400 Subject: filter: working aligned taps loads for regular and decimating filter. Has working QA code but needs significant performance improvements. --- gr-filter/include/filter/fir_filter.h | 12 +++++---- gr-filter/lib/fir_filter.cc | 49 ++++++++++++++++++++++++++-------- gr-filter/lib/fir_filter_XXX_impl.cc.t | 8 +++++- gr-filter/python/qa_fir_filter.py | 13 +++++++++ 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index fba4641bf..1eb70f92a 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -41,8 +41,8 @@ namespace gr { void set_taps(const std::vector &taps); std::vector taps() const; unsigned int ntaps() const; - - float filter(const float input[]); + + inline float filter(const float input[]); void filterN(float output[], const float input[], unsigned long n); @@ -53,8 +53,10 @@ namespace gr { private: unsigned int d_ntaps; - float *d_taps; - float *d_aligned_taps[4]; + float *d_taps; + float **d_aligned_taps; + int d_offset; + float *d_output; }; /**************************************************************/ @@ -100,7 +102,7 @@ namespace gr { gr_complex filter(const gr_complex input[]); void filterN(gr_complex output[], const gr_complex input[], - unsigned long n); + unsigned long n) void filterNdec(gr_complex output[], const gr_complex input[], unsigned long n, diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 3abcda53e..098dd8d36 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -23,6 +23,7 @@ #include #include #include +#include namespace gr { namespace filter { @@ -33,15 +34,29 @@ namespace gr { { d_taps = NULL; set_taps(taps); + d_offset = 0; + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_float(1); } fir_filter_fff::~fir_filter_fff() { + // Free taps if(d_taps != NULL) { fft::free(d_taps); d_taps = NULL; } - } + + // Free all aligned taps + for(int i = 0; i < 4; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + + // Free output sample + fft::free(d_output); + } void fir_filter_fff::set_taps(const std::vector &taps) @@ -50,6 +65,11 @@ namespace gr { if(d_taps != NULL) { fft::free(d_taps); d_taps = NULL; + + for(int i = 0; i < 4; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); } d_ntaps = (int)taps.size(); @@ -58,6 +78,8 @@ namespace gr { d_taps[d_ntaps-i-1] = taps[i]; } + // Make a set of taps at all possible arch alignments + d_aligned_taps = (float**)malloc(4*sizeof(float**)); for(int i = 0; i < 4; i++) { d_aligned_taps[i] = fft::malloc_float(d_ntaps+3); memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+3)); @@ -83,12 +105,9 @@ namespace gr { float fir_filter_fff::filter(const float input[]) { - float output; - - //const float *ar = (float*)((unsigned long)input & ~15); - - volk_32f_x2_dot_prod_32f_u(&output, input, d_taps, d_ntaps); - return output; + volk_32f_x2_dot_prod_32f_a(d_output, input, + d_aligned_taps[d_offset], d_ntaps+3); + return *d_output; } void @@ -96,8 +115,16 @@ namespace gr { const float input[], unsigned long n) { - for(unsigned long i = 0; i < n; i++) - output[i] = filter(&input[i]); + unsigned long ar = ((unsigned long) input); + int off = (ar - (ar & ~15))/4; + + int j = -off; + d_offset = off; + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(&input[j]); + d_offset= (d_offset+1) & 0x03; + j += (d_offset == 0 ? 4 : 0); + } } @@ -108,8 +135,8 @@ namespace gr { unsigned int decimate) { unsigned long j = 0; - for(unsigned long i = 0; i < n; i++){ - output[i] = filter(&input[j]); + for(unsigned long i = 0; i < n; i++) { + filterN(&output[i], &input[j], 1); j += decimate; } } diff --git a/gr-filter/lib/fir_filter_XXX_impl.cc.t b/gr-filter/lib/fir_filter_XXX_impl.cc.t index c3637042d..18bec38be 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.cc.t +++ b/gr-filter/lib/fir_filter_XXX_impl.cc.t @@ -26,6 +26,7 @@ #include "@IMPL_NAME@.h" #include +#include namespace gr { namespace filter { @@ -47,6 +48,10 @@ namespace gr { d_fir = new kernel::@BASE_NAME@(decimation, taps); d_updated = false; set_history(d_fir->ntaps()); + + const int alignment_multiple = + volk_get_alignment() / sizeof(float); + set_alignment(std::max(1, alignment_multiple)); } @IMPL_NAME@::~@IMPL_NAME@() @@ -85,7 +90,8 @@ namespace gr { d_fir->filterN(out, in, noutput_items); } else { - d_fir->filterNdec(out, in, noutput_items, decimation()); + d_fir->filterNdec(out, in, noutput_items, + decimation()); } return noutput_items; diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py index 38bfd9ea5..93974bb89 100755 --- a/gr-filter/python/qa_fir_filter.py +++ b/gr-filter/python/qa_fir_filter.py @@ -41,6 +41,19 @@ class test_filter(gr_unittest.TestCase): result_data = dst.data() self.assertFloatTuplesAlmostEqual(expected_data, result_data, 5) + def test_fir_filter_fff_002(self): + src_data = 10*[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + expected_data = [0.0,] + 4*[3.5, 7.5, 1.5, 5.5, 4.5,] + [3.5, 7.5, 1.5, 5.5] + src = gr.vector_source_f(src_data) + op = filter.fir_filter_fff(4, [0.5, 0.5]) + dst = gr.vector_sink_f() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + print result_data + print expected_data + self.assertFloatTuplesAlmostEqual(expected_data, result_data, 5) + def test_fir_filter_ccf_001(self): src_data = [1+1j, 2+2j, 3+3j, 4+4j] expected_data = [0.5+0.5j, 1.5+1.5j, 2.5+2.5j, 3.5+3.5j] -- cgit From 46bd64dc8231402535bad742a74250599e595148 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 10 Jun 2012 19:21:51 -0400 Subject: filter: Fixed filter_delay and hilbert filter to work with aligned filter loads. --- gr-filter/lib/filter_delay_fc_impl.cc | 16 ++++++++++++---- gr-filter/lib/hilbert_fc_impl.cc | 14 +++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/gr-filter/lib/filter_delay_fc_impl.cc b/gr-filter/lib/filter_delay_fc_impl.cc index ce320c557..279368664 100644 --- a/gr-filter/lib/filter_delay_fc_impl.cc +++ b/gr-filter/lib/filter_delay_fc_impl.cc @@ -25,6 +25,7 @@ #endif #include "filter_delay_fc_impl.h" +#include namespace gr { namespace filter { @@ -44,6 +45,10 @@ namespace gr { d_fir = new kernel::fir_filter_fff(1, taps); d_delay = d_fir->ntaps() / 2; set_history(d_fir->ntaps()); + + const int alignment_multiple = + volk_get_alignment() / sizeof(float); + set_alignment(std::max(1, alignment_multiple)); } filter_delay_fc_impl::~filter_delay_fc_impl() @@ -73,6 +78,7 @@ namespace gr { float *in0 = (float *)input_items[0]; float *in1 = (float *)input_items[1]; gr_complex *out = (gr_complex *)output_items[0]; + float d_out; if(d_update) { d_fir->set_taps(d_taps); @@ -84,15 +90,17 @@ namespace gr { switch(input_items.size ()) { case 1: for(int i = 0; i < noutput_items; i++) { - out[i] = gr_complex (in0[i + d_delay], - d_fir->filter (&in0[i])); + d_fir->filterN(&d_out, &in0[i], 1); + out[i] = gr_complex(in0[i + d_delay], + d_out); } break; case 2: for(int j = 0; j < noutput_items; j++) { - out[j] = gr_complex (in0[j + d_delay], - d_fir->filter (&in1[j])); + d_fir->filterN(&d_out, &in1[j], 1); + out[j] = gr_complex(in0[j + d_delay], + d_out); } break; diff --git a/gr-filter/lib/hilbert_fc_impl.cc b/gr-filter/lib/hilbert_fc_impl.cc index 946b47e4a..7aaa23a62 100644 --- a/gr-filter/lib/hilbert_fc_impl.cc +++ b/gr-filter/lib/hilbert_fc_impl.cc @@ -27,6 +27,7 @@ #include "hilbert_fc_impl.h" #include #include +#include namespace gr { namespace filter { @@ -44,6 +45,10 @@ namespace gr { { d_hilb = new kernel::fir_filter_fff(1, firdes::hilbert(d_ntaps)); set_history (d_ntaps); + + const int alignment_multiple = + volk_get_alignment() / sizeof(float); + set_alignment(std::max(1, alignment_multiple)); } hilbert_fc_impl::~hilbert_fc_impl() @@ -58,10 +63,13 @@ namespace gr { { float *in = (float *)input_items[0]; gr_complex *out = (gr_complex *)output_items[0]; + float h_out; - for(int i = 0; i < noutput_items; i++) - out[i] = gr_complex (in[i + d_ntaps/2], - d_hilb->filter (&in[i])); + for(int i = 0; i < noutput_items; i++) { + d_hilb->filterN(&h_out, &in[i], 1); + out[i] = gr_complex(in[i + d_ntaps/2], + h_out); + } return noutput_items; } -- cgit From 9e2e896e9d4dbc4627702cde82a48e9ee5136f26 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 13 Jun 2012 14:53:41 -0400 Subject: filter: process 4 vectors each time in volk dot_prod to speed up fir filters. This makes the volk version of the SSE FIR filter the same speed as using the hand-crafted float_dotprod from before. --- gr-filter/include/filter/fir_filter.h | 2 +- gr-filter/lib/fir_filter.cc | 38 ++++++++++++++++++-- volk/include/volk/volk_32f_x2_dot_prod_32f_a.h | 48 +++++++++++++++++++------- 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 1eb70f92a..e6194df7b 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -102,7 +102,7 @@ namespace gr { gr_complex filter(const gr_complex input[]); void filterN(gr_complex output[], const gr_complex input[], - unsigned long n) + unsigned long n); void filterNdec(gr_complex output[], const gr_complex input[], unsigned long n, diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 098dd8d36..c6e179246 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -24,6 +24,7 @@ #include #include #include +#include namespace gr { namespace filter { @@ -101,12 +102,16 @@ namespace gr { { return d_ntaps; } - + + /* float fir_filter_fff::filter(const float input[]) { volk_32f_x2_dot_prod_32f_a(d_output, input, - d_aligned_taps[d_offset], d_ntaps+3); + d_aligned_taps[d_offset], + (d_ntaps + d_offset - 1) / 4 + 1); + //*d_output = float_dotprod_sse(input, d_aligned_taps[d_offset], + // (d_ntaps + d_offset - 1) / 4 + 1); return *d_output; } @@ -126,7 +131,34 @@ namespace gr { j += (d_offset == 0 ? 4 : 0); } } + */ + + float + fir_filter_fff::filter(const float input[]) + { + //unsigned long ar = ((unsigned long) input); + //int off = (ar - (ar & ~15))/4; + + const float *ar = (float *)((unsigned long) input & ~15); + unsigned al = input - ar; + + volk_32f_x2_dot_prod_32f_a(d_output, ar, + d_aligned_taps[al], + (d_ntaps + al - 1) / 4 + 1); + //*d_output = float_dotprod_sse(input, d_aligned_taps[d_offset], + // (d_ntaps + d_offset - 1) / 4 + 1); + return *d_output; + } + void + fir_filter_fff::filterN(float output[], + const float input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(&input[i]); + } + } void fir_filter_fff::filterNdec(float output[], @@ -136,7 +168,7 @@ namespace gr { { unsigned long j = 0; for(unsigned long i = 0; i < n; i++) { - filterN(&output[i], &input[j], 1); + output[i] = filter(&input[j]); j += decimate; } } diff --git a/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h b/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h index 448b2fdc0..8753ff615 100644 --- a/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h +++ b/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h @@ -37,26 +37,47 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* const float* aPtr = input; const float* bPtr = taps; - __m128 aVal, bVal, cVal; + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; - __m128 dotProdVal = _mm_setzero_ps(); + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); for(;number < quarterPoints; number++){ - aVal = _mm_load_ps(aPtr); - bVal = _mm_load_ps(bPtr); - - cVal = _mm_mul_ps(aVal, bVal); - - dotProdVal = _mm_add_ps(cVal, dotProdVal); - - aPtr += 4; - bPtr += 4; + a0Val = _mm_load_ps(aPtr); + a1Val = _mm_load_ps(aPtr+4); + a2Val = _mm_load_ps(aPtr+8); + a3Val = _mm_load_ps(aPtr+12); + b0Val = _mm_load_ps(bPtr); + b1Val = _mm_load_ps(bPtr+4); + b2Val = _mm_load_ps(bPtr+8); + b3Val = _mm_load_ps(bPtr+12); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 4*4; + bPtr += 4*4; } + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; - _mm_store_ps(dotProductVector,dotProdVal); // Store the results back into the dot product vector + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector dotProduct = dotProductVector[0]; dotProduct += dotProductVector[1]; @@ -66,6 +87,9 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* number = quarterPoints * 4; for(;number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); } *result = dotProduct; -- cgit From 1b93bb39731517e6d7bb8a78ea9569995e8a371a Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 13 Jun 2012 14:55:16 -0400 Subject: filter wip and probably squash with last. Removing unecessary d_offset counter from fir_filter. --- gr-filter/include/filter/fir_filter.h | 1 - gr-filter/lib/fir_filter.cc | 36 ----------------------------------- 2 files changed, 37 deletions(-) diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index e6194df7b..7047ba1a4 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -55,7 +55,6 @@ namespace gr { unsigned int d_ntaps; float *d_taps; float **d_aligned_taps; - int d_offset; float *d_output; }; diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index c6e179246..30c8325f7 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -35,7 +35,6 @@ namespace gr { { d_taps = NULL; set_taps(taps); - d_offset = 0; // Make sure the output sample is always aligned, too. d_output = fft::malloc_float(1); @@ -103,50 +102,15 @@ namespace gr { return d_ntaps; } - /* float fir_filter_fff::filter(const float input[]) { - volk_32f_x2_dot_prod_32f_a(d_output, input, - d_aligned_taps[d_offset], - (d_ntaps + d_offset - 1) / 4 + 1); - //*d_output = float_dotprod_sse(input, d_aligned_taps[d_offset], - // (d_ntaps + d_offset - 1) / 4 + 1); - return *d_output; - } - - void - fir_filter_fff::filterN(float output[], - const float input[], - unsigned long n) - { - unsigned long ar = ((unsigned long) input); - int off = (ar - (ar & ~15))/4; - - int j = -off; - d_offset = off; - for(unsigned long i = 0; i < n; i++) { - output[i] = filter(&input[j]); - d_offset= (d_offset+1) & 0x03; - j += (d_offset == 0 ? 4 : 0); - } - } - */ - - float - fir_filter_fff::filter(const float input[]) - { - //unsigned long ar = ((unsigned long) input); - //int off = (ar - (ar & ~15))/4; - const float *ar = (float *)((unsigned long) input & ~15); unsigned al = input - ar; volk_32f_x2_dot_prod_32f_a(d_output, ar, d_aligned_taps[al], (d_ntaps + al - 1) / 4 + 1); - //*d_output = float_dotprod_sse(input, d_aligned_taps[d_offset], - // (d_ntaps + d_offset - 1) / 4 + 1); return *d_output; } -- cgit From 7f9f0fc96a3fbfe297b0a5cb18d922bb74fdc34d Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 13 Jun 2012 17:49:44 -0400 Subject: volk: dot_produce for floats does 16 at a time. This was done to make this have the same performance as float_dotprod from before. This makes all flavors of the 32f dotprod work the same way. Because it's expecting the input to have 4x more samples than specified, it's making qa for these fail. --- volk/include/volk/volk_32f_x2_dot_prod_32f_a.h | 84 +++++++++----- volk/include/volk/volk_32f_x2_dot_prod_32f_u.h | 151 +++++++++++++++++-------- 2 files changed, 160 insertions(+), 75 deletions(-) diff --git a/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h b/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h index 8753ff615..0543227b0 100644 --- a/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h +++ b/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h @@ -8,14 +8,17 @@ #ifdef LV_HAVE_GENERIC -static inline void volk_32f_x2_dot_prod_32f_a_generic(float * result, const float * input, const float * taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_a_generic(float * result, const float * input, const float * taps, unsigned int num_4x_points) { float dotProduct = 0; const float* aPtr = input; const float* bPtr= taps; unsigned int number = 0; - for(number = 0; number < num_points; number++){ + for(number = 0; number < num_4x_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -28,10 +31,10 @@ static inline void volk_32f_x2_dot_prod_32f_a_generic(float * result, const floa #ifdef LV_HAVE_SSE -static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* input, const float* taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* input, const float* taps, unsigned int num_4x_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_points / 4; + const unsigned int quarterPoints = num_4x_points / 4; float dotProduct = 0; const float* aPtr = input; @@ -67,8 +70,8 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); - aPtr += 4*4; - bPtr += 4*4; + aPtr += 16; + bPtr += 16; } dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); @@ -84,8 +87,8 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = quarterPoints * 4; - for(;number < num_points; number++){ + number = quarterPoints*4; + for(;number < num_4x_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); @@ -102,41 +105,65 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* #include -static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * input, const float * taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * input, const float * taps, unsigned int num_4x_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_points / 4; + const unsigned int quarterPoints = num_4x_points / 4; float dotProduct = 0; const float* aPtr = input; const float* bPtr = taps; - __m128 aVal, bVal, cVal; + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; - __m128 dotProdVal = _mm_setzero_ps(); + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); for(;number < quarterPoints; number++){ - aVal = _mm_load_ps(aPtr); - bVal = _mm_load_ps(bPtr); + a0Val = _mm_load_ps(aPtr); + a1Val = _mm_load_ps(aPtr+4); + a2Val = _mm_load_ps(aPtr+8); + a3Val = _mm_load_ps(aPtr+12); + b0Val = _mm_load_ps(bPtr); + b1Val = _mm_load_ps(bPtr+4); + b2Val = _mm_load_ps(bPtr+8); + b3Val = _mm_load_ps(bPtr+12); - cVal = _mm_mul_ps(aVal, bVal); + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); - dotProdVal = _mm_hadd_ps(dotProdVal, cVal); + dotProdVal0 = _mm_add_ps(dotProdVal0, c0Val); + dotProdVal1 = _mm_add_ps(dotProdVal1, c1Val); + dotProdVal2 = _mm_add_ps(dotProdVal2, c2Val); + dotProdVal3 = _mm_add_ps(dotProdVal3, c3Val); - aPtr += 4; - bPtr += 4; + aPtr += 16; + bPtr += 16; } - __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; - dotProdVal = _mm_hadd_ps(dotProdVal, dotProdVal); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); - _mm_store_ps(dotProductVector,dotProdVal); // Store the results back into the dot product vector + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector dotProduct = dotProductVector[0]; dotProduct += dotProductVector[1]; + dotProduct += dotProductVector[2]; + dotProduct += dotProductVector[3]; - number = quarterPoints * 4; - for(;number < num_points; number++){ + number = quarterPoints*4; + for(;number < num_4x_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -149,9 +176,9 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * #include -static inline void volk_32f_x2_dot_prod_32f_a_sse4_1(float * result, const float * input, const float* taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_a_sse4_1(float * result, const float * input, const float* taps, unsigned int num_4x_points) { unsigned int number = 0; - const unsigned int sixteenthPoints = num_points / 16; + const unsigned int sixteenthPoints = num_4x_points / 4; float dotProduct = 0; const float* aPtr = input; @@ -196,8 +223,11 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse4_1(float * result, const float dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = sixteenthPoints * 16; - for(;number < num_points; number++){ + number = sixteenthPoints * 4; + for(;number < num_4x_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); } diff --git a/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h b/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h index 3b7284b57..dfafe2239 100644 --- a/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h +++ b/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h @@ -1,20 +1,24 @@ #ifndef INCLUDED_volk_32f_x2_dot_prod_32f_u_H #define INCLUDED_volk_32f_x2_dot_prod_32f_u_H +#include #include #ifdef LV_HAVE_GENERIC -static inline void volk_32f_x2_dot_prod_32f_u_generic(float * result, const float * input, const float * taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_u_generic(float * result, const float * input, const float * taps, unsigned int num_4x_points) { float dotProduct = 0; const float* aPtr = input; const float* bPtr= taps; unsigned int number = 0; - for(number = 0; number < num_points; number++){ + for(number = 0; number < num_4x_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -27,43 +31,67 @@ static inline void volk_32f_x2_dot_prod_32f_u_generic(float * result, const floa #ifdef LV_HAVE_SSE -static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* input, const float* taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* input, const float* taps, unsigned int num_4x_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_points / 4; + const unsigned int quarterPoints = num_4x_points / 4; float dotProduct = 0; const float* aPtr = input; const float* bPtr = taps; - __m128 aVal, bVal, cVal; + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; - __m128 dotProdVal = _mm_setzero_ps(); + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); for(;number < quarterPoints; number++){ - aVal = _mm_loadu_ps(aPtr); - bVal = _mm_loadu_ps(bPtr); - - cVal = _mm_mul_ps(aVal, bVal); - - dotProdVal = _mm_add_ps(cVal, dotProdVal); - - aPtr += 4; - bPtr += 4; + a0Val = _mm_load_ps(aPtr); + a1Val = _mm_load_ps(aPtr+4); + a2Val = _mm_load_ps(aPtr+8); + a3Val = _mm_load_ps(aPtr+12); + b0Val = _mm_load_ps(bPtr); + b1Val = _mm_load_ps(bPtr+4); + b2Val = _mm_load_ps(bPtr+8); + b3Val = _mm_load_ps(bPtr+12); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 16; + bPtr += 16; } + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; - _mm_store_ps(dotProductVector,dotProdVal); // Store the results back into the dot product vector + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector dotProduct = dotProductVector[0]; dotProduct += dotProductVector[1]; dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = quarterPoints * 4; - for(;number < num_points; number++){ + number = quarterPoints*4; + for(;number < num_4x_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -77,41 +105,65 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* #include -static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * input, const float * taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * input, const float * taps, unsigned int num_4x_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_points / 4; + const unsigned int quarterPoints = num_4x_points / 4; float dotProduct = 0; const float* aPtr = input; const float* bPtr = taps; - __m128 aVal, bVal, cVal; + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; - __m128 dotProdVal = _mm_setzero_ps(); + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); for(;number < quarterPoints; number++){ - aVal = _mm_loadu_ps(aPtr); - bVal = _mm_loadu_ps(bPtr); - - cVal = _mm_mul_ps(aVal, bVal); - - dotProdVal = _mm_hadd_ps(dotProdVal, cVal); - - aPtr += 4; - bPtr += 4; + a0Val = _mm_load_ps(aPtr); + a1Val = _mm_load_ps(aPtr+4); + a2Val = _mm_load_ps(aPtr+8); + a3Val = _mm_load_ps(aPtr+12); + b0Val = _mm_load_ps(bPtr); + b1Val = _mm_load_ps(bPtr+4); + b2Val = _mm_load_ps(bPtr+8); + b3Val = _mm_load_ps(bPtr+12); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(dotProdVal0, c0Val); + dotProdVal1 = _mm_add_ps(dotProdVal1, c1Val); + dotProdVal2 = _mm_add_ps(dotProdVal2, c2Val); + dotProdVal3 = _mm_add_ps(dotProdVal3, c3Val); + + aPtr += 16; + bPtr += 16; } - __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; - dotProdVal = _mm_hadd_ps(dotProdVal, dotProdVal); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); - _mm_store_ps(dotProductVector,dotProdVal); // Store the results back into the dot product vector + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector dotProduct = dotProductVector[0]; dotProduct += dotProductVector[1]; + dotProduct += dotProductVector[2]; + dotProduct += dotProductVector[3]; - number = quarterPoints * 4; - for(;number < num_points; number++){ + number = quarterPoints*4; + for(;number < num_4x_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -124,9 +176,9 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * #include -static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float * input, const float* taps, unsigned int num_points) { +static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float * input, const float* taps, unsigned int num_4x_points) { unsigned int number = 0; - const unsigned int sixteenthPoints = num_points / 16; + const unsigned int sixteenthPoints = num_4x_points / 4; float dotProduct = 0; const float* aPtr = input; @@ -141,15 +193,15 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float for(;number < sixteenthPoints; number++){ - aVal1 = _mm_loadu_ps(aPtr); aPtr += 4; - aVal2 = _mm_loadu_ps(aPtr); aPtr += 4; - aVal3 = _mm_loadu_ps(aPtr); aPtr += 4; - aVal4 = _mm_loadu_ps(aPtr); aPtr += 4; + aVal1 = _mm_load_ps(aPtr); aPtr += 4; + aVal2 = _mm_load_ps(aPtr); aPtr += 4; + aVal3 = _mm_load_ps(aPtr); aPtr += 4; + aVal4 = _mm_load_ps(aPtr); aPtr += 4; - bVal1 = _mm_loadu_ps(bPtr); bPtr += 4; - bVal2 = _mm_loadu_ps(bPtr); bPtr += 4; - bVal3 = _mm_loadu_ps(bPtr); bPtr += 4; - bVal4 = _mm_loadu_ps(bPtr); bPtr += 4; + bVal1 = _mm_load_ps(bPtr); bPtr += 4; + bVal2 = _mm_load_ps(bPtr); bPtr += 4; + bVal3 = _mm_load_ps(bPtr); bPtr += 4; + bVal4 = _mm_load_ps(bPtr); bPtr += 4; cVal1 = _mm_dp_ps(aVal1, bVal1, 0xF1); cVal2 = _mm_dp_ps(aVal2, bVal2, 0xF2); @@ -171,8 +223,11 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = sixteenthPoints * 16; - for(;number < num_points; number++){ + number = sixteenthPoints * 4; + for(;number < num_4x_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); + dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); } -- cgit From c24cabd47e2aaadf279c19aec73b311dd7ddce6c Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 14 Jun 2012 12:25:04 -0400 Subject: volk: fixes for 32f dot_prod Accepts num_points like everything else and handles splitting up numbers itself, not expected to be done externally. Adds AVX version, both aligned and unaligned. --- volk/include/volk/volk_32f_x2_dot_prod_32f_a.h | 105 ++++++++++++++++++------- volk/include/volk/volk_32f_x2_dot_prod_32f_u.h | 102 ++++++++++++++++++------ 2 files changed, 154 insertions(+), 53 deletions(-) diff --git a/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h b/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h index 0543227b0..c26fd5e7c 100644 --- a/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h +++ b/volk/include/volk/volk_32f_x2_dot_prod_32f_a.h @@ -8,17 +8,14 @@ #ifdef LV_HAVE_GENERIC -static inline void volk_32f_x2_dot_prod_32f_a_generic(float * result, const float * input, const float * taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_a_generic(float * result, const float * input, const float * taps, unsigned int num_points) { float dotProduct = 0; const float* aPtr = input; const float* bPtr= taps; unsigned int number = 0; - for(number = 0; number < num_4x_points; number++){ - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); + for(number = 0; number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -31,10 +28,10 @@ static inline void volk_32f_x2_dot_prod_32f_a_generic(float * result, const floa #ifdef LV_HAVE_SSE -static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* input, const float* taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* input, const float* taps, unsigned int num_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_4x_points / 4; + const unsigned int sixteenthPoints = num_points / 16; float dotProduct = 0; const float* aPtr = input; @@ -49,7 +46,7 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* __m128 dotProdVal2 = _mm_setzero_ps(); __m128 dotProdVal3 = _mm_setzero_ps(); - for(;number < quarterPoints; number++){ + for(;number < sixteenthPoints; number++){ a0Val = _mm_load_ps(aPtr); a1Val = _mm_load_ps(aPtr+4); @@ -87,11 +84,8 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = quarterPoints*4; - for(;number < num_4x_points; number++){ - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); + number = sixteenthPoints*16; + for(;number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -105,9 +99,9 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse( float* result, const float* #include -static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * input, const float * taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * input, const float * taps, unsigned int num_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_4x_points / 4; + const unsigned int sixteenthPoints = num_points / 16; float dotProduct = 0; const float* aPtr = input; @@ -122,7 +116,7 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * __m128 dotProdVal2 = _mm_setzero_ps(); __m128 dotProdVal3 = _mm_setzero_ps(); - for(;number < quarterPoints; number++){ + for(;number < sixteenthPoints; number++){ a0Val = _mm_load_ps(aPtr); a1Val = _mm_load_ps(aPtr+4); @@ -159,11 +153,8 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = quarterPoints*4; - for(;number < num_4x_points; number++){ - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); + number = sixteenthPoints*16; + for(;number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -176,9 +167,9 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse3(float * result, const float * #include -static inline void volk_32f_x2_dot_prod_32f_a_sse4_1(float * result, const float * input, const float* taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_a_sse4_1(float * result, const float * input, const float* taps, unsigned int num_points) { unsigned int number = 0; - const unsigned int sixteenthPoints = num_4x_points / 4; + const unsigned int sixteenthPoints = num_points / 16; float dotProduct = 0; const float* aPtr = input; @@ -223,11 +214,8 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse4_1(float * result, const float dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = sixteenthPoints * 4; - for(;number < num_4x_points; number++){ - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); + number = sixteenthPoints * 16; + for(;number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -236,4 +224,65 @@ static inline void volk_32f_x2_dot_prod_32f_a_sse4_1(float * result, const float #endif /*LV_HAVE_SSE4_1*/ +#ifdef LV_HAVE_AVX + +static inline void volk_32f_x2_dot_prod_32f_a_avx( float* result, const float* input, const float* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 16; + + float dotProduct = 0; + const float* aPtr = input; + const float* bPtr = taps; + + __m256 a0Val, a1Val; + __m256 b0Val, b1Val; + __m256 c0Val, c1Val; + + __m256 dotProdVal0 = _mm256_setzero_ps(); + __m256 dotProdVal1 = _mm256_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + a0Val = _mm256_load_ps(aPtr); + a1Val = _mm256_load_ps(aPtr+8); + b0Val = _mm256_load_ps(bPtr); + b1Val = _mm256_load_ps(bPtr+8); + + c0Val = _mm256_mul_ps(a0Val, b0Val); + c1Val = _mm256_mul_ps(a1Val, b1Val); + + dotProdVal0 = _mm256_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm256_add_ps(c1Val, dotProdVal1); + + aPtr += 16; + bPtr += 16; + } + + dotProdVal0 = _mm256_add_ps(dotProdVal0, dotProdVal1); + + __VOLK_ATTR_ALIGNED(32) float dotProductVector[8]; + + _mm256_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + dotProduct = dotProductVector[0]; + dotProduct += dotProductVector[1]; + dotProduct += dotProductVector[2]; + dotProduct += dotProductVector[3]; + dotProduct += dotProductVector[4]; + dotProduct += dotProductVector[5]; + dotProduct += dotProductVector[6]; + dotProduct += dotProductVector[7]; + + number = sixteenthPoints*16; + for(;number < num_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + } + + *result = dotProduct; + +} + +#endif /*LV_HAVE_AVX*/ + #endif /*INCLUDED_volk_32f_x2_dot_prod_32f_a_H*/ diff --git a/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h b/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h index dfafe2239..f9ae15094 100644 --- a/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h +++ b/volk/include/volk/volk_32f_x2_dot_prod_32f_u.h @@ -8,17 +8,14 @@ #ifdef LV_HAVE_GENERIC -static inline void volk_32f_x2_dot_prod_32f_u_generic(float * result, const float * input, const float * taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_u_generic(float * result, const float * input, const float * taps, unsigned int num_points) { float dotProduct = 0; const float* aPtr = input; const float* bPtr= taps; unsigned int number = 0; - for(number = 0; number < num_4x_points; number++){ - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); + for(number = 0; number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -31,10 +28,10 @@ static inline void volk_32f_x2_dot_prod_32f_u_generic(float * result, const floa #ifdef LV_HAVE_SSE -static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* input, const float* taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* input, const float* taps, unsigned int num_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_4x_points / 4; + const unsigned int sixteenthPoints = num_points / 16; float dotProduct = 0; const float* aPtr = input; @@ -49,7 +46,7 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* __m128 dotProdVal2 = _mm_setzero_ps(); __m128 dotProdVal3 = _mm_setzero_ps(); - for(;number < quarterPoints; number++){ + for(;number < sixteenthPoints; number++){ a0Val = _mm_load_ps(aPtr); a1Val = _mm_load_ps(aPtr+4); @@ -87,8 +84,8 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = quarterPoints*4; - for(;number < num_4x_points; number++){ + number = sixteenthPoints*16; + for(;number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); dotProduct += ((*aPtr++) * (*bPtr++)); @@ -105,9 +102,9 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse( float* result, const float* #include -static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * input, const float * taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * input, const float * taps, unsigned int num_points) { unsigned int number = 0; - const unsigned int quarterPoints = num_4x_points / 4; + const unsigned int sixteenthPoints = num_points / 16; float dotProduct = 0; const float* aPtr = input; @@ -122,7 +119,7 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * __m128 dotProdVal2 = _mm_setzero_ps(); __m128 dotProdVal3 = _mm_setzero_ps(); - for(;number < quarterPoints; number++){ + for(;number < sixteenthPoints; number++){ a0Val = _mm_load_ps(aPtr); a1Val = _mm_load_ps(aPtr+4); @@ -159,11 +156,8 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = quarterPoints*4; - for(;number < num_4x_points; number++){ - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); + number = sixteenthPoints*16; + for(;number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -176,9 +170,9 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse3(float * result, const float * #include -static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float * input, const float* taps, unsigned int num_4x_points) { +static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float * input, const float* taps, unsigned int num_points) { unsigned int number = 0; - const unsigned int sixteenthPoints = num_4x_points / 4; + const unsigned int sixteenthPoints = num_points / 16; float dotProduct = 0; const float* aPtr = input; @@ -223,11 +217,8 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float dotProduct += dotProductVector[2]; dotProduct += dotProductVector[3]; - number = sixteenthPoints * 4; - for(;number < num_4x_points; number++){ - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); - dotProduct += ((*aPtr++) * (*bPtr++)); + number = sixteenthPoints * 16; + for(;number < num_points; number++){ dotProduct += ((*aPtr++) * (*bPtr++)); } @@ -236,4 +227,65 @@ static inline void volk_32f_x2_dot_prod_32f_u_sse4_1(float * result, const float #endif /*LV_HAVE_SSE4_1*/ +#ifdef LV_HAVE_AVX + +static inline void volk_32f_x2_dot_prod_32f_u_avx( float* result, const float* input, const float* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 16; + + float dotProduct = 0; + const float* aPtr = input; + const float* bPtr = taps; + + __m256 a0Val, a1Val; + __m256 b0Val, b1Val; + __m256 c0Val, c1Val; + + __m256 dotProdVal0 = _mm256_setzero_ps(); + __m256 dotProdVal1 = _mm256_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + a0Val = _mm256_loadu_ps(aPtr); + a1Val = _mm256_loadu_ps(aPtr+8); + b0Val = _mm256_loadu_ps(bPtr); + b1Val = _mm256_loadu_ps(bPtr+8); + + c0Val = _mm256_mul_ps(a0Val, b0Val); + c1Val = _mm256_mul_ps(a1Val, b1Val); + + dotProdVal0 = _mm256_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm256_add_ps(c1Val, dotProdVal1); + + aPtr += 16; + bPtr += 16; + } + + dotProdVal0 = _mm256_add_ps(dotProdVal0, dotProdVal1); + + __VOLK_ATTR_ALIGNED(32) float dotProductVector[8]; + + _mm256_storeu_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + dotProduct = dotProductVector[0]; + dotProduct += dotProductVector[1]; + dotProduct += dotProductVector[2]; + dotProduct += dotProductVector[3]; + dotProduct += dotProductVector[4]; + dotProduct += dotProductVector[5]; + dotProduct += dotProductVector[6]; + dotProduct += dotProductVector[7]; + + number = sixteenthPoints*16; + for(;number < num_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + } + + *result = dotProduct; + +} + +#endif /*LV_HAVE_AVX*/ + #endif /*INCLUDED_volk_32f_x2_dot_prod_32f_u_H*/ -- cgit From dae3b39098c16573f9c70e77f1a9a6b09ecfd041 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 14 Jun 2012 12:26:45 -0400 Subject: filter: fff FIR filters use VOLK machine information to set alignment. Works with SSE and AVX machines. SSE results comparable to float_dotprod still. AVX slightly faster. --- gr-filter/include/filter/fir_filter.h | 2 ++ gr-filter/lib/fir_filter.cc | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 7047ba1a4..99acb7a0a 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -56,6 +56,8 @@ namespace gr { float *d_taps; float **d_aligned_taps; float *d_output; + int d_align; + int d_naligned; }; /**************************************************************/ diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 30c8325f7..1e5990294 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -33,6 +33,9 @@ namespace gr { fir_filter_fff::fir_filter_fff(int decimation, const std::vector &taps) { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(float); + d_taps = NULL; set_taps(taps); @@ -49,7 +52,7 @@ namespace gr { } // Free all aligned taps - for(int i = 0; i < 4; i++) { + for(int i = 0; i < d_naligned; i++) { fft::free(d_aligned_taps[i]); } fft::free(d_aligned_taps); @@ -66,7 +69,7 @@ namespace gr { fft::free(d_taps); d_taps = NULL; - for(int i = 0; i < 4; i++) { + for(int i = 0; i < d_naligned; i++) { fft::free(d_aligned_taps[i]); } fft::free(d_aligned_taps); @@ -79,10 +82,10 @@ namespace gr { } // Make a set of taps at all possible arch alignments - d_aligned_taps = (float**)malloc(4*sizeof(float**)); - for(int i = 0; i < 4; i++) { - d_aligned_taps[i] = fft::malloc_float(d_ntaps+3); - memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+3)); + d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); memcpy(&d_aligned_taps[i][i], d_taps, sizeof(float)*(d_ntaps)); } } @@ -105,12 +108,12 @@ namespace gr { float fir_filter_fff::filter(const float input[]) { - const float *ar = (float *)((unsigned long) input & ~15); + const float *ar = (float *)((unsigned long) input & ~(d_align-1)); unsigned al = input - ar; volk_32f_x2_dot_prod_32f_a(d_output, ar, d_aligned_taps[al], - (d_ntaps + al - 1) / 4 + 1); + d_ntaps+al); return *d_output; } -- cgit From 4e2f38528e90ee4a05644d48d015b098a8651405 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 14 Jun 2012 12:44:41 -0400 Subject: filter: switched ccc FIR filter to use aligned VOLK calls. --- gr-filter/include/filter/fir_filter.h | 8 +++++++ gr-filter/lib/fir_filter.cc | 39 ++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 99acb7a0a..3ce21d304 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -85,6 +85,10 @@ namespace gr { private: unsigned int d_ntaps; gr_complex *d_taps; + float **d_aligned_taps; + gr_complex *d_output; + int d_align; + int d_naligned; }; /**************************************************************/ @@ -112,6 +116,10 @@ namespace gr { private: unsigned int d_ntaps; gr_complex *d_taps; + gr_complex **d_aligned_taps; + gr_complex *d_output; + int d_align; + int d_naligned; }; } /* namespace kernel */ diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 1e5990294..973be45d4 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -224,16 +224,32 @@ namespace gr { fir_filter_ccc::fir_filter_ccc(int decimation, const std::vector &taps) { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(gr_complex); + d_taps = NULL; set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_complex(1); } fir_filter_ccc::~fir_filter_ccc() { + // Free taps if(d_taps != NULL) { fft::free(d_taps); d_taps = NULL; } + + // Free all aligned taps + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + + // Free output sample + fft::free(d_output); } void @@ -243,6 +259,11 @@ namespace gr { if(d_taps != NULL) { fft::free(d_taps); d_taps = NULL; + + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); } d_ntaps = (int)taps.size(); @@ -250,6 +271,14 @@ namespace gr { for(unsigned int i = 0; i < d_ntaps; i++) { d_taps[d_ntaps-i-1] = taps[i]; } + + // Make a set of taps at all possible arch alignments + d_aligned_taps = (gr_complex**)malloc(d_naligned*sizeof(gr_complex**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_complex(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(gr_complex)*(d_ntaps+d_naligned-1)); + memcpy(&d_aligned_taps[i][i], d_taps, sizeof(gr_complex)*(d_ntaps)); + } } std::vector @@ -270,9 +299,13 @@ namespace gr { gr_complex fir_filter_ccc::filter(const gr_complex input[]) { - gr_complex output; - volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); - return output; + const gr_complex *ar = (gr_complex *)((unsigned long) input & ~(d_align-1)); + unsigned al = input - ar; + + volk_32fc_x2_dot_prod_32fc_a(d_output, ar, + d_aligned_taps[al], + (d_ntaps+al)*sizeof(gr_complex)); + return *d_output; } void -- cgit From 1a5a1d4e2e0374f73cef5fbc9a01bf82d8b7d892 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 14 Jun 2012 16:12:00 -0400 Subject: filter: added a ccf Volk dot product to use with ccf filters and used it in fir_filter_ccf. Produces improved results to previous version. --- gr-filter/include/filter/fir_filter.h | 2 +- gr-filter/lib/fir_filter.cc | 30 ++++-- gr-filter/lib/fir_filter_XXX_impl.cc.t | 2 +- volk/include/volk/volk_32fc_32f_dot_prod_32fc_a.h | 111 ++++++++++++++++++++++ 4 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 volk/include/volk/volk_32fc_32f_dot_prod_32fc_a.h diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 3ce21d304..8bfaa4f50 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -84,7 +84,7 @@ namespace gr { private: unsigned int d_ntaps; - gr_complex *d_taps; + float *d_taps; float **d_aligned_taps; gr_complex *d_output; int d_align; diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 973be45d4..18568da9d 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -145,8 +145,14 @@ namespace gr { fir_filter_ccf::fir_filter_ccf(int decimation, const std::vector &taps) { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(gr_complex); + d_taps = NULL; set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_complex(1); } fir_filter_ccf::~fir_filter_ccf() @@ -167,9 +173,17 @@ namespace gr { } d_ntaps = (int)taps.size(); - d_taps = fft::malloc_complex(d_ntaps); + d_taps = fft::malloc_float(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = gr_complex(taps[i],0); + d_taps[d_ntaps-i-1] = taps[i]; + } + + // Make a set of taps at all possible arch alignments + d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); + memcpy(&d_aligned_taps[i][i], d_taps, sizeof(float)*(d_ntaps)); } } @@ -178,7 +192,7 @@ namespace gr { { std::vector t; for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1].real()); + t.push_back(d_taps[d_ntaps-i-1]); return t; } @@ -191,9 +205,13 @@ namespace gr { gr_complex fir_filter_ccf::filter(const gr_complex input[]) { - gr_complex output; - volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); - return output; + const gr_complex *ar = (gr_complex *)((unsigned long) input & ~(d_align-1)); + unsigned al = input - ar; + + volk_32fc_32f_dot_prod_32fc_a(d_output, ar, + d_aligned_taps[al], + (d_ntaps+al)); + return *d_output; } void diff --git a/gr-filter/lib/fir_filter_XXX_impl.cc.t b/gr-filter/lib/fir_filter_XXX_impl.cc.t index 18bec38be..8ea8151f8 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.cc.t +++ b/gr-filter/lib/fir_filter_XXX_impl.cc.t @@ -40,7 +40,7 @@ namespace gr { @IMPL_NAME@::@IMPL_NAME@(int decimation, const std::vector<@TAP_TYPE@> &taps) - : gr_sync_decimator("fir_filter_fff", + : gr_sync_decimator("@BASE_NAME@", gr_make_io_signature(1, 1, sizeof(@I_TYPE@)), gr_make_io_signature(1, 1, sizeof(@O_TYPE@)), decimation) diff --git a/volk/include/volk/volk_32fc_32f_dot_prod_32fc_a.h b/volk/include/volk/volk_32fc_32f_dot_prod_32fc_a.h new file mode 100644 index 000000000..109b787e8 --- /dev/null +++ b/volk/include/volk/volk_32fc_32f_dot_prod_32fc_a.h @@ -0,0 +1,111 @@ +#ifndef INCLUDED_volk_32fc_32f_dot_prod_32fc_a_H +#define INCLUDED_volk_32fc_32f_dot_prod_32fc_a_H + +#include +#include + + +#ifdef LV_HAVE_GENERIC + + +static inline void volk_32fc_32f_dot_prod_32fc_a_generic(lv_32fc_t* result, const lv_32fc_t* input, const float * taps, unsigned int num_points) { + + float res[2]; + float *realpt = &res[0], *imagpt = &res[1]; + const float* aPtr = (float*)input; + const float* bPtr= taps; + unsigned int number = 0; + + *realpt = 0; + *imagpt = 0; + + for(number = 0; number < num_points; number++){ + *realpt += ((*aPtr++) * (*bPtr)); + *imagpt += ((*aPtr++) * (*bPtr++)); + } + + *result = *(lv_32fc_t*)(&res[0]); +} + +#endif /*LV_HAVE_GENERIC*/ + + +#ifdef LV_HAVE_SSE + + +static inline void volk_32fc_32f_dot_prod_32fc_a_sse( lv_32fc_t* result, const lv_32fc_t* input, const float* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 8; + + float res[2]; + float *realpt = &res[0], *imagpt = &res[1]; + const float* aPtr = (float*)input; + const float* bPtr = taps; + + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 x0Val, x1Val, x2Val, x3Val; + __m128 c0Val, c1Val, c2Val, c3Val; + + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + a0Val = _mm_load_ps(aPtr); + a1Val = _mm_load_ps(aPtr+4); + a2Val = _mm_load_ps(aPtr+8); + a3Val = _mm_load_ps(aPtr+12); + + x0Val = _mm_load_ps(bPtr); + x1Val = _mm_load_ps(bPtr); + x2Val = _mm_load_ps(bPtr+4); + x3Val = _mm_load_ps(bPtr+4); + b0Val = _mm_unpacklo_ps(x0Val, x1Val); + b1Val = _mm_unpackhi_ps(x0Val, x1Val); + b2Val = _mm_unpacklo_ps(x2Val, x3Val); + b3Val = _mm_unpackhi_ps(x2Val, x3Val); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 16; + bPtr += 8; + } + + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + *realpt = dotProductVector[0]; + *imagpt = dotProductVector[1]; + *realpt += dotProductVector[2]; + *imagpt += dotProductVector[3]; + + number = sixteenthPoints*8; + for(;number < num_points; number++){ + *realpt += ((*aPtr++) * (*bPtr)); + *imagpt += ((*aPtr++) * (*bPtr++)); + } + + *result = *(lv_32fc_t*)(&res[0]); +} + +#endif /*LV_HAVE_SSE*/ + + +#endif /*INCLUDED_volk_32fc_32f_dot_prod_32fc_a_H*/ -- cgit From a74286a2aa7fcddb52c165ba2c17cb2f55b5b592 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Thu, 14 Jun 2012 16:51:15 -0400 Subject: filter: fixed FIR filter QA code to make sure it excersizes SIMD call. --- gr-filter/python/qa_fir_filter.py | 154 ++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 14 deletions(-) diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py index 93974bb89..ac20286cc 100755 --- a/gr-filter/python/qa_fir_filter.py +++ b/gr-filter/python/qa_fir_filter.py @@ -31,10 +31,32 @@ class test_filter(gr_unittest.TestCase): self.tb = None def test_fir_filter_fff_001(self): - src_data = [1, 2, 3, 4] - expected_data = [0.5, 1.5, 2.5, 3.5] + src_data = 40*[1, 2, 3, 4] + expected_data = (0.5, 1.5, 3.0, 5.0, 5.5, 6.5, 8.0, 10.0, + 10.5, 11.5, 13.0, 15.0, 15.5, 16.5, 18.0, + 20.0, 20.5, 21.5, 23.0, 25.0, 25.5, 26.5, + 28.0, 30.0, 30.5, 31.5, 33.0, 35.0, 35.5, + 36.5, 38.0, 40.0, 40.5, 41.5, 43.0, 45.0, + 45.5, 46.5, 48.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0) src = gr.vector_source_f(src_data) - op = filter.fir_filter_fff(1, [0.5, 0.5]) + op = filter.fir_filter_fff(1, 20*[0.5, 0.5]) dst = gr.vector_sink_f() self.tb.connect(src, op, dst) self.tb.run() @@ -42,23 +64,72 @@ class test_filter(gr_unittest.TestCase): self.assertFloatTuplesAlmostEqual(expected_data, result_data, 5) def test_fir_filter_fff_002(self): - src_data = 10*[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - expected_data = [0.0,] + 4*[3.5, 7.5, 1.5, 5.5, 4.5,] + [3.5, 7.5, 1.5, 5.5] + src_data = 40*[1, 2, 3, 4] + expected_data = (0.5, 5.5, 10.5, 15.5, 20.5, 25.5, 30.5, 35.5, + 40.5, 45.5, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, + 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0) src = gr.vector_source_f(src_data) - op = filter.fir_filter_fff(4, [0.5, 0.5]) + op = filter.fir_filter_fff(4, 20*[0.5, 0.5]) dst = gr.vector_sink_f() self.tb.connect(src, op, dst) self.tb.run() result_data = dst.data() - print result_data - print expected_data self.assertFloatTuplesAlmostEqual(expected_data, result_data, 5) def test_fir_filter_ccf_001(self): - src_data = [1+1j, 2+2j, 3+3j, 4+4j] - expected_data = [0.5+0.5j, 1.5+1.5j, 2.5+2.5j, 3.5+3.5j] + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((0.5+0.5j), (1.5+1.5j), (3+3j), (5+5j), (5.5+5.5j), + (6.5+6.5j), (8+8j), (10+10j), (10.5+10.5j), (11.5+11.5j), + (13+13j), (15+15j), (15.5+15.5j), (16.5+16.5j), (18+18j), + (20+20j), (20.5+20.5j), (21.5+21.5j), (23+23j), (25+25j), + (25.5+25.5j), (26.5+26.5j), (28+28j), (30+30j), (30.5+30.5j), + (31.5+31.5j), (33+33j), (35+35j), (35.5+35.5j), (36.5+36.5j), + (38+38j), (40+40j), (40.5+40.5j), (41.5+41.5j), (43+43j), + (45+45j), (45.5+45.5j), (46.5+46.5j), (48+48j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j)) + + src = gr.vector_source_c(src_data) + op = filter.fir_filter_ccf(1, 20*[0.5, 0.5]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + def test_fir_filter_ccf_002(self): + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((0.5+0.5j), (5.5+5.5j), (10.5+10.5j), (15.5+15.5j), + (20.5+20.5j), (25.5+25.5j), (30.5+30.5j), (35.5+35.5j), + (40.5+40.5j), (45.5+45.5j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j)) + src = gr.vector_source_c(src_data) - op = filter.fir_filter_ccf(1, [0.5, 0.5]) + op = filter.fir_filter_ccf(4, 20*[0.5, 0.5]) dst = gr.vector_sink_c() self.tb.connect(src, op, dst) self.tb.run() @@ -66,10 +137,43 @@ class test_filter(gr_unittest.TestCase): self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) def test_fir_filter_ccc_001(self): - src_data = [1+1j, 2+2j, 3+3j, 4+4j] - expected_data = [-0.5+1.5j, -1.5+4.5j, -2.5+7.5j, -3.5+10.5j] + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((-0.5+1.5j), (-1.5+4.5j), (-3+9j), (-5+15j), + (-5.5+16.5j), (-6.5+19.5j), (-8+24j), (-10+30j), + (-10.5+31.5j), (-11.5+34.5j), (-13+39j), (-15+45j), + (-15.5+46.5j), (-16.5+49.5j), (-18+54j), (-20+60j), + (-20.5+61.5j), (-21.5+64.5j), (-23+69j), (-25+75j), + (-25.5+76.5j), (-26.5+79.5j), (-28+84j), (-30+90j), + (-30.5+91.5j), (-31.5+94.5j), (-33+99j), (-35+105j), + (-35.5+106.5j), (-36.5+109.5j), (-38+114j), (-40+120j), + (-40.5+121.5j), (-41.5+124.5j), (-43+129j), (-45+135j), + (-45.5+136.5j), (-46.5+139.5j), (-48+144j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j)) src = gr.vector_source_c(src_data) - op = filter.fir_filter_ccc(1, [0.5+1j, 0.5+1j]) + op = filter.fir_filter_ccc(1, 20*[0.5+1j, 0.5+1j]) dst = gr.vector_sink_c() self.tb.connect(src, op, dst) self.tb.run() @@ -92,6 +196,28 @@ class test_filter(gr_unittest.TestCase): result_data = dst.data() self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + def test_fir_filter_ccc_003(self): + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((-0.5+1.5j), (-5.5+16.5j), (-10.5+31.5j), + (-15.5+46.5j), (-20.5+61.5j), (-25.5+76.5j), + (-30.5+91.5j), (-35.5+106.5j), (-40.5+121.5j), + (-45.5+136.5j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j)) + src = gr.vector_source_c(src_data) + op = filter.fir_filter_ccc(4, 20*[0.5+1j, 0.5+1j]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + if __name__ == '__main__': gr_unittest.run(test_filter, "test_filter.xml") -- cgit From 5585c71229cfa7886e0bd090828cd1f5104f6b27 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 08:43:20 -0400 Subject: filter: adding ssc and fsf versions of filter with associated new Volk kernels. These routines work and pass QA. They could use some performance work. the FSF is just slightly slower than before; the SCC version is more noticably slower. Both could benefit, probably, by using SSE2 intrinsics to handle the shorts. --- gr-filter/include/filter/CMakeLists.txt | 2 +- gr-filter/include/filter/fir_filter.h | 62 ++++++ gr-filter/lib/CMakeLists.txt | 2 +- gr-filter/lib/fir_filter.cc | 232 ++++++++++++++++++++++ gr-filter/python/qa_fir_filter.py | 95 +++++++++ gr-filter/swig/filter_swig.i | 6 + volk/include/volk/volk_16i_32fc_dot_prod_32fc_a.h | 122 ++++++++++++ volk/include/volk/volk_32f_x2_dot_prod_16i_a.h | 98 +++++++++ 8 files changed, 617 insertions(+), 2 deletions(-) create mode 100644 volk/include/volk/volk_16i_32fc_dot_prod_32fc_a.h create mode 100644 volk/include/volk/volk_32f_x2_dot_prod_16i_a.h diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 5b209873c..2620d3f54 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -64,7 +64,7 @@ endmacro(expand_h) ######################################################################## # Invoke macro to generate various sources ####################################################################### -expand_h(fir_filter_XXX fff ccf ccc) +expand_h(fir_filter_XXX fff ccf ccc scc fsf) add_custom_target(filter_generated_includes DEPENDS ${generated_includes} diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 8bfaa4f50..1fb3afb4d 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -122,6 +122,68 @@ namespace gr { int d_naligned; }; + /**************************************************************/ + + class FILTER_API fir_filter_scc + { + public: + fir_filter_scc(int decimation, + const std::vector &taps); + ~fir_filter_scc(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + unsigned int ntaps() const; + + gr_complex filter(const short input[]); + void filterN(gr_complex output[], + const short input[], + unsigned long n); + void filterNdec(gr_complex output[], + const short input[], + unsigned long n, + unsigned int decimate); + + private: + unsigned int d_ntaps; + gr_complex *d_taps; + gr_complex **d_aligned_taps; + gr_complex *d_output; + int d_align; + int d_naligned; + }; + + /**************************************************************/ + + class FILTER_API fir_filter_fsf + { + public: + fir_filter_fsf(int decimation, + const std::vector &taps); + ~fir_filter_fsf(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + unsigned int ntaps() const; + + short filter(const float input[]); + void filterN(short output[], + const float input[], + unsigned long n); + void filterNdec(short output[], + const float input[], + unsigned long n, + unsigned int decimate); + + private: + unsigned int d_ntaps; + float *d_taps; + float **d_aligned_taps; + short *d_output; + int d_align; + int d_naligned; + }; + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index b51a23bab..f5dbd1bb3 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -80,7 +80,7 @@ endmacro(expand_cc) ######################################################################## # Invoke macro to generate various sources ######################################################################## -expand_cc(fir_filter_XXX_impl fff ccf ccc) +expand_cc(fir_filter_XXX_impl fff ccf ccc scc fsf) ######################################################################## diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 18568da9d..be8017400 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -349,6 +349,238 @@ namespace gr { } } + /**************************************************************/ + + fir_filter_scc::fir_filter_scc(int decimation, + const std::vector &taps) + { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(short); + + d_taps = NULL; + set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_complex(1); + } + + fir_filter_scc::~fir_filter_scc() + { + // Free taps + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + + // Free all aligned taps + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + + // Free output sample + fft::free(d_output); + } + + void + fir_filter_scc::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_complex(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[d_ntaps-i-1] = taps[i]; + } + + // Make a set of taps at all possible arch alignments + d_aligned_taps = (gr_complex**)malloc(d_naligned*sizeof(gr_complex**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_complex(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(gr_complex)*(d_ntaps+d_naligned-1)); + memcpy(&d_aligned_taps[i][i], d_taps, sizeof(gr_complex)*(d_ntaps)); + } + + } + + std::vector + fir_filter_scc::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[d_ntaps-i-1]); + return t; + } + + unsigned int + fir_filter_scc::ntaps() const + { + return d_ntaps; + } + + gr_complex + fir_filter_scc::filter(const short input[]) + { + const short *ar = (short *)((unsigned long) input & ~(d_align-1)); + unsigned al = input - ar; + + volk_16i_32fc_dot_prod_32fc_a(d_output, ar, + d_aligned_taps[al], + (d_ntaps+al)); + + return *d_output; + } + + void + fir_filter_scc::filterN(gr_complex output[], + const short input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) + output[i] = filter(&input[i]); + } + + + void + fir_filter_scc::filterNdec(gr_complex output[], + const short input[], + unsigned long n, + unsigned int decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++){ + output[i] = filter(&input[j]); + j += decimate; + } + } + + /**************************************************************/ + + fir_filter_fsf::fir_filter_fsf(int decimation, + const std::vector &taps) + { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(float); + + d_taps = NULL; + set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = (short*)fft::malloc_float(1); + } + + fir_filter_fsf::~fir_filter_fsf() + { + // Free taps + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + } + + // Free all aligned taps + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + + // Free output sample + fft::free(d_output); + } + + void + fir_filter_fsf::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_taps != NULL) { + fft::free(d_taps); + d_taps = NULL; + + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + } + + d_ntaps = (int)taps.size(); + d_taps = fft::malloc_float(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_taps[d_ntaps-i-1] = taps[i]; + } + + // Make a set of taps at all possible arch alignments + d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); + memcpy(&d_aligned_taps[i][i], d_taps, sizeof(float)*(d_ntaps)); + } + } + + std::vector + fir_filter_fsf::taps() const + { + std::vector t; + for(unsigned int i = 0; i < d_ntaps; i++) + t.push_back(d_taps[d_ntaps-i-1]); + return t; + } + + unsigned int + fir_filter_fsf::ntaps() const + { + return d_ntaps; + } + + short + fir_filter_fsf::filter(const float input[]) + { + const float *ar = (float *)((unsigned long) input & ~(d_align-1)); + unsigned al = input - ar; + + volk_32f_x2_dot_prod_16i_a(d_output, ar, + d_aligned_taps[al], + (d_ntaps+al)); + + //float out = 0; + //for(unsigned int i = 0; i < d_ntaps; i++) { + // out += d_taps[i] * input[i]; + //} + //*d_output = (short)out; + + return *d_output; + } + + void + fir_filter_fsf::filterN(short output[], + const float input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) + output[i] = filter(&input[i]); + } + + void + fir_filter_fsf::filterNdec(short output[], + const float input[], + unsigned long n, + unsigned int decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++){ + output[i] = filter(&input[j]); + j += decimate; + } + } + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py index ac20286cc..2a61498a2 100755 --- a/gr-filter/python/qa_fir_filter.py +++ b/gr-filter/python/qa_fir_filter.py @@ -218,6 +218,101 @@ class test_filter(gr_unittest.TestCase): self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + def test_fir_filter_scc_001(self): + src_data = 40*[1, 2, 3, 4] + expected_data = ((0.5+1j), (1.5+3j), (3+6j), (5+10j), (5.5+11j), + (6.5+13j), (8+16j), (10+20j), (10.5+21j), (11.5+23j), + (13+26j), (15+30j), (15.5+31j), (16.5+33j), (18+36j), + (20+40j), (20.5+41j), (21.5+43j), (23+46j), (25+50j), + (25.5+51j), (26.5+53j), (28+56j), (30+60j), (30.5+61j), + (31.5+63j), (33+66j), (35+70j), (35.5+71j), (36.5+73j), + (38+76j), (40+80j), (40.5+81j), (41.5+83j), (43+86j), + (45+90j), (45.5+91j), (46.5+93j), (48+96j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j)) + src = gr.vector_source_s(src_data) + op = filter.fir_filter_scc(1, 20*[0.5+1j, 0.5+1j]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + + def test_fir_filter_scc_002(self): + src_data = 40*[1, 2, 3, 4] + expected_data = ((0.5+1j), (5.5+11j), (10.5+21j), (15.5+31j), (20.5+41j), + (25.5+51j), (30.5+61j), (35.5+71j), (40.5+81j), (45.5+91j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j), + (50+100j), (50+100j), (50+100j), (50+100j), (50+100j)) + src = gr.vector_source_s(src_data) + op = filter.fir_filter_scc(4, 20*[0.5+1j, 0.5+1j]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + def test_fir_filter_fsf_001(self): + src_data = 40*[1, 2, 3, 4] + expected_data =(0, 1, 3, 5, 5, 6, 8, 10, 10, 11, 13, 15, 15, 16, 18, 20, 20, + 21, 23, 25, 25, 26, 28, 30, 30, 31, 33, 35, 35, 36, 38, 40, 40, + 41, 43, 45, 45, 46, 48, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50) + src = gr.vector_source_f(src_data) + op = filter.fir_filter_fsf(1, 20*[0.5, 0.5]) + dst = gr.vector_sink_s() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + + def test_fir_filter_fsf_002(self): + src_data = 40*[1, 2, 3, 4] + expected_data = (0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50) + src = gr.vector_source_f(src_data) + op = filter.fir_filter_fsf(4, 20*[0.5, 0.5]) + dst = gr.vector_sink_s() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + if __name__ == '__main__': gr_unittest.run(test_filter, "test_filter.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index cc15b5722..c9de3fb9a 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -36,6 +36,8 @@ #include "filter/fir_filter_fff.h" #include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" +#include "filter/fir_filter_scc.h" +#include "filter/fir_filter_fsf.h" #include "filter/fft_filter_ccc.h" #include "filter/fft_filter_fff.h" #include "filter/hilbert_fc.h" @@ -50,6 +52,8 @@ %include "filter/fir_filter_fff.h" %include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" +%include "filter/fir_filter_scc.h" +%include "filter/fir_filter_fsf.h" %include "filter/fft_filter_ccc.h" %include "filter/fft_filter_fff.h" %include "filter/hilbert_fc.h" @@ -61,6 +65,8 @@ GR_SWIG_BLOCK_MAGIC2(filter, filter_delay_fc); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_scc); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, hilbert_fc); diff --git a/volk/include/volk/volk_16i_32fc_dot_prod_32fc_a.h b/volk/include/volk/volk_16i_32fc_dot_prod_32fc_a.h new file mode 100644 index 000000000..940aa5de7 --- /dev/null +++ b/volk/include/volk/volk_16i_32fc_dot_prod_32fc_a.h @@ -0,0 +1,122 @@ +#ifndef INCLUDED_volk_16i_32fc_dot_prod_32fc_a_H +#define INCLUDED_volk_16i_32fc_dot_prod_32fc_a_H + +#include +#include + + +#ifdef LV_HAVE_GENERIC + + +static inline void volk_16i_32fc_dot_prod_32fc_a_generic(lv_32fc_t* result, const short* input, const lv_32fc_t * taps, unsigned int num_points) { + + static const int N_UNROLL = 4; + + lv_32fc_t acc0 = 0; + lv_32fc_t acc1 = 0; + lv_32fc_t acc2 = 0; + lv_32fc_t acc3 = 0; + + unsigned i = 0; + unsigned n = (num_points / N_UNROLL) * N_UNROLL; + + for(i = 0; i < n; i += N_UNROLL) { + acc0 += taps[i + 0] * (float)input[i + 0]; + acc1 += taps[i + 1] * (float)input[i + 1]; + acc2 += taps[i + 2] * (float)input[i + 2]; + acc3 += taps[i + 3] * (float)input[i + 3]; + } + + for(; i < num_points; i++) { + acc0 += taps[i] * (float)input[i]; + } + + *result = acc0 + acc1 + acc2 + acc3; +} + +#endif /*LV_HAVE_GENERIC*/ + + +#ifdef LV_HAVE_SSE + + +static inline void volk_16i_32fc_dot_prod_32fc_a_sse( lv_32fc_t* result, const short* input, const lv_32fc_t* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 8; + + float res[2]; + float *realpt = &res[0], *imagpt = &res[1]; + const short* aPtr = input; + const float* bPtr = (float*)taps; + + __m64 m0, m1; + __m128 f0, f1, f2, f3; + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; + + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + m0 = _mm_set_pi16(*(aPtr+3), *(aPtr+2), *(aPtr+1), *(aPtr+0)); + m1 = _mm_set_pi16(*(aPtr+7), *(aPtr+6), *(aPtr+5), *(aPtr+4)); + f0 = _mm_cvtpi16_ps(m0); + f1 = _mm_cvtpi16_ps(m0); + f2 = _mm_cvtpi16_ps(m1); + f3 = _mm_cvtpi16_ps(m1); + + a0Val = _mm_unpacklo_ps(f0, f1); + a1Val = _mm_unpackhi_ps(f0, f1); + a2Val = _mm_unpacklo_ps(f2, f3); + a3Val = _mm_unpackhi_ps(f2, f3); + + b0Val = _mm_load_ps(bPtr); + b1Val = _mm_load_ps(bPtr+4); + b2Val = _mm_load_ps(bPtr+8); + b3Val = _mm_load_ps(bPtr+12); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 8; + bPtr += 16; + } + + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + *realpt = dotProductVector[0]; + *imagpt = dotProductVector[1]; + *realpt += dotProductVector[2]; + *imagpt += dotProductVector[3]; + + number = sixteenthPoints*8; + for(;number < num_points; number++){ + *realpt += ((*aPtr) * (*bPtr++)); + *imagpt += ((*aPtr++) * (*bPtr++)); + } + + *result = *(lv_32fc_t*)(&res[0]); +} + +#endif /*LV_HAVE_SSE*/ + + +#endif /*INCLUDED_volk_16i_32fc_dot_prod_32fc_a_H*/ diff --git a/volk/include/volk/volk_32f_x2_dot_prod_16i_a.h b/volk/include/volk/volk_32f_x2_dot_prod_16i_a.h new file mode 100644 index 000000000..961c2418c --- /dev/null +++ b/volk/include/volk/volk_32f_x2_dot_prod_16i_a.h @@ -0,0 +1,98 @@ +#ifndef INCLUDED_volk_32f_x2_dot_prod_16i_a_H +#define INCLUDED_volk_32f_x2_dot_prod_16i_a_H + +#include +#include + + +#ifdef LV_HAVE_GENERIC + + +static inline void volk_32f_x2_dot_prod_16i_a_generic(int16_t* result, const float* input, const float* taps, unsigned int num_points) { + + float dotProduct = 0; + const float* aPtr = input; + const float* bPtr= taps; + unsigned int number = 0; + + for(number = 0; number < num_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + } + + *result = (int16_t)dotProduct; +} + +#endif /*LV_HAVE_GENERIC*/ + + +#ifdef LV_HAVE_SSE + + +static inline void volk_32f_x2_dot_prod_16i_a_sse(int16_t* result, const float* input, const float* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 16; + + float dotProduct = 0; + const float* aPtr = input; + const float* bPtr = taps; + + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; + + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + a0Val = _mm_load_ps(aPtr); + a1Val = _mm_load_ps(aPtr+4); + a2Val = _mm_load_ps(aPtr+8); + a3Val = _mm_load_ps(aPtr+12); + b0Val = _mm_load_ps(bPtr); + b1Val = _mm_load_ps(bPtr+4); + b2Val = _mm_load_ps(bPtr+8); + b3Val = _mm_load_ps(bPtr+12); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 16; + bPtr += 16; + } + + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + dotProduct = dotProductVector[0]; + dotProduct += dotProductVector[1]; + dotProduct += dotProductVector[2]; + dotProduct += dotProductVector[3]; + + number = sixteenthPoints*16; + for(;number < num_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + } + + *result = (short)dotProduct; + +} + +#endif /*LV_HAVE_SSE*/ + +#endif /*INCLUDED_volk_32f_x2_dot_prod_16i_a_H*/ -- cgit From 59ad06e0e30ad4c24053b64c453cfdbe95cc7500 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 09:57:45 -0400 Subject: filter: improving documentation and adding GRC blocks for fir_filter_xxx blocks. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/fir_filter_xxx.xml | 80 +++++++++++++++++++++++++++++ gr-filter/include/filter/fir_filter_XXX.h.t | 27 ++++++++++ 4 files changed, 109 insertions(+) create mode 100644 gr-filter/grc/fir_filter_xxx.xml diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 1c69e47c3..663cc0b00 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -21,6 +21,7 @@ install(FILES filter_block_tree.xml dc_blocker_xx.xml fft_filter_xxx.xml + fir_filter_xxx.xml filter_delay_fc.xml hilbert_fc.xml pfb_channelizer.xml diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 0c685d9f6..988897fcf 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -32,6 +32,7 @@ Filters dc_blocker_xx fft_filter_xxx + fir_filter_xxx filter_delay_fc hilbert_fc pfb_channelizer_ccf diff --git a/gr-filter/grc/fir_filter_xxx.xml b/gr-filter/grc/fir_filter_xxx.xml new file mode 100644 index 000000000..3925eb555 --- /dev/null +++ b/gr-filter/grc/fir_filter_xxx.xml @@ -0,0 +1,80 @@ + + + + Decimating FIR Filter + fir_filter_xxx + from gnuradio import filter + from gnuradio.filter import firdes + filter.fir_filter_$(type)($decim, $taps) + set_taps($taps) + + Type + type + enum + + + + + + + + + Decimation + decim + 1 + int + + + Taps + taps + $type.taps + + + in + $type.input + + + out + $type.output + + diff --git a/gr-filter/include/filter/fir_filter_XXX.h.t b/gr-filter/include/filter/fir_filter_XXX.h.t index ef6b8bfc9..647c18db4 100644 --- a/gr-filter/include/filter/fir_filter_XXX.h.t +++ b/gr-filter/include/filter/fir_filter_XXX.h.t @@ -31,6 +31,30 @@ namespace gr { namespace filter { + /*! + * \brief FIR filter with @I_TYPE@ input, @O_TYPE@ output, and @TAP_TYPE@ taps + * \ingroup filter_blk + * + * The fir_filter_XXX blocks create finite impulse response + * (FIR) filters that perform the convolution in the time + * domain: + * + * \code + * out = 0 + * for i in ntaps: + * out += input[n-i] * taps[i] + * \endcode + * + * The taps are a C++ vector (or Python list) of values of the + * type specified by the third letter in the block's suffix. For + * this block, the value is of type @TAP_TYPE@. Taps can be + * created using the firdes or optfir tools. + * + * These versions of the filter can also act as down-samplers + * (or decimators) by specifying an integer value for \p + * decimation. + * + */ class FILTER_API @BASE_NAME@ : virtual public gr_sync_decimator { public: @@ -41,6 +65,9 @@ namespace gr { /*! * \brief FIR filter with @I_TYPE@ input, @O_TYPE@ output, and @TAP_TYPE@ taps * \ingroup filter_blk + * + * \param decimation set the integer decimation rate + * \param taps a vector/list of taps of type @TAP_TYPE@ */ static FILTER_API sptr make(int decimation, const std::vector<@TAP_TYPE@> &taps); -- cgit From 44b525e7fe3cf171c12ad785bfc8e0101e71fb37 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 09:59:05 -0400 Subject: volk: adding new kernels to test and profile. --- volk/apps/volk_profile.cc | 3 +++ volk/lib/testqa.cc | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/volk/apps/volk_profile.cc b/volk/apps/volk_profile.cc index 76b9f4031..09ee8410c 100644 --- a/volk/apps/volk_profile.cc +++ b/volk/apps/volk_profile.cc @@ -32,6 +32,7 @@ int main(int argc, char *argv[]) { //VOLK_PROFILE(volk_16i_permute_and_scalar_add_a, 1e-4, 0, 2046, 10000, &results); //VOLK_PROFILE(volk_16i_x4_quad_max_star_16i_a, 1e-4, 0, 2046, 10000, &results); VOLK_PROFILE(volk_16u_byteswap_a, 0, 0, 204600, 10000, &results); + VOLK_PROFILE(volk_16i_32fc_dot_prod_32fc_a, 1e-4, 0, 204600, 10000, &results); VOLK_PROFILE(volk_32f_accumulator_s32f_a, 1e-4, 0, 204600, 10000, &results); VOLK_PROFILE(volk_32f_x2_add_32f_a, 1e-4, 0, 204600, 10000, &results); VOLK_PROFILE(volk_32f_x2_add_32f_u, 1e-4, 0, 204600, 10000, &results); @@ -48,6 +49,7 @@ int main(int argc, char *argv[]) { VOLK_PROFILE(volk_32fc_deinterleave_real_32f_a, 1e-4, 0, 204600, 5000, &results); VOLK_PROFILE(volk_32fc_deinterleave_real_64f_a, 1e-4, 0, 204600, 1000, &results); VOLK_PROFILE(volk_32fc_x2_dot_prod_32fc_a, 1e-4, 0, 204600, 10000, &results); + VOLK_PROFILE(volk_32fc_32f_dot_prod_32fc_a, 1e-4, 0, 204600, 10000, &results); VOLK_PROFILE(volk_32fc_index_max_16u_a, 3, 0, 204600, 10000, &results); VOLK_PROFILE(volk_32fc_s32f_magnitude_16i_a, 1, 32768, 204600, 100, &results); VOLK_PROFILE(volk_32fc_magnitude_32f_a, 1e-4, 0, 204600, 1000, &results); @@ -75,6 +77,7 @@ int main(int argc, char *argv[]) { VOLK_PROFILE(volk_32f_x2_divide_32f_a, 1e-4, 0, 204600, 2000, &results); VOLK_PROFILE(volk_32f_x2_dot_prod_32f_a, 1e-4, 0, 204600, 5000, &results); VOLK_PROFILE(volk_32f_x2_dot_prod_32f_u, 1e-4, 0, 204600, 5000, &results); + VOLK_PROFILE(volk_32f_x2_dot_prod_16i_a, 1e-4, 0, 204600, 5000, &results); //VOLK_PROFILE(volk_32f_s32f_32f_fm_detect_32f_a, 1e-4, 2046, 10000, &results); VOLK_PROFILE(volk_32f_index_max_16u_a, 3, 0, 204600, 5000, &results); VOLK_PROFILE(volk_32f_x2_s32f_interleave_16ic_a, 1, 32768, 204600, 3000, &results); diff --git a/volk/lib/testqa.cc b/volk/lib/testqa.cc index d0204fc01..cfa8a9195 100644 --- a/volk/lib/testqa.cc +++ b/volk/lib/testqa.cc @@ -20,6 +20,7 @@ VOLK_RUN_TESTS(volk_16i_convert_8i_u, 0, 0, 20460, 1); //VOLK_RUN_TESTS(volk_16i_permute_and_scalar_add_a, 1e-4, 0, 2046, 1000); //VOLK_RUN_TESTS(volk_16i_x4_quad_max_star_16i_a, 1e-4, 0, 2046, 1000); VOLK_RUN_TESTS(volk_16u_byteswap_a, 0, 0, 20460, 1); +//VOLK_RUN_TESTS(volk_16i_32fc_dot_prod_32fc_a, 1e-4, 0, 204600, 1); VOLK_RUN_TESTS(volk_32f_accumulator_s32f_a, 1e-4, 0, 20460, 1); VOLK_RUN_TESTS(volk_32f_x2_add_32f_a, 1e-4, 0, 20460, 1); VOLK_RUN_TESTS(volk_32f_x2_add_32f_u, 1e-4, 0, 20460, 1); @@ -35,6 +36,7 @@ VOLK_RUN_TESTS(volk_32fc_s32f_deinterleave_real_16i_a, 0, 32768, 20460, 1); VOLK_RUN_TESTS(volk_32fc_deinterleave_real_32f_a, 1e-4, 0, 20460, 1); VOLK_RUN_TESTS(volk_32fc_deinterleave_real_64f_a, 1e-4, 0, 20460, 1); VOLK_RUN_TESTS(volk_32fc_x2_dot_prod_32fc_a, 1e-4, 0, 204600, 1); +VOLK_RUN_TESTS(volk_32fc_32f_dot_prod_32fc_a, 1e-4, 0, 204600, 1); VOLK_RUN_TESTS(volk_32fc_index_max_16u_a, 3, 0, 20460, 1); VOLK_RUN_TESTS(volk_32fc_s32f_magnitude_16i_a, 1, 32768, 20460, 1); VOLK_RUN_TESTS(volk_32fc_magnitude_32f_a, 1e-4, 0, 20460, 1); @@ -52,7 +54,8 @@ VOLK_RUN_TESTS(volk_32fc_x2_square_dist_32f_a, 1e-4, 0, 20460, 1); VOLK_RUN_TESTS(volk_32fc_x2_s32f_square_dist_scalar_mult_32f_a, 1e-4, 10, 20460, 1); VOLK_RUN_TESTS(volk_32f_x2_divide_32f_a, 1e-4, 0, 20460, 1); VOLK_RUN_TESTS(volk_32f_x2_dot_prod_32f_a, 1e-4, 0, 204600, 1); -VOLK_RUN_TESTS(volk_32f_x2_dot_prod_32f_u, 1e-4, 0, 204600, 1); +//VOLK_RUN_TESTS(volk_32f_x2_dot_prod_32f_u, 1e-4, 0, 204600, 1); +VOLK_RUN_TESTS(volk_32f_x2_dot_prod_16i_a, 1e-4, 0, 204600, 1); //VOLK_RUN_TESTS(volk_32f_s32f_32f_fm_detect_32f_a, 1e-4, 2046, 10000); VOLK_RUN_TESTS(volk_32f_index_max_16u_a, 3, 0, 20460, 1); VOLK_RUN_TESTS(volk_32f_x2_s32f_interleave_16ic_a, 1, 32767, 20460, 1); -- cgit From 408f44a735ff8e90b3206500b8154b37eb3e65b2 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 11:52:51 -0400 Subject: filter: mods to fir filter bases to make consistent and simpler. --- gr-filter/include/filter/fir_filter.h | 30 +-- gr-filter/include/filter/fir_filter_with_buffer.h | 113 +++++++++- gr-filter/lib/fir_filter.cc | 203 ++++++++---------- gr-filter/lib/fir_filter_with_buffer.cc | 243 +++++++++++++++++++--- 4 files changed, 425 insertions(+), 164 deletions(-) diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 1fb3afb4d..ba82c7f48 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -51,13 +51,13 @@ namespace gr { unsigned long n, unsigned int decimate); - private: - unsigned int d_ntaps; - float *d_taps; - float **d_aligned_taps; - float *d_output; - int d_align; - int d_naligned; + protected: + std::vector d_taps; + unsigned int d_ntaps; + float **d_aligned_taps; + float *d_output; + int d_align; + int d_naligned; }; /**************************************************************/ @@ -82,9 +82,9 @@ namespace gr { unsigned long n, unsigned int decimate); - private: + protected: + std::vector d_taps; unsigned int d_ntaps; - float *d_taps; float **d_aligned_taps; gr_complex *d_output; int d_align; @@ -113,9 +113,9 @@ namespace gr { unsigned long n, unsigned int decimate); - private: + protected: + std::vector d_taps; unsigned int d_ntaps; - gr_complex *d_taps; gr_complex **d_aligned_taps; gr_complex *d_output; int d_align; @@ -144,9 +144,9 @@ namespace gr { unsigned long n, unsigned int decimate); - private: + protected: + std::vector d_taps; unsigned int d_ntaps; - gr_complex *d_taps; gr_complex **d_aligned_taps; gr_complex *d_output; int d_align; @@ -175,9 +175,9 @@ namespace gr { unsigned long n, unsigned int decimate); - private: + protected: + std::vector d_taps; unsigned int d_ntaps; - float *d_taps; float **d_aligned_taps; short *d_output; int d_align; diff --git a/gr-filter/include/filter/fir_filter_with_buffer.h b/gr-filter/include/filter/fir_filter_with_buffer.h index feebb382f..2ccb74906 100644 --- a/gr-filter/include/filter/fir_filter_with_buffer.h +++ b/gr-filter/include/filter/fir_filter_with_buffer.h @@ -38,10 +38,14 @@ namespace gr { class FILTER_API fir_filter_with_buffer_fff { private: - float *d_taps; + std::vector d_taps; + unsigned int d_ntaps; float *d_buffer; unsigned int d_idx; - unsigned int d_ntaps; + float *d_aligned_taps; + float *d_output; + int d_align; + int d_naligned; public: @@ -130,10 +134,14 @@ namespace gr { class FILTER_API fir_filter_with_buffer_ccc { private: - gr_complex *d_taps; + std::vector d_taps; + unsigned int d_ntaps; gr_complex *d_buffer; unsigned int d_idx; - unsigned int d_ntaps; + gr_complex *d_aligned_taps; + gr_complex *d_output; + int d_align; + int d_naligned; public: @@ -211,6 +219,103 @@ namespace gr { std::vector taps() const; }; + + /**************************************************************/ + + + /*! + * \brief FIR with internal buffer for gr_complex input, gr_complex output and gr_complex taps. + * \ingroup filter + */ + class FILTER_API fir_filter_with_buffer_ccf + { + private: + std::vector d_taps; + unsigned int d_ntaps; + gr_complex *d_buffer; + unsigned int d_idx; + float *d_aligned_taps; + gr_complex *d_output; + int d_align; + int d_naligned; + + public: + + // CONSTRUCTORS + + /*! + * \brief construct new FIR with given taps. + * + * Note that taps must be in forward order, e.g., coefficient 0 is + * stored in new_taps[0], coefficient 1 is stored in + * new_taps[1], etc. + */ + fir_filter_with_buffer_ccf(const std::vector &taps); + + ~fir_filter_with_buffer_ccf(); + + // MANIPULATORS + + /*! + * \brief compute a single output value. + * + * \p input is a single input value of the filter type + * + * \returns the filtered input value. + */ + gr_complex filter(gr_complex input); + + /*! + * \brief compute a single output value; designed for decimating filters. + * + * \p input is a single input value of the filter type. The value of dec is the + * decimating value of the filter, so input[] must have dec valid values. + * The filter pushes dec number of items onto the circ. buffer before computing + * a single output. + * + * \returns the filtered input value. + */ + gr_complex filter(const gr_complex input[], unsigned long dec); + + /*! + * \brief compute an array of N output values. + * + * \p input must have (n - 1 + ntaps()) valid entries. + * input[0] .. input[n - 1 + ntaps() - 1] are referenced to compute the output values. + */ + void filterN(gr_complex output[], + const gr_complex input[], + unsigned long n); + + /*! + * \brief compute an array of N output values, decimating the input + * + * \p input must have (decimate * (n - 1) + ntaps()) valid entries. + * input[0] .. input[decimate * (n - 1) + ntaps() - 1] are referenced to + * compute the output values. + */ + void filterNdec(gr_complex output[], const gr_complex input[], + unsigned long n, unsigned long decimate); + + // ACCESSORS + + /*! + * \return number of taps in filter. + */ + unsigned int ntaps() const { return d_ntaps; } + + /*! + * \brief install \p new_taps as the current taps. + */ + void set_taps(const std::vector &taps); + + /*! + * \return current taps + */ + std::vector taps() const; + }; + + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index be8017400..6c82dda5e 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -36,7 +36,7 @@ namespace gr { d_align = volk_get_alignment(); d_naligned = d_align / sizeof(float); - d_taps = NULL; + d_aligned_taps = NULL; set_taps(taps); // Make sure the output sample is always aligned, too. @@ -45,17 +45,14 @@ namespace gr { fir_filter_fff::~fir_filter_fff() { - // Free taps - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - // Free all aligned taps - for(int i = 0; i < d_naligned; i++) { - fft::free(d_aligned_taps[i]); + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; } - fft::free(d_aligned_taps); // Free output sample fft::free(d_output); @@ -65,37 +62,33 @@ namespace gr { fir_filter_fff::set_taps(const std::vector &taps) { // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - + if(d_aligned_taps!= NULL) { for(int i = 0; i < d_naligned; i++) { fft::free(d_aligned_taps[i]); } fft::free(d_aligned_taps); + d_aligned_taps = NULL; } d_ntaps = (int)taps.size(); - d_taps = fft::malloc_float(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; - } + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); // Make a set of taps at all possible arch alignments d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); for(int i = 0; i < d_naligned; i++) { d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); - memcpy(&d_aligned_taps[i][i], d_taps, sizeof(float)*(d_ntaps)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } } std::vector fir_filter_fff::taps() const { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); return t; } @@ -148,7 +141,7 @@ namespace gr { d_align = volk_get_alignment(); d_naligned = d_align / sizeof(gr_complex); - d_taps = NULL; + d_aligned_taps = NULL; set_taps(taps); // Make sure the output sample is always aligned, too. @@ -157,42 +150,50 @@ namespace gr { fir_filter_ccf::~fir_filter_ccf() { - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; + // Free all aligned taps + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; } - } + + // Free output sample + fft::free(d_output); + } void fir_filter_ccf::set_taps(const std::vector &taps) { // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; } d_ntaps = (int)taps.size(); - d_taps = fft::malloc_float(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; - } + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); // Make a set of taps at all possible arch alignments d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); for(int i = 0; i < d_naligned; i++) { d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); - memcpy(&d_aligned_taps[i][i], d_taps, sizeof(float)*(d_ntaps)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } } std::vector fir_filter_ccf::taps() const { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); return t; } @@ -245,7 +246,7 @@ namespace gr { d_align = volk_get_alignment(); d_naligned = d_align / sizeof(gr_complex); - d_taps = NULL; + d_aligned_taps = NULL; set_taps(taps); // Make sure the output sample is always aligned, too. @@ -254,57 +255,50 @@ namespace gr { fir_filter_ccc::~fir_filter_ccc() { - // Free taps - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - // Free all aligned taps - for(int i = 0; i < d_naligned; i++) { - fft::free(d_aligned_taps[i]); + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; } - fft::free(d_aligned_taps); // Free output sample fft::free(d_output); - } + } void fir_filter_ccc::set_taps(const std::vector &taps) { // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - + if(d_aligned_taps != NULL) { for(int i = 0; i < d_naligned; i++) { fft::free(d_aligned_taps[i]); } fft::free(d_aligned_taps); + d_aligned_taps = NULL; } d_ntaps = (int)taps.size(); - d_taps = fft::malloc_complex(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; - } + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); // Make a set of taps at all possible arch alignments d_aligned_taps = (gr_complex**)malloc(d_naligned*sizeof(gr_complex**)); for(int i = 0; i < d_naligned; i++) { d_aligned_taps[i] = fft::malloc_complex(d_ntaps+d_naligned-1); memset(d_aligned_taps[i], 0, sizeof(gr_complex)*(d_ntaps+d_naligned-1)); - memcpy(&d_aligned_taps[i][i], d_taps, sizeof(gr_complex)*(d_ntaps)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } } std::vector fir_filter_ccc::taps() const { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); return t; } @@ -357,7 +351,7 @@ namespace gr { d_align = volk_get_alignment(); d_naligned = d_align / sizeof(short); - d_taps = NULL; + d_aligned_taps = NULL; set_taps(taps); // Make sure the output sample is always aligned, too. @@ -366,58 +360,50 @@ namespace gr { fir_filter_scc::~fir_filter_scc() { - // Free taps - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - // Free all aligned taps - for(int i = 0; i < d_naligned; i++) { - fft::free(d_aligned_taps[i]); + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; } - fft::free(d_aligned_taps); // Free output sample fft::free(d_output); - } + } void fir_filter_scc::set_taps(const std::vector &taps) { // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - + if(d_aligned_taps != NULL) { for(int i = 0; i < d_naligned; i++) { fft::free(d_aligned_taps[i]); } fft::free(d_aligned_taps); + d_aligned_taps = NULL; } d_ntaps = (int)taps.size(); - d_taps = fft::malloc_complex(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; - } + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); // Make a set of taps at all possible arch alignments d_aligned_taps = (gr_complex**)malloc(d_naligned*sizeof(gr_complex**)); for(int i = 0; i < d_naligned; i++) { d_aligned_taps[i] = fft::malloc_complex(d_ntaps+d_naligned-1); memset(d_aligned_taps[i], 0, sizeof(gr_complex)*(d_ntaps+d_naligned-1)); - memcpy(&d_aligned_taps[i][i], d_taps, sizeof(gr_complex)*(d_ntaps)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } - } std::vector fir_filter_scc::taps() const { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); return t; } @@ -471,7 +457,7 @@ namespace gr { d_align = volk_get_alignment(); d_naligned = d_align / sizeof(float); - d_taps = NULL; + d_aligned_taps = NULL; set_taps(taps); // Make sure the output sample is always aligned, too. @@ -480,57 +466,50 @@ namespace gr { fir_filter_fsf::~fir_filter_fsf() { - // Free taps - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - - // Free all aligned taps - for(int i = 0; i < d_naligned; i++) { - fft::free(d_aligned_taps[i]); + // Free all aligned taps + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; } - fft::free(d_aligned_taps); // Free output sample fft::free(d_output); - } + } void fir_filter_fsf::set_taps(const std::vector &taps) { // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - + if(d_aligned_taps != NULL) { for(int i = 0; i < d_naligned; i++) { fft::free(d_aligned_taps[i]); } fft::free(d_aligned_taps); + d_aligned_taps = NULL; } d_ntaps = (int)taps.size(); - d_taps = fft::malloc_float(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; - } + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); // Make a set of taps at all possible arch alignments d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); for(int i = 0; i < d_naligned; i++) { d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); - memcpy(&d_aligned_taps[i][i], d_taps, sizeof(float)*(d_ntaps)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } } std::vector fir_filter_fsf::taps() const { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); return t; } @@ -550,12 +529,6 @@ namespace gr { d_aligned_taps[al], (d_ntaps+al)); - //float out = 0; - //for(unsigned int i = 0; i < d_ntaps; i++) { - // out += d_taps[i] * input[i]; - //} - //*d_output = (short)out; - return *d_output; } diff --git a/gr-filter/lib/fir_filter_with_buffer.cc b/gr-filter/lib/fir_filter_with_buffer.cc index a2b804a08..b1cc589a5 100644 --- a/gr-filter/lib/fir_filter_with_buffer.cc +++ b/gr-filter/lib/fir_filter_with_buffer.cc @@ -27,6 +27,7 @@ #include #include #include +#include namespace gr { namespace filter { @@ -34,14 +35,30 @@ namespace gr { fir_filter_with_buffer_fff::fir_filter_with_buffer_fff(const std::vector &taps) { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(float); + d_buffer = NULL; + d_aligned_taps = NULL; set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_float(1); } fir_filter_with_buffer_fff::~fir_filter_with_buffer_fff() { - if(d_buffer != NULL) + if(d_buffer != NULL) { fft::free(d_buffer); + d_buffer = NULL; + } + + // Free aligned taps + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + + // Free output sample + fft::free(d_output); } void @@ -52,20 +69,32 @@ namespace gr { d_buffer = NULL; } + // Free the taps if already allocated + if(d_aligned_taps != NULL) { + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } + + d_buffer = fft::malloc_float(d_ntaps); + d_ntaps = (int)taps.size(); - d_taps = fft::malloc_float(d_ntaps); + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); + + // Allocate aligned taps + d_aligned_taps = fft::malloc_float(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; + d_aligned_taps[i] = d_taps[i]; } + d_idx = 0; } std::vector fir_filter_with_buffer_fff::taps() const { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); return t; } @@ -79,11 +108,10 @@ namespace gr { if(d_idx >= ntaps()) d_idx = 0; - float output = 0; - volk_32f_x2_dot_prod_32f_u(&output, &d_buffer[d_idx], - d_taps, d_ntaps); - - return output; + volk_32f_x2_dot_prod_32f_a(d_output, d_buffer, + d_aligned_taps, + ntaps()); + return *d_output; } float @@ -100,10 +128,10 @@ namespace gr { d_idx = 0; } - float output = 0; - volk_32f_x2_dot_prod_32f_u(&output, &d_buffer[d_idx], - d_taps, d_ntaps); - return output; + volk_32f_x2_dot_prod_32f_a(d_output, d_buffer, + d_aligned_taps, + ntaps()); + return *d_output; } void @@ -135,14 +163,30 @@ namespace gr { fir_filter_with_buffer_ccc::fir_filter_with_buffer_ccc(const std::vector &taps) { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(gr_complex); + d_buffer = NULL; + d_aligned_taps = NULL; set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_complex(1); } fir_filter_with_buffer_ccc::~fir_filter_with_buffer_ccc() { - if(d_buffer != NULL) + if(d_buffer != NULL) { fft::free(d_buffer); + d_buffer = NULL; + } + + // Free aligned taps + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + + // Free output sample + fft::free(d_output); } void @@ -153,20 +197,32 @@ namespace gr { d_buffer = NULL; } + // Free the taps if already allocated + if(d_aligned_taps != NULL) { + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } + + d_buffer = fft::malloc_complex(d_ntaps); + d_ntaps = (int)taps.size(); - d_taps = fft::malloc_complex(d_ntaps); + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); + + // Allocate aligned taps + d_aligned_taps = fft::malloc_complex(d_ntaps); for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; + d_aligned_taps[i] = d_taps[i]; } + d_idx = 0; } std::vector fir_filter_with_buffer_ccc::taps() const { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); return t; } @@ -180,11 +236,10 @@ namespace gr { if(d_idx >= ntaps()) d_idx = 0; - gr_complex output = 0; - volk_32fc_x2_dot_prod_32fc_u(&output, &d_buffer[d_idx], - d_taps, d_ntaps); - - return output; + volk_32fc_x2_dot_prod_32fc_a(d_output, d_buffer, + d_aligned_taps, + ntaps()); + return *d_output; } gr_complex @@ -201,10 +256,10 @@ namespace gr { d_idx = 0; } - gr_complex output = 0; - volk_32fc_x2_dot_prod_32fc_u(&output, &d_buffer[d_idx], - d_taps, d_ntaps); - return output; + volk_32fc_x2_dot_prod_32fc_a(d_output, d_buffer, + d_aligned_taps, + ntaps()); + return *d_output; } void @@ -230,6 +285,134 @@ namespace gr { } } + + /**************************************************************/ + + + fir_filter_with_buffer_ccf::fir_filter_with_buffer_ccf(const std::vector &taps) + { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(gr_complex); + + d_buffer = NULL; + d_aligned_taps = NULL; + set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_complex(1); + } + + fir_filter_with_buffer_ccf::~fir_filter_with_buffer_ccf() + { + if(d_buffer != NULL) { + fft::free(d_buffer); + d_buffer = NULL; + } + + // Free aligned taps + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + + // Free output sample + fft::free(d_output); + } + + void + fir_filter_with_buffer_ccf::set_taps(const std::vector &taps) + { + if(d_buffer != NULL) { + fft::free(d_buffer); + d_buffer = NULL; + } + + // Free the taps if already allocated + if(d_aligned_taps != NULL) { + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } + + d_buffer = fft::malloc_complex(d_ntaps); + + d_ntaps = (int)taps.size(); + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); + + // Allocate aligned taps + d_aligned_taps = fft::malloc_float(d_ntaps); + for(unsigned int i = 0; i < d_ntaps; i++) { + d_aligned_taps[i] = d_taps[i]; + } + + d_idx = 0; + } + + std::vector + fir_filter_with_buffer_ccf::taps() const + { + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); + return t; + } + + gr_complex + fir_filter_with_buffer_ccf::filter(gr_complex input) + { + d_buffer[d_idx] = input; + d_buffer[d_idx+ntaps()] = input; + + d_idx++; + if(d_idx >= ntaps()) + d_idx = 0; + + volk_32fc_32f_dot_prod_32fc_a(d_output, d_buffer, + d_aligned_taps, + ntaps()); + return *d_output; + } + + gr_complex + fir_filter_with_buffer_ccf::filter(const gr_complex input[], + unsigned long dec) + { + unsigned int i; + + for(i = 0; i < dec; i++) { + d_buffer[d_idx] = input[i]; + d_buffer[d_idx+ntaps()] = input[i]; + d_idx++; + if(d_idx >= ntaps()) + d_idx = 0; + } + + volk_32fc_32f_dot_prod_32fc_a(d_output, d_buffer, + d_aligned_taps, + ntaps()); + return *d_output; + } + + void + fir_filter_with_buffer_ccf::filterN(gr_complex output[], + const gr_complex input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(input[i]); + } + } + + void + fir_filter_with_buffer_ccf::filterNdec(gr_complex output[], + const gr_complex input[], + unsigned long n, + unsigned long decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++) { + output[i] = filter(&input[j], decimate); + j += decimate; + } + } + } /* namespace kernel */ } /* namespace filter */ -- cgit From 1e7aae3678b0ce08e71b444225aca794f490ffaf Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 11:53:46 -0400 Subject: filter: minor coding things. --- gr-filter/lib/pfb_channelizer_ccf_impl.cc | 6 +++--- gr-filter/lib/polyphase_filterbank.cc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.cc b/gr-filter/lib/pfb_channelizer_ccf_impl.cc index 4e06cbc5f..ca92066f5 100644 --- a/gr-filter/lib/pfb_channelizer_ccf_impl.cc +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.cc @@ -81,7 +81,7 @@ namespace gr { d_output_multiple++; set_output_multiple(d_output_multiple); - set_history (d_taps_per_filter+1); + set_history(d_taps_per_filter+1); } pfb_channelizer_ccf_impl::~pfb_channelizer_ccf_impl() @@ -95,7 +95,7 @@ namespace gr { gruel::scoped_lock guard(d_mutex); polyphase_filterbank::set_taps(taps); - set_history (d_taps_per_filter+1); + set_history(d_taps_per_filter+1); d_updated = true; } @@ -143,7 +143,7 @@ namespace gr { gr_complex *in = (gr_complex *) input_items[0]; gr_complex *out = (gr_complex *) output_items[0]; - if (d_updated) { + if(d_updated) { d_updated = false; return 0; // history requirements may have changed. } diff --git a/gr-filter/lib/polyphase_filterbank.cc b/gr-filter/lib/polyphase_filterbank.cc index 9d881d76e..046c23fc0 100644 --- a/gr-filter/lib/polyphase_filterbank.cc +++ b/gr-filter/lib/polyphase_filterbank.cc @@ -59,7 +59,7 @@ namespace gr { } void - polyphase_filterbank::set_taps (const std::vector &taps) + polyphase_filterbank::set_taps(const std::vector &taps) { unsigned int i,j; -- cgit From 05c117f359b831513bbf6c4f43dca9cb181e5920 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 11:53:58 -0400 Subject: filter: updating adaptive FIR filters. No need for our own adaptive_fir class; can do everything with fir_filter. With QA code. --- gr-filter/include/filter/CMakeLists.txt | 1 - gr-filter/include/filter/adaptive_fir.h | 98 --------------- gr-filter/include/filter/adaptive_fir_ccc.h | 6 +- gr-filter/include/filter/adaptive_fir_ccf.h | 5 +- gr-filter/lib/CMakeLists.txt | 1 - gr-filter/lib/adaptive_fir.cc | 185 ---------------------------- gr-filter/lib/adaptive_fir_ccc_impl.cc | 18 ++- gr-filter/lib/adaptive_fir_ccc_impl.h | 5 +- gr-filter/lib/adaptive_fir_ccf_impl.cc | 18 ++- gr-filter/lib/adaptive_fir_ccf_impl.h | 5 +- gr-filter/python/qa_adaptive_fir_filter.py | 159 ++++++++++++++++++++++++ gr-filter/swig/filter_swig.i | 6 + 12 files changed, 208 insertions(+), 299 deletions(-) delete mode 100644 gr-filter/include/filter/adaptive_fir.h delete mode 100644 gr-filter/lib/adaptive_fir.cc create mode 100755 gr-filter/python/qa_adaptive_fir_filter.py diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 2620d3f54..24e63dc0c 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -75,7 +75,6 @@ add_custom_target(filter_generated_includes DEPENDS ######################################################################## install(FILES api.h - adaptive_fir.h firdes.h fir_filter.h fir_filter_with_buffer.h diff --git a/gr-filter/include/filter/adaptive_fir.h b/gr-filter/include/filter/adaptive_fir.h deleted file mode 100644 index 75a5f57db..000000000 --- a/gr-filter/include/filter/adaptive_fir.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2011,2012 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef INCLUDED_FILTER_ADAPTIVE_FIR_H -#define INCLUDED_FILTER_ADAPTIVE_FIR_H - -#include -#include -#include - -namespace gr { - namespace filter { - namespace kernel { - - class FILTER_API adaptive_fir_ccc - { - protected: - int d_decim; - unsigned int d_ntaps; - gr_complex d_error; - gr_complex *d_taps; - - // Override to calculate error signal per output - virtual gr_complex error(const gr_complex &out) = 0; - - // Override to calculate new weight from old, corresponding input - virtual void update_tap(gr_complex &tap, const gr_complex &in) = 0; - - public: - adaptive_fir_ccc(int decimation, - const std::vector &taps); - - void set_taps(const std::vector &taps); - std::vector taps() const; - - int decimation() const; - unsigned int ntaps() const; - - gr_complex filter(gr_complex *input); - void filterN(gr_complex *out, gr_complex *in, int nitems); - }; - - - /**************************************************************/ - - - class FILTER_API adaptive_fir_ccf - { - protected: - int d_decim; - unsigned int d_ntaps; - float d_error; - gr_complex *d_taps; - - // Override to calculate error signal per output - virtual float error(const gr_complex &out) = 0; - - // Override to calculate new weight from old, corresponding input - virtual void update_tap(gr_complex &tap, const gr_complex &in) = 0; - - public: - adaptive_fir_ccf(int decimation, - const std::vector &taps); - - void set_taps(const std::vector &taps); - std::vector taps() const; - - int decimation() const; - unsigned int ntaps() const; - - gr_complex filter(gr_complex *input); - void filterN(gr_complex *out, gr_complex *in, int nitems); - }; - - } /* namespace kernel */ - } /* namespace filter */ -} /* namespace gr */ - -#endif /* INCLUDED_FILTER_ADAPTIVE_FIR_H */ diff --git a/gr-filter/include/filter/adaptive_fir_ccc.h b/gr-filter/include/filter/adaptive_fir_ccc.h index 14e9f6f53..b30cd353c 100644 --- a/gr-filter/include/filter/adaptive_fir_ccc.h +++ b/gr-filter/include/filter/adaptive_fir_ccc.h @@ -24,8 +24,8 @@ #define INCLUDED_FILTER_ADAPTIVE_FIR_CCC_H #include -#include #include +#include namespace gr { namespace filter { @@ -43,8 +43,8 @@ namespace gr { static FILTER_API sptr make(const char *name, int decimation, const std::vector &taps); - void set_taps(const std::vector &taps); - std::vector taps(); + virtual void set_taps(const std::vector &taps) = 0; + virtual std::vector taps() const = 0; }; } /* namespace filter */ diff --git a/gr-filter/include/filter/adaptive_fir_ccf.h b/gr-filter/include/filter/adaptive_fir_ccf.h index 8eb01c877..0503acd4e 100644 --- a/gr-filter/include/filter/adaptive_fir_ccf.h +++ b/gr-filter/include/filter/adaptive_fir_ccf.h @@ -24,7 +24,6 @@ #define INCLUDED_FILTER_ADAPTIVE_FIR_CCF_H #include -#include #include namespace gr { @@ -43,8 +42,8 @@ namespace gr { static FILTER_API sptr make(const char *name, int decimation, const std::vector &taps); - void set_taps(const std::vector &taps); - std::vector taps(); + virtual void set_taps(const std::vector &taps) = 0; + virtual std::vector taps() = 0; }; } /* namespace filter */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index f5dbd1bb3..7a6394b57 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -105,7 +105,6 @@ link_directories(${FFTW3F_LIBRARY_DIRS}) # Setup library ######################################################################## list(APPEND filter_sources - adaptive_fir.cc fir_filter.cc fir_filter_with_buffer.cc fft_filter.cc diff --git a/gr-filter/lib/adaptive_fir.cc b/gr-filter/lib/adaptive_fir.cc deleted file mode 100644 index 9098e86ca..000000000 --- a/gr-filter/lib/adaptive_fir.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2012 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -namespace gr { - namespace filter { - namespace kernel { - - adaptive_fir_ccc::adaptive_fir_ccc(int decimation, - const std::vector &taps) - { - d_taps = NULL; - d_decim = decimation; - set_taps(taps); - } - - void - adaptive_fir_ccc::set_taps(const std::vector &taps) - { - // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - - d_ntaps = (int)taps.size(); - d_taps = fft::malloc_complex(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; - } - } - - std::vector - adaptive_fir_ccc::taps() const - { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1]); - return t; - } - - int - adaptive_fir_ccc::decimation() const - { - return d_decim; - } - - unsigned int - adaptive_fir_ccc::ntaps() const - { - return d_ntaps; - } - - gr_complex - adaptive_fir_ccc::filter(gr_complex *input) - { - gr_complex output; - volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); - return output; - } - - void - adaptive_fir_ccc::filterN(gr_complex *out, gr_complex *in, - int nitems) - { - int j = 0; - unsigned int k; - for(int i = 0; i < nitems; i++) { - out[i] = filter(&in[j]); - - // Adjust taps - d_error = error(out[i]); - for(k = 0; k < d_ntaps; k++) { - update_tap(d_taps[d_ntaps-k-1], in[j+k]); - } - - j += decimation(); - } - } - - - /**************************************************************/ - - - adaptive_fir_ccf::adaptive_fir_ccf(int decimation, - const std::vector &taps) - { - d_taps = NULL; - d_decim = decimation; - set_taps(taps); - } - - void - adaptive_fir_ccf::set_taps(const std::vector &taps) - { - // Free the taps if already allocated - if(d_taps != NULL) { - fft::free(d_taps); - d_taps = NULL; - } - - d_ntaps = (int)taps.size(); - d_taps = fft::malloc_complex(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_taps[d_ntaps-i-1] = taps[i]; - } - } - - std::vector - adaptive_fir_ccf::taps() const - { - std::vector t; - for(unsigned int i = 0; i < d_ntaps; i++) - t.push_back(d_taps[d_ntaps-i-1].real()); - return t; - } - - int - adaptive_fir_ccf::decimation() const - { - return d_decim; - } - - unsigned int - adaptive_fir_ccf::ntaps() const - { - return d_ntaps; - } - - gr_complex - adaptive_fir_ccf::filter(gr_complex *input) - { - gr_complex output; - volk_32fc_x2_dot_prod_32fc_u(&output, input, d_taps, d_ntaps); - return output; - } - - void - adaptive_fir_ccf::filterN(gr_complex *out, gr_complex *in, - int nitems) - { - int j = 0; - unsigned int k; - for(int i = 0; i < nitems; i++) { - out[i] = filter(&in[j]); - - // Adjust taps - d_error = error(out[i]); - for(k = 0; k < d_ntaps; k++) { - update_tap(d_taps[d_ntaps-k-1], in[j+k]); - } - - j += decimation(); - } - } - - } /* namespace kernel */ - } /* namespace filter */ -} /* namespace gr */ diff --git a/gr-filter/lib/adaptive_fir_ccc_impl.cc b/gr-filter/lib/adaptive_fir_ccc_impl.cc index 06736ae6f..515ef90cd 100644 --- a/gr-filter/lib/adaptive_fir_ccc_impl.cc +++ b/gr-filter/lib/adaptive_fir_ccc_impl.cc @@ -43,7 +43,7 @@ namespace gr { gr_make_io_signature(1, 1, sizeof(gr_complex)), gr_make_io_signature(1, 1, sizeof(gr_complex)), decimation), - kernel::adaptive_fir_ccc(decimation, taps), + kernel::fir_filter_ccc(decimation, taps), d_updated(false) { set_history(d_ntaps); @@ -56,6 +56,12 @@ namespace gr { d_updated = true; } + std::vector + adaptive_fir_ccc_impl::taps() const + { + return kernel::fir_filter_ccc::taps(); + } + gr_complex adaptive_fir_ccc_impl::error(const gr_complex &out) { @@ -77,7 +83,7 @@ namespace gr { gr_complex *out = (gr_complex *)output_items[0]; if (d_updated) { - kernel::adaptive_fir_ccc::set_taps(d_new_taps); + kernel::fir_filter_ccc::set_taps(d_new_taps); set_history(d_ntaps); d_updated = false; return 0; // history requirements may have changed. @@ -85,7 +91,13 @@ namespace gr { // Call base class filtering function that uses // overloaded error and update_tap functions. - filterN(out, in, noutput_items); + if (decimation() == 1) { + filterN(out, in, noutput_items); + } + else { + filterNdec(out, in, noutput_items, + decimation()); + } return noutput_items; } diff --git a/gr-filter/lib/adaptive_fir_ccc_impl.h b/gr-filter/lib/adaptive_fir_ccc_impl.h index f145ceeaa..fd6274a1d 100644 --- a/gr-filter/lib/adaptive_fir_ccc_impl.h +++ b/gr-filter/lib/adaptive_fir_ccc_impl.h @@ -24,17 +24,19 @@ #define INCLUDED_FILTER_ADAPTIVE_FIR_CCC_IMPL_H #include +#include #include namespace gr { namespace filter { - class FILTER_API adaptive_fir_ccc_impl : public adaptive_fir_ccc, public kernel::adaptive_fir_ccc + class FILTER_API adaptive_fir_ccc_impl : public adaptive_fir_ccc, public kernel::fir_filter_ccc { private: std::vector d_new_taps; bool d_updated; + protected: // Override to calculate error signal per output gr_complex error(const gr_complex &out); @@ -43,6 +45,7 @@ namespace gr { public: void set_taps(const std::vector &taps); + std::vector taps() const; adaptive_fir_ccc_impl(const char *name, int decimation, const std::vector &taps); diff --git a/gr-filter/lib/adaptive_fir_ccf_impl.cc b/gr-filter/lib/adaptive_fir_ccf_impl.cc index 053facdc2..62d7e5337 100644 --- a/gr-filter/lib/adaptive_fir_ccf_impl.cc +++ b/gr-filter/lib/adaptive_fir_ccf_impl.cc @@ -43,7 +43,7 @@ namespace gr { gr_make_io_signature(1, 1, sizeof(gr_complex)), gr_make_io_signature(1, 1, sizeof(gr_complex)), decimation), - kernel::adaptive_fir_ccf(decimation, taps), + kernel::fir_filter_ccf(decimation, taps), d_updated(false) { set_history(d_ntaps); @@ -56,6 +56,12 @@ namespace gr { d_updated = true; } + std::vector + adaptive_fir_ccf_impl::taps() + { + return kernel::fir_filter_ccf::taps(); + } + float adaptive_fir_ccf_impl::error(const gr_complex &out) { @@ -77,7 +83,7 @@ namespace gr { gr_complex *out = (gr_complex *)output_items[0]; if (d_updated) { - kernel::adaptive_fir_ccf::set_taps(d_new_taps); + kernel::fir_filter_ccf::set_taps(d_new_taps); set_history(d_ntaps); d_updated = false; return 0; // history requirements may have changed. @@ -85,7 +91,13 @@ namespace gr { // Call base class filtering function that uses // overloaded error and update_tap functions. - filterN(out, in, noutput_items); + if (decimation() == 1) { + filterN(out, in, noutput_items); + } + else { + filterNdec(out, in, noutput_items, + decimation()); + } return noutput_items; } diff --git a/gr-filter/lib/adaptive_fir_ccf_impl.h b/gr-filter/lib/adaptive_fir_ccf_impl.h index fa9a42189..2a1c7e5e9 100644 --- a/gr-filter/lib/adaptive_fir_ccf_impl.h +++ b/gr-filter/lib/adaptive_fir_ccf_impl.h @@ -24,17 +24,19 @@ #define INCLUDED_FILTER_ADAPTIVE_FIR_CCF_IMPL_H #include +#include #include namespace gr { namespace filter { - class FILTER_API adaptive_fir_ccf_impl : public adaptive_fir_ccf, public kernel::adaptive_fir_ccf + class FILTER_API adaptive_fir_ccf_impl : public adaptive_fir_ccf, public kernel::fir_filter_ccf { private: std::vector d_new_taps; bool d_updated; + protected: // Override to calculate error signal per output float error(const gr_complex &out); @@ -43,6 +45,7 @@ namespace gr { public: void set_taps(const std::vector &taps); + std::vector taps(); adaptive_fir_ccf_impl(const char *name, int decimation, const std::vector &taps); diff --git a/gr-filter/python/qa_adaptive_fir_filter.py b/gr-filter/python/qa_adaptive_fir_filter.py new file mode 100755 index 000000000..cadce5204 --- /dev/null +++ b/gr-filter/python/qa_adaptive_fir_filter.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# +# Copyright 2008,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter + +class test_adaptive_filter(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block () + + def tearDown(self): + self.tb = None + + def test_adaptive_fir_filter_ccf_001(self): + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((0.5+0.5j), (1.5+1.5j), (3+3j), (5+5j), (5.5+5.5j), + (6.5+6.5j), (8+8j), (10+10j), (10.5+10.5j), (11.5+11.5j), + (13+13j), (15+15j), (15.5+15.5j), (16.5+16.5j), (18+18j), + (20+20j), (20.5+20.5j), (21.5+21.5j), (23+23j), (25+25j), + (25.5+25.5j), (26.5+26.5j), (28+28j), (30+30j), (30.5+30.5j), + (31.5+31.5j), (33+33j), (35+35j), (35.5+35.5j), (36.5+36.5j), + (38+38j), (40+40j), (40.5+40.5j), (41.5+41.5j), (43+43j), + (45+45j), (45.5+45.5j), (46.5+46.5j), (48+48j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j)) + + src = gr.vector_source_c(src_data) + op = filter.adaptive_fir_ccf("test", 1, 20*[0.5, 0.5]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + def test_adaptive_fir_filter_ccf_002(self): + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((0.5+0.5j), (5.5+5.5j), (10.5+10.5j), (15.5+15.5j), + (20.5+20.5j), (25.5+25.5j), (30.5+30.5j), (35.5+35.5j), + (40.5+40.5j), (45.5+45.5j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j), (50+50j), + (50+50j), (50+50j), (50+50j), (50+50j)) + + src = gr.vector_source_c(src_data) + op = filter.adaptive_fir_ccf("test", 4, 20*[0.5, 0.5]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + def test_adaptive_fir_filter_ccc_001(self): + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((-0.5+1.5j), (-1.5+4.5j), (-3+9j), (-5+15j), + (-5.5+16.5j), (-6.5+19.5j), (-8+24j), (-10+30j), + (-10.5+31.5j), (-11.5+34.5j), (-13+39j), (-15+45j), + (-15.5+46.5j), (-16.5+49.5j), (-18+54j), (-20+60j), + (-20.5+61.5j), (-21.5+64.5j), (-23+69j), (-25+75j), + (-25.5+76.5j), (-26.5+79.5j), (-28+84j), (-30+90j), + (-30.5+91.5j), (-31.5+94.5j), (-33+99j), (-35+105j), + (-35.5+106.5j), (-36.5+109.5j), (-38+114j), (-40+120j), + (-40.5+121.5j), (-41.5+124.5j), (-43+129j), (-45+135j), + (-45.5+136.5j), (-46.5+139.5j), (-48+144j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), (-50+150j)) + src = gr.vector_source_c(src_data) + op = filter.adaptive_fir_ccc("test", 1, 20*[0.5+1j, 0.5+1j]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + + + def test_adaptive_fir_filter_ccc_002(self): + src_data = 40*[1+1j, 2+2j, 3+3j, 4+4j] + expected_data = ((-0.5+1.5j), (-5.5+16.5j), (-10.5+31.5j), + (-15.5+46.5j), (-20.5+61.5j), (-25.5+76.5j), + (-30.5+91.5j), (-35.5+106.5j), (-40.5+121.5j), + (-45.5+136.5j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j), (-50+150j), + (-50+150j), (-50+150j), (-50+150j)) + src = gr.vector_source_c(src_data) + op = filter.adaptive_fir_ccc("test", 4, 20*[0.5+1j, 0.5+1j]) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data, 5) + +if __name__ == '__main__': + gr_unittest.run(test_adaptive_filter, "test_adaptive_filter.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index c9de3fb9a..1bcafeb8c 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -30,6 +30,8 @@ %{ #include "filter/firdes.h" #include "filter/pm_remez.h" +#include "filter/adaptive_fir_ccc.h" +#include "filter/adaptive_fir_ccf.h" #include "filter/dc_blocker_cc.h" #include "filter/dc_blocker_ff.h" #include "filter/filter_delay_fc.h" @@ -46,6 +48,8 @@ %include "filter/firdes.h" %include "filter/pm_remez.h" +%include "filter/adaptive_fir_ccc.h" +%include "filter/adaptive_fir_ccf.h" %include "filter/dc_blocker_cc.h" %include "filter/dc_blocker_ff.h" %include "filter/filter_delay_fc.h" @@ -59,6 +63,8 @@ %include "filter/hilbert_fc.h" %include "filter/pfb_channelizer_ccf.h" +GR_SWIG_BLOCK_MAGIC2(filter, adaptive_fir_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, adaptive_fir_ccf); GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_cc); GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_ff); GR_SWIG_BLOCK_MAGIC2(filter, filter_delay_fc); -- cgit From 76a6a4c668579ff2e17f8fa44ceb0db381b9053f Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 15:58:28 -0400 Subject: filter: adding single_pole_iir_filter blocks. QA, docs, and GRC. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/single_pole_iir_filter_xx.xml | 51 ++++++ gr-filter/include/filter/CMakeLists.txt | 3 + gr-filter/include/filter/single_pole_iir.h | 200 +++++++++++++++++++++ .../include/filter/single_pole_iir_filter_cc.h | 78 ++++++++ .../include/filter/single_pole_iir_filter_ff.h | 78 ++++++++ gr-filter/lib/CMakeLists.txt | 2 + gr-filter/lib/single_pole_iir_filter_cc_impl.cc | 89 +++++++++ gr-filter/lib/single_pole_iir_filter_cc_impl.h | 55 ++++++ gr-filter/lib/single_pole_iir_filter_ff_impl.cc | 87 +++++++++ gr-filter/lib/single_pole_iir_filter_ff_impl.h | 54 ++++++ gr-filter/python/qa_single_pole_iir.py | 114 ++++++++++++ gr-filter/swig/filter_swig.i | 6 + 14 files changed, 819 insertions(+) create mode 100644 gr-filter/grc/single_pole_iir_filter_xx.xml create mode 100644 gr-filter/include/filter/single_pole_iir.h create mode 100644 gr-filter/include/filter/single_pole_iir_filter_cc.h create mode 100644 gr-filter/include/filter/single_pole_iir_filter_ff.h create mode 100644 gr-filter/lib/single_pole_iir_filter_cc_impl.cc create mode 100644 gr-filter/lib/single_pole_iir_filter_cc_impl.h create mode 100644 gr-filter/lib/single_pole_iir_filter_ff_impl.cc create mode 100644 gr-filter/lib/single_pole_iir_filter_ff_impl.h create mode 100755 gr-filter/python/qa_single_pole_iir.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 663cc0b00..83036a0b3 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -25,6 +25,7 @@ install(FILES filter_delay_fc.xml hilbert_fc.xml pfb_channelizer.xml + single_pole_iir_filter_xx.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 988897fcf..6711e809f 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -36,5 +36,6 @@ filter_delay_fc hilbert_fc pfb_channelizer_ccf + single_pole_iir_filter_xx diff --git a/gr-filter/grc/single_pole_iir_filter_xx.xml b/gr-filter/grc/single_pole_iir_filter_xx.xml new file mode 100644 index 000000000..3eaf52625 --- /dev/null +++ b/gr-filter/grc/single_pole_iir_filter_xx.xml @@ -0,0 +1,51 @@ + + + + Single Pole IIR Filter + single_pole_iir_filter_xx + from gnuradio import filter + filter.single_pole_iir_filter_$(type.fcn)($alpha, $vlen) + set_taps($alpha) + + Type + type + enum + + + + + Alpha + alpha + 1.0 + real + + + Vec Length + vlen + 1 + int + + $vlen > 0 + + in + $type + $vlen + + + out + $type + $vlen + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 24e63dc0c..49e946536 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -81,6 +81,7 @@ install(FILES fft_filter.h pm_remez.h polyphase_filterbank.h + single_pole_iir.h ${generated_includes} adaptive_fir_ccc.h adaptive_fir_ccf.h @@ -91,6 +92,8 @@ install(FILES fft_filter_fff.h hilbert_fc.h pfb_channelizer_ccf.h + single_pole_iir_filter_cc.h + single_pole_iir_filter_ff.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "fft_devel" ) diff --git a/gr-filter/include/filter/single_pole_iir.h b/gr-filter/include/filter/single_pole_iir.h new file mode 100644 index 000000000..10d1b4791 --- /dev/null +++ b/gr-filter/include/filter/single_pole_iir.h @@ -0,0 +1,200 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2006,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_SINGLE_POLE_IIR_H +#define INCLUDED_SINGLE_POLE_IIR_H + +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief class template for single pole IIR filter + */ + template + class single_pole_iir + { + public: + /*! + * \brief construct new single pole IIR with given alpha + * + * computes y(i) = (1-alpha) * y(i-1) + alpha * x(i) + */ + single_pole_iir(tap_type alpha = 1.0) + { + d_prev_output = 0; + set_taps(alpha); + } + + /*! + * \brief compute a single output value. + * \returns the filtered input value. + */ + o_type filter(const i_type input); + + /*! + * \brief compute an array of N output values. + * \p input must have n valid entries. + */ + void filterN(o_type output[], const i_type input[], unsigned long n); + + /*! + * \brief install \p alpha as the current taps. + */ + void set_taps(tap_type alpha) + { + if(alpha < 0 || alpha > 1) + throw std::out_of_range("Alpha must be in [0, 1]\n"); + + d_alpha = alpha; + d_one_minus_alpha = 1.0 - alpha; + } + + //! reset state to zero + void reset() + { + d_prev_output = 0; + } + + o_type prev_output() const { return d_prev_output; } + + protected: + tap_type d_alpha; + tap_type d_one_minus_alpha; + o_type d_prev_output; + }; + + // + // general case. We may want to specialize this + // + template + o_type + single_pole_iir::filter(const i_type input) + { + o_type output; + + output = d_alpha * input + d_one_minus_alpha * d_prev_output; + d_prev_output = output; + + return (o_type) output; + } + + + template + void + single_pole_iir::filterN(o_type output[], + const i_type input[], + unsigned long n) + { + for(unsigned i = 0; i < n; i++) + output[i] = filter(input[i]); + } + + + // + // Specialized case for gr_complex output and double taps + // We need to have a gr_complexd type for the calculations and prev_output variable (in stead of double) + + template + class single_pole_iir + { + public: + /*! + * \brief construct new single pole IIR with given alpha + * + * computes y(i) = (1-alpha) * y(i-1) + alpha * x(i) + */ + single_pole_iir(double alpha = 1.0) + { + d_prev_output = 0; + set_taps(alpha); + } + + /*! + * \brief compute a single output value. + * \returns the filtered input value. + */ + gr_complex filter(const i_type input); + + /*! + * \brief compute an array of N output values. + * \p input must have n valid entries. + */ + void filterN(gr_complex output[], const i_type input[], unsigned long n); + + /*! + * \brief install \p alpha as the current taps. + */ + void set_taps(double alpha) + { + if(alpha < 0 || alpha > 1) + throw std::out_of_range("Alpha must be in [0, 1]\n"); + + d_alpha = alpha; + d_one_minus_alpha = 1.0 - alpha; + } + + //! reset state to zero + void reset() + { + d_prev_output = 0; + } + + gr_complexd prev_output() const { return d_prev_output; } + + protected: + double d_alpha; + double d_one_minus_alpha; + gr_complexd d_prev_output; + }; + + template< class i_type> + gr_complex + single_pole_iir::filter(const i_type input) + { + gr_complexd output; + + output = d_alpha * (gr_complexd)input + d_one_minus_alpha * d_prev_output; + d_prev_output = output; + + return (gr_complex) output; + } + + //Do we need to specialize this, although it is the same as the general case? + + template + void + single_pole_iir::filterN(gr_complex output[], + const i_type input[], + unsigned long n) + { + for(unsigned i = 0; i < n; i++) + output[i] = filter(input[i]); + } + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_SINGLE_POLE_IIR_H */ diff --git a/gr-filter/include/filter/single_pole_iir_filter_cc.h b/gr-filter/include/filter/single_pole_iir_filter_cc.h new file mode 100644 index 000000000..37db7c65f --- /dev/null +++ b/gr-filter/include/filter/single_pole_iir_filter_cc.h @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004-2006,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_SINGLE_POLE_IIR_FILTER_CC_H +#define INCLUDED_SINGLE_POLE_IIR_FILTER_CC_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief single pole IIR filter with complex input, complex output + * \ingroup filter_blk + * + * The input and output satisfy a difference equation of the form + \htmlonly + \f{ + y[n] - (1-alpha) y[n-1] = alpha x[n] + \f} + \endhtmlonly + + \xmlonly + y[n] - (1-alpha) y[n-1] = alpha x[n] + \endxmlonly + + * with the corresponding rational system function + \htmlonly + \f{ + H(z) = \frac{alpha}{1 - (1-alpha) z^{-1}} + \f} + \endhtmlonly + + \xmlonly + H(z) = \ frac{alpha}{1 - (1-alpha) z^{-1}} + \endxmlonly + + * Note that some texts define the system function with a + in the + * denominator. If you're using that convention, you'll need to + * negate the feedback tap. + */ + class FILTER_API single_pole_iir_filter_cc : virtual public gr_sync_block + { + public: + // gr::filter::single_pole_iir_filter_cc::sptr + typedef boost::shared_ptr sptr; + + static FILTER_API sptr make(double alpha, unsigned int vlen=1); + + virtual void set_taps(double alpha) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_SINGLE_POLE_IIR_FILTER_CC_H */ diff --git a/gr-filter/include/filter/single_pole_iir_filter_ff.h b/gr-filter/include/filter/single_pole_iir_filter_ff.h new file mode 100644 index 000000000..8abce94c9 --- /dev/null +++ b/gr-filter/include/filter/single_pole_iir_filter_ff.h @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_SINGLE_POLE_IIR_FILTER_FF_H +#define INCLUDED_SINGLE_POLE_IIR_FILTER_FF_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief single pole IIR filter with float input, float output + * \ingroup filter_blk + * + * The input and output satisfy a difference equation of the form + \htmlonly + \f{ + y[n] - (1-alpha) y[n-1] = alpha x[n] + \f} + \endhtmlonly + + \xmlonly + y[n] - (1-alpha) y[n-1] = alpha x[n] + \endxmlonly + + * with the corresponding rational system function + \htmlonly + \f{ + H(z) = \frac{alpha}{1 - (1-alpha) z^{-1}} + \f} + \endhtmlonly + + \xmlonly + H(z) = \ frac{alpha}{1 - (1-alpha) z^{-1}} + \endxmlonly + + * Note that some texts define the system function with a + in the + * denominator. If you're using that convention, you'll need to + * negate the feedback tap. + */ + class FILTER_API single_pole_iir_filter_ff : virtual public gr_sync_block + { + public: + // gr::filter::single_pole_iir_filter_ff::sptr + typedef boost::shared_ptr sptr; + + static FILTER_API sptr make(double alpha, unsigned int vlen=1); + + virtual void set_taps (double alpha) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_SINGLE_POLE_IIR_FILTER_FF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 7a6394b57..d661d01b6 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -121,6 +121,8 @@ list(APPEND filter_sources fft_filter_fff_impl.cc hilbert_fc_impl.cc pfb_channelizer_ccf_impl.cc + single_pole_iir_filter_cc_impl.cc + single_pole_iir_filter_ff_impl.cc ) list(APPEND filter_libs diff --git a/gr-filter/lib/single_pole_iir_filter_cc_impl.cc b/gr-filter/lib/single_pole_iir_filter_cc_impl.cc new file mode 100644 index 000000000..9406f2c98 --- /dev/null +++ b/gr-filter/lib/single_pole_iir_filter_cc_impl.cc @@ -0,0 +1,89 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "single_pole_iir_filter_cc_impl.h" +#include + + +namespace gr { + namespace filter { + + single_pole_iir_filter_cc::sptr + single_pole_iir_filter_cc::make(double alpha, unsigned int vlen) + { + return gnuradio::get_initial_sptr + (new single_pole_iir_filter_cc_impl(alpha, vlen)); + } + + single_pole_iir_filter_cc_impl::single_pole_iir_filter_cc_impl + (double alpha, unsigned int vlen) + : gr_sync_block("single_pole_iir_filter_cc", + gr_make_io_signature(1, 1, sizeof (gr_complex) * vlen), + gr_make_io_signature(1, 1, sizeof (gr_complex) * vlen)), + d_vlen(vlen), d_iir(vlen) + { + set_taps(alpha); + } + + single_pole_iir_filter_cc_impl::~single_pole_iir_filter_cc_impl() + { + } + + void + single_pole_iir_filter_cc_impl::set_taps(double alpha) + { + for(unsigned int i = 0; i < d_vlen; i++) { + d_iir[i].set_taps(alpha); + } + } + + int + single_pole_iir_filter_cc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + unsigned int vlen = d_vlen; + + if(d_vlen == 1) { + for(int i = 0; i < noutput_items; i++) { + out[i] = d_iir[0].filter(in[i]); + } + } + else { + for(int i = 0; i < noutput_items; i++) { + for(unsigned int j = 0; j < vlen; j++) { + *out++ = d_iir[j].filter(*in++); + } + } + } + + return noutput_items; + }; + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/single_pole_iir_filter_cc_impl.h b/gr-filter/lib/single_pole_iir_filter_cc_impl.h new file mode 100644 index 000000000..fa627881e --- /dev/null +++ b/gr-filter/lib/single_pole_iir_filter_cc_impl.h @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004-2006,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_SINGLE_POLE_IIR_FILTER_CC_IMPL_H +#define INCLUDED_SINGLE_POLE_IIR_FILTER_CC_IMPL_H + +#include +#include +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API single_pole_iir_filter_cc_impl : public single_pole_iir_filter_cc + { + private: + unsigned int d_vlen; + std::vector > d_iir; + + public: + single_pole_iir_filter_cc_impl(double alpha, unsigned int vlen); + ~single_pole_iir_filter_cc_impl(); + + void set_taps(double alpha); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_SINGLE_POLE_IIR_FILTER_CC_IMPL_H */ diff --git a/gr-filter/lib/single_pole_iir_filter_ff_impl.cc b/gr-filter/lib/single_pole_iir_filter_ff_impl.cc new file mode 100644 index 000000000..7e2bae5db --- /dev/null +++ b/gr-filter/lib/single_pole_iir_filter_ff_impl.cc @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "single_pole_iir_filter_ff_impl.h" +#include + +namespace gr { + namespace filter { + + single_pole_iir_filter_ff::sptr + single_pole_iir_filter_ff::make(double alpha, unsigned int vlen) + { + return gnuradio::get_initial_sptr + (new single_pole_iir_filter_ff_impl(alpha, vlen)); + } + + single_pole_iir_filter_ff_impl::single_pole_iir_filter_ff_impl + (double alpha, unsigned int vlen) + : gr_sync_block("single_pole_iir_filter_ff", + gr_make_io_signature(1, 1, sizeof(float)*vlen), + gr_make_io_signature(1, 1, sizeof(float)*vlen)), + d_vlen(vlen), d_iir(vlen) + { + set_taps(alpha); + } + + single_pole_iir_filter_ff_impl::~single_pole_iir_filter_ff_impl() + { + } + + void + single_pole_iir_filter_ff_impl::set_taps(double alpha) + { + for(unsigned int i = 0; i < d_vlen; i++) { + d_iir[i].set_taps(alpha); + } + } + + int + single_pole_iir_filter_ff_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *in = (const float*)input_items[0]; + float *out = (float*)output_items[0]; + unsigned int vlen = d_vlen; + + if(d_vlen == 1) { + for(int i = 0; i < noutput_items; i++) { + out[i] = d_iir[0].filter (in[i]); + } + } + else { + for(int i = 0; i < noutput_items; i++) { + for(unsigned int j = 0; j < vlen; j++) { + *out++ = d_iir[j].filter(*in++); + } + } + } + return noutput_items; + }; + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/single_pole_iir_filter_ff_impl.h b/gr-filter/lib/single_pole_iir_filter_ff_impl.h new file mode 100644 index 000000000..cba9c188c --- /dev/null +++ b/gr-filter/lib/single_pole_iir_filter_ff_impl.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004-2006,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_SINGLE_POLE_IIR_FILTER_FF_IMPL_H +#define INCLUDED_SINGLE_POLE_IIR_FILTER_FF_IMPL_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API single_pole_iir_filter_ff_impl : public single_pole_iir_filter_ff + { + private: + unsigned int d_vlen; + std::vector > d_iir; + + public: + single_pole_iir_filter_ff_impl(double alpha, unsigned int vlen); + ~single_pole_iir_filter_ff_impl(); + + void set_taps(double alpha); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_SINGLE_POLE_IIR_FILTER_FF_IMPL_H */ diff --git a/gr-filter/python/qa_single_pole_iir.py b/gr-filter/python/qa_single_pole_iir.py new file mode 100755 index 000000000..3608c77f9 --- /dev/null +++ b/gr-filter/python/qa_single_pole_iir.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# +# Copyright 2005,2007,2010 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter + +class test_single_pole_iir_filter(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_ff_001(self): + src_data = (0, 1000, 2000, 3000, 4000, 5000) + expected_result = src_data + src = gr.vector_source_f(src_data) + op = filter.single_pole_iir_filter_ff(1.0) + dst = gr.vector_sink_f() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_ff_002(self): + src_data = (0, 1000, 2000, 3000, 4000, 5000) + expected_result = (0, 125, 359.375, 689.453125, 1103.271484, 1590.36255) + src = gr.vector_source_f(src_data) + op = filter.single_pole_iir_filter_ff(0.125) + dst = gr.vector_sink_f() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data, 3) + + def test_ff_003(self): + block_size = 2 + src_data = (0, 1000, 2000, 3000, 4000, 5000) + expected_result = (0, 125, 250, 484.375, 718.75, 1048.828125) + src = gr.vector_source_f(src_data) + s2p = gr.serial_to_parallel(gr.sizeof_float, block_size) + op = filter.single_pole_iir_filter_ff (0.125, block_size) + p2s = gr.parallel_to_serial(gr.sizeof_float, block_size) + dst = gr.vector_sink_f() + self.tb.connect(src, s2p, op, p2s, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data, 3) + + def test_cc_001(self): + src_data = (0+0j, 1000+1000j, 2000+2000j, 3000+3000j, 4000+4000j, 5000+5000j) + expected_result = src_data + src = gr.vector_source_c(src_data) + op = filter.single_pole_iir_filter_cc(1.0) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_result, result_data) + + def test_cc_002(self): + src_data = (complex(0,0), complex(1000,-1000), complex(2000,-2000), + complex(3000,-3000), complex(4000,-4000), complex(5000,-5000)) + expected_result = (complex(0,0), complex(125,-125), complex(359.375,-359.375), + complex(689.453125,-689.453125), complex(1103.271484,-1103.271484), + complex(1590.36255,-1590.36255)) + src = gr.vector_source_c(src_data) + op = filter.single_pole_iir_filter_cc(0.125) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_result, result_data, 3) + + def test_cc_003(self): + block_size = 2 + src_data = (complex(0,0), complex(1000,-1000), complex(2000,-2000), + complex(3000,-3000), complex(4000,-4000), complex(5000,-5000)) + expected_result = (complex(0,0), complex(125,-125), complex(250,-250), + complex(484.375,-484.375), complex(718.75,-718.75), + complex(1048.828125,-1048.828125)) + src = gr.vector_source_c(src_data) + s2p = gr.serial_to_parallel(gr.sizeof_gr_complex, block_size) + op = filter.single_pole_iir_filter_cc(0.125, block_size) + p2s = gr.parallel_to_serial(gr.sizeof_gr_complex, block_size) + dst = gr.vector_sink_c() + self.tb.connect(src, s2p, op, p2s, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_result, result_data, 3) + +if __name__ == '__main__': + gr_unittest.run(test_single_pole_iir_filter, "test_single_pole_iir_filter.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 1bcafeb8c..8585bda32 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -44,6 +44,8 @@ #include "filter/fft_filter_fff.h" #include "filter/hilbert_fc.h" #include "filter/pfb_channelizer_ccf.h" +#include "filter/single_pole_iir_filter_cc.h" +#include "filter/single_pole_iir_filter_ff.h" %} %include "filter/firdes.h" @@ -62,6 +64,8 @@ %include "filter/fft_filter_fff.h" %include "filter/hilbert_fc.h" %include "filter/pfb_channelizer_ccf.h" +%include "filter/single_pole_iir_filter_cc.h" +%include "filter/single_pole_iir_filter_ff.h" GR_SWIG_BLOCK_MAGIC2(filter, adaptive_fir_ccc); GR_SWIG_BLOCK_MAGIC2(filter, adaptive_fir_ccf); @@ -77,3 +81,5 @@ GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, hilbert_fc); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_cc); +GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_ff); -- cgit From b70bcc34ce4bd778a56e91da4b061475cda7cb6d Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 16:34:19 -0400 Subject: filter: copied over interpolator_taps.h to gr-filter. --- gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/interpolator_taps.h | 140 +++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 gr-filter/include/filter/interpolator_taps.h diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 49e946536..e0577fa00 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -79,6 +79,7 @@ install(FILES fir_filter.h fir_filter_with_buffer.h fft_filter.h + interpolator_taps.h pm_remez.h polyphase_filterbank.h single_pole_iir.h diff --git a/gr-filter/include/filter/interpolator_taps.h b/gr-filter/include/filter/interpolator_taps.h new file mode 100644 index 000000000..a232edd38 --- /dev/null +++ b/gr-filter/include/filter/interpolator_taps.h @@ -0,0 +1,140 @@ +/* + * This file was machine generated by gen_interpolator_taps. + * DO NOT EDIT BY HAND. + */ + +static const int NTAPS = 8; +static const int NSTEPS = 128; + +static const float taps[NSTEPS+1][NTAPS] = { + // -4 -3 -2 -1 0 1 2 3 mu + { 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 1.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00 }, // 0/128 + { -1.54700e-04, 8.53777e-04, -2.76968e-03, 7.89295e-03, 9.98534e-01, -5.41054e-03, 1.24642e-03, -1.98993e-04 }, // 1/128 + { -3.09412e-04, 1.70888e-03, -5.55134e-03, 1.58840e-02, 9.96891e-01, -1.07209e-02, 2.47942e-03, -3.96391e-04 }, // 2/128 + { -4.64053e-04, 2.56486e-03, -8.34364e-03, 2.39714e-02, 9.95074e-01, -1.59305e-02, 3.69852e-03, -5.92100e-04 }, // 3/128 + { -6.18544e-04, 3.42130e-03, -1.11453e-02, 3.21531e-02, 9.93082e-01, -2.10389e-02, 4.90322e-03, -7.86031e-04 }, // 4/128 + { -7.72802e-04, 4.27773e-03, -1.39548e-02, 4.04274e-02, 9.90917e-01, -2.60456e-02, 6.09305e-03, -9.78093e-04 }, // 5/128 + { -9.26747e-04, 5.13372e-03, -1.67710e-02, 4.87921e-02, 9.88580e-01, -3.09503e-02, 7.26755e-03, -1.16820e-03 }, // 6/128 + { -1.08030e-03, 5.98883e-03, -1.95925e-02, 5.72454e-02, 9.86071e-01, -3.57525e-02, 8.42626e-03, -1.35627e-03 }, // 7/128 + { -1.23337e-03, 6.84261e-03, -2.24178e-02, 6.57852e-02, 9.83392e-01, -4.04519e-02, 9.56876e-03, -1.54221e-03 }, // 8/128 + { -1.38589e-03, 7.69462e-03, -2.52457e-02, 7.44095e-02, 9.80543e-01, -4.50483e-02, 1.06946e-02, -1.72594e-03 }, // 9/128 + { -1.53777e-03, 8.54441e-03, -2.80746e-02, 8.31162e-02, 9.77526e-01, -4.95412e-02, 1.18034e-02, -1.90738e-03 }, // 10/128 + { -1.68894e-03, 9.39154e-03, -3.09033e-02, 9.19033e-02, 9.74342e-01, -5.39305e-02, 1.28947e-02, -2.08645e-03 }, // 11/128 + { -1.83931e-03, 1.02356e-02, -3.37303e-02, 1.00769e-01, 9.70992e-01, -5.82159e-02, 1.39681e-02, -2.26307e-03 }, // 12/128 + { -1.98880e-03, 1.10760e-02, -3.65541e-02, 1.09710e-01, 9.67477e-01, -6.23972e-02, 1.50233e-02, -2.43718e-03 }, // 13/128 + { -2.13733e-03, 1.19125e-02, -3.93735e-02, 1.18725e-01, 9.63798e-01, -6.64743e-02, 1.60599e-02, -2.60868e-03 }, // 14/128 + { -2.28483e-03, 1.27445e-02, -4.21869e-02, 1.27812e-01, 9.59958e-01, -7.04471e-02, 1.70776e-02, -2.77751e-03 }, // 15/128 + { -2.43121e-03, 1.35716e-02, -4.49929e-02, 1.36968e-01, 9.55956e-01, -7.43154e-02, 1.80759e-02, -2.94361e-03 }, // 16/128 + { -2.57640e-03, 1.43934e-02, -4.77900e-02, 1.46192e-01, 9.51795e-01, -7.80792e-02, 1.90545e-02, -3.10689e-03 }, // 17/128 + { -2.72032e-03, 1.52095e-02, -5.05770e-02, 1.55480e-01, 9.47477e-01, -8.17385e-02, 2.00132e-02, -3.26730e-03 }, // 18/128 + { -2.86289e-03, 1.60193e-02, -5.33522e-02, 1.64831e-01, 9.43001e-01, -8.52933e-02, 2.09516e-02, -3.42477e-03 }, // 19/128 + { -3.00403e-03, 1.68225e-02, -5.61142e-02, 1.74242e-01, 9.38371e-01, -8.87435e-02, 2.18695e-02, -3.57923e-03 }, // 20/128 + { -3.14367e-03, 1.76185e-02, -5.88617e-02, 1.83711e-01, 9.33586e-01, -9.20893e-02, 2.27664e-02, -3.73062e-03 }, // 21/128 + { -3.28174e-03, 1.84071e-02, -6.15931e-02, 1.93236e-01, 9.28650e-01, -9.53307e-02, 2.36423e-02, -3.87888e-03 }, // 22/128 + { -3.41815e-03, 1.91877e-02, -6.43069e-02, 2.02814e-01, 9.23564e-01, -9.84679e-02, 2.44967e-02, -4.02397e-03 }, // 23/128 + { -3.55283e-03, 1.99599e-02, -6.70018e-02, 2.12443e-01, 9.18329e-01, -1.01501e-01, 2.53295e-02, -4.16581e-03 }, // 24/128 + { -3.68570e-03, 2.07233e-02, -6.96762e-02, 2.22120e-01, 9.12947e-01, -1.04430e-01, 2.61404e-02, -4.30435e-03 }, // 25/128 + { -3.81671e-03, 2.14774e-02, -7.23286e-02, 2.31843e-01, 9.07420e-01, -1.07256e-01, 2.69293e-02, -4.43955e-03 }, // 26/128 + { -3.94576e-03, 2.22218e-02, -7.49577e-02, 2.41609e-01, 9.01749e-01, -1.09978e-01, 2.76957e-02, -4.57135e-03 }, // 27/128 + { -4.07279e-03, 2.29562e-02, -7.75620e-02, 2.51417e-01, 8.95936e-01, -1.12597e-01, 2.84397e-02, -4.69970e-03 }, // 28/128 + { -4.19774e-03, 2.36801e-02, -8.01399e-02, 2.61263e-01, 8.89984e-01, -1.15113e-01, 2.91609e-02, -4.82456e-03 }, // 29/128 + { -4.32052e-03, 2.43930e-02, -8.26900e-02, 2.71144e-01, 8.83893e-01, -1.17526e-01, 2.98593e-02, -4.94589e-03 }, // 30/128 + { -4.44107e-03, 2.50946e-02, -8.52109e-02, 2.81060e-01, 8.77666e-01, -1.19837e-01, 3.05345e-02, -5.06363e-03 }, // 31/128 + { -4.55932e-03, 2.57844e-02, -8.77011e-02, 2.91006e-01, 8.71305e-01, -1.22047e-01, 3.11866e-02, -5.17776e-03 }, // 32/128 + { -4.67520e-03, 2.64621e-02, -9.01591e-02, 3.00980e-01, 8.64812e-01, -1.24154e-01, 3.18153e-02, -5.28823e-03 }, // 33/128 + { -4.78866e-03, 2.71272e-02, -9.25834e-02, 3.10980e-01, 8.58189e-01, -1.26161e-01, 3.24205e-02, -5.39500e-03 }, // 34/128 + { -4.89961e-03, 2.77794e-02, -9.49727e-02, 3.21004e-01, 8.51437e-01, -1.28068e-01, 3.30021e-02, -5.49804e-03 }, // 35/128 + { -5.00800e-03, 2.84182e-02, -9.73254e-02, 3.31048e-01, 8.44559e-01, -1.29874e-01, 3.35600e-02, -5.59731e-03 }, // 36/128 + { -5.11376e-03, 2.90433e-02, -9.96402e-02, 3.41109e-01, 8.37557e-01, -1.31581e-01, 3.40940e-02, -5.69280e-03 }, // 37/128 + { -5.21683e-03, 2.96543e-02, -1.01915e-01, 3.51186e-01, 8.30432e-01, -1.33189e-01, 3.46042e-02, -5.78446e-03 }, // 38/128 + { -5.31716e-03, 3.02507e-02, -1.04150e-01, 3.61276e-01, 8.23188e-01, -1.34699e-01, 3.50903e-02, -5.87227e-03 }, // 39/128 + { -5.41467e-03, 3.08323e-02, -1.06342e-01, 3.71376e-01, 8.15826e-01, -1.36111e-01, 3.55525e-02, -5.95620e-03 }, // 40/128 + { -5.50931e-03, 3.13987e-02, -1.08490e-01, 3.81484e-01, 8.08348e-01, -1.37426e-01, 3.59905e-02, -6.03624e-03 }, // 41/128 + { -5.60103e-03, 3.19495e-02, -1.10593e-01, 3.91596e-01, 8.00757e-01, -1.38644e-01, 3.64044e-02, -6.11236e-03 }, // 42/128 + { -5.68976e-03, 3.24843e-02, -1.12650e-01, 4.01710e-01, 7.93055e-01, -1.39767e-01, 3.67941e-02, -6.18454e-03 }, // 43/128 + { -5.77544e-03, 3.30027e-02, -1.14659e-01, 4.11823e-01, 7.85244e-01, -1.40794e-01, 3.71596e-02, -6.25277e-03 }, // 44/128 + { -5.85804e-03, 3.35046e-02, -1.16618e-01, 4.21934e-01, 7.77327e-01, -1.41727e-01, 3.75010e-02, -6.31703e-03 }, // 45/128 + { -5.93749e-03, 3.39894e-02, -1.18526e-01, 4.32038e-01, 7.69305e-01, -1.42566e-01, 3.78182e-02, -6.37730e-03 }, // 46/128 + { -6.01374e-03, 3.44568e-02, -1.20382e-01, 4.42134e-01, 7.61181e-01, -1.43313e-01, 3.81111e-02, -6.43358e-03 }, // 47/128 + { -6.08674e-03, 3.49066e-02, -1.22185e-01, 4.52218e-01, 7.52958e-01, -1.43968e-01, 3.83800e-02, -6.48585e-03 }, // 48/128 + { -6.15644e-03, 3.53384e-02, -1.23933e-01, 4.62289e-01, 7.44637e-01, -1.44531e-01, 3.86247e-02, -6.53412e-03 }, // 49/128 + { -6.22280e-03, 3.57519e-02, -1.25624e-01, 4.72342e-01, 7.36222e-01, -1.45004e-01, 3.88454e-02, -6.57836e-03 }, // 50/128 + { -6.28577e-03, 3.61468e-02, -1.27258e-01, 4.82377e-01, 7.27714e-01, -1.45387e-01, 3.90420e-02, -6.61859e-03 }, // 51/128 + { -6.34530e-03, 3.65227e-02, -1.28832e-01, 4.92389e-01, 7.19116e-01, -1.45682e-01, 3.92147e-02, -6.65479e-03 }, // 52/128 + { -6.40135e-03, 3.68795e-02, -1.30347e-01, 5.02377e-01, 7.10431e-01, -1.45889e-01, 3.93636e-02, -6.68698e-03 }, // 53/128 + { -6.45388e-03, 3.72167e-02, -1.31800e-01, 5.12337e-01, 7.01661e-01, -1.46009e-01, 3.94886e-02, -6.71514e-03 }, // 54/128 + { -6.50285e-03, 3.75341e-02, -1.33190e-01, 5.22267e-01, 6.92808e-01, -1.46043e-01, 3.95900e-02, -6.73929e-03 }, // 55/128 + { -6.54823e-03, 3.78315e-02, -1.34515e-01, 5.32164e-01, 6.83875e-01, -1.45993e-01, 3.96678e-02, -6.75943e-03 }, // 56/128 + { -6.58996e-03, 3.81085e-02, -1.35775e-01, 5.42025e-01, 6.74865e-01, -1.45859e-01, 3.97222e-02, -6.77557e-03 }, // 57/128 + { -6.62802e-03, 3.83650e-02, -1.36969e-01, 5.51849e-01, 6.65779e-01, -1.45641e-01, 3.97532e-02, -6.78771e-03 }, // 58/128 + { -6.66238e-03, 3.86006e-02, -1.38094e-01, 5.61631e-01, 6.56621e-01, -1.45343e-01, 3.97610e-02, -6.79588e-03 }, // 59/128 + { -6.69300e-03, 3.88151e-02, -1.39150e-01, 5.71370e-01, 6.47394e-01, -1.44963e-01, 3.97458e-02, -6.80007e-03 }, // 60/128 + { -6.71985e-03, 3.90083e-02, -1.40136e-01, 5.81063e-01, 6.38099e-01, -1.44503e-01, 3.97077e-02, -6.80032e-03 }, // 61/128 + { -6.74291e-03, 3.91800e-02, -1.41050e-01, 5.90706e-01, 6.28739e-01, -1.43965e-01, 3.96469e-02, -6.79662e-03 }, // 62/128 + { -6.76214e-03, 3.93299e-02, -1.41891e-01, 6.00298e-01, 6.19318e-01, -1.43350e-01, 3.95635e-02, -6.78902e-03 }, // 63/128 + { -6.77751e-03, 3.94578e-02, -1.42658e-01, 6.09836e-01, 6.09836e-01, -1.42658e-01, 3.94578e-02, -6.77751e-03 }, // 64/128 + { -6.78902e-03, 3.95635e-02, -1.43350e-01, 6.19318e-01, 6.00298e-01, -1.41891e-01, 3.93299e-02, -6.76214e-03 }, // 65/128 + { -6.79662e-03, 3.96469e-02, -1.43965e-01, 6.28739e-01, 5.90706e-01, -1.41050e-01, 3.91800e-02, -6.74291e-03 }, // 66/128 + { -6.80032e-03, 3.97077e-02, -1.44503e-01, 6.38099e-01, 5.81063e-01, -1.40136e-01, 3.90083e-02, -6.71985e-03 }, // 67/128 + { -6.80007e-03, 3.97458e-02, -1.44963e-01, 6.47394e-01, 5.71370e-01, -1.39150e-01, 3.88151e-02, -6.69300e-03 }, // 68/128 + { -6.79588e-03, 3.97610e-02, -1.45343e-01, 6.56621e-01, 5.61631e-01, -1.38094e-01, 3.86006e-02, -6.66238e-03 }, // 69/128 + { -6.78771e-03, 3.97532e-02, -1.45641e-01, 6.65779e-01, 5.51849e-01, -1.36969e-01, 3.83650e-02, -6.62802e-03 }, // 70/128 + { -6.77557e-03, 3.97222e-02, -1.45859e-01, 6.74865e-01, 5.42025e-01, -1.35775e-01, 3.81085e-02, -6.58996e-03 }, // 71/128 + { -6.75943e-03, 3.96678e-02, -1.45993e-01, 6.83875e-01, 5.32164e-01, -1.34515e-01, 3.78315e-02, -6.54823e-03 }, // 72/128 + { -6.73929e-03, 3.95900e-02, -1.46043e-01, 6.92808e-01, 5.22267e-01, -1.33190e-01, 3.75341e-02, -6.50285e-03 }, // 73/128 + { -6.71514e-03, 3.94886e-02, -1.46009e-01, 7.01661e-01, 5.12337e-01, -1.31800e-01, 3.72167e-02, -6.45388e-03 }, // 74/128 + { -6.68698e-03, 3.93636e-02, -1.45889e-01, 7.10431e-01, 5.02377e-01, -1.30347e-01, 3.68795e-02, -6.40135e-03 }, // 75/128 + { -6.65479e-03, 3.92147e-02, -1.45682e-01, 7.19116e-01, 4.92389e-01, -1.28832e-01, 3.65227e-02, -6.34530e-03 }, // 76/128 + { -6.61859e-03, 3.90420e-02, -1.45387e-01, 7.27714e-01, 4.82377e-01, -1.27258e-01, 3.61468e-02, -6.28577e-03 }, // 77/128 + { -6.57836e-03, 3.88454e-02, -1.45004e-01, 7.36222e-01, 4.72342e-01, -1.25624e-01, 3.57519e-02, -6.22280e-03 }, // 78/128 + { -6.53412e-03, 3.86247e-02, -1.44531e-01, 7.44637e-01, 4.62289e-01, -1.23933e-01, 3.53384e-02, -6.15644e-03 }, // 79/128 + { -6.48585e-03, 3.83800e-02, -1.43968e-01, 7.52958e-01, 4.52218e-01, -1.22185e-01, 3.49066e-02, -6.08674e-03 }, // 80/128 + { -6.43358e-03, 3.81111e-02, -1.43313e-01, 7.61181e-01, 4.42134e-01, -1.20382e-01, 3.44568e-02, -6.01374e-03 }, // 81/128 + { -6.37730e-03, 3.78182e-02, -1.42566e-01, 7.69305e-01, 4.32038e-01, -1.18526e-01, 3.39894e-02, -5.93749e-03 }, // 82/128 + { -6.31703e-03, 3.75010e-02, -1.41727e-01, 7.77327e-01, 4.21934e-01, -1.16618e-01, 3.35046e-02, -5.85804e-03 }, // 83/128 + { -6.25277e-03, 3.71596e-02, -1.40794e-01, 7.85244e-01, 4.11823e-01, -1.14659e-01, 3.30027e-02, -5.77544e-03 }, // 84/128 + { -6.18454e-03, 3.67941e-02, -1.39767e-01, 7.93055e-01, 4.01710e-01, -1.12650e-01, 3.24843e-02, -5.68976e-03 }, // 85/128 + { -6.11236e-03, 3.64044e-02, -1.38644e-01, 8.00757e-01, 3.91596e-01, -1.10593e-01, 3.19495e-02, -5.60103e-03 }, // 86/128 + { -6.03624e-03, 3.59905e-02, -1.37426e-01, 8.08348e-01, 3.81484e-01, -1.08490e-01, 3.13987e-02, -5.50931e-03 }, // 87/128 + { -5.95620e-03, 3.55525e-02, -1.36111e-01, 8.15826e-01, 3.71376e-01, -1.06342e-01, 3.08323e-02, -5.41467e-03 }, // 88/128 + { -5.87227e-03, 3.50903e-02, -1.34699e-01, 8.23188e-01, 3.61276e-01, -1.04150e-01, 3.02507e-02, -5.31716e-03 }, // 89/128 + { -5.78446e-03, 3.46042e-02, -1.33189e-01, 8.30432e-01, 3.51186e-01, -1.01915e-01, 2.96543e-02, -5.21683e-03 }, // 90/128 + { -5.69280e-03, 3.40940e-02, -1.31581e-01, 8.37557e-01, 3.41109e-01, -9.96402e-02, 2.90433e-02, -5.11376e-03 }, // 91/128 + { -5.59731e-03, 3.35600e-02, -1.29874e-01, 8.44559e-01, 3.31048e-01, -9.73254e-02, 2.84182e-02, -5.00800e-03 }, // 92/128 + { -5.49804e-03, 3.30021e-02, -1.28068e-01, 8.51437e-01, 3.21004e-01, -9.49727e-02, 2.77794e-02, -4.89961e-03 }, // 93/128 + { -5.39500e-03, 3.24205e-02, -1.26161e-01, 8.58189e-01, 3.10980e-01, -9.25834e-02, 2.71272e-02, -4.78866e-03 }, // 94/128 + { -5.28823e-03, 3.18153e-02, -1.24154e-01, 8.64812e-01, 3.00980e-01, -9.01591e-02, 2.64621e-02, -4.67520e-03 }, // 95/128 + { -5.17776e-03, 3.11866e-02, -1.22047e-01, 8.71305e-01, 2.91006e-01, -8.77011e-02, 2.57844e-02, -4.55932e-03 }, // 96/128 + { -5.06363e-03, 3.05345e-02, -1.19837e-01, 8.77666e-01, 2.81060e-01, -8.52109e-02, 2.50946e-02, -4.44107e-03 }, // 97/128 + { -4.94589e-03, 2.98593e-02, -1.17526e-01, 8.83893e-01, 2.71144e-01, -8.26900e-02, 2.43930e-02, -4.32052e-03 }, // 98/128 + { -4.82456e-03, 2.91609e-02, -1.15113e-01, 8.89984e-01, 2.61263e-01, -8.01399e-02, 2.36801e-02, -4.19774e-03 }, // 99/128 + { -4.69970e-03, 2.84397e-02, -1.12597e-01, 8.95936e-01, 2.51417e-01, -7.75620e-02, 2.29562e-02, -4.07279e-03 }, // 100/128 + { -4.57135e-03, 2.76957e-02, -1.09978e-01, 9.01749e-01, 2.41609e-01, -7.49577e-02, 2.22218e-02, -3.94576e-03 }, // 101/128 + { -4.43955e-03, 2.69293e-02, -1.07256e-01, 9.07420e-01, 2.31843e-01, -7.23286e-02, 2.14774e-02, -3.81671e-03 }, // 102/128 + { -4.30435e-03, 2.61404e-02, -1.04430e-01, 9.12947e-01, 2.22120e-01, -6.96762e-02, 2.07233e-02, -3.68570e-03 }, // 103/128 + { -4.16581e-03, 2.53295e-02, -1.01501e-01, 9.18329e-01, 2.12443e-01, -6.70018e-02, 1.99599e-02, -3.55283e-03 }, // 104/128 + { -4.02397e-03, 2.44967e-02, -9.84679e-02, 9.23564e-01, 2.02814e-01, -6.43069e-02, 1.91877e-02, -3.41815e-03 }, // 105/128 + { -3.87888e-03, 2.36423e-02, -9.53307e-02, 9.28650e-01, 1.93236e-01, -6.15931e-02, 1.84071e-02, -3.28174e-03 }, // 106/128 + { -3.73062e-03, 2.27664e-02, -9.20893e-02, 9.33586e-01, 1.83711e-01, -5.88617e-02, 1.76185e-02, -3.14367e-03 }, // 107/128 + { -3.57923e-03, 2.18695e-02, -8.87435e-02, 9.38371e-01, 1.74242e-01, -5.61142e-02, 1.68225e-02, -3.00403e-03 }, // 108/128 + { -3.42477e-03, 2.09516e-02, -8.52933e-02, 9.43001e-01, 1.64831e-01, -5.33522e-02, 1.60193e-02, -2.86289e-03 }, // 109/128 + { -3.26730e-03, 2.00132e-02, -8.17385e-02, 9.47477e-01, 1.55480e-01, -5.05770e-02, 1.52095e-02, -2.72032e-03 }, // 110/128 + { -3.10689e-03, 1.90545e-02, -7.80792e-02, 9.51795e-01, 1.46192e-01, -4.77900e-02, 1.43934e-02, -2.57640e-03 }, // 111/128 + { -2.94361e-03, 1.80759e-02, -7.43154e-02, 9.55956e-01, 1.36968e-01, -4.49929e-02, 1.35716e-02, -2.43121e-03 }, // 112/128 + { -2.77751e-03, 1.70776e-02, -7.04471e-02, 9.59958e-01, 1.27812e-01, -4.21869e-02, 1.27445e-02, -2.28483e-03 }, // 113/128 + { -2.60868e-03, 1.60599e-02, -6.64743e-02, 9.63798e-01, 1.18725e-01, -3.93735e-02, 1.19125e-02, -2.13733e-03 }, // 114/128 + { -2.43718e-03, 1.50233e-02, -6.23972e-02, 9.67477e-01, 1.09710e-01, -3.65541e-02, 1.10760e-02, -1.98880e-03 }, // 115/128 + { -2.26307e-03, 1.39681e-02, -5.82159e-02, 9.70992e-01, 1.00769e-01, -3.37303e-02, 1.02356e-02, -1.83931e-03 }, // 116/128 + { -2.08645e-03, 1.28947e-02, -5.39305e-02, 9.74342e-01, 9.19033e-02, -3.09033e-02, 9.39154e-03, -1.68894e-03 }, // 117/128 + { -1.90738e-03, 1.18034e-02, -4.95412e-02, 9.77526e-01, 8.31162e-02, -2.80746e-02, 8.54441e-03, -1.53777e-03 }, // 118/128 + { -1.72594e-03, 1.06946e-02, -4.50483e-02, 9.80543e-01, 7.44095e-02, -2.52457e-02, 7.69462e-03, -1.38589e-03 }, // 119/128 + { -1.54221e-03, 9.56876e-03, -4.04519e-02, 9.83392e-01, 6.57852e-02, -2.24178e-02, 6.84261e-03, -1.23337e-03 }, // 120/128 + { -1.35627e-03, 8.42626e-03, -3.57525e-02, 9.86071e-01, 5.72454e-02, -1.95925e-02, 5.98883e-03, -1.08030e-03 }, // 121/128 + { -1.16820e-03, 7.26755e-03, -3.09503e-02, 9.88580e-01, 4.87921e-02, -1.67710e-02, 5.13372e-03, -9.26747e-04 }, // 122/128 + { -9.78093e-04, 6.09305e-03, -2.60456e-02, 9.90917e-01, 4.04274e-02, -1.39548e-02, 4.27773e-03, -7.72802e-04 }, // 123/128 + { -7.86031e-04, 4.90322e-03, -2.10389e-02, 9.93082e-01, 3.21531e-02, -1.11453e-02, 3.42130e-03, -6.18544e-04 }, // 124/128 + { -5.92100e-04, 3.69852e-03, -1.59305e-02, 9.95074e-01, 2.39714e-02, -8.34364e-03, 2.56486e-03, -4.64053e-04 }, // 125/128 + { -3.96391e-04, 2.47942e-03, -1.07209e-02, 9.96891e-01, 1.58840e-02, -5.55134e-03, 1.70888e-03, -3.09412e-04 }, // 126/128 + { -1.98993e-04, 1.24642e-03, -5.41054e-03, 9.98534e-01, 7.89295e-03, -2.76968e-03, 8.53777e-04, -1.54700e-04 }, // 127/128 + { 0.00000e+00, 0.00000e+00, 0.00000e+00, 1.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00 }, // 128/128 +}; -- cgit From 3cd0ea45255e2ca43931b2f067ec82fc92f4293e Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 15 Jun 2012 17:56:33 -0400 Subject: filter: adding IIR filter. Include QA and GRC. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/iir_filter_ffd.xml | 31 +++++ gr-filter/include/filter/CMakeLists.txt | 2 + gr-filter/include/filter/iir_filter.h | 183 ++++++++++++++++++++++++++++++ gr-filter/include/filter/iir_filter_ffd.h | 82 +++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/iir_filter_ffd_impl.cc | 85 ++++++++++++++ gr-filter/lib/iir_filter_ffd_impl.h | 56 +++++++++ gr-filter/python/qa_iir_filter.py | 157 +++++++++++++++++++++++++ gr-filter/swig/filter_swig.i | 3 + 11 files changed, 602 insertions(+) create mode 100644 gr-filter/grc/iir_filter_ffd.xml create mode 100644 gr-filter/include/filter/iir_filter.h create mode 100644 gr-filter/include/filter/iir_filter_ffd.h create mode 100644 gr-filter/lib/iir_filter_ffd_impl.cc create mode 100644 gr-filter/lib/iir_filter_ffd_impl.h create mode 100755 gr-filter/python/qa_iir_filter.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 83036a0b3..63434fe75 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -24,6 +24,7 @@ install(FILES fir_filter_xxx.xml filter_delay_fc.xml hilbert_fc.xml + iir_filter_ffd.xml pfb_channelizer.xml single_pole_iir_filter_xx.xml DESTINATION ${GRC_BLOCKS_DIR} diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 6711e809f..2ae4eb628 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -35,6 +35,7 @@ fir_filter_xxx filter_delay_fc hilbert_fc + iir_filter_ffd pfb_channelizer_ccf single_pole_iir_filter_xx diff --git a/gr-filter/grc/iir_filter_ffd.xml b/gr-filter/grc/iir_filter_ffd.xml new file mode 100644 index 000000000..261aba320 --- /dev/null +++ b/gr-filter/grc/iir_filter_ffd.xml @@ -0,0 +1,31 @@ + + + + IIR Filter + iir_filter_ffd + from gnuradio import filter + filter.iir_filter_ffd($fftaps, $fbtaps) + set_taps($fftaps, $fbtaps) + + Feed-forward Taps + fftaps + real_vector + + + Feedback Taps + fbtaps + real_vector + + + in + float + + + out + float + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index e0577fa00..ca8c7bf07 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -79,6 +79,7 @@ install(FILES fir_filter.h fir_filter_with_buffer.h fft_filter.h + iir_filter.h interpolator_taps.h pm_remez.h polyphase_filterbank.h @@ -92,6 +93,7 @@ install(FILES fft_filter_ccc.h fft_filter_fff.h hilbert_fc.h + iir_filter_ffd.h pfb_channelizer_ccf.h single_pole_iir_filter_cc.h single_pole_iir_filter_ff.h diff --git a/gr-filter/include/filter/iir_filter.h b/gr-filter/include/filter/iir_filter.h new file mode 100644 index 000000000..667acec35 --- /dev/null +++ b/gr-filter/include/filter/iir_filter.h @@ -0,0 +1,183 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_IIR_FILTER_H +#define INCLUDED_IIR_FILTER_H + +#include +#include +#include + +namespace gr { + namespace filter { + namespace kernel { + + /*! + * \brief base class template for Infinite Impulse Response filter (IIR) + */ + template + class iir_filter + { + public: + /*! + * \brief Construct an IIR with the given taps. + * + * This filter uses the Direct Form I implementation, where + * \p fftaps contains the feed-forward taps, and \p fbtaps the feedback ones. + * + * \p fftaps and \p fbtaps must have equal numbers of taps + * + * The input and output satisfy a difference equation of the form + + \f[ + y[n] - \sum_{k=1}^{M} a_k y[n-k] = \sum_{k=0}^{N} b_k x[n-k] + \f] + + * with the corresponding rational system function + + \f[ + H(z) = \frac{\sum_{k=0}^{N} b_k z^{-k}}{1 - \sum_{k=1}^{M} a_k z^{-k}} + \f] + + * Note that some texts define the system function with a + in + * the denominator. If you're using that convention, you'll + * need to negate the feedback taps. + */ + iir_filter(const std::vector& fftaps, + const std::vector& fbtaps) throw (std::invalid_argument) + { + set_taps(fftaps, fbtaps); + } + + iir_filter() : d_latest_n(0),d_latest_m(0) { } + + ~iir_filter() {} + + /*! + * \brief compute a single output value. + * \returns the filtered input value. + */ + o_type filter(const i_type input); + + /*! + * \brief compute an array of N output values. + * \p input must have N valid entries. + */ + void filter_n(o_type output[], const i_type input[], long n); + + /*! + * \return number of taps in filter. + */ + unsigned ntaps_ff() const { return d_fftaps.size(); } + unsigned ntaps_fb() const { return d_fbtaps.size(); } + + /*! + * \brief install new taps. + */ + void set_taps(const std::vector &fftaps, + const std::vector &fbtaps) throw (std::invalid_argument) + { + d_latest_n = 0; + d_latest_m = 0; + d_fftaps = fftaps; + d_fbtaps = fbtaps; + + int n = fftaps.size(); + int m = fbtaps.size(); + d_prev_input.resize(2 * n); + d_prev_output.resize(2 * m); + + for(int i = 0; i < 2 * n; i++) { + d_prev_input[i] = 0; + } + for(int i = 0; i < 2 * m; i++) { + d_prev_output[i] = 0; + } + } + + protected: + std::vector d_fftaps; + std::vector d_fbtaps; + int d_latest_n; + int d_latest_m; + std::vector d_prev_output; + std::vector d_prev_input; + }; + + // + // general case. We may want to specialize this + // + template + o_type + iir_filter::filter(const i_type input) + { + tap_type acc; + unsigned i = 0; + unsigned n = ntaps_ff(); + unsigned m = ntaps_fb(); + + if(n == 0) + return (o_type)0; + + int latest_n = d_latest_n; + int latest_m = d_latest_m; + + acc = d_fftaps[0] * input; + for(i = 1; i < n; i ++) + acc += (d_fftaps[i] * d_prev_input[latest_n + i]); + for(i = 1; i < m; i ++) + acc += (d_fbtaps[i] * d_prev_output[latest_m + i]); + + // store the values twice to avoid having to handle wrap-around in the loop + d_prev_output[latest_m] = acc; + d_prev_output[latest_m+m] = acc; + d_prev_input[latest_n] = input; + d_prev_input[latest_n+n] = input; + + latest_n--; + latest_m--; + if(latest_n < 0) + latest_n += n; + if(latest_m < 0) + latest_m += m; + + d_latest_m = latest_m; + d_latest_n = latest_n; + return (o_type)acc; + } + + template + void + iir_filter::filter_n(o_type output[], + const i_type input[], + long n) + { + for(int i = 0; i < n; i++) + output[i] = filter(input[i]); + } + + } /* namespace kernel */ + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_IIR_FILTER_H */ + diff --git a/gr-filter/include/filter/iir_filter_ffd.h b/gr-filter/include/filter/iir_filter_ffd.h new file mode 100644 index 000000000..c23286ddc --- /dev/null +++ b/gr-filter/include/filter/iir_filter_ffd.h @@ -0,0 +1,82 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_IIR_FILTER_FFD_H +#define INCLUDED_IIR_FILTER_FFD_H + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief IIR filter with float input, float output and double taps + * \ingroup filter_blk + * + * This filter uses the Direct Form I implementation, where + * \p fftaps contains the feed-forward taps, and \p fbtaps the feedback ones. + * + * + * The input and output satisfy a difference equation of the form + \htmlonly + \f{ + y[n] - \sum_{k=1}^{M} a_k y[n-k] = \sum_{k=0}^{N} b_k x[n-k] + \f} + \endhtmlonly + + \xmlonly + y[n] - \sum_{k=1}^{M} a_k y[n-k] = \sum_{k=0}^{N} b_k x[n-k] + \endxmlonly + + * with the corresponding rational system function + \htmlonly + \f{ + H(z) = \ frac{\sum_{k=0}^{M} b_k z^{-k}}{1 - \sum_{k=1}^{N} a_k z^{-k}} + \f} + \endhtmlonly + + \xmlonly + H(z) = \ frac{\sum_{k=0}^{M} b_k z^{-k}}{1 - \sum_{k=1}^{N} a_k z^{-k}} + \endxmlonly + + * Note that some texts define the system function with a + in the + * denominator. If you're using that convention, you'll need to + * negate the feedback taps. + */ + class FILTER_API iir_filter_ffd : virtual public gr_sync_block + { + public: + // gr::filter::iir_filter_ffd::sptr + typedef boost::shared_ptr sptr; + + static FILTER_API sptr make(const std::vector &fftaps, + const std::vector &fbtaps); + + virtual void set_taps(const std::vector &fftaps, + const std::vector &fbtaps) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_IIR_FILTER_FFD_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index d661d01b6..7a8b5aa0b 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -120,6 +120,7 @@ list(APPEND filter_sources fft_filter_ccc_impl.cc fft_filter_fff_impl.cc hilbert_fc_impl.cc + iir_filter_ffd_impl.cc pfb_channelizer_ccf_impl.cc single_pole_iir_filter_cc_impl.cc single_pole_iir_filter_ff_impl.cc diff --git a/gr-filter/lib/iir_filter_ffd_impl.cc b/gr-filter/lib/iir_filter_ffd_impl.cc new file mode 100644 index 000000000..24bfad190 --- /dev/null +++ b/gr-filter/lib/iir_filter_ffd_impl.cc @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "iir_filter_ffd_impl.h" +#include + +namespace gr { + namespace filter { + + iir_filter_ffd::sptr + iir_filter_ffd::make(const std::vector &fftaps, + const std::vector &fbtaps) + { + return gnuradio::get_initial_sptr(new iir_filter_ffd_impl(fftaps, fbtaps)); + } + + iir_filter_ffd_impl::iir_filter_ffd_impl(const std::vector &fftaps, + const std::vector &fbtaps) + + : gr_sync_block("iir_filter_ffd", + gr_make_io_signature(1, 1, sizeof (float)), + gr_make_io_signature(1, 1, sizeof (float))), + d_updated(false) + { + d_iir = new kernel::iir_filter(fftaps, fbtaps); + //d_iir = new gri_iir(fftaps, fbtaps); + } + + iir_filter_ffd_impl::~iir_filter_ffd_impl() + { + delete d_iir; + } + + void + iir_filter_ffd_impl::set_taps(const std::vector &fftaps, + const std::vector &fbtaps) + { + d_new_fftaps = fftaps; + d_new_fbtaps = fbtaps; + d_updated = true; + } + + int + iir_filter_ffd_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *in = (const float*)input_items[0]; + float *out = (float*)output_items[0]; + + if(d_updated) { + d_iir->set_taps(d_new_fftaps, d_new_fbtaps); + d_updated = false; + } + + d_iir->filter_n(out, in, noutput_items); + return noutput_items; + }; + + } /* namespace filter */ +} /* namespace gr */ + diff --git a/gr-filter/lib/iir_filter_ffd_impl.h b/gr-filter/lib/iir_filter_ffd_impl.h new file mode 100644 index 000000000..45aab6c22 --- /dev/null +++ b/gr-filter/lib/iir_filter_ffd_impl.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_IIR_FILTER_FFD_IMPL_H +#define INCLUDED_IIR_FILTER_FFD_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API iir_filter_ffd_impl : public iir_filter_ffd + { + private: + bool d_updated; + kernel::iir_filter *d_iir; + std::vector d_new_fftaps; + std::vector d_new_fbtaps; + + public: + iir_filter_ffd_impl(const std::vector &fftaps, + const std::vector &fbtaps); + ~iir_filter_ffd_impl(); + + void set_taps(const std::vector &fftaps, + const std::vector &fbtaps); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_IIR_FILTER_FFD_IMPL_H */ diff --git a/gr-filter/python/qa_iir_filter.py b/gr-filter/python/qa_iir_filter.py new file mode 100755 index 000000000..645c4b66e --- /dev/null +++ b/gr-filter/python/qa_iir_filter.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2010 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter + +class test_iir_filter(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_iir_direct_001(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = () + fbtaps = () + expected_result = (0, 0, 0, 0, 0, 0, 0, 0) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_iir_direct_002(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2,) + fbtaps = (0,) + expected_result = (2, 4, 6, 8, 10, 12, 14, 16) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_iir_direct_003(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2, 11) + fbtaps = (0, 0) + expected_result = (2, 15, 28, 41, 54, 67, 80, 93) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_iir_direct_004(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2, 11) + fbtaps = (0, -1) + expected_result = (2, 13, 15, 26, 28, 39, 41, 52) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_iir_direct_005(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2, 11, 0) + fbtaps = (0, -1, 3) + expected_result = (2, 13, 21, 59, 58, 186, 68, 583) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_iir_direct_006(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + expected_result = (2, 13, 21, 59, 58, 186, 68, 583) + fftaps = (2, 1) + fbtaps = (0, -1) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + fftaps = (2, 11, 0) + fbtaps = (0, -1, 3) + op.set_taps(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_iir_direct_007(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + expected_result = (2,2,5,5,8,8,11,11) + fftaps = (2, 1) + fbtaps = (0, -1) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + fftaps = (2,0,1) + fbtaps = (0, -1) + op.set_taps(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual(expected_result, result_data) + + def test_iir_direct_008(self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + expected_result = (2,4,4,10,18,14,26,56) + fftaps = (2,) + fbtaps = (0, 1) + src = gr.vector_source_f(src_data) + op = filter.iir_filter_ffd(fftaps, fbtaps) + fftaps_data = (1) + fbtaps = (0,0, -1,3) + op.set_taps(fftaps, fbtaps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + +if __name__ == '__main__': + gr_unittest.run(test_iir_filter, "test_iir_filter.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 8585bda32..be5338f4b 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -43,6 +43,7 @@ #include "filter/fft_filter_ccc.h" #include "filter/fft_filter_fff.h" #include "filter/hilbert_fc.h" +#include "filter/iir_filter_ffd.h" #include "filter/pfb_channelizer_ccf.h" #include "filter/single_pole_iir_filter_cc.h" #include "filter/single_pole_iir_filter_ff.h" @@ -63,6 +64,7 @@ %include "filter/fft_filter_ccc.h" %include "filter/fft_filter_fff.h" %include "filter/hilbert_fc.h" +%include "filter/iir_filter_ffd.h" %include "filter/pfb_channelizer_ccf.h" %include "filter/single_pole_iir_filter_cc.h" %include "filter/single_pole_iir_filter_ff.h" @@ -80,6 +82,7 @@ GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, hilbert_fc); +GR_SWIG_BLOCK_MAGIC2(filter, iir_filter_ffd); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_cc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_ff); -- cgit From d647fbbaa07554ad314c9eb2b5c1735b6265d13a Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sat, 16 Jun 2012 10:55:36 -0400 Subject: filter: adding freq_xlating_filter. Includes QA and GRC. Update to build_utils adds a FIR_TYPE for the type of filter and CFIR_TYPE for the type of filter where the taps are always complex (needed for freq_xlating_filter). --- gnuradio-core/src/python/build_utils.py | 2 + gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/freq_xlating_fir_filter_xxx.xml | 93 +++++ gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/fir_filter.h | 31 ++ .../include/filter/freq_xlating_fir_filter_XXX.h.t | 90 +++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/fir_filter.cc | 108 +++++ .../lib/freq_xlating_fir_filter_XXX_impl.cc.t | 143 +++++++ gr-filter/lib/freq_xlating_fir_filter_XXX_impl.h.t | 71 ++++ gr-filter/python/qa_freq_xlating_fir_filter.py | 445 +++++++++++++++++++++ gr-filter/swig/filter_swig.i | 18 + 13 files changed, 1005 insertions(+) create mode 100644 gr-filter/grc/freq_xlating_fir_filter_xxx.xml create mode 100644 gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t create mode 100644 gr-filter/lib/freq_xlating_fir_filter_XXX_impl.cc.t create mode 100644 gr-filter/lib/freq_xlating_fir_filter_XXX_impl.h.t create mode 100644 gr-filter/python/qa_freq_xlating_fir_filter.py diff --git a/gnuradio-core/src/python/build_utils.py b/gnuradio-core/src/python/build_utils.py index 0898f46c4..e468b9bd1 100644 --- a/gnuradio-core/src/python/build_utils.py +++ b/gnuradio-core/src/python/build_utils.py @@ -214,6 +214,8 @@ def standard_impl_dict2 (name, code3, package): d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' d['COPYRIGHT'] = copyright + d['FIR_TYPE'] = "fir_filter_" + code3 + d['CFIR_TYPE'] = "fir_filter_" + code3[0:2] + 'c' d['TYPE'] = i_type (code3) d['I_TYPE'] = i_type (code3) d['O_TYPE'] = o_type (code3) diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 63434fe75..531dc5271 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -23,6 +23,7 @@ install(FILES fft_filter_xxx.xml fir_filter_xxx.xml filter_delay_fc.xml + freq_xlating_fir_filter_xxx.xml hilbert_fc.xml iir_filter_ffd.xml pfb_channelizer.xml diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 2ae4eb628..8ab43644b 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -34,6 +34,7 @@ fft_filter_xxx fir_filter_xxx filter_delay_fc + freq_xlating_fir_filter_xxx hilbert_fc iir_filter_ffd pfb_channelizer_ccf diff --git a/gr-filter/grc/freq_xlating_fir_filter_xxx.xml b/gr-filter/grc/freq_xlating_fir_filter_xxx.xml new file mode 100644 index 000000000..178a42f48 --- /dev/null +++ b/gr-filter/grc/freq_xlating_fir_filter_xxx.xml @@ -0,0 +1,93 @@ + + + + Frequency Xlating FIR Filter + freq_xlating_fir_filter_xxx + from gnuradio import filter + from gnuradio.filter import firdes + filter.freq_xlating_fir_filter_$(type)($decim, $taps, $center_freq, $samp_rate) + set_taps($taps) + set_center_freq($center_freq) + + Type + type + enum + + + + + + + + + Decimation + decim + 1 + int + + + Taps + taps + $type.taps + + + Center Frequency + center_freq + 0 + real + + + Sample Rate + samp_rate + samp_rate + real + + + in + $type.input + + + out + $type.output + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index ca8c7bf07..5da171ffa 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -65,6 +65,7 @@ endmacro(expand_h) # Invoke macro to generate various sources ####################################################################### expand_h(fir_filter_XXX fff ccf ccc scc fsf) +expand_h(freq_xlating_fir_filter_XXX ccc ccf fcc fcf scc scf) add_custom_target(filter_generated_includes DEPENDS ${generated_includes} diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index ba82c7f48..42b4ac2e0 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -93,6 +93,37 @@ namespace gr { /**************************************************************/ + class FILTER_API fir_filter_fcc + { + public: + fir_filter_fcc(int decimation, + const std::vector &taps); + ~fir_filter_fcc(); + + void set_taps(const std::vector &taps); + std::vector taps() const; + unsigned int ntaps() const; + + gr_complex filter(const float input[]); + void filterN(gr_complex output[], + const float input[], + unsigned long n); + void filterNdec(gr_complex output[], + const float input[], + unsigned long n, + unsigned int decimate); + + protected: + std::vector d_taps; + unsigned int d_ntaps; + gr_complex **d_aligned_taps; + gr_complex *d_output; + int d_align; + int d_naligned; + }; + + /**************************************************************/ + class FILTER_API fir_filter_ccc { public: diff --git a/gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t b/gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t new file mode 100644 index 000000000..d56303461 --- /dev/null +++ b/gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * WARNING: This file is automatically generated by cmake. + * Any changes made to this file will be overwritten. + */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include +#include + +namespace gr { + namespace filter { + + + /*! + * \brief FIR filter combined with frequency translation with + * @I_TYPE@ input, @O_TYPE@ output and @TAP_TYPE@ taps \ingroup + * filter_blk + * + * This class efficiently combines a frequency translation + * (typically "down conversion") with a FIR filter (typically + * low-pass) and decimation. It is ideally suited for a "channel + * selection filter" and can be efficiently used to select and + * decimate a narrow band signal out of wide bandwidth input. + * + * Uses a single input array to produce a single output array. + * Additional inputs and/or outputs are ignored. + */ + class FILTER_API @BASE_NAME@ : virtual public gr_sync_decimator + { + public: + // gr::filter::@BASE_NAME@::sptr + typedef boost::shared_ptr<@BASE_NAME@> sptr; + + /*! + * \brief FIR filter with @I_TYPE@ input, @O_TYPE@ output, and + * @TAP_TYPE@ taps that also frequency translates a signal from + * \p center_freq. + * \ingroup filter_blk + * + * Construct a FIR filter with the given taps and a composite + * frequency translation that shifts center_freq down to zero + * Hz. The frequency translation logically comes before the + * filtering operation. + * + * \param decimation set the integer decimation rate + * \param taps a vector/list of taps of type @TAP_TYPE@ + * \param center_freq Center frequency of signal to down convert from (Hz) + * \param sampling_freq Sampling rate of signal (in Hz) + */ + static FILTER_API sptr make(int decimation, + const std::vector<@TAP_TYPE@> &taps, + double center_freq, + double sampling_freq); + + virtual void set_center_freq(double center_freq) = 0; + virtual double center_freq() const = 0; + + virtual void set_taps(const std::vector<@TAP_TYPE@> &taps) = 0; + virtual std::vector<@TAP_TYPE@> taps() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 7a8b5aa0b..055e3da90 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -81,6 +81,7 @@ endmacro(expand_cc) # Invoke macro to generate various sources ######################################################################## expand_cc(fir_filter_XXX_impl fff ccf ccc scc fsf) +expand_cc(freq_xlating_fir_filter_XXX_impl ccc ccf fcc fcf scc scf) ######################################################################## diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 6c82dda5e..7ca1fae19 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -238,6 +238,114 @@ namespace gr { } } + + /**************************************************************/ + + + fir_filter_fcc::fir_filter_fcc(int decimation, + const std::vector &taps) + { + d_align = volk_get_alignment(); + d_naligned = d_align / sizeof(float); + + d_aligned_taps = NULL; + set_taps(taps); + + // Make sure the output sample is always aligned, too. + d_output = fft::malloc_complex(1); + } + + fir_filter_fcc::~fir_filter_fcc() + { + // Free all aligned taps + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } + + // Free output sample + fft::free(d_output); + } + + void + fir_filter_fcc::set_taps(const std::vector &taps) + { + // Free the taps if already allocated + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } + + d_ntaps = (int)taps.size(); + d_taps = taps; + std::reverse(d_taps.begin(), d_taps.end()); + + // Make a set of taps at all possible arch alignments + d_aligned_taps = (gr_complex**)malloc(d_naligned*sizeof(gr_complex**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_complex(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(gr_complex)*(d_ntaps+d_naligned-1)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; + } + } + + std::vector + fir_filter_fcc::taps() const + { + std::vector t = d_taps; + std::reverse(t.begin(), t.end()); + return t; + } + + unsigned int + fir_filter_fcc::ntaps() const + { + return d_ntaps; + } + + gr_complex + fir_filter_fcc::filter(const float input[]) + { + const float *ar = (float *)((unsigned long) input & ~(d_align-1)); + unsigned al = input - ar; + + volk_32fc_32f_dot_prod_32fc_a(d_output, + d_aligned_taps[al], + ar, + (d_ntaps+al)); + return *d_output; + } + + void + fir_filter_fcc::filterN(gr_complex output[], + const float input[], + unsigned long n) + { + for(unsigned long i = 0; i < n; i++) + output[i] = filter(&input[i]); + } + + + void + fir_filter_fcc::filterNdec(gr_complex output[], + const float input[], + unsigned long n, + unsigned int decimate) + { + unsigned long j = 0; + for(unsigned long i = 0; i < n; i++){ + output[i] = filter(&input[j]); + j += decimate; + } + } + /**************************************************************/ fir_filter_ccc::fir_filter_ccc(int decimation, diff --git a/gr-filter/lib/freq_xlating_fir_filter_XXX_impl.cc.t b/gr-filter/lib/freq_xlating_fir_filter_XXX_impl.cc.t new file mode 100644 index 000000000..a59fa12b3 --- /dev/null +++ b/gr-filter/lib/freq_xlating_fir_filter_XXX_impl.cc.t @@ -0,0 +1,143 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * WARNING: This file is automatically generated by cmake. + * Any changes made to this file will be overwritten. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "@IMPL_NAME@.h" +#include +#include + +namespace gr { + namespace filter { + + @BASE_NAME@::sptr + @BASE_NAME@::make(int decimation, + const std::vector<@TAP_TYPE@> &taps, + double center_freq, + double sampling_freq) + { + return gnuradio::get_initial_sptr(new @IMPL_NAME@ + (decimation, taps, + center_freq, + sampling_freq)); + } + + @IMPL_NAME@::@IMPL_NAME@(int decimation, + const std::vector<@TAP_TYPE@> &taps, + double center_freq, + double sampling_freq) + : gr_sync_decimator("@BASE_NAME@", + gr_make_io_signature(1, 1, sizeof(@I_TYPE@)), + gr_make_io_signature(1, 1, sizeof(@O_TYPE@)), + decimation), + d_proto_taps(taps), d_center_freq(center_freq), + d_sampling_freq(sampling_freq), + d_updated(false) + { + std::vector dummy_taps; + d_composite_fir = new kernel::@CFIR_TYPE@(decimation, dummy_taps); + + set_history(d_proto_taps.size()); + build_composite_fir(); + } + + @IMPL_NAME@::~@IMPL_NAME@() + { + delete d_composite_fir; + } + + void + @IMPL_NAME@::build_composite_fir() + { + std::vector ctaps(d_proto_taps.size()); + + float fwT0 = -2 * M_PI * d_center_freq / d_sampling_freq; + for(unsigned int i = 0; i < d_proto_taps.size(); i++) { + ctaps[i] = d_proto_taps[i] * exp(gr_complex(0, i * fwT0)); + } + + std::reverse(ctaps.begin(), ctaps.end()); + d_composite_fir->set_taps(ctaps); + d_r.set_phase_incr(exp(gr_complex(0, fwT0 * decimation()))); + } + + void + @IMPL_NAME@::set_center_freq(double center_freq) + { + d_center_freq = center_freq; + d_updated = true; + } + + double + @IMPL_NAME@::center_freq() const + { + return d_center_freq; + } + + void + @IMPL_NAME@::set_taps(const std::vector<@TAP_TYPE@> &taps) + { + d_proto_taps = taps; + d_updated = true; + } + + std::vector<@TAP_TYPE@> + @IMPL_NAME@::taps() const + { + return d_proto_taps; + } + + int + @IMPL_NAME@::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + @I_TYPE@ *in = (@I_TYPE@ *)input_items[0]; + @O_TYPE@ *out = (@O_TYPE@ *)output_items[0]; + + // rebuild composite FIR if the center freq has changed + if(d_updated) { + set_history(d_proto_taps.size()); + build_composite_fir(); + d_updated = false; + return 0; // history requirements may have changed. + } + + unsigned j = 0; + for (int i = 0; i < noutput_items; i++){ + out[i] = d_r.rotate(d_composite_fir->filter(&in[j])); + j += decimation(); + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ + diff --git a/gr-filter/lib/freq_xlating_fir_filter_XXX_impl.h.t b/gr-filter/lib/freq_xlating_fir_filter_XXX_impl.h.t new file mode 100644 index 000000000..0cf976d58 --- /dev/null +++ b/gr-filter/lib/freq_xlating_fir_filter_XXX_impl.h.t @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * WARNING: This file is automatically generated by cmake. + * Any changes made to this file will be overwritten. + */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API @IMPL_NAME@ : public @BASE_NAME@ + { + protected: + std::vector<@TAP_TYPE@> d_proto_taps; + kernel::@CFIR_TYPE@ *d_composite_fir; + gr_rotator d_r; + double d_center_freq; + double d_sampling_freq; + bool d_updated; + + virtual void build_composite_fir(); + public: + + @IMPL_NAME@(int decimation, + const std::vector<@TAP_TYPE@> &taps, + double center_freq, + double sampling_freq); + virtual ~@IMPL_NAME@(); + + void set_center_freq(double center_freq); + double center_freq() const; + + void set_taps(const std::vector<@TAP_TYPE@> &taps); + std::vector<@TAP_TYPE@> taps() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/python/qa_freq_xlating_fir_filter.py b/gr-filter/python/qa_freq_xlating_fir_filter.py new file mode 100644 index 000000000..ee38eb7df --- /dev/null +++ b/gr-filter/python/qa_freq_xlating_fir_filter.py @@ -0,0 +1,445 @@ +#!/usr/bin/env python +# +# Copyright 2008,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import cmath, math + +class test_freq_xlating_filter(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block () + + def tearDown(self): + self.tb = None + + def generate_ccf_source(self): + self.fs = fs = 1 + self.fc = fc = 0.3 + self.bw = bw = 0.1 + self.taps = filter.firdes.low_pass(1, fs, bw, bw/4) + times = xrange(100) + self.src_data = map(lambda t: cmath.exp(-2j*cmath.pi*fc/fs*(t/100.0)), times) + + def generate_ccc_source(self): + self.fs = fs = 1 + self.fc = fc = 0.3 + self.bw = bw = 0.1 + self.taps = filter.firdes.complex_band_pass(1, fs, -bw/2, bw/2, bw/4) + times = xrange(100) + self.src_data = map(lambda t: cmath.exp(-2j*cmath.pi*fc/fs*(t/100.0)), times) + + def generate_fcf_source(self): + self.fs = fs = 1 + self.fc = fc = 0.3 + self.bw = bw = 0.1 + self.taps = filter.firdes.low_pass(1, fs, bw, bw/4) + times = xrange(100) + self.src_data = map(lambda t: math.sin(2*cmath.pi*fc/fs*(t/100.0)), times) + + def generate_fcc_source(self): + self.fs = fs = 1 + self.fc = fc = 0.3 + self.bw = bw = 0.1 + self.taps = filter.firdes.complex_band_pass(1, fs, -bw/2, bw/2, bw/4) + times = xrange(100) + self.src_data = map(lambda t: math.sin(2*cmath.pi*fc/fs*(t/100.0)), times) + + def generate_scf_source(self): + self.fs = fs = 1 + self.fc = fc = 0.3 + self.bw = bw = 0.1 + self.taps = filter.firdes.low_pass(1, fs, bw, bw/4) + times = xrange(100) + self.src_data = map(lambda t: int(100*math.sin(2*cmath.pi*fc/fs*(t/100.0))), times) + + def generate_scc_source(self): + self.fs = fs = 1 + self.fc = fc = 0.3 + self.bw = bw = 0.1 + self.taps = filter.firdes.complex_band_pass(1, fs, -bw/2, bw/2, bw/4) + times = xrange(100) + self.src_data = map(lambda t: int(100*math.sin(2*cmath.pi*fc/fs*(t/100.0))), times) + + + def test_fir_filter_ccf_001(self): + self.generate_ccf_source() + expected_data = ((0.001697700354270637+0.004312471952289343j), + (0.003520616563037038-0.003014103975147009j), + (0.004252811893820763-0.008337559178471565j), + (0.0030743128154426813-0.010262271389365196j), + (0.0007344777695834637-0.007861139252781868j), + (-0.0011067686136811972-0.0028924935031682253j), + (-0.002371778478845954+0.0019914964213967323j), + (-0.003023319412022829+0.005717850290238857j), + (-0.0021738125942647457+0.007211698684841394j), + (-0.0004628606839105487+0.005501383915543556j), + (0.0007428556564264+0.0019867848604917526j), + (0.001634795218706131-0.0013514887541532516j), + (0.002205110155045986-0.00402155052870512j), + (0.0015480631263926625-0.005179159343242645j), + (0.00026722141774371266-0.003887997241690755j), + (-0.0004911854630336165-0.0013578246580436826j), + (-0.0011226939968764782+0.0009080552263185382j), + (-0.0016229727771133184+0.0028335191309452057j), + (-0.0010890064295381308+0.0037298379465937614j), + (-0.00012392725329846144+0.0027196139562875032j)) + src = gr.vector_source_c(self.src_data) + op = filter.freq_xlating_fir_filter_ccf(1, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_ccf_002(self): + self.generate_ccf_source() + expected_data = ((6.419439159799367e-05-0.0006292851758189499j), + (-0.00037074743886478245+0.0013245552545413375j), + (0.0006853155209682882-0.0023769831750541925j), + (-0.001427714480087161+0.002608160488307476j), + (0.0015907397028058767-0.000811046629678458j), + (-0.0004226673918310553-0.0024389736354351044j), + (-0.0013841050677001476+0.006231029983609915j), + (0.0035029184073209763-0.009738259017467499j), + (-0.005924836732447147+0.010320881381630898j), + (0.006831614300608635-0.003950652200728655j), + (-0.0021247887052595615-0.015604906715452671j), + (-0.04283163696527481+0.09995654970407486j), + (-0.01391829177737236+0.07924056798219681j), + (0.010886997915804386-0.02463012933731079j), + (-0.0056075905449688435+0.004998659715056419j), + (0.0016976913902908564+0.004312459379434586j), + (0.0007344821933656931-0.007861112244427204j), + (-0.002173811662942171+0.007211671676486731j), + (0.0022051059640944004-0.00402153329923749j), + (-0.0011226903880015016+0.0009080505697056651j)) + src = gr.vector_source_c(self.src_data) + op = filter.freq_xlating_fir_filter_ccf(4, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_ccc_001(self): + self.generate_ccc_source() + expected_data = ((0.0036842757836-0.0114002721384j), + (0.00324621866457-0.0108166672289j), + (0.00206564785913-0.00923090614378j), + (0.00109899020754-0.00656201224774j), + (0.000506619049702-0.00402844604105j), + (-0.000523390364833-0.00166808743961j), + (-0.00140534969978+0.00103991874494j), + (-0.00154365820345+0.00315759982914j), + (-0.00180402118713+0.00427215453237j), + (-0.00216706306674+0.00524478312582j), + (-0.00178848754149+0.0057489364408j), + (-0.00129876169376+0.00512680830434j), + (-0.00122803379782+0.00427244976163j), + (-0.000722666736692+0.00351428100839j), + (5.53092104383e-05+0.00207865727134j), + (0.000227351076319+0.000517217209563j), + (0.000414477253798-0.000383921898901j), + (0.000998671515845-0.00135387131013j), + (0.00104933069088-0.00243046949618j), + (0.000765930046327-0.0026717747096j)) + src = gr.vector_source_c(self.src_data) + op = filter.freq_xlating_fir_filter_ccc(1, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_ccc_002(self): + self.generate_ccc_source() + expected_data = ((-0.000650451984257+0.00120697380044j), + (-9.59713361226e-05+0.00102412770502j), + (0.000958710326813-0.00145424995571j), + (0.000901343999431-0.00290832063183j), + (-0.000822560978122+0.000296717538731j), + (-0.00211223773658+0.00519825471565j), + (-0.00037001183955+0.00358242215589j), + (0.00327983591706-0.00616005761549j), + (0.00356886954978-0.0117237549275j), + (-0.00328874029219+0.00182871113066j), + (-0.0139285130426+0.0320657044649j), + (-0.0198133718222+0.0562113076448j), + (-0.0157803222537+0.0530290603638j), + (-0.00550725404173+0.0255754813552j), + (0.00252919178456-0.00232240976766j), + (0.00368427345529-0.0114002330229j), + (0.000506620621309-0.00402843113989j), + (-0.00180401885882+0.00427213776857j), + (-0.00122803344857+0.00427243299782j), + (0.000414476031438-0.000383919978049j)) + src = gr.vector_source_c(self.src_data) + op = filter.freq_xlating_fir_filter_ccc(4, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_fcf_001(self): + self.generate_fcf_source() + expected_data = ((0.000247188087087-0.000157509770361j), + (-0.000155229790835-0.000246197130764j), + (-0.000264906557277+0.000174603672349j), + (6.99016964063e-05+0.000174961372977j), + (-5.48477692064e-05-0.0001131295503j), + (-0.000237467131228+0.000118011368613j), + (0.000136614587973+0.000229531884543j), + (0.000229347482673-0.000166581812664j), + (-0.000106010869786-0.000150042149471j), + (2.92293734674e-05+0.000142060467624j), + (0.000228707227507-9.30760797928e-05j), + (-0.000124306126963-0.000216641055886j), + (-0.000204823678359+0.00016052465071j), + (0.00012825592421+0.000133123627165j), + (-1.18284006021e-05-0.000159015646204j), + (-0.000219973371713+7.5438656495e-05j), + (0.000114713984658+0.000205190401175j), + (0.000185727752978-0.000154630601173j), + (-0.000141745767905-0.000120098840853j), + (-3.9850056055e-07+0.000168364742422j)) + src = gr.vector_source_f(self.src_data) + op = filter.freq_xlating_fir_filter_fcf(1, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_fcf_002(self): + self.generate_fcf_source() + expected_data = ((7.3052920925e-05-3.91741014028e-06j), + (3.11913172482e-05-0.000109872074972j), + (-0.000128646017401-3.49857727997e-05j), + (-5.49546712136e-05+8.96326746442e-05j), + (5.14321582159e-05+9.64698920143e-05j), + (0.000120189361041+2.4231892894e-05j), + (0.000100405508419-0.000223224604269j), + (-0.000274751859251-2.33274622587e-05j), + (1.52600114234e-06+0.000133301247843j), + (3.77224641852e-05+5.29596509296e-05j), + (-3.60160379387e-06+0.000247975171078j), + (0.00113093166146-0.000663110695314j), + (0.00059568521101-0.00099650840275j), + (-0.000475480686873+0.000250602373853j), + (0.000191397906747+0.000271986238658j), + (0.000247183139436-0.000157510468853j), + (-5.48357638763e-05-0.000113135029096j), + (-0.00010601492977-0.00015005269961j), + (-0.000204817260965+0.000160534662427j), + (0.000114742244477+0.000205190313864j)) + src = gr.vector_source_f(self.src_data) + op = filter.freq_xlating_fir_filter_fcf(4, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_fcc_001(self): + self.generate_fcc_source() + expected_data = ((-0.000337088305969+7.46004516259e-05j), + (-5.63409266761e-05+0.000301144464174j), + (9.16960561881e-05-2.89259278361e-05j), + (-0.000231866899412-6.25764005235e-05j), + (-0.000116414688819+0.000258557556663j), + (0.000206079319469+5.05045172758e-05j), + (-3.85114690289e-05-0.00019276549574j), + (-0.000146380873048+0.000112079876999j), + (0.000215423395275+0.000116706331028j), + (0.000136050162837-0.000232611957472j), + (-0.000155499437824-5.41604022146e-05j), + (0.000106907449663+0.00016310159117j), + (0.000224392410018-0.000156331108883j), + (-0.000131131906528-0.000172063446371j), + (-5.92393880652e-05+0.00016801241145j), + (0.000214921761653-5.32235890205e-06j), + (-5.96960526309e-05-0.000211164733628j), + (-0.000193948610104+0.000113364716526j), + (0.000134820176754+0.000142527525895j), + (4.74465123261e-05-0.000175131688593j)) + src = gr.vector_source_f(self.src_data) + op = filter.freq_xlating_fir_filter_fcc(1, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_fcc_002(self): + self.generate_fcc_source() + expected_data = ((-6.94218761055e-05-9.90489479591e-06j), + (-2.56973435171e-05+8.05932795629e-05j), + (7.01698663761e-05+7.36373840482e-05j), + (7.57163215894e-05-4.65324592369e-05j), + (-3.01657128148e-05-0.000122838056996j), + (-9.53659764491e-05-3.73945695173e-05j), + (-2.33501577895e-05+0.000109135726234j), + (6.65136758471e-05+0.000125709688291j), + (3.08501912514e-05-9.16842873266e-06j), + (-2.64703612629e-05-0.000135892929393j), + (0.000136643866426-0.000162003751029j), + (0.000501801609062-0.000185820827028j), + (0.000694551155902-0.000299874518532j), + (0.000424396857852-0.00038379128091j), + (-9.1786707344e-05-0.000242479465669j), + (-0.000337087287335+7.45999423089e-05j), + (-0.000116414521472+0.000258556567132j), + (0.000215422536712+0.000116706112749j), + (0.000224391726078-0.000156330308528j), + (-5.96956087975e-05-0.000211163976928j)) + src = gr.vector_source_f(self.src_data) + op = filter.freq_xlating_fir_filter_fcc(4, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_scf_001(self): + self.generate_scf_source() + expected_data = ((-0.0330070219934+0.101965591311j), + (-0.0484239049256+0.0872343629599j), + (-0.0214109234512+0.102555386722j), + (0.0484058149159+0.0557125210762j), + (0.0574690811336-0.0350844524801j), + (0.0365394353867-0.0802438184619j), + (0.0453781634569-0.130992040038j), + (0.00801951438189-0.214278846979j), + (-0.0770946145058-0.230616629124j), + (-0.105601429939-0.190731987357j), + (-0.105361394584-0.177761554718j), + (-0.131518915296-0.136102750897j), + (-0.103761836886-0.0382263250649j), + (-0.0167790111154+0.0152206514031j), + (0.0277570039034+0.0300403907895j), + (0.056065287441+0.0806603953242j), + (0.118084669113+0.104863211513j), + (0.128281414509+0.0677760615945j), + (0.0748447552323+0.0619902014732j), + (0.0512856245041+0.0775099247694j)) + src = gr.vector_source_s(self.src_data) + op = filter.freq_xlating_fir_filter_scf(1, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_scf_002(self): + self.generate_scf_source() + expected_data = ((0.00824625696987-1.50158575707e-05j), + (0.0020101047121-0.0116540221497j), + (-0.0126378880814-0.00259830290452j), + (-0.00363933457993+0.00837504956871j), + (0.00107023562305+0.00915473792702j), + (0.0169738996774+0.00422182958573j), + (0.00630031805485-0.025423232466j), + (-0.0283014029264+0.00104465708137j), + (0.00890890974551+0.0115978596732j), + (-0.0142687577754+0.00306978379376j), + (0.02845691517+0.0331163145602j), + (0.0538152232766-0.0908300876617j), + (-0.0843691527843-0.0956566259265j), + (0.0476895272732+0.0747984498739j), + (0.0898786485195+0.082478672266j), + (-0.0330070182681+0.101965606213j), + (0.0574697069824-0.0350842289627j), + (-0.0770940706134-0.230615705252j), + (-0.103762261569-0.0382265634835j), + (0.11808334291+0.104863762856j)) + src = gr.vector_source_s(self.src_data) + op = filter.freq_xlating_fir_filter_scf(4, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_scc_001(self): + self.generate_scc_source() + expected_data = ((-0.00775564694777+0.0437113791704j), + (0.0108830630779+0.0433648750186j), + (0.015553932637-0.0133284125477j), + (-0.0264905355871-0.0403266139328j), + (-0.0243480335921-0.03030154109j), + (-0.000327925226884-0.069333948195j), + (-0.0306392069906-0.107313856483j), + (-0.0452371090651-0.0854917764664j), + (-0.0108894333243-0.0875641107559j), + (-0.0182112380862-0.118961036205j), + (-0.0447825863957-0.0922874584794j), + (-0.0147479763255-0.0572904124856j), + (0.00204290449619-0.0721436738968j), + (-0.027713002637-0.0548989400268j), + (-0.0149045493454-0.00210141134448j), + (0.0176361314952-0.00149522523861j), + (-0.00527482619509-0.00698099425063j), + (-0.0151527002454+0.036265052855j), + (0.0199296213686+0.0452499426901j), + (0.0122985243797+0.0143278446048j)) + src = gr.vector_source_s(self.src_data) + op = filter.freq_xlating_fir_filter_scc(1, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + + def test_fir_filter_scc_002(self): + self.generate_scc_source() + expected_data = ((-0.0080680437386-0.00158522999845j), + (-0.0037795654498+0.00733159901574j), + (0.00842926371843+0.00777021236718j), + (0.0112090632319-0.00249325321056j), + (-0.0027476802934-0.0115710813552j), + (-0.0158688724041-0.00715934624895j), + (-0.00802888441831+0.00620818417519j), + (0.0131985172629+0.0149175003171j), + (0.0190298333764+0.00956719089299j), + (-0.00112380902283-0.00936658866704j), + (-0.0204226914793-0.0333464704454j), + (-0.00695514678955-0.0437445007265j), + (0.0314490310848-0.0207983348519j), + (0.0529675260186+0.0302227605134j), + (0.0317338332534+0.0667510479689j), + (-0.00775565672666+0.0437112376094j), + (-0.024347923696-0.0303014591336j), + (-0.0108893103898-0.0875638127327j), + (0.00204296782613-0.0721434056759j), + (-0.00527479872108-0.00698097236454j)) + src = gr.vector_source_s(self.src_data) + op = filter.freq_xlating_fir_filter_scc(4, self.taps, self.fc, self.fs) + dst = gr.vector_sink_c() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_data, result_data[-20:], 5) + +if __name__ == '__main__': + gr_unittest.run(test_freq_xlating_filter, "test_freq_xlating_filter.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index be5338f4b..161fd4fe3 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -42,6 +42,12 @@ #include "filter/fir_filter_fsf.h" #include "filter/fft_filter_ccc.h" #include "filter/fft_filter_fff.h" +#include "filter/freq_xlating_fir_filter_ccc.h" +#include "filter/freq_xlating_fir_filter_ccf.h" +#include "filter/freq_xlating_fir_filter_fcc.h" +#include "filter/freq_xlating_fir_filter_fcf.h" +#include "filter/freq_xlating_fir_filter_scc.h" +#include "filter/freq_xlating_fir_filter_scf.h" #include "filter/hilbert_fc.h" #include "filter/iir_filter_ffd.h" #include "filter/pfb_channelizer_ccf.h" @@ -63,6 +69,12 @@ %include "filter/fir_filter_fsf.h" %include "filter/fft_filter_ccc.h" %include "filter/fft_filter_fff.h" +%include "filter/freq_xlating_fir_filter_ccc.h" +%include "filter/freq_xlating_fir_filter_ccf.h" +%include "filter/freq_xlating_fir_filter_fcc.h" +%include "filter/freq_xlating_fir_filter_fcf.h" +%include "filter/freq_xlating_fir_filter_scc.h" +%include "filter/freq_xlating_fir_filter_scf.h" %include "filter/hilbert_fc.h" %include "filter/iir_filter_ffd.h" %include "filter/pfb_channelizer_ccf.h" @@ -81,6 +93,12 @@ GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); +GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_fcc); +GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_fcf); +GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_scc); +GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_scf); GR_SWIG_BLOCK_MAGIC2(filter, hilbert_fc); GR_SWIG_BLOCK_MAGIC2(filter, iir_filter_ffd); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); -- cgit From ce3d887a47b47cb2d5351e14066ceb56b2b3c892 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 17 Jun 2012 16:44:04 -0400 Subject: filter: added mmse_fir_interpolator with QA code. --- gr-filter/include/filter/CMakeLists.txt | 2 + gr-filter/include/filter/fir_filter.h | 2 +- .../include/filter/mmse_fir_interpolator_cc.h | 79 +++++++++++++ .../include/filter/mmse_fir_interpolator_ff.h | 76 +++++++++++++ gr-filter/lib/CMakeLists.txt | 4 + gr-filter/lib/mmse_fir_interpolator_cc.cc | 77 +++++++++++++ gr-filter/lib/mmse_fir_interpolator_ff.cc | 77 +++++++++++++ gr-filter/lib/qa_filter.cc | 6 +- gr-filter/lib/qa_mmse_fir_interpolator_cc.cc | 125 +++++++++++++++++++++ gr-filter/lib/qa_mmse_fir_interpolator_cc.h | 48 ++++++++ gr-filter/lib/qa_mmse_fir_interpolator_ff.cc | 69 ++++++++++++ gr-filter/lib/qa_mmse_fir_interpolator_ff.h | 45 ++++++++ 12 files changed, 608 insertions(+), 2 deletions(-) create mode 100644 gr-filter/include/filter/mmse_fir_interpolator_cc.h create mode 100644 gr-filter/include/filter/mmse_fir_interpolator_ff.h create mode 100644 gr-filter/lib/mmse_fir_interpolator_cc.cc create mode 100644 gr-filter/lib/mmse_fir_interpolator_ff.cc create mode 100644 gr-filter/lib/qa_mmse_fir_interpolator_cc.cc create mode 100644 gr-filter/lib/qa_mmse_fir_interpolator_cc.h create mode 100644 gr-filter/lib/qa_mmse_fir_interpolator_ff.cc create mode 100644 gr-filter/lib/qa_mmse_fir_interpolator_ff.h diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 5da171ffa..3a1b5f150 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -82,6 +82,8 @@ install(FILES fft_filter.h iir_filter.h interpolator_taps.h + mmse_fir_interpolator_cc.h + mmse_fir_interpolator_ff.h pm_remez.h polyphase_filterbank.h single_pole_iir.h diff --git a/gr-filter/include/filter/fir_filter.h b/gr-filter/include/filter/fir_filter.h index 42b4ac2e0..e86111268 100644 --- a/gr-filter/include/filter/fir_filter.h +++ b/gr-filter/include/filter/fir_filter.h @@ -42,7 +42,7 @@ namespace gr { std::vector taps() const; unsigned int ntaps() const; - inline float filter(const float input[]); + float filter(const float input[]); void filterN(float output[], const float input[], unsigned long n); diff --git a/gr-filter/include/filter/mmse_fir_interpolator_cc.h b/gr-filter/include/filter/mmse_fir_interpolator_cc.h new file mode 100644 index 000000000..0436b4a0f --- /dev/null +++ b/gr-filter/include/filter/mmse_fir_interpolator_cc.h @@ -0,0 +1,79 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2007,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef _GRI_MMSE_FIR_INTERPOLATOR_CC_H_ +#define _GRI_MMSE_FIR_INTERPOLATOR_CC_H_ + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief Compute intermediate samples between signal samples x(k*Ts) + * \ingroup filter_primitive + * + * This implements a Mininum Mean Squared Error interpolator with + * 8 taps. It is suitable for signals where the bandwidth of + * interest B = 1/(4*Ts) Where Ts is the time between samples. + * + * Although mu, the fractional delay, is specified as a float, it + * is actually quantized. 0.0 <= mu <= 1.0. That is, mu is + * quantized in the interpolate method to 32nd's of a sample. + * + * For more information, in the GNU Radio source code, see: + * \li gnuradio-core/src/gen_interpolator_taps/README + * \li gnuradio-core/src/gen_interpolator_taps/praxis.txt + */ + + class FILTER_API mmse_fir_interpolator_cc + { + public: + mmse_fir_interpolator_cc(); + ~mmse_fir_interpolator_cc(); + + unsigned ntaps() const; + unsigned nsteps() const; + + /*! + * \brief compute a single interpolated output value. + * + * \p input must have ntaps() valid entries and be 8-byte aligned. + * input[0] .. input[ntaps() - 1] are referenced to compute the output value. + * \throws std::invalid_argument if input is not 8-byte aligned. + * + * \p mu must be in the range [0, 1] and specifies the fractional delay. + * + * \returns the interpolated input value. + */ + gr_complex interpolate(const gr_complex input[], float mu) const; + + protected: + std::vector filters; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* _MMSE_FIR_INTERPOLATOR_CC_H_ */ diff --git a/gr-filter/include/filter/mmse_fir_interpolator_ff.h b/gr-filter/include/filter/mmse_fir_interpolator_ff.h new file mode 100644 index 000000000..4353aa94f --- /dev/null +++ b/gr-filter/include/filter/mmse_fir_interpolator_ff.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _MMSE_FIR_INTERPOLATOR_FF_H_ +#define _MMSE_FIR_INTERPOLATOR_FF_H_ + +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief Compute intermediate samples between signal samples x(k*Ts) + * \ingroup filter_primitive + * + * This implements a Mininum Mean Squared Error interpolator with + * 8 taps. It is suitable for signals where the bandwidth of + * interest B = 1/(4*Ts) Where Ts is the time between samples. + * + * Although mu, the fractional delay, is specified as a float, it + * is actually quantized. 0.0 <= mu <= 1.0. That is, mu is + * quantized in the interpolate method to 32nd's of a sample. + * + * For more information, in the GNU Radio source code, see: + * \li gnuradio-core/src/gen_interpolator_taps/README + * \li gnuradio-core/src/gen_interpolator_taps/praxis.txt + */ + class FILTER_API mmse_fir_interpolator_ff + { + public: + mmse_fir_interpolator_ff(); + ~mmse_fir_interpolator_ff(); + + unsigned ntaps() const; + unsigned nsteps() const; + + /*! + * \brief compute a single interpolated output value. + * \p input must have ntaps() valid entries. + * input[0] .. input[ntaps() - 1] are referenced to compute the output value. + * + * \p mu must be in the range [0, 1] and specifies the fractional delay. + * + * \returns the interpolated input value. + */ + float interpolate(const float input[], float mu) const; + + protected: + std::vector filters; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* _MMSE_FIR_INTERPOLATOR_FF_H_ */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 055e3da90..4d950230a 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -110,6 +110,8 @@ list(APPEND filter_sources fir_filter_with_buffer.cc fft_filter.cc firdes.cc + mmse_fir_interpolator_cc.cc + mmse_fir_interpolator_ff.cc pm_remez.cc polyphase_filterbank.cc ${generated_sources} @@ -148,6 +150,8 @@ list(APPEND test_gr_filter_sources ${CMAKE_CURRENT_SOURCE_DIR}/test_gr_filter.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_filter.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_firdes.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_fir_interpolator_cc.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_fir_interpolator_ff.cc ) add_executable(test-gr-filter ${test_gr_filter_sources}) diff --git a/gr-filter/lib/mmse_fir_interpolator_cc.cc b/gr-filter/lib/mmse_fir_interpolator_cc.cc new file mode 100644 index 000000000..8af1fb39a --- /dev/null +++ b/gr-filter/lib/mmse_fir_interpolator_cc.cc @@ -0,0 +1,77 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +namespace gr { + namespace filter { + + mmse_fir_interpolator_cc::mmse_fir_interpolator_cc() + { + filters.resize (NSTEPS + 1); + + for(int i = 0; i < NSTEPS + 1; i++) { + std::vector t (&taps[i][0], &taps[i][NTAPS]); + filters[i] = new kernel::fir_filter_ccf(1, t); + } + } + + mmse_fir_interpolator_cc::~mmse_fir_interpolator_cc() + { + for(int i = 0; i < NSTEPS + 1; i++) + delete filters[i]; + } + + unsigned + mmse_fir_interpolator_cc::ntaps() const + { + return NTAPS; + } + + unsigned + mmse_fir_interpolator_cc::nsteps() const + { + return NSTEPS; + } + + gr_complex + mmse_fir_interpolator_cc::interpolate(const gr_complex input[], + float mu) const + { + int imu = (int)rint(mu * NSTEPS); + + if((imu < 0) || (imu > NSTEPS)) { + throw std::runtime_error("mmse_fir_interpolator_cc: imu out of bounds.\n"); + } + + gr_complex r = filters[imu]->filter(input); + return r; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/mmse_fir_interpolator_ff.cc b/gr-filter/lib/mmse_fir_interpolator_ff.cc new file mode 100644 index 000000000..ff2c4dd87 --- /dev/null +++ b/gr-filter/lib/mmse_fir_interpolator_ff.cc @@ -0,0 +1,77 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +namespace gr { + namespace filter { + + mmse_fir_interpolator_ff::mmse_fir_interpolator_ff() + { + filters.resize(NSTEPS + 1); + + for(int i = 0; i < NSTEPS + 1; i++) { + std::vector t(&taps[i][0], &taps[i][NTAPS]); + filters[i] = new kernel::fir_filter_fff(1, t); + } + } + + mmse_fir_interpolator_ff::~mmse_fir_interpolator_ff() + { + for(int i = 0; i < NSTEPS + 1; i++) + delete filters[i]; + } + + unsigned + mmse_fir_interpolator_ff::ntaps() const + { + return NTAPS; + } + + unsigned + mmse_fir_interpolator_ff::nsteps() const + { + return NSTEPS; + } + + float + mmse_fir_interpolator_ff::interpolate(const float input[], + float mu) const + { + int imu = (int)rint(mu * NSTEPS); + + if((imu < 0) || (imu > NSTEPS)) { + throw std::runtime_error("mmse_fir_interpolator_ff: imu out of bounds.\n"); + } + + float r = filters[imu]->filter(input); + return r; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/qa_filter.cc b/gr-filter/lib/qa_filter.cc index 86d4860de..8aa24651c 100644 --- a/gr-filter/lib/qa_filter.cc +++ b/gr-filter/lib/qa_filter.cc @@ -27,13 +27,17 @@ #include #include +#include +#include CppUnit::TestSuite * qa_gr_filter::suite () { CppUnit::TestSuite *s = new CppUnit::TestSuite ("gr-filter"); - s->addTest(gr::filter::qa_firdes::suite ()); + s->addTest(gr::filter::qa_firdes::suite()); + s->addTest(gr::filter::qa_mmse_fir_interpolator_cc::suite()); + s->addTest(gr::filter::qa_mmse_fir_interpolator_ff::suite()); return s; } diff --git a/gr-filter/lib/qa_mmse_fir_interpolator_cc.cc b/gr-filter/lib/qa_mmse_fir_interpolator_cc.cc new file mode 100644 index 000000000..5850cb86c --- /dev/null +++ b/gr-filter/lib/qa_mmse_fir_interpolator_cc.cc @@ -0,0 +1,125 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2007,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +namespace gr { + namespace filter { + +#define NELEM(x) (sizeof(x) / sizeof(x[0])) + + static float + test_fcn_sin(double index) + { + return (2 * sin (index * 0.25 * 2 * M_PI + 0.125 * M_PI) + + 3 * sin (index * 0.077 * 2 * M_PI + 0.3 * M_PI)); + } + + static float + test_fcn_cos(double index) + { + return (2 * cos (index * 0.25 * 2 * M_PI + 0.125 * M_PI) + + 3 * cos (index * 0.077 * 2 * M_PI + 0.3 * M_PI)); + } + + static gr_complex + test_fcn(double index) + { + return gr_complex(test_fcn_cos(index), test_fcn_sin(index)); + } + + + void + qa_mmse_fir_interpolator_cc::t1() + { + static const unsigned N = 100; + __GR_ATTR_ALIGNED(8) gr_complex input[N + 10]; + + for(unsigned i = 0; i < NELEM(input); i++) + input[i] = test_fcn((double) i); + + mmse_fir_interpolator_cc intr; + float inv_nsteps = 1.0 / intr.nsteps(); + + for(unsigned i = 0; i < N; i++) { + for(unsigned imu = 0; imu <= intr.nsteps (); imu += 1) { + gr_complex expected = test_fcn((i + 3) + imu * inv_nsteps); + gr_complex actual = intr.interpolate(&input[i], imu * inv_nsteps); + + CPPUNIT_ASSERT_COMPLEXES_EQUAL(expected, actual, 0.004); + // printf ("%9.6f %9.6f %9.6f\n", expected, actual, expected - actual); + } + } + } + + /* + * Force bad alignment and confirm that it raises an exception + */ + void + qa_mmse_fir_interpolator_cc::t2_body() + { + static const unsigned N = 100; + float float_input[2*(N+10) + 1]; + gr_complex *input; + + // We require that gr_complex be aligned on an 8-byte boundary. + // Ensure that we ARE NOT ;) + + if(((intptr_t) float_input & 0x7) == 0) + input = reinterpret_cast(&float_input[1]); + else + input = reinterpret_cast(&float_input[0]); + + for(unsigned i = 0; i < (N+10); i++) + input[i] = test_fcn((double) i); + + mmse_fir_interpolator_cc intr; + float inv_nsteps = 1.0 / intr.nsteps(); + + for(unsigned i = 0; i < N; i++) { + for(unsigned imu = 0; imu <= intr.nsteps (); imu += 1) { + gr_complex expected = test_fcn((i + 3) + imu * inv_nsteps); + gr_complex actual = intr.interpolate(&input[i], imu * inv_nsteps); + + CPPUNIT_ASSERT_COMPLEXES_EQUAL(expected, actual, 0.004); + // printf ("%9.6f %9.6f %9.6f\n", expected, actual, expected - actual); + } + } + } + + void + qa_mmse_fir_interpolator_cc::t2() + { + CPPUNIT_ASSERT_THROW(t2_body(), std::invalid_argument); + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/qa_mmse_fir_interpolator_cc.h b/gr-filter/lib/qa_mmse_fir_interpolator_cc.h new file mode 100644 index 000000000..a45965ca2 --- /dev/null +++ b/gr-filter/lib/qa_mmse_fir_interpolator_cc.h @@ -0,0 +1,48 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2007,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _QA_MMSE_FIR_INTERPOLATOR_CC_H_ +#define _QA_MMSE_FIR_INTERPOLATOR_CC_H_ + +#include +#include + +namespace gr { + namespace filter { + + class qa_mmse_fir_interpolator_cc : public CppUnit::TestCase + { + CPPUNIT_TEST_SUITE(qa_mmse_fir_interpolator_cc); + CPPUNIT_TEST(t1); + // CPPUNIT_TEST(t2); + CPPUNIT_TEST_SUITE_END(); + + private: + void t1(); + void t2(); + void t2_body(); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* _QA_MMSE_FIR_INTERPOLATOR_CC_H_ */ diff --git a/gr-filter/lib/qa_mmse_fir_interpolator_ff.cc b/gr-filter/lib/qa_mmse_fir_interpolator_ff.cc new file mode 100644 index 000000000..128b638b9 --- /dev/null +++ b/gr-filter/lib/qa_mmse_fir_interpolator_ff.cc @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +namespace gr { + namespace filter { + +#define NELEM(x) (sizeof(x) / sizeof(x[0])) + + static float + test_fcn(double index) + { + return (2 * sin(index * 0.25 * 2 * M_PI + 0.125 * M_PI) + + 3 * sin(index * 0.077 * 2 * M_PI + 0.3 * M_PI)); + } + + void + qa_mmse_fir_interpolator_ff::t1() + { + static const unsigned N = 100; + float input[N + 10]; + + for(unsigned i = 0; i < NELEM(input); i++) + input[i] = test_fcn((double) i); + + mmse_fir_interpolator_ff intr; + float inv_nsteps = 1.0 / intr.nsteps(); + + for(unsigned i = 0; i < N; i++) { + for(unsigned imu = 0; imu <= intr.nsteps (); imu += 1) { + float expected = test_fcn((i + 3) + imu * inv_nsteps); + float actual = intr.interpolate(&input[i], imu * inv_nsteps); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, 0.004); + // printf ("%9.6f %9.6f %9.6f\n", expected, actual, expected - actual); + } + } + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/qa_mmse_fir_interpolator_ff.h b/gr-filter/lib/qa_mmse_fir_interpolator_ff.h new file mode 100644 index 000000000..833ec173a --- /dev/null +++ b/gr-filter/lib/qa_mmse_fir_interpolator_ff.h @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _QA_MMSE_FIR_INTERPOLATOR_FF_H_ +#define _QA_MMSE_FIR_INTERPOLATOR_FF_H_ + +#include +#include + +namespace gr { + namespace filter { + + class qa_mmse_fir_interpolator_ff : public CppUnit::TestCase + { + CPPUNIT_TEST_SUITE(qa_mmse_fir_interpolator_ff); + CPPUNIT_TEST(t1); + CPPUNIT_TEST_SUITE_END(); + + private: + void t1(); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* _QA_MMSE_FIR_INTERPOLATOR_FF_H_ */ -- cgit From 765d82d839e1773c77556e5d73ab229cbe6b96dc Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 17 Jun 2012 16:48:35 -0400 Subject: filter: redoing hilbert and fir_delay to use normal filter calls since alignment is properly handled. --- gr-filter/lib/filter_delay_fc_impl.cc | 7 ++----- gr-filter/lib/hilbert_fc_impl.cc | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/gr-filter/lib/filter_delay_fc_impl.cc b/gr-filter/lib/filter_delay_fc_impl.cc index 279368664..3bb550864 100644 --- a/gr-filter/lib/filter_delay_fc_impl.cc +++ b/gr-filter/lib/filter_delay_fc_impl.cc @@ -78,7 +78,6 @@ namespace gr { float *in0 = (float *)input_items[0]; float *in1 = (float *)input_items[1]; gr_complex *out = (gr_complex *)output_items[0]; - float d_out; if(d_update) { d_fir->set_taps(d_taps); @@ -90,17 +89,15 @@ namespace gr { switch(input_items.size ()) { case 1: for(int i = 0; i < noutput_items; i++) { - d_fir->filterN(&d_out, &in0[i], 1); out[i] = gr_complex(in0[i + d_delay], - d_out); + d_fir->filter(&in0[i])); } break; case 2: for(int j = 0; j < noutput_items; j++) { - d_fir->filterN(&d_out, &in1[j], 1); out[j] = gr_complex(in0[j + d_delay], - d_out); + d_fir->filter(&in1[j])); } break; diff --git a/gr-filter/lib/hilbert_fc_impl.cc b/gr-filter/lib/hilbert_fc_impl.cc index 7aaa23a62..52b2dabae 100644 --- a/gr-filter/lib/hilbert_fc_impl.cc +++ b/gr-filter/lib/hilbert_fc_impl.cc @@ -63,12 +63,10 @@ namespace gr { { float *in = (float *)input_items[0]; gr_complex *out = (gr_complex *)output_items[0]; - float h_out; for(int i = 0; i < noutput_items; i++) { - d_hilb->filterN(&h_out, &in[i], 1); out[i] = gr_complex(in[i + d_ntaps/2], - h_out); + d_hilb->filter(&in[i])); } return noutput_items; -- cgit From 6b2dbab570adceb3a7fa29f298da24a5e53cbf64 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 17 Jun 2012 20:35:53 -0400 Subject: filter: fixed fir_filter_with_buffer and added QA code to check. --- gr-filter/include/filter/fir_filter_with_buffer.h | 6 +- gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/fir_filter.cc | 1 - gr-filter/lib/fir_filter_with_buffer.cc | 133 ++++--- gr-filter/lib/qa_filter.cc | 4 + gr-filter/lib/qa_fir_filter_with_buffer.cc | 408 ++++++++++++++++++++++ gr-filter/lib/qa_fir_filter_with_buffer.h | 94 +++++ 7 files changed, 604 insertions(+), 43 deletions(-) create mode 100644 gr-filter/lib/qa_fir_filter_with_buffer.cc create mode 100644 gr-filter/lib/qa_fir_filter_with_buffer.h diff --git a/gr-filter/include/filter/fir_filter_with_buffer.h b/gr-filter/include/filter/fir_filter_with_buffer.h index 2ccb74906..8b5d9e064 100644 --- a/gr-filter/include/filter/fir_filter_with_buffer.h +++ b/gr-filter/include/filter/fir_filter_with_buffer.h @@ -42,7 +42,7 @@ namespace gr { unsigned int d_ntaps; float *d_buffer; unsigned int d_idx; - float *d_aligned_taps; + float **d_aligned_taps; float *d_output; int d_align; int d_naligned; @@ -138,7 +138,7 @@ namespace gr { unsigned int d_ntaps; gr_complex *d_buffer; unsigned int d_idx; - gr_complex *d_aligned_taps; + gr_complex **d_aligned_taps; gr_complex *d_output; int d_align; int d_naligned; @@ -234,7 +234,7 @@ namespace gr { unsigned int d_ntaps; gr_complex *d_buffer; unsigned int d_idx; - float *d_aligned_taps; + float **d_aligned_taps; gr_complex *d_output; int d_align; int d_naligned; diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 4d950230a..97afe7139 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -150,6 +150,7 @@ list(APPEND test_gr_filter_sources ${CMAKE_CURRENT_SOURCE_DIR}/test_gr_filter.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_filter.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_firdes.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_fir_filter_with_buffer.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_fir_interpolator_cc.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_fir_interpolator_ff.cc ) diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 7ca1fae19..8e585f245 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -24,7 +24,6 @@ #include #include #include -#include namespace gr { namespace filter { diff --git a/gr-filter/lib/fir_filter_with_buffer.cc b/gr-filter/lib/fir_filter_with_buffer.cc index b1cc589a5..58ef67fb7 100644 --- a/gr-filter/lib/fir_filter_with_buffer.cc +++ b/gr-filter/lib/fir_filter_with_buffer.cc @@ -28,6 +28,7 @@ #include #include #include +#include namespace gr { namespace filter { @@ -54,8 +55,13 @@ namespace gr { } // Free aligned taps - fft::free(d_aligned_taps); - d_aligned_taps = NULL; + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } // Free output sample fft::free(d_output); @@ -71,20 +77,27 @@ namespace gr { // Free the taps if already allocated if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } fft::free(d_aligned_taps); d_aligned_taps = NULL; } - d_buffer = fft::malloc_float(d_ntaps); - d_ntaps = (int)taps.size(); d_taps = taps; std::reverse(d_taps.begin(), d_taps.end()); + d_buffer = fft::malloc_float(2*d_ntaps); + memset(d_buffer, 0, 2*d_ntaps*sizeof(float)); + // Allocate aligned taps - d_aligned_taps = fft::malloc_float(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_aligned_taps[i] = d_taps[i]; + d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } d_idx = 0; @@ -108,9 +121,12 @@ namespace gr { if(d_idx >= ntaps()) d_idx = 0; - volk_32f_x2_dot_prod_32f_a(d_output, d_buffer, - d_aligned_taps, - ntaps()); + const float *ar = (float*)((unsigned long)(&d_buffer[d_idx]) & ~(d_align-1)); + unsigned al = (&d_buffer[d_idx]) - ar; + + volk_32f_x2_dot_prod_32f_a(d_output, ar, + d_aligned_taps[al], + ntaps()+al); return *d_output; } @@ -128,9 +144,12 @@ namespace gr { d_idx = 0; } - volk_32f_x2_dot_prod_32f_a(d_output, d_buffer, - d_aligned_taps, - ntaps()); + const float *ar = (float*)((unsigned long)(&d_buffer[d_idx]) & ~(d_align-1)); + unsigned al = (&d_buffer[d_idx]) - ar; + + volk_32f_x2_dot_prod_32f_a(d_output, ar, + d_aligned_taps[al], + ntaps()+al); return *d_output; } @@ -182,8 +201,13 @@ namespace gr { } // Free aligned taps - fft::free(d_aligned_taps); - d_aligned_taps = NULL; + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } // Free output sample fft::free(d_output); @@ -199,20 +223,27 @@ namespace gr { // Free the taps if already allocated if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } fft::free(d_aligned_taps); d_aligned_taps = NULL; } - d_buffer = fft::malloc_complex(d_ntaps); - d_ntaps = (int)taps.size(); d_taps = taps; std::reverse(d_taps.begin(), d_taps.end()); + d_buffer = fft::malloc_complex(2*d_ntaps); + memset(d_buffer, 0, 2*d_ntaps*sizeof(gr_complex)); + // Allocate aligned taps - d_aligned_taps = fft::malloc_complex(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_aligned_taps[i] = d_taps[i]; + d_aligned_taps = (gr_complex**)malloc(d_naligned*sizeof(gr_complex**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_complex(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(gr_complex)*(d_ntaps+d_naligned-1)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } d_idx = 0; @@ -236,9 +267,12 @@ namespace gr { if(d_idx >= ntaps()) d_idx = 0; - volk_32fc_x2_dot_prod_32fc_a(d_output, d_buffer, - d_aligned_taps, - ntaps()); + const gr_complex *ar = (gr_complex *)((unsigned long)(&d_buffer[d_idx]) & ~(d_align-1)); + unsigned al = (&d_buffer[d_idx]) - ar; + + volk_32fc_x2_dot_prod_32fc_a(d_output, ar, + d_aligned_taps[al], + (ntaps()+al)*sizeof(gr_complex)); return *d_output; } @@ -256,9 +290,12 @@ namespace gr { d_idx = 0; } - volk_32fc_x2_dot_prod_32fc_a(d_output, d_buffer, - d_aligned_taps, - ntaps()); + const gr_complex *ar = (gr_complex *)((unsigned long)(&d_buffer[d_idx]) & ~(d_align-1)); + unsigned al = (&d_buffer[d_idx]) - ar; + + volk_32fc_x2_dot_prod_32fc_a(d_output, ar, + d_aligned_taps[al], + (ntaps()+al)*sizeof(gr_complex)); return *d_output; } @@ -310,8 +347,13 @@ namespace gr { } // Free aligned taps - fft::free(d_aligned_taps); - d_aligned_taps = NULL; + if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } + fft::free(d_aligned_taps); + d_aligned_taps = NULL; + } // Free output sample fft::free(d_output); @@ -327,20 +369,27 @@ namespace gr { // Free the taps if already allocated if(d_aligned_taps != NULL) { + for(int i = 0; i < d_naligned; i++) { + fft::free(d_aligned_taps[i]); + } fft::free(d_aligned_taps); d_aligned_taps = NULL; } - d_buffer = fft::malloc_complex(d_ntaps); - d_ntaps = (int)taps.size(); d_taps = taps; std::reverse(d_taps.begin(), d_taps.end()); + d_buffer = fft::malloc_complex(2*d_ntaps); + memset(d_buffer, 0, 2*d_ntaps*sizeof(gr_complex)); + // Allocate aligned taps - d_aligned_taps = fft::malloc_float(d_ntaps); - for(unsigned int i = 0; i < d_ntaps; i++) { - d_aligned_taps[i] = d_taps[i]; + d_aligned_taps = (float**)malloc(d_naligned*sizeof(float**)); + for(int i = 0; i < d_naligned; i++) { + d_aligned_taps[i] = fft::malloc_float(d_ntaps+d_naligned-1); + memset(d_aligned_taps[i], 0, sizeof(float)*(d_ntaps+d_naligned-1)); + for(unsigned int j = 0; j < d_ntaps; j++) + d_aligned_taps[i][i+j] = d_taps[j]; } d_idx = 0; @@ -364,9 +413,12 @@ namespace gr { if(d_idx >= ntaps()) d_idx = 0; - volk_32fc_32f_dot_prod_32fc_a(d_output, d_buffer, - d_aligned_taps, - ntaps()); + const gr_complex *ar = (gr_complex *)((unsigned long)(&d_buffer[d_idx]) & ~(d_align-1)); + unsigned al = (&d_buffer[d_idx]) - ar; + + volk_32fc_32f_dot_prod_32fc_a(d_output, ar, + d_aligned_taps[al], + ntaps()+al); return *d_output; } @@ -384,9 +436,12 @@ namespace gr { d_idx = 0; } - volk_32fc_32f_dot_prod_32fc_a(d_output, d_buffer, - d_aligned_taps, - ntaps()); + const gr_complex *ar = (gr_complex *)((unsigned long)(&d_buffer[d_idx]) & ~(d_align-1)); + unsigned al = (&d_buffer[d_idx]) - ar; + + volk_32fc_32f_dot_prod_32fc_a(d_output, ar, + d_aligned_taps[al], + ntaps()+al); return *d_output; } diff --git a/gr-filter/lib/qa_filter.cc b/gr-filter/lib/qa_filter.cc index 8aa24651c..b7760b19e 100644 --- a/gr-filter/lib/qa_filter.cc +++ b/gr-filter/lib/qa_filter.cc @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -36,6 +37,9 @@ qa_gr_filter::suite () CppUnit::TestSuite *s = new CppUnit::TestSuite ("gr-filter"); s->addTest(gr::filter::qa_firdes::suite()); + s->addTest(gr::filter::fff::qa_fir_filter_with_buffer_fff::suite()); + s->addTest(gr::filter::ccc::qa_fir_filter_with_buffer_ccc::suite()); + s->addTest(gr::filter::ccf::qa_fir_filter_with_buffer_ccf::suite()); s->addTest(gr::filter::qa_mmse_fir_interpolator_cc::suite()); s->addTest(gr::filter::qa_mmse_fir_interpolator_ff::suite()); diff --git a/gr-filter/lib/qa_fir_filter_with_buffer.cc b/gr-filter/lib/qa_fir_filter_with_buffer.cc new file mode 100644 index 000000000..c97e2479c --- /dev/null +++ b/gr-filter/lib/qa_fir_filter_with_buffer.cc @@ -0,0 +1,408 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + + +namespace gr { + namespace filter { + +#define MAX_DATA (16383) +#define ERR_DELTA (1e-5) + +#define NELEM(x) (sizeof(x) / sizeof(x[0])) + + static float + uniform() + { + return 2.0 * ((float) random() / RANDOM_MAX - 0.5); // uniformly (-1, 1) + } + + static void + random_floats(float *buf, unsigned n) + { + for(unsigned i = 0; i < n; i++) + buf[i] = (float)rint(uniform() * 32767); + } + + static void + random_complex(gr_complex *buf, unsigned n) + { + for(unsigned i = 0; i < n; i++) { + float re = rint(uniform () * MAX_DATA); + float im = rint(uniform () * MAX_DATA); + buf[i] = gr_complex(re, im); + } + } + + namespace fff { + + typedef float i_type; + typedef float o_type; + typedef float tap_type; + typedef float acc_type; + + using std::vector; + + static o_type + ref_dotprod(const i_type input[], const tap_type taps[], int ntaps) + { + acc_type sum = 0; + for (int i = 0; i < ntaps; i++) { + sum += input[i] * taps[i]; + } + return sum; + } + + void + qa_fir_filter_with_buffer_fff::t1() + { + test_decimate(1); + } + + void + qa_fir_filter_with_buffer_fff::t2() + { + test_decimate(2); + } + + void + qa_fir_filter_with_buffer_fff::t3() + { + test_decimate(5); + } + + // + // Test for ntaps in [0,9], and input lengths in [0,17]. + // This ensures that we are building the shifted taps correctly, + // and exercises all corner cases on input alignment and length. + // + void + qa_fir_filter_with_buffer_fff::test_decimate(unsigned int decimate) + { + const int MAX_TAPS = 29; + const int OUTPUT_LEN = 37; + const int INPUT_LEN = MAX_TAPS + OUTPUT_LEN; + + // Mem aligned buffer not really necessary, but why not? + i_type *input = fft::malloc_float(INPUT_LEN); + i_type *dline = fft::malloc_float(INPUT_LEN); + o_type *expected_output = fft::malloc_float(OUTPUT_LEN); + o_type *actual_output = fft::malloc_float(OUTPUT_LEN); + tap_type *taps = fft::malloc_float(MAX_TAPS); + + srandom(0); // we want reproducibility + memset(dline, 0, INPUT_LEN*sizeof(i_type)); + + for(int n = 0; n <= MAX_TAPS; n++) { + for(int ol = 0; ol <= OUTPUT_LEN; ol++) { + + // build random test case + random_floats(input, INPUT_LEN); + random_floats(taps, MAX_TAPS); + + // compute expected output values + memset(dline, 0, INPUT_LEN*sizeof(i_type)); + for(int o = 0; o < (int)(ol/decimate); o++) { + // use an actual delay line for this test + for(int dd = 0; dd < (int)decimate; dd++) { + for(int oo = INPUT_LEN-1; oo > 0; oo--) + dline[oo] = dline[oo-1]; + dline[0] = input[decimate*o+dd]; + } + expected_output[o] = ref_dotprod(dline, taps, n); + } + + // build filter + vector f1_taps(&taps[0], &taps[n]); + kernel::fir_filter_with_buffer_fff *f1 = \ + new kernel::fir_filter_with_buffer_fff(f1_taps); + + // zero the output, then do the filtering + memset(actual_output, 0, sizeof(actual_output)); + f1->filterNdec(actual_output, input, ol/decimate, decimate); + + // check results + // + // we use a sloppy error margin because on the x86 architecture, + // our reference implementation is using 80 bit floating point + // arithmetic, while the SSE version is using 32 bit float point + // arithmetic. + + for(int o = 0; o < (int)(ol/decimate); o++) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected_output[o], actual_output[o], + sqrt((float)n)*0.25*MAX_DATA*MAX_DATA * ERR_DELTA); + } + delete f1; + } + } + fft::free(input); + fft::free(dline); + fft::free(expected_output); + fft::free(actual_output); + fft::free(taps); + } + + } /* namespace fff */ + + + /**************************************************************/ + + + namespace ccc { + + typedef gr_complex i_type; + typedef gr_complex o_type; + typedef gr_complex tap_type; + typedef gr_complex acc_type; + + using std::vector; + + static o_type + ref_dotprod(const i_type input[], const tap_type taps[], int ntaps) + { + acc_type sum = 0; + for(int i = 0; i < ntaps; i++) { + sum += input[i] * taps[i]; + } + + return sum; + } + + void + qa_fir_filter_with_buffer_ccc::t1() + { + test_decimate(1); + } + + void + qa_fir_filter_with_buffer_ccc::t2() + { + test_decimate(2); + } + + void + qa_fir_filter_with_buffer_ccc::t3() + { + test_decimate(5); + } + + // + // Test for ntaps in [0,9], and input lengths in [0,17]. + // This ensures that we are building the shifted taps correctly, + // and exercises all corner cases on input alignment and length. + // + void + qa_fir_filter_with_buffer_ccc::test_decimate(unsigned int decimate) + { + const int MAX_TAPS = 29; + const int OUTPUT_LEN = 37; + const int INPUT_LEN = MAX_TAPS + OUTPUT_LEN; + + // Mem aligned buffer not really necessary, but why not? + i_type *input = fft::malloc_complex(INPUT_LEN); + i_type *dline = fft::malloc_complex(INPUT_LEN); + o_type *expected_output = fft::malloc_complex(OUTPUT_LEN); + o_type *actual_output = fft::malloc_complex(OUTPUT_LEN); + tap_type *taps = fft::malloc_complex(MAX_TAPS); + + srandom(0); // we want reproducibility + memset(dline, 0, INPUT_LEN*sizeof(i_type)); + + for(int n = 0; n <= MAX_TAPS; n++) { + for(int ol = 0; ol <= OUTPUT_LEN; ol++) { + + // build random test case + random_complex(input, INPUT_LEN); + random_complex(taps, MAX_TAPS); + + // compute expected output values + memset(dline, 0, INPUT_LEN*sizeof(i_type)); + for(int o = 0; o < (int)(ol/decimate); o++) { + // use an actual delay line for this test + for(int dd = 0; dd < (int)decimate; dd++) { + for(int oo = INPUT_LEN-1; oo > 0; oo--) + dline[oo] = dline[oo-1]; + dline[0] = input[decimate*o+dd]; + } + expected_output[o] = ref_dotprod(dline, taps, n); + } + + // build filter + vector f1_taps(&taps[0], &taps[n]); + kernel::fir_filter_with_buffer_ccc *f1 = \ + new kernel::fir_filter_with_buffer_ccc(f1_taps); + + // zero the output, then do the filtering + memset(actual_output, 0, sizeof(actual_output)); + f1->filterNdec(actual_output, input, ol/decimate, decimate); + + // check results + // + // we use a sloppy error margin because on the x86 architecture, + // our reference implementation is using 80 bit floating point + // arithmetic, while the SSE version is using 32 bit float point + // arithmetic. + + for(int o = 0; o < (int)(ol/decimate); o++) { + CPPUNIT_ASSERT_COMPLEXES_EQUAL(expected_output[o], actual_output[o], + sqrt((float)n)*0.25*MAX_DATA*MAX_DATA * ERR_DELTA); + } + delete f1; + } + } + fft::free(input); + fft::free(dline); + fft::free(expected_output); + fft::free(actual_output); + fft::free(taps); + } + + } /* namespace ccc */ + + + /**************************************************************/ + + namespace ccf { + + typedef gr_complex i_type; + typedef gr_complex o_type; + typedef float tap_type; + typedef gr_complex acc_type; + + using std::vector; + + static o_type + ref_dotprod(const i_type input[], const tap_type taps[], int ntaps) + { + acc_type sum = 0; + for(int i = 0; i < ntaps; i++) { + sum += input[i] * taps[i]; + } + + //return gr_complex(121,9)*sum; + return sum; + } + + void + qa_fir_filter_with_buffer_ccf::t1() + { + test_decimate(1); + } + + void + qa_fir_filter_with_buffer_ccf::t2() + { + test_decimate(2); + } + + void + qa_fir_filter_with_buffer_ccf::t3() + { + test_decimate(5); + } + + // + // Test for ntaps in [0,9], and input lengths in [0,17]. + // This ensures that we are building the shifted taps correctly, + // and exercises all corner cases on input alignment and length. + // + void + qa_fir_filter_with_buffer_ccf::test_decimate(unsigned int decimate) + { + const int MAX_TAPS = 29; + const int OUTPUT_LEN = 37; + const int INPUT_LEN = MAX_TAPS + OUTPUT_LEN; + + // Mem aligned buffer not really necessary, but why not? + i_type *input = fft::malloc_complex(INPUT_LEN); + i_type *dline = fft::malloc_complex(INPUT_LEN); + o_type *expected_output = fft::malloc_complex(OUTPUT_LEN); + o_type *actual_output = fft::malloc_complex(OUTPUT_LEN); + tap_type *taps = fft::malloc_float(MAX_TAPS); + + srandom(0); // we want reproducibility + memset(dline, 0, INPUT_LEN*sizeof(i_type)); + + for(int n = 0; n <= MAX_TAPS; n++) { + for(int ol = 0; ol <= OUTPUT_LEN; ol++) { + + // build random test case + random_complex(input, INPUT_LEN); + random_floats(taps, MAX_TAPS); + + // compute expected output values + memset(dline, 0, INPUT_LEN*sizeof(i_type)); + for(int o = 0; o < (int)(ol/decimate); o++) { + // use an actual delay line for this test + for(int dd = 0; dd < (int)decimate; dd++) { + for(int oo = INPUT_LEN-1; oo > 0; oo--) + dline[oo] = dline[oo-1]; + dline[0] = input[decimate*o+dd]; + } + expected_output[o] = ref_dotprod(dline, taps, n); + } + + // build filter + vector f1_taps(&taps[0], &taps[n]); + kernel::fir_filter_with_buffer_ccf *f1 = \ + new kernel::fir_filter_with_buffer_ccf(f1_taps); + + // zero the output, then do the filtering + memset(actual_output, 0, sizeof(actual_output)); + f1->filterNdec(actual_output, input, ol/decimate, decimate); + + // check results + // + // we use a sloppy error margin because on the x86 architecture, + // our reference implementation is using 80 bit floating point + // arithmetic, while the SSE version is using 32 bit float point + // arithmetic. + + for(int o = 0; o < (int)(ol/decimate); o++) { + CPPUNIT_ASSERT_COMPLEXES_EQUAL(expected_output[o], actual_output[o], + sqrt((float)n)*0.25*MAX_DATA*MAX_DATA * ERR_DELTA); + } + delete f1; + } + } + fft::free(input); + fft::free(dline); + fft::free(expected_output); + fft::free(actual_output); + fft::free(taps); + } + + } /* namespace ccf */ + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/qa_fir_filter_with_buffer.h b/gr-filter/lib/qa_fir_filter_with_buffer.h new file mode 100644 index 000000000..8850ada20 --- /dev/null +++ b/gr-filter/lib/qa_fir_filter_with_buffer.h @@ -0,0 +1,94 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef _QA_FIR_FILTER_WITH_BUFFER_H_ +#define _QA_FIR_FILTER_WITH_BUFFER_H_ + +#include +#include + +namespace gr { + namespace filter { + + namespace fff { + + class qa_fir_filter_with_buffer_fff : public CppUnit::TestCase + { + CPPUNIT_TEST_SUITE(qa_fir_filter_with_buffer_fff); + CPPUNIT_TEST(t1); + CPPUNIT_TEST(t2); + CPPUNIT_TEST(t3); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_decimate(unsigned int decimate); + + void t1(); + void t2(); + void t3(); + }; + + } /* namespace fff */ + + namespace ccc { + + class qa_fir_filter_with_buffer_ccc : public CppUnit::TestCase + { + CPPUNIT_TEST_SUITE(qa_fir_filter_with_buffer_ccc); + CPPUNIT_TEST(t1); + CPPUNIT_TEST(t2); + CPPUNIT_TEST(t3); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_decimate(unsigned int decimate); + + void t1(); + void t2(); + void t3(); + }; + + } /* namespace ccc */ + + namespace ccf { + + class qa_fir_filter_with_buffer_ccf : public CppUnit::TestCase + { + CPPUNIT_TEST_SUITE(qa_fir_filter_with_buffer_ccf); + CPPUNIT_TEST(t1); + CPPUNIT_TEST(t2); + CPPUNIT_TEST(t3); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_decimate(unsigned int decimate); + + void t1(); + void t2(); + void t3(); + }; + + } /* namespace ccf */ + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* _QA_FIR_FILTER_WITH_BUFFER_H_ */ -- cgit From f049a184c38a9344efea24ec813a13499237e23b Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Sun, 17 Jun 2012 21:49:35 -0400 Subject: filter: adding fractional_interpolator_{cc,ff} with QA and GRC. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/fractional_interpolator_xx.xml | 46 ++++++++ gr-filter/include/filter/CMakeLists.txt | 2 + .../include/filter/fractional_interpolator_cc.h | 54 +++++++++ .../include/filter/fractional_interpolator_ff.h | 54 +++++++++ gr-filter/lib/CMakeLists.txt | 2 + gr-filter/lib/fractional_interpolator_cc_impl.cc | 125 +++++++++++++++++++++ gr-filter/lib/fractional_interpolator_cc_impl.h | 61 ++++++++++ gr-filter/lib/fractional_interpolator_ff_impl.cc | 125 +++++++++++++++++++++ gr-filter/lib/fractional_interpolator_ff_impl.h | 61 ++++++++++ gr-filter/python/qa_fractional_interpolator.py | 39 +++++++ gr-filter/swig/filter_swig.i | 6 + 13 files changed, 577 insertions(+) create mode 100644 gr-filter/grc/fractional_interpolator_xx.xml create mode 100644 gr-filter/include/filter/fractional_interpolator_cc.h create mode 100644 gr-filter/include/filter/fractional_interpolator_ff.h create mode 100644 gr-filter/lib/fractional_interpolator_cc_impl.cc create mode 100644 gr-filter/lib/fractional_interpolator_cc_impl.h create mode 100644 gr-filter/lib/fractional_interpolator_ff_impl.cc create mode 100644 gr-filter/lib/fractional_interpolator_ff_impl.h create mode 100755 gr-filter/python/qa_fractional_interpolator.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 531dc5271..48d452d39 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -23,6 +23,7 @@ install(FILES fft_filter_xxx.xml fir_filter_xxx.xml filter_delay_fc.xml + fractional_interpolator_xx.xml freq_xlating_fir_filter_xxx.xml hilbert_fc.xml iir_filter_ffd.xml diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 8ab43644b..7f34a1b70 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -34,6 +34,7 @@ fft_filter_xxx fir_filter_xxx filter_delay_fc + fractional_interpolator_xx freq_xlating_fir_filter_xxx hilbert_fc iir_filter_ffd diff --git a/gr-filter/grc/fractional_interpolator_xx.xml b/gr-filter/grc/fractional_interpolator_xx.xml new file mode 100644 index 000000000..760e8bb06 --- /dev/null +++ b/gr-filter/grc/fractional_interpolator_xx.xml @@ -0,0 +1,46 @@ + + + + Fractional Interpolator + fractional_interpolator_xx + from gnuradio import filter + filter.fractional_interpolator_$(type.fcn)($phase_shift, $interp_ratio) + set_interp_ratio($interp_ratio) + + Type + type + enum + + + + + Phase Shift + phase_shift + real + + + Interpolation Ratio + interp_ratio + real + + + in + $type + + + out + $type + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 3a1b5f150..267b8b66b 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -95,6 +95,8 @@ install(FILES filter_delay_fc.h fft_filter_ccc.h fft_filter_fff.h + fractional_interpolator_cc.h + fractional_interpolator_ff.h hilbert_fc.h iir_filter_ffd.h pfb_channelizer_ccf.h diff --git a/gr-filter/include/filter/fractional_interpolator_cc.h b/gr-filter/include/filter/fractional_interpolator_cc.h new file mode 100644 index 000000000..e5d066bbc --- /dev/null +++ b/gr-filter/include/filter/fractional_interpolator_cc.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FRACTIONAL_INTERPOLATOR_CC_H +#define INCLUDED_FRACTIONAL_INTERPOLATOR_CC_H + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief Interpolating MMSE filter with complex input, complex output + * \ingroup filter_blk + */ + class FILTER_API fractional_interpolator_cc : virtual public gr_block + { + public: + // gr::filter::fractional_interpolator_cc::sptr + typedef boost::shared_ptr sptr; + + static FILTER_API sptr make(float phase_shift, + float interp_ratio); + + virtual float mu() const = 0; + virtual float interp_ratio() const = 0; + virtual void set_mu (float mu) = 0; + virtual void set_interp_ratio(float interp_ratio) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FRACTIONAL_INTERPOLATOR_FF_H */ diff --git a/gr-filter/include/filter/fractional_interpolator_ff.h b/gr-filter/include/filter/fractional_interpolator_ff.h new file mode 100644 index 000000000..8280baee3 --- /dev/null +++ b/gr-filter/include/filter/fractional_interpolator_ff.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FRACTIONAL_INTERPOLATOR_FF_H +#define INCLUDED_FRACTIONAL_INTERPOLATOR_FF_H + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief Interpolating MMSE filter with float input, float output + * \ingroup filter_blk + */ + class FILTER_API fractional_interpolator_ff : virtual public gr_block + { + public: + // gr::filter::fractional_interpolator_ff::sptr + typedef boost::shared_ptr sptr; + + static FILTER_API sptr make(float phase_shift, + float interp_ratio); + + virtual float mu() const = 0; + virtual float interp_ratio() const = 0; + virtual void set_mu (float mu) = 0; + virtual void set_interp_ratio(float interp_ratio) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FRACTIONAL_INTERPOLATOR_FF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 97afe7139..334720766 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -122,6 +122,8 @@ list(APPEND filter_sources filter_delay_fc_impl.cc fft_filter_ccc_impl.cc fft_filter_fff_impl.cc + fractional_interpolator_cc_impl.cc + fractional_interpolator_ff_impl.cc hilbert_fc_impl.cc iir_filter_ffd_impl.cc pfb_channelizer_ccf_impl.cc diff --git a/gr-filter/lib/fractional_interpolator_cc_impl.cc b/gr-filter/lib/fractional_interpolator_cc_impl.cc new file mode 100644 index 000000000..8fd89f437 --- /dev/null +++ b/gr-filter/lib/fractional_interpolator_cc_impl.cc @@ -0,0 +1,125 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "fractional_interpolator_cc_impl.h" +#include + +namespace gr { + namespace filter { + + fractional_interpolator_cc::sptr + fractional_interpolator_cc::make(float phase_shift, float interp_ratio) + { + return gnuradio::get_initial_sptr( + new fractional_interpolator_cc_impl(phase_shift, interp_ratio)); + } + + fractional_interpolator_cc_impl::fractional_interpolator_cc_impl + (float phase_shift, float interp_ratio) + : gr_block("fractional_interpolator_cc", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex))), + d_mu (phase_shift), d_mu_inc (interp_ratio), + d_interp(new mmse_fir_interpolator_cc()) + { + if(interp_ratio <= 0) + throw std::out_of_range("interpolation ratio must be > 0"); + if(phase_shift < 0 || phase_shift > 1) + throw std::out_of_range("phase shift ratio must be > 0 and < 1"); + + set_relative_rate(1.0 / interp_ratio); + } + + fractional_interpolator_cc_impl::~fractional_interpolator_cc_impl() + { + delete d_interp; + } + + void + fractional_interpolator_cc_impl::forecast(int noutput_items, + gr_vector_int &ninput_items_required) + { + unsigned ninputs = ninput_items_required.size(); + for(unsigned i=0; i < ninputs; i++) { + ninput_items_required[i] = + (int)ceil((noutput_items * d_mu_inc) + d_interp->ntaps()); + } + } + + int + fractional_interpolator_cc_impl::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + + int ii = 0; // input index + int oo = 0; // output index + + while(oo < noutput_items) { + out[oo++] = d_interp->interpolate(&in[ii], d_mu); + + double s = d_mu + d_mu_inc; + double f = floor(s); + int incr = (int)f; + d_mu = s - f; + ii += incr; + } + + consume_each(ii); + + return noutput_items; + } + + float + fractional_interpolator_cc_impl::mu() const + { + return d_mu; + } + + float + fractional_interpolator_cc_impl::interp_ratio() const + { + return d_mu_inc; + } + + void + fractional_interpolator_cc_impl::set_mu(float mu) + { + d_mu = mu; + } + + void + fractional_interpolator_cc_impl::set_interp_ratio(float interp_ratio) + { + d_mu_inc = interp_ratio; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fractional_interpolator_cc_impl.h b/gr-filter/lib/fractional_interpolator_cc_impl.h new file mode 100644 index 000000000..cb4218603 --- /dev/null +++ b/gr-filter/lib/fractional_interpolator_cc_impl.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FRACTIONAL_INTERPOLATOR_CC_IMPL_H +#define INCLUDED_FRACTIONAL_INTERPOLATOR_CC_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fractional_interpolator_cc_impl + : public fractional_interpolator_cc + { + private: + float d_mu; + float d_mu_inc; + mmse_fir_interpolator_cc *d_interp; + + public: + fractional_interpolator_cc_impl(float phase_shift, + float interp_ratio); + ~fractional_interpolator_cc_impl(); + + void forecast(int noutput_items, + gr_vector_int &ninput_items_required); + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + float mu() const; + float interp_ratio() const; + void set_mu(float mu); + void set_interp_ratio(float interp_ratio); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FRACTIONAL_INTERPOLATOR_CC_IMPL_H */ diff --git a/gr-filter/lib/fractional_interpolator_ff_impl.cc b/gr-filter/lib/fractional_interpolator_ff_impl.cc new file mode 100644 index 000000000..6b35dc106 --- /dev/null +++ b/gr-filter/lib/fractional_interpolator_ff_impl.cc @@ -0,0 +1,125 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "fractional_interpolator_ff_impl.h" +#include + +namespace gr { + namespace filter { + + fractional_interpolator_ff::sptr + fractional_interpolator_ff::make(float phase_shift, float interp_ratio) + { + return gnuradio::get_initial_sptr( + new fractional_interpolator_ff_impl(phase_shift, interp_ratio)); + } + + fractional_interpolator_ff_impl::fractional_interpolator_ff_impl + (float phase_shift, float interp_ratio) + : gr_block("fractional_interpolator_ff", + gr_make_io_signature(1, 1, sizeof(float)), + gr_make_io_signature(1, 1, sizeof(float))), + d_mu (phase_shift), d_mu_inc (interp_ratio), + d_interp(new mmse_fir_interpolator_ff()) + { + if(interp_ratio <= 0) + throw std::out_of_range("interpolation ratio must be > 0"); + if(phase_shift < 0 || phase_shift > 1) + throw std::out_of_range("phase shift ratio must be > 0 and < 1"); + + set_relative_rate(1.0 / interp_ratio); + } + + fractional_interpolator_ff_impl::~fractional_interpolator_ff_impl() + { + delete d_interp; + } + + void + fractional_interpolator_ff_impl::forecast(int noutput_items, + gr_vector_int &ninput_items_required) + { + unsigned ninputs = ninput_items_required.size(); + for(unsigned i=0; i < ninputs; i++) { + ninput_items_required[i] = + (int)ceil((noutput_items * d_mu_inc) + d_interp->ntaps()); + } + } + + int + fractional_interpolator_ff_impl::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *in = (const float*)input_items[0]; + float *out = (float*)output_items[0]; + + int ii = 0; // input index + int oo = 0; // output index + + while(oo < noutput_items) { + out[oo++] = d_interp->interpolate(&in[ii], d_mu); + + double s = d_mu + d_mu_inc; + double f = floor(s); + int incr = (int)f; + d_mu = s - f; + ii += incr; + } + + consume_each(ii); + + return noutput_items; + } + + float + fractional_interpolator_ff_impl::mu() const + { + return d_mu; + } + + float + fractional_interpolator_ff_impl::interp_ratio() const + { + return d_mu_inc; + } + + void + fractional_interpolator_ff_impl::set_mu(float mu) + { + d_mu = mu; + } + + void + fractional_interpolator_ff_impl::set_interp_ratio(float interp_ratio) + { + d_mu_inc = interp_ratio; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fractional_interpolator_ff_impl.h b/gr-filter/lib/fractional_interpolator_ff_impl.h new file mode 100644 index 000000000..d31385cc4 --- /dev/null +++ b/gr-filter/lib/fractional_interpolator_ff_impl.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FRACTIONAL_INTERPOLATOR_FF_IMPL_H +#define INCLUDED_FRACTIONAL_INTERPOLATOR_FF_IMPL_H + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API fractional_interpolator_ff_impl + : public fractional_interpolator_ff + { + private: + float d_mu; + float d_mu_inc; + mmse_fir_interpolator_ff *d_interp; + + public: + fractional_interpolator_ff_impl(float phase_shift, + float interp_ratio); + ~fractional_interpolator_ff_impl(); + + void forecast(int noutput_items, + gr_vector_int &ninput_items_required); + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + float mu() const; + float interp_ratio() const; + void set_mu(float mu); + void set_interp_ratio(float interp_ratio); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FRACTIONAL_INTERPOLATOR_FF_IMPL_H */ diff --git a/gr-filter/python/qa_fractional_interpolator.py b/gr-filter/python/qa_fractional_interpolator.py new file mode 100755 index 000000000..51c8f9530 --- /dev/null +++ b/gr-filter/python/qa_fractional_interpolator.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# +# Copyright 2007,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter + +class test_fractional_resampler(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_000_make(self): + op = gr.fractional_interpolator_ff(0.0, 1.0) + op2 = gr.fractional_interpolator_cc(0.0, 1.0) + +if __name__ == '__main__': + gr_unittest.run(test_fractional_resampler, "test_fractional_resampler.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 161fd4fe3..33c52b7a4 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -42,6 +42,8 @@ #include "filter/fir_filter_fsf.h" #include "filter/fft_filter_ccc.h" #include "filter/fft_filter_fff.h" +#include "filter/fractional_interpolator_cc.h" +#include "filter/fractional_interpolator_ff.h" #include "filter/freq_xlating_fir_filter_ccc.h" #include "filter/freq_xlating_fir_filter_ccf.h" #include "filter/freq_xlating_fir_filter_fcc.h" @@ -69,6 +71,8 @@ %include "filter/fir_filter_fsf.h" %include "filter/fft_filter_ccc.h" %include "filter/fft_filter_fff.h" +%include "filter/fractional_interpolator_cc.h" +%include "filter/fractional_interpolator_ff.h" %include "filter/freq_xlating_fir_filter_ccc.h" %include "filter/freq_xlating_fir_filter_ccf.h" %include "filter/freq_xlating_fir_filter_fcc.h" @@ -93,6 +97,8 @@ GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); +GR_SWIG_BLOCK_MAGIC2(filter, fractional_interpolator_cc); +GR_SWIG_BLOCK_MAGIC2(filter, fractional_interpolator_ff); GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_fcc); -- cgit From f5449ce2e0a665b725a27855cd94f28a976da792 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Mon, 18 Jun 2012 13:34:51 -0400 Subject: filter: added interp_fir_filter with QA and GRC. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/interp_fir_filter_xxx.xml | 80 +++++++++++ gr-filter/include/filter/CMakeLists.txt | 5 +- gr-filter/include/filter/fir_filter_XXX.h.t | 4 +- gr-filter/include/filter/interp_fir_filter_XXX.h.t | 81 +++++++++++ gr-filter/lib/CMakeLists.txt | 5 +- gr-filter/lib/fir_filter_XXX_impl.cc.t | 2 + gr-filter/lib/interp_fir_filter_XXX_impl.cc.t | 151 +++++++++++++++++++++ gr-filter/lib/interp_fir_filter_XXX_impl.h.t | 62 +++++++++ gr-filter/python/qa_interp_fir_filter.py | 60 ++++++++ gr-filter/swig/filter_swig.i | 45 ++++-- 12 files changed, 479 insertions(+), 18 deletions(-) create mode 100644 gr-filter/grc/interp_fir_filter_xxx.xml create mode 100644 gr-filter/include/filter/interp_fir_filter_XXX.h.t create mode 100644 gr-filter/lib/interp_fir_filter_XXX_impl.cc.t create mode 100644 gr-filter/lib/interp_fir_filter_XXX_impl.h.t create mode 100755 gr-filter/python/qa_interp_fir_filter.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 48d452d39..a269c2b25 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -27,6 +27,7 @@ install(FILES freq_xlating_fir_filter_xxx.xml hilbert_fc.xml iir_filter_ffd.xml + interp_fir_filter_xxx.xml pfb_channelizer.xml single_pole_iir_filter_xx.xml DESTINATION ${GRC_BLOCKS_DIR} diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 7f34a1b70..a5630bc88 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -38,6 +38,7 @@ freq_xlating_fir_filter_xxx hilbert_fc iir_filter_ffd + interp_fir_filter_xxx pfb_channelizer_ccf single_pole_iir_filter_xx diff --git a/gr-filter/grc/interp_fir_filter_xxx.xml b/gr-filter/grc/interp_fir_filter_xxx.xml new file mode 100644 index 000000000..98a143f53 --- /dev/null +++ b/gr-filter/grc/interp_fir_filter_xxx.xml @@ -0,0 +1,80 @@ + + + + Interpolating FIR Filter + interp_fir_filter_xxx + from gnuradio import filter + from gnuradio.filter import firdes + filter.interp_fir_filter_$(type)($interp, $taps) + set_taps($taps) + + Type + type + enum + + + + + + + + + Interpolation + interp + 1 + int + + + Taps + taps + $type.taps + + + in + $type.input + + + out + $type.output + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 267b8b66b..e9ba8c246 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -64,8 +64,9 @@ endmacro(expand_h) ######################################################################## # Invoke macro to generate various sources ####################################################################### -expand_h(fir_filter_XXX fff ccf ccc scc fsf) -expand_h(freq_xlating_fir_filter_XXX ccc ccf fcc fcf scc scf) +expand_h(fir_filter_XXX fff ccc ccf fcc fff fsf scc) +expand_h(freq_xlating_fir_filter_XXX ccc ccf fcc fcf scf scc) +expand_h(interp_fir_filter_XXX ccc ccf fcc fff fsf scc) add_custom_target(filter_generated_includes DEPENDS ${generated_includes} diff --git a/gr-filter/include/filter/fir_filter_XXX.h.t b/gr-filter/include/filter/fir_filter_XXX.h.t index 647c18db4..fc768bcad 100644 --- a/gr-filter/include/filter/fir_filter_XXX.h.t +++ b/gr-filter/include/filter/fir_filter_XXX.h.t @@ -72,8 +72,8 @@ namespace gr { static FILTER_API sptr make(int decimation, const std::vector<@TAP_TYPE@> &taps); - virtual void set_taps (const std::vector<@TAP_TYPE@> &taps) = 0; - virtual std::vector<@TAP_TYPE@> taps () const = 0; + virtual void set_taps(const std::vector<@TAP_TYPE@> &taps) = 0; + virtual std::vector<@TAP_TYPE@> taps() const = 0; }; } /* namespace filter */ diff --git a/gr-filter/include/filter/interp_fir_filter_XXX.h.t b/gr-filter/include/filter/interp_fir_filter_XXX.h.t new file mode 100644 index 000000000..eaf215200 --- /dev/null +++ b/gr-filter/include/filter/interp_fir_filter_XXX.h.t @@ -0,0 +1,81 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief Interpolating FIR filter with @I_TYPE@ input, @O_TYPE@ output and @TAP_TYPE@ taps + * \ingroup filter_blk + * + * The fir_filter_XXX blocks create finite impulse response + * (FIR) filters that perform the convolution in the time + * domain: + * + * \code + * out = 0 + * for i in ntaps: + * out += input[n-i] * taps[i] + * \endcode + * + * The taps are a C++ vector (or Python list) of values of the + * type specified by the third letter in the block's suffix. For + * this block, the value is of type @TAP_TYPE@. Taps can be + * created using the firdes or optfir tools. + * + * These versions of the filter can also act as up-samplers + * (or interpolators) by specifying an integer value for \p + * interpolation. + * + */ + class FILTER_API @BASE_NAME@ : virtual public gr_sync_interpolator + { + public: + // gr::filter::@BASE_NAME@::sptr + typedef boost::shared_ptr<@BASE_NAME@> sptr; + + /*! + * \brief Interpolating FIR filter with @I_TYPE@ input, @O_TYPE@ output, and @TAP_TYPE@ taps + * \ingroup filter_blk + * + * \param interpolation set the integer interpolation rate + * \param taps a vector/list of taps of type @TAP_TYPE@ + */ + static FILTER_API sptr make(unsigned interpolation, + const std::vector<@TAP_TYPE@> &taps); + + virtual void set_taps(const std::vector<@TAP_TYPE@> &taps) = 0; + virtual std::vector<@TAP_TYPE@> taps() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 334720766..11135b864 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -80,8 +80,9 @@ endmacro(expand_cc) ######################################################################## # Invoke macro to generate various sources ######################################################################## -expand_cc(fir_filter_XXX_impl fff ccf ccc scc fsf) -expand_cc(freq_xlating_fir_filter_XXX_impl ccc ccf fcc fcf scc scf) +expand_cc(fir_filter_XXX_impl ccc ccf fcc fff fsf scc) +expand_cc(freq_xlating_fir_filter_XXX_impl ccc ccf fcc fcf scf scc) +expand_cc(interp_fir_filter_XXX_impl ccc ccf fcc fff fsf scc) ######################################################################## diff --git a/gr-filter/lib/fir_filter_XXX_impl.cc.t b/gr-filter/lib/fir_filter_XXX_impl.cc.t index 8ea8151f8..529c51e2b 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.cc.t +++ b/gr-filter/lib/fir_filter_XXX_impl.cc.t @@ -20,6 +20,8 @@ * Boston, MA 02110-1301, USA. */ +/* @WARNING@ */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/gr-filter/lib/interp_fir_filter_XXX_impl.cc.t b/gr-filter/lib/interp_fir_filter_XXX_impl.cc.t new file mode 100644 index 000000000..c9127d737 --- /dev/null +++ b/gr-filter/lib/interp_fir_filter_XXX_impl.cc.t @@ -0,0 +1,151 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "@IMPL_NAME@.h" +#include +#include +#include + +namespace gr { + namespace filter { + + @BASE_NAME@::sptr + @BASE_NAME@::make(unsigned interpolation, + const std::vector<@TAP_TYPE@> &taps) + { + return gnuradio::get_initial_sptr(new @IMPL_NAME@ + (interpolation, taps)); + } + + @IMPL_NAME@::@IMPL_NAME@(unsigned interpolation, + const std::vector<@TAP_TYPE@> &taps) + : gr_sync_interpolator("@BASE_NAME@", + gr_make_io_signature(1, 1, sizeof(@I_TYPE@)), + gr_make_io_signature(1, 1, sizeof(@O_TYPE@)), + interpolation), + d_updated(false), d_firs(interpolation) + { + if(interpolation == 0) { + throw std::out_of_range("@IMPL_NAME@: interpolation must be > 0\n"); + } + + std::vector<@TAP_TYPE@> dummy_taps; + + for(unsigned i = 0; i < interpolation; i++) { + d_firs[i] = new kernel::@FIR_TYPE@(1, dummy_taps); + } + + set_taps(taps); + install_taps(d_new_taps); + } + + @IMPL_NAME@::~@IMPL_NAME@() + { + for(unsigned i = 0; i < interpolation(); i++) { + delete d_firs[i]; + } + } + + void + @IMPL_NAME@::set_taps(const std::vector<@TAP_TYPE@> &taps) + { + d_new_taps = taps; + d_updated = true; + + // round up length to a multiple of the interpolation factor + int n = taps.size() % interpolation(); + if(n > 0) { + n = interpolation() - n; + while(n-- > 0) { + d_new_taps.insert(d_new_taps.begin(), 0); + } + } + + if(d_new_taps.size() % interpolation() != 0) { + throw std::runtime_error("@IMPL_NAME@: error setting interpolator taps.\n"); + } + } + + void + @IMPL_NAME@::install_taps(const std::vector<@TAP_TYPE@> &taps) + { + unsigned nfilters = interpolation(); + int nt = taps.size() / nfilters; + + std::vector< std::vector <@TAP_TYPE@> > xtaps(nfilters); + + for(unsigned n = 0; n < nfilters; n++) { + xtaps[n].resize (nt); + } + + for(size_t i = 0; i < taps.size(); i++) { + xtaps[i % nfilters][i / nfilters] = taps[i]; + } + + for(unsigned n = 0; n < nfilters; n++) { + d_firs[n]->set_taps (xtaps[n]); + } + + set_history(nt); + d_updated = false; + } + + std::vector<@TAP_TYPE@> + @IMPL_NAME@::taps() const + { + return d_new_taps; + } + + int + @NAME@::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const @I_TYPE@ *in = (const @I_TYPE@ *)input_items[0]; + @O_TYPE@ *out = (@O_TYPE@ *)output_items[0]; + + if(d_updated) { + install_taps(d_new_taps); + return 0; // history requirements may have changed. + } + + int nfilters = interpolation(); + int ni = noutput_items / interpolation(); + + for(int i = 0; i < ni; i++) { + for(int nf = 0; nf < nfilters; nf++) { + out[nf] = d_firs[nf]->filter(&in[i]); + } + out += nfilters; + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/interp_fir_filter_XXX_impl.h.t b/gr-filter/lib/interp_fir_filter_XXX_impl.h.t new file mode 100644 index 000000000..e505fca69 --- /dev/null +++ b/gr-filter/lib/interp_fir_filter_XXX_impl.h.t @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API @IMPL_NAME@ : public @BASE_NAME@ + { + private: + bool d_updated; + std::vector d_firs; + std::vector<@TAP_TYPE@> d_new_taps; + + void install_taps(const std::vector<@TAP_TYPE@> &taps); + + public: + @IMPL_NAME@(unsigned interpolation, + const std::vector<@TAP_TYPE@> &taps); + + ~@IMPL_NAME@(); + + void set_taps(const std::vector<@TAP_TYPE@> &taps); + std::vector<@TAP_TYPE@> taps() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/python/qa_interp_fir_filter.py b/gr-filter/python/qa_interp_fir_filter.py new file mode 100755 index 000000000..97901963e --- /dev/null +++ b/gr-filter/python/qa_interp_fir_filter.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_interp_fir_filter(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_fff(self): + taps = [1, 10, 100, 1000, 10000] + src_data = (0, 2, 3, 5, 7, 11, 13, 17) + interpolation = 3 + xr = (0,0,0,0, + 2,20,200,2003,20030, + 300,3005,30050, + 500,5007,50070, + 700,7011,70110, + 1100,11013,110130, + 1300,13017,130170) + expected_result = tuple([float(x) for x in xr]) + + src = gr.vector_source_f(src_data) + op = gr.interp_fir_filter_fff(interpolation, taps) + dst = gr.vector_sink_f() + self.tb.connect(src, op) + self.tb.connect(op, dst) + self.tb.run() + result_data = dst.data() + L = min(len(result_data), len(expected_result)) + self.assertEqual(expected_result[0:L], result_data[0:L]) + +if __name__ == '__main__': + gr_unittest.run(test_interp_fir_filter, "test_interp_fir_filter.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 33c52b7a4..7a7f548a9 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -35,11 +35,12 @@ #include "filter/dc_blocker_cc.h" #include "filter/dc_blocker_ff.h" #include "filter/filter_delay_fc.h" -#include "filter/fir_filter_fff.h" -#include "filter/fir_filter_ccf.h" #include "filter/fir_filter_ccc.h" -#include "filter/fir_filter_scc.h" +#include "filter/fir_filter_ccf.h" +#include "filter/fir_filter_fcc.h" +#include "filter/fir_filter_fff.h" #include "filter/fir_filter_fsf.h" +#include "filter/fir_filter_scc.h" #include "filter/fft_filter_ccc.h" #include "filter/fft_filter_fff.h" #include "filter/fractional_interpolator_cc.h" @@ -48,10 +49,16 @@ #include "filter/freq_xlating_fir_filter_ccf.h" #include "filter/freq_xlating_fir_filter_fcc.h" #include "filter/freq_xlating_fir_filter_fcf.h" -#include "filter/freq_xlating_fir_filter_scc.h" #include "filter/freq_xlating_fir_filter_scf.h" +#include "filter/freq_xlating_fir_filter_scc.h" #include "filter/hilbert_fc.h" #include "filter/iir_filter_ffd.h" +#include "filter/interp_fir_filter_ccc.h" +#include "filter/interp_fir_filter_ccf.h" +#include "filter/interp_fir_filter_fcc.h" +#include "filter/interp_fir_filter_fff.h" +#include "filter/interp_fir_filter_fsf.h" +#include "filter/interp_fir_filter_scc.h" #include "filter/pfb_channelizer_ccf.h" #include "filter/single_pole_iir_filter_cc.h" #include "filter/single_pole_iir_filter_ff.h" @@ -64,11 +71,12 @@ %include "filter/dc_blocker_cc.h" %include "filter/dc_blocker_ff.h" %include "filter/filter_delay_fc.h" -%include "filter/fir_filter_fff.h" -%include "filter/fir_filter_ccf.h" %include "filter/fir_filter_ccc.h" -%include "filter/fir_filter_scc.h" +%include "filter/fir_filter_ccf.h" +%include "filter/fir_filter_fcc.h" +%include "filter/fir_filter_fff.h" %include "filter/fir_filter_fsf.h" +%include "filter/fir_filter_scc.h" %include "filter/fft_filter_ccc.h" %include "filter/fft_filter_fff.h" %include "filter/fractional_interpolator_cc.h" @@ -77,10 +85,16 @@ %include "filter/freq_xlating_fir_filter_ccf.h" %include "filter/freq_xlating_fir_filter_fcc.h" %include "filter/freq_xlating_fir_filter_fcf.h" -%include "filter/freq_xlating_fir_filter_scc.h" %include "filter/freq_xlating_fir_filter_scf.h" +%include "filter/freq_xlating_fir_filter_scc.h" %include "filter/hilbert_fc.h" %include "filter/iir_filter_ffd.h" +%include "filter/interp_fir_filter_ccc.h" +%include "filter/interp_fir_filter_ccf.h" +%include "filter/interp_fir_filter_fcc.h" +%include "filter/interp_fir_filter_fff.h" +%include "filter/interp_fir_filter_fsf.h" +%include "filter/interp_fir_filter_scc.h" %include "filter/pfb_channelizer_ccf.h" %include "filter/single_pole_iir_filter_cc.h" %include "filter/single_pole_iir_filter_ff.h" @@ -90,11 +104,12 @@ GR_SWIG_BLOCK_MAGIC2(filter, adaptive_fir_ccf); GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_cc); GR_SWIG_BLOCK_MAGIC2(filter, dc_blocker_ff); GR_SWIG_BLOCK_MAGIC2(filter, filter_delay_fc); -GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); -GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccc); -GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_scc); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fcc); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fsf); +GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fractional_interpolator_cc); @@ -103,10 +118,16 @@ GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_ccc); GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_fcc); GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_fcf); -GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_scf); +GR_SWIG_BLOCK_MAGIC2(filter, freq_xlating_fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, hilbert_fc); GR_SWIG_BLOCK_MAGIC2(filter, iir_filter_ffd); +GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fcc); +GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fff); +GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fsf); +GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_cc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_ff); -- cgit From 622464a324abc90f2724d3f846f0b1bdb38b97fc Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Mon, 18 Jun 2012 14:37:49 -0400 Subject: filter: adding PFB decimator with GRC and QA. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/pfb_decimator.xml | 43 ++++++++ gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/pfb_channelizer_ccf.h | 3 +- gr-filter/include/filter/pfb_decimator_ccf.h | 132 ++++++++++++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/pfb_channelizer_ccf_impl.h | 1 + gr-filter/lib/pfb_decimator_ccf_impl.cc | 137 +++++++++++++++++++++++++ gr-filter/lib/pfb_decimator_ccf_impl.h | 65 ++++++++++++ gr-filter/python/qa_freq_xlating_fir_filter.py | 0 gr-filter/python/qa_pfb_channelizer.py | 0 gr-filter/python/qa_pfb_decimator.py | 125 ++++++++++++++++++++++ gr-filter/swig/filter_swig.i | 3 + 14 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 gr-filter/grc/pfb_decimator.xml create mode 100644 gr-filter/include/filter/pfb_decimator_ccf.h create mode 100644 gr-filter/lib/pfb_decimator_ccf_impl.cc create mode 100644 gr-filter/lib/pfb_decimator_ccf_impl.h mode change 100644 => 100755 gr-filter/python/qa_freq_xlating_fir_filter.py mode change 100644 => 100755 gr-filter/python/qa_pfb_channelizer.py create mode 100755 gr-filter/python/qa_pfb_decimator.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index a269c2b25..a160b00bb 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -29,6 +29,7 @@ install(FILES iir_filter_ffd.xml interp_fir_filter_xxx.xml pfb_channelizer.xml + pfb_decimator.xml single_pole_iir_filter_xx.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index a5630bc88..2a0d02346 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -40,6 +40,7 @@ iir_filter_ffd interp_fir_filter_xxx pfb_channelizer_ccf + pfb_decimator_ccf single_pole_iir_filter_xx diff --git a/gr-filter/grc/pfb_decimator.xml b/gr-filter/grc/pfb_decimator.xml new file mode 100644 index 000000000..dea5bdb43 --- /dev/null +++ b/gr-filter/grc/pfb_decimator.xml @@ -0,0 +1,43 @@ + + + + Polyphase Decimator + pfb_decimator_ccf + from gnuradio import filter + from gnuradio.filter import firdes + filter.pfb.decimator_ccf( + $decim, + $taps, + $channel) + + set_taps($taps) + + Decimation + decim + int + + + Taps + taps + None + real_vector + + + Output Channel + channel + 0 + int + + + in + complex + + + out + complex + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index e9ba8c246..77ae78e35 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -101,6 +101,7 @@ install(FILES hilbert_fc.h iir_filter_ffd.h pfb_channelizer_ccf.h + pfb_decimator_ccf.h single_pole_iir_filter_cc.h single_pole_iir_filter_ff.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter diff --git a/gr-filter/include/filter/pfb_channelizer_ccf.h b/gr-filter/include/filter/pfb_channelizer_ccf.h index ab79f7036..ba55782b4 100644 --- a/gr-filter/include/filter/pfb_channelizer_ccf.h +++ b/gr-filter/include/filter/pfb_channelizer_ccf.h @@ -26,13 +26,12 @@ #include #include -#include namespace gr { namespace filter { /*! - * \class gr_pfb_channelizer_ccf + * \class pfb_channelizer_ccf * * \brief Polyphase filterbank channelizer with * gr_complex input, gr_complex output and float taps diff --git a/gr-filter/include/filter/pfb_decimator_ccf.h b/gr-filter/include/filter/pfb_decimator_ccf.h new file mode 100644 index 000000000..a933fbb5b --- /dev/null +++ b/gr-filter/include/filter/pfb_decimator_ccf.h @@ -0,0 +1,132 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_DECIMATOR_CCF_H +#define INCLUDED_PFB_DECIMATOR_CCF_H + +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \class pfb_decimator_ccf + * \brief Polyphase filterbank bandpass decimator with gr_complex + * input, gr_complex output and float taps + * + * \ingroup filter_blk + * \ingroup pfb_blk + * + * This block takes in a signal stream and performs interger down- + * sampling (decimation) with a polyphase filterbank. The first + * input is the integer specifying how much to decimate by. The + * second input is a vector (Python list) of floating-point taps + * of the prototype filter. The third input specifies the channel + * to extract. By default, the zeroth channel is used, which is + * the baseband channel (first Nyquist zone). + * + * The channel parameter specifies which channel to use + * since this class is capable of bandpass decimation. Given a + * complex input stream at a sampling rate of fs and a + * decimation rate of decim, the input frequency domain + * is split into decim channels that represent the + * Nyquist zones. Using the polyphase filterbank, we can select + * any one of these channels to decimate. + * + * The output signal will be the basebanded and decimated signal + * from that channel. This concept is very similar to the PFB + * channelizer (see #gr_pfb_channelizer_ccf) where only a single + * channel is extracted at a time. + * + * The filter's taps should be based on the sampling rate before + * decimation. + * + * For example, using the GNU Radio's firdes utility to building + * filters, we build a low-pass filter with a sampling rate of + * fs, a 3-dB bandwidth of BW and a transition + * bandwidth of TB. We can also specify the out-of-band + * attenuation to use, ATT, and the filter window + * function (a Blackman-harris window in this case). The first + * input is the gain of the filter, which we specify here as + * unity. + * + * self._taps = gr.firdes.low_pass_2(1, fs, BW, TB, + * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * + * The PFB decimator code takes the taps generated above and + * builds a set of filters. The set contains decim number + * of filters and each filter contains ceil(taps.size()/decim) + * number of taps. Each tap from the filter prototype is + * sequentially inserted into the next filter. When all of the + * input taps are used, the remaining filters in the filterbank + * are filled out with 0's to make sure each filter has the same + * number of taps. + * + * The theory behind this block can be found in Chapter 6 of + * the following book. + * + * f. harris, "Multirate Signal Processing for Communication + * Systems," Upper Saddle River, NJ: Prentice Hall, Inc. 2004. + */ + + class FILTER_API pfb_decimator_ccf : virtual public gr_sync_block + { + public: + // gr::filter::pfb_decimator_ccf::sptr + typedef boost::shared_ptr sptr; + + /*! + * Build the polyphase filterbank decimator. + * \param decim (unsigned integer) Specifies the decimation rate to use + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + * \param channel (unsigned integer) Selects the channel to return [default=0]. + */ + static FILTER_API sptr make(unsigned int decim, + const std::vector &taps, + unsigned int channel); + + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + */ + virtual void set_taps(const std::vector &taps) = 0; + + /*! + * Return a vector> of the filterbank taps + */ + virtual std::vector > taps() const = 0; + + /*! + * Print all of the filterbank taps to screen. + */ + virtual void print_taps() = 0; + + //virtual void set_channel(unsigned int channel) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_PFB_DECIMATOR_CCF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 11135b864..40faa8f0e 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -128,6 +128,7 @@ list(APPEND filter_sources hilbert_fc_impl.cc iir_filter_ffd_impl.cc pfb_channelizer_ccf_impl.cc + pfb_decimator_ccf_impl.cc single_pole_iir_filter_cc_impl.cc single_pole_iir_filter_ff_impl.cc ) diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.h b/gr-filter/lib/pfb_channelizer_ccf_impl.h index 8cabc4e34..16b112b9a 100644 --- a/gr-filter/lib/pfb_channelizer_ccf_impl.h +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace gr { namespace filter { diff --git a/gr-filter/lib/pfb_decimator_ccf_impl.cc b/gr-filter/lib/pfb_decimator_ccf_impl.cc new file mode 100644 index 000000000..139cd9bec --- /dev/null +++ b/gr-filter/lib/pfb_decimator_ccf_impl.cc @@ -0,0 +1,137 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pfb_decimator_ccf_impl.h" +#include + +namespace gr { + namespace filter { + + pfb_decimator_ccf::sptr + pfb_decimator_ccf::make(unsigned int nfilts, + const std::vector &taps, + unsigned int channel) + { + return gnuradio::get_initial_sptr + (new pfb_decimator_ccf_impl(nfilts, taps, channel)); + } + + + pfb_decimator_ccf_impl::pfb_decimator_ccf_impl(unsigned int decim, + const std::vector &taps, + unsigned int channel) + : gr_sync_block("pfb_decimator_ccf", + gr_make_io_signature(decim, decim, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex))), + polyphase_filterbank(decim, taps), + d_updated(false), d_chan(channel) + { + d_rate = decim; + d_rotator = new gr_complex[d_rate]; + + set_relative_rate(1.0/(float)decim); + set_history(d_taps_per_filter+1); + } + + pfb_decimator_ccf_impl::~pfb_decimator_ccf_impl() + { + } + + void + pfb_decimator_ccf_impl::set_taps(const std::vector &taps) + { + gruel::scoped_lock guard(d_mutex); + + polyphase_filterbank::set_taps(taps); + set_history(d_taps_per_filter+1); + d_updated = true; + } + + void + pfb_decimator_ccf_impl::print_taps() + { + polyphase_filterbank::print_taps(); + } + + std::vector > + pfb_decimator_ccf_impl::taps() const + { + return polyphase_filterbank::taps(); + } + +#define ROTATEFFT + + int + pfb_decimator_ccf_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gruel::scoped_lock guard(d_mutex); + + gr_complex *in; + gr_complex *out = (gr_complex *)output_items[0]; + + if(d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + int i; + for(i = 0; i < noutput_items; i++) { + // Move through filters from bottom to top + out[i] = 0; + for(int j = d_rate-1; j >= 0; j--) { + // Take in the items from the first input stream to d_rate + in = (gr_complex*)input_items[d_rate - 1 - j]; + + // Filter current input stream from bottom filter to top + // The rotate them by expj(j*k*2pi/M) where M is the number of filters + // (the decimation rate) and k is the channel number to extract + + // This is the real math that goes on; we abuse the FFT to do this quickly + // for decimation rates > N where N is a small number (~5): + // out[i] += d_filters[j]->filter(&in[i])*gr_expj(j*d_chan*2*M_PI/d_rate); +#ifdef ROTATEFFT + d_fft->get_inbuf()[j] = d_filters[j]->filter(&in[i]); +#else + out[i] += d_filters[j]->filter(&in[i])*d_rotator[i]; +#endif + } + +#ifdef ROTATEFFT + // Perform the FFT to do the complex multiply despinning for all channels + d_fft->execute(); + + // Select only the desired channel out + out[i] = d_fft->get_outbuf()[d_chan]; +#endif + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/pfb_decimator_ccf_impl.h b/gr-filter/lib/pfb_decimator_ccf_impl.h new file mode 100644 index 000000000..c4338a5ec --- /dev/null +++ b/gr-filter/lib/pfb_decimator_ccf_impl.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_DECIMATOR_CCF_IMPL_H +#define INCLUDED_PFB_DECIMATOR_CCF_IMPL_H + +#include +#include +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API pfb_decimator_ccf_impl : public pfb_decimator_ccf, kernel::polyphase_filterbank + { + private: + bool d_updated; + unsigned int d_rate; + unsigned int d_chan; + gr_complex *d_rotator; + gruel::mutex d_mutex; // mutex to protect set/work access + + public: + pfb_decimator_ccf_impl(unsigned int decim, + const std::vector &taps, + unsigned int channel); + + ~pfb_decimator_ccf_impl(); + + void set_taps(const std::vector &taps); + void print_taps(); + std::vector > taps() const; + //void set_channel(unsigned int channel); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_DECIMATOR_CCF_IMPL_H */ diff --git a/gr-filter/python/qa_freq_xlating_fir_filter.py b/gr-filter/python/qa_freq_xlating_fir_filter.py old mode 100644 new mode 100755 diff --git a/gr-filter/python/qa_pfb_channelizer.py b/gr-filter/python/qa_pfb_channelizer.py old mode 100644 new mode 100755 diff --git a/gr-filter/python/qa_pfb_decimator.py b/gr-filter/python/qa_pfb_decimator.py new file mode 100755 index 000000000..c8fd408be --- /dev/null +++ b/gr-filter/python/qa_pfb_decimator.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_pfb_decimator(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_000(self): + N = 1000 # number of samples to use + M = 5 # Number of channels + fs = 1000 # baseband sampling rate + ifs = M*fs # input samp rate to decimator + channel = 0 # Extract channel 0 + + taps = gr.firdes.low_pass_2(1, ifs, fs/2, fs/10, + attenuation_dB=80, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + signals = list() + add = gr.add_cc() + freqs = [-200, -100, 0, 100, 200] + for i in xrange(len(freqs)): + f = freqs[i] + (M/2-M+i+1)*fs + signals.append(gr.sig_source_c(ifs, gr.GR_SIN_WAVE, f, 1)) + self.tb.connect(signals[i], (add,i)) + + head = gr.head(gr.sizeof_gr_complex, N) + s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, M) + pfb = filter.pfb_decimator_ccf(M, taps, channel) + snk = gr.vector_sink_c() + + self.tb.connect(add, head, s2ss) + for i in xrange(M): + self.tb.connect((s2ss,i), (pfb,i)) + self.tb.connect(pfb, snk) + + self.tb.run() + + Ntest = 50 + L = len(snk.data()) + t = map(lambda x: float(x)/fs, xrange(L)) + + # Create known data as complex sinusoids for the baseband freq + # of the extracted channel is due to decimator output order. + phase = 0 + expected_data = map(lambda x: math.cos(2.*math.pi*freqs[2]*x+phase) + \ + 1j*math.sin(2.*math.pi*freqs[2]*x+phase), t) + + dst_data = snk.data() + + self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + + def test_001(self): + N = 1000 # number of samples to use + M = 5 # Number of channels + fs = 1000 # baseband sampling rate + ifs = M*fs # input samp rate to decimator + channel = 1 # Extract channel 0 + + taps = gr.firdes.low_pass_2(1, ifs, fs/2, fs/10, + attenuation_dB=80, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + signals = list() + add = gr.add_cc() + freqs = [-200, -100, 0, 100, 200] + for i in xrange(len(freqs)): + f = freqs[i] + (M/2-M+i+1)*fs + signals.append(gr.sig_source_c(ifs, gr.GR_SIN_WAVE, f, 1)) + self.tb.connect(signals[i], (add,i)) + + head = gr.head(gr.sizeof_gr_complex, N) + s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, M) + pfb = filter.pfb_decimator_ccf(M, taps, channel) + snk = gr.vector_sink_c() + + self.tb.connect(add, head, s2ss) + for i in xrange(M): + self.tb.connect((s2ss,i), (pfb,i)) + self.tb.connect(pfb, snk) + + self.tb.run() + + Ntest = 50 + L = len(snk.data()) + t = map(lambda x: float(x)/fs, xrange(L)) + + # Create known data as complex sinusoids for the baseband freq + # of the extracted channel is due to decimator output order. + phase = 6.15746 + expected_data = map(lambda x: math.cos(2.*math.pi*freqs[3]*x+phase) + \ + 1j*math.sin(2.*math.pi*freqs[3]*x+phase), t) + dst_data = snk.data() + + self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + +if __name__ == '__main__': + gr_unittest.run(test_pfb_decimator, "test_pfb_decimator.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 7a7f548a9..0f814f955 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -60,6 +60,7 @@ #include "filter/interp_fir_filter_fsf.h" #include "filter/interp_fir_filter_scc.h" #include "filter/pfb_channelizer_ccf.h" +#include "filter/pfb_decimator_ccf.h" #include "filter/single_pole_iir_filter_cc.h" #include "filter/single_pole_iir_filter_ff.h" %} @@ -96,6 +97,7 @@ %include "filter/interp_fir_filter_fsf.h" %include "filter/interp_fir_filter_scc.h" %include "filter/pfb_channelizer_ccf.h" +%include "filter/pfb_decimator_ccf.h" %include "filter/single_pole_iir_filter_cc.h" %include "filter/single_pole_iir_filter_ff.h" @@ -129,5 +131,6 @@ GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, pfb_decimator_ccf); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_cc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_ff); -- cgit From 5365daf7ae85c9df88f0b8f3667ece022d93f637 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Mon, 18 Jun 2012 15:42:14 -0400 Subject: filter: adding PFB interpolator with GRC and QA. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/pfb_decimator.xml | 2 +- gr-filter/grc/pfb_interpolator.xml | 36 ++++++++ gr-filter/include/filter/CMakeLists.txt | 5 +- gr-filter/include/filter/pfb_decimator_ccf.h | 1 - gr-filter/include/filter/pfb_interpolator_ccf.h | 118 ++++++++++++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/pfb_decimator_ccf_impl.cc | 4 +- gr-filter/lib/pfb_interpolator_ccf_impl.cc | 107 +++++++++++++++++++++ gr-filter/lib/pfb_interpolator_ccf_impl.h | 61 ++++++++++++ gr-filter/python/qa_pfb_interpolator.py | 71 ++++++++++++++ gr-filter/swig/filter_swig.i | 3 + 13 files changed, 405 insertions(+), 6 deletions(-) create mode 100644 gr-filter/grc/pfb_interpolator.xml create mode 100644 gr-filter/include/filter/pfb_interpolator_ccf.h create mode 100644 gr-filter/lib/pfb_interpolator_ccf_impl.cc create mode 100644 gr-filter/lib/pfb_interpolator_ccf_impl.h create mode 100755 gr-filter/python/qa_pfb_interpolator.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index a160b00bb..7fc129d8a 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -30,6 +30,7 @@ install(FILES interp_fir_filter_xxx.xml pfb_channelizer.xml pfb_decimator.xml + pfb_interpolator.xml single_pole_iir_filter_xx.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 2a0d02346..0ec9e5dd0 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -41,6 +41,7 @@ interp_fir_filter_xxx pfb_channelizer_ccf pfb_decimator_ccf + pfb_interpolator_ccf single_pole_iir_filter_xx diff --git a/gr-filter/grc/pfb_decimator.xml b/gr-filter/grc/pfb_decimator.xml index dea5bdb43..329d59c3d 100644 --- a/gr-filter/grc/pfb_decimator.xml +++ b/gr-filter/grc/pfb_decimator.xml @@ -1,7 +1,7 @@ diff --git a/gr-filter/grc/pfb_interpolator.xml b/gr-filter/grc/pfb_interpolator.xml new file mode 100644 index 000000000..600493123 --- /dev/null +++ b/gr-filter/grc/pfb_interpolator.xml @@ -0,0 +1,36 @@ + + + + Polyphase Interpolator + pfb_interpolator_ccf + from gnuradio import filter + from gnuradio.filter import firdes + filter.pfb.interpolator_ccf( + $interp, + $taps) + + set_taps($taps) + + Interpolation + interp + int + + + Taps + taps + None + real_vector + + + in + complex + + + out + complex + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 77ae78e35..3d8e0c932 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -64,7 +64,7 @@ endmacro(expand_h) ######################################################################## # Invoke macro to generate various sources ####################################################################### -expand_h(fir_filter_XXX fff ccc ccf fcc fff fsf scc) +expand_h(fir_filter_XXX ccc ccf fcc fff fsf scc) expand_h(freq_xlating_fir_filter_XXX ccc ccf fcc fcf scf scc) expand_h(interp_fir_filter_XXX ccc ccf fcc fff fsf scc) @@ -102,9 +102,10 @@ install(FILES iir_filter_ffd.h pfb_channelizer_ccf.h pfb_decimator_ccf.h + pfb_interpolator_ccf.h single_pole_iir_filter_cc.h single_pole_iir_filter_ff.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter - COMPONENT "fft_devel" + COMPONENT "filter_devel" ) diff --git a/gr-filter/include/filter/pfb_decimator_ccf.h b/gr-filter/include/filter/pfb_decimator_ccf.h index a933fbb5b..e0b8dce15 100644 --- a/gr-filter/include/filter/pfb_decimator_ccf.h +++ b/gr-filter/include/filter/pfb_decimator_ccf.h @@ -26,7 +26,6 @@ #include #include -#include namespace gr { namespace filter { diff --git a/gr-filter/include/filter/pfb_interpolator_ccf.h b/gr-filter/include/filter/pfb_interpolator_ccf.h new file mode 100644 index 000000000..d32b8b688 --- /dev/null +++ b/gr-filter/include/filter/pfb_interpolator_ccf.h @@ -0,0 +1,118 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_INTERPOLATOR_CCF_H +#define INCLUDED_PFB_INTERPOLATOR_CCF_H + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \class gr_pfb_interpolator_ccf + * + * \brief Polyphase filterbank interpolator with gr_complex input, + * gr_complex output and float taps + * + * \ingroup filter_blk + * \ingroup pfb_blk + * + * This block takes in a signal stream and performs interger up- + * sampling (interpolation) with a polyphase filterbank. The first + * input is the integer specifying how much to interpolate by. The + * second input is a vector (Python list) of floating-point taps + * of the prototype filter. + * + * The filter's taps should be based on the interpolation rate + * specified. That is, the bandwidth specified is relative to the + * bandwidth after interpolation. + * + * For example, using the GNU Radio's firdes utility to building + * filters, we build a low-pass filter with a sampling rate of + * fs, a 3-dB bandwidth of BW and a transition + * bandwidth of TB. We can also specify the out-of-band + * attenuation to use, ATT, and the filter window function (a + * Blackman-harris window in this case). The first input is the + * gain, which is also specified as the interpolation rate so that + * the output levels are the same as the input (this creates an + * overall increase in power). + * + * self._taps = gr.firdes.low_pass_2(interp, interp*fs, BW, TB, + * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * + * The PFB interpolator code takes the taps generated above and + * builds a set of filters. The set contains interp + * number of filters and each filter contains + * ceil(taps.size()/interp) number of taps. Each tap from the + * filter prototype is sequentially inserted into the next + * filter. When all of the input taps are used, the remaining + * filters in the filterbank are filled out with 0's to make sure + * each filter has the same number of taps. + * + * The theory behind this block can be found in Chapter 7.1 of the + * following book. + * + * f. harris, "Multirate Signal Processing for Communication + * Systems," Upper Saddle River, NJ: Prentice Hall, + * Inc. 2004. + */ + + class FILTER_API pfb_interpolator_ccf : virtual public gr_sync_interpolator + { + public: + // gr::filter::pfb_interpolator_ccf::sptr + typedef boost::shared_ptr sptr; + + /*! + * Build the polyphase filterbank interpolator. + * \param interp (unsigned integer) Specifies the interpolation rate to use + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. The taps + * should be generated at the interpolated sampling rate. + */ + static FILTER_API sptr make(unsigned int interp, + const std::vector &taps); + + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + * The taps should be generated at the interpolated sampling rate. + */ + virtual void set_taps(const std::vector &taps) = 0; + + /*! + * Return a vector> of the filterbank taps + */ + virtual std::vector > taps() const = 0; + + /*! + * Print all of the filterbank taps to screen. + */ + virtual void print_taps() = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_PFB_INTERPOLATOR_CCF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 40faa8f0e..d1ddd2a92 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -129,6 +129,7 @@ list(APPEND filter_sources iir_filter_ffd_impl.cc pfb_channelizer_ccf_impl.cc pfb_decimator_ccf_impl.cc + pfb_interpolator_ccf_impl.cc single_pole_iir_filter_cc_impl.cc single_pole_iir_filter_ff_impl.cc ) diff --git a/gr-filter/lib/pfb_decimator_ccf_impl.cc b/gr-filter/lib/pfb_decimator_ccf_impl.cc index 139cd9bec..f9a60cb28 100644 --- a/gr-filter/lib/pfb_decimator_ccf_impl.cc +++ b/gr-filter/lib/pfb_decimator_ccf_impl.cc @@ -31,12 +31,12 @@ namespace gr { namespace filter { pfb_decimator_ccf::sptr - pfb_decimator_ccf::make(unsigned int nfilts, + pfb_decimator_ccf::make(unsigned int decim, const std::vector &taps, unsigned int channel) { return gnuradio::get_initial_sptr - (new pfb_decimator_ccf_impl(nfilts, taps, channel)); + (new pfb_decimator_ccf_impl(decim, taps, channel)); } diff --git a/gr-filter/lib/pfb_interpolator_ccf_impl.cc b/gr-filter/lib/pfb_interpolator_ccf_impl.cc new file mode 100644 index 000000000..572db728b --- /dev/null +++ b/gr-filter/lib/pfb_interpolator_ccf_impl.cc @@ -0,0 +1,107 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pfb_interpolator_ccf_impl.h" +#include + +namespace gr { + namespace filter { + + pfb_interpolator_ccf::sptr + pfb_interpolator_ccf::make(unsigned int interp, + const std::vector &taps) + { + return gnuradio::get_initial_sptr + (new pfb_interpolator_ccf_impl(interp, taps)); + } + + + pfb_interpolator_ccf_impl::pfb_interpolator_ccf_impl(unsigned int interp, + const std::vector &taps) + : gr_sync_interpolator("pfb_interpolator_ccf", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex)), + interp), + polyphase_filterbank(interp, taps), + d_updated (false), d_rate(interp) + { + set_history(d_taps_per_filter+1); + } + + pfb_interpolator_ccf_impl::~pfb_interpolator_ccf_impl() + { + } + + void + pfb_interpolator_ccf_impl::set_taps(const std::vector &taps) + { + gruel::scoped_lock guard(d_mutex); + + polyphase_filterbank::set_taps(taps); + set_history(d_taps_per_filter+1); + d_updated = true; + } + + void + pfb_interpolator_ccf_impl::print_taps() + { + polyphase_filterbank::print_taps(); + } + + std::vector > + pfb_interpolator_ccf_impl::taps() const + { + return polyphase_filterbank::taps(); + } + + int + pfb_interpolator_ccf_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in = (gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + + if(d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + int i = 0, count = 0; + + while(i < noutput_items) { + for(unsigned int j = 0; j < d_rate; j++) { + out[i] = d_filters[j]->filter(&in[count]); + i++; + } + count++; + } + + return i; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/pfb_interpolator_ccf_impl.h b/gr-filter/lib/pfb_interpolator_ccf_impl.h new file mode 100644 index 000000000..7e1fe2ca1 --- /dev/null +++ b/gr-filter/lib/pfb_interpolator_ccf_impl.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_INTERPOLATOR_CCF_IMPL_H +#define INCLUDED_PFB_INTERPOLATOR_CCF_IMPL_H + +#include +#include +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API pfb_interpolator_ccf_impl : public pfb_interpolator_ccf, kernel::polyphase_filterbank + { + private: + bool d_updated; + unsigned int d_rate; + gruel::mutex d_mutex; // mutex to protect set/work access + + public: + pfb_interpolator_ccf_impl(unsigned int interp, + const std::vector &taps); + + ~pfb_interpolator_ccf_impl(); + + void set_taps(const std::vector &taps); + void print_taps(); + std::vector > taps() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_INTERPOLATOR_CCF_IMPL_H */ diff --git a/gr-filter/python/qa_pfb_interpolator.py b/gr-filter/python/qa_pfb_interpolator.py new file mode 100755 index 000000000..f83c57d60 --- /dev/null +++ b/gr-filter/python/qa_pfb_interpolator.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_pfb_interpolator(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_000(self): + N = 1000 # number of samples to use + M = 5 # Number of channels + fs = 1000 # baseband sampling rate + ifs = M*fs # input samp rate to decimator + + taps = gr.firdes.low_pass_2(M, ifs, fs/2, fs/10, + attenuation_dB=80, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + freq = 100 + signal = gr.sig_source_c(fs, gr.GR_COS_WAVE, freq, 1) + head = gr.head(gr.sizeof_gr_complex, N) + pfb = filter.pfb_interpolator_ccf(M, taps) + snk = gr.vector_sink_c() + + self.tb.connect(signal, head, pfb) + self.tb.connect(pfb, snk) + + self.tb.run() + + Ntest = 50 + L = len(snk.data()) + t = map(lambda x: float(x)/ifs, xrange(L)) + + # Create known data as complex sinusoids at freq + # of the channel at the interpolated rate. + phase = 0.62833 + expected_data = map(lambda x: math.cos(2.*math.pi*freq*x+phase) + \ + 1j*math.sin(2.*math.pi*freq*x+phase), t) + + dst_data = snk.data() + + self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + +if __name__ == '__main__': + gr_unittest.run(test_pfb_interpolator, "test_pfb_interpolator.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 0f814f955..cc2e51d96 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -61,6 +61,7 @@ #include "filter/interp_fir_filter_scc.h" #include "filter/pfb_channelizer_ccf.h" #include "filter/pfb_decimator_ccf.h" +#include "filter/pfb_interpolator_ccf.h" #include "filter/single_pole_iir_filter_cc.h" #include "filter/single_pole_iir_filter_ff.h" %} @@ -98,6 +99,7 @@ %include "filter/interp_fir_filter_scc.h" %include "filter/pfb_channelizer_ccf.h" %include "filter/pfb_decimator_ccf.h" +%include "filter/pfb_interpolator_ccf.h" %include "filter/single_pole_iir_filter_cc.h" %include "filter/single_pole_iir_filter_ff.h" @@ -132,5 +134,6 @@ GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); GR_SWIG_BLOCK_MAGIC2(filter, pfb_decimator_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, pfb_interpolator_ccf); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_cc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_ff); -- cgit From e5aead883b8bf82cb0ebd63e825b99b5172048cd Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 00:07:58 -0400 Subject: filter: adding PFB arbitrary resampler (ccf and fff) with GRC and QA. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/pfb_arb_resampler.xml | 61 ++++++ gr-filter/grc/pfb_decimator.xml | 2 +- gr-filter/include/filter/CMakeLists.txt | 2 + gr-filter/include/filter/pfb_arb_resampler_ccf.h | 138 +++++++++++++ gr-filter/include/filter/pfb_arb_resampler_fff.h | 139 +++++++++++++ gr-filter/include/filter/pfb_decimator_ccf.h | 2 +- gr-filter/lib/CMakeLists.txt | 2 + gr-filter/lib/pfb_arb_resampler_ccf_impl.cc | 235 ++++++++++++++++++++++ gr-filter/lib/pfb_arb_resampler_ccf_impl.h | 86 ++++++++ gr-filter/lib/pfb_arb_resampler_fff_impl.cc | 237 +++++++++++++++++++++++ gr-filter/lib/pfb_arb_resampler_fff_impl.h | 85 ++++++++ gr-filter/python/qa_pfb_arb_resampler.py | 95 +++++++++ gr-filter/swig/filter_swig.i | 6 + 15 files changed, 1090 insertions(+), 2 deletions(-) create mode 100644 gr-filter/grc/pfb_arb_resampler.xml create mode 100644 gr-filter/include/filter/pfb_arb_resampler_ccf.h create mode 100644 gr-filter/include/filter/pfb_arb_resampler_fff.h create mode 100644 gr-filter/lib/pfb_arb_resampler_ccf_impl.cc create mode 100644 gr-filter/lib/pfb_arb_resampler_ccf_impl.h create mode 100644 gr-filter/lib/pfb_arb_resampler_fff_impl.cc create mode 100644 gr-filter/lib/pfb_arb_resampler_fff_impl.h create mode 100755 gr-filter/python/qa_pfb_arb_resampler.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 7fc129d8a..3110e0f1b 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -28,6 +28,7 @@ install(FILES hilbert_fc.xml iir_filter_ffd.xml interp_fir_filter_xxx.xml + pfb_arb_resampler.xml pfb_channelizer.xml pfb_decimator.xml pfb_interpolator.xml diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index 0ec9e5dd0..ecc97dd23 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -39,6 +39,7 @@ hilbert_fc iir_filter_ffd interp_fir_filter_xxx + pfb_arb_resampler_xxx pfb_channelizer_ccf pfb_decimator_ccf pfb_interpolator_ccf diff --git a/gr-filter/grc/pfb_arb_resampler.xml b/gr-filter/grc/pfb_arb_resampler.xml new file mode 100644 index 000000000..f3048000a --- /dev/null +++ b/gr-filter/grc/pfb_arb_resampler.xml @@ -0,0 +1,61 @@ + + + + Polyphase Arbitrary Resampler + pfb_arb_resampler_xxx + from gnuradio import filter + from gnuradio.filter import firdes + filter.pfb_arb_resampler_$(type)( + $rrate, + $taps, + $nfilts) + + set_taps($taps) + + Type + type + enum + + + + + Resampling Rate + rrate + real + + + Taps + taps + $type.taps + + + Number of Filters + nfilts + 32 + int + + + in + complex + + + out + complex + + diff --git a/gr-filter/grc/pfb_decimator.xml b/gr-filter/grc/pfb_decimator.xml index 329d59c3d..b0540d3e2 100644 --- a/gr-filter/grc/pfb_decimator.xml +++ b/gr-filter/grc/pfb_decimator.xml @@ -9,7 +9,7 @@ pfb_decimator_ccf from gnuradio import filter from gnuradio.filter import firdes - filter.pfb.decimator_ccf( + filter.pfb_decimator_ccf( $decim, $taps, $channel) diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 3d8e0c932..b0412aaba 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -100,6 +100,8 @@ install(FILES fractional_interpolator_ff.h hilbert_fc.h iir_filter_ffd.h + pfb_arb_resampler_ccf.h + pfb_arb_resampler_fff.h pfb_channelizer_ccf.h pfb_decimator_ccf.h pfb_interpolator_ccf.h diff --git a/gr-filter/include/filter/pfb_arb_resampler_ccf.h b/gr-filter/include/filter/pfb_arb_resampler_ccf.h new file mode 100644 index 000000000..1436674b2 --- /dev/null +++ b/gr-filter/include/filter/pfb_arb_resampler_ccf.h @@ -0,0 +1,138 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_ARB_RESAMPLER_CCF_H +#define INCLUDED_PFB_ARB_RESAMPLER_CCF_H + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \class pfb_arb_resampler_ccf + * + * \brief Polyphase filterbank arbitrary resampler with + * gr_complex input, gr_complex output and float taps + * + * \ingroup filter_blk + * \ingroup pfb_blk + * + * This block takes in a signal stream and performs arbitrary + * resampling. The resampling rate can be any real number + * r. The resampling is done by constructing N + * filters where N is the interpolation rate. We then + * calculate D where D = floor(N/r). + * + * Using N and D, we can perform rational + * resampling where N/D is a rational number close to the + * input rate r where we have N filters and we + * cycle through them as a polyphase filterbank with a stride of + * D so that i+1 = (i + D) % N. + * + * To get the arbitrary rate, we want to interpolate between two + * points. For each value out, we take an output from the current + * filter, i, and the next filter i+1 and then + * linearly interpolate between the two based on the real + * resampling rate we want. + * + * The linear interpolation only provides us with an approximation + * to the real sampling rate specified. The error is a + * quantization error between the two filters we used as our + * interpolation points. To this end, the number of filters, + * N, used determines the quantization error; the larger + * N, the smaller the noise. You can design for a + * specified noise floor by setting the filter size (parameters + * filter_size). The size defaults to 32 filters, which + * is about as good as most implementations need. + * + * The trick with designing this filter is in how to specify the + * taps of the prototype filter. Like the PFB interpolator, the + * taps are specified using the interpolated filter rate. In this + * case, that rate is the input sample rate multiplied by the + * number of filters in the filterbank, which is also the + * interpolation rate. All other values should be relative to this + * rate. + * + * For example, for a 32-filter arbitrary resampler and using the + * GNU Radio's firdes utility to build the filter, we build a + * low-pass filter with a sampling rate of fs, a 3-dB + * bandwidth of BW and a transition bandwidth of + * TB. We can also specify the out-of-band attenuation to + * use, ATT, and the filter window function (a + * Blackman-harris window in this case). The first input is the + * gain of the filter, which we specify here as the interpolation + * rate (32). + * + * self._taps = gr.firdes.low_pass_2(32, 32*fs, BW, TB, + * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * + * The theory behind this block can be found in Chapter 7.5 of + * the following book. + * + * f. harris, "Multirate Signal Processing for Communication + * Systems", Upper Saddle River, NJ: Prentice Hall, Inc. 2004. + */ + + class FILTER_API pfb_arb_resampler_ccf : virtual public gr_block + { + public: + // gr::filter::pfb_arb_resampler_ccf::sptr + typedef boost::shared_ptr sptr; + + /*! + * Build the polyphase filterbank arbitray resampler. + * \param rate (float) Specifies the resampling rate to use + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. The taps + * should be generated at the filter_size sampling rate. + * \param filter_size (unsigned int) The number of filters in the filter bank. This is directly + * related to quantization noise introduced during the resampling. + * Defaults to 32 filters. + */ + static FILTER_API sptr make(float rate, + const std::vector &taps, + unsigned int filter_size=32); + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + */ + virtual void set_taps(const std::vector &taps) = 0; + + /*! + * Return a vector> of the filterbank taps + */ + virtual std::vector > taps() const = 0; + + /*! + * Print all of the filterbank taps to screen. + */ + virtual void print_taps() = 0; + + virtual void set_rate (float rate) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_ARB_RESAMPLER_CCF_H */ diff --git a/gr-filter/include/filter/pfb_arb_resampler_fff.h b/gr-filter/include/filter/pfb_arb_resampler_fff.h new file mode 100644 index 000000000..7449ea0cb --- /dev/null +++ b/gr-filter/include/filter/pfb_arb_resampler_fff.h @@ -0,0 +1,139 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009-2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_ARB_RESAMPLER_FFF_H +#define INCLUDED_PFB_ARB_RESAMPLER_FFF_H + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \class pfb_arb_resampler_fff + * + * \brief Polyphase filterbank arbitrary resampler with + * float input, float output and float taps + * + * \ingroup filter_blk + * \ingroup pfb_blk + * + * This block takes in a signal stream and performs arbitrary + * resampling. The resampling rate can be any real number + * r. The resampling is done by constructing N + * filters where N is the interpolation rate. We then + * calculate D where D = floor(N/r). + * + * Using N and D, we can perform rational + * resampling where N/D is a rational number close to the + * input rate r where we have N filters and we + * cycle through them as a polyphase filterbank with a stride of + * D so that i+1 = (i + D) % N. + * + * To get the arbitrary rate, we want to interpolate between two + * points. For each value out, we take an output from the current + * filter, i, and the next filter i+1 and then + * linearly interpolate between the two based on the real + * resampling rate we want. + * + * The linear interpolation only provides us with an approximation + * to the real sampling rate specified. The error is a + * quantization error between the two filters we used as our + * interpolation points. To this end, the number of filters, + * N, used determines the quantization error; the larger + * N, the smaller the noise. You can design for a + * specified noise floor by setting the filter size (parameters + * filter_size). The size defaults to 32 filters, which + * is about as good as most implementations need. + * + * The trick with designing this filter is in how to specify the + * taps of the prototype filter. Like the PFB interpolator, the + * taps are specified using the interpolated filter rate. In this + * case, that rate is the input sample rate multiplied by the + * number of filters in the filterbank, which is also the + * interpolation rate. All other values should be relative to this + * rate. + * + * For example, for a 32-filter arbitrary resampler and using the + * GNU Radio's firdes utility to build the filter, we build a + * low-pass filter with a sampling rate of fs, a 3-dB + * bandwidth of BW and a transition bandwidth of + * TB. We can also specify the out-of-band attenuation to + * use, ATT, and the filter window function (a + * Blackman-harris window in this case). The first input is the + * gain of the filter, which we specify here as the interpolation + * rate (32). + * + * self._taps = gr.firdes.low_pass_2(32, 32*fs, BW, TB, + * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * + * The theory behind this block can be found in Chapter 7.5 of the + * following book. + * + * f. harris, "Multirate Signal Processing for Communication + * Systems", Upper Saddle River, NJ: Prentice Hall, Inc. 2004. + */ + + class FILTER_API pfb_arb_resampler_fff : virtual public gr_block + { + public: + // gr::filter::pfb_arb_resampler_fff::sptr + typedef boost::shared_ptr sptr; + + /*! + * Build the polyphase filterbank arbitray resampler. + * \param rate (float) Specifies the resampling rate to use + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. The taps + * should be generated at the filter_size sampling rate. + * \param filter_size (unsigned int) The number of filters in the filter bank. This is directly + * related to quantization noise introduced during the resampling. + * Defaults to 32 filters. + */ + static FILTER_API sptr make(float rate, + const std::vector &taps, + unsigned int filter_size=32); + + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param taps (vector/list of floats) The prototype filter to populate the filterbank. + */ + virtual void set_taps(const std::vector &taps) = 0; + + /*! + * Return a vector> of the filterbank taps + */ + virtual std::vector > taps() const = 0; + + /*! + * Print all of the filterbank taps to screen. + */ + virtual void print_taps() = 0; + + virtual void set_rate (float rate) = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_ARB_RESAMPLER_FFF_H */ diff --git a/gr-filter/include/filter/pfb_decimator_ccf.h b/gr-filter/include/filter/pfb_decimator_ccf.h index e0b8dce15..e41f16cd2 100644 --- a/gr-filter/include/filter/pfb_decimator_ccf.h +++ b/gr-filter/include/filter/pfb_decimator_ccf.h @@ -128,4 +128,4 @@ namespace gr { } /* namespace filter */ } /* namespace gr */ -#endif /* INCLUDED_FILTER_PFB_DECIMATOR_CCF_H */ +#endif /* INCLUDED_PFB_DECIMATOR_CCF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index d1ddd2a92..8077baf9c 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -127,6 +127,8 @@ list(APPEND filter_sources fractional_interpolator_ff_impl.cc hilbert_fc_impl.cc iir_filter_ffd_impl.cc + pfb_arb_resampler_ccf_impl.cc + pfb_arb_resampler_fff_impl.cc pfb_channelizer_ccf_impl.cc pfb_decimator_ccf_impl.cc pfb_interpolator_ccf_impl.cc diff --git a/gr-filter/lib/pfb_arb_resampler_ccf_impl.cc b/gr-filter/lib/pfb_arb_resampler_ccf_impl.cc new file mode 100644 index 000000000..bb0906aa5 --- /dev/null +++ b/gr-filter/lib/pfb_arb_resampler_ccf_impl.cc @@ -0,0 +1,235 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pfb_arb_resampler_ccf_impl.h" +#include +#include + +namespace gr { + namespace filter { + + pfb_arb_resampler_ccf::sptr + pfb_arb_resampler_ccf::make(float rate, + const std::vector &taps, + unsigned int filter_size) + { + return gnuradio::get_initial_sptr + (new pfb_arb_resampler_ccf_impl(rate, taps, filter_size)); + } + + + pfb_arb_resampler_ccf_impl::pfb_arb_resampler_ccf_impl(float rate, + const std::vector &taps, + unsigned int filter_size) + : gr_block("pfb_arb_resampler_ccf", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex))), + d_updated (false) + { + d_acc = 0; // start accumulator at 0 + + /* The number of filters is specified by the user as the filter + size; this is also the interpolation rate of the filter. We + use it and the rate provided to determine the decimation + rate. This acts as a rational resampler. The flt_rate is + calculated as the residual between the integer decimation + rate and the real decimation rate and will be used to + determine to interpolation point of the resampling process. + */ + d_int_rate = filter_size; + set_rate(rate); + + // Store the last filter between calls to work + d_last_filter = 0; + + d_start_index = 0; + + d_filters = std::vector(d_int_rate); + d_diff_filters = std::vector(d_int_rate); + + // Create an FIR filter for each channel and zero out the taps + std::vector vtaps(0, d_int_rate); + for(unsigned int i = 0; i < d_int_rate; i++) { + d_filters[i] = new kernel::fir_filter_ccf(1, vtaps); + d_diff_filters[i] = new kernel::fir_filter_ccf(1, vtaps); + } + + // Now, actually set the filters' taps + std::vector dtaps; + create_diff_taps(taps, dtaps); + create_taps(taps, d_taps, d_filters); + create_taps(dtaps, d_dtaps, d_diff_filters); + } + + pfb_arb_resampler_ccf_impl::~pfb_arb_resampler_ccf_impl() + { + for(unsigned int i = 0; i < d_int_rate; i++) { + delete d_filters[i]; + delete d_diff_filters[i]; + } + } + + void + pfb_arb_resampler_ccf_impl::create_taps(const std::vector &newtaps, + std::vector< std::vector > &ourtaps, + std::vector &ourfilter) + { + unsigned int ntaps = newtaps.size(); + d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_int_rate); + + // Create d_numchan vectors to store each channel's taps + ourtaps.resize(d_int_rate); + + // Make a vector of the taps plus fill it out with 0's to fill + // each polyphase filter with exactly d_taps_per_filter + std::vector tmp_taps; + tmp_taps = newtaps; + while((float)(tmp_taps.size()) < d_int_rate*d_taps_per_filter) { + tmp_taps.push_back(0.0); + } + + // Partition the filter + for(unsigned int i = 0; i < d_int_rate; i++) { + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + ourtaps[d_int_rate-1-i] = std::vector(d_taps_per_filter, 0); + for(unsigned int j = 0; j < d_taps_per_filter; j++) { + ourtaps[d_int_rate - 1 - i][j] = tmp_taps[i + j*d_int_rate]; + } + + // Build a filter for each channel and add it's taps to it + ourfilter[i]->set_taps(ourtaps[d_int_rate-1-i]); + } + + // Set the history to ensure enough input items for each filter + set_history (d_taps_per_filter + 1); + + d_updated = true; + } + + void + pfb_arb_resampler_ccf_impl::create_diff_taps(const std::vector &newtaps, + std::vector &difftaps) + { + // Calculate the differential taps (derivative filter) by taking the difference + // between two taps. Duplicate the last one to make both filters the same length. + float tap; + difftaps.clear(); + for(unsigned int i = 0; i < newtaps.size()-1; i++) { + tap = newtaps[i+1] - newtaps[i]; + difftaps.push_back(tap); + } + difftaps.push_back(tap); + } + + void + pfb_arb_resampler_ccf_impl::set_taps(const std::vector &taps) + { + gruel::scoped_lock guard(d_mutex); + } + + std::vector > + pfb_arb_resampler_ccf_impl::taps() const + { + return d_taps; + } + + void + pfb_arb_resampler_ccf_impl::print_taps() + { + unsigned int i, j; + for(i = 0; i < d_int_rate; i++) { + printf("filter[%d]: [", i); + for(j = 0; j < d_taps_per_filter; j++) { + printf(" %.4e", d_taps[i][j]); + } + printf("]\n"); + } + } + + void + pfb_arb_resampler_ccf_impl::set_rate(float rate) + { + d_dec_rate = (unsigned int)floor(d_int_rate/rate); + d_flt_rate = (d_int_rate/rate) - d_dec_rate; + set_relative_rate(rate); + } + + int + pfb_arb_resampler_ccf_impl::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in = (gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + + if(d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + int i = 0, count = d_start_index; + unsigned int j; + gr_complex o0, o1; + + // Restore the last filter position + j = d_last_filter; + + // produce output as long as we can and there are enough input samples + int max_input = ninput_items[0] - (int)d_taps_per_filter; + while((i < noutput_items) && (count < max_input)) { + // start j by wrapping around mod the number of channels + while((j < d_int_rate) && (i < noutput_items)) { + // Take the current filter and derivative filter output + o0 = d_filters[j]->filter(&in[count]); + o1 = d_diff_filters[j]->filter(&in[count]); + + out[i] = o0 + o1*d_acc; // linearly interpolate between samples + i++; + + // Adjust accumulator and index into filterbank + d_acc += d_flt_rate; + j += d_dec_rate + (int)floor(d_acc); + d_acc = fmodf(d_acc, 1.0); + } + if(i < noutput_items) { // keep state for next entry + float ss = (int)(j / d_int_rate); // number of items to skip ahead by + count += ss; // we have fully consumed another input + j = j % d_int_rate; // roll filter around + } + } + + // Store the current filter position and start of next sample + d_last_filter = j; + d_start_index = std::max(0, count - ninput_items[0]); + + // consume all we've processed but no more than we can + consume_each(std::min(count, ninput_items[0])); + return i; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/pfb_arb_resampler_ccf_impl.h b/gr-filter/lib/pfb_arb_resampler_ccf_impl.h new file mode 100644 index 000000000..8e7e993cb --- /dev/null +++ b/gr-filter/lib/pfb_arb_resampler_ccf_impl.h @@ -0,0 +1,86 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_ARB_RESAMPLER_CCF_IMPL_H +#define INCLUDED_PFB_ARB_RESAMPLER_CCF_IMPL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API pfb_arb_resampler_ccf_impl : public pfb_arb_resampler_ccf + { + private: + std::vector d_filters; + std::vector d_diff_filters; + std::vector< std::vector > d_taps; + std::vector< std::vector > d_dtaps; + unsigned int d_int_rate; // the number of filters (interpolation rate) + unsigned int d_dec_rate; // the stride through the filters (decimation rate) + float d_flt_rate; // residual rate for the linear interpolation + float d_acc; + unsigned int d_last_filter; + int d_start_index; + unsigned int d_taps_per_filter; + bool d_updated; + gruel::mutex d_mutex; // mutex to protect set/work access + + void create_diff_taps(const std::vector &newtaps, + std::vector &difftaps); + + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param newtaps (vector of floats) The prototype filter to populate the filterbank. + * The taps should be generated at the interpolated sampling rate. + * \param ourtaps (vector of floats) Reference to our internal member of holding the taps. + * \param ourfilter (vector of filters) Reference to our internal filter to set the taps for. + */ + void create_taps(const std::vector &newtaps, + std::vector< std::vector > &ourtaps, + std::vector &ourfilter); + + public: + pfb_arb_resampler_ccf_impl(float rate, + const std::vector &taps, + unsigned int filter_size); + + ~pfb_arb_resampler_ccf_impl(); + + void set_taps(const std::vector &taps); + std::vector > taps() const; + void print_taps(); + void set_rate(float rate); + + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_ARB_RESAMPLER_CCF_IMPL_H */ diff --git a/gr-filter/lib/pfb_arb_resampler_fff_impl.cc b/gr-filter/lib/pfb_arb_resampler_fff_impl.cc new file mode 100644 index 000000000..79c19655a --- /dev/null +++ b/gr-filter/lib/pfb_arb_resampler_fff_impl.cc @@ -0,0 +1,237 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009-2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pfb_arb_resampler_fff_impl.h" +#include +#include + +namespace gr { + namespace filter { + + pfb_arb_resampler_fff::sptr + pfb_arb_resampler_fff::make(float rate, + const std::vector &taps, + unsigned int filter_size) + { + return gnuradio::get_initial_sptr + (new pfb_arb_resampler_fff_impl(rate, taps, filter_size)); + } + + + pfb_arb_resampler_fff_impl::pfb_arb_resampler_fff_impl(float rate, + const std::vector &taps, + unsigned int filter_size) + : gr_block("pfb_arb_resampler_fff", + gr_make_io_signature(1, 1, sizeof(float)), + gr_make_io_signature(1, 1, sizeof(float))), + d_updated(false) + { + d_acc = 0; // start accumulator at 0 + + /* The number of filters is specified by the user as the filter + size; this is also the interpolation rate of the filter. We + use it and the rate provided to determine the decimation + rate. This acts as a rational resampler. The flt_rate is + calculated as the residual between the integer decimation + rate and the real decimation rate and will be used to + determine to interpolation point of the resampling process. + */ + d_int_rate = filter_size; + set_rate(rate); + + // Store the last filter between calls to work + d_last_filter = 0; + + d_start_index = 0; + + d_filters = std::vector(d_int_rate); + d_diff_filters = std::vector(d_int_rate); + + // Create an FIR filter for each channel and zero out the taps + std::vector vtaps(0, d_int_rate); + for(unsigned int i = 0; i < d_int_rate; i++) { + d_filters[i] = new kernel::fir_filter_fff(1, vtaps); + d_diff_filters[i] = new kernel::fir_filter_fff(1, vtaps); + } + + // Now, actually set the filters' taps + std::vector dtaps; + create_diff_taps(taps, dtaps); + create_taps(taps, d_taps, d_filters); + create_taps(dtaps, d_dtaps, d_diff_filters); + } + + pfb_arb_resampler_fff_impl::~pfb_arb_resampler_fff_impl() + { + for(unsigned int i = 0; i < d_int_rate; i++) { + delete d_filters[i]; + delete d_diff_filters[i]; + } + } + + void + pfb_arb_resampler_fff_impl::create_taps(const std::vector &newtaps, + std::vector< std::vector > &ourtaps, + std::vector &ourfilter) + { + unsigned int ntaps = newtaps.size(); + d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_int_rate); + + // Create d_numchan vectors to store each channel's taps + ourtaps.resize(d_int_rate); + + // Make a vector of the taps plus fill it out with 0's to fill + // each polyphase filter with exactly d_taps_per_filter + std::vector tmp_taps; + tmp_taps = newtaps; + while((float)(tmp_taps.size()) < d_int_rate*d_taps_per_filter) { + tmp_taps.push_back(0.0); + } + + // Partition the filter + for(unsigned int i = 0; i < d_int_rate; i++) { + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + ourtaps[d_int_rate-1-i] = std::vector(d_taps_per_filter, 0); + for(unsigned int j = 0; j < d_taps_per_filter; j++) { + ourtaps[d_int_rate - 1 - i][j] = tmp_taps[i + j*d_int_rate]; + } + + // Build a filter for each channel and add it's taps to it + ourfilter[i]->set_taps(ourtaps[d_int_rate-1-i]); + } + + // Set the history to ensure enough input items for each filter + set_history(d_taps_per_filter + 1); + + d_updated = true; + } + + void + pfb_arb_resampler_fff_impl::create_diff_taps(const std::vector &newtaps, + std::vector &difftaps) + { + // Calculate the differential taps (derivative filter) by taking the difference + // between two taps. Duplicate the last one to make both filters the same length. + float tap; + difftaps.clear(); + for(unsigned int i = 0; i < newtaps.size()-1; i++) { + tap = newtaps[i+1] - newtaps[i]; + difftaps.push_back(tap); + } + difftaps.push_back(tap); + } + + void + pfb_arb_resampler_fff_impl::set_taps(const std::vector &taps) + { + gruel::scoped_lock guard(d_mutex); + } + + std::vector > + pfb_arb_resampler_fff_impl::taps() const + { + return d_taps; + } + + void + pfb_arb_resampler_fff_impl::print_taps() + { + unsigned int i, j; + for(i = 0; i < d_int_rate; i++) { + printf("filter[%d]: [", i); + for(j = 0; j < d_taps_per_filter; j++) { + printf(" %.4e", d_taps[i][j]); + } + printf("]\n"); + } + } + + void + pfb_arb_resampler_fff_impl::set_rate(float rate) + { + d_dec_rate = (unsigned int)floor(d_int_rate/rate); + d_flt_rate = (d_int_rate/rate) - d_dec_rate; + set_relative_rate(rate); + } + + int + pfb_arb_resampler_fff_impl::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gruel::scoped_lock guard(d_mutex); + + float *in = (float*)input_items[0]; + float *out = (float*)output_items[0]; + + if(d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + int i = 0, count = d_start_index; + unsigned int j; + float o0, o1; + + // Restore the last filter position + j = d_last_filter; + + // produce output as long as we can and there are enough input samples + int max_input = ninput_items[0] - (int)d_taps_per_filter; + while((i < noutput_items) && (count < max_input)) { + // start j by wrapping around mod the number of channels + while((j < d_int_rate) && (i < noutput_items)) { + // Take the current filter and derivative filter output + o0 = d_filters[j]->filter(&in[count]); + o1 = d_diff_filters[j]->filter(&in[count]); + + out[i] = o0 + o1*d_acc; // linearly interpolate between samples + i++; + + // Adjust accumulator and index into filterbank + d_acc += d_flt_rate; + j += d_dec_rate + (int)floor(d_acc); + d_acc = fmodf(d_acc, 1.0); + } + if(i < noutput_items) { // keep state for next entry + float ss = (int)(j / d_int_rate); // number of items to skip ahead by + count += ss; // we have fully consumed another input + j = j % d_int_rate; // roll filter around + } + } + + // Store the current filter position and start of next sample + d_last_filter = j; + d_start_index = std::max(0, count - ninput_items[0]); + + // consume all we've processed but no more than we can + consume_each(std::min(count, ninput_items[0])); + return i; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/pfb_arb_resampler_fff_impl.h b/gr-filter/lib/pfb_arb_resampler_fff_impl.h new file mode 100644 index 000000000..54e01375a --- /dev/null +++ b/gr-filter/lib/pfb_arb_resampler_fff_impl.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009-2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_ARB_RESAMPLER_FFF_IMPL_H +#define INCLUDED_PFB_ARB_RESAMPLER_FFF_IMPL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API pfb_arb_resampler_fff_impl : public pfb_arb_resampler_fff + { + private: + std::vector d_filters; + std::vector d_diff_filters; + std::vector< std::vector > d_taps; + std::vector< std::vector > d_dtaps; + unsigned int d_int_rate; // the number of filters (interpolation rate) + unsigned int d_dec_rate; // the stride through the filters (decimation rate) + float d_flt_rate; // residual rate for the linear interpolation + float d_acc; + unsigned int d_last_filter; + int d_start_index; + unsigned int d_taps_per_filter; + bool d_updated; + gruel::mutex d_mutex; // mutex to protect set/work access + + void create_diff_taps(const std::vector &newtaps, + std::vector &difftaps); + + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param newtaps (vector of floats) The prototype filter to populate the filterbank. + * The taps should be generated at the interpolated sampling rate. + * \param ourtaps (vector of floats) Reference to our internal member of holding the taps. + * \param ourfilter (vector of filters) Reference to our internal filter to set the taps for. + */ + void create_taps(const std::vector &newtaps, + std::vector< std::vector > &ourtaps, + std::vector &ourfilter); + public: + pfb_arb_resampler_fff_impl(float rate, + const std::vector &taps, + unsigned int filter_size); + + ~pfb_arb_resampler_fff_impl(); + + void set_taps(const std::vector &taps); + std::vector > taps() const; + void print_taps(); + void set_rate(float rate); + + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_ARB_RESAMPLER_FFF_IMPL_H */ diff --git a/gr-filter/python/qa_pfb_arb_resampler.py b/gr-filter/python/qa_pfb_arb_resampler.py new file mode 100755 index 000000000..655b680f0 --- /dev/null +++ b/gr-filter/python/qa_pfb_arb_resampler.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_pfb_arb_resampler(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_fff_000(self): + N = 1000 # number of samples to use + fs = 1000 # baseband sampling rate + rrate = 1.123 # resampling rate + + nfilts = 32 + taps = gr.firdes.low_pass_2(nfilts, nfilts*fs, fs/2, fs/10, + attenuation_dB=80, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + freq = 100 + signal = gr.sig_source_f(fs, gr.GR_SIN_WAVE, freq, 1) + head = gr.head(gr.sizeof_float, N) + pfb = filter.pfb_arb_resampler_fff(rrate, taps) + snk = gr.vector_sink_f() + + self.tb.connect(signal, head, pfb, snk) + self.tb.run() + + Ntest = 50 + L = len(snk.data()) + t = map(lambda x: float(x)/(fs*rrate), xrange(L)) + + phase = 0.53013 + expected_data = map(lambda x: math.sin(2.*math.pi*freq*x+phase), t) + + dst_data = snk.data() + self.assertFloatTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) + + def test_ccf_000(self): + N = 1000 # number of samples to use + fs = 1000 # baseband sampling rate + rrate = 1.123 # resampling rate + + nfilts = 32 + taps = gr.firdes.low_pass_2(nfilts, nfilts*fs, fs/2, fs/10, + attenuation_dB=80, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + freq = 100 + signal = gr.sig_source_c(fs, gr.GR_SIN_WAVE, freq, 1) + head = gr.head(gr.sizeof_gr_complex, N) + pfb = filter.pfb_arb_resampler_ccf(rrate, taps) + snk = gr.vector_sink_c() + + self.tb.connect(signal, head, pfb, snk) + self.tb.run() + + Ntest = 50 + L = len(snk.data()) + t = map(lambda x: float(x)/(fs*rrate), xrange(L)) + + phase = 0.53013 + expected_data = map(lambda x: math.cos(2.*math.pi*freq*x+phase) + \ + 1j*math.sin(2.*math.pi*freq*x+phase), t) + + dst_data = snk.data() + self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) + +if __name__ == '__main__': + gr_unittest.run(test_pfb_arb_resampler, "test_pfb_arb_resampler.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index cc2e51d96..b72a67f15 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -59,6 +59,8 @@ #include "filter/interp_fir_filter_fff.h" #include "filter/interp_fir_filter_fsf.h" #include "filter/interp_fir_filter_scc.h" +#include "filter/pfb_arb_resampler_ccf.h" +#include "filter/pfb_arb_resampler_fff.h" #include "filter/pfb_channelizer_ccf.h" #include "filter/pfb_decimator_ccf.h" #include "filter/pfb_interpolator_ccf.h" @@ -97,6 +99,8 @@ %include "filter/interp_fir_filter_fff.h" %include "filter/interp_fir_filter_fsf.h" %include "filter/interp_fir_filter_scc.h" +%include "filter/pfb_arb_resampler_ccf.h" +%include "filter/pfb_arb_resampler_fff.h" %include "filter/pfb_channelizer_ccf.h" %include "filter/pfb_decimator_ccf.h" %include "filter/pfb_interpolator_ccf.h" @@ -132,6 +136,8 @@ GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fcc); GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, interp_fir_filter_scc); +GR_SWIG_BLOCK_MAGIC2(filter, pfb_arb_resampler_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, pfb_arb_resampler_fff); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); GR_SWIG_BLOCK_MAGIC2(filter, pfb_decimator_ccf); GR_SWIG_BLOCK_MAGIC2(filter, pfb_interpolator_ccf); -- cgit From 5ab78aaeedb87f4a30c41dd9e7ede56b5dc01d04 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 18:10:59 -0400 Subject: filter: adding rational resampler with GRC and QA. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/rational_resampler_base_xxx.xml | 86 +++++++ gr-filter/include/filter/CMakeLists.txt | 1 + .../include/filter/rational_resampler_base_XXX.h.t | 61 +++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/fir_filter_XXX_impl.h.t | 1 - .../lib/rational_resampler_base_XXX_impl.cc.t | 174 ++++++++++++++ gr-filter/lib/rational_resampler_base_XXX_impl.h.t | 72 ++++++ gr-filter/python/qa_rational_resampler.py | 257 +++++++++++++++++++++ gr-filter/swig/filter_swig.i | 18 ++ 11 files changed, 672 insertions(+), 1 deletion(-) create mode 100644 gr-filter/grc/rational_resampler_base_xxx.xml create mode 100644 gr-filter/include/filter/rational_resampler_base_XXX.h.t create mode 100644 gr-filter/lib/rational_resampler_base_XXX_impl.cc.t create mode 100644 gr-filter/lib/rational_resampler_base_XXX_impl.h.t create mode 100755 gr-filter/python/qa_rational_resampler.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 3110e0f1b..0747c7240 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -32,6 +32,7 @@ install(FILES pfb_channelizer.xml pfb_decimator.xml pfb_interpolator.xml + rational_resampler_base_xxx.xml single_pole_iir_filter_xx.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index ecc97dd23..de840a3fd 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -43,6 +43,7 @@ pfb_channelizer_ccf pfb_decimator_ccf pfb_interpolator_ccf + rational_resampler_base_xxx single_pole_iir_filter_xx diff --git a/gr-filter/grc/rational_resampler_base_xxx.xml b/gr-filter/grc/rational_resampler_base_xxx.xml new file mode 100644 index 000000000..399bfc74c --- /dev/null +++ b/gr-filter/grc/rational_resampler_base_xxx.xml @@ -0,0 +1,86 @@ + + + + Rational Resampler Base + rational_resampler_base_xxx + from gnuradio import filter + from gnuradio.filter import firdes + filter.rational_resampler_base_$(type)($interp, $decim, $taps) + set_taps($taps) + + Type + type + enum + + + + + + + + + Interpolation + interp + 1 + int + + + Decimation + decim + 1 + int + + + Taps + taps + $type.taps + + + in + $type.input + + + out + $type.output + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index b0412aaba..9174bfc84 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -67,6 +67,7 @@ endmacro(expand_h) expand_h(fir_filter_XXX ccc ccf fcc fff fsf scc) expand_h(freq_xlating_fir_filter_XXX ccc ccf fcc fcf scf scc) expand_h(interp_fir_filter_XXX ccc ccf fcc fff fsf scc) +expand_h(rational_resampler_base_XXX ccc ccf fcc fff fsf scc) add_custom_target(filter_generated_includes DEPENDS ${generated_includes} diff --git a/gr-filter/include/filter/rational_resampler_base_XXX.h.t b/gr-filter/include/filter/rational_resampler_base_XXX.h.t new file mode 100644 index 000000000..c37eb30a7 --- /dev/null +++ b/gr-filter/include/filter/rational_resampler_base_XXX.h.t @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include + + +namespace gr { + namespace filter { + + /*! + * \brief Rational Resampling Polyphase FIR filter with @I_TYPE@ + * input, @O_TYPE@ output and @TAP_TYPE@ taps. + * + *\ingroup filter_blk + */ + class FILTER_API @NAME@ : virtual public gr_block + { + public: + // gr::filter::@BASE_NAME@::sptr + typedef boost::shared_ptr<@BASE_NAME@> sptr; + + static FILTER_API sptr make(unsigned interpolation, + unsigned decimation, + const std::vector<@TAP_TYPE@> &taps); + + virtual unsigned interpolation() const = 0; + virtual unsigned decimation() const = 0; + + virtual void set_taps(const std::vector<@TAP_TYPE@> &taps) = 0; + virtual std::vector<@TAP_TYPE@> taps() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 8077baf9c..b3d456ec9 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -83,6 +83,7 @@ endmacro(expand_cc) expand_cc(fir_filter_XXX_impl ccc ccf fcc fff fsf scc) expand_cc(freq_xlating_fir_filter_XXX_impl ccc ccf fcc fcf scf scc) expand_cc(interp_fir_filter_XXX_impl ccc ccf fcc fff fsf scc) +expand_cc(rational_resampler_base_XXX_impl ccc ccf fcc fff fsf scc) ######################################################################## diff --git a/gr-filter/lib/fir_filter_XXX_impl.h.t b/gr-filter/lib/fir_filter_XXX_impl.h.t index d5bf7104d..ef0a7adfe 100644 --- a/gr-filter/lib/fir_filter_XXX_impl.h.t +++ b/gr-filter/lib/fir_filter_XXX_impl.h.t @@ -25,7 +25,6 @@ #ifndef @GUARD_NAME@ #define @GUARD_NAME@ -#include #include #include diff --git a/gr-filter/lib/rational_resampler_base_XXX_impl.cc.t b/gr-filter/lib/rational_resampler_base_XXX_impl.cc.t new file mode 100644 index 000000000..2e9161eeb --- /dev/null +++ b/gr-filter/lib/rational_resampler_base_XXX_impl.cc.t @@ -0,0 +1,174 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "@IMPL_NAME@.h" +#include +#include +#include + +namespace gr { + namespace filter { + + @BASE_NAME@::sptr + @BASE_NAME@::make(unsigned interpolation, + unsigned decimation, + const std::vector<@TAP_TYPE@> &taps) + { + return gnuradio::get_initial_sptr + (new @IMPL_NAME@(interpolation, decimation, taps)); + } + + @IMPL_NAME@::@IMPL_NAME@(unsigned interpolation, + unsigned decimation, + const std::vector<@TAP_TYPE@> &taps) + : gr_block("@BASE_NAME@", + gr_make_io_signature(1, 1, sizeof(@I_TYPE@)), + gr_make_io_signature(1, 1, sizeof(@O_TYPE@))), + d_history(1), + d_interpolation(interpolation), + d_decimation(decimation), + d_ctr(0), + d_firs(interpolation), + d_updated(false) + { + if(interpolation == 0) + throw std::out_of_range("@IMPL_NAME@: interpolation must be > 0"); + if(decimation == 0) + throw std::out_of_range("@IMPL_NAME@: decimation must be > 0"); + + set_relative_rate(1.0 * interpolation / decimation); + set_output_multiple(1); + + std::vector<@TAP_TYPE@> dummy_taps; + + for(unsigned i = 0; i < interpolation; i++) { + d_firs[i] = new kernel::@FIR_TYPE@(1, dummy_taps); + } + + set_taps(taps); + install_taps(d_new_taps); + } + + @IMPL_NAME@::~@IMPL_NAME@() + { + int interp = interpolation(); + for(int i = 0; i < interp; i++) { + delete d_firs[i]; + } + } + + void + @IMPL_NAME@::set_taps(const std::vector<@TAP_TYPE@> &taps) + { + d_new_taps = taps; + d_updated = true; + + // round up length to a multiple of the interpolation factor + int n = taps.size() % interpolation(); + if(n > 0) { + n = interpolation() - n; + while(n-- > 0) { + d_new_taps.insert(d_new_taps.begin(), 0); + } + } + + assert(d_new_taps.size() % interpolation() == 0); + } + + void + @IMPL_NAME@::install_taps(const std::vector<@TAP_TYPE@> &taps) + { + int nfilters = interpolation(); + int nt = taps.size() / nfilters; + + assert(nt * nfilters == (int) taps.size()); + + std::vector< std::vector <@TAP_TYPE@> > xtaps(nfilters); + + for(int n = 0; n < nfilters; n++) + xtaps[n].resize (nt); + + for(int i = 0; i < (int)taps.size(); i++) + xtaps[i % nfilters][i / nfilters] = taps[i]; + + for(int n = 0; n < nfilters; n++) + d_firs[n]->set_taps(xtaps[n]); + + set_history(nt); + d_updated = false; + } + + std::vector<@TAP_TYPE@> + @IMPL_NAME@::taps() const + { + return d_new_taps; + } + + void + @IMPL_NAME@::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + int nreqd = std::max((unsigned)1, (int)((double) (noutput_items+1) * \ + decimation() / interpolation()) + history() - 1); + unsigned ninputs = ninput_items_required.size(); + for(unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = nreqd; + } + + int + @IMPL_NAME@::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const @I_TYPE@ *in = (const @I_TYPE@ *)input_items[0]; + @O_TYPE@ *out = (@O_TYPE@ *)output_items[0]; + + if(d_updated) { + install_taps(d_new_taps); + return 0; // history requirement may have increased. + } + + unsigned int ctr = d_ctr; + + int i = 0; + while(i < noutput_items) { + out[i++] = d_firs[ctr]->filter(in); + ctr += decimation(); + while(ctr >= interpolation()) { + ctr -= interpolation(); + in++; + } + } + + d_ctr = ctr; + consume_each(in - (@I_TYPE@*)input_items[0]); + return i; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/rational_resampler_base_XXX_impl.h.t b/gr-filter/lib/rational_resampler_base_XXX_impl.h.t new file mode 100644 index 000000000..4396656da --- /dev/null +++ b/gr-filter/lib/rational_resampler_base_XXX_impl.h.t @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API @IMPL_NAME@ : public @BASE_NAME@ + { + private: + unsigned d_history; + unsigned d_interpolation; + unsigned d_decimation; + unsigned d_ctr; + std::vector<@TAP_TYPE@> d_new_taps; + std::vector d_firs; + bool d_updated; + + void install_taps(const std::vector<@TAP_TYPE@> &taps); + + public: + @IMPL_NAME@(unsigned interpolation, unsigned decimation, + const std::vector<@TAP_TYPE@> &taps); + + ~@IMPL_NAME@(); + + unsigned history() const { return d_history; } + void set_history(unsigned history) { d_history = history; } + + unsigned interpolation() const { return d_interpolation; } + unsigned decimation() const { return d_decimation; } + + void set_taps(const std::vector<@TAP_TYPE@> &taps); + std::vector<@TAP_TYPE@> taps() const; + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-filter/python/qa_rational_resampler.py b/gr-filter/python/qa_rational_resampler.py new file mode 100755 index 000000000..eb86ef542 --- /dev/null +++ b/gr-filter/python/qa_rational_resampler.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2007,2010 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math +import random +import sys + + +def random_floats(n): + r = [] + for x in xrange(n): + # r.append(float(random.randint(-32768, 32768))) + r.append(float(random.random())) + return tuple(r) + + +def reference_dec_filter(src_data, decim, taps): + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.fir_filter_fff(decim, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + tb = None + return result_data + +def reference_interp_filter(src_data, interp, taps): + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.interp_fir_filter_fff(interp, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + tb = None + return result_data + +def reference_interp_dec_filter(src_data, interp, decim, taps): + tb = gr.top_block() + src = gr.vector_source_f(src_data) + up = filter.interp_fir_filter_fff(interp, (1,)) + dn = filter.fir_filter_fff(decim, taps) + dst = gr.vector_sink_f() + tb.connect(src, up, dn, dst) + tb.run() + result_data = dst.data() + tb = None + return result_data + + +class test_rational_resampler (gr_unittest.TestCase): + + def setUp(self): + random.seed(0) + + def tearDown(self): + pass + + + def test_000_1_to_1(self): + taps = (-4, 5) + src_data = (234, -4, 23, -56, 45, 98, -23, -7) + xr = (1186, -112, 339, -460, -167, 582) + expected_result = tuple([float(x) for x in xr]) + + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.rational_resampler_base_fff(1, 1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op) + tb.connect(op, dst) + tb.run() + result_data = dst.data() + self.assertEqual(expected_result, result_data) + + def test_001_interp(self): + taps = [1, 10, 100, 1000, 10000] + src_data = (0, 2, 3, 5, 7, 11, 13, 17) + interpolation = 3 + xr = (0,2,20,200,2003,20030, + 300,3005,30050, + 500,5007,50070, + 700,7011,70110, + 1100,11013,110130, + 1300,13017,130170, + 1700.0,17000.0,170000.0) + expected_result = tuple([float(x) for x in xr]) + + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.rational_resampler_base_fff(interpolation, 1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op) + tb.connect(op, dst) + tb.run() + result_data = dst.data() + self.assertEqual(expected_result, result_data) + + def test_002_interp(self): + taps = random_floats(31) + src_data = random_floats(10000) + interpolation = 3 + + expected_result = reference_interp_filter(src_data, interpolation, taps) + + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.rational_resampler_base_fff(interpolation, 1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op) + tb.connect(op, dst) + tb.run() + result_data = dst.data() + + N = 1000 + offset = len(taps)-1 + self.assertEqual(expected_result[offset:offset+N], result_data[0:N]) + + def xtest_003_interp(self): + taps = random_floats(9) + src_data = random_floats(10000) + decimation = 3 + + expected_result = reference_dec_filter(src_data, decimation, taps) + + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.rational_resampler_base_fff(1, decimation, taps) + dst = gr.vector_sink_f() + tb.connect(src, op) + tb.connect(op, dst) + tb.run() + result_data = dst.data() + + N = 10 + offset = 10#len(taps)-1 + print expected_result[100+offset:100+offset+N] + print result_data[100:100+N] + #self.assertEqual(expected_result[offset:offset+N], result_data[0:N]) + + # FIXME disabled. Triggers hang on SuSE 10.0 + def xtest_004_decim_random_vals(self): + MAX_TAPS = 9 + MAX_DECIM = 7 + OUTPUT_LEN = 9 + + random.seed(0) # we want reproducibility + + for ntaps in xrange(1, MAX_TAPS + 1): + for decim in xrange(1, MAX_DECIM+1): + for ilen in xrange(ntaps + decim, ntaps + OUTPUT_LEN*decim): + src_data = random_floats(ilen) + taps = random_floats(ntaps) + expected_result = reference_dec_filter(src_data, decim, taps) + + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.rational_resampler_base_fff(1, decim, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + tb = None + result_data = dst.data() + + L1 = len(result_data) + L2 = len(expected_result) + L = min(L1, L2) + if False: + sys.stderr.write('delta = %2d: ntaps = %d decim = %d ilen = %d\n' % (L2 - L1, ntaps, decim, ilen)) + sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' % + (len(result_data), len(expected_result))) + self.assertEqual(expected_result[0:L], result_data[0:L]) + + + # FIXME disabled. Triggers hang on SuSE 10.0 + def xtest_005_interp_random_vals(self): + MAX_TAPS = 9 + MAX_INTERP = 7 + INPUT_LEN = 9 + + random.seed(0) # we want reproducibility + + for ntaps in xrange(1, MAX_TAPS + 1): + for interp in xrange(1, MAX_INTERP+1): + for ilen in xrange(ntaps, ntaps + INPUT_LEN): + src_data = random_floats(ilen) + taps = random_floats(ntaps) + expected_result = reference_interp_filter(src_data, interp, taps) + + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.rational_resampler_base_fff(interp, 1, taps) + dst = gr.vector_sink_f() + tb.connect(src, op, dst) + tb.run() + tb = None + result_data = dst.data() + L1 = len(result_data) + L2 = len(expected_result) + L = min(L1, L2) + #if True or abs(L1-L2) > 1: + if False: + sys.stderr.write('delta = %2d: ntaps = %d interp = %d ilen = %d\n' % (L2 - L1, ntaps, interp, ilen)) + #sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' % + # (len(result_data), len(expected_result))) + #self.assertEqual(expected_result[0:L], result_data[0:L]) + # FIXME check first ntaps+1 answers + self.assertEqual(expected_result[ntaps+1:L], result_data[ntaps+1:L]) + + def test_006_interp_decim(self): + taps = random_floats(31) + src_data = random_floats(10000) + interp = 3 + decimation = 2 + + expected_result = reference_interp_dec_filter(src_data, interp, decimation, taps) + + tb = gr.top_block() + src = gr.vector_source_f(src_data) + op = filter.rational_resampler_base_fff(interp, decimation, taps) + dst = gr.vector_sink_f() + tb.connect(src, op) + tb.connect(op, dst) + tb.run() + result_data = dst.data() + + N = 1000 + offset = len(taps)/2 + self.assertFloatTuplesAlmostEqual(expected_result[offset:offset+N], result_data[0:N], 5) + + +if __name__ == '__main__': + # FIXME: Disabled, see ticket:210 + gr_unittest.run(test_rational_resampler, "test_rational_resampler.xml") + diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index b72a67f15..5a8ebf975 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -64,6 +64,12 @@ #include "filter/pfb_channelizer_ccf.h" #include "filter/pfb_decimator_ccf.h" #include "filter/pfb_interpolator_ccf.h" +#include "filter/rational_resampler_base_ccc.h" +#include "filter/rational_resampler_base_ccf.h" +#include "filter/rational_resampler_base_fcc.h" +#include "filter/rational_resampler_base_fff.h" +#include "filter/rational_resampler_base_fsf.h" +#include "filter/rational_resampler_base_scc.h" #include "filter/single_pole_iir_filter_cc.h" #include "filter/single_pole_iir_filter_ff.h" %} @@ -104,6 +110,12 @@ %include "filter/pfb_channelizer_ccf.h" %include "filter/pfb_decimator_ccf.h" %include "filter/pfb_interpolator_ccf.h" +%include "filter/rational_resampler_base_ccc.h" +%include "filter/rational_resampler_base_ccf.h" +%include "filter/rational_resampler_base_fcc.h" +%include "filter/rational_resampler_base_fff.h" +%include "filter/rational_resampler_base_fsf.h" +%include "filter/rational_resampler_base_scc.h" %include "filter/single_pole_iir_filter_cc.h" %include "filter/single_pole_iir_filter_ff.h" @@ -141,5 +153,11 @@ GR_SWIG_BLOCK_MAGIC2(filter, pfb_arb_resampler_fff); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); GR_SWIG_BLOCK_MAGIC2(filter, pfb_decimator_ccf); GR_SWIG_BLOCK_MAGIC2(filter, pfb_interpolator_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_fcc); +GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_fff); +GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_fsf); +GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_scc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_cc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_ff); -- cgit From fad32dc397d731493dc88367d2b4648b35bb905b Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 20:36:04 -0400 Subject: filter: adding PFB synthesis filter with GRC and QA. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/grc/pfb_synthesizer.xml | 57 +++++ gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/pfb_synthesizer_ccf.h | 107 +++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/pfb_synthesizer_ccf_impl.cc | 288 +++++++++++++++++++++++++ gr-filter/lib/pfb_synthesizer_ccf_impl.h | 85 ++++++++ gr-filter/python/qa_pfb_synthesizer.py | 87 ++++++++ gr-filter/swig/filter_swig.i | 3 + 10 files changed, 631 insertions(+) create mode 100644 gr-filter/grc/pfb_synthesizer.xml create mode 100644 gr-filter/include/filter/pfb_synthesizer_ccf.h create mode 100644 gr-filter/lib/pfb_synthesizer_ccf_impl.cc create mode 100644 gr-filter/lib/pfb_synthesizer_ccf_impl.h create mode 100755 gr-filter/python/qa_pfb_synthesizer.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 0747c7240..8bbb4c9e0 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -32,6 +32,7 @@ install(FILES pfb_channelizer.xml pfb_decimator.xml pfb_interpolator.xml + pfb_synthesizer.xml rational_resampler_base_xxx.xml single_pole_iir_filter_xx.xml DESTINATION ${GRC_BLOCKS_DIR} diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index de840a3fd..b16966eae 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -43,6 +43,7 @@ pfb_channelizer_ccf pfb_decimator_ccf pfb_interpolator_ccf + pfb_synthesizer_ccf rational_resampler_base_xxx single_pole_iir_filter_xx diff --git a/gr-filter/grc/pfb_synthesizer.xml b/gr-filter/grc/pfb_synthesizer.xml new file mode 100644 index 000000000..e84b25e62 --- /dev/null +++ b/gr-filter/grc/pfb_synthesizer.xml @@ -0,0 +1,57 @@ + + + + Polyphase Synthesizer + pfb_synthesizer_ccf + from gnuradio import filter + from gnuradio.filter import firdes + filter.pfb_synthesizer_ccf( + $numchans, $taps, $twox) +self.$(id).set_channel_map($ch_map) + + set_taps($taps) + set_channel_map($ch_map) + + + Channels + numchans + 2 + int + + + Connections + connections + 2 + int + + + Taps + taps + real_vector + + + 2x Sample Rate + twox + False + bool + + + Channel Map + ch_map + [] + int_vector + + + in + complex + $connections + + + out + complex + + diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index 9174bfc84..b30a1fc76 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -106,6 +106,7 @@ install(FILES pfb_channelizer_ccf.h pfb_decimator_ccf.h pfb_interpolator_ccf.h + pfb_synthesizer_ccf.h single_pole_iir_filter_cc.h single_pole_iir_filter_ff.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter diff --git a/gr-filter/include/filter/pfb_synthesizer_ccf.h b/gr-filter/include/filter/pfb_synthesizer_ccf.h new file mode 100644 index 000000000..ec6fb49c2 --- /dev/null +++ b/gr-filter/include/filter/pfb_synthesizer_ccf.h @@ -0,0 +1,107 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_SYNTHESIZER_CCF_H +#define INCLUDED_PFB_SYNTHESIZER_CCF_H + +#include +#include + +namespace gr { + namespace filter { + + /*! + * \class pfb_synthesizer_ccf + * + * \brief Polyphase synthesis filterbank with + * gr_complex input, gr_complex output and float taps + * + * \ingroup filter_blk + * \ingroup pfb_blk + */ + + class FILTER_API pfb_synthesizer_ccf : virtual public gr_sync_interpolator + { + public: + // gr::filter::pfb_synthesizer_ccf::sptr + typedef boost::shared_ptr sptr; + + /*! + * Build the polyphase synthesis filterbank. + * \param numchans (unsigned integer) Specifies the number of + * channels M + * \param taps (vector/list of floats) The prototype filter to + * populate the filterbank. + * \param twox (bool) use 2x oversampling or not (default is no) + */ + static FILTER_API sptr make(unsigned int numchans, + const std::vector &taps, + bool twox=false); + + /*! + * Resets the filterbank's filter taps with the new prototype filter + * \param taps (vector/list of floats) The prototype filter to + * populate the filterbank. + */ + virtual void set_taps(const std::vector &taps) = 0; + + /*! + * Print all of the filterbank taps to screen. + */ + virtual void print_taps() = 0; + + /*! + * Return a vector> of the filterbank taps + */ + virtual std::vector > taps() const = 0; + + /*! + * Set the channel map. Channels are numbers as: + * N/2+1 | ... | N-1 | 0 | 1 | 2 | ... | N/2 + * <------------------- 0 --------------------> + * freq + * + * So input stream 0 goes to channel 0, etc. Setting a new channel + * map allows the user to specify where in frequency he/she wants + * the input stream to go. This is especially useful to avoid + * putting signals into the channels on the edge of the spectrum + * which can either wrap around (in the case of odd number of + * channels) and be affected by filter rolloff in the transmitter. + * + * The map must be at least the number of streams being sent to the + * block. Less and the algorithm will not have enough data to + * properly setup the buffers. Any more channels specified will be + * ignored. + */ + virtual void set_channel_map(const std::vector &map) = 0; + + /*! + * Gets the current channel map. + */ + virtual std::vector channel_map() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_SYNTHESIZER_CCF_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index b3d456ec9..e29d69db5 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -133,6 +133,7 @@ list(APPEND filter_sources pfb_channelizer_ccf_impl.cc pfb_decimator_ccf_impl.cc pfb_interpolator_ccf_impl.cc + pfb_synthesizer_ccf_impl.cc single_pole_iir_filter_cc_impl.cc single_pole_iir_filter_ff_impl.cc ) diff --git a/gr-filter/lib/pfb_synthesizer_ccf_impl.cc b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc new file mode 100644 index 000000000..1e9c099c1 --- /dev/null +++ b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc @@ -0,0 +1,288 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pfb_synthesizer_ccf_impl.h" +#include +#include + +namespace gr { + namespace filter { + + pfb_synthesizer_ccf::sptr + pfb_synthesizer_ccf::make(unsigned int numchans, + const std::vector &taps, + bool twox) + { + return gnuradio::get_initial_sptr + (new pfb_synthesizer_ccf_impl(numchans, taps, twox)); + } + + + pfb_synthesizer_ccf_impl::pfb_synthesizer_ccf_impl(unsigned int numchans, + const std::vector &taps, + bool twox) + : gr_sync_interpolator("pfb_synthesizer_ccf", + gr_make_io_signature(1, numchans, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex)), + numchans), + d_updated (false), d_numchans(numchans), d_state(0) + { + // set up 2x multiplier; if twox==True, set to 2, otherwise to 1 + d_twox = (twox ? 2 : 1); + if(d_numchans % d_twox != 0) { + throw std::invalid_argument("pfb_synthesizer_ccf: number of channels must be even for 2x oversampling.\n"); + } + + d_filters = std::vector(d_twox*d_numchans); + d_channel_map.resize(d_twox*d_numchans); + + // Create a FIR filter for each channel and zero out the taps + std::vector vtaps(0, d_twox*d_numchans); + for(unsigned int i = 0; i < d_twox*d_numchans; i++) { + d_filters[i] = new kernel::fir_filter_with_buffer_ccf(vtaps); + d_channel_map[i] = i; + } + + // Now, actually set the filters' taps + set_taps(taps); + + // Create the IFFT to handle the input channel rotations + d_fft = new fft::fft_complex(d_twox*d_numchans, false); + memset(d_fft->get_inbuf(), 0, d_twox*d_numchans*sizeof(gr_complex)); + + set_output_multiple(d_numchans); + } + + pfb_synthesizer_ccf_impl::~pfb_synthesizer_ccf_impl() + { + for(unsigned int i = 0; i < d_twox*d_numchans; i++) { + delete d_filters[i]; + } + } + + void + pfb_synthesizer_ccf_impl::set_taps(const std::vector &taps) + { + gruel::scoped_lock guard(d_mutex); + + if(d_twox == 1) + set_taps1(taps); + else + set_taps2(taps); + + // Set the history to ensure enough input items for each filter + set_history(d_taps_per_filter+1); + + d_updated = true; + } + + void + pfb_synthesizer_ccf_impl::set_taps1(const std::vector &taps) + { + unsigned int i,j; + + unsigned int ntaps = taps.size(); + d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_numchans); + + // Create d_numchan vectors to store each channel's taps + d_taps.resize(d_numchans); + + // Make a vector of the taps plus fill it out with 0's to fill + // each polyphase filter with exactly d_taps_per_filter + std::vector tmp_taps; + tmp_taps = taps; + while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) { + tmp_taps.push_back(0.0); + } + + // Partition the filter + for(i = 0; i < d_numchans; i++) { + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + d_taps[i] = std::vector(d_taps_per_filter, 0); + for(j = 0; j < d_taps_per_filter; j++) { + d_taps[i][j] = tmp_taps[i + j*d_numchans]; // add taps to channels in reverse order + } + + // Build a filter for each channel and add it's taps to it + d_filters[i]->set_taps(d_taps[i]); + } + } + + void + pfb_synthesizer_ccf_impl::set_taps2 (const std::vector &taps) + { + unsigned int i,j; + int state = 0; + + unsigned int ntaps = taps.size(); + d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_numchans); + + // Create d_numchan vectors to store each channel's taps + d_taps.resize(d_twox*d_numchans); + + // Make a vector of the taps plus fill it out with 0's to fill + // each polyphase filter with exactly d_taps_per_filter + std::vector tmp_taps; + tmp_taps = taps; + while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) { + tmp_taps.push_back(0.0); + } + + // Partition the filter + for(i = 0; i < d_numchans; i++) { + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + d_taps[i] = std::vector(d_taps_per_filter, 0); + d_taps[d_numchans+i] = std::vector(d_taps_per_filter, 0); + state = 0; + for(j = 0; j < d_taps_per_filter; j++) { + // add taps to channels in reverse order + // Zero out every other tap + if(state == 0) { + d_taps[i][j] = tmp_taps[i + j*d_numchans]; + d_taps[d_numchans + i][j] = 0; + state = 1; + } + else { + d_taps[i][j] = 0; + d_taps[d_numchans + i][j] = tmp_taps[i + j*d_numchans]; + state = 0; + } + } + + // Build a filter for each channel and add it's taps to it + d_filters[i]->set_taps(d_taps[i]); + d_filters[d_numchans + i]->set_taps(d_taps[d_numchans + i]); + } + } + + void + pfb_synthesizer_ccf_impl::print_taps() + { + unsigned int i, j; + for(i = 0; i < d_twox*d_numchans; i++) { + printf("filter[%d]: [", i); + for(j = 0; j < d_taps_per_filter; j++) { + printf(" %.4e", d_taps[i][j]); + } + printf("]\n\n"); + } + } + + std::vector< std::vector > + pfb_synthesizer_ccf_impl::taps() const + { + return d_taps; + } + + void + pfb_synthesizer_ccf_impl::set_channel_map(const std::vector &map) + { + gruel::scoped_lock guard(d_mutex); + + if(map.size() > 0) { + unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end()); + unsigned int min = (unsigned int)*std::min_element(map.begin(), map.end()); + if((max >= d_twox*d_numchans) || (min < 0)) { + throw std::invalid_argument("gr_pfb_synthesizer_ccf::set_channel_map: map range out of bounds.\n"); + } + d_channel_map = map; + + // Zero out fft buffer so that unused channels are always 0 + memset(d_fft->get_inbuf(), 0,d_twox*d_numchans*sizeof(gr_complex)); + } + } + + std::vector + pfb_synthesizer_ccf_impl::channel_map() const + { + return d_channel_map; + } + + int + pfb_synthesizer_ccf_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gruel::scoped_lock guard(d_mutex); + + gr_complex *in = (gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + + if(d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + unsigned int n, i; + size_t ninputs = input_items.size(); + + // Algoritm for critically sampled channels + if(d_twox == 1) { + for(n = 0; n < noutput_items/d_numchans; n++) { + for(i = 0; i < ninputs; i++) { + in = (gr_complex*)input_items[i]; + d_fft->get_inbuf()[d_channel_map[i]] = in[n]; + } + + // spin through IFFT + d_fft->execute(); + + for(i = 0; i < d_numchans; i++) { + out[i] = d_filters[i]->filter(d_fft->get_outbuf()[i]); + } + out += d_numchans; + } + } + + // Algorithm for oversampling by 2x + else { + for(n = 0; n < noutput_items/d_numchans; n++) { + for(i = 0; i < ninputs; i++) { + in = (gr_complex*)input_items[i]; + d_fft->get_inbuf()[d_channel_map[i]] = in[n]; + } + + // spin through IFFT + d_fft->execute(); + + // Output is sum of two filters, but the input buffer to the filters must be circularly + // shifted by numchans every time through, done by using d_state to determine which IFFT + // buffer position to pull from. + for(i = 0; i < d_numchans; i++) { + out[i] = d_filters[i]->filter(d_fft->get_outbuf()[d_state*d_numchans+i]); + out[i] += d_filters[d_numchans+i]->filter(d_fft->get_outbuf()[(d_state^1)*d_numchans+i]); + } + d_state ^= 1; + + out += d_numchans; + } + } + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/pfb_synthesizer_ccf_impl.h b/gr-filter/lib/pfb_synthesizer_ccf_impl.h new file mode 100644 index 000000000..adffc143f --- /dev/null +++ b/gr-filter/lib/pfb_synthesizer_ccf_impl.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_PFB_SYNTHESIZER_CCF_IMPL_H +#define INCLUDED_PFB_SYNTHESIZER_CCF_IMPL_H + +#include +#include +#include +#include + +namespace gr { + namespace filter { + + // While this is a polyphase_filterbank, we don't use the normal + // parent class because we have to use the fir_filter_with_buffer + // objects instead of normal filters. + + class FILTER_API pfb_synthesizer_ccf_impl : public pfb_synthesizer_ccf + { + private: + bool d_updated; + unsigned int d_numchans; + unsigned int d_taps_per_filter; + fft::fft_complex *d_fft; + std::vector< kernel::fir_filter_with_buffer_ccf*> d_filters; + std::vector< std::vector > d_taps; + int d_state; + std::vector d_channel_map; + unsigned int d_twox; + gruel::mutex d_mutex; // mutex to protect set/work access + + /*! + * \brief Tap setting algorithm for critically sampled channels + */ + void set_taps1(const std::vector &taps); + + /*! + * \brief Tap setting algorithm for 2x over-sampled channels + */ + void set_taps2(const std::vector &taps); + + + public: + pfb_synthesizer_ccf_impl(unsigned int numchans, + const std::vector &taps, + bool twox); + ~pfb_synthesizer_ccf_impl(); + + void set_taps(const std::vector &taps); + std::vector > taps() const; + void print_taps(); + + void set_channel_map(const std::vector &map); + std::vector channel_map() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_PFB_SYNTHESIZER_CCF_IMPL_H */ diff --git a/gr-filter/python/qa_pfb_synthesizer.py b/gr-filter/python/qa_pfb_synthesizer.py new file mode 100755 index 000000000..8b69ccb12 --- /dev/null +++ b/gr-filter/python/qa_pfb_synthesizer.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter +import math + +class test_pfb_synthesizer(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_000(self): + N = 10000 # number of samples to use + M = 5 # Number of channels + fs = 1000 # baseband sampling rate + ofs = M*fs # input samp rate to decimator + + taps = gr.firdes.low_pass_2(M, ofs, fs/2, fs/10, + attenuation_dB=80, + window=gr.firdes.WIN_BLACKMAN_hARRIS) + + signals = list() + freqs = [0, 100, 200, -200, -100] + for i in xrange(len(freqs)): + signals.append(gr.sig_source_c(fs, gr.GR_SIN_WAVE, freqs[i], 1)) + + head = gr.head(gr.sizeof_gr_complex, N) + pfb = filter.pfb_synthesizer_ccf(M, taps) + snk = gr.vector_sink_c() + + for i in xrange(M): + self.tb.connect(signals[i], (pfb,i)) + + self.tb.connect(pfb, head, snk) + + self.tb.run() + + Ntest = 1000 + L = len(snk.data()) + t = map(lambda x: float(x)/ofs, xrange(L)) + + # Create known data as sum of complex sinusoids at freqs + # of the output channels. + freqs = [-2200, -1100, 0, 1100, 2200] + expected_data = len(t)*[0,] + for i in xrange(len(t)): + expected_data[i] = math.cos(2.*math.pi*freqs[0]*t[i]) + \ + 1j*math.sin(2.*math.pi*freqs[0]*t[i]) + \ + math.cos(2.*math.pi*freqs[1]*t[i]) + \ + 1j*math.sin(2.*math.pi*freqs[1]*t[i]) + \ + math.cos(2.*math.pi*freqs[2]*t[i]) + \ + 1j*math.sin(2.*math.pi*freqs[2]*t[i]) + \ + math.cos(2.*math.pi*freqs[3]*t[i]) + \ + 1j*math.sin(2.*math.pi*freqs[3]*t[i]) + \ + math.cos(2.*math.pi*freqs[4]*t[i]) + \ + 1j*math.sin(2.*math.pi*freqs[4]*t[i]) + dst_data = snk.data() + + offset = 25 + self.assertComplexTuplesAlmostEqual(expected_data[2000-offset:2000-offset+Ntest], + dst_data[2000:2000+Ntest], 4) + +if __name__ == '__main__': + gr_unittest.run(test_pfb_synthesizer, "test_pfb_synthesizer.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index 5a8ebf975..bb4eab381 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -64,6 +64,7 @@ #include "filter/pfb_channelizer_ccf.h" #include "filter/pfb_decimator_ccf.h" #include "filter/pfb_interpolator_ccf.h" +#include "filter/pfb_synthesizer_ccf.h" #include "filter/rational_resampler_base_ccc.h" #include "filter/rational_resampler_base_ccf.h" #include "filter/rational_resampler_base_fcc.h" @@ -110,6 +111,7 @@ %include "filter/pfb_channelizer_ccf.h" %include "filter/pfb_decimator_ccf.h" %include "filter/pfb_interpolator_ccf.h" +%include "filter/pfb_synthesizer_ccf.h" %include "filter/rational_resampler_base_ccc.h" %include "filter/rational_resampler_base_ccf.h" %include "filter/rational_resampler_base_fcc.h" @@ -153,6 +155,7 @@ GR_SWIG_BLOCK_MAGIC2(filter, pfb_arb_resampler_fff); GR_SWIG_BLOCK_MAGIC2(filter, pfb_channelizer_ccf); GR_SWIG_BLOCK_MAGIC2(filter, pfb_decimator_ccf); GR_SWIG_BLOCK_MAGIC2(filter, pfb_interpolator_ccf); +GR_SWIG_BLOCK_MAGIC2(filter, pfb_synthesizer_ccf); GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_ccc); GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_ccf); GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_fcc); -- cgit From a5a03740b546cc0b4f3dc631e4cf1f354a143652 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 20:59:13 -0400 Subject: filter: Moved over filter-specific Python hier_blocks. --- gr-filter/python/CMakeLists.txt | 2 + gr-filter/python/__init__.py | 2 + gr-filter/python/optfir.py | 339 +++++++++++++++++++++++++++++++++ gr-filter/python/pfb.py | 203 +++++++++++++++++++- gr-filter/python/rational_resampler.py | 131 +++++++++++++ 5 files changed, 674 insertions(+), 3 deletions(-) create mode 100644 gr-filter/python/optfir.py create mode 100644 gr-filter/python/rational_resampler.py diff --git a/gr-filter/python/CMakeLists.txt b/gr-filter/python/CMakeLists.txt index 99a1aa3a9..07f03fac4 100644 --- a/gr-filter/python/CMakeLists.txt +++ b/gr-filter/python/CMakeLists.txt @@ -23,7 +23,9 @@ include(GrPython) GR_PYTHON_INSTALL( FILES __init__.py + optfir.py pfb.py + rational_resampler.py DESTINATION ${GR_PYTHON_DIR}/gnuradio/filter COMPONENT "filter_python" ) diff --git a/gr-filter/python/__init__.py b/gr-filter/python/__init__.py index 90b5ce0a4..65a62d828 100644 --- a/gr-filter/python/__init__.py +++ b/gr-filter/python/__init__.py @@ -25,4 +25,6 @@ processing blocks for FILTER and related functions. ''' from filter_swig import * +from rational_resampler import * import pfb +import optfir diff --git a/gr-filter/python/optfir.py b/gr-filter/python/optfir.py new file mode 100644 index 000000000..bccb8c68d --- /dev/null +++ b/gr-filter/python/optfir.py @@ -0,0 +1,339 @@ +# +# Copyright 2004,2005,2009 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +''' +Routines for designing optimal FIR filters. + +For a great intro to how all this stuff works, see section 6.6 of +"Digital Signal Processing: A Practical Approach", Emmanuael C. Ifeachor +and Barrie W. Jervis, Adison-Wesley, 1993. ISBN 0-201-54413-X. +''' + +import math, cmath +import filter_swig as filter + +# ---------------------------------------------------------------- + +## Builds a low pass filter. +# @param gain Filter gain in the passband (linear) +# @param Fs Sampling rate (sps) +# @param freq1 End of pass band (in Hz) +# @param freq2 Start of stop band (in Hz) +# @param passband_ripple_db Pass band ripple in dB (should be small, < 1) +# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60) +# @param nextra_taps Extra taps to use in the filter (default=2) +def low_pass (gain, Fs, freq1, freq2, passband_ripple_db, stopband_atten_db, + nextra_taps=2): + passband_dev = passband_ripple_to_dev (passband_ripple_db) + stopband_dev = stopband_atten_to_dev (stopband_atten_db) + desired_ampls = (gain, 0) + (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls, + [passband_dev, stopband_dev], Fs) + # The remezord typically under-estimates the filter order, so add 2 taps by default + taps = filter.pm_remez (n + nextra_taps, fo, ao, w, "bandpass") + return taps + +## Builds a band pass filter. +# @param gain Filter gain in the passband (linear) +# @param Fs Sampling rate (sps) +# @param freq_sb1 End of stop band (in Hz) +# @param freq_pb1 Start of pass band (in Hz) +# @param freq_pb2 End of pass band (in Hz) +# @param freq_sb2 Start of stop band (in Hz) +# @param passband_ripple_db Pass band ripple in dB (should be small, < 1) +# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60) +# @param nextra_taps Extra taps to use in the filter (default=2) +def band_pass (gain, Fs, freq_sb1, freq_pb1, freq_pb2, freq_sb2, + passband_ripple_db, stopband_atten_db, + nextra_taps=2): + passband_dev = passband_ripple_to_dev (passband_ripple_db) + stopband_dev = stopband_atten_to_dev (stopband_atten_db) + desired_ampls = (0, gain, 0) + desired_freqs = [freq_sb1, freq_pb1, freq_pb2, freq_sb2] + desired_ripple = [stopband_dev, passband_dev, stopband_dev] + (n, fo, ao, w) = remezord (desired_freqs, desired_ampls, + desired_ripple, Fs) + # The remezord typically under-estimates the filter order, so add 2 taps by default + taps = filter.pm_remez (n + nextra_taps, fo, ao, w, "bandpass") + return taps + + +## Builds a band pass filter with complex taps by making an LPF and +# spinning it up to the right center frequency +# @param gain Filter gain in the passband (linear) +# @param Fs Sampling rate (sps) +# @param freq_sb1 End of stop band (in Hz) +# @param freq_pb1 Start of pass band (in Hz) +# @param freq_pb2 End of pass band (in Hz) +# @param freq_sb2 Start of stop band (in Hz) +# @param passband_ripple_db Pass band ripple in dB (should be small, < 1) +# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60) +# @param nextra_taps Extra taps to use in the filter (default=2) +def complex_band_pass (gain, Fs, freq_sb1, freq_pb1, freq_pb2, freq_sb2, + passband_ripple_db, stopband_atten_db, + nextra_taps=2): + center_freq = (freq_pb2 + freq_pb1) / 2.0 + lp_pb = (freq_pb2 - center_freq)/1.0 + lp_sb = freq_sb2 - center_freq + lptaps = low_pass(gain, Fs, lp_pb, lp_sb, passband_ripple_db, + stopband_atten_db, nextra_taps) + spinner = [cmath.exp(2j*cmath.pi*center_freq/Fs*i) for i in xrange(len(lptaps))] + taps = [s*t for s,t in zip(spinner, lptaps)] + return taps + + +## Builds a band reject filter +# spinning it up to the right center frequency +# @param gain Filter gain in the passband (linear) +# @param Fs Sampling rate (sps) +# @param freq_pb1 End of pass band (in Hz) +# @param freq_sb1 Start of stop band (in Hz) +# @param freq_sb2 End of stop band (in Hz) +# @param freq_pb2 Start of pass band (in Hz) +# @param passband_ripple_db Pass band ripple in dB (should be small, < 1) +# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60) +# @param nextra_taps Extra taps to use in the filter (default=2) +def band_reject (gain, Fs, freq_pb1, freq_sb1, freq_sb2, freq_pb2, + passband_ripple_db, stopband_atten_db, + nextra_taps=2): + passband_dev = passband_ripple_to_dev (passband_ripple_db) + stopband_dev = stopband_atten_to_dev (stopband_atten_db) + desired_ampls = (gain, 0, gain) + desired_freqs = [freq_pb1, freq_sb1, freq_sb2, freq_pb2] + desired_ripple = [passband_dev, stopband_dev, passband_dev] + (n, fo, ao, w) = remezord (desired_freqs, desired_ampls, + desired_ripple, Fs) + # Make sure we use an odd number of taps + if((n+nextra_taps)%2 == 1): + n += 1 + # The remezord typically under-estimates the filter order, so add 2 taps by default + taps = filter.pm_remez (n + nextra_taps, fo, ao, w, "bandpass") + return taps + + +## Builds a high pass filter. +# @param gain Filter gain in the passband (linear) +# @param Fs Sampling rate (sps) +# @param freq1 End of stop band (in Hz) +# @param freq2 Start of pass band (in Hz) +# @param passband_ripple_db Pass band ripple in dB (should be small, < 1) +# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60) +# @param nextra_taps Extra taps to use in the filter (default=2) +def high_pass (gain, Fs, freq1, freq2, passband_ripple_db, stopband_atten_db, + nextra_taps=2): + passband_dev = passband_ripple_to_dev (passband_ripple_db) + stopband_dev = stopband_atten_to_dev (stopband_atten_db) + desired_ampls = (0, 1) + (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls, + [stopband_dev, passband_dev], Fs) + # For a HPF, we need to use an odd number of taps + # In filter.remez, ntaps = n+1, so n must be even + if((n+nextra_taps)%2 == 1): + n += 1 + + # The remezord typically under-estimates the filter order, so add 2 taps by default + taps = filter.pm_remez (n + nextra_taps, fo, ao, w, "bandpass") + return taps + +# ---------------------------------------------------------------- + +def stopband_atten_to_dev (atten_db): + """Convert a stopband attenuation in dB to an absolute value""" + return 10**(-atten_db/20) + +def passband_ripple_to_dev (ripple_db): + """Convert passband ripple spec expressed in dB to an absolute value""" + return (10**(ripple_db/20)-1)/(10**(ripple_db/20)+1) + +# ---------------------------------------------------------------- + +def remezord (fcuts, mags, devs, fsamp = 2): + ''' + FIR order estimator (lowpass, highpass, bandpass, mulitiband). + + (n, fo, ao, w) = remezord (f, a, dev) + (n, fo, ao, w) = remezord (f, a, dev, fs) + + (n, fo, ao, w) = remezord (f, a, dev) finds the approximate order, + normalized frequency band edges, frequency band amplitudes, and + weights that meet input specifications f, a, and dev, to use with + the remez command. + + * f is a sequence of frequency band edges (between 0 and Fs/2, where + Fs is the sampling frequency), and a is a sequence specifying the + desired amplitude on the bands defined by f. The length of f is + twice the length of a, minus 2. The desired function is + piecewise constant. + + * dev is a sequence the same size as a that specifies the maximum + allowable deviation or ripples between the frequency response + and the desired amplitude of the output filter, for each band. + + Use remez with the resulting order n, frequency sequence fo, + amplitude response sequence ao, and weights w to design the filter b + which approximately meets the specifications given by remezord + input parameters f, a, and dev: + + b = remez (n, fo, ao, w) + + (n, fo, ao, w) = remezord (f, a, dev, Fs) specifies a sampling frequency Fs. + + Fs defaults to 2 Hz, implying a Nyquist frequency of 1 Hz. You can + therefore specify band edges scaled to a particular applications + sampling frequency. + + In some cases remezord underestimates the order n. If the filter + does not meet the specifications, try a higher order such as n+1 + or n+2. + ''' + # get local copies + fcuts = fcuts[:] + mags = mags[:] + devs = devs[:] + + for i in range (len (fcuts)): + fcuts[i] = float (fcuts[i]) / fsamp + + nf = len (fcuts) + nm = len (mags) + nd = len (devs) + nbands = nm + + if nm != nd: + raise ValueError, "Length of mags and devs must be equal" + + if nf != 2 * (nbands - 1): + raise ValueError, "Length of f must be 2 * len (mags) - 2" + + for i in range (len (mags)): + if mags[i] != 0: # if not stopband, get relative deviation + devs[i] = devs[i] / mags[i] + + # separate the passband and stopband edges + f1 = fcuts[0::2] + f2 = fcuts[1::2] + + n = 0 + min_delta = 2 + for i in range (len (f1)): + if f2[i] - f1[i] < min_delta: + n = i + min_delta = f2[i] - f1[i] + + if nbands == 2: + # lowpass or highpass case (use formula) + l = lporder (f1[n], f2[n], devs[0], devs[1]) + else: + # bandpass or multipass case + # try different lowpasses and take the worst one that + # goes through the BP specs + l = 0 + for i in range (1, nbands-1): + l1 = lporder (f1[i-1], f2[i-1], devs[i], devs[i-1]) + l2 = lporder (f1[i], f2[i], devs[i], devs[i+1]) + l = max (l, l1, l2) + + n = int (math.ceil (l)) - 1 # need order, not length for remez + + # cook up remez compatible result + ff = [0] + fcuts + [1] + for i in range (1, len (ff) - 1): + ff[i] *= 2 + + aa = [] + for a in mags: + aa = aa + [a, a] + + max_dev = max (devs) + wts = [1] * len(devs) + for i in range (len (wts)): + wts[i] = max_dev / devs[i] + + return (n, ff, aa, wts) + +# ---------------------------------------------------------------- + +def lporder (freq1, freq2, delta_p, delta_s): + ''' + FIR lowpass filter length estimator. freq1 and freq2 are + normalized to the sampling frequency. delta_p is the passband + deviation (ripple), delta_s is the stopband deviation (ripple). + + Note, this works for high pass filters too (freq1 > freq2), but + doesnt work well if the transition is near f == 0 or f == fs/2 + + From Herrmann et al (1973), Practical design rules for optimum + finite impulse response filters. Bell System Technical J., 52, 769-99 + ''' + df = abs (freq2 - freq1) + ddp = math.log10 (delta_p) + dds = math.log10 (delta_s) + + a1 = 5.309e-3 + a2 = 7.114e-2 + a3 = -4.761e-1 + a4 = -2.66e-3 + a5 = -5.941e-1 + a6 = -4.278e-1 + + b1 = 11.01217 + b2 = 0.5124401 + + t1 = a1 * ddp * ddp + t2 = a2 * ddp + t3 = a4 * ddp * ddp + t4 = a5 * ddp + + dinf=((t1 + t2 + a3) * dds) + (t3 + t4 + a6) + ff = b1 + b2 * (ddp - dds) + n = dinf / df - ff * df + 1 + return n + + +def bporder (freq1, freq2, delta_p, delta_s): + ''' + FIR bandpass filter length estimator. freq1 and freq2 are + normalized to the sampling frequency. delta_p is the passband + deviation (ripple), delta_s is the stopband deviation (ripple). + + From Mintzer and Liu (1979) + ''' + df = abs (freq2 - freq1) + ddp = math.log10 (delta_p) + dds = math.log10 (delta_s) + + a1 = 0.01201 + a2 = 0.09664 + a3 = -0.51325 + a4 = 0.00203 + a5 = -0.57054 + a6 = -0.44314 + + t1 = a1 * ddp * ddp + t2 = a2 * ddp + t3 = a4 * ddp * ddp + t4 = a5 * ddp + + cinf = dds * (t1 + t2 + a3) + t3 + t4 + a6 + ginf = -14.6 * math.log10 (delta_p / delta_s) - 16.9 + n = cinf / df + ginf * df + 1 + return n + diff --git a/gr-filter/python/pfb.py b/gr-filter/python/pfb.py index 9c7e18e31..ddf289982 100644 --- a/gr-filter/python/pfb.py +++ b/gr-filter/python/pfb.py @@ -20,8 +20,9 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, optfir +from gnuradio import gr import filter_swig as filter +import optfir class channelizer_ccf(gr.hier_block2): ''' @@ -32,8 +33,8 @@ class channelizer_ccf(gr.hier_block2): ''' def __init__(self, numchans, taps=None, oversample_rate=1, atten=100): gr.hier_block2.__init__(self, "pfb_channelizer_ccf", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(numchans, numchans, gr.sizeof_gr_complex)) # Output signature + gr.io_signature(1, 1, gr.sizeof_gr_complex), + gr.io_signature(numchans, numchans, gr.sizeof_gr_complex)) self._nchans = numchans self._oversample_rate = oversample_rate @@ -72,3 +73,199 @@ class channelizer_ccf(gr.hier_block2): self.pfb.set_channel_map(newmap) + +class interpolator_ccf(gr.hier_block2): + ''' + Make a Polyphase Filter interpolator (complex in, complex out, floating-point taps) + + The block takes a single complex stream in and outputs a single complex + stream out. As such, it requires no extra glue to handle the input/output + streams. This block is provided to be consistent with the interface to the + other PFB block. + ''' + def __init__(self, interp, taps=None, atten=100): + gr.hier_block2.__init__(self, "pfb_interpolator_ccf", + gr.io_signature(1, 1, gr.sizeof_gr_complex), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) + + self._interp = interp + self._taps = taps + + if taps is not None: + self._taps = taps + else: + # Create a filter that covers the full bandwidth of the input signal + bw = 0.4 + tb = 0.2 + ripple = 0.99 + made = False + while not made: + try: + self._taps = optfir.low_pass(self._interp, self._interp, bw, bw+tb, ripple, atten) + made = True + except RuntimeError: + ripple += 0.01 + made = False + print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple)) + + # Build in an exit strategy; if we've come this far, it ain't working. + if(ripple >= 1.0): + raise RuntimeError("optfir could not generate an appropriate filter.") + + self.pfb = filter.pfb_interpolator_ccf(self._interp, self._taps) + + self.connect(self, self.pfb) + self.connect(self.pfb, self) + + +class decimator_ccf(gr.hier_block2): + ''' + Make a Polyphase Filter decimator (complex in, complex out, floating-point taps) + + This simplifies the interface by allowing a single input stream to connect to this block. + It will then output a stream that is the decimated output stream. + ''' + def __init__(self, decim, taps=None, channel=0, atten=100): + gr.hier_block2.__init__(self, "pfb_decimator_ccf", + gr.io_signature(1, 1, gr.sizeof_gr_complex), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) + + self._decim = decim + self._channel = channel + + if taps is not None: + self._taps = taps + else: + # Create a filter that covers the full bandwidth of the input signal + bw = 0.4 + tb = 0.2 + ripple = 0.1 + made = False + while not made: + try: + self._taps = optfir.low_pass(1, self._decim, bw, bw+tb, ripple, atten) + made = True + except RuntimeError: + ripple += 0.01 + made = False + print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple)) + + # Build in an exit strategy; if we've come this far, it ain't working. + if(ripple >= 1.0): + raise RuntimeError("optfir could not generate an appropriate filter.") + + self.s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, self._decim) + self.pfb = filter.pfb_decimator_ccf(self._decim, self._taps, self._channel) + + self.connect(self, self.s2ss) + + for i in xrange(self._decim): + self.connect((self.s2ss,i), (self.pfb,i)) + + self.connect(self.pfb, self) + + +class arb_resampler_ccf(gr.hier_block2): + ''' + Convenience wrapper for the polyphase filterbank arbitrary resampler. + + The block takes a single complex stream in and outputs a single complex + stream out. As such, it requires no extra glue to handle the input/output + streams. This block is provided to be consistent with the interface to the + other PFB block. + ''' + def __init__(self, rate, taps=None, flt_size=32, atten=100): + gr.hier_block2.__init__(self, "pfb_arb_resampler_ccf", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._rate = rate + self._size = flt_size + + if taps is not None: + self._taps = taps + else: + # Create a filter that covers the full bandwidth of the input signal + bw = 0.4 + tb = 0.2 + ripple = 0.1 + #self._taps = filter.firdes.low_pass_2(self._size, self._size, bw, tb, atten) + made = False + while not made: + try: + self._taps = optfir.low_pass(self._size, self._size, bw, bw+tb, ripple, atten) + made = True + except RuntimeError: + ripple += 0.01 + made = False + print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple)) + + # Build in an exit strategy; if we've come this far, it ain't working. + if(ripple >= 1.0): + raise RuntimeError("optfir could not generate an appropriate filter.") + + self.pfb = filter.pfb_arb_resampler_ccf(self._rate, self._taps, self._size) + #print "PFB has %d taps\n" % (len(self._taps),) + + self.connect(self, self.pfb) + self.connect(self.pfb, self) + + # Note -- set_taps not implemented in base class yet + def set_taps(self, taps): + self.pfb.set_taps(taps) + + def set_rate(self, rate): + self.pfb.set_rate(rate) + + +class arb_resampler_fff(gr.hier_block2): + ''' + Convenience wrapper for the polyphase filterbank arbitrary resampler. + + The block takes a single float stream in and outputs a single float + stream out. As such, it requires no extra glue to handle the input/output + streams. This block is provided to be consistent with the interface to the + other PFB block. + ''' + def __init__(self, rate, taps=None, flt_size=32, atten=100): + gr.hier_block2.__init__(self, "pfb_arb_resampler_fff", + gr.io_signature(1, 1, gr.sizeof_float), # Input signature + gr.io_signature(1, 1, gr.sizeof_float)) # Output signature + + self._rate = rate + self._size = flt_size + + if taps is not None: + self._taps = taps + else: + # Create a filter that covers the full bandwidth of the input signal + bw = 0.4 + tb = 0.2 + ripple = 0.1 + #self._taps = filter.firdes.low_pass_2(self._size, self._size, bw, tb, atten) + made = False + while not made: + try: + self._taps = optfir.low_pass(self._size, self._size, bw, bw+tb, ripple, atten) + made = True + except RuntimeError: + ripple += 0.01 + made = False + print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple)) + + # Build in an exit strategy; if we've come this far, it ain't working. + if(ripple >= 1.0): + raise RuntimeError("optfir could not generate an appropriate filter.") + + self.pfb = filter.pfb_arb_resampler_fff(self._rate, self._taps, self._size) + #print "PFB has %d taps\n" % (len(self._taps),) + + self.connect(self, self.pfb) + self.connect(self.pfb, self) + + # Note -- set_taps not implemented in base class yet + def set_taps(self, taps): + self.pfb.set_taps(taps) + + def set_rate(self, rate): + self.pfb.set_rate(rate) diff --git a/gr-filter/python/rational_resampler.py b/gr-filter/python/rational_resampler.py new file mode 100644 index 000000000..eea12af95 --- /dev/null +++ b/gr-filter/python/rational_resampler.py @@ -0,0 +1,131 @@ +# +# Copyright 2005,2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru + +_plot = None + +def design_filter(interpolation, decimation, fractional_bw): + """ + Given the interpolation rate, decimation rate and a fractional bandwidth, + design a set of taps. + + @param interpolation: interpolation factor + @type interpolation: integer > 0 + @param decimation: decimation factor + @type decimation: integer > 0 + @param fractional_bw: fractional bandwidth in (0, 0.5) 0.4 works well. + @type fractional_bw: float + @returns: sequence of numbers + """ + + if fractional_bw >= 0.5 or fractional_bw <= 0: + raise ValueError, "Invalid fractional_bandwidth, must be in (0, 0.5)" + + beta = 5.0 + trans_width = 0.5 - fractional_bw + mid_transition_band = 0.5 - trans_width/2 + + taps = gr.firdes.low_pass(interpolation, # gain + 1, # Fs + mid_transition_band/interpolation, # trans mid point + trans_width/interpolation, # transition width + gr.firdes.WIN_KAISER, + beta # beta + ) + + return taps + + + +class _rational_resampler_base(gr.hier_block2): + """ + base class for all rational resampler variants. + """ + def __init__(self, resampler_base, + interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter. + + Either taps or fractional_bw may be specified, but not both. + If neither is specified, a reasonable default, 0.4, is used as + the fractional_bw. + + @param interpolation: interpolation factor + @type interpolation: integer > 0 + @param decimation: decimation factor + @type decimation: integer > 0 + @param taps: optional filter coefficients + @type taps: sequence + @param fractional_bw: fractional bandwidth in (0, 0.5), measured at final freq (use 0.4) + @type fractional_bw: float + """ + + if not isinstance(interpolation, int) or interpolation < 1: + raise ValueError, "interpolation must be an integer >= 1" + + if not isinstance(decimation, int) or decimation < 1: + raise ValueError, "decimation must be an integer >= 1" + + if taps is None and fractional_bw is None: + fractional_bw = 0.4 + + d = gru.gcd(interpolation, decimation) + interpolation = interpolation // d + decimation = decimation // d + + if taps is None: + taps = design_filter(interpolation, decimation, fractional_bw) + + resampler = resampler_base(interpolation, decimation, taps) + gr.hier_block2.__init__(self, "rational_resampler", + gr.io_signature(1, 1, resampler.input_signature().sizeof_stream_item(0)), + gr.io_signature(1, 1, resampler.output_signature().sizeof_stream_item(0))) + + self.connect(self, resampler, self) + + +class rational_resampler_fff(_rational_resampler_base): + def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + float input, float output and float taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_fff, + interpolation, decimation, taps, fractional_bw) + +class rational_resampler_ccf(_rational_resampler_base): + def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + complex input, complex output and float taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccf, + interpolation, decimation, taps, fractional_bw) + +class rational_resampler_ccc(_rational_resampler_base): + def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + complex input, complex output and complex taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccc, + interpolation, decimation, taps, fractional_bw) -- cgit From b41f7f77cb3a59b54e4511be5069edbbfc27c2ad Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 21:10:23 -0400 Subject: filter: removed all references to filter functions from gr namespace to filter. --- gr-filter/examples/channelize.py | 6 +++--- gr-filter/examples/chirp_channelize.py | 5 +++-- gr-filter/examples/fft_filter_ccc.py | 9 --------- gr-filter/examples/fir_filter_ccc.py | 9 --------- gr-filter/examples/fir_filter_fff.py | 8 -------- gr-filter/include/filter/pfb_arb_resampler_ccf.h | 4 ++-- gr-filter/include/filter/pfb_arb_resampler_fff.h | 4 ++-- gr-filter/include/filter/pfb_channelizer_ccf.h | 4 ++-- gr-filter/include/filter/pfb_decimator_ccf.h | 4 ++-- gr-filter/include/filter/pfb_interpolator_ccf.h | 4 ++-- gr-filter/include/filter/polyphase_filterbank.h | 4 ++-- gr-filter/python/qa_fractional_interpolator.py | 4 ++-- gr-filter/python/qa_interp_fir_filter.py | 2 +- gr-filter/python/qa_pfb_arb_resampler.py | 12 ++++++------ gr-filter/python/qa_pfb_channelizer.py | 6 +++--- gr-filter/python/qa_pfb_decimator.py | 10 +++++----- gr-filter/python/qa_pfb_interpolator.py | 6 +++--- gr-filter/python/qa_pfb_synthesizer.py | 6 +++--- gr-filter/python/qa_pm_remez.py | 2 +- gr-filter/python/rational_resampler.py | 20 +++++++++----------- 20 files changed, 51 insertions(+), 78 deletions(-) diff --git a/gr-filter/examples/channelize.py b/gr-filter/examples/channelize.py index affce7b57..1f784d9b0 100755 --- a/gr-filter/examples/channelize.py +++ b/gr-filter/examples/channelize.py @@ -48,9 +48,9 @@ class pfb_top_block(gr.top_block): self._ifs = M*self._fs # initial sampling rate # Create a set of taps for the PFB channelizer - self._taps = gr.firdes.low_pass_2(1, self._ifs, 475.50, 50, - attenuation_dB=100, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + self._taps = filter.firdes.low_pass_2(1, self._ifs, 475.50, 50, + attenuation_dB=100, + window=filter.firdes.WIN_BLACKMAN_hARRIS) # Calculate the number of taps per channel for our own information tpc = scipy.ceil(float(len(self._taps)) / float(self._M)) diff --git a/gr-filter/examples/chirp_channelize.py b/gr-filter/examples/chirp_channelize.py index 99bf3b6e2..3638c55d1 100755 --- a/gr-filter/examples/chirp_channelize.py +++ b/gr-filter/examples/chirp_channelize.py @@ -47,8 +47,9 @@ class pfb_top_block(gr.top_block): self._M = 9 # Number of channels to channelize # Create a set of taps for the PFB channelizer - self._taps = gr.firdes.low_pass_2(1, self._fs, 500, 20, - attenuation_dB=10, window=gr.firdes.WIN_BLACKMAN_hARRIS) + self._taps = filter.firdes.low_pass_2(1, self._fs, 500, 20, + attenuation_dB=10, + window=filter.firdes.WIN_BLACKMAN_hARRIS) # Calculate the number of taps per channel for our own information tpc = scipy.ceil(float(len(self._taps)) / float(self._M)) diff --git a/gr-filter/examples/fft_filter_ccc.py b/gr-filter/examples/fft_filter_ccc.py index cc70e2b15..45e241a6f 100755 --- a/gr-filter/examples/fft_filter_ccc.py +++ b/gr-filter/examples/fft_filter_ccc.py @@ -37,16 +37,12 @@ class example_fft_filter_ccc(gr.top_block): self.head = gr.head(gr.sizeof_gr_complex, self._nsamps) self.filt0 = filter.fft_filter_ccc(self._decim, taps) - self.filt1 = gr.fft_filter_ccc(self._decim, taps) - #self.filt1 = filter.fft_filter_ccc(self._decim, taps) self.vsnk_src = gr.vector_sink_c() self.vsnk_out = gr.vector_sink_c() - self.vsnk_gr = gr.vector_sink_c() self.connect(self.src, self.head, self.vsnk_src) self.connect(self.head, self.filt0, self.vsnk_out) - self.connect(self.head, self.filt1, self.vsnk_gr) def main(): parser = OptionParser(option_class=eng_option, conflict_handler="resolve") @@ -77,7 +73,6 @@ def main(): data_src = scipy.array(put.vsnk_src.data()) data_snk = scipy.array(put.vsnk_out.data()) - data_gr = scipy.array(put.vsnk_gr.data()) # Plot the signals PSDs nfft = 1024 @@ -87,15 +82,11 @@ def main(): Fs=options.samplerate) s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, Fs=options.samplerate) - s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, - Fs=options.samplerate) - f2 = pylab.figure(2, figsize=(12,10)) s2 = f2.add_subplot(1,1,1) s2.plot(data_src) s2.plot(data_snk.real, 'g') - s2.plot(data_gr.real, 'r--') pylab.show() diff --git a/gr-filter/examples/fir_filter_ccc.py b/gr-filter/examples/fir_filter_ccc.py index b5a461d2c..baa05eeb7 100755 --- a/gr-filter/examples/fir_filter_ccc.py +++ b/gr-filter/examples/fir_filter_ccc.py @@ -34,16 +34,12 @@ class example_fir_filter_ccc(gr.top_block): self.head = gr.head(gr.sizeof_gr_complex, self._nsamps) self.filt0 = filter.fir_filter_ccc(self._decim, taps) - self.filt1 = gr.fir_filter_ccc(self._decim, taps) - #self.filt1 = filter.fft_filter_ccc(self._decim, taps) self.vsnk_src = gr.vector_sink_c() self.vsnk_out = gr.vector_sink_c() - self.vsnk_gr = gr.vector_sink_c() self.connect(self.src, self.head, self.vsnk_src) self.connect(self.head, self.filt0, self.vsnk_out) - self.connect(self.head, self.filt1, self.vsnk_gr) def main(): parser = OptionParser(option_class=eng_option, conflict_handler="resolve") @@ -71,7 +67,6 @@ def main(): data_src = scipy.array(put.vsnk_src.data()) data_snk = scipy.array(put.vsnk_out.data()) - data_gr = scipy.array(put.vsnk_gr.data()) # Plot the signals PSDs nfft = 1024 @@ -81,15 +76,11 @@ def main(): Fs=options.samplerate) s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, Fs=options.samplerate) - s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, - Fs=options.samplerate) - f2 = pylab.figure(2, figsize=(12,10)) s2 = f2.add_subplot(1,1,1) s2.plot(data_src) s2.plot(data_snk.real, 'g') - s2.plot(data_gr.real, 'r--') pylab.show() diff --git a/gr-filter/examples/fir_filter_fff.py b/gr-filter/examples/fir_filter_fff.py index 983088622..48769f00d 100755 --- a/gr-filter/examples/fir_filter_fff.py +++ b/gr-filter/examples/fir_filter_fff.py @@ -34,15 +34,12 @@ class example_fir_filter_fff(gr.top_block): self.head = gr.head(gr.sizeof_float, self._nsamps) self.filt0 = filter.fir_filter_fff(self._decim, taps) - self.filt1 = gr.fir_filter_fff(self._decim, taps) self.vsnk_src = gr.vector_sink_f() self.vsnk_out = gr.vector_sink_f() - self.vsnk_gr = gr.vector_sink_f() self.connect(self.src, self.head, self.vsnk_src) self.connect(self.head, self.filt0, self.vsnk_out) - self.connect(self.head, self.filt1, self.vsnk_gr) def main(): parser = OptionParser(option_class=eng_option, conflict_handler="resolve") @@ -70,7 +67,6 @@ def main(): data_src = scipy.array(put.vsnk_src.data()) data_snk = scipy.array(put.vsnk_out.data()) - data_gr = scipy.array(put.vsnk_gr.data()) # Plot the signals PSDs nfft = 1024 @@ -80,15 +76,11 @@ def main(): Fs=options.samplerate) s1.psd(data_snk, NFFT=nfft, noverlap=nfft/4, Fs=options.samplerate) - s1.psd(data_gr, NFFT=nfft, noverlap=nfft/4, - Fs=options.samplerate) - f2 = pylab.figure(2, figsize=(12,10)) s2 = f2.add_subplot(1,1,1) s2.plot(data_src) s2.plot(data_snk.real, 'g') - s2.plot(data_gr.real, 'r--') pylab.show() diff --git a/gr-filter/include/filter/pfb_arb_resampler_ccf.h b/gr-filter/include/filter/pfb_arb_resampler_ccf.h index 1436674b2..cf5fa4a3b 100644 --- a/gr-filter/include/filter/pfb_arb_resampler_ccf.h +++ b/gr-filter/include/filter/pfb_arb_resampler_ccf.h @@ -85,8 +85,8 @@ namespace gr { * gain of the filter, which we specify here as the interpolation * rate (32). * - * self._taps = gr.firdes.low_pass_2(32, 32*fs, BW, TB, - * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * self._taps = filter.firdes.low_pass_2(32, 32*fs, BW, TB, + * attenuation_dB=ATT, window=filter.firdes.WIN_BLACKMAN_hARRIS) * * The theory behind this block can be found in Chapter 7.5 of * the following book. diff --git a/gr-filter/include/filter/pfb_arb_resampler_fff.h b/gr-filter/include/filter/pfb_arb_resampler_fff.h index 7449ea0cb..2504c92ec 100644 --- a/gr-filter/include/filter/pfb_arb_resampler_fff.h +++ b/gr-filter/include/filter/pfb_arb_resampler_fff.h @@ -85,8 +85,8 @@ namespace gr { * gain of the filter, which we specify here as the interpolation * rate (32). * - * self._taps = gr.firdes.low_pass_2(32, 32*fs, BW, TB, - * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * self._taps = filter.firdes.low_pass_2(32, 32*fs, BW, TB, + * attenuation_dB=ATT, window=filter.firdes.WIN_BLACKMAN_hARRIS) * * The theory behind this block can be found in Chapter 7.5 of the * following book. diff --git a/gr-filter/include/filter/pfb_channelizer_ccf.h b/gr-filter/include/filter/pfb_channelizer_ccf.h index ba55782b4..7726809f2 100644 --- a/gr-filter/include/filter/pfb_channelizer_ccf.h +++ b/gr-filter/include/filter/pfb_channelizer_ccf.h @@ -81,8 +81,8 @@ namespace gr { * function (a Blackman-harris window in this case). The first input * is the gain of the filter, which we specify here as unity. * - * self._taps = gr.firdes.low_pass_2(1, fs, BW, TB, - * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * self._taps = filter.firdes.low_pass_2(1, fs, BW, TB, + * attenuation_dB=ATT, window=filter.firdes.WIN_BLACKMAN_hARRIS) * * The filter output can also be overs ampled. The over sampling rate * is the ratio of the the actual output sampling rate to the normal diff --git a/gr-filter/include/filter/pfb_decimator_ccf.h b/gr-filter/include/filter/pfb_decimator_ccf.h index e41f16cd2..9db11b88d 100644 --- a/gr-filter/include/filter/pfb_decimator_ccf.h +++ b/gr-filter/include/filter/pfb_decimator_ccf.h @@ -71,8 +71,8 @@ namespace gr { * input is the gain of the filter, which we specify here as * unity. * - * self._taps = gr.firdes.low_pass_2(1, fs, BW, TB, - * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * self._taps = filter.firdes.low_pass_2(1, fs, BW, TB, + * attenuation_dB=ATT, window=filter.firdes.WIN_BLACKMAN_hARRIS) * * The PFB decimator code takes the taps generated above and * builds a set of filters. The set contains decim number diff --git a/gr-filter/include/filter/pfb_interpolator_ccf.h b/gr-filter/include/filter/pfb_interpolator_ccf.h index d32b8b688..a31ca7129 100644 --- a/gr-filter/include/filter/pfb_interpolator_ccf.h +++ b/gr-filter/include/filter/pfb_interpolator_ccf.h @@ -59,8 +59,8 @@ namespace gr { * the output levels are the same as the input (this creates an * overall increase in power). * - * self._taps = gr.firdes.low_pass_2(interp, interp*fs, BW, TB, - * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * self._taps = filter.firdes.low_pass_2(interp, interp*fs, BW, TB, + * attenuation_dB=ATT, window=filter.firdes.WIN_BLACKMAN_hARRIS) * * The PFB interpolator code takes the taps generated above and * builds a set of filters. The set contains interp diff --git a/gr-filter/include/filter/polyphase_filterbank.h b/gr-filter/include/filter/polyphase_filterbank.h index 50d087340..33e9522e1 100644 --- a/gr-filter/include/filter/polyphase_filterbank.h +++ b/gr-filter/include/filter/polyphase_filterbank.h @@ -87,8 +87,8 @@ namespace gr { * input is the gain of the filter, which we specify here as * unity. * - * self._taps = gr.firdes.low_pass_2(1, fs, BW, TB, - * attenuation_dB=ATT, window=gr.firdes.WIN_BLACKMAN_hARRIS) + * self._taps = filter.firdes.low_pass_2(1, fs, BW, TB, + * attenuation_dB=ATT, window=filter.firdes.WIN_BLACKMAN_hARRIS) * * More on the theory of polyphase filterbanks can be found in * the following book. diff --git a/gr-filter/python/qa_fractional_interpolator.py b/gr-filter/python/qa_fractional_interpolator.py index 51c8f9530..74dae5d85 100755 --- a/gr-filter/python/qa_fractional_interpolator.py +++ b/gr-filter/python/qa_fractional_interpolator.py @@ -32,8 +32,8 @@ class test_fractional_resampler(gr_unittest.TestCase): self.tb = None def test_000_make(self): - op = gr.fractional_interpolator_ff(0.0, 1.0) - op2 = gr.fractional_interpolator_cc(0.0, 1.0) + op = filter.fractional_interpolator_ff(0.0, 1.0) + op2 = filter.fractional_interpolator_cc(0.0, 1.0) if __name__ == '__main__': gr_unittest.run(test_fractional_resampler, "test_fractional_resampler.xml") diff --git a/gr-filter/python/qa_interp_fir_filter.py b/gr-filter/python/qa_interp_fir_filter.py index 97901963e..839330539 100755 --- a/gr-filter/python/qa_interp_fir_filter.py +++ b/gr-filter/python/qa_interp_fir_filter.py @@ -46,7 +46,7 @@ class test_interp_fir_filter(gr_unittest.TestCase): expected_result = tuple([float(x) for x in xr]) src = gr.vector_source_f(src_data) - op = gr.interp_fir_filter_fff(interpolation, taps) + op = filter.interp_fir_filter_fff(interpolation, taps) dst = gr.vector_sink_f() self.tb.connect(src, op) self.tb.connect(op, dst) diff --git a/gr-filter/python/qa_pfb_arb_resampler.py b/gr-filter/python/qa_pfb_arb_resampler.py index 655b680f0..a4e22f4c0 100755 --- a/gr-filter/python/qa_pfb_arb_resampler.py +++ b/gr-filter/python/qa_pfb_arb_resampler.py @@ -38,9 +38,9 @@ class test_pfb_arb_resampler(gr_unittest.TestCase): rrate = 1.123 # resampling rate nfilts = 32 - taps = gr.firdes.low_pass_2(nfilts, nfilts*fs, fs/2, fs/10, - attenuation_dB=80, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + taps = filter.firdes.low_pass_2(nfilts, nfilts*fs, fs/2, fs/10, + attenuation_dB=80, + window=filter.firdes.WIN_BLACKMAN_hARRIS) freq = 100 signal = gr.sig_source_f(fs, gr.GR_SIN_WAVE, freq, 1) @@ -67,9 +67,9 @@ class test_pfb_arb_resampler(gr_unittest.TestCase): rrate = 1.123 # resampling rate nfilts = 32 - taps = gr.firdes.low_pass_2(nfilts, nfilts*fs, fs/2, fs/10, - attenuation_dB=80, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + taps = filter.firdes.low_pass_2(nfilts, nfilts*fs, fs/2, fs/10, + attenuation_dB=80, + window=filter.firdes.WIN_BLACKMAN_hARRIS) freq = 100 signal = gr.sig_source_c(fs, gr.GR_SIN_WAVE, freq, 1) diff --git a/gr-filter/python/qa_pfb_channelizer.py b/gr-filter/python/qa_pfb_channelizer.py index b52c80e8b..33d2b2188 100755 --- a/gr-filter/python/qa_pfb_channelizer.py +++ b/gr-filter/python/qa_pfb_channelizer.py @@ -38,9 +38,9 @@ class test_pfb_channelizer(gr_unittest.TestCase): fs = 1000 # baseband sampling rate ifs = M*fs # input samp rate to channelizer - taps = gr.firdes.low_pass_2(1, ifs, 500, 50, - attenuation_dB=80, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + taps = filter.firdes.low_pass_2(1, ifs, 500, 50, + attenuation_dB=80, + window=filter.firdes.WIN_BLACKMAN_hARRIS) signals = list() add = gr.add_cc() diff --git a/gr-filter/python/qa_pfb_decimator.py b/gr-filter/python/qa_pfb_decimator.py index c8fd408be..063845f63 100755 --- a/gr-filter/python/qa_pfb_decimator.py +++ b/gr-filter/python/qa_pfb_decimator.py @@ -39,9 +39,9 @@ class test_pfb_decimator(gr_unittest.TestCase): ifs = M*fs # input samp rate to decimator channel = 0 # Extract channel 0 - taps = gr.firdes.low_pass_2(1, ifs, fs/2, fs/10, - attenuation_dB=80, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + taps = filter.firdes.low_pass_2(1, ifs, fs/2, fs/10, + attenuation_dB=80, + window=filter.firdes.WIN_BLACKMAN_hARRIS) signals = list() add = gr.add_cc() @@ -84,9 +84,9 @@ class test_pfb_decimator(gr_unittest.TestCase): ifs = M*fs # input samp rate to decimator channel = 1 # Extract channel 0 - taps = gr.firdes.low_pass_2(1, ifs, fs/2, fs/10, + taps = filter.firdes.low_pass_2(1, ifs, fs/2, fs/10, attenuation_dB=80, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + window=filter.firdes.WIN_BLACKMAN_hARRIS) signals = list() add = gr.add_cc() diff --git a/gr-filter/python/qa_pfb_interpolator.py b/gr-filter/python/qa_pfb_interpolator.py index f83c57d60..5b84b7c64 100755 --- a/gr-filter/python/qa_pfb_interpolator.py +++ b/gr-filter/python/qa_pfb_interpolator.py @@ -38,9 +38,9 @@ class test_pfb_interpolator(gr_unittest.TestCase): fs = 1000 # baseband sampling rate ifs = M*fs # input samp rate to decimator - taps = gr.firdes.low_pass_2(M, ifs, fs/2, fs/10, - attenuation_dB=80, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + taps = filter.firdes.low_pass_2(M, ifs, fs/2, fs/10, + attenuation_dB=80, + window=filter.firdes.WIN_BLACKMAN_hARRIS) freq = 100 signal = gr.sig_source_c(fs, gr.GR_COS_WAVE, freq, 1) diff --git a/gr-filter/python/qa_pfb_synthesizer.py b/gr-filter/python/qa_pfb_synthesizer.py index 8b69ccb12..e8164d268 100755 --- a/gr-filter/python/qa_pfb_synthesizer.py +++ b/gr-filter/python/qa_pfb_synthesizer.py @@ -38,9 +38,9 @@ class test_pfb_synthesizer(gr_unittest.TestCase): fs = 1000 # baseband sampling rate ofs = M*fs # input samp rate to decimator - taps = gr.firdes.low_pass_2(M, ofs, fs/2, fs/10, - attenuation_dB=80, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + taps = filter.firdes.low_pass_2(M, ofs, fs/2, fs/10, + attenuation_dB=80, + window=filter.firdes.WIN_BLACKMAN_hARRIS) signals = list() freqs = [0, 100, 200, -200, -100] diff --git a/gr-filter/python/qa_pm_remez.py b/gr-filter/python/qa_pm_remez.py index 765e2ea6a..a76e14c93 100755 --- a/gr-filter/python/qa_pm_remez.py +++ b/gr-filter/python/qa_pm_remez.py @@ -159,7 +159,7 @@ class test_pm_remez(gr_unittest.TestCase): desired_ampls = (gain, 0) (n, fo, ao, w) = remezord([freq1, freq2], desired_ampls, [passband_dev, stopband_dev], Fs) - new_taps = gr.remez(n + 2, fo, ao, w, "bandpass") + new_taps = filter.pm_remez(n + 2, fo, ao, w, "bandpass") known_taps = (-0.0008370135734511828, -0.0006622211673134374, 0.0008501079576365787, 0.003059609130249229, diff --git a/gr-filter/python/rational_resampler.py b/gr-filter/python/rational_resampler.py index eea12af95..312b011d3 100644 --- a/gr-filter/python/rational_resampler.py +++ b/gr-filter/python/rational_resampler.py @@ -44,14 +44,12 @@ def design_filter(interpolation, decimation, fractional_bw): trans_width = 0.5 - fractional_bw mid_transition_band = 0.5 - trans_width/2 - taps = gr.firdes.low_pass(interpolation, # gain - 1, # Fs - mid_transition_band/interpolation, # trans mid point - trans_width/interpolation, # transition width - gr.firdes.WIN_KAISER, - beta # beta - ) - + taps = filter.firdes.low_pass(interpolation, # gain + 1, # Fs + mid_transition_band/interpolation, # trans mid point + trans_width/interpolation, # transition width + filter.firdes.WIN_KAISER, + beta) # beta return taps @@ -109,7 +107,7 @@ class rational_resampler_fff(_rational_resampler_base): Rational resampling polyphase FIR filter with float input, float output and float taps. """ - _rational_resampler_base.__init__(self, gr.rational_resampler_base_fff, + _rational_resampler_base.__init__(self, filter.rational_resampler_base_fff, interpolation, decimation, taps, fractional_bw) class rational_resampler_ccf(_rational_resampler_base): @@ -118,7 +116,7 @@ class rational_resampler_ccf(_rational_resampler_base): Rational resampling polyphase FIR filter with complex input, complex output and float taps. """ - _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccf, + _rational_resampler_base.__init__(self, filter.rational_resampler_base_ccf, interpolation, decimation, taps, fractional_bw) class rational_resampler_ccc(_rational_resampler_base): @@ -127,5 +125,5 @@ class rational_resampler_ccc(_rational_resampler_base): Rational resampling polyphase FIR filter with complex input, complex output and complex taps. """ - _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccc, + _rational_resampler_base.__init__(self, filter.rational_resampler_base_ccc, interpolation, decimation, taps, fractional_bw) -- cgit From 8a0ead8481c2418560dd98d8bb64c82842a76bcc Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 21:32:37 -0400 Subject: filter: Moved over PFB examples. --- gr-filter/examples/CMakeLists.txt | 14 +- gr-filter/examples/decimate.py | 181 ++++++++++ gr-filter/examples/fmtest.py | 226 ++++++++++++ gr-filter/examples/interpolate.py | 240 +++++++++++++ gr-filter/examples/reconstruction.py | 164 +++++++++ gr-filter/examples/resampler.py | 128 +++++++ gr-filter/examples/resampler_demo.grc | 630 ++++++++++++++++++++++++++++++++++ gr-filter/examples/synth_filter.py | 85 +++++ gr-filter/examples/synth_to_chan.py | 118 +++++++ 9 files changed, 1783 insertions(+), 3 deletions(-) create mode 100755 gr-filter/examples/decimate.py create mode 100755 gr-filter/examples/fmtest.py create mode 100755 gr-filter/examples/interpolate.py create mode 100755 gr-filter/examples/reconstruction.py create mode 100755 gr-filter/examples/resampler.py create mode 100644 gr-filter/examples/resampler_demo.grc create mode 100755 gr-filter/examples/synth_filter.py create mode 100755 gr-filter/examples/synth_to_chan.py diff --git a/gr-filter/examples/CMakeLists.txt b/gr-filter/examples/CMakeLists.txt index 2eca31726..d94e5c16e 100644 --- a/gr-filter/examples/CMakeLists.txt +++ b/gr-filter/examples/CMakeLists.txt @@ -21,11 +21,19 @@ include(GrPython) # Base stuff GR_PYTHON_INSTALL(PROGRAMS - fir_filter_fff.py - fir_filter_ccc.py - fft_filter_ccc.py channelize.py chirp_channelize.py + decimate.py + fft_filter_ccc.py + fir_filter_ccc.py + fir_filter_fff.py + fmtest.py + interpolate.py + reconstruction.py + resampler.py + resampler_demo.grc + synth_filter.py + synth_to_chan.py DESTINATION ${GR_PKG_FILTER_EXAMPLES_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/examples/decimate.py b/gr-filter/examples/decimate.py new file mode 100755 index 000000000..7f1f03d8f --- /dev/null +++ b/gr-filter/examples/decimate.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# +# Copyright 2009,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import sys, time + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab + from pylab import mlab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +class pfb_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self._N = 10000000 # number of samples to use + self._fs = 10000 # initial sampling rate + self._decim = 20 # Decimation rate + + # Generate the prototype filter taps for the decimators with a 200 Hz bandwidth + self._taps = filter.firdes.low_pass_2(1, self._fs, + 200, 150, + attenuation_dB=120, + window=filter.firdes.WIN_BLACKMAN_hARRIS) + + # Calculate the number of taps per channel for our own information + tpc = scipy.ceil(float(len(self._taps)) / float(self._decim)) + print "Number of taps: ", len(self._taps) + print "Number of filters: ", self._decim + print "Taps per channel: ", tpc + + # Build the input signal source + # We create a list of freqs, and a sine wave is generated and added to the source + # for each one of these frequencies. + self.signals = list() + self.add = gr.add_cc() + freqs = [10, 20, 2040] + for i in xrange(len(freqs)): + self.signals.append(gr.sig_source_c(self._fs, gr.GR_SIN_WAVE, freqs[i], 1)) + self.connect(self.signals[i], (self.add,i)) + + self.head = gr.head(gr.sizeof_gr_complex, self._N) + + # Construct a PFB decimator filter + self.pfb = filter.pfb.decimator_ccf(self._decim, self._taps, 0) + + # Construct a standard FIR decimating filter + self.dec = filter.fir_filter_ccf(self._decim, self._taps) + + self.snk_i = gr.vector_sink_c() + + # Connect the blocks + self.connect(self.add, self.head, self.pfb) + self.connect(self.add, self.snk_i) + + # Create the sink for the decimated siganl + self.snk = gr.vector_sink_c() + self.connect(self.pfb, self.snk) + + +def main(): + tb = pfb_top_block() + + tstart = time.time() + tb.run() + tend = time.time() + print "Run time: %f" % (tend - tstart) + + if 1: + fig1 = pylab.figure(1, figsize=(16,9)) + fig2 = pylab.figure(2, figsize=(16,9)) + + Ns = 10000 + Ne = 10000 + + fftlen = 8192 + winfunc = scipy.blackman + fs = tb._fs + + # Plot the input to the decimator + + d = tb.snk_i.data()[Ns:Ns+Ne] + sp1_f = fig1.add_subplot(2, 1, 1) + + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_in = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_in = scipy.arange(-fs/2.0, fs/2.0, fs/float(X_in.size)) + p1_f = sp1_f.plot(f_in, X_in, "b") + sp1_f.set_xlim([min(f_in), max(f_in)+1]) + sp1_f.set_ylim([-200.0, 50.0]) + + sp1_f.set_title("Input Signal", weight="bold") + sp1_f.set_xlabel("Frequency (Hz)") + sp1_f.set_ylabel("Power (dBW)") + + Ts = 1.0/fs + Tmax = len(d)*Ts + + t_in = scipy.arange(0, Tmax, Ts) + x_in = scipy.array(d) + sp1_t = fig1.add_subplot(2, 1, 2) + p1_t = sp1_t.plot(t_in, x_in.real, "b") + p1_t = sp1_t.plot(t_in, x_in.imag, "r") + sp1_t.set_ylim([-tb._decim*1.1, tb._decim*1.1]) + + sp1_t.set_xlabel("Time (s)") + sp1_t.set_ylabel("Amplitude") + + + # Plot the output of the decimator + fs_o = tb._fs / tb._decim + + sp2_f = fig2.add_subplot(2, 1, 1) + d = tb.snk.data()[Ns:Ns+Ne] + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs_o, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_o = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_o = scipy.arange(-fs_o/2.0, fs_o/2.0, fs_o/float(X_o.size)) + p2_f = sp2_f.plot(f_o, X_o, "b") + sp2_f.set_xlim([min(f_o), max(f_o)+1]) + sp2_f.set_ylim([-200.0, 50.0]) + + sp2_f.set_title("PFB Decimated Signal", weight="bold") + sp2_f.set_xlabel("Frequency (Hz)") + sp2_f.set_ylabel("Power (dBW)") + + + Ts_o = 1.0/fs_o + Tmax_o = len(d)*Ts_o + + x_o = scipy.array(d) + t_o = scipy.arange(0, Tmax_o, Ts_o) + sp2_t = fig2.add_subplot(2, 1, 2) + p2_t = sp2_t.plot(t_o, x_o.real, "b-o") + p2_t = sp2_t.plot(t_o, x_o.imag, "r-o") + sp2_t.set_ylim([-2.5, 2.5]) + + sp2_t.set_xlabel("Time (s)") + sp2_t.set_ylabel("Amplitude") + + pylab.show() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/fmtest.py b/gr-filter/examples/fmtest.py new file mode 100755 index 000000000..9b02526d9 --- /dev/null +++ b/gr-filter/examples/fmtest.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# +# Copyright 2009,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blks2 +from gnuradio import filter +import sys, math, time + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + + +class fmtx(gr.hier_block2): + def __init__(self, lo_freq, audio_rate, if_rate): + + gr.hier_block2.__init__(self, "build_fm", + gr.io_signature(1, 1, gr.sizeof_float), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) + + fmtx = blks2.nbfm_tx (audio_rate, if_rate, max_dev=5e3, tau=75e-6) + + # Local oscillator + lo = gr.sig_source_c (if_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + lo_freq, #frequency + 1.0, # amplitude + 0) # DC Offset + mixer = gr.multiply_cc () + + self.connect (self, fmtx, (mixer, 0)) + self.connect (lo, (mixer, 1)) + self.connect (mixer, self) + +class fmtest(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self._nsamples = 1000000 + self._audio_rate = 8000 + + # Set up N channels with their own baseband and IF frequencies + self._N = 5 + chspacing = 16000 + freq = [10, 20, 30, 40, 50] + f_lo = [0, 1*chspacing, -1*chspacing, 2*chspacing, -2*chspacing] + + self._if_rate = 4*self._N*self._audio_rate + + # Create a signal source and frequency modulate it + self.sum = gr.add_cc () + for n in xrange(self._N): + sig = gr.sig_source_f(self._audio_rate, gr.GR_SIN_WAVE, freq[n], 0.5) + fm = fmtx(f_lo[n], self._audio_rate, self._if_rate) + self.connect(sig, fm) + self.connect(fm, (self.sum, n)) + + self.head = gr.head(gr.sizeof_gr_complex, self._nsamples) + self.snk_tx = gr.vector_sink_c() + self.channel = blks2.channel_model(0.1) + + self.connect(self.sum, self.head, self.channel, self.snk_tx) + + + # Design the channlizer + self._M = 10 + bw = chspacing/2.0 + t_bw = chspacing/10.0 + self._chan_rate = self._if_rate / self._M + self._taps = filter.firdes.low_pass_2(1, self._if_rate, bw, t_bw, + attenuation_dB=100, + window=filter.firdes.WIN_BLACKMAN_hARRIS) + tpc = math.ceil(float(len(self._taps)) / float(self._M)) + + print "Number of taps: ", len(self._taps) + print "Number of channels: ", self._M + print "Taps per channel: ", tpc + + self.pfb = filter.pfb.channelizer_ccf(self._M, self._taps) + + self.connect(self.channel, self.pfb) + + # Create a file sink for each of M output channels of the filter and connect it + self.fmdet = list() + self.squelch = list() + self.snks = list() + for i in xrange(self._M): + self.fmdet.append(blks2.nbfm_rx(self._audio_rate, self._chan_rate)) + self.squelch.append(blks2.standard_squelch(self._audio_rate*10)) + self.snks.append(gr.vector_sink_f()) + self.connect((self.pfb, i), self.fmdet[i], self.squelch[i], self.snks[i]) + + def num_tx_channels(self): + return self._N + + def num_rx_channels(self): + return self._M + +def main(): + + fm = fmtest() + + tstart = time.time() + fm.run() + tend = time.time() + + if 1: + fig1 = pylab.figure(1, figsize=(12,10), facecolor="w") + fig2 = pylab.figure(2, figsize=(12,10), facecolor="w") + fig3 = pylab.figure(3, figsize=(12,10), facecolor="w") + + Ns = 10000 + Ne = 100000 + + fftlen = 8192 + winfunc = scipy.blackman + + # Plot transmitted signal + fs = fm._if_rate + + d = fm.snk_tx.data()[Ns:Ns+Ne] + sp1_f = fig1.add_subplot(2, 1, 1) + + X,freq = sp1_f.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + visible=False) + X_in = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_in = scipy.arange(-fs/2.0, fs/2.0, fs/float(X_in.size)) + p1_f = sp1_f.plot(f_in, X_in, "b") + sp1_f.set_xlim([min(f_in), max(f_in)+1]) + sp1_f.set_ylim([-120.0, 20.0]) + + sp1_f.set_title("Input Signal", weight="bold") + sp1_f.set_xlabel("Frequency (Hz)") + sp1_f.set_ylabel("Power (dBW)") + + Ts = 1.0/fs + Tmax = len(d)*Ts + + t_in = scipy.arange(0, Tmax, Ts) + x_in = scipy.array(d) + sp1_t = fig1.add_subplot(2, 1, 2) + p1_t = sp1_t.plot(t_in, x_in.real, "b-o") + #p1_t = sp1_t.plot(t_in, x_in.imag, "r-o") + sp1_t.set_ylim([-5, 5]) + + # Set up the number of rows and columns for plotting the subfigures + Ncols = int(scipy.floor(scipy.sqrt(fm.num_rx_channels()))) + Nrows = int(scipy.floor(fm.num_rx_channels() / Ncols)) + if(fm.num_rx_channels() % Ncols != 0): + Nrows += 1 + + # Plot each of the channels outputs. Frequencies on Figure 2 and + # time signals on Figure 3 + fs_o = fm._audio_rate + for i in xrange(len(fm.snks)): + # remove issues with the transients at the beginning + # also remove some corruption at the end of the stream + # this is a bug, probably due to the corner cases + d = fm.snks[i].data()[Ns:Ne] + + sp2_f = fig2.add_subplot(Nrows, Ncols, 1+i) + X,freq = sp2_f.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs_o, + window = lambda d: d*winfunc(fftlen), + visible=False) + #X_o = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + X_o = 10.0*scipy.log10(abs(X)) + #f_o = scipy.arange(-fs_o/2.0, fs_o/2.0, fs_o/float(X_o.size)) + f_o = scipy.arange(0, fs_o/2.0, fs_o/2.0/float(X_o.size)) + p2_f = sp2_f.plot(f_o, X_o, "b") + sp2_f.set_xlim([min(f_o), max(f_o)+0.1]) + sp2_f.set_ylim([-120.0, 20.0]) + sp2_f.grid(True) + + sp2_f.set_title(("Channel %d" % i), weight="bold") + sp2_f.set_xlabel("Frequency (kHz)") + sp2_f.set_ylabel("Power (dBW)") + + + Ts = 1.0/fs_o + Tmax = len(d)*Ts + t_o = scipy.arange(0, Tmax, Ts) + + x_t = scipy.array(d) + sp2_t = fig3.add_subplot(Nrows, Ncols, 1+i) + p2_t = sp2_t.plot(t_o, x_t.real, "b") + p2_t = sp2_t.plot(t_o, x_t.imag, "r") + sp2_t.set_xlim([min(t_o), max(t_o)+1]) + sp2_t.set_ylim([-1, 1]) + + sp2_t.set_xlabel("Time (s)") + sp2_t.set_ylabel("Amplitude") + + + pylab.show() + + +if __name__ == "__main__": + main() diff --git a/gr-filter/examples/interpolate.py b/gr-filter/examples/interpolate.py new file mode 100755 index 000000000..56d78d597 --- /dev/null +++ b/gr-filter/examples/interpolate.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# +# Copyright 2009,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import sys, time + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab + from pylab import mlab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +class pfb_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self._N = 100000 # number of samples to use + self._fs = 2000 # initial sampling rate + self._interp = 5 # Interpolation rate for PFB interpolator + self._ainterp = 5.5 # Resampling rate for the PFB arbitrary resampler + + # Frequencies of the signals we construct + freq1 = 100 + freq2 = 200 + + # Create a set of taps for the PFB interpolator + # This is based on the post-interpolation sample rate + self._taps = filter.firdes.low_pass_2(self._interp, + self._interp*self._fs, + freq2+50, 50, + attenuation_dB=120, + window=filter.firdes.WIN_BLACKMAN_hARRIS) + + # Create a set of taps for the PFB arbitrary resampler + # The filter size is the number of filters in the filterbank; 32 will give very low side-lobes, + # and larger numbers will reduce these even farther + # The taps in this filter are based on a sampling rate of the filter size since it acts + # internally as an interpolator. + flt_size = 32 + self._taps2 = filter.firdes.low_pass_2(flt_size, + flt_size*self._fs, + freq2+50, 150, + attenuation_dB=120, + window=filter.firdes.WIN_BLACKMAN_hARRIS) + + # Calculate the number of taps per channel for our own information + tpc = scipy.ceil(float(len(self._taps)) / float(self._interp)) + print "Number of taps: ", len(self._taps) + print "Number of filters: ", self._interp + print "Taps per channel: ", tpc + + # Create a couple of signals at different frequencies + self.signal1 = gr.sig_source_c(self._fs, gr.GR_SIN_WAVE, freq1, 0.5) + self.signal2 = gr.sig_source_c(self._fs, gr.GR_SIN_WAVE, freq2, 0.5) + self.signal = gr.add_cc() + + self.head = gr.head(gr.sizeof_gr_complex, self._N) + + # Construct the PFB interpolator filter + self.pfb = filter.pfb.interpolator_ccf(self._interp, self._taps) + + # Construct the PFB arbitrary resampler filter + self.pfb_ar = filter.pfb.arb_resampler_ccf(self._ainterp, self._taps2, flt_size) + self.snk_i = gr.vector_sink_c() + + #self.pfb_ar.pfb.print_taps() + #self.pfb.pfb.print_taps() + + # Connect the blocks + self.connect(self.signal1, self.head, (self.signal,0)) + self.connect(self.signal2, (self.signal,1)) + self.connect(self.signal, self.pfb) + self.connect(self.signal, self.pfb_ar) + self.connect(self.signal, self.snk_i) + + # Create the sink for the interpolated signals + self.snk1 = gr.vector_sink_c() + self.snk2 = gr.vector_sink_c() + self.connect(self.pfb, self.snk1) + self.connect(self.pfb_ar, self.snk2) + + +def main(): + tb = pfb_top_block() + + tstart = time.time() + tb.run() + tend = time.time() + print "Run time: %f" % (tend - tstart) + + + if 1: + fig1 = pylab.figure(1, figsize=(12,10), facecolor="w") + fig2 = pylab.figure(2, figsize=(12,10), facecolor="w") + fig3 = pylab.figure(3, figsize=(12,10), facecolor="w") + + Ns = 10000 + Ne = 10000 + + fftlen = 8192 + winfunc = scipy.blackman + + # Plot input signal + fs = tb._fs + + d = tb.snk_i.data()[Ns:Ns+Ne] + sp1_f = fig1.add_subplot(2, 1, 1) + + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_in = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_in = scipy.arange(-fs/2.0, fs/2.0, fs/float(X_in.size)) + p1_f = sp1_f.plot(f_in, X_in, "b") + sp1_f.set_xlim([min(f_in), max(f_in)+1]) + sp1_f.set_ylim([-200.0, 50.0]) + + + sp1_f.set_title("Input Signal", weight="bold") + sp1_f.set_xlabel("Frequency (Hz)") + sp1_f.set_ylabel("Power (dBW)") + + Ts = 1.0/fs + Tmax = len(d)*Ts + + t_in = scipy.arange(0, Tmax, Ts) + x_in = scipy.array(d) + sp1_t = fig1.add_subplot(2, 1, 2) + p1_t = sp1_t.plot(t_in, x_in.real, "b-o") + #p1_t = sp1_t.plot(t_in, x_in.imag, "r-o") + sp1_t.set_ylim([-2.5, 2.5]) + + sp1_t.set_title("Input Signal", weight="bold") + sp1_t.set_xlabel("Time (s)") + sp1_t.set_ylabel("Amplitude") + + + # Plot output of PFB interpolator + fs_int = tb._fs*tb._interp + + sp2_f = fig2.add_subplot(2, 1, 1) + d = tb.snk1.data()[Ns:Ns+(tb._interp*Ne)] + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_o = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_o = scipy.arange(-fs_int/2.0, fs_int/2.0, fs_int/float(X_o.size)) + p2_f = sp2_f.plot(f_o, X_o, "b") + sp2_f.set_xlim([min(f_o), max(f_o)+1]) + sp2_f.set_ylim([-200.0, 50.0]) + + sp2_f.set_title("Output Signal from PFB Interpolator", weight="bold") + sp2_f.set_xlabel("Frequency (Hz)") + sp2_f.set_ylabel("Power (dBW)") + + Ts_int = 1.0/fs_int + Tmax = len(d)*Ts_int + + t_o = scipy.arange(0, Tmax, Ts_int) + x_o1 = scipy.array(d) + sp2_t = fig2.add_subplot(2, 1, 2) + p2_t = sp2_t.plot(t_o, x_o1.real, "b-o") + #p2_t = sp2_t.plot(t_o, x_o.imag, "r-o") + sp2_t.set_ylim([-2.5, 2.5]) + + sp2_t.set_title("Output Signal from PFB Interpolator", weight="bold") + sp2_t.set_xlabel("Time (s)") + sp2_t.set_ylabel("Amplitude") + + + # Plot output of PFB arbitrary resampler + fs_aint = tb._fs * tb._ainterp + + sp3_f = fig3.add_subplot(2, 1, 1) + d = tb.snk2.data()[Ns:Ns+(tb._interp*Ne)] + X,freq = mlab.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + scale_by_freq=True) + X_o = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_o = scipy.arange(-fs_aint/2.0, fs_aint/2.0, fs_aint/float(X_o.size)) + p3_f = sp3_f.plot(f_o, X_o, "b") + sp3_f.set_xlim([min(f_o), max(f_o)+1]) + sp3_f.set_ylim([-200.0, 50.0]) + + sp3_f.set_title("Output Signal from PFB Arbitrary Resampler", weight="bold") + sp3_f.set_xlabel("Frequency (Hz)") + sp3_f.set_ylabel("Power (dBW)") + + Ts_aint = 1.0/fs_aint + Tmax = len(d)*Ts_aint + + t_o = scipy.arange(0, Tmax, Ts_aint) + x_o2 = scipy.array(d) + sp3_f = fig3.add_subplot(2, 1, 2) + p3_f = sp3_f.plot(t_o, x_o2.real, "b-o") + p3_f = sp3_f.plot(t_o, x_o1.real, "m-o") + #p3_f = sp3_f.plot(t_o, x_o2.imag, "r-o") + sp3_f.set_ylim([-2.5, 2.5]) + + sp3_f.set_title("Output Signal from PFB Arbitrary Resampler", weight="bold") + sp3_f.set_xlabel("Time (s)") + sp3_f.set_ylabel("Amplitude") + + pylab.show() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/reconstruction.py b/gr-filter/examples/reconstruction.py new file mode 100755 index 000000000..9e38f3669 --- /dev/null +++ b/gr-filter/examples/reconstruction.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# Copyright 2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, digital +from gnuradio import filter + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +fftlen = 8192 + +def main(): + N = 10000 + fs = 2000.0 + Ts = 1.0/fs + t = scipy.arange(0, N*Ts, Ts) + + # When playing with the number of channels, be careful about the filter + # specs and the channel map of the synthesizer set below. + nchans = 10 + + # Build the filter(s) + bw = 1000 + tb = 400 + proto_taps = filter.firdes.low_pass_2(1, nchans*fs, + bw, tb, 80, + filter.firdes.WIN_BLACKMAN_hARRIS) + print "Filter length: ", len(proto_taps) + + + # Create a modulated signal + npwr = 0.01 + data = scipy.random.randint(0, 256, N) + rrc_taps = filter.firdes.root_raised_cosine(1, 2, 1, 0.35, 41) + + src = gr.vector_source_b(data.astype(scipy.uint8).tolist(), False) + mod = digital.bpsk_mod(samples_per_symbol=2) + chan = gr.channel_model(npwr) + rrc = filter.fft_filter_ccc(1, rrc_taps) + + # Split it up into pieces + channelizer = filter.pfb.channelizer_ccf(nchans, proto_taps, 2) + + # Put the pieces back together again + syn_taps = [nchans*t for t in proto_taps] + synthesizer = filter.pfb_synthesizer_ccf(nchans, syn_taps, True) + src_snk = gr.vector_sink_c() + snk = gr.vector_sink_c() + + # Remap the location of the channels + # Can be done in synth or channelizer (watch out for rotattions in + # the channelizer) + synthesizer.set_channel_map([ 0, 1, 2, 3, 4, + 15, 16, 17, 18, 19]) + + tb = gr.top_block() + tb.connect(src, mod, chan, rrc, channelizer) + tb.connect(rrc, src_snk) + + vsnk = [] + for i in xrange(nchans): + tb.connect((channelizer,i), (synthesizer, i)) + + vsnk.append(gr.vector_sink_c()) + tb.connect((channelizer,i), vsnk[i]) + + tb.connect(synthesizer, snk) + tb.run() + + sin = scipy.array(src_snk.data()[1000:]) + sout = scipy.array(snk.data()[1000:]) + + + # Plot original signal + fs_in = nchans*fs + f1 = pylab.figure(1, figsize=(16,12), facecolor='w') + s11 = f1.add_subplot(2,2,1) + s11.psd(sin, NFFT=fftlen, Fs=fs_in) + s11.set_title("PSD of Original Signal") + s11.set_ylim([-200, -20]) + + s12 = f1.add_subplot(2,2,2) + s12.plot(sin.real[1000:1500], "o-b") + s12.plot(sin.imag[1000:1500], "o-r") + s12.set_title("Original Signal in Time") + + start = 1 + skip = 4 + s13 = f1.add_subplot(2,2,3) + s13.plot(sin.real[start::skip], sin.imag[start::skip], "o") + s13.set_title("Constellation") + s13.set_xlim([-2, 2]) + s13.set_ylim([-2, 2]) + + # Plot channels + nrows = int(scipy.sqrt(nchans)) + ncols = int(scipy.ceil(float(nchans)/float(nrows))) + + f2 = pylab.figure(2, figsize=(16,12), facecolor='w') + for n in xrange(nchans): + s = f2.add_subplot(nrows, ncols, n+1) + s.psd(vsnk[n].data(), NFFT=fftlen, Fs=fs_in) + s.set_title("Channel {0}".format(n)) + s.set_ylim([-200, -20]) + + # Plot reconstructed signal + fs_out = 2*nchans*fs + f3 = pylab.figure(3, figsize=(16,12), facecolor='w') + s31 = f3.add_subplot(2,2,1) + s31.psd(sout, NFFT=fftlen, Fs=fs_out) + s31.set_title("PSD of Reconstructed Signal") + s31.set_ylim([-200, -20]) + + s32 = f3.add_subplot(2,2,2) + s32.plot(sout.real[1000:1500], "o-b") + s32.plot(sout.imag[1000:1500], "o-r") + s32.set_title("Reconstructed Signal in Time") + + start = 2 + skip = 4 + s33 = f3.add_subplot(2,2,3) + s33.plot(sout.real[start::skip], sout.imag[start::skip], "o") + s33.set_title("Constellation") + s33.set_xlim([-2, 2]) + s33.set_ylim([-2, 2]) + + pylab.show() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-filter/examples/resampler.py b/gr-filter/examples/resampler.py new file mode 100755 index 000000000..395e095cb --- /dev/null +++ b/gr-filter/examples/resampler.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# +# Copyright 2009,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import sys + +try: + import scipy +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +class mytb(gr.top_block): + def __init__(self, fs_in, fs_out, fc, N=10000): + gr.top_block.__init__(self) + + rerate = float(fs_out) / float(fs_in) + print "Resampling from %f to %f by %f " %(fs_in, fs_out, rerate) + + # Creating our own taps + taps = gr.firdes.low_pass_2(32, 32, 0.25, 0.1, 80) + + self.src = gr.sig_source_c(fs_in, gr.GR_SIN_WAVE, fc, 1) + #self.src = gr.noise_source_c(gr.GR_GAUSSIAN, 1) + self.head = gr.head(gr.sizeof_gr_complex, N) + + # A resampler with our taps + self.resamp_0 = filter.pfb.arb_resampler_ccf(rerate, taps, + flt_size=32) + + # A resampler that just needs a resampling rate. + # Filter is created for us and designed to cover + # entire bandwidth of the input signal. + # An optional atten=XX rate can be used here to + # specify the out-of-band rejection (default=80). + self.resamp_1 = filter.pfb.arb_resampler_ccf(rerate) + + self.snk_in = gr.vector_sink_c() + self.snk_0 = gr.vector_sink_c() + self.snk_1 = gr.vector_sink_c() + + self.connect(self.src, self.head, self.snk_in) + self.connect(self.head, self.resamp_0, self.snk_0) + self.connect(self.head, self.resamp_1, self.snk_1) + +def main(): + fs_in = 8000 + fs_out = 20000 + fc = 1000 + N = 10000 + + tb = mytb(fs_in, fs_out, fc, N) + tb.run() + + + # Plot PSD of signals + nfftsize = 2048 + fig1 = pylab.figure(1, figsize=(10,10), facecolor="w") + sp1 = fig1.add_subplot(2,1,1) + sp1.psd(tb.snk_in.data(), NFFT=nfftsize, + noverlap=nfftsize/4, Fs = fs_in) + sp1.set_title(("Input Signal at f_s=%.2f kHz" % (fs_in/1000.0))) + sp1.set_xlim([-fs_in/2, fs_in/2]) + + sp2 = fig1.add_subplot(2,1,2) + sp2.psd(tb.snk_0.data(), NFFT=nfftsize, + noverlap=nfftsize/4, Fs = fs_out, + label="With our filter") + sp2.psd(tb.snk_1.data(), NFFT=nfftsize, + noverlap=nfftsize/4, Fs = fs_out, + label="With auto-generated filter") + sp2.set_title(("Output Signals at f_s=%.2f kHz" % (fs_out/1000.0))) + sp2.set_xlim([-fs_out/2, fs_out/2]) + sp2.legend() + + # Plot signals in time + Ts_in = 1.0/fs_in + Ts_out = 1.0/fs_out + t_in = scipy.arange(0, len(tb.snk_in.data())*Ts_in, Ts_in) + t_out = scipy.arange(0, len(tb.snk_0.data())*Ts_out, Ts_out) + + fig2 = pylab.figure(2, figsize=(10,10), facecolor="w") + sp21 = fig2.add_subplot(2,1,1) + sp21.plot(t_in, tb.snk_in.data()) + sp21.set_title(("Input Signal at f_s=%.2f kHz" % (fs_in/1000.0))) + sp21.set_xlim([t_in[100], t_in[200]]) + + sp22 = fig2.add_subplot(2,1,2) + sp22.plot(t_out, tb.snk_0.data(), + label="With our filter") + sp22.plot(t_out, tb.snk_1.data(), + label="With auto-generated filter") + sp22.set_title(("Output Signals at f_s=%.2f kHz" % (fs_out/1000.0))) + r = float(fs_out)/float(fs_in) + sp22.set_xlim([t_out[r * 100], t_out[r * 200]]) + sp22.legend() + + pylab.show() + +if __name__ == "__main__": + main() + diff --git a/gr-filter/examples/resampler_demo.grc b/gr-filter/examples/resampler_demo.grc new file mode 100644 index 000000000..b3991ac19 --- /dev/null +++ b/gr-filter/examples/resampler_demo.grc @@ -0,0 +1,630 @@ + + + Tue Jun 19 21:31:30 2012 + + options + + id + resampler_demo + + + _enabled + True + + + title + + + + author + + + + description + + + + window_size + 1280, 1024 + + + generate_options + wx_gui + + + category + Custom + + + run_options + prompt + + + run + True + + + realtime_scheduling + + + + _coordinate + (10, 10) + + + _rotation + 0 + + + + import + + id + import_0 + + + _enabled + True + + + import + import math + + + _coordinate + (11, 59) + + + _rotation + 0 + + + + variable + + id + rs_taps + + + _enabled + True + + + value + firdes.low_pass(nphases, nphases, frac_bw, 0.5-frac_bw) + + + _coordinate + (273, 154) + + + _rotation + 0 + + + + gr_add_const_vxx + + id + adder + + + _enabled + True + + + type + float + + + const + -1.0 + + + vlen + 1 + + + _coordinate + (227, 303) + + + _rotation + 0 + + + + gr_throttle + + id + throttle + + + _enabled + True + + + type + float + + + samples_per_second + samp_rate + + + vlen + 1 + + + _coordinate + (227, 493) + + + _rotation + 0 + + + + wxgui_fftsink2 + + id + orig_fft + + + _enabled + True + + + type + complex + + + title + Original Spectrum + + + samp_rate + samp_rate + + + baseband_freq + 0 + + + y_per_div + 10 + + + y_divs + 10 + + + ref_level + 30 + + + ref_scale + 2.0 + + + fft_size + 1024 + + + fft_rate + 30 + + + peak_hold + False + + + average + False + + + avg_alpha + 0 + + + win + None + + + win_size + + + + grid_pos + 1, 0, 1, 3 + + + notebook + + + + _coordinate + (409, 289) + + + _rotation + 180 + + + + gr_sig_source_x + + id + tri_source + + + _enabled + True + + + type + float + + + samp_rate + samp_rate + + + waveform + gr.GR_TRI_WAVE + + + freq + 0.05 + + + amp + 2.0 + + + offset + 0 + + + _coordinate + (21, 271) + + + _rotation + 0 + + + + variable + + id + nphases + + + _enabled + True + + + value + 32 + + + _coordinate + (185, 153) + + + _rotation + 0 + + + + variable_static_text + + id + samp_rate + + + _enabled + True + + + label + Sample Rate + + + value + 44100 + + + converver + float_converter + + + formatter + None + + + grid_pos + 0, 0, 1, 1 + + + notebook + + + + _coordinate + (179, 14) + + + _rotation + 0 + + + + variable_static_text + + id + new_rate + + + _enabled + True + + + label + Resampled Rate + + + value + 48000 + + + converver + float_converter + + + formatter + None + + + grid_pos + 0, 1, 1, 1 + + + notebook + + + + _coordinate + (328, 15) + + + _rotation + 0 + + + + variable_static_text + + id + frac_bw + + + _enabled + True + + + label + Fractional Bandwidth + + + value + 0.45 + + + converver + float_converter + + + formatter + lambda x: "%0.2f"%x + + + grid_pos + 0,2,1,1 + + + notebook + + + + _coordinate + (473, 14) + + + _rotation + 0 + + + + gr_frequency_modulator_fc + + id + fm_mod + + + _enabled + True + + + sensitivity + math.pi + + + _coordinate + (411, 493) + + + _rotation + 0 + + + + wxgui_fftsink2 + + id + resamp_fft + + + _enabled + True + + + type + complex + + + title + Resampled Spectrum + + + samp_rate + new_rate + + + baseband_freq + 0 + + + y_per_div + 10 + + + y_divs + 10 + + + ref_level + 30 + + + ref_scale + 2.0 + + + fft_size + 1024 + + + fft_rate + 30 + + + peak_hold + True + + + average + False + + + avg_alpha + 0 + + + win + None + + + win_size + + + + grid_pos + 2, 0, 1, 3 + + + notebook + + + + _coordinate + (640, 256) + + + _rotation + 180 + + + + pfb_arb_resampler_xxx + + id + pfb_arb_resampler_xxx_0 + + + _enabled + True + + + type + ccf + + + rrate + float(new_rate)/samp_rate + + + taps + rs_taps + + + nfilts + nphases + + + _coordinate + (649, 469) + + + _rotation + 0 + + + + tri_source + adder + 0 + 0 + + + adder + throttle + 0 + 0 + + + fm_mod + orig_fft + 0 + 0 + + + throttle + fm_mod + 0 + 0 + + + fm_mod + pfb_arb_resampler_xxx_0 + 0 + 0 + + + pfb_arb_resampler_xxx_0 + resamp_fft + 0 + 0 + + diff --git a/gr-filter/examples/synth_filter.py b/gr-filter/examples/synth_filter.py new file mode 100755 index 000000000..a5b6fdf4c --- /dev/null +++ b/gr-filter/examples/synth_filter.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# Copyright 2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import sys + +try: + import scipy +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +def main(): + N = 1000000 + fs = 8000 + + freqs = [100, 200, 300, 400, 500] + nchans = 7 + + sigs = list() + for fi in freqs: + s = gr.sig_source_c(fs, gr.GR_SIN_WAVE, fi, 1) + sigs.append(s) + + taps = filter.firdes.low_pass_2(len(freqs), fs, + fs/float(nchans)/2, 100, 100) + print "Num. Taps = %d (taps per filter = %d)" % (len(taps), + len(taps)/nchans) + filtbank = filter.pfb_synthesizer_ccf(nchans, taps) + + head = gr.head(gr.sizeof_gr_complex, N) + snk = gr.vector_sink_c() + + tb = gr.top_block() + tb.connect(filtbank, head, snk) + + for i,si in enumerate(sigs): + tb.connect(si, (filtbank, i)) + + tb.run() + + if 1: + f1 = pylab.figure(1) + s1 = f1.add_subplot(1,1,1) + s1.plot(snk.data()[1000:]) + + fftlen = 2048 + f2 = pylab.figure(2) + s2 = f2.add_subplot(1,1,1) + winfunc = scipy.blackman + s2.psd(snk.data()[10000:], NFFT=fftlen, + Fs = nchans*fs, + noverlap=fftlen/4, + window = lambda d: d*winfunc(fftlen)) + + pylab.show() + +if __name__ == "__main__": + main() diff --git a/gr-filter/examples/synth_to_chan.py b/gr-filter/examples/synth_to_chan.py new file mode 100755 index 000000000..b5f4c958e --- /dev/null +++ b/gr-filter/examples/synth_to_chan.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# Copyright 2010,2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blks2 +from gnuradio import filter +import sys + +try: + import scipy +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + +def main(): + N = 1000000 + fs = 8000 + + freqs = [100, 200, 300, 400, 500] + nchans = 7 + + sigs = list() + fmtx = list() + for fi in freqs: + s = gr.sig_source_f(fs, gr.GR_SIN_WAVE, fi, 1) + fm = blks2.nbfm_tx (fs, 4*fs, max_dev=10000, tau=75e-6) + sigs.append(s) + fmtx.append(fm) + + syntaps = filter.firdes.low_pass_2(len(freqs), fs, fs/float(nchans)/2, 100, 100) + print "Synthesis Num. Taps = %d (taps per filter = %d)" % (len(syntaps), + len(syntaps)/nchans) + chtaps = filter.firdes.low_pass_2(len(freqs), fs, fs/float(nchans)/2, 100, 100) + print "Channelizer Num. Taps = %d (taps per filter = %d)" % (len(chtaps), + len(chtaps)/nchans) + filtbank = filter.pfb_synthesizer_ccf(nchans, syntaps) + channelizer = filter.pfb.channelizer_ccf(nchans, chtaps) + + noise_level = 0.01 + head = gr.head(gr.sizeof_gr_complex, N) + noise = gr.noise_source_c(gr.GR_GAUSSIAN, noise_level) + addnoise = gr.add_cc() + snk_synth = gr.vector_sink_c() + + tb = gr.top_block() + + tb.connect(noise, (addnoise,0)) + tb.connect(filtbank, head, (addnoise, 1)) + tb.connect(addnoise, channelizer) + tb.connect(addnoise, snk_synth) + + snk = list() + for i,si in enumerate(sigs): + tb.connect(si, fmtx[i], (filtbank, i)) + + for i in xrange(nchans): + snk.append(gr.vector_sink_c()) + tb.connect((channelizer, i), snk[i]) + + tb.run() + + if 1: + channel = 1 + data = snk[channel].data()[1000:] + + f1 = pylab.figure(1) + s1 = f1.add_subplot(1,1,1) + s1.plot(data[10000:10200] ) + s1.set_title(("Output Signal from Channel %d" % channel)) + + fftlen = 2048 + winfunc = scipy.blackman + #winfunc = scipy.hamming + + f2 = pylab.figure(2) + s2 = f2.add_subplot(1,1,1) + s2.psd(data, NFFT=fftlen, + Fs = nchans*fs, + noverlap=fftlen/4, + window = lambda d: d*winfunc(fftlen)) + s2.set_title(("Output PSD from Channel %d" % channel)) + + f3 = pylab.figure(3) + s3 = f3.add_subplot(1,1,1) + s3.psd(snk_synth.data()[1000:], NFFT=fftlen, + Fs = nchans*fs, + noverlap=fftlen/4, + window = lambda d: d*winfunc(fftlen)) + s3.set_title("Output of Synthesis Filter") + + pylab.show() + +if __name__ == "__main__": + main() -- cgit From d8d8e992e3f94792fefa74c61d32494903f77dfb Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 22:22:06 -0400 Subject: filter: added channel_model block with GRC. Dummy QA test needs work. More documentation. --- gr-filter/grc/CMakeLists.txt | 1 + gr-filter/grc/channel_model.xml | 61 ++++++++++++++ gr-filter/grc/filter_block_tree.xml | 1 + gr-filter/include/filter/CMakeLists.txt | 1 + gr-filter/include/filter/channel_model.h | 63 +++++++++++++++ gr-filter/lib/CMakeLists.txt | 1 + gr-filter/lib/channel_model_impl.cc | 135 +++++++++++++++++++++++++++++++ gr-filter/lib/channel_model_impl.h | 74 +++++++++++++++++ gr-filter/python/qa_channel_model.py | 43 ++++++++++ gr-filter/swig/filter_swig.i | 3 + 10 files changed, 383 insertions(+) create mode 100644 gr-filter/grc/channel_model.xml create mode 100644 gr-filter/include/filter/channel_model.h create mode 100644 gr-filter/lib/channel_model_impl.cc create mode 100644 gr-filter/lib/channel_model_impl.h create mode 100755 gr-filter/python/qa_channel_model.py diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 8bbb4c9e0..2e3fef4f0 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -35,6 +35,7 @@ install(FILES pfb_synthesizer.xml rational_resampler_base_xxx.xml single_pole_iir_filter_xx.xml + channel_model.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/grc/channel_model.xml b/gr-filter/grc/channel_model.xml new file mode 100644 index 000000000..6d780974a --- /dev/null +++ b/gr-filter/grc/channel_model.xml @@ -0,0 +1,61 @@ + + + + Channel Model + channel_model + from gnuradio import filter + from gnuradio.filter import firdes + filter.channel_model( + noise_voltage=$noise_voltage, + frequency_offset=$freq_offset, + epsilon=$epsilon, + taps=$taps, + noise_seed=$seed, +) + set_noise_voltage($noise_voltage) + set_frequency_offset($freq_offset) + set_taps($taps) + set_timing_offset($epsilon) + + Noise Voltage + noise_voltage + 0.0 + real + + + Frequency Offset + freq_offset + 0.0 + real + + + Epsilon + epsilon + 1.0 + real + + + Taps + taps + 1.0 + 1.0j + complex_vector + + + Seed + seed + 0 + int + + + in + complex + + + out + complex + + diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index b16966eae..711ce4059 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -46,5 +46,6 @@ pfb_synthesizer_ccf rational_resampler_base_xxx single_pole_iir_filter_xx + channel_model diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt index b30a1fc76..c6bf109cd 100644 --- a/gr-filter/include/filter/CMakeLists.txt +++ b/gr-filter/include/filter/CMakeLists.txt @@ -109,6 +109,7 @@ install(FILES pfb_synthesizer_ccf.h single_pole_iir_filter_cc.h single_pole_iir_filter_ff.h + channel_model.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter COMPONENT "filter_devel" ) diff --git a/gr-filter/include/filter/channel_model.h b/gr-filter/include/filter/channel_model.h new file mode 100644 index 000000000..42b879515 --- /dev/null +++ b/gr-filter/include/filter/channel_model.h @@ -0,0 +1,63 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_CHANNEL_MODEL_H +#define INCLUDED_FILTER_CHANNEL_MODEL_H + +#include +#include +#include + +namespace gr { + namespace filter { + + /*! + * \brief channel simulator + * \ingroup misc_blk + */ + class FILTER_API channel_model : virtual public gr_hier_block2 + { + public: + // gr::filter::channel_model::sptr + typedef boost::shared_ptr sptr; + + static FILTER_API sptr make(double noise_voltage, + double frequency_offset, + double epsilon, + const std::vector &taps, + double noise_seed=0); + + virtual void set_noise_voltage(double noise_voltage) = 0; + virtual void set_frequency_offset(double frequency_offset) = 0; + virtual void set_taps(const std::vector &taps) = 0; + virtual void set_timing_offset(double epsilon) = 0; + + virtual double noise_voltage() const = 0; + virtual double frequency_offset() const = 0; + virtual std::vector taps() const = 0; + virtual double timing_offset() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_CHANNEL_MODEL_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index e29d69db5..a54cd7b1d 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -136,6 +136,7 @@ list(APPEND filter_sources pfb_synthesizer_ccf_impl.cc single_pole_iir_filter_cc_impl.cc single_pole_iir_filter_ff_impl.cc + channel_model_impl.cc ) list(APPEND filter_libs diff --git a/gr-filter/lib/channel_model_impl.cc b/gr-filter/lib/channel_model_impl.cc new file mode 100644 index 000000000..f9e995479 --- /dev/null +++ b/gr-filter/lib/channel_model_impl.cc @@ -0,0 +1,135 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "channel_model_impl.h" +#include +#include + +namespace gr { + namespace filter { + + channel_model::sptr + channel_model::make(double noise_voltage, + double frequency_offset, + double epsilon, + const std::vector &taps, + double noise_seed) + { + return gnuradio::get_initial_sptr + (new channel_model_impl(noise_voltage, + frequency_offset, + epsilon, + taps, + noise_seed)); + } + + // Hierarchical block constructor + channel_model_impl::channel_model_impl(double noise_voltage, + double frequency_offset, + double epsilon, + const std::vector &taps, + double noise_seed) + : gr_hier_block2("gr_channel_model", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex))) + { + d_taps = taps; + while(d_taps.size() < 2) { + d_taps.push_back(0); + } + + d_timing_offset = fractional_interpolator_cc::make(0, epsilon); + + d_multipath = fir_filter_ccc::make(1, d_taps); + + d_noise_adder = gr_make_add_cc(); + d_noise = gr_make_noise_source_c(GR_GAUSSIAN, noise_voltage, noise_seed); + d_freq_offset = gr_make_sig_source_c(1, GR_SIN_WAVE, frequency_offset, 1.0, 0.0); + d_mixer_offset = gr_make_multiply_cc(); + + connect(self(), 0, d_timing_offset, 0); + connect(d_timing_offset, 0, d_multipath, 0); + connect(d_multipath, 0, d_mixer_offset, 0); + connect(d_freq_offset, 0, d_mixer_offset, 1); + connect(d_mixer_offset, 0, d_noise_adder, 1); + connect(d_noise, 0, d_noise_adder, 0); + connect(d_noise_adder, 0, self(), 0); + } + + channel_model_impl::~channel_model_impl() + { + } + + void + channel_model_impl::set_noise_voltage(double noise_voltage) + { + d_noise->set_amplitude(noise_voltage); + } + + void + channel_model_impl::set_frequency_offset(double frequency_offset) + { + d_freq_offset->set_frequency(frequency_offset); + } + + void + channel_model_impl::set_taps(const std::vector &taps) + { + d_taps = taps; + while(d_taps.size() < 2) { + d_taps.push_back(0); + } + d_multipath->set_taps(d_taps); + } + + void + channel_model_impl::set_timing_offset(double epsilon) + { + d_timing_offset->set_interp_ratio(epsilon); + } + + double + channel_model_impl::noise_voltage() const + { + return d_noise->amplitude(); + } + + double + channel_model_impl::frequency_offset() const + { + return d_freq_offset->frequency(); + } + + std::vector + channel_model_impl::taps() const + { + return d_multipath->taps(); + } + + double + channel_model_impl::timing_offset() const + { + return d_timing_offset->interp_ratio(); + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/channel_model_impl.h b/gr-filter/lib/channel_model_impl.h new file mode 100644 index 000000000..95a63d790 --- /dev/null +++ b/gr-filter/lib/channel_model_impl.h @@ -0,0 +1,74 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_FILTER_CHANNEL_MODEL_IMPL_H +#define INCLUDED_FILTER_CHANNEL_MODEL_IMPL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gr { + namespace filter { + + class FILTER_API channel_model_impl : public channel_model + { + private: + gr_sig_source_c_sptr d_freq_offset; + gr_add_cc_sptr d_noise_adder; + gr_noise_source_c_sptr d_noise; + gr_multiply_cc_sptr d_mixer_offset; + + fractional_interpolator_cc::sptr d_timing_offset; + fir_filter_ccc::sptr d_multipath; + + std::vector d_taps; + + public: + channel_model_impl(double noise_voltage, + double frequency_offset, + double epsilon, + const std::vector &taps, + double noise_seed); + + ~channel_model_impl(); + + void set_noise_voltage(double noise_voltage); + void set_frequency_offset(double frequency_offset); + void set_taps(const std::vector &taps); + void set_timing_offset(double epsilon); + + double noise_voltage() const; + double frequency_offset() const; + std::vector taps() const; + double timing_offset() const; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_CHANNEL_MODEL_IMPL_H */ diff --git a/gr-filter/python/qa_channel_model.py b/gr-filter/python/qa_channel_model.py new file mode 100755 index 000000000..d908f83f7 --- /dev/null +++ b/gr-filter/python/qa_channel_model.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import filter_swig as filter + +class test_channel_model(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_000_make(self): + op = filter.channel_model(0.0, 0.0, 1.0, [1,], 0) + op.set_noise_voltage(0.0) + op.set_frequency_offset(0.0) + op.set_taps([1,]) + op.set_timing_offset(1.0) + + +if __name__ == '__main__': + gr_unittest.run(test_channel_model, "test_channel_model.xml") diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index bb4eab381..05b84d4d6 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -73,6 +73,7 @@ #include "filter/rational_resampler_base_scc.h" #include "filter/single_pole_iir_filter_cc.h" #include "filter/single_pole_iir_filter_ff.h" +#include "filter/channel_model.h" %} %include "filter/firdes.h" @@ -120,6 +121,7 @@ %include "filter/rational_resampler_base_scc.h" %include "filter/single_pole_iir_filter_cc.h" %include "filter/single_pole_iir_filter_ff.h" +%include "filter/channel_model.h" GR_SWIG_BLOCK_MAGIC2(filter, adaptive_fir_ccc); GR_SWIG_BLOCK_MAGIC2(filter, adaptive_fir_ccf); @@ -164,3 +166,4 @@ GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_fsf); GR_SWIG_BLOCK_MAGIC2(filter, rational_resampler_base_scc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_cc); GR_SWIG_BLOCK_MAGIC2(filter, single_pole_iir_filter_ff); +GR_SWIG_BLOCK_MAGIC2(filter, channel_model); -- cgit From a04d3d784fe25447ad653ba28d243fa40d268c52 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 22:25:40 -0400 Subject: filter: fixed some include issues. --- gr-filter/lib/fft_filter.cc | 1 + gr-filter/lib/qa_fir_filter_with_buffer.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gr-filter/lib/fft_filter.cc b/gr-filter/lib/fft_filter.cc index 130886d7f..1b256a3da 100644 --- a/gr-filter/lib/fft_filter.cc +++ b/gr-filter/lib/fft_filter.cc @@ -27,6 +27,7 @@ #include #include #include +#include namespace gr { namespace filter { diff --git a/gr-filter/lib/qa_fir_filter_with_buffer.cc b/gr-filter/lib/qa_fir_filter_with_buffer.cc index c97e2479c..f8af9ea0b 100644 --- a/gr-filter/lib/qa_fir_filter_with_buffer.cc +++ b/gr-filter/lib/qa_fir_filter_with_buffer.cc @@ -31,7 +31,7 @@ #include #include #include - +#include namespace gr { namespace filter { -- cgit From 21bdb18789c818ea78deb81ae0f251c5e0043462 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 23:35:28 -0400 Subject: filter: improved QA code for fractional_interpolator and channel_model. Setting various parameters in the channel model should already be checked by the QA code of the respective components since this is a hier_block2. --- gr-filter/python/qa_channel_model.py | 21 +++++++++- gr-filter/python/qa_fractional_interpolator.py | 56 ++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/gr-filter/python/qa_channel_model.py b/gr-filter/python/qa_channel_model.py index d908f83f7..7f1c61b4e 100755 --- a/gr-filter/python/qa_channel_model.py +++ b/gr-filter/python/qa_channel_model.py @@ -22,6 +22,7 @@ from gnuradio import gr, gr_unittest import filter_swig as filter +import math class test_channel_model(gr_unittest.TestCase): @@ -31,13 +32,29 @@ class test_channel_model(gr_unittest.TestCase): def tearDown(self): self.tb = None - def test_000_make(self): + def test_000(self): + N = 1000 # number of samples to use + fs = 1000 # baseband sampling rate + freq = 100 + + signal = gr.sig_source_c(fs, gr.GR_SIN_WAVE, freq, 1) + head = gr.head(gr.sizeof_gr_complex, N) op = filter.channel_model(0.0, 0.0, 1.0, [1,], 0) + snk = gr.vector_sink_c() + snk1 = gr.vector_sink_c() + op.set_noise_voltage(0.0) op.set_frequency_offset(0.0) op.set_taps([1,]) op.set_timing_offset(1.0) - + self.tb.connect(signal, head, op, snk) + self.tb.connect(op, snk1) + self.tb.run() + + dst_data = snk.data() + exp_data = snk1.data() + self.assertComplexTuplesAlmostEqual(exp_data, dst_data, 5) + if __name__ == '__main__': gr_unittest.run(test_channel_model, "test_channel_model.xml") diff --git a/gr-filter/python/qa_fractional_interpolator.py b/gr-filter/python/qa_fractional_interpolator.py index 74dae5d85..9e0f685d8 100755 --- a/gr-filter/python/qa_fractional_interpolator.py +++ b/gr-filter/python/qa_fractional_interpolator.py @@ -22,6 +22,7 @@ from gnuradio import gr, gr_unittest import filter_swig as filter +import math class test_fractional_resampler(gr_unittest.TestCase): @@ -31,9 +32,58 @@ class test_fractional_resampler(gr_unittest.TestCase): def tearDown(self): self.tb = None - def test_000_make(self): - op = filter.fractional_interpolator_ff(0.0, 1.0) - op2 = filter.fractional_interpolator_cc(0.0, 1.0) + def test_001_ff(self): + N = 10000 # number of samples to use + fs = 1000 # baseband sampling rate + rrate = 1.123 # resampling rate + + freq = 10 + signal = gr.sig_source_f(fs, gr.GR_SIN_WAVE, freq, 1) + head = gr.head(gr.sizeof_float, N) + op = filter.fractional_interpolator_ff(0, rrate) + snk = gr.vector_sink_f() + + self.tb.connect(signal, head, op, snk) + self.tb.run() + + Ntest = 5000 + L = len(snk.data()) + t = map(lambda x: float(x)/(fs/rrate), xrange(L)) + + phase = 0.1884 + expected_data = map(lambda x: math.sin(2.*math.pi*freq*x+phase), t) + + dst_data = snk.data() + + self.assertFloatTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) + + + def test_002_cc(self): + N = 10000 # number of samples to use + fs = 1000 # baseband sampling rate + rrate = 1.123 # resampling rate + + freq = 10 + signal = gr.sig_source_c(fs, gr.GR_SIN_WAVE, freq, 1) + head = gr.head(gr.sizeof_gr_complex, N) + op = filter.fractional_interpolator_cc(0.0, rrate) + snk = gr.vector_sink_c() + + self.tb.connect(signal, head, op, snk) + self.tb.run() + + Ntest = 5000 + L = len(snk.data()) + t = map(lambda x: float(x)/(fs/rrate), xrange(L)) + + phase = 0.1884 + expected_data = map(lambda x: math.cos(2.*math.pi*freq*x+phase) + \ + 1j*math.sin(2.*math.pi*freq*x+phase), t) + + dst_data = snk.data() + + self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) + if __name__ == '__main__': gr_unittest.run(test_fractional_resampler, "test_fractional_resampler.xml") -- cgit From d8830d6dfcb2ddb8d1794c2575019993ca7eb3ad Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 19 Jun 2012 23:49:53 -0400 Subject: filter: improving documentation. --- gr-filter/include/filter/channel_model.h | 34 ++++++++++++++++++---- .../include/filter/fractional_interpolator_cc.h | 6 ++++ .../include/filter/fractional_interpolator_ff.h | 6 ++++ .../include/filter/freq_xlating_fir_filter_XXX.h.t | 6 ++-- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/gr-filter/include/filter/channel_model.h b/gr-filter/include/filter/channel_model.h index 42b879515..44d7f1a64 100644 --- a/gr-filter/include/filter/channel_model.h +++ b/gr-filter/include/filter/channel_model.h @@ -33,6 +33,16 @@ namespace gr { /*! * \brief channel simulator * \ingroup misc_blk + * + * This block implements a basic channel model simulator that can + * be used to help evaluate, design, and test various signals, + * waveforms, and algorithms. This model allows the user to set + * the voltage of an AWGN noise source, a (normalized) frequency + * offset, a sample timing offset, and a noise seed to randomize + * the AWGN noise source. + * + * Multipath can be approximated in this model by using a FIR + * filter representation of a multipath delay profile.. */ class FILTER_API channel_model : virtual public gr_hier_block2 { @@ -40,11 +50,25 @@ namespace gr { // gr::filter::channel_model::sptr typedef boost::shared_ptr sptr; - static FILTER_API sptr make(double noise_voltage, - double frequency_offset, - double epsilon, - const std::vector &taps, - double noise_seed=0); + /*! \brief Build the channel simulator. + * + * \param noise_voltage The AWGN noise level as a voltage (to be + * calculated externally to meet, say, a + * desired SNR). + * \param frequency_offset The normalized frequency offset. 0 is + * no offset; 0.25 would be, for a digital + * modem, one quarter of the symbol rate. + * \param epsilon The sample timing offset to emulate the + * different rates between the sample clocks of + * the transmitter and receiver. 1.0 is no difference. + * \param taps Taps of a FIR filter to emulate a multipath delay profile. + * \param noise_seed A random number generator seed for the noise source. + */ + static FILTER_API sptr make(double noise_voltage=0.0, + double frequency_offset=0.0, + double epsilon=1.0, + const std::vector &taps=std::vector(1,1), + double noise_seed=3021); virtual void set_noise_voltage(double noise_voltage) = 0; virtual void set_frequency_offset(double frequency_offset) = 0; diff --git a/gr-filter/include/filter/fractional_interpolator_cc.h b/gr-filter/include/filter/fractional_interpolator_cc.h index e5d066bbc..3f196c80f 100644 --- a/gr-filter/include/filter/fractional_interpolator_cc.h +++ b/gr-filter/include/filter/fractional_interpolator_cc.h @@ -39,6 +39,12 @@ namespace gr { // gr::filter::fractional_interpolator_cc::sptr typedef boost::shared_ptr sptr; + /*! + * \brief Build the interpolating MMSE filter (complex input, complex output) + * + * \param phase_shift The phase shift of the output signal to the input + * \param interp_ratio The interpolation ratio = input_rate / output_rate. + */ static FILTER_API sptr make(float phase_shift, float interp_ratio); diff --git a/gr-filter/include/filter/fractional_interpolator_ff.h b/gr-filter/include/filter/fractional_interpolator_ff.h index 8280baee3..e05da847d 100644 --- a/gr-filter/include/filter/fractional_interpolator_ff.h +++ b/gr-filter/include/filter/fractional_interpolator_ff.h @@ -39,6 +39,12 @@ namespace gr { // gr::filter::fractional_interpolator_ff::sptr typedef boost::shared_ptr sptr; + /*! + * \brief Build the interpolating MMSE filter (float input, float output) + * + * \param phase_shift The phase shift of the output signal to the input + * \param interp_ratio The interpolation ratio = input_rate / output_rate. + */ static FILTER_API sptr make(float phase_shift, float interp_ratio); diff --git a/gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t b/gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t index d56303461..8764c4f0d 100644 --- a/gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t +++ b/gr-filter/include/filter/freq_xlating_fir_filter_XXX.h.t @@ -38,8 +38,9 @@ namespace gr { /*! * \brief FIR filter combined with frequency translation with - * @I_TYPE@ input, @O_TYPE@ output and @TAP_TYPE@ taps \ingroup - * filter_blk + * @I_TYPE@ input, @O_TYPE@ output and @TAP_TYPE@ taps + * + * \ingroup filter_blk * * This class efficiently combines a frequency translation * (typically "down conversion") with a FIR filter (typically @@ -60,7 +61,6 @@ namespace gr { * \brief FIR filter with @I_TYPE@ input, @O_TYPE@ output, and * @TAP_TYPE@ taps that also frequency translates a signal from * \p center_freq. - * \ingroup filter_blk * * Construct a FIR filter with the given taps and a composite * frequency translation that shifts center_freq down to zero -- cgit From edfa18840e2caf738596578acd0bf4efedc88fd1 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 20 Jun 2012 09:10:23 -0400 Subject: filter: added documentation to adaptive_filter blocks. --- gr-filter/include/filter/adaptive_fir_ccc.h | 36 +++++++++++++++++++++++++++-- gr-filter/include/filter/adaptive_fir_ccf.h | 31 ++++++++++++++++++++++++- gr-filter/lib/adaptive_fir_ccf_impl.cc | 2 +- gr-filter/lib/adaptive_fir_ccf_impl.h | 2 +- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/gr-filter/include/filter/adaptive_fir_ccc.h b/gr-filter/include/filter/adaptive_fir_ccc.h index b30cd353c..616a52f73 100644 --- a/gr-filter/include/filter/adaptive_fir_ccc.h +++ b/gr-filter/include/filter/adaptive_fir_ccc.h @@ -30,6 +30,35 @@ namespace gr { namespace filter { + /*! + * \brief Adaptive FIR filter with gr_complex input, gr_complex output and gr_complex taps + * \ingroup filter_blk + * + * This is a base class to implement an adaptive FIR + * filter. Generally, another block will inherit from this one to + * build a new type of adaptive filter such as an equalizer. + * + * This class implements two functions that are designed to be + * overloaded by the child class: error(gr_complex out) and + * update_tap(gr_complex tap, gr_complex in). + * + * The error() function calculates the error value that will be + * used to adjust the taps. The update_tap function then uses the + * error and the input signal value to update a particular + * tap. Typically, the error is calculated for a given output and + * then this is used in a loop to update all of the filter taps in + * a loop: + * + * \code + * d_error = error(sum); + * for(k = 0; k < l; k++) { + * update_tap(d_taps[ntaps-k-1], in[i+k]); + * } + * \endcode + * + * See digital::cma_equalizer_cc and digital::lms_dd_equalizer_cc + * for example usage. + */ class FILTER_API adaptive_fir_ccc : virtual public gr_sync_decimator { public: @@ -37,8 +66,11 @@ namespace gr { typedef boost::shared_ptr sptr; /*! - * \brief Adaptive FIR filter with gr_complex input, gr_complex output and float taps - * \ingroup filter_blk + * \brief Adaptive FIR filter with gr_complex input, gr_complex output and gr_complex taps + * + * \param name Provides a name to identify this type of algorithm + * \param decimation (interger) decimation rate of the filter + * \param taps (complex) filter taps */ static FILTER_API sptr make(const char *name, int decimation, const std::vector &taps); diff --git a/gr-filter/include/filter/adaptive_fir_ccf.h b/gr-filter/include/filter/adaptive_fir_ccf.h index 0503acd4e..0c19611d5 100644 --- a/gr-filter/include/filter/adaptive_fir_ccf.h +++ b/gr-filter/include/filter/adaptive_fir_ccf.h @@ -29,6 +29,32 @@ namespace gr { namespace filter { + /*! + * \brief Adaptive FIR filter with gr_complex input, gr_complex output and float taps + * \ingroup filter_blk + * + * This is a base class to implement an adaptive FIR + * filter. Generally, another block will inherit from this one to + * build a new type of adaptive filter such as an equalizer. + * + * This class implements two functions that are designed to be + * overloaded by the child class: error(gr_complex out) and + * update_tap(float tap, gr_complex in). + * + * The error() function calculates the error value that will be + * used to adjust the taps. The update_tap function then uses the + * error and the input signal value to update a particular + * tap. Typically, the error is calculated for a given output and + * then this is used in a loop to update all of the filter taps in + * a loop: + * + * \code + * d_error = error(sum); + * for(k = 0; k < l; k++) { + * update_tap(d_taps[ntaps-k-1], in[i+k]); + * } + * \endcode + */ class FILTER_API adaptive_fir_ccf : virtual public gr_sync_decimator { public: @@ -37,7 +63,10 @@ namespace gr { /*! * \brief Adaptive FIR filter with gr_complex input, gr_complex output and float taps - * \ingroup filter_blk + * + * \param name Provides a name to identify this type of algorithm + * \param decimation (interger) decimation rate of the filter + * \param taps (real) filter taps */ static FILTER_API sptr make(const char *name, int decimation, const std::vector &taps); diff --git a/gr-filter/lib/adaptive_fir_ccf_impl.cc b/gr-filter/lib/adaptive_fir_ccf_impl.cc index 62d7e5337..004a9286b 100644 --- a/gr-filter/lib/adaptive_fir_ccf_impl.cc +++ b/gr-filter/lib/adaptive_fir_ccf_impl.cc @@ -69,7 +69,7 @@ namespace gr { } void - adaptive_fir_ccf_impl::update_tap(gr_complex &tap, const gr_complex &in) + adaptive_fir_ccf_impl::update_tap(float &tap, const gr_complex &in) { tap = tap; } diff --git a/gr-filter/lib/adaptive_fir_ccf_impl.h b/gr-filter/lib/adaptive_fir_ccf_impl.h index 2a1c7e5e9..a0c9581ea 100644 --- a/gr-filter/lib/adaptive_fir_ccf_impl.h +++ b/gr-filter/lib/adaptive_fir_ccf_impl.h @@ -41,7 +41,7 @@ namespace gr { float error(const gr_complex &out); // Override to calculate new weight from old, corresponding input - void update_tap(gr_complex &tap, const gr_complex &in); + void update_tap(float &tap, const gr_complex &in); public: void set_taps(const std::vector &taps); -- cgit From 81bb55f3a7bb8f64c2bc75b02306700fb5b60a43 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 20 Jun 2012 10:27:32 -0400 Subject: filter: fixed bug with includes. --- gr-filter/lib/fir_filter.cc | 1 + gr-filter/lib/fir_filter_with_buffer.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/gr-filter/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc index 8e585f245..233405824 100644 --- a/gr-filter/lib/fir_filter.cc +++ b/gr-filter/lib/fir_filter.cc @@ -24,6 +24,7 @@ #include #include #include +#include namespace gr { namespace filter { diff --git a/gr-filter/lib/fir_filter_with_buffer.cc b/gr-filter/lib/fir_filter_with_buffer.cc index 58ef67fb7..105214087 100644 --- a/gr-filter/lib/fir_filter_with_buffer.cc +++ b/gr-filter/lib/fir_filter_with_buffer.cc @@ -29,6 +29,7 @@ #include #include #include +#include namespace gr { namespace filter { -- cgit