summaryrefslogtreecommitdiff
path: root/gr-fft
diff options
context:
space:
mode:
authorThomas Habets <thomas@habets.se>2020-01-03 18:16:03 +0000
committerMartin Braun <martin.braun@ettus.com>2020-01-04 23:35:20 -0800
commiteae138c160a6fb2834eb5eda392d0ed25fbc48d0 (patch)
tree4746bd59abb3d00658a0ed56630265b652338c4b /gr-fft
parentebe8ffc58503a1b4f167a13d5663e8849379eaaf (diff)
gr-fft: Modernize fft code
* Add const where possible * Disable copy assignment and copy constructor where not safe (for now) to copy * Manual memory management -> smart pointers and vectors * De-pointerify where possible * assert -> static_assert
Diffstat (limited to 'gr-fft')
-rw-r--r--gr-fft/include/gnuradio/fft/fft.h54
-rw-r--r--gr-fft/include/gnuradio/fft/goertzel.h2
-rw-r--r--gr-fft/include/gnuradio/fft/goertzel_fc.h4
-rw-r--r--gr-fft/lib/fft.cc80
-rw-r--r--gr-fft/lib/fft_vcc_fftw.cc22
-rw-r--r--gr-fft/lib/fft_vcc_fftw.h10
-rw-r--r--gr-fft/lib/fft_vfc_fftw.cc18
-rw-r--r--gr-fft/lib/fft_vfc_fftw.h8
-rw-r--r--gr-fft/lib/goertzel.cc2
-rw-r--r--gr-fft/lib/goertzel_fc_impl.cc6
-rw-r--r--gr-fft/lib/goertzel_fc_impl.h6
11 files changed, 90 insertions, 122 deletions
diff --git a/gr-fft/include/gnuradio/fft/fft.h b/gr-fft/include/gnuradio/fft/fft.h
index d6f52d4c7d..749a5e874c 100644
--- a/gr-fft/include/gnuradio/fft/fft.h
+++ b/gr-fft/include/gnuradio/fft/fft.h
@@ -29,6 +29,7 @@
#include <gnuradio/fft/api.h>
#include <gnuradio/gr_complex.h>
+#include <volk/volk_alloc.hh>
#include <boost/thread.hpp>
namespace gr {
@@ -36,6 +37,7 @@ namespace fft {
/*! \brief Helper function for allocating complex* buffers
+ * TODO: Remove once the single user of this stops using it.
*/
FFT_API gr_complex* malloc_complex(int size);
@@ -48,6 +50,7 @@ FFT_API float* malloc_float(int size);
FFT_API double* malloc_double(int size);
/*! \brief Helper function for freeing fft buffers
+ * TODO: Remove once the single user of this stops using it.
*/
FFT_API void free(void* b);
@@ -71,14 +74,17 @@ public:
*/
class FFT_API fft_complex
{
- int d_fft_size;
+ const int d_fft_size;
int d_nthreads;
- gr_complex* d_inbuf;
- gr_complex* d_outbuf;
+ volk::vector<gr_complex> d_inbuf;
+ volk::vector<gr_complex> d_outbuf;
void* d_plan;
public:
fft_complex(int fft_size, bool forward = true, int nthreads = 1);
+ // Copy disabled due to d_plan.
+ fft_complex(const fft_complex&) = delete;
+ fft_complex& operator=(const fft_complex&) = delete;
virtual ~fft_complex();
/*
@@ -86,11 +92,11 @@ public:
* into which input and output take place. It's done this way in
* order to ensure optimal alignment for SIMD instructions.
*/
- gr_complex* get_inbuf() const { return d_inbuf; }
- gr_complex* get_outbuf() const { return d_outbuf; }
+ gr_complex* get_inbuf() { return d_inbuf.data(); }
+ gr_complex* get_outbuf() { return d_outbuf.data(); }
- int inbuf_length() const { return d_fft_size; }
- int outbuf_length() const { return d_fft_size; }
+ int inbuf_length() const { return d_inbuf.size(); }
+ int outbuf_length() const { return d_outbuf.size(); }
/*!
* Set the number of threads to use for caclulation.
@@ -115,14 +121,17 @@ public:
*/
class FFT_API fft_real_fwd
{
- int d_fft_size;
+ const int d_fft_size;
int d_nthreads;
- float* d_inbuf;
- gr_complex* d_outbuf;
+ volk::vector<float> d_inbuf;
+ volk::vector<gr_complex> d_outbuf;
void* d_plan;
public:
fft_real_fwd(int fft_size, int nthreads = 1);
+ // Copy disabled due to d_plan.
+ fft_real_fwd(const fft_real_fwd&) = delete;
+ fft_real_fwd& operator=(const fft_real_fwd&) = delete;
virtual ~fft_real_fwd();
/*
@@ -130,11 +139,11 @@ public:
* into which input and output take place. It's done this way in
* order to ensure optimal alignment for SIMD instructions.
*/
- float* get_inbuf() const { return d_inbuf; }
- gr_complex* get_outbuf() const { return d_outbuf; }
+ float* get_inbuf() { return d_inbuf.data(); }
+ gr_complex* get_outbuf() { return d_outbuf.data(); }
- int inbuf_length() const { return d_fft_size; }
- int outbuf_length() const { return d_fft_size / 2 + 1; }
+ int inbuf_length() const { return d_inbuf.size(); }
+ int outbuf_length() const { return d_outbuf.size(); }
/*!
* Set the number of threads to use for caclulation.
@@ -159,14 +168,17 @@ public:
*/
class FFT_API fft_real_rev
{
- int d_fft_size;
+ const int d_fft_size;
int d_nthreads;
- gr_complex* d_inbuf;
- float* d_outbuf;
+ volk::vector<gr_complex> d_inbuf;
+ volk::vector<float> d_outbuf;
void* d_plan;
public:
fft_real_rev(int fft_size, int nthreads = 1);
+ // Copy disabled due to d_plan.
+ fft_real_rev(const fft_real_rev&) = delete;
+ fft_real_rev& operator=(const fft_real_rev&) = delete;
virtual ~fft_real_rev();
/*
@@ -174,11 +186,11 @@ public:
* into which input and output take place. It's done this way in
* order to ensure optimal alignment for SIMD instructions.
*/
- gr_complex* get_inbuf() const { return d_inbuf; }
- float* get_outbuf() const { return d_outbuf; }
+ gr_complex* get_inbuf() { return d_inbuf.data(); }
+ float* get_outbuf() { return d_outbuf.data(); }
- int inbuf_length() const { return d_fft_size / 2 + 1; }
- int outbuf_length() const { return d_fft_size; }
+ int inbuf_length() const { return d_inbuf.size(); }
+ int outbuf_length() const { return d_outbuf.size(); }
/*!
* Set the number of threads to use for caclulation.
diff --git a/gr-fft/include/gnuradio/fft/goertzel.h b/gr-fft/include/gnuradio/fft/goertzel.h
index 38b03a1b25..e807e23cbc 100644
--- a/gr-fft/include/gnuradio/fft/goertzel.h
+++ b/gr-fft/include/gnuradio/fft/goertzel.h
@@ -41,7 +41,7 @@ public:
void set_params(int rate, int len, float freq);
// Process a input array
- gr_complex batch(float* in);
+ gr_complex batch(const float* in);
// Process sample by sample
void input(const float& in);
diff --git a/gr-fft/include/gnuradio/fft/goertzel_fc.h b/gr-fft/include/gnuradio/fft/goertzel_fc.h
index 75d97380fd..3539697561 100644
--- a/gr-fft/include/gnuradio/fft/goertzel_fc.h
+++ b/gr-fft/include/gnuradio/fft/goertzel_fc.h
@@ -45,9 +45,9 @@ public:
virtual void set_rate(int rate) = 0;
- virtual float freq() = 0;
+ virtual float freq() const = 0;
- virtual int rate() = 0;
+ virtual int rate() const = 0;
};
} /* namespace fft */
diff --git a/gr-fft/lib/fft.cc b/gr-fft/lib/fft.cc
index bc73e31b2f..c48fb6dc47 100644
--- a/gr-fft/lib/fft.cc
+++ b/gr-fft/lib/fft.cc
@@ -47,7 +47,6 @@ static int my_fftw_read_char(void* f) { return fgetc((FILE*)f); }
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <cassert>
#include <stdexcept>
#include <boost/filesystem/operations.hpp>
@@ -167,37 +166,24 @@ static void export_wisdom()
// ----------------------------------------------------------------
fft_complex::fft_complex(int fft_size, bool forward, int nthreads)
+ : d_fft_size(fft_size), d_nthreads(nthreads), d_inbuf(fft_size), d_outbuf(fft_size)
{
// Hold global mutex during plan construction and destruction.
planner::scoped_lock lock(planner::mutex());
- assert(sizeof(fftwf_complex) == sizeof(gr_complex));
+ static_assert(sizeof(fftwf_complex) == sizeof(gr_complex));
if (fft_size <= 0) {
throw std::out_of_range("fft_impl_fftw: invalid fft_size");
}
- d_fft_size = fft_size;
- d_inbuf = (gr_complex*)volk_malloc(sizeof(gr_complex) * inbuf_length(),
- volk_get_alignment());
- if (d_inbuf == 0) {
- throw std::runtime_error("volk_malloc");
- }
- d_outbuf = (gr_complex*)volk_malloc(sizeof(gr_complex) * outbuf_length(),
- volk_get_alignment());
- if (d_outbuf == 0) {
- volk_free(d_inbuf);
- throw std::runtime_error("volk_malloc");
- }
-
- d_nthreads = nthreads;
config_threading(nthreads);
lock_wisdom();
import_wisdom(); // load prior wisdom from disk
d_plan = fftwf_plan_dft_1d(fft_size,
- reinterpret_cast<fftwf_complex*>(d_inbuf),
- reinterpret_cast<fftwf_complex*>(d_outbuf),
+ reinterpret_cast<fftwf_complex*>(d_inbuf.data()),
+ reinterpret_cast<fftwf_complex*>(d_outbuf.data()),
forward ? FFTW_FORWARD : FFTW_BACKWARD,
FFTW_MEASURE);
@@ -215,8 +201,6 @@ fft_complex::~fft_complex()
planner::scoped_lock lock(planner::mutex());
fftwf_destroy_plan((fftwf_plan)d_plan);
- volk_free(d_inbuf);
- volk_free(d_outbuf);
}
void fft_complex::set_nthreads(int n)
@@ -236,36 +220,28 @@ void fft_complex::execute() { fftwf_execute((fftwf_plan)d_plan); }
// ----------------------------------------------------------------
fft_real_fwd::fft_real_fwd(int fft_size, int nthreads)
+ : d_fft_size(fft_size),
+ d_nthreads(nthreads),
+ d_inbuf(fft_size),
+ d_outbuf(fft_size / 2 + 1)
{
// Hold global mutex during plan construction and destruction.
planner::scoped_lock lock(planner::mutex());
- assert(sizeof(fftwf_complex) == sizeof(gr_complex));
+ static_assert(sizeof(fftwf_complex) == sizeof(gr_complex));
if (fft_size <= 0) {
throw std::out_of_range("gr::fft: invalid fft_size");
}
- d_fft_size = fft_size;
- d_inbuf = (float*)volk_malloc(sizeof(float) * inbuf_length(), volk_get_alignment());
- if (d_inbuf == 0) {
- throw std::runtime_error("volk_malloc");
- }
-
- d_outbuf = (gr_complex*)volk_malloc(sizeof(gr_complex) * outbuf_length(),
- volk_get_alignment());
- if (d_outbuf == 0) {
- volk_free(d_inbuf);
- throw std::runtime_error("volk_malloc");
- }
-
- d_nthreads = nthreads;
config_threading(nthreads);
lock_wisdom();
import_wisdom(); // load prior wisdom from disk
- d_plan = fftwf_plan_dft_r2c_1d(
- fft_size, d_inbuf, reinterpret_cast<fftwf_complex*>(d_outbuf), FFTW_MEASURE);
+ d_plan = fftwf_plan_dft_r2c_1d(fft_size,
+ d_inbuf.data(),
+ reinterpret_cast<fftwf_complex*>(d_outbuf.data()),
+ FFTW_MEASURE);
if (d_plan == NULL) {
fprintf(stderr, "gr::fft::fft_real_fwd: error creating plan\n");
@@ -281,8 +257,6 @@ fft_real_fwd::~fft_real_fwd()
planner::scoped_lock lock(planner::mutex());
fftwf_destroy_plan((fftwf_plan)d_plan);
- volk_free(d_inbuf);
- volk_free(d_outbuf);
}
void fft_real_fwd::set_nthreads(int n)
@@ -303,30 +277,20 @@ void fft_real_fwd::execute() { fftwf_execute((fftwf_plan)d_plan); }
// ----------------------------------------------------------------
fft_real_rev::fft_real_rev(int fft_size, int nthreads)
+ : d_fft_size(fft_size),
+ d_nthreads(nthreads),
+ d_inbuf(fft_size / 2 + 1),
+ d_outbuf(fft_size)
{
// Hold global mutex during plan construction and destruction.
planner::scoped_lock lock(planner::mutex());
- assert(sizeof(fftwf_complex) == sizeof(gr_complex));
+ static_assert(sizeof(fftwf_complex) == sizeof(gr_complex));
if (fft_size <= 0) {
throw std::out_of_range("gr::fft::fft_real_rev: invalid fft_size");
}
- d_fft_size = fft_size;
- d_inbuf = (gr_complex*)volk_malloc(sizeof(gr_complex) * inbuf_length(),
- volk_get_alignment());
- if (d_inbuf == 0) {
- throw std::runtime_error("volk_malloc");
- }
-
- d_outbuf = (float*)volk_malloc(sizeof(float) * outbuf_length(), volk_get_alignment());
- if (d_outbuf == 0) {
- volk_free(d_inbuf);
- throw std::runtime_error("volk_malloc");
- }
-
- d_nthreads = nthreads;
config_threading(nthreads);
lock_wisdom();
import_wisdom(); // load prior wisdom from disk
@@ -334,8 +298,10 @@ fft_real_rev::fft_real_rev(int fft_size, int nthreads)
// FIXME If there's ever a chance that the planning functions
// will be called in multiple threads, we've got to ensure single
// threaded access. They are not thread-safe.
- d_plan = fftwf_plan_dft_c2r_1d(
- fft_size, reinterpret_cast<fftwf_complex*>(d_inbuf), d_outbuf, FFTW_MEASURE);
+ d_plan = fftwf_plan_dft_c2r_1d(fft_size,
+ reinterpret_cast<fftwf_complex*>(d_inbuf.data()),
+ d_outbuf.data(),
+ FFTW_MEASURE);
if (d_plan == NULL) {
fprintf(stderr, "gr::fft::fft_real_rev: error creating plan\n");
@@ -351,8 +317,6 @@ fft_real_rev::~fft_real_rev()
planner::scoped_lock lock(planner::mutex());
fftwf_destroy_plan((fftwf_plan)d_plan);
- volk_free(d_inbuf);
- volk_free(d_outbuf);
}
void fft_real_rev::set_nthreads(int n)
diff --git a/gr-fft/lib/fft_vcc_fftw.cc b/gr-fft/lib/fft_vcc_fftw.cc
index 0d9b7d1c6b..404ffc538e 100644
--- a/gr-fft/lib/fft_vcc_fftw.cc
+++ b/gr-fft/lib/fft_vcc_fftw.cc
@@ -52,18 +52,16 @@ fft_vcc_fftw::fft_vcc_fftw(int fft_size,
io_signature::make(1, 1, fft_size * sizeof(gr_complex))),
d_fft_size(fft_size),
d_forward(forward),
+ d_fft(fft_size, forward, nthreads),
d_shift(shift)
{
- d_fft = new fft_complex(d_fft_size, forward, nthreads);
if (!set_window(window))
throw std::runtime_error("fft_vcc: window not the same length as fft_size");
}
-fft_vcc_fftw::~fft_vcc_fftw() { delete d_fft; }
+void fft_vcc_fftw::set_nthreads(int n) { d_fft.set_nthreads(n); }
-void fft_vcc_fftw::set_nthreads(int n) { d_fft->set_nthreads(n); }
-
-int fft_vcc_fftw::nthreads() const { return d_fft->nthreads(); }
+int fft_vcc_fftw::nthreads() const { return d_fft.nthreads(); }
bool fft_vcc_fftw::set_window(const std::vector<float>& window)
{
@@ -90,7 +88,7 @@ int fft_vcc_fftw::work(int noutput_items,
// copy input into optimally aligned buffer
if (!d_window.empty()) {
- gr_complex* dst = d_fft->get_inbuf();
+ gr_complex* dst = d_fft.get_inbuf();
if (!d_forward && d_shift) {
unsigned int offset = (!d_forward && d_shift) ? (d_fft_size / 2) : 0;
int fft_m_offset = d_fft_size - offset;
@@ -103,30 +101,30 @@ int fft_vcc_fftw::work(int noutput_items,
}
} else {
if (!d_forward && d_shift) { // apply an ifft shift on the data
- gr_complex* dst = d_fft->get_inbuf();
+ gr_complex* dst = d_fft.get_inbuf();
unsigned int len = (unsigned int)(floor(
d_fft_size / 2.0)); // half length of complex array
memcpy(&dst[0], &in[len], sizeof(gr_complex) * (d_fft_size - len));
memcpy(&dst[d_fft_size - len], &in[0], sizeof(gr_complex) * len);
} else {
- memcpy(d_fft->get_inbuf(), in, input_data_size);
+ memcpy(d_fft.get_inbuf(), in, input_data_size);
}
}
// compute the fft
- d_fft->execute();
+ d_fft.execute();
// copy result to our output
if (d_forward && d_shift) { // apply a fft shift on the data
unsigned int len = (unsigned int)(ceil(d_fft_size / 2.0));
memcpy(&out[0],
- &d_fft->get_outbuf()[len],
+ &d_fft.get_outbuf()[len],
sizeof(gr_complex) * (d_fft_size - len));
memcpy(&out[d_fft_size - len],
- &d_fft->get_outbuf()[0],
+ &d_fft.get_outbuf()[0],
sizeof(gr_complex) * len);
} else {
- memcpy(out, d_fft->get_outbuf(), output_data_size);
+ memcpy(out, d_fft.get_outbuf(), output_data_size);
}
in += d_fft_size;
diff --git a/gr-fft/lib/fft_vcc_fftw.h b/gr-fft/lib/fft_vcc_fftw.h
index 631e41d9ca..cbf04c8c91 100644
--- a/gr-fft/lib/fft_vcc_fftw.h
+++ b/gr-fft/lib/fft_vcc_fftw.h
@@ -32,11 +32,11 @@ namespace fft {
class FFT_API fft_vcc_fftw : public fft_vcc
{
private:
- fft_complex* d_fft;
- unsigned int d_fft_size;
+ const unsigned int d_fft_size;
+ const bool d_forward;
+ fft_complex d_fft;
std::vector<float> d_window;
- bool d_forward;
- bool d_shift;
+ const bool d_shift;
public:
fft_vcc_fftw(int fft_size,
@@ -45,8 +45,6 @@ public:
bool shift,
int nthreads = 1);
- ~fft_vcc_fftw();
-
void set_nthreads(int n);
int nthreads() const;
bool set_window(const std::vector<float>& window);
diff --git a/gr-fft/lib/fft_vfc_fftw.cc b/gr-fft/lib/fft_vfc_fftw.cc
index b2f2ebc4d7..320d90fcc8 100644
--- a/gr-fft/lib/fft_vfc_fftw.cc
+++ b/gr-fft/lib/fft_vfc_fftw.cc
@@ -47,18 +47,16 @@ fft_vfc_fftw::fft_vfc_fftw(int fft_size,
io_signature::make(1, 1, fft_size * sizeof(float)),
io_signature::make(1, 1, fft_size * sizeof(gr_complex))),
d_fft_size(fft_size),
- d_forward(forward)
+ d_forward(forward),
+ d_fft(fft_size, forward, nthreads)
{
- d_fft = new fft_complex(d_fft_size, d_forward, nthreads);
if (!set_window(window))
throw std::runtime_error("fft_vfc: window not the same length as fft_size");
}
-fft_vfc_fftw::~fft_vfc_fftw() { delete d_fft; }
+void fft_vfc_fftw::set_nthreads(int n) { d_fft.set_nthreads(n); }
-void fft_vfc_fftw::set_nthreads(int n) { d_fft->set_nthreads(n); }
-
-int fft_vfc_fftw::nthreads() const { return d_fft->nthreads(); }
+int fft_vfc_fftw::nthreads() const { return d_fft.nthreads(); }
bool fft_vfc_fftw::set_window(const std::vector<float>& window)
{
@@ -84,20 +82,20 @@ int fft_vfc_fftw::work(int noutput_items,
// copy input into optimally aligned buffer
if (!d_window.empty()) {
- gr_complex* dst = d_fft->get_inbuf();
+ gr_complex* dst = d_fft.get_inbuf();
for (unsigned int i = 0; i < d_fft_size; i++) // apply window
dst[i] = in[i] * d_window[i];
} else {
- gr_complex* dst = d_fft->get_inbuf();
+ gr_complex* dst = d_fft.get_inbuf();
for (unsigned int i = 0; i < d_fft_size; i++) // float to complex conversion
dst[i] = in[i];
}
// compute the fft
- d_fft->execute();
+ d_fft.execute();
// copy result to output stream
- memcpy(out, d_fft->get_outbuf(), output_data_size);
+ memcpy(out, d_fft.get_outbuf(), output_data_size);
in += d_fft_size;
out += d_fft_size;
diff --git a/gr-fft/lib/fft_vfc_fftw.h b/gr-fft/lib/fft_vfc_fftw.h
index 47c1ca529c..acee4c38bf 100644
--- a/gr-fft/lib/fft_vfc_fftw.h
+++ b/gr-fft/lib/fft_vfc_fftw.h
@@ -32,10 +32,10 @@ namespace fft {
class FFT_API fft_vfc_fftw : public fft_vfc
{
private:
- fft_complex* d_fft;
- unsigned int d_fft_size;
+ const unsigned int d_fft_size;
+ const bool d_forward;
+ fft_complex d_fft;
std::vector<float> d_window;
- bool d_forward;
public:
fft_vfc_fftw(int fft_size,
@@ -43,8 +43,6 @@ public:
const std::vector<float>& window,
int nthreads = 1);
- ~fft_vfc_fftw();
-
void set_nthreads(int n);
int nthreads() const;
bool set_window(const std::vector<float>& window);
diff --git a/gr-fft/lib/goertzel.cc b/gr-fft/lib/goertzel.cc
index 96b41b0126..f50ffeb81f 100644
--- a/gr-fft/lib/goertzel.cc
+++ b/gr-fft/lib/goertzel.cc
@@ -45,7 +45,7 @@ void goertzel::set_params(int rate, int len, float freq)
d_processed = 0;
}
-gr_complex goertzel::batch(float* in)
+gr_complex goertzel::batch(const float* in)
{
d_d1 = 0.0;
d_d2 = 0.0;
diff --git a/gr-fft/lib/goertzel_fc_impl.cc b/gr-fft/lib/goertzel_fc_impl.cc
index a8f9d58b5a..4f40de439e 100644
--- a/gr-fft/lib/goertzel_fc_impl.cc
+++ b/gr-fft/lib/goertzel_fc_impl.cc
@@ -40,9 +40,9 @@ goertzel_fc_impl::goertzel_fc_impl(int rate, int len, float freq)
io_signature::make(1, 1, sizeof(float)),
io_signature::make(1, 1, sizeof(gr_complex)),
len),
- d_goertzel(rate, len, freq)
+ d_goertzel(rate, len, freq),
+ d_len(len)
{
- d_len = len;
d_rate = rate;
d_freq = freq;
}
@@ -65,7 +65,7 @@ int goertzel_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];
+ const float* in = static_cast<const float*>(input_items[0]);
gr_complex* out = (gr_complex*)output_items[0];
for (int i = 0; i < noutput_items; i++) {
diff --git a/gr-fft/lib/goertzel_fc_impl.h b/gr-fft/lib/goertzel_fc_impl.h
index c84512b586..e771bc153c 100644
--- a/gr-fft/lib/goertzel_fc_impl.h
+++ b/gr-fft/lib/goertzel_fc_impl.h
@@ -33,7 +33,7 @@ class FFT_API goertzel_fc_impl : public goertzel_fc
{
private:
goertzel d_goertzel;
- int d_len;
+ const int d_len;
float d_freq;
int d_rate;
@@ -45,8 +45,8 @@ public:
void set_freq(float freq);
void set_rate(int rate);
- float freq() { return d_freq; }
- int rate() { return d_rate; }
+ float freq() const { return d_freq; }
+ int rate() const { return d_rate; }
int work(int noutput_items,
gr_vector_const_void_star& input_items,