El proyecto de GNU Radio permite el desarrollo de bloques de procesamiento de señales que pueden ser escritos tanto en lenguaje de programación Python o C++. Este tipo de módulos son conocidos como out-of-tree, ya que, aunque los módulos serán integrados dentro del catálogo de bloques de GNU Radio no se van a integrar al proyecto para su distribución.

Procedimiento.

Para crear un módulo out-of-tree se ejecuta desde una terminal de linux el siguiente comando

$ gr_modtool create
Name of the new module:Tu_modulo
Creating out-of-tree module in ./gr-Tu_modulo... Done.
Use 'gr_modtool add' to add a new block to this currently empty module.

El nombre que se indique para el módulo es con el que va a aparecer en el catálogo de GNU Radio.

Para crear los bloques que integrarán el módulo se utiliza la herramienta gr_modtool en la carpeta raíz del proyecto. En este caso se creará un bloque que realizará la función de un convertidor binario a decimal para los tipos de datos float.

$ cd gr-Tu_modulo
$ gr_modtool add
GNU Radio module name identified: Tu_modulo
Enter code type: decimator
Language: C++
Enter name of block/code (without module name prefix): bin2dec_ff
Block/code identifier: bin2dec_ff
Enter valid argument list, including default arguments: int vec_size
Add Python QA code? [Y/n]
Add C++ QA code? [y/N]
Adding file 'lib/bin2dec_ff_impl.h'...
Adding file 'lib/bin2dec_ff_impl.cc'...
Adding file 'include/Tu_modulo/bin2dec_ff.h'...
Editing swig/Tu_modulo_swig.i...
Adding file 'python/qa_bin2dec_ff.py'...
Editing python/CMakeLists.txt...
Adding file 'grc/Tu_modulo_bin2dec_ff.xml'...
Editing grc/CMakeLists.txt...

y a su vez, se desarrollará un bloque que realice la operación inversa, esto es, un convertidor decimal a binario.

$ gr_modtool add
GNU Radio module name identified: Tu_modulo
Enter code type: interpolator
Language: C++
Enter name of block/code (without module name prefix): dec2bin_ff
Block/code identifier: dec2bin_ff
Enter valid argument list, including default arguments: int vec_size
Add Python QA code? [Y/n]
Add C++ QA code? [y/N]
Adding file 'lib/dec2bin_ff_impl.h'...
Adding file 'lib/dec2bin_ff_impl.cc'...
Adding file 'include/Tu_modulo/dec2bin_ff.h'...
Editing swig/Tu_modulo_swig.i...
Adding file 'python/qa_dec2bin_ff.py'...
Editing python/CMakeLists.txt...
Adding file 'grc/Tu_modulo_dec2bin_ff.xml'...
Editing grc/CmakeLists.txt...

de esta manera se han creado dos bloques, un decimador para el convertidor binario a decimal y un interpolador para el decimal a binario.

Los archivos donde se realizará la programación en código c++ se ubican en ~/gr-Tu_modulo/lib y se llama dec2bin_ff_impl.cc para el convertidor decimal a binario y bin2dec_ff_impl.cc para el convertidor binario a decimal.

Convertidor binario a decimal

Dentro del archivo bin2dec_ff_impl.cc hay que indicar las librerías que se utilizarán, en este caso sólo requerimos la librería math.h, después de los include ya definidos agregamos:

#include <math.h>

En la sección de bin2dec_ff_impl::bin2dec_ff_impl(int vec_size) se definen la cantidad de flujos de datos que manejará el bloque de procesamiento, en este caso, sólo es un flujo de entrada y uno de salida, el factor de decimación definido por la variable vec_size y además se asigna a la variable chunk el valor de vec_size, ya que vec_size representa la cantidad de bits que se requieren para convertir el número a decimal.

bin2dec_ff_impl::bin2dec_ff_impl(int vec_size)
  : gr::sync_decimator("bin2dec_ff",
          gr::io_signature::make(1, 1, sizeof(float)),
          gr::io_signature::make(1, 1, sizeof(float)), vec_size)
{
	chunk = vec_size;
}

En la parte correspondiente a // Do <+signal processing+> es donde se realiza el procesamiento de datos que manipulará el bloque, para la conversión de binario a decimal se desarrolla el código de programación en C++, quedando de la siguiente manera:

int
bin2dec_ff_impl::work(int noutput_items,
		  gr_vector_const_void_star &input_items,
		  gr_vector_void_star &output_items)
{
    const float *in = (const float *) input_items[0];
    float *out = (float *) output_items[0];

    int j = 0, a = 0;
    for (int i = 0; i < noutput_items * chunk; i = i + chunk){
    	out[j] = 0;
    	a = chunk - 1;
    	for (int k = 0; k < chunk; k++){
    		out[j] = out[j] + (in[i + k] * pow (2,a));
    		a = a - 1;
    	}
    	j++;
    }

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

El archivo quedaría de la siguiente forma, ya con todas las modificaciones incluidas:

/* -*- c++ -*- */
/*
 * Copyright 2014 IVAN RODRIGUEZ.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

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

#include <gnuradio/io_signature.h>
#include "bin2dec_ff_impl.h"
#include <math.h>

namespace gr {
  namespace Tu_modulo {

    bin2dec_ff::sptr
    bin2dec_ff::make(int vec_size)
    {
      return gnuradio::get_initial_sptr
        (new bin2dec_ff_impl(vec_size));
    }

    /*
     * The private constructor
     */
    bin2dec_ff_impl::bin2dec_ff_impl(int vec_size)
      : gr::sync_decimator("bin2dec_ff",
              gr::io_signature::make(1, 1, sizeof(float)),
              gr::io_signature::make(1, 1, sizeof(float)), vec_size)
    {
    	chunk = vec_size;
    }

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

    int
    bin2dec_ff_impl::work(int noutput_items,
			  gr_vector_const_void_star &input_items,
			  gr_vector_void_star &output_items)
   {
        const float *in = (const float *) input_items[0];
        float *out = (float *) output_items[0];

        int j = 0, a = 0;
        for (int i = 0; i < noutput_items * chunk; i = i + chunk){
        	out[j] = 0;
        	a = chunk - 1;
        	for (int k = 0; k < chunk; k++){
        		out[j] = out[j] + (in[i + k] * pow (2,a));
        		a = a - 1;
        	}
        	j++;
        }

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

  } /* namespace Tu_modulo */
} /* namespace gr */

Convertidor decimal a binario

Para corroborar el correcto funcionamiento del bloque de convertidor binario a decimal se propone realizar un convertidor decimal a binario que realizará la operación inversa del bloque anterior.

El archivo donde se realiza la programación es dec2bin_ff_impl.cc. Como primer paso se indica la librería que utilizará el bloque de procesamiento, de la misma forma que se indicó en el bloque anterior.

#include <math.h>

Nuevamente, en la sección de dec2bin_ff_impl::dec2bin_ff_impl(int vec_size) se indica la cantidad de flujos de entrada y salida que manejará el bloque de procesamiento, así también el factor de interpolación definido por la variable vec_size y por último la variable chunk, quedando como sigue:

dec2bin_ff_impl::dec2bin_ff_impl(int vec_size)
  : gr::sync_interpolator("dec2bin_ff",
          gr::io_signature::make(1, 1, sizeof(float)),
          gr::io_signature::make(1, 1, sizeof(float)), vec_size)
{
	chunk = vec_size;
}

En la sección de // Do <+signal processing+> se escribe el código que realizará la conversión decimal a binario.

int
dec2bin_ff_impl::work(int noutput_items,
		  gr_vector_const_void_star &input_items,
		  gr_vector_void_star &output_items)
{
    const float *in = (const float *) input_items[0];
    float *out = (float *) output_items[0];

    int l = 0, m = 0, a = 0, b[chunk];
    for (int i = 0; i < noutput_items / chunk; i++){
    	a = in[i];
    	l = chunk - 1;
    	for (int j = 0; j < chunk; j++){
    		b[j] = a % 2;
    		a = a / 2;
    	}
    	for (int k = 0; k < chunk; k++){
    		out[m] = b [l];
    		l = l - 1;
    		m++;
    	}
    }

El archivo completo queda de la siguiente manera:

/* -*- c++ -*- */
/*
 * Copyright 2014 IVAN RODRIGUEZ.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

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

#include <gnuradio/io_signature.h>
#include "dec2bin_ff_impl.h"
#include <math.h>

namespace gr {
  namespace Tu_modulo {

    dec2bin_ff::sptr
    dec2bin_ff::make(int vec_size)
    {
      return gnuradio::get_initial_sptr
        (new dec2bin_ff_impl(vec_size));
    }

    /*
     * The private constructor
     */
    dec2bin_ff_impl::dec2bin_ff_impl(int vec_size)
      : gr::sync_interpolator("dec2bin_ff",
              gr::io_signature::make(1, 1, sizeof(float)),
              gr::io_signature::make(1, 1, sizeof(float)), vec_size)
    {
    	chunk = vec_size;
    }

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

    int
    dec2bin_ff_impl::work(int noutput_items,
			  gr_vector_const_void_star &input_items,
			  gr_vector_void_star &output_items)
    {
        const float *in = (const float *) input_items[0];
        float *out = (float *) output_items[0];

        int l = 0, m = 0, a = 0, b[chunk];
        for (int i = 0; i < noutput_items / chunk; i++){
        	a = in[i];
        	l = chunk - 1;
        	for (int j = 0; j < chunk; j++){
        		b[j] = a % 2;
        		a = a / 2;
        	}
        	for (int k = 0; k < chunk; k++){
        		out[m] = b [l];
        		l = l - 1;
        		m++;
        	}
        }
        // Tell runtime system how many output items we produced.
        return noutput_items;
    }

  } /* namespace Tu_modulo */
} /* namespace gr */

Adicionalmente hay que declarar la variable chunk de tal forma que sea pública y poder utilizarla en las diferentes secciones del bloque de procesamiento, esto se hace modificando el archivo bin2dec_ff_impl.h para el convertidor binario a decimal y el archivo dec2bin_ff_impl.h para el convertidor decimal a binario. En la sección de public se agrega:

 public:
  bin2dec_ff_impl(int vec_size);
  int chunk;
  ~bin2dec_ff_impl();

Quedando los archivos como siguen

/* -*- c++ -*- */
/*
 * Copyright 2014 IVAN RODRIGUEZ.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifndef INCLUDED_TU_MODULO_BIN2DEC_FF_IMPL_H
#define INCLUDED_TU_MODULO_BIN2DEC_FF_IMPL_H

#include <Tu_modulo/bin2dec_ff.h>

namespace gr {
  namespace Tu_modulo {

    class bin2dec_ff_impl : public bin2dec_ff
    {
     private:
      // Nothing to declare in this block.

     public:
      bin2dec_ff_impl(int vec_size);
      int chunk;
      ~bin2dec_ff_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 Tu_modulo
} // namespace gr

#endif /* INCLUDED_TU_MODULO_BIN2DEC_FF_IMPL_H */
/* -*- c++ -*- */
/*
 * Copyright 2014 IVAN RODRIGUEZ.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifndef INCLUDED_TU_MODULO_DEC2BIN_FF_IMPL_H
#define INCLUDED_TU_MODULO_DEC2BIN_FF_IMPL_H

#include <Tu_modulo/dec2bin_ff.h>

namespace gr {
  namespace Tu_modulo {

    class dec2bin_ff_impl : public dec2bin_ff
    {
     private:
      // Nothing to declare in this block.

     public:
      dec2bin_ff_impl(int vec_size);
      int chunk;
      ~dec2bin_ff_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 Tu_modulo
} // namespace gr

#endif /* INCLUDED_TU_MODULO_DEC2BIN_FF_IMPL_H */

Interfaz GNU Radio Companion

Los bloques dentro de GNU Radio Companion (GRC) son archivos xml que se ubican en ~/gr-Tu_modulo/grc, para el convertidor binario a decimal se llama Tu_modulo_bin2dec_ff.xml y para el convertidor decimal a binario Tu_modulo_dec2bin_ff.xml.

Dentro del archivo xml hay varios parámetros que pueden ser personalizados, tal como el nombre del bloque, este parámetro se define en la línea correspondiente a <name>.

<name>Binary To Decimal</name>

En la parte de <param> se indica la variable de entrada del bloque

  <param>
    <name>Vector size</name>
    <key>vec_size</key>
    <type>int</type>
  </param>

El conector de entrada del bloque de procesamiento se define en <sink>

  <sink>
    <name>in</name>
    <type>float</type>
  </sink>

El conector de salida en <source>

  <source>
    <name>out</name>
    <type>float</type>
  </source>

De igual manera se modifican las secciones de <name>, <param>, <sink> y <source> en el archivo Tu_modulo_dec2bin_ff.xml para el bloque de procesamiento del convertidor decimal a binario.

Los archivos quedarán finalmente de la siguiente forma:

<?xml version="1.0"?>
<block>
  <name>Binary To Decimal</name>
  <key>Tu_modulo_bin2dec_ff</key>
  <category>Tu_modulo</category>
  <import>import Tu_modulo</import>
  <make>Tu_modulo.bin2dec_ff($vec_size)</make>
  <!-- Make one 'param' node for every Parameter you want settable from the GUI.
       Sub-nodes:
       * name
       * key (makes the value accessible as $keyname, e.g. in the make node)
       * type -->
  <param>
    <name>Vector size</name>
    <key>vec_size</key>
    <type>int</type>
  </param>

  <!-- Make one 'sink' node per input. Sub-nodes:
       * name (an identifier for the GUI)
       * type
       * vlen
       * optional (set to 1 for optional inputs) -->
  <sink>
    <name>in</name>
    <type>float</type>
  </sink>

  <!-- Make one 'source' node per output. Sub-nodes:
       * name (an identifier for the GUI)
       * type
       * vlen
       * optional (set to 1 for optional inputs) -->
  <source>
    <name>out</name>
    <type>float</type>
  </source>
</block>
<?xml version="1.0"?>
<block>
  <name>Decimal To Binary</name>
  <key>Tu_modulo_dec2bin_ff</key>
  <category>Tu_modulo</category>
  <import>import Tu_modulo</import>
  <make>Tu_modulo.dec2bin_ff($vec_size)</make>
  <!-- Make one 'param' node for every Parameter you want settable from the GUI.
       Sub-nodes:
       * name
       * key (makes the value accessible as $keyname, e.g. in the make node)
       * type -->
  <param>
    <name>Vector size</name>
    <key>vec_size</key>
    <type>int</type>
  </param>

  <!-- Make one 'sink' node per input. Sub-nodes:
       * name (an identifier for the GUI)
       * type
       * vlen
       * optional (set to 1 for optional inputs) -->
  <sink>
    <name>in</name>
    <type>float</type>
  </sink>

  <!-- Make one 'source' node per output. Sub-nodes:
       * name (an identifier for the GUI)
       * type
       * vlen
       * optional (set to 1 for optional inputs) -->
  <source>
    <name>out</name>
    <type>float</type>
  </source>
</block>

Compilación de los bloques

Una vez ya realizado todos los procesos referentes al código de programación de los bloques de procesamiento se procede a integrarlos en el proyecto de GNU Radio. Esto se realiza por medio de una serie de comandos desde una terminal de linux en la raíz del proyecto. En caso de no tener una carpeta de build, se crea por medio de comandos de linux

$ mkdir build
$ cd build

Una vez dentro de la carpeta de build, se procede a compilar el proyecto

$ cmake ../
$ make
$ sudo make install
$ sudo ldconfig

De esa manera queda integrado el módulo out-of-tree dentro de GNU Radio.

@viktor_ivan