diff --git a/src/correlate_access_code_bb_ts_fl_impl.cpp b/src/correlate_access_code_bb_ts_fl_impl.cpp index fbfd64e..1d81e05 100644 --- a/src/correlate_access_code_bb_ts_fl_impl.cpp +++ b/src/correlate_access_code_bb_ts_fl_impl.cpp @@ -47,9 +47,9 @@ correlate_access_code_bb_ts_fl_impl::correlate_access_code_bb_ts_fl_impl( io_signature::make(1, 1, sizeof(char))), d_data_reg(0), d_mask(0), d_threshold(threshold), d_len(0) { set_tag_propagation_policy(TPP_DONT); - //set_ninput_items_required(len * 8); - set_min_noutput_items(len * 8); - set_output_multiple(len * 8); + // set_ninput_items_required(len * 8); + set_min_noutput_items(len * 8); + set_output_multiple(len * 8); if (!set_access_code(access_code)) { GR_LOG_ERROR(d_logger, "access_code is > 64 bits"); @@ -61,7 +61,7 @@ correlate_access_code_bb_ts_fl_impl::correlate_access_code_bb_ts_fl_impl( d_me = pmt::string_to_symbol(str.str()); d_key = pmt::string_to_symbol(tag_name); - d_pkt_len = 8 * len; + d_pkt_len = 8 * len; } correlate_access_code_bb_ts_fl_impl::~correlate_access_code_bb_ts_fl_impl() {} @@ -94,47 +94,48 @@ int correlate_access_code_bb_ts_fl_impl::general_work( int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - // Streaming correlate access code. + // Streaming correlate access code. const unsigned char *in = (const unsigned char *)input_items[0]; unsigned char *out = (unsigned char *)output_items[0]; uint64_t abs_out_sample_cnt = nitems_written(0); - int nprod = 0; - - // shift in new data - d_data_reg = (d_data_reg << 1) | (in[0] & 0x1); - - // compute hamming distance between desired access code and current data - uint64_t wrong_bits = 0; - uint64_t nwrong = d_threshold + 1; - - wrong_bits = (d_data_reg ^ d_access_code) & d_mask; - volk_64u_popcnt(&nwrong, wrong_bits); - - if (nwrong <= d_threshold) { - if (d_pkt_len > noutput_items) { - GR_LOG_FATAL(d_logger, - boost::format("cannot write tagged stream not enough output_items available")); - return 0; - } - - GR_LOG_DEBUG(d_logger, - boost::format("writing tag at sample %llu") % - (abs_out_sample_cnt)); - // MAKE A TAG OUT OF THIS AND UPDATE OFFSET - add_item_tag(0, // stream ID - abs_out_sample_cnt, // sample - d_key, // length key - pmt::from_long(d_pkt_len), // length data - d_me); // block src id - - for (int j = 0; j < d_pkt_len; j++) { - out[nprod++] = in[j]; - } - } - - consume_each(1); + int nprod = 0; + + // shift in new data + d_data_reg = (d_data_reg << 1) | (in[0] & 0x1); + + // compute hamming distance between desired access code and current data + uint64_t wrong_bits = 0; + uint64_t nwrong = d_threshold + 1; + + wrong_bits = (d_data_reg ^ d_access_code) & d_mask; + volk_64u_popcnt(&nwrong, wrong_bits); + + if (nwrong <= d_threshold) { + if (d_pkt_len > noutput_items) { + GR_LOG_FATAL( + d_logger, + boost::format( + "cannot write tagged stream not enough output_items available")); + return 0; + } + + GR_LOG_DEBUG(d_logger, boost::format("writing tag at sample %llu") % + (abs_out_sample_cnt)); + // MAKE A TAG OUT OF THIS AND UPDATE OFFSET + add_item_tag(0, // stream ID + abs_out_sample_cnt, // sample + d_key, // length key + pmt::from_long(d_pkt_len), // length data + d_me); // block src id + + for (int j = 0; j < d_pkt_len; j++) { + out[nprod++] = in[j]; + } + } + + consume_each(1); return nprod; } diff --git a/src/main.cpp b/src/main.cpp index 765ca89..7e193db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,9 @@ +#include +#include #include #include #include #include -#include -#include #include "correlate_access_code_bb_ts_fl.h" #include "rational_resampler.h" @@ -30,7 +30,7 @@ #include int main(int argc, char **argv) { - auto pre = env::prefix("DECODER"); + auto pre = env::prefix("DECODER"); gr::top_block_sptr tb; @@ -53,24 +53,27 @@ int main(int argc, char **argv) { float samp_rate = 2000000; float bandwidth_sdr = 1000000; float bandwidth_xlat = 5000; - float transition_bw = 1000; + float transition_bw = 1000; int decimation = static_cast(bandwidth_sdr / bandwidth_xlat); float baud = 2400; unsigned rs_interpolation = 24; unsigned rs_decimation = 25; - float sps = samp_rate / decimation / baud * (float) rs_interpolation / (float) rs_decimation; + float sps = samp_rate / decimation / baud * (float)rs_interpolation / + (float)rs_decimation; int RF, IF, BB; std::string device_string; - + const auto freq_id = pre.register_required_variable("FREQUENCY"); - const auto xlat_center_freq_id = pre.register_required_variable("OFFSET"); + const auto xlat_center_freq_id = + pre.register_required_variable("OFFSET"); const auto RF_id = pre.register_variable("RF"); const auto IF_id = pre.register_variable("IF"); const auto BB_id = pre.register_variable("BB"); - const auto device_string_id = pre.register_variable("DEVICE_STRING"); - + const auto device_string_id = + pre.register_variable("DEVICE_STRING"); + const auto parsed_and_validated_pre = pre.parse_and_validate(); - + if (parsed_and_validated_pre.ok()) { freq = parsed_and_validated_pre.get(freq_id); xlat_center_freq = parsed_and_validated_pre.get(xlat_center_freq_id); @@ -81,7 +84,7 @@ int main(int argc, char **argv) { } else { std::cout << parsed_and_validated_pre.warning_message(); std::cout << parsed_and_validated_pre.error_message(); - + return EXIT_FAILURE; } @@ -90,7 +93,10 @@ int main(int argc, char **argv) { transition_bw); gr_vector_float fir1_taps = gr::filter::firdes::high_pass(1.0, samp_rate / decimation, 100, 50); - gr_vector_float fir2_taps = std::vector({0.002334677756186128, 0.01938096025639799, 0.14012609258404307, 0.25997995536747043, 0.24015818184610402, 0.25997995536747043, 0.14012609258404307, 0.01938096025639799, 0.002334677756186128}); + gr_vector_float fir2_taps = std::vector( + {0.002334677756186128, 0.01938096025639799, 0.14012609258404307, + 0.25997995536747043, 0.24015818184610402, 0.25997995536747043, + 0.14012609258404307, 0.01938096025639799, 0.002334677756186128}); tb = gr::make_top_block("fg"); @@ -109,19 +115,19 @@ int main(int argc, char **argv) { xlat = gr::filter::freq_xlating_fir_filter_ccc::make( decimation, xlat_taps, xlat_center_freq, samp_rate); resampler = gr::filter::rational_resampler_ccc::make( - rs_interpolation, rs_decimation, std::vector(), 0.4); + rs_interpolation, rs_decimation, std::vector(), 0.4); demod1 = gr::analog::quadrature_demod_cf::make(1.0); fir1 = gr::filter::fir_filter_fff::make(1, fir1_taps); hilbert = gr::filter::hilbert_fc::make(65); demod2 = gr::analog::quadrature_demod_cf::make(4.0); - addConst = gr::blocks::add_const_ff::make(-1.5*M_PI); + addConst = gr::blocks::add_const_ff::make(-1.5 * M_PI); fir2 = gr::filter::fir_filter_fff::make(1, fir2_taps); clockRecovery = gr::digital::clock_recovery_mm_ff::make( sps, 0.25f * 0.175f * 0.175f, 0.5, 0.175, 0.01); multiplyConst = gr::blocks::multiply_const_ff::make(-1.0f); slicer = gr::digital::binary_slicer_fb::make(); - // set this to 24 to receive all possible telegrams. - // set it to 13 to receive not more data then needed for R09.16 + // set this to 24 to receive all possible telegrams. + // set it to 13 to receive not more data then needed for R09.16 correlate = gr::reveng::correlate_access_code_bb_ts_fl::make( "1111110000000001", 1, "packet_len", 13); taggedStreamToPdu = gr::blocks::tagged_stream_to_pdu::make( @@ -160,7 +166,7 @@ int main(int argc, char **argv) { tb->start(); - tb->wait(); + tb->wait(); return EXIT_SUCCESS; } diff --git a/src/rational_resampler.h b/src/rational_resampler.h index 7fdcfd9..2db874c 100644 --- a/src/rational_resampler.h +++ b/src/rational_resampler.h @@ -14,7 +14,6 @@ #include #include - namespace gr { namespace filter { @@ -55,36 +54,38 @@ namespace filter { * prefilter. */ template -class FILTER_API rational_resampler : virtual public block -{ +class FILTER_API rational_resampler : virtual public block { public: - typedef boost::shared_ptr> sptr; + typedef boost::shared_ptr> sptr; - /*! - * Make a rational resampling FIR filter. - * - * \param interpolation The integer interpolation rate of the filter - * \param decimation The integer decimation rate of the filter - * \param taps The filter taps to control images and aliases - * \param fractional_bw The fractional bandwidth of the filter (0 to 0.5) - */ - static sptr make(unsigned interpolation, - unsigned decimation, - const std::vector& taps = std::vector(), - float fractional_bw = 0.0); + /*! + * Make a rational resampling FIR filter. + * + * \param interpolation The integer interpolation rate of the filter + * \param decimation The integer decimation rate of the filter + * \param taps The filter taps to control images and aliases + * \param fractional_bw The fractional bandwidth of the filter (0 to 0.5) + */ + static sptr make(unsigned interpolation, unsigned decimation, + const std::vector &taps = std::vector(), + float fractional_bw = 0.0); - virtual unsigned interpolation() const = 0; - virtual unsigned decimation() const = 0; + virtual unsigned interpolation() const = 0; + virtual unsigned decimation() const = 0; - virtual void set_taps(const std::vector& taps) = 0; - virtual std::vector taps() const = 0; + virtual void set_taps(const std::vector &taps) = 0; + virtual std::vector taps() const = 0; }; -typedef rational_resampler rational_resampler_ccc; -typedef rational_resampler rational_resampler_ccf; -typedef rational_resampler rational_resampler_fcc; +typedef rational_resampler + rational_resampler_ccc; +typedef rational_resampler + rational_resampler_ccf; +typedef rational_resampler + rational_resampler_fcc; typedef rational_resampler rational_resampler_fff; typedef rational_resampler rational_resampler_fsf; -typedef rational_resampler rational_resampler_scc; +typedef rational_resampler + rational_resampler_scc; } /* namespace filter */ } /* namespace gr */ diff --git a/src/rational_resampler_impl.cc b/src/rational_resampler_impl.cc index 0c46c94..9e8b32a 100644 --- a/src/rational_resampler_impl.cc +++ b/src/rational_resampler_impl.cc @@ -17,241 +17,232 @@ #include #include #include -#include #include +#include namespace gr { namespace filter { - /** - * @brief Given the interpolation rate, decimation rate and a fractional bandwidth, - design a set of taps. + * @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) + * @param fractional_bw fractional bandwidth in (0, 0.5) 0.4 works well. + (float) */ template std::vector 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_type::WIN_KAISER, - beta); /* beta*/ + 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_type::WIN_KAISER, beta); /* beta*/ } template <> -std::vector design_resampler_filter(const unsigned interpolation, - const unsigned decimation, - const float fractional_bw) -{ - auto real_taps = - design_resampler_filter(interpolation, decimation, fractional_bw); - - std::vector cplx_taps(real_taps.size()); - for (size_t i = 0; i < real_taps.size(); i++) { - cplx_taps[i] = real_taps[i]; - } - - return cplx_taps; +std::vector +design_resampler_filter(const unsigned interpolation, + const unsigned decimation, + const float fractional_bw) { + auto real_taps = + design_resampler_filter(interpolation, decimation, fractional_bw); + + std::vector 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 typename rational_resampler::sptr rational_resampler::make(unsigned interpolation, unsigned decimation, - const std::vector& taps, - float fractional_bw) -{ - return gnuradio::get_initial_sptr(new rational_resampler_impl( - interpolation, decimation, taps, fractional_bw)); + const std::vector &taps, + float fractional_bw) { + return gnuradio::get_initial_sptr( + new rational_resampler_impl(interpolation, decimation, + taps, fractional_bw)); } template rational_resampler_impl::rational_resampler_impl( - unsigned interpolation, - unsigned decimation, - const std::vector& taps, + unsigned interpolation, unsigned decimation, const std::vector &taps, float fractional_bw) : block("rational_resampler", io_signature::make(1, 1, sizeof(IN_T)), - io_signature::make(1, 1, sizeof(OUT_T))) -{ - if (interpolation == 0) { - throw std::out_of_range( - "rational_resampler_impl: interpolation must be > 0"); - } - if (decimation == 0) { - throw std::out_of_range( - "rational_resampler_impl: 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; + io_signature::make(1, 1, sizeof(OUT_T))) { + if (interpolation == 0) { + throw std::out_of_range( + "rational_resampler_impl: interpolation must be > 0"); + } + if (decimation == 0) { + throw std::out_of_range( + "rational_resampler_impl: 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( + this->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 staps; + if (taps.size() == 0) { + if (fractional_bw <= 0) { + fractional_bw = 0.4; } - - auto d = GR_GCD(interpolation, decimation); - - if (taps.size() && (d > 1)) { - GR_LOG_INFO( - this->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 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(interpolation, decimation, fractional_bw); - } else { - staps = taps; - } - - d_decimation = decimation; - - 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(1, std::vector()); - } - - set_taps(staps); - install_taps(d_new_taps); + // 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(interpolation, decimation, + fractional_bw); + } else { + staps = taps; + } + + d_decimation = decimation; + + 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(1, std::vector()); + } + + set_taps(staps); + install_taps(d_new_taps); } template -void rational_resampler_impl::set_taps(const std::vector& 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); - } +void rational_resampler_impl::set_taps( + const std::vector &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); + assert(d_new_taps.size() % this->interpolation() == 0); } template void rational_resampler_impl::install_taps( - const std::vector& taps) -{ - int nfilters = this->interpolation(); - int nt = taps.size() / nfilters; + const std::vector &taps) { + int nfilters = this->interpolation(); + int nt = taps.size() / nfilters; - assert(nt * nfilters == (int)taps.size()); + assert(nt * nfilters == (int)taps.size()); - std::vector> xtaps(nfilters); + std::vector> xtaps(nfilters); - for (int n = 0; n < nfilters; n++) - xtaps[n].resize(nt); + 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 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]); + for (int n = 0; n < nfilters; n++) + d_firs[n].set_taps(xtaps[n]); - set_history(nt); - d_updated = false; + set_history(nt); + d_updated = false; } template -std::vector rational_resampler_impl::taps() const -{ - return d_new_taps; +std::vector rational_resampler_impl::taps() const { + return d_new_taps; } template void rational_resampler_impl::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; + 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 int rational_resampler_impl::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(input_items[0]); - auto out = reinterpret_cast(output_items[0]); - - if (d_updated) { - install_taps(d_new_taps); - return 0; // history requirement may have increased. - } + 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(input_items[0]); + auto out = reinterpret_cast(output_items[0]); - 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++; - } + 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; + d_ctr = ctr; + this->consume_each(count); + return i; } template class rational_resampler; template class rational_resampler; diff --git a/src/rational_resampler_impl.h b/src/rational_resampler_impl.h index 7835641..4b573cf 100644 --- a/src/rational_resampler_impl.h +++ b/src/rational_resampler_impl.h @@ -11,45 +11,43 @@ #ifndef RATIONAL_RESAMPLER_IMPL_H #define RATIONAL_RESAMPLER_IMPL_H -#include #include "rational_resampler.h" +#include namespace gr { namespace filter { template -class FILTER_API rational_resampler_impl : public rational_resampler -{ +class FILTER_API rational_resampler_impl + : public rational_resampler { private: - unsigned d_history; - unsigned d_decimation; - unsigned d_ctr = 0; - std::vector d_new_taps; - std::vector> d_firs; - bool d_updated = false; + unsigned d_history; + unsigned d_decimation; + unsigned d_ctr = 0; + std::vector d_new_taps; + std::vector> d_firs; + bool d_updated = false; - void install_taps(const std::vector& taps); + void install_taps(const std::vector &taps); public: - rational_resampler_impl(unsigned interpolation, - unsigned decimation, - const std::vector& taps, - float fractional_bw); + rational_resampler_impl(unsigned interpolation, unsigned decimation, + const std::vector &taps, float fractional_bw); - unsigned history() const { return d_history; } - void set_history(unsigned history) { d_history = history; } + unsigned history() const { return d_history; } + void set_history(unsigned history) { d_history = history; } - unsigned interpolation() const override { return d_firs.size(); } - unsigned decimation() const override { return d_decimation; } + unsigned interpolation() const override { return d_firs.size(); } + unsigned decimation() const override { return d_decimation; } - void set_taps(const std::vector& taps) override; - std::vector taps() const override; + void set_taps(const std::vector &taps) override; + std::vector taps() const override; - void forecast(int noutput_items, gr_vector_int& ninput_items_required) override; - int general_work(int noutput_items, - gr_vector_int& ninput_items, - gr_vector_const_void_star& input_items, - gr_vector_void_star& output_items) override; + void forecast(int noutput_items, + gr_vector_int &ninput_items_required) override; + int general_work(int noutput_items, gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) override; }; } /* namespace filter */