Skip to content
Josh Blum edited this page Aug 5, 2013 · 6 revisions
http://i.imgur.com/z3yTwgo.png

The following guide explains how to build and install runtime-loadable modules for the block factory. Modules contain IP blocks that will be loaded into the element factory for users to instantiate at runtime. This guide will go into details of how to build modules, where to install modules, and how to customize the installation paths so the runtime can find those modules.

The following contains a minimal 4 file demonstration project, with a C++ block, GRC wrapper, unit test, and CMake build script. This project will build the C++ block, and install the library module containing the block and the GRC file so that the framework can find these files at runtime. Users should copy this example project and customize it for their own needs.

This is the simplest possible CMakeLists for a user's custom project. Simple copy this file and fill it in with your custom sources. The GRAS_TOOL will handle building and installing into the proper location.

CMakeLists.txt

####################################
## Project setup
####################################
cmake_minimum_required(VERSION 2.8)
project(my_project CXX C)
enable_testing()

####################################
## Setup module path
####################################
if(GRAS_ROOT)
    #GRAS_ROOT already set
elseif($ENV{GRAS_ROOT})
    set(GRAS_ROOT $ENV{GRAS_ROOT})
else(GRAS_ROOT)
    set(GRAS_ROOT ${CMAKE_INSTALL_PREFIX})
endif(GRAS_ROOT)
list(APPEND CMAKE_MODULE_PATH ${GRAS_ROOT}/share/gras/cmake/Modules)

####################################
## Setup build and install
####################################
include(GRASTool)
GRAS_TOOL(
    SOURCES
        my_block.cpp
        my_block.xml
    TARGET my_project
)

####################################
## Register unit tests
####################################
include(GRASTest)
GRAS_ADD_TEST(my_block_test ${GRAS_TEST_PYEXE} ${CMAKE_CURRENT_SOURCE_DIR}/my_block_test.py)

This is an example block that registers itself into the factory. The important part is how calls are registered into the block and how the block is registered into the factory. Registration makes the magic happen, you block is now accessable in python. We dont fill in too many of the details. Thats for the code guide wiki page: https://github.com/guruofquality/gras/wiki/Codeguide

my_block.cpp

#include <gras/block.hpp>
#include <gras/factory.hpp>

struct MyBlock : gras::Block
{
    MyBlock(const int arg0, const std::string &arg1):
        gras::Block("MyBlock")
    {
        //init, do something with args...
        this->register_call("set_foo", &MyBlock::set_foo);
    }

    void work(const InputItems &ins, const OutputItems &outs)
    {
        //work function
        //handle IO, produce, consume
        //this->consume(n);
        //this->produce(n);
    }

    void set_foo(const std::vector<int> &new_foo)
    {
        //do something with foo
    }

};

//register my block into the block factory
GRAS_REGISTER_FACTORY2("/my_project/my_block", MyBlock, int, std::string)

This is an example Python unit test. We use python unit tests becasue they are quick and eazy. Notice that we use python unittest module and the TestUtils that come with GRAS. The TestUtils provide simple blocks for setting up test input and collecting output. Notice how we use the block factory and call the registered method on my_block.

my_block_test.py

import unittest
import gras
from gras import TestUtils
import numpy
import time

class test_my_block(unittest.TestCase):

    def setUp(self):
        self.tb = gras.TopBlock()

    def tearDown(self):
        self.tb = None

    def test_something(self):

        my_block = gras.make("/my_project/my_block", 42, "awesome")
        my_block.set_foo([1, 2, 3])

        src_data = tuple(numpy.random.randint(-1024, 1024, 10))
        src = TestUtils.VectorSource(numpy.int32, src_data)
        dst = TestUtils.VectorSink(numpy.int32)

        self.tb.connect(src, my_block, dst)

        self.tb.run()

        self.assertEqual(src_data, dst.data())

if __name__ == '__main__':
    unittest.main()

This information is already well documented on GRC's official wiki page: http://gnuradio.org/redmine/projects/gnuradio/wiki/GNURadioCompanion#Adding-Custom-Blocks

And finally to bring it all together and build the project. Put all the files into path_to_my_project_source and run the following commands:

mkdir path_to_build_directory
cd path_to_build_directory
cmake path_to_my_project_source
make -j4
make test
sudo make install

This part of the module guide is for the advanced use case; so users can really understand the path structure and learn how to customize the environment. For users that building and installing with default options, chances are this part of the guide is not relevant: Modules must be installed into the file system in order for GRAS to find them at runtime. The user has several options when installing modules:

  • Install modules into fixed hierarchy at the $GRAS_ROOT
  • Install modules into fixed hierarchy in the $GRAS_PATH
  • Install anywhere and set specific envionment variables

GRAS_ROOT refers to a directory where the GRAS installation is located. This directory contains all of the GRAS include directories, libraries, and python modules. The GRAS_ROOT is actually set when GRAS itself is configured in cmake via the -DCMAKE_INSTALL_PREFIX configuration option. By default GRAS_ROOT will be /usr/local on a linux system, and C:\Program Files\gras on a windows system.

In some cases: the GRAS installation will be configured for one installation directory, but actually installed into another. This happens often with distribution packages, where the installer allows the user to choose a destination directory. In this case, the GRAS_ROOT environment variable should be set for the actual installation path (Hopefully the installer will automatically do this). Once GRAS knows where it has been installed to, every thing will be fine.

GRAS will look for modules in the following root directories:

  • Library modules in $GRAS_ROOT/lib${libsuffix}/gras/modules
  • Python modules in $GRAS_ROOT/lib${libsuffix}/gras/python
  • GRC block wrappers in $GRAS_ROOT/share/gnuradio/grc/blocks

Users may wish to install modules into different installation prefix than GRAS_ROOT. For example: perhaps the user only has write access to their home directory, or the user has many simultaneous projects and needs to separate their installations. The GRAS_PATH environment variable allows the user to specify a list of installation prefixes for GRAS to search at runtime for modules.

GRAS will also look for modules in the following path directories:

  • Library modules in $GRAS_PATH/lib${libsuffix}/gras/modules
  • Python modules in $GRAS_PATH/lib${libsuffix}/gras/python
  • GRC block wrappers in $GRAS_PATH/share/gnuradio/grc/blocks

Example where the user configures a build for a different prefix:

mkdir path_to_build_directory
cd path_to_build_directory
cmake -DCMAKE_INSTALL_PREFIX=/home/user/projects -DGRAS_ROOT=/usr/local path_to_my_project_source
make -j4
make install

#later in the ~/.bashrc
export GRAS_ROOT=/usr/local
export GRAS_PATH=/home/user/projects:${GRAS_PATH}

Now, if ther user does not care about the hierarchy structure for installing modules, either with GRAS_ROOT or GRAS_PATH, they can install modules anywhere. There is a specific environment variable search path to set for each kind of module:

  • GRAS_MODULE_PATH: a list of library modules or directories containing such.
  • GRAS_PYTHON_PATH: a list of python files or directories containing such.
  • GRC_BLOCKS_PATH: a list of directories containing GRC block wrappers.
Clone this wiki locally