2

I am trying to build a sink block with FFT and Kurtosis capability using gr_modtool.

The code itself can be compiled without error.

But when I run the flow graph in GRC, it produces following error message.

Generating: '/home/nomo/gr-Kurtosis/kurtosis.py'

Executing: /usr/bin/python3 -u /home/nomo/gr-Kurtosis/kurtosis.py

Traceback (most recent call last):
  File "/home/nomo/gr-Kurtosis/kurtosis.py", line 134, in <module>
    main()
  File "/home/nomo/gr-Kurtosis/kurtosis.py", line 112, in main
    tb = top_block_cls()
  File "/home/nomo/gr-Kurtosis/kurtosis.py", line 81, in __init__
    self.Kurtosis_Kurtosis_c_0 = Kurtosis.Kurtosis_c(1024)
AttributeError: module 'Kurtosis' has no attribute 'Kurtosis_c'

Below is the implmentation source file:

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

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

namespace gr {
  namespace Kurtosis {

    Kurtosis_c::sptr
    Kurtosis_c::make(int fftsize)
    {
      return gnuradio::get_initial_sptr
        (new Kurtosis_c_impl(fftsize));
    }


    /*
     * The private constructor
     */
    Kurtosis_c_impl::Kurtosis_c_impl(int fftsize)
      : gr::sync_block("Kurtosis_c",
              gr::io_signature::make(1, 1, sizeof(gr_complex)),
              gr::io_signature::make(0, 0, 0)),
              d_N(fftsize)
    {
      d_input = (fftw_complex*)fftw_malloc(sizeof(gr_complex) * d_N);
      K = (fftw_complex*)fftw_malloc(sizeof(gr_complex) * d_N);
      d_plan = fftw_plan_dft_1d(d_N, d_input, d_input, FFTW_BACKWARD, FFTW_ESTIMATE);
    }

    /*
     * Our virtual destructor.
     */
    Kurtosis_c_impl::~Kurtosis_c_impl()
    {
    }

    int
    Kurtosis_c_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];

      for(int i = 0; i < noutput_items; i++)
      {
        d_input[i][0] = in[i].real();
        d_input[i][1] = in[i].imag();
      }
      
      fftw_execute(d_plan);
      
      for(int i = 0; i < noutput_items ;i++)
      {   
        S1[i][0] = S1[i][0] + d_input[i][0];
        S1[i][1] = S1[i][1] + d_input[i][1];
        
        S2[i][0] = S2[i][0] + d_input[i][0] * d_input[i][0];
        S2[i][1] = S2[i][1] + d_input[i][1] * d_input[i][1];
        
        S3[i][0] = S3[i][0] + d_input[i][0] * d_input[i][0] * d_input[i][0];
        S3[i][1] = S3[i][1] + d_input[i][1] * d_input[i][1] * d_input[i][1];
        
        S4[i][0] = S4[i][0] + d_input[i][0] * d_input[i][0] * d_input[i][0] * d_input[i][0];
        S4[i][1] = S4[i][1] + d_input[i][1] * d_input[i][1] * d_input[i][1] * d_input[i][1];
        
        dc[i][0] = dc[i][0] + 1;
        dc[i][1] = dc[i][1] + 1;
      }
    
      for(int i = 0; i < noutput_items; i++)
      {
        Myu1[i][0] = S1[i][0] / dc[i][0];
        Myu1[i][1] = S1[i][1] / dc[i][1];
      
        Myu2[i][0] = S2[i][0] / dc[i][0];
        Myu2[i][1] = S2[i][1] / dc[i][1];
      
        Myu3[i][0] = S3[i][0] / dc[i][0];
        Myu3[i][1] = S3[i][1] / dc[i][1];
      
        Myu4[i][0] = S4[i][0] / dc[i][0];
        Myu4[i][1] = S4[i][1] / dc[i][1];
      }

    

      for(int i = 0; i < noutput_items; i++)
      {
        K[i][0] = (Myu4[i][0] - 4 * Myu3[i][0] * Myu1[i][0] + 6 * Myu2[i][0] * Myu1[i][0] 
                   * Myu1[i][0] - 3 * Myu1[i][0] * Myu1[i][0] * Myu1[i][0] * Myu1[i][0]) / 
                    ((Myu2[i][0] - Myu1[i][0] * Myu1[i][0]) * (Myu2[i][0] - Myu1[i][0] 
                      * Myu1[i][0]));
      
        K[i][1] = (Myu4[i][1] - 4 * Myu3[i][1] * Myu1[i][1] + 6 * Myu2[i][1] * Myu1[i][1] 
                   * Myu1[i][1] - 3 * Myu1[i][1] * Myu1[i][1] * Myu1[i][1] * Myu1[i][1]) / 
                    ((Myu2[i][1] - Myu1[i][1] * Myu1[i][1]) * (Myu2[i][1] - Myu1[i][1] 
                      * Myu1[i][1]));
      }

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

  } /* namespace Kurtosis1 */
} /* namespace gr */

The implementation header file is as below:

#ifndef INCLUDED_KURTOSIS1_KURTOSIS_C1_IMPL_H
#define INCLUDED_KURTOSIS1_KURTOSIS_C1_IMPL_H

#include <Kurtosis/Kurtosis_c.h>
#include <fftw3.h>

namespace gr {
  namespace Kurtosis {

    class Kurtosis_c_impl : public Kurtosis_c
    {
     private:
       int d_N;
       
        
       fftw_complex *K;
       fftw_complex *S1;
       fftw_complex *S2;
       fftw_complex *S3; 
       fftw_complex *S4;
       fftw_complex *Myu1;
       fftw_complex *Myu2;
       fftw_complex *Myu3;
       fftw_complex *Myu4;
       fftw_complex *dc;
       
   
      
       fftw_complex *d_input;
       fftw_plan     d_plan;

     public:
      Kurtosis_c_impl(int fftsize);
      ~Kurtosis_c_impl();
     

      // Where all the action really happens
      int work(
              int noutput_items,
              gr_vector_const_void_star &input_items,
              gr_vector_void_star &output_items
      );
    };

  } // namespace Kurtosis1
} // namespace gr

#endif /* INCLUDED_KURTOSIS1_KURTOSIS_C1_IMPL_H */

The following python code is generated when a flow graph is run in GRC

from distutils.version import StrictVersion

if __name__ == '__main__':
    import ctypes
    import sys
    if sys.platform.startswith('linux'):
        try:
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
            x11.XInitThreads()
        except:
            print("Warning: failed to XInitThreads()")

from gnuradio import blocks
import pmt
from gnuradio import gr
from gnuradio.filter import firdes
import sys
import signal
from PyQt5 import Qt
from argparse import ArgumentParser
from gnuradio.eng_arg import eng_float, intx
from gnuradio import eng_notation
import Kurtosis
from gnuradio import qtgui

class kurtosis(gr.top_block, Qt.QWidget):

    def __init__(self):
        gr.top_block.__init__(self, "Not titled yet")
        Qt.QWidget.__init__(self)
        self.setWindowTitle("Not titled yet")
        qtgui.util.check_set_qss()
        try:
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
        except:
            pass
        self.top_scroll_layout = Qt.QVBoxLayout()
        self.setLayout(self.top_scroll_layout)
        self.top_scroll = Qt.QScrollArea()
        self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
        self.top_scroll_layout.addWidget(self.top_scroll)
        self.top_scroll.setWidgetResizable(True)
        self.top_widget = Qt.QWidget()
        self.top_scroll.setWidget(self.top_widget)
        self.top_layout = Qt.QVBoxLayout(self.top_widget)
        self.top_grid_layout = Qt.QGridLayout()
        self.top_layout.addLayout(self.top_grid_layout)

        self.settings = Qt.QSettings("GNU Radio", "kurtosis")

        try:
            if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
                self.restoreGeometry(self.settings.value("geometry").toByteArray())
            else:
                self.restoreGeometry(self.settings.value("geometry"))
        except:
            pass

        ##################################################
        # Variables
        ##################################################
        self.samp_rate = samp_rate = 32000

        ##################################################
        # Blocks
        ##################################################
        self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True)
        self.blocks_file_source_0 = blocks.file_source(gr.sizeof_gr_complex*1, '/home/nomo/gr-Kurtosis/b200_220215_0718_30_40_6000_TEST.dat', True, 0, 0)
        self.blocks_file_source_0.set_begin_tag(pmt.PMT_NIL)
        self.Kurtosis_Kurtosis_c_0 = Kurtosis.Kurtosis_c(1024)



        ##################################################
        # Connections
        ##################################################
        self.connect((self.blocks_file_source_0, 0), (self.blocks_throttle_0, 0))
        self.connect((self.blocks_throttle_0, 0), (self.Kurtosis_Kurtosis_c_0, 0))

    def closeEvent(self, event):
        self.settings = Qt.QSettings("GNU Radio", "kurtosis")
        self.settings.setValue("geometry", self.saveGeometry())
        event.accept()

    def get_samp_rate(self):
        return self.samp_rate

    def set_samp_rate(self, samp_rate):
        self.samp_rate = samp_rate
        self.blocks_throttle_0.set_sample_rate(self.samp_rate)



def main(top_block_cls=kurtosis, options=None):

    if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
        style = gr.prefs().get_string('qtgui', 'style', 'raster')
        Qt.QApplication.setGraphicsSystem(style)
    qapp = Qt.QApplication(sys.argv)

    tb = top_block_cls()
    tb.start()
    tb.show()

    def sig_handler(sig=None, frame=None):
        Qt.QApplication.quit()

    signal.signal(signal.SIGINT, sig_handler)
    signal.signal(signal.SIGTERM, sig_handler)

    timer = Qt.QTimer()
    timer.start(500)
    timer.timeout.connect(lambda: None)

    def quitting():
        tb.stop()
        tb.wait()
    qapp.aboutToQuit.connect(quitting)
    qapp.exec_()


if __name__ == '__main__':
    main()

Question: How to solve this error?

Thank you in advance for any guidance you may be able to provide.

yu_20
  • 29
  • 5
  • Have you checked the generated `python` file? Is something out of order there, is it what you would expect? – Melon May 17 '22 at 08:06
  • Yes, I have checked. But, I don't know why I get "AtrributeError". – yu_20 May 18 '22 at 02:58
  • Do you have `Kurtosis_c` defined in the generated python file? – Melon May 18 '22 at 05:33
  • I added the generated python code. I would be appreciated if you could check the code. I don't think it is defined. Isn't it automatically defined when the flow graph is executed? – yu_20 May 18 '22 at 06:59
  • So you are importing module `Kurtosis` and it does not have `Kurtosis_c`. What is inside `Kurtosis` module? – Melon May 18 '22 at 08:37
  • I must apologize for the delay in my reply. I can't find the `Kurtosis` module that is imported when I run the flow graph... – yu_20 May 19 '22 at 04:59
  • `sudo find / -name Kurtosis.py` – Melon May 19 '22 at 05:23
  • I searched for `Kurtosis.py` using `find`command, but could not find it. – yu_20 May 19 '22 at 08:11
  • All I can suggest at the moment, is to make you implementation as minimal as possible and check if the problem reproduces. If not, gradually add code and check when/where it breaks. – Melon May 19 '22 at 08:42
  • I understand. I will do so. Thank you very much for all the information. – yu_20 May 19 '22 at 09:14

1 Answers1

1

There are two probable reasons:

  1. You don't have swig installed on your system and the python binding does not get generated.

#include <fftw3.h>

  1. You don't link all external libraries - fftw3 in your case.

Open your top-level CMakeLists.txt and add

find_package(FFTW3)

then open lib/CMakeLists.txt and add fftw3::fftw3 to target_link_libraries list, something like

target_link_libraries(gnuradio-Kurtosis PUBLIC
  gnuradio::gnuradio-runtime fftw3::fftw3 
)

One more thing is to improve the error handling - open python/__init__.py and change ImportError to ModuleNotFoundError

After all these changes you need to reinstall your OOT module.

Vasil Velichkov
  • 1,236
  • 11
  • 17
  • I must apologize for the delay in my reply. Thank you for your response. I made the change and reinstalled, and `AttributeError` was resolved. However, I got the following error. `ImportError: /usr/local/lib/x86_64-linux-gnu/libgnuradio-Kurtosis.so.1.0.0git: undefined symbol: fftw_execute` – yu_20 May 24 '22 at 05:15
  • @yu_20 You need to link with the library that provides `fftw_execute` function. I'm not familiar with these `fftw3` libraries so I can't tell you which is the correct library. It seems in my answer I've mixed `fftw3` with `fftw3f`, I'm going to edit my answer to fix this – Vasil Velichkov May 25 '22 at 10:00
  • I see. I understand. Thank you very much for all the information. I will investigate a little more. – yu_20 May 26 '22 at 10:22