diff options
author | mormj <mormjb@gmail.com> | 2020-11-18 10:10:17 -0500 |
---|---|---|
committer | mormj <34754695+mormj@users.noreply.github.com> | 2020-12-17 08:30:02 -0500 |
commit | 436e40867e05274f58cdff778da7d520cd100809 (patch) | |
tree | 17c512979d5c09682c97829b98c08971f83eb7a4 | |
parent | df01f13a0cea8899a49b8f6edbd089741f1135c5 (diff) |
filter: replace rational_resampler_base with rational_resampler
The bulk of the rational resampler code is named rational_resampler_base
and only in Python is the automatic designing of filters if taps are not
specified. This pushes that code into the C++ classes (which get
binded) so rational_resampler.py can go away, removing a layer of
wrapping
19 files changed, 385 insertions, 495 deletions
diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 706a9b49c3..61ef4f11c4 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -29,7 +29,6 @@ install(FILES filter_pfb_interpolator.block.yml filter_pfb_synthesizer.block.yml filter_rational_resampler_xxx.block.yml - filter_rational_resampler_base_xxx.block.yml filter_single_pole_iir_filter_xx.block.yml filter_low_pass_filter.block.yml filter_high_pass_filter.block.yml diff --git a/gr-filter/grc/filter.tree.yml b/gr-filter/grc/filter.tree.yml index 098e413d33..a297ba63f7 100644 --- a/gr-filter/grc/filter.tree.yml +++ b/gr-filter/grc/filter.tree.yml @@ -25,7 +25,6 @@ - mmse_interpolator_xx - pfb_arb_resampler_xxx - rational_resampler_xxx - - rational_resampler_base_xxx - ival_decimator - Channelizers: - freq_xlating_fft_filter_ccc diff --git a/gr-filter/grc/filter_rational_resampler_base_xxx.block.yml b/gr-filter/grc/filter_rational_resampler_base_xxx.block.yml deleted file mode 100644 index 9e81d59557..0000000000 --- a/gr-filter/grc/filter_rational_resampler_base_xxx.block.yml +++ /dev/null @@ -1,47 +0,0 @@ -id: rational_resampler_base_xxx -label: Rational Resampler Base -flags: [ python ] - -parameters: -- id: type - label: Type - dtype: enum - options: [ccc, ccf, fcc, fff, fsf, scc] - option_labels: [Complex->Complex (Complex Taps), Complex->Complex (Real Taps), - Float->Complex (Complex Taps), Float->Float (Real Taps), Float->Short (Real - Taps), Short->Complex (Complex Taps)] - option_attributes: - input: [complex, complex, float, float, float, short] - output: [complex, complex, complex, float, short, complex] - taps: [complex_vector, real_vector, complex_vector, real_vector, real_vector, - complex_vector] - hide: part -- id: interp - label: Interpolation - dtype: int - default: '1' -- id: decim - label: Decimation - dtype: int - default: '1' -- id: taps - label: Taps - dtype: ${ type.taps } - -inputs: -- domain: stream - dtype: ${ type.input } - -outputs: -- domain: stream - dtype: ${ type.output } - -templates: - imports: |- - from gnuradio import filter - from gnuradio.filter import firdes - make: filter.rational_resampler_base_${type}(${interp}, ${decim}, ${taps}) - callbacks: - - set_taps(${taps}) - -file_format: 1 diff --git a/gr-filter/grc/filter_rational_resampler_xxx.block.yml b/gr-filter/grc/filter_rational_resampler_xxx.block.yml index 1ac8238b74..10284d4cfd 100644 --- a/gr-filter/grc/filter_rational_resampler_xxx.block.yml +++ b/gr-filter/grc/filter_rational_resampler_xxx.block.yml @@ -25,6 +25,7 @@ parameters: - id: taps label: Taps dtype: ${ type.taps } + default: '[]' - id: fbw label: Fractional BW dtype: real @@ -49,56 +50,45 @@ templates: % if taps: taps=${taps}, % else: - taps=None, + taps=[], % endif % if float(fbw) != 0: fractional_bw=${fbw}) % else: - fractional_bw=None) + fractional_bw=-1.0) % endif callbacks: - set_taps(${taps}) cpp_templates: - includes: ['#include <gnuradio/filter/rational_resampler_base.h>'] - declarations: 'gr::filter::rational_resampler_base_${type}::sptr ${id};' + includes: ['#include <gnuradio/filter/rational_resampler.h>'] + declarations: 'gr::filter::rational_resampler_${type}::sptr ${id};' make: |- <% taps = self.context.get('taps') try: taps - except (NameError): taps = None + except: taps = None taps = None if (len(taps) == 0) else taps %> % if taps == None or len(taps) == 0: - <% - # Leverage python module to generate default values for taps - from gnuradio import filter - %> - <% - fbw = self.context.get('fbw') - fbw = float(fbw) if (len(fbw) > 0 and float(fbw) != 0) else None - iotype = self.context.get('type') - ftype = eval('filter.rational_resampler.rational_resampler_' + str(iotype)) - fltr = ftype(int(self.context.get('interp')), int(self.context.get('decim')), taps, fbw) - taps = fltr.taps() - # Format complex taps values for C++ - if iotype == 'fcc' or iotype == 'ccc': - cmplx_taps = [] - for cmplx in taps: - cmplx_taps.append({cmplx.real, cmplx.imag}) - taps = cmplx_taps - %> - % endif - - % if str(type.taps) == "complex_vector": - std::vector<gr_complex> taps = {${str(taps)[1:-1]}}; + % if str(type.taps) == "complex_vector": + std::vector<gr_complex> taps; + % else: + std::vector<float> taps; + % endif % else: - std::vector<float> taps = {${str(taps)[1:-1]}}; + % if str(type.taps) == "complex_vector": + std::vector<gr_complex> taps = {${str(taps)[1:-1]}}; + % else: + std::vector<float> taps = {${str(taps)[1:-1]}}; + % endif % endif - this->${id} = gr::filter::rational_resampler_base_${type}::make( + + this->${id} = gr::filter::rational_resampler_${type}::make( ${interp}, ${decim}, - taps); + taps, + ${fbw}); link: ['gnuradio-filter'] callbacks: - set_taps(${taps}) diff --git a/gr-filter/include/gnuradio/filter/CMakeLists.txt b/gr-filter/include/gnuradio/filter/CMakeLists.txt index 4104aff1c9..5e24dbffa4 100644 --- a/gr-filter/include/gnuradio/filter/CMakeLists.txt +++ b/gr-filter/include/gnuradio/filter/CMakeLists.txt @@ -53,7 +53,7 @@ install(FILES pfb_decimator_ccf.h pfb_interpolator_ccf.h pfb_synthesizer_ccf.h - rational_resampler_base.h + rational_resampler.h single_pole_iir_filter_cc.h single_pole_iir_filter_ff.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/filter diff --git a/gr-filter/include/gnuradio/filter/rational_resampler_base.h b/gr-filter/include/gnuradio/filter/rational_resampler.h index 9b0af4b540..2ac18b0904 100644 --- a/gr-filter/include/gnuradio/filter/rational_resampler_base.h +++ b/gr-filter/include/gnuradio/filter/rational_resampler.h @@ -8,8 +8,8 @@ * */ -#ifndef RATIONAL_RESAMPLER_BASE_H -#define RATIONAL_RESAMPLER_BASE_H +#ifndef RATIONAL_RESAMPLER_H +#define RATIONAL_RESAMPLER_H #include <gnuradio/block.h> #include <gnuradio/filter/api.h> @@ -55,10 +55,10 @@ namespace filter { * prefilter. */ template <class IN_T, class OUT_T, class TAP_T> -class FILTER_API rational_resampler_base : virtual public block +class FILTER_API rational_resampler : virtual public block { public: - typedef std::shared_ptr<rational_resampler_base<IN_T, OUT_T, TAP_T>> sptr; + typedef std::shared_ptr<rational_resampler<IN_T, OUT_T, TAP_T>> sptr; /*! * Make a rational resampling FIR filter. @@ -67,8 +67,10 @@ public: * \param decimation The integer decimation rate of the filter * \param taps The filter taps to control images and aliases */ - static sptr - make(unsigned interpolation, unsigned decimation, const std::vector<TAP_T>& taps); + static sptr make(unsigned interpolation, + unsigned decimation, + const std::vector<TAP_T>& taps = std::vector<TAP_T>(), + float fractional_bw = 0.0); virtual unsigned interpolation() const = 0; virtual unsigned decimation() const = 0; @@ -76,18 +78,14 @@ public: virtual void set_taps(const std::vector<TAP_T>& taps) = 0; virtual std::vector<TAP_T> taps() const = 0; }; -typedef rational_resampler_base<gr_complex, gr_complex, gr_complex> - rational_resampler_base_ccc; -typedef rational_resampler_base<gr_complex, gr_complex, float> - rational_resampler_base_ccf; -typedef rational_resampler_base<float, gr_complex, gr_complex> - rational_resampler_base_fcc; -typedef rational_resampler_base<float, float, float> rational_resampler_base_fff; -typedef rational_resampler_base<float, std::int16_t, float> rational_resampler_base_fsf; -typedef rational_resampler_base<std::int16_t, gr_complex, gr_complex> - rational_resampler_base_scc; +typedef rational_resampler<gr_complex, gr_complex, gr_complex> rational_resampler_ccc; +typedef rational_resampler<gr_complex, gr_complex, float> rational_resampler_ccf; +typedef rational_resampler<float, gr_complex, gr_complex> rational_resampler_fcc; +typedef rational_resampler<float, float, float> rational_resampler_fff; +typedef rational_resampler<float, std::int16_t, float> rational_resampler_fsf; +typedef rational_resampler<std::int16_t, gr_complex, gr_complex> rational_resampler_scc; } /* namespace filter */ } /* namespace gr */ -#endif /* RATIONAL_RESAMPLER_BASE_H */ +#endif /* RATIONAL_RESAMPLER_H */ diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 69a56dd2c5..1e3189b7af 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -50,7 +50,7 @@ add_library(gnuradio-filter pfb_decimator_ccf_impl.cc pfb_interpolator_ccf_impl.cc pfb_synthesizer_ccf_impl.cc - rational_resampler_base_impl.cc + rational_resampler_impl.cc single_pole_iir_filter_cc_impl.cc single_pole_iir_filter_ff_impl.cc ) diff --git a/gr-filter/lib/rational_resampler_base_impl.cc b/gr-filter/lib/rational_resampler_base_impl.cc deleted file mode 100644 index 1ce199d1e4..0000000000 --- a/gr-filter/lib/rational_resampler_base_impl.cc +++ /dev/null @@ -1,166 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004,2010,2012,2018 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "rational_resampler_base_impl.h" -#include <gnuradio/io_signature.h> -#include <volk/volk.h> -#include <stdexcept> - -namespace gr { -namespace filter { - -template <class IN_T, class OUT_T, class TAP_T> -typename rational_resampler_base<IN_T, OUT_T, TAP_T>::sptr -rational_resampler_base<IN_T, OUT_T, TAP_T>::make(unsigned interpolation, - unsigned decimation, - const std::vector<TAP_T>& taps) -{ - return gnuradio::make_block_sptr<rational_resampler_base_impl<IN_T, OUT_T, TAP_T>>( - interpolation, decimation, taps); -} - -template <class IN_T, class OUT_T, class TAP_T> -rational_resampler_base_impl<IN_T, OUT_T, TAP_T>::rational_resampler_base_impl( - unsigned interpolation, unsigned decimation, const std::vector<TAP_T>& taps) - : block("rational_resampler_base<IN_T,OUT_T,TAP_T>", - io_signature::make(1, 1, sizeof(IN_T)), - io_signature::make(1, 1, sizeof(OUT_T))), - d_history(1), - d_decimation(decimation), - d_ctr(0), - d_updated(false) -{ - if (interpolation == 0) - throw std::out_of_range( - "rational_resampler_base_impl<IN_T,OUT_T,TAP_T>: interpolation must be > 0"); - if (decimation == 0) - throw std::out_of_range( - "rational_resampler_base_impl<IN_T,OUT_T,TAP_T>: decimation must be > 0"); - - this->set_relative_rate((uint64_t)interpolation, (uint64_t)decimation); - this->set_output_multiple(1); - - d_firs.reserve(interpolation); - for (unsigned i = 0; i < interpolation; i++) { - d_firs.emplace_back(std::vector<TAP_T>()); - } - - set_taps(taps); - install_taps(d_new_taps); -} - -template <class IN_T, class OUT_T, class TAP_T> -void rational_resampler_base_impl<IN_T, OUT_T, TAP_T>::set_taps( - const std::vector<TAP_T>& taps) -{ - d_new_taps = taps; - d_updated = true; - - // round up length to a multiple of the interpolation factor - int n = taps.size() % this->interpolation(); - if (n > 0) { - n = this->interpolation() - n; - while (n-- > 0) { - d_new_taps.insert(d_new_taps.end(), 0); - } - } - - assert(d_new_taps.size() % this->interpolation() == 0); -} - -template <class IN_T, class OUT_T, class TAP_T> -void rational_resampler_base_impl<IN_T, OUT_T, TAP_T>::install_taps( - const std::vector<TAP_T>& taps) -{ - int nfilters = this->interpolation(); - int nt = taps.size() / nfilters; - - assert(nt * nfilters == (int)taps.size()); - - std::vector<std::vector<TAP_T>> 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; -} - -template <class IN_T, class OUT_T, class TAP_T> -std::vector<TAP_T> rational_resampler_base_impl<IN_T, OUT_T, TAP_T>::taps() const -{ - return d_new_taps; -} - -template <class IN_T, class OUT_T, class TAP_T> -void rational_resampler_base_impl<IN_T, OUT_T, TAP_T>::forecast( - int noutput_items, gr_vector_int& ninput_items_required) -{ - int nreqd = std::max( - (unsigned)1, - (int)((double)(noutput_items + 1) * this->decimation() / this->interpolation()) + - history() - 1); - unsigned ninputs = ninput_items_required.size(); - for (unsigned i = 0; i < ninputs; i++) - ninput_items_required[i] = nreqd; -} - -template <class IN_T, class OUT_T, class TAP_T> -int rational_resampler_base_impl<IN_T, OUT_T, TAP_T>::general_work( - int noutput_items, - gr_vector_int& ninput_items, - gr_vector_const_void_star& input_items, - gr_vector_void_star& output_items) -{ - const IN_T* in = (const IN_T*)input_items[0]; - OUT_T* out = (OUT_T*)output_items[0]; - - if (d_updated) { - install_taps(d_new_taps); - return 0; // history requirement may have increased. - } - - unsigned int ctr = d_ctr; - int count = 0; - - int i = 0; - while ((i < noutput_items) && (count < ninput_items[0])) { - out[i++] = d_firs[ctr].filter(in); - ctr += this->decimation(); - while (ctr >= this->interpolation()) { - ctr -= this->interpolation(); - in++; - count++; - } - } - - d_ctr = ctr; - this->consume_each(count); - return i; -} -template class rational_resampler_base<gr_complex, gr_complex, gr_complex>; -template class rational_resampler_base<gr_complex, gr_complex, float>; -template class rational_resampler_base<float, gr_complex, gr_complex>; -template class rational_resampler_base<float, float, float>; -template class rational_resampler_base<float, std::int16_t, float>; -template class rational_resampler_base<std::int16_t, gr_complex, gr_complex>; - -} /* namespace filter */ -} /* namespace gr */ diff --git a/gr-filter/lib/rational_resampler_impl.cc b/gr-filter/lib/rational_resampler_impl.cc new file mode 100644 index 0000000000..32f253822f --- /dev/null +++ b/gr-filter/lib/rational_resampler_impl.cc @@ -0,0 +1,263 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2010,2012,2018 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rational_resampler_impl.h" +#include <gnuradio/filter/firdes.h> +#include <gnuradio/integer_math.h> +#include <gnuradio/io_signature.h> +#include <volk/volk.h> +#include <stdexcept> + +namespace gr { +namespace filter { + + +/** + * @brief Given the interpolation rate, decimation rate and a fractional bandwidth, + design a set of taps. + * + * Uses default parameters to build a low pass filter using a Kaiser Window + * + * @param interpolation interpolation factor (integer > 0) + * @param decimation decimation factor (integer > 0) + * @param fractional_bw fractional bandwidth in (0, 0.5) 0.4 works well. (float) + */ +template <typename TAP_T> +std::vector<TAP_T> design_resampler_filter(const unsigned interpolation, + const unsigned decimation, + const float fractional_bw) +{ + + if (fractional_bw >= 0.5 || fractional_bw <= 0) { + throw std::range_error("Invalid fractional_bandwidth, must be in (0, 0.5)"); + } + + // These are default values used to generate the filter when no taps are known + // Pulled from rational_resampler.py + float beta = 7.0; + float halfband = 0.5; + float rate = float(interpolation) / float(decimation); + float trans_width, mid_transition_band; + + if (rate >= 1.0) { + trans_width = halfband - fractional_bw; + mid_transition_band = halfband - trans_width / 2.0; + } else { + trans_width = rate * (halfband - fractional_bw); + mid_transition_band = rate * halfband - trans_width / 2.0; + } + + return firdes::low_pass(interpolation, /* gain */ + interpolation, /* Fs */ + mid_transition_band, /* trans mid point */ + trans_width, /* transition width */ + firdes::WIN_KAISER, + beta); /* beta*/ +} + +template <> +std::vector<gr_complex> design_resampler_filter<gr_complex>(const unsigned interpolation, + const unsigned decimation, + const float fractional_bw) +{ + auto real_taps = + design_resampler_filter<float>(interpolation, decimation, fractional_bw); + + std::vector<gr_complex> cplx_taps(real_taps.size()); + for (size_t i = 0; i < real_taps.size(); i++) { + cplx_taps[i] = real_taps[i]; + } + + return cplx_taps; +} + +template <class IN_T, class OUT_T, class TAP_T> +typename rational_resampler<IN_T, OUT_T, TAP_T>::sptr +rational_resampler<IN_T, OUT_T, TAP_T>::make(unsigned interpolation, + unsigned decimation, + const std::vector<TAP_T>& taps, + float fractional_bw) +{ + return gnuradio::make_block_sptr<rational_resampler_impl<IN_T, OUT_T, TAP_T>>( + interpolation, decimation, taps, fractional_bw); +} + +template <class IN_T, class OUT_T, class TAP_T> +rational_resampler_impl<IN_T, OUT_T, TAP_T>::rational_resampler_impl( + unsigned interpolation, + unsigned decimation, + const std::vector<TAP_T>& taps, + float fractional_bw) + : block("rational_resampler<IN_T,OUT_T,TAP_T>", + io_signature::make(1, 1, sizeof(IN_T)), + io_signature::make(1, 1, sizeof(OUT_T))), + d_history(1), + d_decimation(decimation) +{ + if (interpolation == 0) { + throw std::out_of_range( + "rational_resampler_impl<IN_T,OUT_T,TAP_T>: interpolation must be > 0"); + } + if (decimation == 0) { + throw std::out_of_range( + "rational_resampler_impl<IN_T,OUT_T,TAP_T>: decimation must be > 0"); + } + + // If taps are not specified, we need to design them based on fractional_bw + if (taps.size() == 0 && fractional_bw <= 0) { + fractional_bw = 0.4; + } + + auto d = GR_GCD(interpolation, decimation); + + if (taps.size() && (d > 1)) { + GR_LOG_INFO( + d_logger, + boost::format( + "Rational resampler has user-provided taps but interpolation (%1%) and " + "decimation (%2%) have a GCD of %3%, which increases the complexity of " + "the filterbank. Consider reducing these values by the GCD.") % + interpolation % decimation % d); + } + + std::vector<TAP_T> staps; + if (taps.size() == 0) { + if (fractional_bw <= 0) { + fractional_bw = 0.4; + } + // If we don't have user-provided taps, reduce the interp and + // decim values by the GCD (if there is one) and then define + // the taps from these new values. + interpolation /= d; + decimation /= d; + staps = design_resampler_filter<TAP_T>(interpolation, decimation, fractional_bw); + } else { + staps = taps; + } + + this->set_relative_rate(uint64_t{ interpolation }, uint64_t{ decimation }); + this->set_output_multiple(1); + + d_firs.reserve(interpolation); + for (unsigned i = 0; i < interpolation; i++) { + d_firs.emplace_back(std::vector<TAP_T>()); + } + + set_taps(staps); + install_taps(d_new_taps); +} + +template <class IN_T, class OUT_T, class TAP_T> +void rational_resampler_impl<IN_T, OUT_T, TAP_T>::set_taps(const std::vector<TAP_T>& taps) +{ + d_new_taps = taps; + d_updated = true; + + // round up length to a multiple of the interpolation factor + int n = taps.size() % this->interpolation(); + if (n > 0) { + n = this->interpolation() - n; + while (n-- > 0) { + d_new_taps.insert(d_new_taps.end(), 0); + } + } + + assert(d_new_taps.size() % this->interpolation() == 0); +} + +template <class IN_T, class OUT_T, class TAP_T> +void rational_resampler_impl<IN_T, OUT_T, TAP_T>::install_taps( + const std::vector<TAP_T>& taps) +{ + int nfilters = this->interpolation(); + int nt = taps.size() / nfilters; + + assert(nt * nfilters == (int)taps.size()); + + std::vector<std::vector<TAP_T>> 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; +} + +template <class IN_T, class OUT_T, class TAP_T> +std::vector<TAP_T> rational_resampler_impl<IN_T, OUT_T, TAP_T>::taps() const +{ + return d_new_taps; +} + +template <class IN_T, class OUT_T, class TAP_T> +void rational_resampler_impl<IN_T, OUT_T, TAP_T>::forecast( + int noutput_items, gr_vector_int& ninput_items_required) +{ + int nreqd = std::max( + 1U, + (int)((double)(noutput_items + 1) * this->decimation() / this->interpolation()) + + history() - 1); + unsigned ninputs = ninput_items_required.size(); + for (unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = nreqd; +} + +template <class IN_T, class OUT_T, class TAP_T> +int rational_resampler_impl<IN_T, OUT_T, TAP_T>::general_work( + int noutput_items, + gr_vector_int& ninput_items, + gr_vector_const_void_star& input_items, + gr_vector_void_star& output_items) +{ + auto in = reinterpret_cast<const IN_T*>(input_items[0]); + auto out = reinterpret_cast<OUT_T*>(output_items[0]); + + if (d_updated) { + install_taps(d_new_taps); + return 0; // history requirement may have increased. + } + + unsigned int ctr = d_ctr; + int count = 0; + + int i = 0; + while ((i < noutput_items) && (count < ninput_items[0])) { + out[i++] = d_firs[ctr].filter(in); + ctr += this->decimation(); + while (ctr >= this->interpolation()) { + ctr -= this->interpolation(); + in++; + count++; + } + } + + d_ctr = ctr; + this->consume_each(count); + return i; +} +template class rational_resampler<gr_complex, gr_complex, gr_complex>; +template class rational_resampler<gr_complex, gr_complex, float>; +template class rational_resampler<float, gr_complex, gr_complex>; +template class rational_resampler<float, float, float>; +template class rational_resampler<float, std::int16_t, float>; +template class rational_resampler<std::int16_t, gr_complex, gr_complex>; + +} /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/rational_resampler_base_impl.h b/gr-filter/lib/rational_resampler_impl.h index 32d40f77bb..e9be526d30 100644 --- a/gr-filter/lib/rational_resampler_base_impl.h +++ b/gr-filter/lib/rational_resampler_impl.h @@ -8,33 +8,35 @@ * */ -#ifndef RATIONAL_RESAMPLER_IMPL_BASE_H -#define RATIONAL_RESAMPLER_IMPL_BASE_H +#ifndef RATIONAL_RESAMPLER_IMPL_H +#define RATIONAL_RESAMPLER_IMPL_H #include <gnuradio/filter/fir_filter.h> -#include <gnuradio/filter/rational_resampler_base.h> +#include <gnuradio/filter/rational_resampler.h> namespace gr { namespace filter { template <class IN_T, class OUT_T, class TAP_T> -class FILTER_API rational_resampler_base_impl - : public rational_resampler_base<IN_T, OUT_T, TAP_T> +class FILTER_API rational_resampler_impl : public rational_resampler<IN_T, OUT_T, TAP_T> { private: unsigned d_history; unsigned d_decimation; - unsigned d_ctr; + unsigned d_ctr = 0; std::vector<TAP_T> d_new_taps; std::vector<kernel::fir_filter<IN_T, OUT_T, TAP_T>> d_firs; - bool d_updated; + bool d_updated = false; void install_taps(const std::vector<TAP_T>& taps); + gr::logger_ptr d_logger; + public: - rational_resampler_base_impl(unsigned interpolation, - unsigned decimation, - const std::vector<TAP_T>& taps); + rational_resampler_impl(unsigned interpolation, + unsigned decimation, + const std::vector<TAP_T>& taps, + float fractional_bw); unsigned history() const { return d_history; } void set_history(unsigned history) { d_history = history; } @@ -55,4 +57,4 @@ public: } /* namespace filter */ } /* namespace gr */ -#endif /* RATIONAL_RESAMPLER_IMPL_BASE_H */ +#endif /* RATIONAL_RESAMPLER_IMPL_H */ diff --git a/gr-filter/python/filter/CMakeLists.txt b/gr-filter/python/filter/CMakeLists.txt index 765af55868..1fb908fe68 100644 --- a/gr-filter/python/filter/CMakeLists.txt +++ b/gr-filter/python/filter/CMakeLists.txt @@ -15,7 +15,6 @@ GR_PYTHON_INSTALL( freq_xlating_fft_filter.py optfir.py pfb.py - rational_resampler.py file_taps_loader.py DESTINATION ${GR_PYTHON_DIR}/gnuradio/filter ) diff --git a/gr-filter/python/filter/__init__.py b/gr-filter/python/filter/__init__.py index 8e1fa48c68..e08457e6e0 100644 --- a/gr-filter/python/filter/__init__.py +++ b/gr-filter/python/filter/__init__.py @@ -23,7 +23,6 @@ except ImportError: from .filterbank import * from .freq_xlating_fft_filter import * -from .rational_resampler import * from . import pfb from . import optfir diff --git a/gr-filter/python/filter/bindings/CMakeLists.txt b/gr-filter/python/filter/bindings/CMakeLists.txt index 47fa64cafe..1757159ba3 100644 --- a/gr-filter/python/filter/bindings/CMakeLists.txt +++ b/gr-filter/python/filter/bindings/CMakeLists.txt @@ -46,7 +46,7 @@ list(APPEND filter_python_files pfb_synthesizer_ccf_python.cc pm_remez_python.cc polyphase_filterbank_python.cc - rational_resampler_base_python.cc + rational_resampler_python.cc single_pole_iir_python.cc single_pole_iir_filter_cc_python.cc single_pole_iir_filter_ff_python.cc diff --git a/gr-filter/python/filter/bindings/docstrings/rational_resampler_base_pydoc_template.h b/gr-filter/python/filter/bindings/docstrings/rational_resampler_pydoc_template.h index a187d6f1ef..a187d6f1ef 100644 --- a/gr-filter/python/filter/bindings/docstrings/rational_resampler_base_pydoc_template.h +++ b/gr-filter/python/filter/bindings/docstrings/rational_resampler_pydoc_template.h diff --git a/gr-filter/python/filter/bindings/python_bindings.cc b/gr-filter/python/filter/bindings/python_bindings.cc index e7daa19c30..8147847e4a 100644 --- a/gr-filter/python/filter/bindings/python_bindings.cc +++ b/gr-filter/python/filter/bindings/python_bindings.cc @@ -58,7 +58,7 @@ void bind_pfb_interpolator_ccf(py::module&); void bind_pfb_synthesizer_ccf(py::module&); void bind_pm_remez(py::module&); void bind_polyphase_filterbank(py::module&); -void bind_rational_resampler_base(py::module&); +void bind_rational_resampler(py::module&); void bind_single_pole_iir(py::module&); void bind_single_pole_iir_filter_cc(py::module&); void bind_single_pole_iir_filter_ff(py::module&); @@ -125,7 +125,7 @@ PYBIND11_MODULE(filter_python, m) bind_pfb_synthesizer_ccf(m); bind_pm_remez(m); bind_polyphase_filterbank(m); - bind_rational_resampler_base(m); + bind_rational_resampler(m); bind_single_pole_iir(m); bind_single_pole_iir_filter_cc(m); bind_single_pole_iir_filter_ff(m); diff --git a/gr-filter/python/filter/bindings/rational_resampler_base_python.cc b/gr-filter/python/filter/bindings/rational_resampler_base_python.cc deleted file mode 100644 index 09083e798b..0000000000 --- a/gr-filter/python/filter/bindings/rational_resampler_base_python.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2020 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -/***********************************************************************************/ -/* This file is automatically generated using bindtool and can be manually edited */ -/* The following lines can be configured to regenerate this file during cmake */ -/* If manual edits are made, the following tags should be modified accordingly. */ -/* BINDTOOL_GEN_AUTOMATIC(0) */ -/* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(rational_resampler_base.h) */ -/* BINDTOOL_HEADER_FILE_HASH(8c16e10c6c05947272429eefa70c443e) */ -/***********************************************************************************/ - -#include <pybind11/complex.h> -#include <pybind11/pybind11.h> -#include <pybind11/stl.h> - -namespace py = pybind11; - -#include <gnuradio/filter/rational_resampler_base.h> - -template <class IN_T, class OUT_T, class TAP_T> -void bind_rational_resampler_base_template(py::module& m, const char* classname) -{ - using rational_resampler_base = - gr::filter::rational_resampler_base<IN_T, OUT_T, TAP_T>; - - py::class_<rational_resampler_base, - gr::block, - gr::basic_block, - std::shared_ptr<rational_resampler_base>>(m, classname) - .def(py::init(&gr::filter::rational_resampler_base<IN_T, OUT_T, TAP_T>::make), - py::arg("interpolation"), - py::arg("decimation"), - py::arg("taps")) - - .def("set_taps", &rational_resampler_base::set_taps, py::arg("taps")) - .def("taps", &rational_resampler_base::taps) - .def("interpolation", &rational_resampler_base::interpolation) - .def("decimation", &rational_resampler_base::decimation); -} - -void bind_rational_resampler_base(py::module& m) -{ - bind_rational_resampler_base_template<gr_complex, gr_complex, gr_complex>( - m, "rational_resampler_base_ccc"); - bind_rational_resampler_base_template<gr_complex, gr_complex, float>( - m, "rational_resampler_base_ccf"); - bind_rational_resampler_base_template<float, gr_complex, gr_complex>( - m, "rational_resampler_base_fcc"); - bind_rational_resampler_base_template<float, float, float>( - m, "rational_resampler_base_fff"); - bind_rational_resampler_base_template<float, std::int16_t, float>( - m, "rational_resampler_base_fsf"); - bind_rational_resampler_base_template<std::int16_t, gr_complex, gr_complex>( - m, "rational_resampler_base_scc"); -} diff --git a/gr-filter/python/filter/bindings/rational_resampler_python.cc b/gr-filter/python/filter/bindings/rational_resampler_python.cc new file mode 100644 index 0000000000..0f77b73415 --- /dev/null +++ b/gr-filter/python/filter/bindings/rational_resampler_python.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +/***********************************************************************************/ +/* This file is automatically generated using bindtool and can be manually edited */ +/* The following lines can be configured to regenerate this file during cmake */ +/* If manual edits are made, the following tags should be modified accordingly. */ +/* BINDTOOL_GEN_AUTOMATIC(0) */ +/* BINDTOOL_USE_PYGCCXML(0) */ +/* BINDTOOL_HEADER_FILE(rational_resampler.h) */ +/* BINDTOOL_HEADER_FILE_HASH(79b3bf084bb620b66770a08c158f7ad9) */ +/***********************************************************************************/ + +#include <pybind11/complex.h> +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> + +namespace py = pybind11; + +#include <gnuradio/filter/rational_resampler.h> + +template <class IN_T, class OUT_T, class TAP_T> +void bind_rational_resampler_template(py::module& m, const char* classname) +{ + using rational_resampler = gr::filter::rational_resampler<IN_T, OUT_T, TAP_T>; + + py::class_<rational_resampler, + gr::block, + gr::basic_block, + std::shared_ptr<rational_resampler>>(m, classname) + .def(py::init(&gr::filter::rational_resampler<IN_T, OUT_T, TAP_T>::make), + py::arg("interpolation"), + py::arg("decimation"), + py::arg("taps") = std::vector<TAP_T>(), + py::arg("fractional_bw") = 0.0) + + .def("set_taps", &rational_resampler::set_taps, py::arg("taps")) + .def("taps", &rational_resampler::taps) + .def("interpolation", &rational_resampler::interpolation) + .def("decimation", &rational_resampler::decimation); +} + +void bind_rational_resampler(py::module& m) +{ + bind_rational_resampler_template<gr_complex, gr_complex, gr_complex>( + m, "rational_resampler_ccc"); + bind_rational_resampler_template<gr_complex, gr_complex, float>( + m, "rational_resampler_ccf"); + bind_rational_resampler_template<float, gr_complex, gr_complex>( + m, "rational_resampler_fcc"); + bind_rational_resampler_template<float, float, float>(m, "rational_resampler_fff"); + bind_rational_resampler_template<float, std::int16_t, float>( + m, "rational_resampler_fsf"); + bind_rational_resampler_template<std::int16_t, gr_complex, gr_complex>( + m, "rational_resampler_scc"); +} diff --git a/gr-filter/python/filter/qa_rational_resampler.py b/gr-filter/python/filter/qa_rational_resampler.py index bd4122d93d..4c67dc793b 100644 --- a/gr-filter/python/filter/qa_rational_resampler.py +++ b/gr-filter/python/filter/qa_rational_resampler.py @@ -76,7 +76,7 @@ class test_rational_resampler (gr_unittest.TestCase): tb = gr.top_block() src = blocks.vector_source_f(src_data) - op = filter.rational_resampler_base_fff(1, 1, taps) + op = filter.rational_resampler_fff(1, 1, taps) dst = blocks.vector_sink_f() tb.connect(src, op) tb.connect(op, dst) @@ -99,7 +99,7 @@ class test_rational_resampler (gr_unittest.TestCase): tb = gr.top_block() src = blocks.vector_source_f(src_data) - op = filter.rational_resampler_base_fff(interpolation, 1, taps) + op = filter.rational_resampler_fff(interpolation, 1, taps) dst = blocks.vector_sink_f() tb.connect(src, op) tb.connect(op, dst) @@ -117,7 +117,7 @@ class test_rational_resampler (gr_unittest.TestCase): tb = gr.top_block() src = blocks.vector_source_f(src_data) - op = filter.rational_resampler_base_fff(interpolation, 1, taps) + op = filter.rational_resampler_fff(interpolation, 1, taps) dst = blocks.vector_sink_f() tb.connect(src, op) tb.connect(op, dst) @@ -137,7 +137,7 @@ class test_rational_resampler (gr_unittest.TestCase): tb = gr.top_block() src = blocks.vector_source_f(src_data) - op = filter.rational_resampler_base_fff(1, decimation, taps) + op = filter.rational_resampler_fff(1, decimation, taps) dst = blocks.vector_sink_f() tb.connect(src, op) tb.connect(op, dst) @@ -168,7 +168,7 @@ class test_rational_resampler (gr_unittest.TestCase): tb = gr.top_block() src = blocks.vector_source_f(src_data) - op = filter.rational_resampler_base_fff(1, decim, taps) + op = filter.rational_resampler_fff(1, decim, taps) dst = blocks.vector_sink_f() tb.connect(src, op, dst) tb.run() @@ -206,7 +206,7 @@ class test_rational_resampler (gr_unittest.TestCase): tb = gr.top_block() src = blocks.vector_source_f(src_data) - op = filter.rational_resampler_base_fff(interp, 1, taps) + op = filter.rational_resampler_fff(interp, 1, taps) dst = blocks.vector_sink_f() tb.connect(src, op, dst) tb.run() @@ -238,7 +238,7 @@ class test_rational_resampler (gr_unittest.TestCase): tb = gr.top_block() src = blocks.vector_source_f(src_data) - op = filter.rational_resampler_base_fff(interp, decimation, taps) + op = filter.rational_resampler_fff(interp, decimation, taps) dst = blocks.vector_sink_f() tb.connect(src, op) tb.connect(op, dst) diff --git a/gr-filter/python/filter/rational_resampler.py b/gr-filter/python/filter/rational_resampler.py deleted file mode 100644 index 1af9698e2b..0000000000 --- a/gr-filter/python/filter/rational_resampler.py +++ /dev/null @@ -1,145 +0,0 @@ -# -# Copyright 2005,2007 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# - - -import math -from gnuradio import gr -from . import filter_python as filter - -_plot = None - - -def design_filter(interpolation, decimation, fractional_bw): - """ - Given the interpolation rate, decimation rate and a fractional bandwidth, - design a set of taps. - - Args: - interpolation: interpolation factor (integer > 0) - decimation: decimation factor (integer > 0) - fractional_bw: fractional bandwidth in (0, 0.5) 0.4 works well. (float) - Returns: - : sequence of numbers - """ - - if fractional_bw >= 0.5 or fractional_bw <= 0: - raise ValueError("Invalid fractional_bandwidth, must be in (0, 0.5)") - - beta = 7.0 - halfband = 0.5 - rate = float(interpolation) / float(decimation) - if(rate >= 1.0): - trans_width = halfband - fractional_bw - mid_transition_band = halfband - trans_width / 2.0 - else: - trans_width = rate*(halfband - fractional_bw) - mid_transition_band = rate*halfband - trans_width / 2.0 - - taps = filter.firdes.low_pass(interpolation, # gain - interpolation, # Fs - mid_transition_band, # trans mid point - trans_width, # transition width - filter.firdes.WIN_KAISER, - beta) # beta - - return taps - - - -class _rational_resampler_base(gr.hier_block2): - """ - base class for all rational resampler variants. - """ - def __init__(self, resampler_base, - interpolation, decimation, taps=None, fractional_bw=None): - """ - Rational resampling polyphase FIR filter. - - Either taps or fractional_bw may be specified, but not both. - If neither is specified, a reasonable default, 0.4, is used as - the fractional_bw. - - Args: - interpolation: interpolation factor (integer > 0) - decimation: decimation factor (integer > 0) - taps: optional filter coefficients (sequence) - fractional_bw: fractional bandwidth in (0, 0.5), measured at final freq (use 0.4) (float) - """ - - if not isinstance(interpolation, int) or interpolation < 1: - raise ValueError("interpolation must be an integer >= 1") - - if not isinstance(decimation, int) or decimation < 1: - raise ValueError("decimation must be an integer >= 1") - - if taps is None and fractional_bw is None: - fractional_bw = 0.4 - - d = math.gcd(interpolation, decimation) - - # If we have user-provided taps and the interp and decim - # values have a common divisor, we don't reduce these values - # by the GCD but issue a warning to the user that this might - # increase the complexity of the filter. - if taps and (d > 1): - gr.log.info("Rational resampler has user-provided taps but interpolation ({0}) and decimation ({1}) have a GCD of {2}, which increases the complexity of the filterbank. Consider reducing these values by the GCD.".format(interpolation, decimation, d)) - - # If we don't have user-provided taps, reduce the interp and - # decim values by the GCD (if there is one) and then define - # the taps from these new values. - if taps is None: - interpolation = interpolation // d - decimation = decimation // d - taps = design_filter(interpolation, decimation, fractional_bw) - - self.resampler = resampler_base(interpolation, decimation, taps) - gr.hier_block2.__init__(self, "rational_resampler", - gr.io_signature(1, 1, self.resampler.input_signature().sizeof_stream_item(0)), - gr.io_signature(1, 1, self.resampler.output_signature().sizeof_stream_item(0))) - - self.connect(self, self.resampler, self) - - def taps(self): - return self.resampler.taps() - -class rational_resampler_fff(_rational_resampler_base): - def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): - """ - Rational resampling polyphase FIR filter with - float input, float output and float taps. - """ - _rational_resampler_base.__init__(self, filter.rational_resampler_base_fff, - interpolation, decimation, taps, fractional_bw) - -class rational_resampler_ccf(_rational_resampler_base): - def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): - """ - Rational resampling polyphase FIR filter with - complex input, complex output and float taps. - """ - _rational_resampler_base.__init__(self, filter.rational_resampler_base_ccf, - interpolation, decimation, taps, fractional_bw) - -class rational_resampler_ccc(_rational_resampler_base): - def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): - """ - Rational resampling polyphase FIR filter with - complex input, complex output and complex taps. - """ - _rational_resampler_base.__init__(self, filter.rational_resampler_base_ccc, - interpolation, decimation, taps, fractional_bw) - -class rational_resampler_fcc(_rational_resampler_base): - def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): - """ - Rational resampling polyphase FIR filter with - float input, complex output and complex taps. - """ - _rational_resampler_base.__init__(self, filter.rational_resampler_base_fcc, - interpolation, decimation, taps, fractional_bw) |