-
Notifications
You must be signed in to change notification settings - Fork 11
Moduleguide
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.
Table of Contents
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.