diff options
Diffstat (limited to 'gr-filter/lib')
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; +} |