Search code examples
c++eclipseubuntublockgnuradio

GNU Radio: How to define a "get_*" method inside a sink block


I'd like to code my own sink block with 1 input port and 0 output ports for GNU Radio in C++. I read and followed the steps described here:

I'm using

  • Ubuntu 14.04.3 LTS
  • Eclipse 4.5.1
  • CDT 8.8.0.201509131935
  • GNU Radio 3.7.8

Using gr_modtool I created the new module "jammertrap". Inside it, I created the block "bandpower". This created among others the three files

  • bandpower.h inside /home/sdr/gnuradio/gr-jammertrap/include/jammertrap/
  • bandpower_impl.h inside /home/sdr/gnuradio/gr-jammertrap/lib/
  • bandpower_impl.cc inside /home/sdr/gnuradio/gr-jammertrap/lib/

bandpower.h:

#ifndef INCLUDED_JAMMERTRAP_BANDPOWER_H
#define INCLUDED_JAMMERTRAP_BANDPOWER_H

#include <jammertrap/api.h>
#include <gnuradio/block.h>

namespace gr
{
    namespace jammertrap
    {
        class JAMMERTRAP_API bandpower : virtual public gr::block
        {
            public:
                typedef boost::shared_ptr<bandpower> sptr;

                // Return a shared_ptr to a new instance of jammertrap::bandpower.
                // To avoid accidental use of raw pointers, jammertrap::bandpower's constructor is in a private implementation class.
                // jammertrap::bandpower::make is the public interface for creating new instances.
                static sptr make();
        };
    } // namespace jammertrap
} // namespace gr

#endif /* INCLUDED_JAMMERTRAP_BANDPOWER_H */

bandpower_impl.h:

#ifndef INCLUDED_JAMMERTRAP_BANDPOWER_IMPL_H
#define INCLUDED_JAMMERTRAP_BANDPOWER_IMPL_H

#include <jammertrap/bandpower.h>

namespace gr
{
    namespace jammertrap
    {
        class bandpower_impl : public bandpower
        {
            private:
                double d_bandpower_;

            public:
                bandpower_impl();
                ~bandpower_impl();

                void forecast (int noutput_items, gr_vector_int &ninput_items_required);

                // Where all the action really happens
                int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items);

                // Returns the calculated RMS Bandpower
                double get_bandpower();
        };

    } // namespace jammertrap
} // namespace gr

#endif /* INCLUDED_JAMMERTRAP_BANDPOWER_IMPL_H */

bandpower_impl.cc:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gnuradio/io_signature.h>
#include "bandpower_impl.h"

namespace gr
{
    namespace jammertrap
    {
        bandpower::sptr
        bandpower::make()
        {
            return gnuradio::get_initial_sptr (new bandpower_impl());
        }

        // The private constructor
        bandpower_impl::bandpower_impl() : gr::block("bandpower", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(0, 0, 0))
        {
            d_bandpower_ = 0;
        }

        // Our virtual destructor
        bandpower_impl::~bandpower_impl()
        {}

        void bandpower_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required)
        {
            // ninput_items_required[0] = noutput_items;
        }

        int bandpower_impl::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items)
        {
            const gr_complex *in = (const gr_complex *) input_items[0];

            d_bandpower_ = 0;

            for(int i = 0; i < noutput_items; i++)
            {
                d_bandpower_ += (in[i].real() * in[i].real()) + (in[i].imag() * in[i].imag());
            }
            d_bandpower_ = sqrt(d_bandpower_ / noutput_items);

            // Tell runtime system how many input items we consumed on each input stream.
            consume_each (noutput_items);

            // Tell runtime system how many output items we produced
            return noutput_items;
        }

        double bandpower_impl::get_bandpower()
        {
            return d_bandpower_;
        }
    } /* namespace jammertrap */
} /* namespace gr */

To make and install this new block, I entered the following commands inside /home/sdr/gnuradio/gr-jammertrap/build:

  • cmake ../
  • make
  • make test // Result: "test_jammertrap" and "qa_bandpower" passed successful
  • sudo make install

This sink block "bandpower" should receive items of type gr_complex, compute the average received power and store this value inside the private member "d_bandpower_". Additional I defined the method "get_bandpower()" to get the stored value.

Inside another program, I created a flowgraph class with the two blocks

osmosdr::source:sptr osmosdr_source_;
gr::jammertrap::bandpower::sptr bandpower_measurement_;

and instanced them with

osmosdr_source_ = osmosdr::source::make(std::string());
bandpower_measurement_ = gr::jammertrap::bandpower::make();

After starting the flowgraph, I want to read the calculated bandpower by calling get_bandpower() but Eclipse shows no method "bandpower_measurement_->get_bandpower()"

What have I forgotten to write inside bandpower.h, bandpower_impl.h or bandpower_impl.cc?


Solution

  • The public API of the normal OOT layout is in the bandpower.h, so you must add a

    virtual double get_bandpower() = 0;
    

    in that file.

    Then, you overload/implement that, like you do, in the _impl.cc/_impl.h.

    By the way, I slightly object the math behind your implementation: as noutput_items, ie. the number of input items available, changes depending on buffer fillage / runtime behaviour, your "averaging length" is not constant, which means that if your flow graph runs fast, your buffers will usually be full, and your averaging length high, whilst in a "trickle" situation, the length will be much smaller (down to noutput_items==1, in extreme cases). Hence, the variance of your power estimator will depend on computational aspects.

    That's not a good thing. Better work with a constant number of items you average about. In your case, you can use set_output_multiple (because a sink is also a sync block, this also affects the input multiples) to guarantee you always get a multiple of a fixed number.

    Other than that, there's already blocks that can do what you want:

    three ways to probe signal

    • Probe Avg Mag²: has a method level() which does the same as your get_bandpower (aside from the √(.) )
    • Complex to MagDecimating FIR FilterProbe Signal: does the √(Re²+Im²), before passing it on to a filter with 123 (that was just my arbitrary fixed length) taps of 1/length, and decimating it to one value every average. Result get send to the Signal Probe, which has a signal() method, which does what your get_bandpower does. CPU load of this is relatively small -- it's really just the magnitude finding for every sample, and then 123 real multiplications + 123 real additions per 123 samples,in the filter, all SIMD increased, so basically, less than 1 FMAC per sample.
    • Complex to MagMoving AverageProbe Signal: Names say it all. Nothing magical here.