summaryrefslogtreecommitdiff
path: root/gr-filter/lib
diff options
context:
space:
mode:
Diffstat (limited to 'gr-filter/lib')
-rw-r--r--gr-filter/lib/CMakeLists.txt184
-rw-r--r--gr-filter/lib/adaptive_fir_ccc_impl.cc106
-rw-r--r--gr-filter/lib/adaptive_fir_ccc_impl.h61
-rw-r--r--gr-filter/lib/adaptive_fir_ccf_impl.cc106
-rw-r--r--gr-filter/lib/adaptive_fir_ccf_impl.h61
-rw-r--r--gr-filter/lib/channel_model_impl.cc135
-rw-r--r--gr-filter/lib/channel_model_impl.h74
-rw-r--r--gr-filter/lib/dc_blocker_cc_impl.cc144
-rw-r--r--gr-filter/lib/dc_blocker_cc_impl.h76
-rw-r--r--gr-filter/lib/dc_blocker_ff_impl.cc142
-rw-r--r--gr-filter/lib/dc_blocker_ff_impl.h76
-rw-r--r--gr-filter/lib/fft_filter.cc316
-rw-r--r--gr-filter/lib/fft_filter_ccc_impl.cc118
-rw-r--r--gr-filter/lib/fft_filter_ccc_impl.h61
-rw-r--r--gr-filter/lib/fft_filter_fff_impl.cc119
-rw-r--r--gr-filter/lib/fft_filter_fff_impl.h61
-rw-r--r--gr-filter/lib/filter_delay_fc_impl.cc112
-rw-r--r--gr-filter/lib/filter_delay_fc_impl.h56
-rw-r--r--gr-filter/lib/fir_filter.cc667
-rw-r--r--gr-filter/lib/fir_filter_XXX_impl.cc.t107
-rw-r--r--gr-filter/lib/fir_filter_XXX_impl.h.t56
-rw-r--r--gr-filter/lib/fir_filter_with_buffer.cc496
-rw-r--r--gr-filter/lib/firdes.cc855
-rw-r--r--gr-filter/lib/fractional_interpolator_cc_impl.cc125
-rw-r--r--gr-filter/lib/fractional_interpolator_cc_impl.h61
-rw-r--r--gr-filter/lib/fractional_interpolator_ff_impl.cc125
-rw-r--r--gr-filter/lib/fractional_interpolator_ff_impl.h61
-rw-r--r--gr-filter/lib/freq_xlating_fir_filter_XXX_impl.cc.t143
-rw-r--r--gr-filter/lib/freq_xlating_fir_filter_XXX_impl.h.t71
-rw-r--r--gr-filter/lib/hilbert_fc_impl.cc76
-rw-r--r--gr-filter/lib/hilbert_fc_impl.h52
-rw-r--r--gr-filter/lib/iir_filter_ffd_impl.cc85
-rw-r--r--gr-filter/lib/iir_filter_ffd_impl.h56
-rw-r--r--gr-filter/lib/interp_fir_filter_XXX_impl.cc.t155
-rw-r--r--gr-filter/lib/interp_fir_filter_XXX_impl.h.t62
-rw-r--r--gr-filter/lib/mmse_fir_interpolator_cc.cc77
-rw-r--r--gr-filter/lib/mmse_fir_interpolator_ff.cc77
-rw-r--r--gr-filter/lib/pfb_arb_resampler_ccf_impl.cc258
-rw-r--r--gr-filter/lib/pfb_arb_resampler_ccf_impl.h89
-rw-r--r--gr-filter/lib/pfb_arb_resampler_fff_impl.cc258
-rw-r--r--gr-filter/lib/pfb_arb_resampler_fff_impl.h88
-rw-r--r--gr-filter/lib/pfb_channelizer_ccf_impl.cc191
-rw-r--r--gr-filter/lib/pfb_channelizer_ccf_impl.h69
-rw-r--r--gr-filter/lib/pfb_decimator_ccf_impl.cc137
-rw-r--r--gr-filter/lib/pfb_decimator_ccf_impl.h65
-rw-r--r--gr-filter/lib/pfb_interpolator_ccf_impl.cc107
-rw-r--r--gr-filter/lib/pfb_interpolator_ccf_impl.h61
-rw-r--r--gr-filter/lib/pfb_synthesizer_ccf_impl.cc289
-rw-r--r--gr-filter/lib/pfb_synthesizer_ccf_impl.h85
-rw-r--r--gr-filter/lib/pm_remez.cc834
-rw-r--r--gr-filter/lib/polyphase_filterbank.cc114
-rw-r--r--gr-filter/lib/qa_filter.cc47
-rw-r--r--gr-filter/lib/qa_filter.h38
-rw-r--r--gr-filter/lib/qa_fir_filter_with_buffer.cc406
-rw-r--r--gr-filter/lib/qa_fir_filter_with_buffer.h94
-rw-r--r--gr-filter/lib/qa_firdes.cc621
-rw-r--r--gr-filter/lib/qa_firdes.h56
-rw-r--r--gr-filter/lib/qa_mmse_fir_interpolator_cc.cc126
-rw-r--r--gr-filter/lib/qa_mmse_fir_interpolator_cc.h48
-rw-r--r--gr-filter/lib/qa_mmse_fir_interpolator_ff.cc71
-rw-r--r--gr-filter/lib/qa_mmse_fir_interpolator_ff.h45
-rw-r--r--gr-filter/lib/rational_resampler_base_XXX_impl.cc.t174
-rw-r--r--gr-filter/lib/rational_resampler_base_XXX_impl.h.t72
-rw-r--r--gr-filter/lib/single_pole_iir_filter_cc_impl.cc89
-rw-r--r--gr-filter/lib/single_pole_iir_filter_cc_impl.h55
-rw-r--r--gr-filter/lib/single_pole_iir_filter_ff_impl.cc87
-rw-r--r--gr-filter/lib/single_pole_iir_filter_ff_impl.h54
-rw-r--r--gr-filter/lib/test_gr_filter.cc43
68 files changed, 10186 insertions, 0 deletions
diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt
new file mode 100644
index 000000000..626f5521f
--- /dev/null
+++ b/gr-filter/lib/CMakeLists.txt
@@ -0,0 +1,184 @@
+# 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_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 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)
+
+
+########################################################################
+# Setup the include and linker paths
+########################################################################
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${GR_FILTER_INCLUDE_DIRS}
+ ${GR_FFT_INCLUDE_DIRS}
+ ${GNURADIO_CORE_INCLUDE_DIRS}
+ ${VOLK_INCLUDE_DIRS}
+ ${GRUEL_INCLUDE_DIRS}
+ ${Boost_INCLUDE_DIRS}
+)
+
+link_directories(${Boost_LIBRARY_DIRS})
+
+########################################################################
+# Setup library
+########################################################################
+list(APPEND filter_sources
+ fir_filter.cc
+ 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}
+ 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
+ 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_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
+ 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
+ gnuradio-core
+ gnuradio-fft
+ volk
+ ${Boost_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)
+
+
+########################################################################
+# QA C++ Code for gr-filter
+########################################################################
+if(ENABLE_TESTING)
+ include(GrTest)
+
+ include_directories(${CPPUNIT_INCLUDE_DIRS})
+ link_directories(${CPPUNIT_LIBRARY_DIRS})
+
+ 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
+ )
+
+ add_executable(test-gr-filter ${test_gr_filter_sources})
+
+ list(APPEND GR_TEST_TARGET_DEPS test-gr-filter gnuradio-filter gnuradio-fft)
+
+ target_link_libraries(
+ test-gr-filter
+ gnuradio-core
+ gnuradio-filter
+ ${Boost_LIBRARIES}
+ ${CPPUNIT_LIBRARIES}
+ )
+
+ GR_ADD_TEST(test_gr_filter test-gr-filter)
+endif(ENABLE_TESTING)
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..515ef90cd
--- /dev/null
+++ b/gr-filter/lib/adaptive_fir_ccc_impl.cc
@@ -0,0 +1,106 @@
+/* -*- 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 <gr_io_signature.h>
+
+namespace gr {
+ namespace filter {
+
+ adaptive_fir_ccc::sptr adaptive_fir_ccc::make(const char *name, int decimation,
+ const std::vector<gr_complex> &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<gr_complex> &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::fir_filter_ccc(decimation, taps),
+ d_updated(false)
+ {
+ set_history(d_ntaps);
+ }
+
+ void
+ adaptive_fir_ccc_impl::set_taps(const std::vector<gr_complex> &taps)
+ {
+ d_new_taps = taps;
+ d_updated = true;
+ }
+
+ std::vector<gr_complex>
+ adaptive_fir_ccc_impl::taps() const
+ {
+ return kernel::fir_filter_ccc::taps();
+ }
+
+ 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::fir_filter_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.
+ if (decimation() == 1) {
+ filterN(out, in, noutput_items);
+ }
+ else {
+ filterNdec(out, in, noutput_items,
+ decimation());
+ }
+
+ 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..fd6274a1d
--- /dev/null
+++ b/gr-filter/lib/adaptive_fir_ccc_impl.h
@@ -0,0 +1,61 @@
+/* -*- 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 <filter/adaptive_fir_ccc.h>
+#include <filter/fir_filter.h>
+#include <gr_types.h>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API adaptive_fir_ccc_impl : public adaptive_fir_ccc, public kernel::fir_filter_ccc
+ {
+ private:
+ std::vector<gr_complex> d_new_taps;
+ bool d_updated;
+
+ protected:
+ // 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<gr_complex> &taps);
+ std::vector<gr_complex> taps() const;
+
+ adaptive_fir_ccc_impl(const char *name, int decimation,
+ const std::vector<gr_complex> &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 */
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..004a9286b
--- /dev/null
+++ b/gr-filter/lib/adaptive_fir_ccf_impl.cc
@@ -0,0 +1,106 @@
+/* -*- 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 <gr_io_signature.h>
+
+namespace gr {
+ namespace filter {
+
+ adaptive_fir_ccf::sptr adaptive_fir_ccf::make(const char *name, int decimation,
+ const std::vector<float> &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<float> &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::fir_filter_ccf(decimation, taps),
+ d_updated(false)
+ {
+ set_history(d_ntaps);
+ }
+
+ void
+ adaptive_fir_ccf_impl::set_taps(const std::vector<float> &taps)
+ {
+ d_new_taps = taps;
+ d_updated = true;
+ }
+
+ std::vector<float>
+ adaptive_fir_ccf_impl::taps()
+ {
+ return kernel::fir_filter_ccf::taps();
+ }
+
+ float
+ adaptive_fir_ccf_impl::error(const gr_complex &out)
+ {
+ return 0;
+ }
+
+ void
+ adaptive_fir_ccf_impl::update_tap(float &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::fir_filter_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.
+ if (decimation() == 1) {
+ filterN(out, in, noutput_items);
+ }
+ else {
+ filterNdec(out, in, noutput_items,
+ decimation());
+ }
+
+ 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..a0c9581ea
--- /dev/null
+++ b/gr-filter/lib/adaptive_fir_ccf_impl.h
@@ -0,0 +1,61 @@
+/* -*- 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 <filter/adaptive_fir_ccf.h>
+#include <filter/fir_filter.h>
+#include <gr_types.h>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API adaptive_fir_ccf_impl : public adaptive_fir_ccf, public kernel::fir_filter_ccf
+ {
+ private:
+ std::vector<float> d_new_taps;
+ bool d_updated;
+
+ protected:
+ // 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(float &tap, const gr_complex &in);
+
+ public:
+ void set_taps(const std::vector<float> &taps);
+ std::vector<float> taps();
+
+ adaptive_fir_ccf_impl(const char *name, int decimation,
+ const std::vector<float> &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 */
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 <gr_io_signature.h>
+#include <iostream>
+
+namespace gr {
+ namespace filter {
+
+ channel_model::sptr
+ channel_model::make(double noise_voltage,
+ double frequency_offset,
+ double epsilon,
+ const std::vector<gr_complex> &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<gr_complex> &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<gr_complex> &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<gr_complex>
+ 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 <gr_top_block.h>
+#include <gr_sig_source_c.h>
+#include <gr_add_cc.h>
+#include <gr_multiply_cc.h>
+#include <gr_noise_source_c.h>
+#include <filter/channel_model.h>
+#include <filter/fractional_interpolator_cc.h>
+#include <filter/fir_filter_ccc.h>
+
+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<gr_complex> d_taps;
+
+ public:
+ channel_model_impl(double noise_voltage,
+ double frequency_offset,
+ double epsilon,
+ const std::vector<gr_complex> &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<gr_complex> &taps);
+ void set_timing_offset(double epsilon);
+
+ double noise_voltage() const;
+ double frequency_offset() const;
+ std::vector<gr_complex> taps() const;
+ double timing_offset() const;
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* INCLUDED_FILTER_CHANNEL_MODEL_IMPL_H */
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..663ba94f1
--- /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 <gr_io_signature.h>
+#include <cstdio>
+
+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<gr_complex>(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<gr_complex>(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::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..6f8bc16c7
--- /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 <filter/dc_blocker_cc.h>
+#include <deque>
+
+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<gr_complex> 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<gr_complex> d_delay_line;
+
+ public:
+ dc_blocker_cc_impl(int D, bool long_form);
+
+ ~dc_blocker_cc_impl();
+
+ int 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..22822d147
--- /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 <gr_io_signature.h>
+#include <cstdio>
+
+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<float>(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<float>(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::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..5ae60e2e4
--- /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 <filter/dc_blocker_ff.h>
+#include <deque>
+
+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<float> 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<float> d_delay_line;
+
+ public:
+ dc_blocker_ff_impl(int D, bool long_form);
+
+ ~dc_blocker_ff_impl();
+
+ int 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/lib/fft_filter.cc b/gr-filter/lib/fft_filter.cc
new file mode 100644
index 000000000..1b256a3da
--- /dev/null
+++ b/gr-filter/lib/fft_filter.cc
@@ -0,0 +1,316 @@
+/* -*- 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 <filter/fft_filter.h>
+#include <volk/volk.h>
+#include <iostream>
+#include <cstring>
+
+namespace gr {
+ namespace filter {
+ namespace kernel {
+
+ #define VERBOSE 0
+
+ fft_filter_fff::fft_filter_fff(int decimation,
+ const std::vector<float> &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<float> &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<gr_complex> &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<gr_complex> &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 kernel */
+ } /* 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..0a2002991
--- /dev/null
+++ b/gr-filter/lib/fft_filter_ccc_impl.cc
@@ -0,0 +1,118 @@
+/* -*- 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 <gr_io_signature.h>
+
+#include <math.h>
+#include <assert.h>
+#include <stdexcept>
+
+namespace gr {
+ namespace filter {
+
+ fft_filter_ccc::sptr fft_filter_ccc::make(int decimation,
+ const std::vector<gr_complex> &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<gr_complex> &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<gr_complex> &taps)
+ {
+ d_new_taps = taps;
+ d_updated = true;
+ }
+
+ std::vector<gr_complex>
+ 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..82be00915
--- /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 <filter/api.h>
+#include <filter/fft_filter.h>
+#include <filter/fft_filter_ccc.h>
+
+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<gr_complex> d_new_taps;
+
+ public:
+ fft_filter_ccc_impl(int decimation,
+ const std::vector<gr_complex> &taps,
+ int nthreads=1);
+
+ ~fft_filter_ccc_impl();
+
+ void set_taps(const std::vector<gr_complex> &taps);
+ std::vector<gr_complex> 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..1d6eb02db
--- /dev/null
+++ b/gr-filter/lib/fft_filter_fff_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_fff_impl.h"
+#include <gr_io_signature.h>
+
+#include <math.h>
+#include <assert.h>
+#include <stdexcept>
+
+namespace gr {
+ namespace filter {
+
+ fft_filter_fff::sptr fft_filter_fff::make(int decimation,
+ const std::vector<float> &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<float> &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<float> &taps)
+ {
+ d_new_taps = taps;
+ d_updated = true;
+ }
+
+ std::vector<float>
+ 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
new file mode 100644
index 000000000..df35d3df7
--- /dev/null
+++ b/gr-filter/lib/fft_filter_fff_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_FFF_IMPL_H
+#define INCLUDED_FILTER_FFT_FILTER_FFF_IMPL_H
+
+#include <filter/api.h>
+#include <filter/fft_filter.h>
+#include <filter/fft_filter_fff.h>
+
+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<float> d_new_taps;
+
+ public:
+ fft_filter_fff_impl(int decimation,
+ const std::vector<float> &taps,
+ int nthreads=1);
+
+ ~fft_filter_fff_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ std::vector<float> 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/filter_delay_fc_impl.cc b/gr-filter/lib/filter_delay_fc_impl.cc
new file mode 100644
index 000000000..3bb550864
--- /dev/null
+++ b/gr-filter/lib/filter_delay_fc_impl.cc
@@ -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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "filter_delay_fc_impl.h"
+#include <volk/volk.h>
+
+namespace gr {
+ namespace filter {
+
+ filter_delay_fc::sptr filter_delay_fc::make(const std::vector<float> &taps)
+ {
+ return gnuradio::get_initial_sptr(new filter_delay_fc_impl(taps));
+ }
+
+ filter_delay_fc_impl::filter_delay_fc_impl(const std::vector<float> &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());
+
+ const int alignment_multiple =
+ volk_get_alignment() / sizeof(float);
+ set_alignment(std::max(1, alignment_multiple));
+ }
+
+ filter_delay_fc_impl::~filter_delay_fc_impl()
+ {
+ delete d_fir;
+ }
+
+ std::vector<float>
+ filter_delay_fc_impl::taps()
+ {
+ return d_fir->taps();
+ }
+
+ void
+ filter_delay_fc_impl::set_taps(const std::vector<float> &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 <filter/filter_delay_fc.h>
+#include <filter/fir_filter.h>
+#include <gr_io_signature.h>
+
+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<float> d_taps;
+ bool d_update;
+
+ public:
+ filter_delay_fc_impl(const std::vector<float> &taps);
+ ~filter_delay_fc_impl();
+
+ std::vector<float> taps();
+ void set_taps(const std::vector<float> &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/lib/fir_filter.cc b/gr-filter/lib/fir_filter.cc
new file mode 100644
index 000000000..d5cf3cd54
--- /dev/null
+++ b/gr-filter/lib/fir_filter.cc
@@ -0,0 +1,667 @@
+/* -*- 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 <filter/fir_filter.h>
+#include <fft/fft.h>
+#include <volk/volk.h>
+#include <cstdio>
+#include <cstring>
+
+namespace gr {
+ namespace filter {
+ namespace kernel {
+
+ fir_filter_fff::fir_filter_fff(int decimation,
+ const std::vector<float> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(float));
+
+ d_aligned_taps = NULL;
+ set_taps(taps);
+
+ // Make sure the output sample is always aligned, too.
+ d_output = fft::malloc_float(1);
+ }
+
+ fir_filter_fff::~fir_filter_fff()
+ {
+ // Free all aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_fff::set_taps(const std::vector<float> &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]);
+ }
+ ::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 = (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];
+ }
+ }
+
+ std::vector<float>
+ fir_filter_fff::taps() const
+ {
+ std::vector<float> t = d_taps;
+ std::reverse(t.begin(), t.end());
+ return t;
+ }
+
+ unsigned int
+ fir_filter_fff::ntaps() const
+ {
+ return d_ntaps;
+ }
+
+ float
+ fir_filter_fff::filter(const float input[])
+ {
+ 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);
+ 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[],
+ 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<float> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(gr_complex));
+
+ d_aligned_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()
+ {
+ // Free all aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_ccf::set_taps(const std::vector<float> &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]);
+ }
+ ::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 = (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];
+ }
+ }
+
+ std::vector<float>
+ fir_filter_ccf::taps() const
+ {
+ std::vector<float> t = d_taps;
+ std::reverse(t.begin(), t.end());
+ return t;
+ }
+
+ unsigned int
+ fir_filter_ccf::ntaps() const
+ {
+ return d_ntaps;
+ }
+
+ gr_complex
+ fir_filter_ccf::filter(const gr_complex input[])
+ {
+ 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
+ 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_fcc::fir_filter_fcc(int decimation,
+ const std::vector<gr_complex> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, 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]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_fcc::set_taps(const std::vector<gr_complex> &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]);
+ }
+ ::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<gr_complex>
+ fir_filter_fcc::taps() const
+ {
+ std::vector<gr_complex> 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,
+ const std::vector<gr_complex> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(gr_complex));
+
+ d_aligned_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 all aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_ccc::set_taps(const std::vector<gr_complex> &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]);
+ }
+ ::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<gr_complex>
+ fir_filter_ccc::taps() const
+ {
+ std::vector<gr_complex> t = d_taps;
+ std::reverse(t.begin(), t.end());
+ return t;
+ }
+
+ unsigned int
+ fir_filter_ccc::ntaps() const
+ {
+ return d_ntaps;
+ }
+
+ gr_complex
+ fir_filter_ccc::filter(const gr_complex input[])
+ {
+ 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));
+ return *d_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;
+ }
+ }
+
+ /**************************************************************/
+
+ fir_filter_scc::fir_filter_scc(int decimation,
+ const std::vector<gr_complex> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(short));
+
+ d_aligned_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 all aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_scc::set_taps(const std::vector<gr_complex> &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]);
+ }
+ ::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<gr_complex>
+ fir_filter_scc::taps() const
+ {
+ std::vector<gr_complex> t = d_taps;
+ std::reverse(t.begin(), t.end());
+ 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<float> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(float));
+
+ d_aligned_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 all aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_fsf::set_taps(const std::vector<float> &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]);
+ }
+ ::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 = (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];
+ }
+ }
+
+ std::vector<float>
+ fir_filter_fsf::taps() const
+ {
+ std::vector<float> t = d_taps;
+ std::reverse(t.begin(), t.end());
+ 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));
+
+ 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/lib/fir_filter_XXX_impl.cc.t b/gr-filter/lib/fir_filter_XXX_impl.cc.t
new file mode 100644
index 000000000..319c26727
--- /dev/null
+++ b/gr-filter/lib/fir_filter_XXX_impl.cc.t
@@ -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.
+ */
+
+/* @WARNING@ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "@IMPL_NAME@.h"
+#include <gr_io_signature.h>
+#include <volk/volk.h>
+
+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("@BASE_NAME@",
+ gr_make_io_signature(1, 1, sizeof(@I_TYPE@)),
+ gr_make_io_signature(1, 1, sizeof(@O_TYPE@)),
+ decimation)
+ {
+ 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@()
+ {
+ delete d_fir;
+ }
+
+ void
+ @IMPL_NAME@::set_taps(const std::vector<@TAP_TYPE@> &taps)
+ {
+ gruel::scoped_lock l(d_setlock);
+ 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)
+ {
+ gruel::scoped_lock l(d_setlock);
+
+ 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());
+ 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..ef0a7adfe
--- /dev/null
+++ b/gr-filter/lib/fir_filter_XXX_impl.h.t
@@ -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.
+ */
+
+/* @WARNING@ */
+
+#ifndef @GUARD_NAME@
+#define @GUARD_NAME@
+
+#include <filter/fir_filter.h>
+#include <filter/@BASE_NAME@.h>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API @IMPL_NAME@ : public @BASE_NAME@
+ {
+ private:
+ kernel::@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_with_buffer.cc b/gr-filter/lib/fir_filter_with_buffer.cc
new file mode 100644
index 000000000..7c37361c1
--- /dev/null
+++ b/gr-filter/lib/fir_filter_with_buffer.cc
@@ -0,0 +1,496 @@
+/* -*- 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 <filter/fir_filter_with_buffer.h>
+#include <fft/fft.h>
+#include <volk/volk.h>
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+namespace gr {
+ namespace filter {
+ namespace kernel {
+
+ fir_filter_with_buffer_fff::fir_filter_with_buffer_fff(const std::vector<float> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(float));
+
+ d_buffer_ptr = 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_ptr != NULL) {
+ fft::free(d_buffer_ptr);
+ d_buffer_ptr = NULL;
+ }
+
+ // Free aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_with_buffer_fff::set_taps(const std::vector<float> &taps)
+ {
+ if(d_buffer_ptr != NULL) {
+ fft::free(d_buffer_ptr);
+ d_buffer_ptr = NULL;
+ }
+
+ // 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]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ d_ntaps = (int)taps.size();
+ d_taps = taps;
+ std::reverse(d_taps.begin(), d_taps.end());
+
+ // We allocate enough to be able to look back and forth
+ // d_naligned beyond the buffer boundaries and make sure these
+ // are zeroed out (or they may be nan, which will cause
+ // problems). We then set d_buffer to the position in the
+ // d_buffer_ptr such that we only touch the internally
+ // allocated space.
+ d_buffer_ptr = fft::malloc_float(2*(d_ntaps + d_naligned));
+ memset(d_buffer_ptr, 0, 2*(d_ntaps + d_naligned)*sizeof(float));
+ d_buffer = d_buffer_ptr + d_naligned;
+
+ // Allocate aligned taps
+ 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;
+ }
+
+ std::vector<float>
+ fir_filter_with_buffer_fff::taps() const
+ {
+ std::vector<float> t = d_taps;
+ std::reverse(t.begin(), t.end());
+ 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;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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<gr_complex> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(gr_complex));
+
+ d_buffer_ptr = 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_ptr != NULL) {
+ fft::free(d_buffer_ptr);
+ d_buffer_ptr = NULL;
+ }
+
+ // Free aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ // Free output sample
+ fft::free(d_output);
+ }
+
+ void
+ fir_filter_with_buffer_ccc::set_taps(const std::vector<gr_complex> &taps)
+ {
+ if(d_buffer_ptr != NULL) {
+ fft::free(d_buffer_ptr);
+ d_buffer_ptr = NULL;
+ }
+
+ // 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]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ d_ntaps = (int)taps.size();
+ d_taps = taps;
+ std::reverse(d_taps.begin(), d_taps.end());
+
+ // We allocate enough to be able to look back and forth
+ // d_naligned beyond the buffer boundaries and make sure these
+ // are zeroed out (or they may be nan, which will cause
+ // problems). We then set d_buffer to the position in the
+ // d_buffer_ptr such that we only touch the internally
+ // allocated space.
+ d_buffer_ptr = fft::malloc_complex(2*(d_ntaps + d_naligned));
+ memset(d_buffer_ptr, 0, 2*(d_ntaps + d_naligned)*sizeof(gr_complex));
+ d_buffer = d_buffer_ptr + d_naligned;
+
+ // Allocate aligned taps
+ 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;
+ }
+
+ std::vector<gr_complex>
+ fir_filter_with_buffer_ccc::taps() const
+ {
+ std::vector<gr_complex> t = d_taps;
+ std::reverse(t.begin(), t.end());
+ 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;
+
+ 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));
+ return *d_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;
+ }
+
+ 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));
+ return *d_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;
+ }
+ }
+
+
+ /**************************************************************/
+
+
+ fir_filter_with_buffer_ccf::fir_filter_with_buffer_ccf(const std::vector<float> &taps)
+ {
+ d_align = volk_get_alignment();
+ d_naligned = std::max((size_t)1, d_align / sizeof(gr_complex));
+
+ d_buffer_ptr = 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_ptr != NULL) {
+ fft::free(d_buffer_ptr);
+ d_buffer_ptr = NULL;
+ }
+
+ // Free aligned taps
+ if(d_aligned_taps != NULL) {
+ for(int i = 0; i < d_naligned; i++) {
+ fft::free(d_aligned_taps[i]);
+ }
+ ::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<float> &taps)
+ {
+ if(d_buffer_ptr != NULL) {
+ fft::free(d_buffer_ptr);
+ d_buffer_ptr = NULL;
+ }
+
+ // 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]);
+ }
+ ::free(d_aligned_taps);
+ d_aligned_taps = NULL;
+ }
+
+ d_ntaps = (int)taps.size();
+ d_taps = taps;
+ std::reverse(d_taps.begin(), d_taps.end());
+
+ // We allocate enough to be able to look back and forth
+ // d_naligned beyond the buffer boundaries and make sure these
+ // are zeroed out (or they may be nan, which will cause
+ // problems). We then set d_buffer to the position in the
+ // d_buffer_ptr such that we only touch the internally
+ // allocated space.
+ d_buffer_ptr = fft::malloc_complex(2*(d_ntaps + d_naligned));
+ memset(d_buffer_ptr, 0, 2*(d_ntaps + d_naligned)*sizeof(gr_complex));
+ d_buffer = d_buffer_ptr + d_naligned;
+
+ // Allocate aligned taps
+ 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;
+ }
+
+ std::vector<float>
+ fir_filter_with_buffer_ccf::taps() const
+ {
+ std::vector<float> 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;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 */
+} /* namespace gr */
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 <config.h>
+#endif
+
+#include <filter/firdes.h>
+#include <stdexcept>
+
+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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<gr_complex>
+ 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<gr_complex> taps(ntaps);
+ vector<float> lptaps(ntaps);
+ vector<float> 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<gr_complex>
+ 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<gr_complex> taps(ntaps);
+ vector<float> lptaps(ntaps);
+ vector<float> 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<lptaps.size();i++) {
+ *optr++ = gr_complex(*iptr * cos(phase), *iptr * sin(phase));
+ iptr++, phase += freq;
+ }
+
+ return taps;
+ }
+
+ //
+ // === Band Reject ===
+ //
+
+ vector<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ 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<float> taps(ntaps);
+ vector<float> 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<float>
+ firdes::gaussian(double gain,
+ double spb,
+ double bt,
+ int ntaps)
+ {
+ vector<float> 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<ntaps;i++) {
+ t0++;
+ ts = s*dt*t0;
+ taps[i] = exp(-0.5*ts*ts);
+ scale += taps[i];
+ }
+ for(int i=0;i<ntaps;i++)
+ taps[i] = taps[i] / scale * gain;
+
+ return taps;
+ }
+
+
+ //
+ // Root Raised Cosine
+ //
+
+ vector<float>
+ 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<float> 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<float>
+ firdes::window (win_type type, int ntaps, double beta)
+ {
+ vector<float> 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<ntaps; i++) {
+ temp = i * inm1;
+ //fprintf(stderr, "temp = %g\n", temp);
+ taps[i] = Izero(beta*sqrt(1.0-temp*temp)) * IBeta;
+ //fprintf(stderr, "taps[%d] = %g\n", i, taps[i]);
+ }
+ }
+ break;
+
+ default:
+ throw std::out_of_range("firdes:window: type out of range");
+ }
+
+ return taps;
+ }
+
+ void
+ firdes::sanity_check_1f(double sampling_freq,
+ double fa, // 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(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/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 <gr_io_signature.h>
+#include "fractional_interpolator_cc_impl.h"
+#include <stdexcept>
+
+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 <filter/fractional_interpolator_cc.h>
+#include <filter/mmse_fir_interpolator_cc.h>
+
+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 <gr_io_signature.h>
+#include "fractional_interpolator_ff_impl.h"
+#include <stdexcept>
+
+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 <filter/fractional_interpolator_ff.h>
+#include <filter/mmse_fir_interpolator_ff.h>
+
+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/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 <gr_io_signature.h>
+#include <volk/volk.h>
+
+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<gr_complex> 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<gr_complex> 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 <filter/api.h>
+#include <filter/fir_filter.h>
+#include <filter/@BASE_NAME@.h>
+
+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/lib/hilbert_fc_impl.cc b/gr-filter/lib/hilbert_fc_impl.cc
new file mode 100644
index 000000000..52b2dabae
--- /dev/null
+++ b/gr-filter/lib/hilbert_fc_impl.cc
@@ -0,0 +1,76 @@
+/* -*- 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 <filter/firdes.h>
+#include <gr_io_signature.h>
+#include <volk/volk.h>
+
+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);
+
+ const int alignment_multiple =
+ volk_get_alignment() / sizeof(float);
+ set_alignment(std::max(1, alignment_multiple));
+ }
+
+ 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..d2b41b573
--- /dev/null
+++ b/gr-filter/lib/hilbert_fc_impl.h
@@ -0,0 +1,52 @@
+/* -*- 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 <filter/hilbert_fc.h>
+#include <filter/fir_filter.h>
+#include <gr_types.h>
+
+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/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 <gr_io_signature.h>
+
+namespace gr {
+ namespace filter {
+
+ iir_filter_ffd::sptr
+ iir_filter_ffd::make(const std::vector<double> &fftaps,
+ const std::vector<double> &fbtaps)
+ {
+ return gnuradio::get_initial_sptr(new iir_filter_ffd_impl(fftaps, fbtaps));
+ }
+
+ iir_filter_ffd_impl::iir_filter_ffd_impl(const std::vector<double> &fftaps,
+ const std::vector<double> &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<float,float,double>(fftaps, fbtaps);
+ //d_iir = new gri_iir<float,float,double>(fftaps, fbtaps);
+ }
+
+ iir_filter_ffd_impl::~iir_filter_ffd_impl()
+ {
+ delete d_iir;
+ }
+
+ void
+ iir_filter_ffd_impl::set_taps(const std::vector<double> &fftaps,
+ const std::vector<double> &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 <filter/iir_filter.h>
+#include <filter/iir_filter_ffd.h>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API iir_filter_ffd_impl : public iir_filter_ffd
+ {
+ private:
+ bool d_updated;
+ kernel::iir_filter<float,float,double> *d_iir;
+ std::vector<double> d_new_fftaps;
+ std::vector<double> d_new_fbtaps;
+
+ public:
+ iir_filter_ffd_impl(const std::vector<double> &fftaps,
+ const std::vector<double> &fbtaps);
+ ~iir_filter_ffd_impl();
+
+ void set_taps(const std::vector<double> &fftaps,
+ const std::vector<double> &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/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..fea3b1c8e
--- /dev/null
+++ b/gr-filter/lib/interp_fir_filter_XXX_impl.cc.t
@@ -0,0 +1,155 @@
+/* -*- 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 <gr_io_signature.h>
+#include <volk/volk.h>
+#include <stdexcept>
+
+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");
+ }
+
+ if(taps.size() == 0) {
+ throw std::runtime_error("@IMPL_NAME@: no filter taps provided.\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 <filter/api.h>
+#include <filter/fir_filter.h>
+#include <filter/@BASE_NAME@.h>
+#include <vector>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API @IMPL_NAME@ : public @BASE_NAME@
+ {
+ private:
+ bool d_updated;
+ std::vector<kernel::@FIR_TYPE@ *> 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/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 <config.h>
+#endif
+
+#include <filter/mmse_fir_interpolator_cc.h>
+#include <filter/interpolator_taps.h>
+#include <stdexcept>
+
+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<float> 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 <config.h>
+#endif
+
+#include <filter/mmse_fir_interpolator_ff.h>
+#include <filter/interpolator_taps.h>
+#include <stdexcept>
+
+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<float> 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/pfb_arb_resampler_ccf_impl.cc b/gr-filter/lib/pfb_arb_resampler_ccf_impl.cc
new file mode 100644
index 000000000..5480366de
--- /dev/null
+++ b/gr-filter/lib/pfb_arb_resampler_ccf_impl.cc
@@ -0,0 +1,258 @@
+/* -*- 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 <gr_io_signature.h>
+#include <cstdio>
+
+namespace gr {
+ namespace filter {
+
+ pfb_arb_resampler_ccf::sptr
+ pfb_arb_resampler_ccf::make(float rate,
+ const std::vector<float> &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<float> &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<kernel::fir_filter_ccf*>(d_int_rate);
+ d_diff_filters = std::vector<kernel::fir_filter_ccf*>(d_int_rate);
+
+ // Create an FIR filter for each channel and zero out the taps
+ std::vector<float> 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
+ set_taps(taps);
+ d_updated = false;
+ }
+
+ 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<float> &newtaps,
+ std::vector< std::vector<float> > &ourtaps,
+ std::vector<kernel::fir_filter_ccf*> &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<float> 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<float>(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]);
+ }
+ }
+
+ void
+ pfb_arb_resampler_ccf_impl::create_diff_taps(const std::vector<float> &newtaps,
+ std::vector<float> &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<float> &taps)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ std::vector<float> dtaps;
+ create_diff_taps(taps, dtaps);
+ create_taps(taps, d_taps, d_filters);
+ create_taps(dtaps, d_dtaps, d_diff_filters);
+ set_history(d_taps_per_filter + 1);
+ d_updated = true;
+ }
+
+ std::vector<std::vector<float> >
+ 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)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ d_dec_rate = (unsigned int)floor(d_int_rate/rate);
+ d_flt_rate = (d_int_rate/rate) - d_dec_rate;
+ set_relative_rate(rate);
+ }
+
+ void
+ pfb_arb_resampler_ccf_impl::set_phase(float ph)
+ {
+ gruel::scoped_lock guard(d_mutex);
+ if((ph < 0) || (ph >= 2.0*M_PI)) {
+ throw std::runtime_error("pfb_arb_resampler_ccf: set_phase value out of bounds [0, 2pi).\n");
+ }
+
+ float ph_diff = 2.0*M_PI / (float)d_filters.size();
+ d_last_filter = static_cast<int>(ph / ph_diff);
+ }
+
+ float
+ pfb_arb_resampler_ccf_impl::phase() const
+ {
+ float ph_diff = 2.0*M_PI / static_cast<float>(d_filters.size());
+ return d_last_filter * ph_diff;
+ }
+
+ 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)
+ {
+ 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.
+ }
+
+ 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..891e601e0
--- /dev/null
+++ b/gr-filter/lib/pfb_arb_resampler_ccf_impl.h
@@ -0,0 +1,89 @@
+/* -*- 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 <filter/pfb_arb_resampler_ccf.h>
+#include <filter/fir_filter.h>
+#include <gruel/thread.h>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API pfb_arb_resampler_ccf_impl : public pfb_arb_resampler_ccf
+ {
+ private:
+ std::vector<kernel::fir_filter_ccf*> d_filters;
+ std::vector<kernel::fir_filter_ccf*> d_diff_filters;
+ std::vector< std::vector<float> > d_taps;
+ std::vector< std::vector<float> > 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<float> &newtaps,
+ std::vector<float> &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<float> &newtaps,
+ std::vector< std::vector<float> > &ourtaps,
+ std::vector<kernel::fir_filter_ccf*> &ourfilter);
+
+ public:
+ pfb_arb_resampler_ccf_impl(float rate,
+ const std::vector<float> &taps,
+ unsigned int filter_size);
+
+ ~pfb_arb_resampler_ccf_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ std::vector<std::vector<float> > taps() const;
+ void print_taps();
+ void set_rate(float rate);
+
+ void set_phase(float ph);
+ float phase() 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 /* 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..6aff374fd
--- /dev/null
+++ b/gr-filter/lib/pfb_arb_resampler_fff_impl.cc
@@ -0,0 +1,258 @@
+/* -*- 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 <gr_io_signature.h>
+#include <cstdio>
+
+namespace gr {
+ namespace filter {
+
+ pfb_arb_resampler_fff::sptr
+ pfb_arb_resampler_fff::make(float rate,
+ const std::vector<float> &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<float> &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<kernel::fir_filter_fff*>(d_int_rate);
+ d_diff_filters = std::vector<kernel::fir_filter_fff*>(d_int_rate);
+
+ // Create an FIR filter for each channel and zero out the taps
+ std::vector<float> 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<float> 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<float> &newtaps,
+ std::vector< std::vector<float> > &ourtaps,
+ std::vector<kernel::fir_filter_fff*> &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<float> 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<float>(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<float> &newtaps,
+ std::vector<float> &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<float> &taps)
+ {
+ gruel::scoped_lock guard(d_mutex);
+ }
+
+ std::vector<std::vector<float> >
+ 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)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ d_dec_rate = (unsigned int)floor(d_int_rate/rate);
+ d_flt_rate = (d_int_rate/rate) - d_dec_rate;
+ set_relative_rate(rate);
+ }
+
+ void
+ pfb_arb_resampler_fff_impl::set_phase(float ph)
+ {
+ gruel::scoped_lock guard(d_mutex);
+ if((ph < 0) || (ph >= 2.0*M_PI)) {
+ throw std::runtime_error("pfb_arb_resampler_ccf: set_phase value out of bounds [0, 2pi).\n");
+ }
+
+ float ph_diff = 2.0*M_PI / (float)d_filters.size();
+ d_last_filter = static_cast<int>(ph / ph_diff);
+ }
+
+ float
+ pfb_arb_resampler_fff_impl::phase() const
+ {
+ float ph_diff = 2.0*M_PI / static_cast<float>(d_filters.size());
+ return d_last_filter * ph_diff;
+ }
+
+ 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..588962711
--- /dev/null
+++ b/gr-filter/lib/pfb_arb_resampler_fff_impl.h
@@ -0,0 +1,88 @@
+/* -*- 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 <filter/pfb_arb_resampler_fff.h>
+#include <filter/fir_filter.h>
+#include <gruel/thread.h>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API pfb_arb_resampler_fff_impl : public pfb_arb_resampler_fff
+ {
+ private:
+ std::vector<kernel::fir_filter_fff*> d_filters;
+ std::vector<kernel::fir_filter_fff*> d_diff_filters;
+ std::vector< std::vector<float> > d_taps;
+ std::vector< std::vector<float> > 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<float> &newtaps,
+ std::vector<float> &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<float> &newtaps,
+ std::vector< std::vector<float> > &ourtaps,
+ std::vector<kernel::fir_filter_fff*> &ourfilter);
+ public:
+ pfb_arb_resampler_fff_impl(float rate,
+ const std::vector<float> &taps,
+ unsigned int filter_size);
+
+ ~pfb_arb_resampler_fff_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ std::vector<std::vector<float> > taps() const;
+ void print_taps();
+ void set_rate(float rate);
+
+ void set_phase(float ph);
+ float phase() 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 /* INCLUDED_PFB_ARB_RESAMPLER_FFF_IMPL_H */
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..28fc1dcc1
--- /dev/null
+++ b/gr-filter/lib/pfb_channelizer_ccf_impl.cc
@@ -0,0 +1,191 @@
+/* -*- 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 <gr_io_signature.h>
+
+namespace gr {
+ namespace filter {
+
+ pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int nfilts,
+ const std::vector<float> &taps,
+ float oversample_rate)
+ {
+ return gnuradio::get_initial_sptr(new pfb_channelizer_ccf_impl(nfilts, taps,
+ oversample_rate));
+ }
+
+ pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int nfilts,
+ const std::vector<float> &taps,
+ float oversample_rate)
+ : gr_block("pfb_channelizer_ccf",
+ 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
+ // 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(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);
+
+ // Default channel map
+ d_channel_map.resize(d_nfilts);
+ for(unsigned int i = 0; i < d_nfilts; i++) {
+ d_channel_map[i] = i;
+ }
+
+ // 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_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_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;
+ }
+
+ void
+ pfb_channelizer_ccf_impl::set_taps(const std::vector<float> &taps)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ polyphase_filterbank::set_taps(taps);
+ set_history(d_taps_per_filter+1);
+ d_updated = true;
+ }
+
+ void
+ pfb_channelizer_ccf_impl::print_taps()
+ {
+ polyphase_filterbank::print_taps();
+ }
+
+ std::vector<std::vector<float> >
+ pfb_channelizer_ccf_impl::taps() const
+ {
+ return polyphase_filterbank::taps();
+ }
+
+ void
+ pfb_channelizer_ccf_impl::set_channel_map(const std::vector<int> &map)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ if(map.size() > 0) {
+ unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end());
+ if(max >= d_nfilts) {
+ throw std::invalid_argument("pfb_channelizer_ccf_impl::set_channel_map: map range out of bounds.\n");
+ }
+ d_channel_map = map;
+ }
+ }
+
+ std::vector<int>
+ 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_nfilts;
+ 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_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]);
+ j++;
+ i--;
+ }
+
+ n += (i+d_rate_ratio) >= (int)d_nfilts;
+
+ // 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..16b112b9a
--- /dev/null
+++ b/gr-filter/lib/pfb_channelizer_ccf_impl.h
@@ -0,0 +1,69 @@
+/* -*- 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 <filter/pfb_channelizer_ccf.h>
+#include <filter/polyphase_filterbank.h>
+#include <filter/fir_filter.h>
+#include <fft/fft.h>
+#include <gruel/thread.h>
+
+namespace gr {
+ namespace filter {
+
+ class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf, kernel::polyphase_filterbank
+ {
+ private:
+ bool d_updated;
+ float d_oversample_rate;
+ int *d_idxlut;
+ int d_rate_ratio;
+ int d_output_multiple;
+ std::vector<int> d_channel_map;
+ gruel::mutex d_mutex; // mutex to protect set/work access
+
+ public:
+ pfb_channelizer_ccf_impl(unsigned int nfilts,
+ const std::vector<float> &taps,
+ float oversample_rate);
+
+ ~pfb_channelizer_ccf_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ void print_taps();
+ std::vector<std::vector<float> > taps() const;
+
+ void set_channel_map(const std::vector<int> &map);
+ std::vector<int> 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/lib/pfb_decimator_ccf_impl.cc b/gr-filter/lib/pfb_decimator_ccf_impl.cc
new file mode 100644
index 000000000..f9a60cb28
--- /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 <gr_io_signature.h>
+
+namespace gr {
+ namespace filter {
+
+ pfb_decimator_ccf::sptr
+ pfb_decimator_ccf::make(unsigned int decim,
+ const std::vector<float> &taps,
+ unsigned int channel)
+ {
+ return gnuradio::get_initial_sptr
+ (new pfb_decimator_ccf_impl(decim, taps, channel));
+ }
+
+
+ pfb_decimator_ccf_impl::pfb_decimator_ccf_impl(unsigned int decim,
+ const std::vector<float> &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<float> &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<std::vector<float> >
+ 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 <filter/pfb_decimator_ccf.h>
+#include <filter/polyphase_filterbank.h>
+#include <filter/fir_filter.h>
+#include <fft/fft.h>
+#include <gruel/thread.h>
+
+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<float> &taps,
+ unsigned int channel);
+
+ ~pfb_decimator_ccf_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ void print_taps();
+ std::vector<std::vector<float> > 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/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 <gr_io_signature.h>
+
+namespace gr {
+ namespace filter {
+
+ pfb_interpolator_ccf::sptr
+ pfb_interpolator_ccf::make(unsigned int interp,
+ const std::vector<float> &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<float> &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<float> &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<std::vector<float> >
+ 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 <filter/pfb_interpolator_ccf.h>
+#include <filter/polyphase_filterbank.h>
+#include <filter/fir_filter.h>
+#include <fft/fft.h>
+#include <gruel/thread.h>
+
+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<float> &taps);
+
+ ~pfb_interpolator_ccf_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ void print_taps();
+ std::vector<std::vector<float> > 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/lib/pfb_synthesizer_ccf_impl.cc b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc
new file mode 100644
index 000000000..f89b48b12
--- /dev/null
+++ b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc
@@ -0,0 +1,289 @@
+/* -*- 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 <gr_io_signature.h>
+#include <cstdio>
+
+namespace gr {
+ namespace filter {
+
+ pfb_synthesizer_ccf::sptr
+ pfb_synthesizer_ccf::make(unsigned int numchans,
+ const std::vector<float> &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<float> &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)),
+ (twox ? numchans/2 : 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<kernel::fir_filter_with_buffer_ccf*>(d_numchans);
+ d_channel_map.resize(d_numchans);
+
+ // Create a FIR filter for each channel and zero out the taps
+ std::vector<float> vtaps(0, d_numchans);
+ for(unsigned int i = 0; i < 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_numchans, false);
+ memset(d_fft->get_inbuf(), 0, 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_numchans; i++) {
+ delete d_filters[i];
+ }
+ }
+
+ void
+ pfb_synthesizer_ccf_impl::set_taps(const std::vector<float> &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<float> &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<float> 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<float>(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<float> &taps)
+ {
+ unsigned int i,j;
+ int state = 0;
+
+ unsigned int ntaps = 2*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<float> 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
+ unsigned int halfchans = d_numchans/2;
+ for(i = 0; i < halfchans; i++) {
+ // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out
+ d_taps[i] = std::vector<float>(d_taps_per_filter, 0);
+ d_taps[halfchans+i] = std::vector<float>(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*halfchans];
+ d_taps[halfchans + i][j] = 0;
+ state = 1;
+ }
+ else {
+ d_taps[i][j] = 0;
+ d_taps[halfchans + i][j] = tmp_taps[i + j*halfchans];
+ 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[halfchans + i]->set_taps(d_taps[halfchans + i]);
+ }
+ }
+
+ void
+ pfb_synthesizer_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<float> >
+ pfb_synthesizer_ccf_impl::taps() const
+ {
+ return d_taps;
+ }
+
+ void
+ pfb_synthesizer_ccf_impl::set_channel_map(const std::vector<int> &map)
+ {
+ gruel::scoped_lock guard(d_mutex);
+
+ if(map.size() > 0) {
+ unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end());
+ if(max >= d_numchans) {
+ 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_numchans*sizeof(gr_complex));
+ }
+ }
+
+ std::vector<int>
+ 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 {
+ unsigned int halfchans = d_numchans/2;
+ for(n = 0; n < noutput_items/halfchans; 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 < halfchans; i++) {
+ out[i] = d_filters[i]->filter(d_fft->get_outbuf()[d_state*halfchans+i]);
+ out[i] += d_filters[halfchans+i]->filter(d_fft->get_outbuf()[(d_state^1)*halfchans+i]);
+ }
+ d_state ^= 1;
+
+ out += halfchans;
+ }
+ }
+
+ 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 <filter/pfb_synthesizer_ccf.h>
+#include <filter/fir_filter_with_buffer.h>
+#include <fft/fft.h>
+#include <gruel/thread.h>
+
+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<float> > d_taps;
+ int d_state;
+ std::vector<int> 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<float> &taps);
+
+ /*!
+ * \brief Tap setting algorithm for 2x over-sampled channels
+ */
+ void set_taps2(const std::vector<float> &taps);
+
+
+ public:
+ pfb_synthesizer_ccf_impl(unsigned int numchans,
+ const std::vector<float> &taps,
+ bool twox);
+ ~pfb_synthesizer_ccf_impl();
+
+ void set_taps(const std::vector<float> &taps);
+ std::vector<std::vector<float> > taps() const;
+ void print_taps();
+
+ void set_channel_map(const std::vector<int> &map);
+ std::vector<int> 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/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 <filter/pm_remez.h>
+#include <cmath>
+#include <assert.h>
+#include <iostream>
+
+#ifndef LOCAL_BUFFER
+#include <vector>
+#define LOCAL_BUFFER(T, buf, size) \
+ std::vector<T> 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<k; i++) {
+ D[j] = des[2*band] + i*(des[2*band+1]-des[2*band])/(k-1);
+ W[j] = weight[band];
+ Grid[j] = lowf;
+ lowf += delf;
+ j++;
+ }
+ Grid[j-1] = highf;
+ }
+
+ /*
+ * Similar to above, if odd symmetry, last grid point can't be .5
+ * - but, if there are even taps, leave the last grid point at .5
+ */
+ if((symmetry == NEGATIVE) &&
+ (Grid[gridsize-1] > (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]<gridsize);
+ }
+ k--;
+ extra--;
+ }
+
+ for(i = 0; i <= r; i++) {
+ assert(foundExt[i]<gridsize);
+ Ext[i] = foundExt[i]; /* Copy found extremals to Ext[] */
+ }
+
+ free(foundExt);
+ return 0;
+ }
+
+
+ /*********************
+ * freq_sample
+ *============
+ * Simple frequency sampling algorithm to determine the impulse
+ * response h[] from A's found in compute_A
+ *
+ *
+ * INPUT:
+ * ------
+ * int N - Number of filter coefficients
+ * double A[] - Sample points of desired response [N/2]
+ * int symmetry - Symmetry of desired filter
+ *
+ * OUTPUT:
+ * -------
+ * double h[] - Impulse Response of final filter [N]
+ *********************/
+ static void
+ freq_sample(int N, double A[], double h[], int symm)
+ {
+ int n, k;
+ double x, val, M;
+
+ M = (N-1.0)/2.0;
+ if(symm == POSITIVE) {
+ if(N % 2) {
+ for(n = 0; n < N; n++) {
+ val = A[0];
+ x = Pi2 * (n - M)/N;
+ for(k=1; k<=M; k++)
+ val += 2.0 * A[k] * cos(x*k);
+ h[n] = val/N;
+ }
+ }
+ else {
+ for(n = 0; n < N; n++) {
+ val = A[0];
+ x = Pi2 * (n - M)/N;
+ for(k = 1; k <= (N/2-1); k++)
+ val += 2.0 * A[k] * cos(x*k);
+ h[n] = val/N;
+ }
+ }
+ }
+ else {
+ if(N % 2) {
+ for(n = 0; n < N; n++) {
+ val = 0;
+ x = Pi2 * (n - M)/N;
+ for(k = 1; k <= M; k++)
+ val += 2.0 * A[k] * sin(x*k);
+ h[n] = val/N;
+ }
+ }
+ else {
+ for(n = 0; n < N; n++) {
+ val = A[N/2] * sin(Pi * (n - M));
+ x = Pi2 * (n - M)/N;
+ for(k = 1; k <= (N/2-1); k++)
+ val += 2.0 * A[k] * sin(x*k);
+ h[n] = val/N;
+ }
+ }
+ }
+ }
+
+ /*******************
+ * is_done
+ *========
+ * Checks to see if the error function is small enough to consider
+ * the result to have converged.
+ *
+ * INPUT:
+ * ------
+ * int r - 1/2 the number of filter coeffiecients
+ * int Ext[] - Indexes to extremal frequencies [r+1]
+ * double E[] - Error function on the dense grid [gridsize]
+ *
+ * OUTPUT:
+ * -------
+ * Returns 1 if the result converged
+ * Returns 0 if the result has not converged
+ ********************/
+
+ static bool
+ is_done(int r, int Ext[], double E[])
+ {
+ int i;
+ double min, max, current;
+
+ min = max = fabs(E[Ext[0]]);
+ for(i = 1; i <= r; i++) {
+ current = fabs(E[Ext[i]]);
+ if(current < min)
+ min = current;
+ if(current > 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<MAXITERATIONS?0:-1;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // GNU Radio interface
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ static void
+ punt(const std::string msg)
+ {
+ std::cerr << msg << '\n';
+ throw std::runtime_error(msg);
+ }
+
+ std::vector<double>
+ pm_remez(int order,
+ const std::vector<double> &arg_bands,
+ const std::vector<double> &arg_response,
+ const std::vector<double> &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<double>(&coeff[0], &coeff[numtaps]);
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/polyphase_filterbank.cc b/gr-filter/lib/polyphase_filterbank.cc
new file mode 100644
index 000000000..046c23fc0
--- /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 <filter/polyphase_filterbank.h>
+#include <cstdio>
+
+namespace gr {
+ namespace filter {
+ namespace kernel {
+
+ polyphase_filterbank::polyphase_filterbank(unsigned int nfilts,
+ const std::vector<float> &taps)
+ : d_nfilts(nfilts)
+ {
+ d_filters = std::vector<kernel::fir_filter_ccf*>(d_nfilts);
+
+ // Create an FIR filter for each channel and zero out the taps
+ std::vector<float> 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<float> &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<float> 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<float>(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<float> >
+ polyphase_filterbank::taps() const
+ {
+ return d_taps;
+ }
+
+ } /* namespace kernel */
+ } /* 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..b7760b19e
--- /dev/null
+++ b/gr-filter/lib/qa_filter.cc
@@ -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.
+ */
+
+/*
+ * 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 <qa_filter.h>
+#include <qa_firdes.h>
+#include <qa_fir_filter_with_buffer.h>
+#include <qa_mmse_fir_interpolator_cc.h>
+#include <qa_mmse_fir_interpolator_ff.h>
+
+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::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());
+
+ 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 <gruel/attributes.h>
+#include <cppunit/TestSuite.h>
+
+//! 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_fir_filter_with_buffer.cc b/gr-filter/lib/qa_fir_filter_with_buffer.cc
new file mode 100644
index 000000000..ab2958ed1
--- /dev/null
+++ b/gr-filter/lib/qa_fir_filter_with_buffer.cc
@@ -0,0 +1,406 @@
+/* -*- 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 <gr_types.h>
+#include <qa_fir_filter_with_buffer.h>
+#include <filter/fir_filter_with_buffer.h>
+#include <fft/fft.h>
+#include <cppunit/TestAssert.h>
+#include <cmath>
+#include <random.h>
+#include <cstring>
+
+namespace gr {
+ namespace filter {
+
+#define MAX_DATA (16383)
+#define ERR_DELTA (1e-5)
+
+ 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<tap_type> 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, OUTPUT_LEN*sizeof(o_type));
+ 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<tap_type> 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, OUTPUT_LEN*sizeof(o_type));
+ 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<tap_type> 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(OUTPUT_LEN*sizeof(gr_complex)));
+ 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 <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestCase.h>
+
+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_ */
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 <qa_firdes.h>
+#include <filter/firdes.h>
+#include <cppunit/TestAssert.h>
+#include <gr_complex.h>
+#include <string.h>
+#include <iostream>
+#include <iomanip>
+#include <stdio.h>
+
+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<float> &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<float> &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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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 <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestCase.h>
+
+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/qa_mmse_fir_interpolator_cc.cc b/gr-filter/lib/qa_mmse_fir_interpolator_cc.cc
new file mode 100644
index 000000000..268b8801c
--- /dev/null
+++ b/gr-filter/lib/qa_mmse_fir_interpolator_cc.cc
@@ -0,0 +1,126 @@
+/* -*- 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 <config.h>
+#endif
+
+#include <cppunit/TestAssert.h>
+#include <qa_mmse_fir_interpolator_cc.h>
+#include <filter/mmse_fir_interpolator_cc.h>
+#include <fft/fft.h>
+#include <cstdio>
+#include <cmath>
+#include <stdexcept>
+#include <stdint.h>
+
+namespace gr {
+ namespace filter {
+
+ 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_complex *input = fft::malloc_complex(N + 10);
+
+ 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);
+ }
+ }
+ fft::free(input);
+ }
+
+ /*
+ * 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<gr_complex *>(&float_input[1]);
+ else
+ input = reinterpret_cast<gr_complex *>(&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 <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestCase.h>
+
+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..54387fd9b
--- /dev/null
+++ b/gr-filter/lib/qa_mmse_fir_interpolator_ff.cc
@@ -0,0 +1,71 @@
+/* -*- 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 <config.h>
+#endif
+
+#include <cppunit/TestAssert.h>
+#include <qa_mmse_fir_interpolator_ff.h>
+#include <filter/mmse_fir_interpolator_ff.h>
+#include <fft/fft.h>
+#include <cstdio>
+#include <cmath>
+
+namespace gr {
+ namespace filter {
+
+ 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()
+ {
+ // use aligned malloc and make sure that everything in this
+ // buffer is properly initialized.
+ static const unsigned N = 100;
+ float *input = fft::malloc_float(N + 10);
+
+ for(unsigned i = 0; i < N+10; 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);
+ }
+ }
+ fft::free(input);
+ }
+
+ } /* 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 <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestCase.h>
+
+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_ */
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 <gr_io_signature.h>
+#include <volk/volk.h>
+#include <stdexcept>
+
+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 <filter/fir_filter.h>
+#include <filter/@BASE_NAME@.h>
+
+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<kernel::@FIR_TYPE@ *> 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/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 <gr_io_signature.h>
+
+
+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 <filter/single_pole_iir.h>
+#include <filter/single_pole_iir_filter_cc.h>
+#include <gr_sync_block.h>
+#include <gr_complex.h>
+#include <stdexcept>
+
+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<single_pole_iir<gr_complex,gr_complex,double> > 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 <gr_io_signature.h>
+
+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 <filter/single_pole_iir.h>
+#include <filter/single_pole_iir_filter_ff.h>
+#include <gr_sync_block.h>
+#include <stdexcept>
+
+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<single_pole_iir<float,float,double> > 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/lib/test_gr_filter.cc b/gr-filter/lib/test_gr_filter.cc
new file mode 100644
index 000000000..915b6286b
--- /dev/null
+++ b/gr-filter/lib/test_gr_filter.cc
@@ -0,0 +1,43 @@
+/* -*- 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 <cppunit/TextTestRunner.h>
+#include <cppunit/XmlOutputter.h>
+
+#include <gr_unittests.h>
+#include <qa_filter.h>
+#include <iostream>
+
+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;
+}