https://wiki.gnuradio.org/api.php?action=feedcontributions&user=M3x1m0m&feedformat=atomGNU Radio - User contributions [en]2024-03-28T16:58:36ZUser contributionsMediaWiki 1.39.5https://wiki.gnuradio.org/index.php?title=TutorialsDebugging&diff=4182TutorialsDebugging2018-05-15T18:21:53Z<p>M3x1m0m: /* Linking Issues */</p>
<hr />
<div>= How to debug GNU Radio applications =<br />
<br />
Once you've started creating GNU Radio applications, you will probably stumble upon some errors sooner or later. Here is some advice on how to tackle those problems.<br />
<br />
{{toc}}<br />
<br />
== Simple debugging otions ==<br />
<br />
Most often, it is enough to inspect the data flowing out of blocks during run-time to get an idea where an error might occur. This is usually the case if a flow graph runs without crashing, but the final result is not correct.<br />
<br />
The following options are easy to implement and will be useful for GNU Radio users of any skill level.<br />
<br />
=== Use the QA codes ===<br />
<br />
This is the most obvious and simple tool anyone should use. For every block you write, add QA code as well. In fact, write it ''first''. Test as many options as you can think of which might cause trouble. Individual blocks should always pass tests.<br />
<br />
If your blocks are failing, here's some suggestions on how to hunt down bugs:<br />
<br />
* Use <code>ctest -V</code> instead of <code>make test</code> to make the testing output more verbose. If you only want to run a specific test, use the <code>-R</code> switch to select tests that match a certain regular expression (e.g. <code>ctest -V -R foobar_ff</code> will only run tests that match <code>foobar_ff</code>, and will show all output)<br />
* If necessary, add additional <code>print</code> statements in the code (or even in your block) to show intermediary states until the tests pass<br />
* As a last resort, attach debuggers such as gdb or pdb (see the section on Expert Debugging Tools below)<br />
<br />
=== Using GRC and the graphical sinks ===<br />
<br />
This is a very simple solution. If possible, try and develop your applications with the [[GNURadioCompanion|GNU Radio companion]]. This tool has graphical sinks which you can simply attach to your block. Among the ''WX GUI Widgets'' and the ''QT GUI Widgets'' you can find FFT plots, Oscilloscopes and number sinks (which will simply display the value of the data). Depending on what kind of data you have, choose an approprate sink and attach it to your block. You can disable the graphical sink later if you don't want to delete it from the flow graph.<br />
<br />
=== Dumping data into files between blocks ===<br />
<br />
For a more detailed analysis of your data, you might want to perform an off-line analysis using other tools than GNU Radio, e.g. Octave, SciPy (with Matplotlib) or Matlab. The easiest way is to connect a ''file sink'' to the block you suspect is making trouble, run the flow graph and then load the file with the tool of your choice. Read [[Octave|the guide to using Octave and Matlab with GNU Radio]].<br />
<br />
== Expert debugging tools ==<br />
<br />
There's some tools you may use to inspect your code on a deeper level:<br />
<br />
* gdb - The GNU debugger (assuming you're using gcc). This allows you to step through your code, but it might not be useful for multithreaded debugging.<br />
<br />
If your block isn't working, and you can't sort it out through python test cases or a few printfs in the code, you may want to use gdb to debug it. This makes sense if the blocks you're using are written in C++ (even if called from Python).<br />
<br />
Try this: In your python test code, after the relevant imports, print out the process id and wait for a keystroke. In another window run gdb and tell it to attach to the python process with the given process id. At this point you can set breakpoints or whatever in your code. Go back to the python window and hit Enter so it'll continue.<br />
<br />
There's also:<br />
<br />
* oprofile - Profiling tool<br />
* kcachegrind / valgrind - Another profiling tool<br />
<br />
=== Tutorial: Using gdb with Pythonic GR applications ===<br />
<br />
Note that this tutorial assumes some familiarity with gdb.<br />
<br />
To try this at home, make and install the gr-howto-write-a-block module that comes with GNU Radio. Make sure that you can access the module from Python by calling <code>import howto</code>.<br />
<br />
This is the script we want to debug:<br />
<br />
<pre>&quot;&quot;&quot; Testing GDB, yay &quot;&quot;&quot;<br />
<br />
import os<br />
from gnuradio import gr<br />
import howto<br />
<br />
class SquareThat(gr.top_block):<br />
def __init__(self):<br />
gr.top_block.__init__(self, name=&quot;square_that&quot;)<br />
self.src = gr.vector_source_f((1, 2, 3, 4, )*5)<br />
self.sqr = howto.square2_ff()<br />
self.sink = gr.vector_sink_f()<br />
self.connect(self.src, self.sqr, self.sink)<br />
<br />
def main():<br />
&quot;&quot;&quot; go, go, go &quot;&quot;&quot;<br />
top_block = SquareThat()<br />
top_block.run()<br />
<br />
if __name__ == &quot;__main__&quot;:<br />
print 'Blocked waiting for GDB attach (pid = %d)' % (os.getpid(),)<br />
raw_input ('Press Enter to continue: ')<br />
main()</pre><br />
First of all, it helps if you compiled the howto module with debug symbols. CMake will do that for you if you invoke it with<br />
<br />
<pre>$ cmake .. -DCMAKE_BUILD_TYPE=Debug</pre><br />
Make sure to re-make and re-install if you recompiled this way.<br />
<br />
Now, all you have to do is start the script. Let's assume it's saved as <code>test_gdb.py</code>:<br />
<br />
<pre>$ python test_gdb.py<br />
Blocked waiting for GDB attach (pid = 27049)<br />
Press Enter to continue: </pre><br />
As you can see, the script is stalled, waiting for the user to hit enter. We will use this pause to call gdb in a different terminal window:<br />
<br />
<pre>$ gdb -p 27049</pre><br />
Make sure to use the same PID as the Python script has.<br />
<br />
'''Ubuntu users:''' The kernel will not simply let you poke around in processes, even if they have the same UID as you do, so the command above will give you an error message. Either call <code>sudo gdb</code>, or deactivate the ptrace blocking by calling <code>echo 0 &gt; /proc/sys/kernel/yama/ptrace_scope</code> as root. To permanently allow this, set the corresponding value to 0 in <code>/etc/sysctl.d/10-ptrace.conf</code>. For more information, see the Ubuntu page on [https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace%20Protection kernel hardening] .<br />
<br />
Once gdb has started, and you've successfully reached the gdb prompt, press enter in the terminal window running the Python script. gdb is now in control of your process, so it won't continue before you tell it to.<br />
<br />
Now, at the moment, gdb is stuck somewhere in the middle of nowhere, in some weird libs you've probably never heard of. To get straight to the heart of your block, set a breakpoint and wait until it's reached. Use the gdb tab-completion to navigate through the namespaces. You could for example set it to break in the work() function of the square2_ff block:<br />
<br />
<pre>(gdb) break gr::howto::square2_ff_impl::work(int, std::vector &gt;&amp;, std::vector &gt;&amp;) <br />
Breakpoint 1 at 0x7f4938ab2303: file [...]/gr-howto-write-a-block/lib/square2_ff_impl.cc, line 86.</pre><br />
Note that square2_ff::work() is virtual, so remember the impl.<br />
<br />
If you continue now, it will stop right there:<br />
<br />
<pre>(gdb) cont<br />
Continuing.<br />
[New Thread 0x7f4938aae700 (LWP 27863)]<br />
[New Thread 0x7f49382ad700 (LWP 27864)]<br />
[Thread 0x7f4938aae700 (LWP 27863) exited]<br />
[New Thread 0x7f4937aac700 (LWP 27865)]<br />
[Switching to Thread 0x7f49382ad700 (LWP 27864)]<br />
<br />
Breakpoint 1, gr::howto::square2_ff_impl::work (this=0x273ac30, noutput_items=20, input_items=..., <br />
output_items=...) at /home/braun/tmp/gr-howto-write-a-block/lib/square2_ff_impl.cc:86<br />
86 const float *in = (const float*)input_items[0];</pre><br />
Recognize that last line? It's the first line of <code>square2_ff_impl::work()</code>. Now you can step merrily through your functions as you wish.<br />
<br />
Note that GNU Radio is heavily multi-threaded, which can make usage of gdb quite complicated. The gdb command <code>info threads</code> will give you a list of active threads.<br />
<br />
==== Directly debugging the QA codes ====<br />
<br />
If your block is failing during QA, you don't have to install the module. However, ctest buffers the output, so the line showing the PID won't work. Instead, just put in the line that waits for the input:<br />
<br />
<pre>if __name__ == '__main__':<br />
raw_input ('Press Enter to continue: ')<br />
gr_unittest.run(qa_howto, &quot;qa_howto.xml&quot;)</pre><br />
And then figure out the PID using some system tool, e.g.:<br />
<br />
<pre>$ ps ux | grep qa_howto.py <br />
XXXXX 28518 0.1 0.2 307476 23660 pts/9 tNl+ 13:45 0:00 /usr/bin/python [...]/gr-howto-write-a-block/python/qa_howto.py</pre><br />
Then you can use <code>gdb -p 28518</code> as before.<br />
<br />
==== The faster alternative: Debugging crashed programs (Post-Mortem) ====<br />
<br />
An alternative method uses GDB to read a core dump file that is produced when a program crashes. Running a program in GDB can slow things down and potentially hide the error that would occur under normal operation. This method avoids that problem.<br />
<br />
First you must enable core dumps (which are mostly disabled by default these days). The <code>ulimit -a</code> command queries the current limits:<br />
<br />
<pre>$ ulimit -a<br />
core file size (blocks, -c) 0</pre><br />
We use the ulimit command to turn off the limit:<br />
<br />
<pre>$ ulimit -c unlimited<br />
$ ulimit -a<br />
core file size (blocks, -c) unlimited</pre><br />
Now run the app, and wait for the crash. It should generate a file called '''core''' or '''core.PID''' in the current directory. There are a few other obscure reasons that could also prevent the core file from appearing at this point (already a file with the same name but not owned by the user; user does not own the directory; executable is setuid, etc).<br />
<br />
For this example assume the core file is named '''core.12345'''. Now do<br />
<br />
<pre>gdb /usr/bin/python core.12345</pre><br />
This should load the core dump into GDB. Now you can do<br />
<br />
<pre>i stack</pre><br />
to display the call trace.<br />
<br />
==== Linking Issues ====<br />
<br />
It happened quite often to me, that I saw the following error message when trying to run a freshly designed/redesigned block. <br />
<br />
<pre>AttributeError: 'module' object has no attribute 'MODULE_NAME'</pre><br />
<br />
Internal exception handling prevents, that the error messages actually comes to light. To make it visible nevertheless browser to the ''swig'' directory in the build tree. Open a python console and type <br />
<br />
<pre>import _MODULE_NAME_swig</pre><br />
<br />
That will most likely spit out the undefined symbol.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4128Interfacing Hardware with a C++ OOT Module2018-03-28T16:06:24Z<p>M3x1m0m: </p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
__TOC__<br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library and corresponding dependencies correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this writing a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module] for every external library. Modules for cmake in GNURadio OOTs are located under ''cmake/Modules''. In this directory create a file and give it a proper name e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<syntaxhighlight lang="C++"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</syntaxhighlight><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module: <br />
<syntaxhighlight lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</syntaxhighlight> <br />
The code above can be placed into the segment where cmake searches for GNURadio dependencies. <br />
<br />
So far so good. We triggered cmake to seek the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed on the system. cmake needs to know, that we want to link this library against our code. Therefore, it provides a macro, which is called in ''lib/CMakeLists.txt''. <br />
<syntaxhighlight lang="cmake"><br />
target_link_libraries(gnuradio-limesdr ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${LIMESUITE_LIBRARIES})<br />
</syntaxhighlight><br />
The final step is to take the last unused variable and communicate to cmake, that there are header files we want to have added to our project. <br />
<syntaxhighlight lang="cmake"><br />
########################################################################<br />
# Setup the include and linker paths<br />
########################################################################<br />
include_directories(<br />
${CMAKE_SOURCE_DIR}/lib<br />
${CMAKE_SOURCE_DIR}/include<br />
${CMAKE_BINARY_DIR}/lib<br />
${CMAKE_BINARY_DIR}/include<br />
${Boost_INCLUDE_DIRS}<br />
${CPPUNIT_INCLUDE_DIRS}<br />
${GNURADIO_ALL_INCLUDE_DIRS}<br />
${LIMESUITE_INCLUDE_DIRS}<br />
)<br />
</syntaxhighlight><br />
In case you did not mess up something, you will be able to use the the driver library from now own in your code with a normal include like<br />
<syntaxhighlight lang="c++"><br />
#include <LimeSuite.h><br />
</syntaxhighlight><br />
If something is wrong, you perhaps will get a quite nasty and really annoying error message similar to ''AttributeError: 'module' object has no attribute 'MODULE_NAME'''. Usually, this can be interpreted as, that you did configure something wrong in the steps described above and you should iterate through the whole procedure again. <br />
<br />
Fortunately, there is a way provided by a program called ''ldd'' ("print shared object dependencies", says man ldd) to find out if you linked the dynamic library properly against your library (produced and deployed when you type sudo make install):<br />
<syntaxhighlight><br />
ldd /usr/local/lib/libgnuradio-limesdr.so | grep LimeSuite<br />
</syntaxhighlight><br />
The output should be something like<br />
<syntaxhighlight><br />
libLimeSuite.so.18.02-1 => /usr/local/lib/libLimeSuite.so.18.02-1 (0x00007f5b86be8000)<br />
</syntaxhighlight><br />
and can be interpreted as a success of the previous steps. Hurray! If you can not scream the 'Hurray!' yet, since it does not work, the following might be some helpful piece code for you<br />
<syntaxhighlight lang="cmake"><br />
########################################################################<br />
# Debugging<br />
########################################################################<br />
set(PRINT_ALL_VARS 1)<br />
if(PRINT_ALL_VARS)<br />
get_cmake_property(_variableNames VARIABLES)<br />
foreach (_variableName ${_variableNames})<br />
message(STATUS "${_variableName}=${${_variableName}}")<br />
endforeach()<br />
endif(PRINT_ALL_VARS)<br />
</syntaxhighlight> <br />
which is borrowed from [https://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script#9328525 here].<br />
<br />
== Using gr::block Facilities ==<br />
But how is the hardware actually correctly initialized and deinitialized within GNURadio? What to do when something goes wrong? This section tries to answer these questions. <br />
<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block], which is the base class for every block, provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. <br />
<br />
''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. <br />
<br />
However, one can not solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One approach for a sane design might be to call ''stop ()'' oneself as part of the exception handling in a catch block. An example implementation of ''start()'' is the following:<br />
<br />
<syntaxhighlight lang="c++"><br />
try<br />
{<br />
LOG << "start() called." << std::endl;<br />
// Try to initialize HW. Throws if HW ends up in a faulty state.<br />
init_hw(d_settings);<br />
}<br />
catch(const std::exception& e)<br />
{<br />
LOG << "Problem during initialization: "<< e.what();<br />
stop();<br />
// Tell GNURadio.<br />
throw;<br />
} <br />
</syntaxhighlight><br />
<br />
The above example tries to initialize the HW, if it fails, the device will be deinitialized again as ''stop()'' is called.<br />
<br />
It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly, given, that you implemented the ''stop()'' function correctly. <br />
<br />
It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called acc. to standard C++, which is something nobody wants, right? So '''DO NOT THROW EXCEPTIONS IN stop()''' unless you know exactly what you are doing. <br />
<br />
The implementation of the stop function can be much simpler as it avoids exceptions:<br />
<br />
<syntaxhighlight lang="c++"><br />
LOG << "stop() called.";<br />
// Deinitialize HW.<br />
deinit_hw();<br />
</syntaxhighlight><br />
<br />
One more thing to mention is, that having a signal source with a fixed sample rate, should embed the function ''set_output_multiple(...)'' provided by [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] somewhere in the constructor. The argument of this function should be the size of the buffer for communication between the (hopefully) asynchronous driver and the GNURadio block. This makes sure, that the data size the work function has to output (''noutput_items'') is always a multiple of buffer's size and it is always OK to return less samples. Yet, it is not recommended to produce less samples than specified with ''noutput_items''.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4127Interfacing Hardware with a C++ OOT Module2018-03-28T16:05:21Z<p>M3x1m0m: </p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library and corresponding dependencies correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this writing a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module] for every external library. Modules for cmake in GNURadio OOTs are located under ''cmake/Modules''. In this directory create a file and give it a proper name e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<syntaxhighlight lang="C++"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</syntaxhighlight><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module: <br />
<syntaxhighlight lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</syntaxhighlight> <br />
The code above can be placed into the segment where cmake searches for GNURadio dependencies. <br />
<br />
So far so good. We triggered cmake to seek the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed on the system. cmake needs to know, that we want to link this library against our code. Therefore, it provides a macro, which is called in ''lib/CMakeLists.txt''. <br />
<syntaxhighlight lang="cmake"><br />
target_link_libraries(gnuradio-limesdr ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${LIMESUITE_LIBRARIES})<br />
</syntaxhighlight><br />
The final step is to take the last unused variable and communicate to cmake, that there are header files we want to have added to our project. <br />
<syntaxhighlight lang="cmake"><br />
########################################################################<br />
# Setup the include and linker paths<br />
########################################################################<br />
include_directories(<br />
${CMAKE_SOURCE_DIR}/lib<br />
${CMAKE_SOURCE_DIR}/include<br />
${CMAKE_BINARY_DIR}/lib<br />
${CMAKE_BINARY_DIR}/include<br />
${Boost_INCLUDE_DIRS}<br />
${CPPUNIT_INCLUDE_DIRS}<br />
${GNURADIO_ALL_INCLUDE_DIRS}<br />
${LIMESUITE_INCLUDE_DIRS}<br />
)<br />
</syntaxhighlight><br />
In case you did not mess up something, you will be able to use the the driver library from now own in your code with a normal include like<br />
<syntaxhighlight lang="c++"><br />
#include <LimeSuite.h><br />
</syntaxhighlight><br />
If something is wrong, you perhaps will get a quite nasty and really annoying error message similar to ''AttributeError: 'module' object has no attribute 'MODULE_NAME'''. Usually, this can be interpreted as, that you did configure something wrong in the steps described above and you should iterate through the whole procedure again. <br />
<br />
Fortunately, there is a way provided by a program called ''ldd'' ("print shared object dependencies", says man ldd) to find out if you linked the dynamic library properly against your library (produced and deployed when you type sudo make install):<br />
<syntaxhighlight><br />
ldd /usr/local/lib/libgnuradio-limesdr.so | grep LimeSuite<br />
</syntaxhighlight><br />
The output should be something like<br />
<syntaxhighlight><br />
libLimeSuite.so.18.02-1 => /usr/local/lib/libLimeSuite.so.18.02-1 (0x00007f5b86be8000)<br />
</syntaxhighlight><br />
and can be interpreted as a success of the previous steps. Hurray! If you can not scream the 'Hurray!' yet, since it does not work, the following might be some helpful piece code for you<br />
<syntaxhighlight lang="cmake"><br />
########################################################################<br />
# Debugging<br />
########################################################################<br />
set(PRINT_ALL_VARS 1)<br />
if(PRINT_ALL_VARS)<br />
get_cmake_property(_variableNames VARIABLES)<br />
foreach (_variableName ${_variableNames})<br />
message(STATUS "${_variableName}=${${_variableName}}")<br />
endforeach()<br />
endif(PRINT_ALL_VARS)<br />
</syntaxhighlight> <br />
which is borrowed from [https://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script#9328525 here].<br />
<br />
== Using gr::block Facilities ==<br />
But how is the hardware actually correctly initialized and deinitialized within GNURadio? What to do when something goes wrong? This section tries to answer these questions. <br />
<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block], which is the base class for every block, provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. <br />
<br />
''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. <br />
<br />
However, one can not solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One approach for a sane design might be to call ''stop ()'' oneself as part of the exception handling in a catch block. An example implementation of ''start()'' is the following:<br />
<br />
<syntaxhighlight lang="c++"><br />
try<br />
{<br />
LOG << "start() called." << std::endl;<br />
// Try to initialize HW. Throws if HW ends up in a faulty state.<br />
init_hw(d_settings);<br />
}<br />
catch(const std::exception& e)<br />
{<br />
LOG << "Problem during initialization: "<< e.what();<br />
stop();<br />
// Tell GNURadio.<br />
throw;<br />
} <br />
</syntaxhighlight><br />
<br />
The above example tries to initialize the HW, if it fails, the device will be deinitialized again as ''stop()'' is called.<br />
<br />
It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly, given, that you implemented the ''stop()'' function correctly. <br />
<br />
It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called acc. to standard C++, which is something nobody wants, right? So '''DO NOT THROW EXCEPTIONS IN stop()''' unless you know exactly what you are doing. <br />
<br />
The implementation of the stop function can be much simpler as it avoids exceptions:<br />
<br />
<syntaxhighlight lang="c++"><br />
LOG << "stop() called.";<br />
// Deinitialize HW.<br />
deinit_hw();<br />
</syntaxhighlight><br />
<br />
One more thing to mention is, that having a signal source with a fixed sample rate, should embed the function ''set_output_multiple(...)'' provided by [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] somewhere in the constructor. The argument of this function should be the size of the buffer for communication between the (hopefully) asynchronous driver and the GNURadio block. This makes sure, that the data size the work function has to output (''noutput_items'') is always a multiple of buffer's size and it is always OK to return less samples. Yet, it is not recommended to produce less samples than specified with ''noutput_items''.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=TutorialsDebugging&diff=4122TutorialsDebugging2018-03-15T20:15:51Z<p>M3x1m0m: </p>
<hr />
<div>= How to debug GNU Radio applications =<br />
<br />
Once you've started creating GNU Radio applications, you will probably stumble upon some errors sooner or later. Here is some advice on how to tackle those problems.<br />
<br />
{{toc}}<br />
<br />
== Simple debugging otions ==<br />
<br />
Most often, it is enough to inspect the data flowing out of blocks during run-time to get an idea where an error might occur. This is usually the case if a flow graph runs without crashing, but the final result is not correct.<br />
<br />
The following options are easy to implement and will be useful for GNU Radio users of any skill level.<br />
<br />
=== Use the QA codes ===<br />
<br />
This is the most obvious and simple tool anyone should use. For every block you write, add QA code as well. In fact, write it ''first''. Test as many options as you can think of which might cause trouble. Individual blocks should always pass tests.<br />
<br />
If your blocks are failing, here's some suggestions on how to hunt down bugs:<br />
<br />
* Use <code>ctest -V</code> instead of <code>make test</code> to make the testing output more verbose. If you only want to run a specific test, use the <code>-R</code> switch to select tests that match a certain regular expression (e.g. <code>ctest -V -R foobar_ff</code> will only run tests that match <code>foobar_ff</code>, and will show all output)<br />
* If necessary, add additional <code>print</code> statements in the code (or even in your block) to show intermediary states until the tests pass<br />
* As a last resort, attach debuggers such as gdb or pdb (see the section on Expert Debugging Tools below)<br />
<br />
=== Using GRC and the graphical sinks ===<br />
<br />
This is a very simple solution. If possible, try and develop your applications with the [[GNURadioCompanion|GNU Radio companion]]. This tool has graphical sinks which you can simply attach to your block. Among the ''WX GUI Widgets'' and the ''QT GUI Widgets'' you can find FFT plots, Oscilloscopes and number sinks (which will simply display the value of the data). Depending on what kind of data you have, choose an approprate sink and attach it to your block. You can disable the graphical sink later if you don't want to delete it from the flow graph.<br />
<br />
=== Dumping data into files between blocks ===<br />
<br />
For a more detailed analysis of your data, you might want to perform an off-line analysis using other tools than GNU Radio, e.g. Octave, SciPy (with Matplotlib) or Matlab. The easiest way is to connect a ''file sink'' to the block you suspect is making trouble, run the flow graph and then load the file with the tool of your choice. Read [[Octave|the guide to using Octave and Matlab with GNU Radio]].<br />
<br />
== Expert debugging tools ==<br />
<br />
There's some tools you may use to inspect your code on a deeper level:<br />
<br />
* gdb - The GNU debugger (assuming you're using gcc). This allows you to step through your code, but it might not be useful for multithreaded debugging.<br />
<br />
If your block isn't working, and you can't sort it out through python test cases or a few printfs in the code, you may want to use gdb to debug it. This makes sense if the blocks you're using are written in C++ (even if called from Python).<br />
<br />
Try this: In your python test code, after the relevant imports, print out the process id and wait for a keystroke. In another window run gdb and tell it to attach to the python process with the given process id. At this point you can set breakpoints or whatever in your code. Go back to the python window and hit Enter so it'll continue.<br />
<br />
There's also:<br />
<br />
* oprofile - Profiling tool<br />
* kcachegrind / valgrind - Another profiling tool<br />
<br />
=== Tutorial: Using gdb with Pythonic GR applications ===<br />
<br />
Note that this tutorial assumes some familiarity with gdb.<br />
<br />
To try this at home, make and install the gr-howto-write-a-block module that comes with GNU Radio. Make sure that you can access the module from Python by calling <code>import howto</code>.<br />
<br />
This is the script we want to debug:<br />
<br />
<pre>&quot;&quot;&quot; Testing GDB, yay &quot;&quot;&quot;<br />
<br />
import os<br />
from gnuradio import gr<br />
import howto<br />
<br />
class SquareThat(gr.top_block):<br />
def __init__(self):<br />
gr.top_block.__init__(self, name=&quot;square_that&quot;)<br />
self.src = gr.vector_source_f((1, 2, 3, 4, )*5)<br />
self.sqr = howto.square2_ff()<br />
self.sink = gr.vector_sink_f()<br />
self.connect(self.src, self.sqr, self.sink)<br />
<br />
def main():<br />
&quot;&quot;&quot; go, go, go &quot;&quot;&quot;<br />
top_block = SquareThat()<br />
top_block.run()<br />
<br />
if __name__ == &quot;__main__&quot;:<br />
print 'Blocked waiting for GDB attach (pid = %d)' % (os.getpid(),)<br />
raw_input ('Press Enter to continue: ')<br />
main()</pre><br />
First of all, it helps if you compiled the howto module with debug symbols. CMake will do that for you if you invoke it with<br />
<br />
<pre>$ cmake .. -DCMAKE_BUILD_TYPE=Debug</pre><br />
Make sure to re-make and re-install if you recompiled this way.<br />
<br />
Now, all you have to do is start the script. Let's assume it's saved as <code>test_gdb.py</code>:<br />
<br />
<pre>$ python test_gdb.py<br />
Blocked waiting for GDB attach (pid = 27049)<br />
Press Enter to continue: </pre><br />
As you can see, the script is stalled, waiting for the user to hit enter. We will use this pause to call gdb in a different terminal window:<br />
<br />
<pre>$ gdb -p 27049</pre><br />
Make sure to use the same PID as the Python script has.<br />
<br />
'''Ubuntu users:''' The kernel will not simply let you poke around in processes, even if they have the same UID as you do, so the command above will give you an error message. Either call <code>sudo gdb</code>, or deactivate the ptrace blocking by calling <code>echo 0 &gt; /proc/sys/kernel/yama/ptrace_scope</code> as root. To permanently allow this, set the corresponding value to 0 in <code>/etc/sysctl.d/10-ptrace.conf</code>. For more information, see the Ubuntu page on [https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace%20Protection kernel hardening] .<br />
<br />
Once gdb has started, and you've successfully reached the gdb prompt, press enter in the terminal window running the Python script. gdb is now in control of your process, so it won't continue before you tell it to.<br />
<br />
Now, at the moment, gdb is stuck somewhere in the middle of nowhere, in some weird libs you've probably never heard of. To get straight to the heart of your block, set a breakpoint and wait until it's reached. Use the gdb tab-completion to navigate through the namespaces. You could for example set it to break in the work() function of the square2_ff block:<br />
<br />
<pre>(gdb) break gr::howto::square2_ff_impl::work(int, std::vector &gt;&amp;, std::vector &gt;&amp;) <br />
Breakpoint 1 at 0x7f4938ab2303: file [...]/gr-howto-write-a-block/lib/square2_ff_impl.cc, line 86.</pre><br />
Note that square2_ff::work() is virtual, so remember the impl.<br />
<br />
If you continue now, it will stop right there:<br />
<br />
<pre>(gdb) cont<br />
Continuing.<br />
[New Thread 0x7f4938aae700 (LWP 27863)]<br />
[New Thread 0x7f49382ad700 (LWP 27864)]<br />
[Thread 0x7f4938aae700 (LWP 27863) exited]<br />
[New Thread 0x7f4937aac700 (LWP 27865)]<br />
[Switching to Thread 0x7f49382ad700 (LWP 27864)]<br />
<br />
Breakpoint 1, gr::howto::square2_ff_impl::work (this=0x273ac30, noutput_items=20, input_items=..., <br />
output_items=...) at /home/braun/tmp/gr-howto-write-a-block/lib/square2_ff_impl.cc:86<br />
86 const float *in = (const float*)input_items[0];</pre><br />
Recognize that last line? It's the first line of <code>square2_ff_impl::work()</code>. Now you can step merrily through your functions as you wish.<br />
<br />
Note that GNU Radio is heavily multi-threaded, which can make usage of gdb quite complicated. The gdb command <code>info threads</code> will give you a list of active threads.<br />
<br />
==== Directly debugging the QA codes ====<br />
<br />
If your block is failing during QA, you don't have to install the module. However, ctest buffers the output, so the line showing the PID won't work. Instead, just put in the line that waits for the input:<br />
<br />
<pre>if __name__ == '__main__':<br />
raw_input ('Press Enter to continue: ')<br />
gr_unittest.run(qa_howto, &quot;qa_howto.xml&quot;)</pre><br />
And then figure out the PID using some system tool, e.g.:<br />
<br />
<pre>$ ps ux | grep qa_howto.py <br />
XXXXX 28518 0.1 0.2 307476 23660 pts/9 tNl+ 13:45 0:00 /usr/bin/python [...]/gr-howto-write-a-block/python/qa_howto.py</pre><br />
Then you can use <code>gdb -p 28518</code> as before.<br />
<br />
==== The faster alternative: Debugging crashed programs (Post-Mortem) ====<br />
<br />
An alternative method uses GDB to read a core dump file that is produced when a program crashes. Running a program in GDB can slow things down and potentially hide the error that would occur under normal operation. This method avoids that problem.<br />
<br />
First you must enable core dumps (which are mostly disabled by default these days). The <code>ulimit -a</code> command queries the current limits:<br />
<br />
<pre>$ ulimit -a<br />
core file size (blocks, -c) 0</pre><br />
We use the ulimit command to turn off the limit:<br />
<br />
<pre>$ ulimit -c unlimited<br />
$ ulimit -a<br />
core file size (blocks, -c) unlimited</pre><br />
Now run the app, and wait for the crash. It should generate a file called '''core''' or '''core.PID''' in the current directory. There are a few other obscure reasons that could also prevent the core file from appearing at this point (already a file with the same name but not owned by the user; user does not own the directory; executable is setuid, etc).<br />
<br />
For this example assume the core file is named '''core.12345'''. Now do<br />
<br />
<pre>gdb /usr/bin/python core.12345</pre><br />
This should load the core dump into GDB. Now you can do<br />
<br />
<pre>i stack</pre><br />
to display the call trace.<br />
<br />
==== Linking Issues ====<br />
<br />
It happened quite often to me, that I saw the following error message when trying to run a freshly designed/redesigned block. <br />
<br />
<pre>AttributeError: 'module' object has no attribute 'MODULE_NAME'</pre><br />
<br />
Internal exception handling prevents, that the error messages actually comes to light. To make it visible nevertheless browser to the ''swig'' directory in the build tree. Open a python console and type <br />
<br />
<pre>import MODULE_NAME_swig</pre><br />
<br />
That will most likely spit out the undefined symbol.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Coding_guide_impl&diff=4113Coding guide impl2018-03-12T10:04:52Z<p>M3x1m0m: </p>
<hr />
<div>= Coding Guide =<br />
<br />
'''THIS PAGE IS A WORK IN PROGRESS TO DOCUMENT THE NEW CODING STYLE AS OF v3.7'''<br />
<br />
This document specifies the coding style and structure of blocks in GNU Radio as of version 3.7. The blocks are structured under top-level component (TLC) directories that combine similar blocks/algorithms (e.g., all filtering blocks are found in gr-filter). For purposes of explanation here, we will work with the generic TLC called '''foo''' and the block '''bar'''. Data types for member variables are generically called '''dtype'''.<br />
<br />
== Directory Structure ==<br />
<br />
* apps: any full-fledged applications specific to the component<br />
* doc: place for extra documentation, including Doxygen .dox files to describe the component.<br />
* examples: example code to demonstrate usage of component blocks and algorithms.<br />
* grc: GNU Radio Companion files and block tree.<br />
* include: public header files<br />
** include/foo: actual location of public header files. Headers in included using <code>#include <foo/bar.h></code>.<br />
* lib: location of the implementation source and private header files (bar_impl.h and bar_impl.cc, generally).<br />
* swig: location of SWIG interface file. We use a simplified structure that only includes the public header file for SWIGing.<br />
<br />
== Namespaces ==<br />
<br />
Components are described under the namespace '''gr::foo'''<br />
<br />
In Python, each TLC is a component under the '''gnuradio''' module. So to get access to blocks in '''foo''', do:<br />
<br />
<syntaxhighlight lang="python">from gnuradio import foo</syntaxhighlight><br />
<br />
== Code Format ==<br />
<br />
We want to try and make a consistent look and feel to the code in GNU Radio. Here are a few things to keep in mind while writing code.<br />
<br />
All lines of code should not exceed 70 characters on a width. Often, there is a logical break in the line of code that allows it to easily span two lines, such as between arguments to the function. Sometimes not, though, so make your best judgment here. The reason for this is to make the code format easily readable in both an editor and when printed out.<br />
<br />
As stated elsewhere, no CamelCase. All distinct words in a function or variable name should use underscores:<br />
<br />
<syntaxhighlight lang="C++">int foo_bar</syntaxhighlight><br />
<br />
In the header file, functions should all be written on the same line, up to the 70-character limit.<br />
<br />
<syntaxhighlight lang="C++">int bar(int arg1, int arg2);</syntaxhighlight><br />
<br />
In the C++ implementation file, the function should have its return data type indicated on a separate line.<br />
<br />
<syntaxhighlight lang="C++"><br />
int <br />
foo::bar(int arg1, int arg2)<br />
</syntaxhighlight><br />
<br />
Curly brace convention is as follows. After a function name, the curly brace should follow on the next line, and the ending curly brace should be on its own line. Inside of a function, such as for an '''if''' statement or '''for''' loop, the beginning curly brace should come after the statement, while the end curly brace should be on its own line. Note the spacing used in this example, too.<br />
<br />
<syntaxhighlight lang="C++"><br />
int <br />
foo::bar(int arg1, int arg2)<br />
{<br />
if(arg1 == 0) {<br />
;<br />
}<br />
else {<br />
;<br />
}<br />
return 0;<br />
}</syntaxhighlight><br />
Although C++ allows single-line statements after a '''for''' or '''if''' statement to exist without curly braces surrounding them, it is recommended to ''always'' use curly braces. This may help prevent problems in the future if another line is added to the code block.<br />
<br />
=== Block naming conventions ===<br />
<br />
Blocks should be given a name that indicates its purpose as well as its input and output data types. If a block lives in the GNU Radio core and performs the function FOO that takes complex in and produces floating points outputs, it's name should be:<br />
<br />
<code>foo_cf</code><br />
<br />
This boils down to:<br />
<br />
<code><name>_<input><ouput></code><br />
<br />
This lives in the namespace '''gr::<module>'''.<br />
<br />
The ''name'' indicates the function of the block, such as "add" or "fir_filter".<br />
<br />
The ''input'' indicates the input type while ''output'' indicates the output type. Currently, the types are:<br />
<br />
* c: complex (32-bit floats for both I and Q)<br />
* f: float (32-bit single precision IEEE 754 style)<br />
* i: integer (32-bit signed integers)<br />
* s: short (16-bit signed integers)<br />
* b: bytes (8-bit signed integers)<br />
* TODO: How do we indicate a block has no inputs (a source) or no outputs (a sink)?<br />
<br />
There is no differentiation between bytes and bits, even though some blocks use unpacked format where a single bit is represented in a byte. If this is the case, it should be made clear in the documentation for the block.<br />
<br />
GNU Radio also has a concept of vectors, where the data into or out of the block are vectors of items. The "v" is then used to indicate if this is the case. A block that uses vectors should indicate it by the type:<br />
<br />
<code><vector in><input><vector out><output></code><br />
<br />
So a block that takes vectors in but streams samples out would look like ''vcc'', but a block that does vectors for both input and output will look like ''vcvc''.<br />
<br />
'''NOTE''': this is a new convention and older blocks that use vectors do not follow this. They generally only indicate that vectors are used somewhere by putting a single "v" in front of the data type indicator.<br />
<br />
== Block Coding Guidelines ==<br />
<br />
All GNU Radio blocks have a structure and style that we try to keep consistent. There are also a number of concepts and general practices to understand when building a new block. We have a separate page describing the [[BlocksCodingGuide|block coding guidelines]]:<br />
<br />
== Standard command line options ==<br />
<br />
When writing programs that are executable from the command line, please follow these guidelines for command line argument names (short and long) and types of the arguments. We list them below using the Python optparse syntax. In general, the default value should be coded into the help string using the "... [default=%default]" syntax.<br />
<br />
The following shows the boilerplate text for using the optparse Python module (see http://docs.python.org/library/optparse.html).<br />
<br />
<syntaxhighlight lang="python"><br />
from optparse import OptionParser<br />
….<br />
def :<br />
usage = ””<br />
desc = “”<br />
parser = OptionParser(usage=usage, description=desc, option_class=eng_option)<br />
parser.add_option("-a", "--args", type="string", default="",<br />
help="UHD device address args , [default=%default]")<br />
….<br />
(options, args) = parser.parse_args()<br />
</syntaxhighlight><br />
=== Standard options names by parameter ===<br />
<br />
Whenever you want an integer, use the "intx" type. This allows the user to input decimal, hex or octal numbers. E.g., 10, 012, 0xa.<br />
<br />
Whenever you want a float, use the "eng_float" type. This allows the user to input numbers with SI suffixes. E.g, 10000, 10k, 10M, 10m, 92.1M<br />
<br />
If your program allows the user to specify values for any of the following parameters, please use these options to specify them:<br />
<br />
* To specify a frequency (typically an RF center frequency) use:<br />
<br />
<code>add_option("-f", "--freq", type="eng_float", default=<your-default-here>, help="set frequency to FREQ [default=%default]")</code><br />
<br />
* To specify a sample rate use:<br />
<br />
<code>add_option("-s", "--samp-rate", type="eng_float", default=<your-default-here>, help="set sample rate to DECIM [default=%default]")</code><br />
<br />
* To specify a gain setting use:<br />
<br />
<code>add_option("-g", "--gain", type="eng_float", default=<your-default-here>, help="set gain in dB [default=%default]")</code><br />
<br />
* To specify the number of channels of something use:<br />
<br />
<code>add_option("-n", "--nchannels", type="intx", default=1, help="specify number of channels [default=%default]")</code><br />
<br />
* To specify an output filename use:<br />
<br />
<code>add_option("-o", "--output-filename", type="string", default=<your-default-here>, help="specify output-filename [default=%default]")</code><br />
<br />
* If your application has a verbose option, use:<br />
<br />
<code>add_option('-v', '--verbose', action="store_true", default=False, help="verbose output")</code><br />
<br />
=== Example: Audio source ===<br />
<br />
<code>add_option("-I", "--audio-input", type="string", default="", help="pcm input device name. E.g., hw:0,0 or /dev/dsp")</code><br />
<br />
The default must be "". This allows an audio module-dependent default to be specified in the user preferences file.<br />
<br />
=== Example: Audio sink ===<br />
<br />
<code>add_option("-O", "--audio-output", type="string", default="", help="pcm output device name. E.g., hw:0,0 or /dev/dsp")</code><br />
<br />
The default must be "". This allows an audio module-dependent default to be specified in the user preferences file.<br />
<br />
== C++ ==<br />
<br />
=== Global names ===<br />
<br />
All globally visible names (types, functions, variables, consts, etc) shall begin with a "package prefix", followed by an '_'.<br />
<br />
=== Package prefixes ===<br />
<br />
We have moved away from the concept of package prefixes and use namespaces instead. See the [[BlocksCodingGuide]] page for description and examples of this. For older code, we specified what kind of block it was using a package prefix. For example, we had:<br />
<br />
<code>gr_</code>: Almost everything<br />
<br />
<code>gri_</code>: Implementation primitives. Sometimes we have both a gr_<foo> and a gri_<foo>. In that case, gr_<foo> would be derived from gr_block and gri_<foo> would be the low level guts of the function.<br />
<br />
<code>qa_</code>: Quality Assurance. Test code.<br />
<br />
Now, a block that used to be called '''gr_foo''' that is a general block would now be located in the top-level component '''gr-block'''. The header file would be in '''gr-block/include/block/foo.h'''. It would then be in the namespace '''gr::block'''.<br />
<br />
Implementation files are just named for what they are. For example, the '''fft_vcc_fftw''' block uses the implementation of the FFT found in '''gr::fft::fft'''. Sometimes, when there is a name collisions, we put these under the namespace '''gr::foo::kernel'''. See the '''gr-filter''' blocks to create FIR filters.<br />
<br />
=== Naming conventions ===<br />
<br />
* Functions and variable are underscore_separated_words (No CamelCase).<br />
* All data members of a class use a "d_" prefix to them, such as '''d_bar'''. They should also be private unless there is a very good reason not to.<br />
<br />
All data member should have a settable and gettable accessor function of the form:<br />
<br />
* void set_variable(type var);<br />
* type variable();<br />
<br />
These accessors should perform any required range checking and throw a "std::out_of_range" exceptions when not met. Other conditions that are not met can throw a "std::invalid_argument" exception.<br />
<br />
The big win is when you're staring at a block of code, it's obvious which of the things being assigned to persist outside of the block. This also keeps you from having to be creative with parameter names for methods and constructors. You just use the same name as the instance variable, without the <code>d_</code>.<br />
<br />
Example:<br />
<syntaxhighlight lang="C++"><br />
class gr_wonderfulness <br />
{<br />
std::string d_name;<br />
double d_wonderfulness_factor;<br />
<br />
public:<br />
wonderfulness(std::string name, double wonderfulness_factor)<br />
: d_name(name), d_wonderfulness_factor(wonderfulness_factor)<br />
{<br />
...<br />
}<br />
...<br />
};<br />
</syntaxhighlight><br />
* All class static data members shall begin with “s_” as in “s_<bar>”.<br />
<br />
=== Accessor Functions ===<br />
<br />
All signal processing blocks should have accessors to get and set values. For parameter param, the get function is '''param()''' and the set function is '''set_param(type new_value)''' for any variables that can be changed at runtime.<br />
<br />
=== Storage management ===<br />
<br />
Strongly consider using the boost smart pointer templates, scoped_ptr and shared_ptr. scoped_ptr should be used for locals that contain pointers to objects that we need to delete when we exit the current scope. shared_ptr implements transparent reference counting and is a major win. You never have to worry about calling delete. The right thing happens.<br />
<br />
See http://www.boost.org/libs/smart_ptr/smart_ptr.htm<br />
<br />
=== Exceptions ===<br />
<br />
Use the following exceptions:<br />
<br />
* std::runtime_error: when a runtime error has occurred<br />
* std::out_of_range: when a variable to be set is not within a given range<br />
* std::invalid_argument: when an argument is set to an improper value<br />
<br />
=== White Space Removal ===<br />
<br />
While this might sound a little pedantic, we encourage everyone to remove trailing white space on any lines edited as well as extraneous lines at the end of the file. This helps keep our code clean, but more importantly, it helps keep our git logs clean. Removal of trailing white spaces can be done by asking your favorite editor to do it for you on save. See the [[Coding_guide_impl#Setup-Recommendations-for-Standard-Editors|Setup Recommendations for Standard Editors]] guide at the end here for hints about how to get your editor to help you remove any trailing white space.<br />
<br />
You can use a program written for GNU Radio to remove any trailing white spaces from all files in your current directory tree. You can find this file in <source tree>/dtools/bin/remove-whitespace. It's a sed script that does the following:<br />
<br />
<pre>sed -i -e 's/\s\+$//g' $(find . -type f | grep -v '.git' | grep -v 'png' | grep -v 'eps')</pre><br />
Notice that it ignores all .git, .png, and .eps files to preserve their standard formatting.<br />
<br />
We infrequently run this script on the GNU Radio source tree, so depending on when you've last updated the source, you may or may not have a lot of white space that's snuck into the code.<br />
<br />
== Unit testing ==<br />
<br />
Build unit tests for everything non-trivial and run them after every change. Check out Extreme Programming: http://c2.com/cgi/wiki?ExtremeProgrammingRoadmap<br />
<br />
Unit tests should also be written for all examples.<br />
<br />
Unit tests can be written in Python or in C++. If possible, use Python unit tests (e.g., to test the output of signal processing blocks).<br />
<br />
=== C++ unit tests ===<br />
<br />
For C++, we use '''cppunit''', which has some issues, we have it nicely integrated into the build system. http://cppunit.sf.net<br />
<br />
Currently each top-level component <tlc> contains files qa_<tlc>.{h,cc} in the '''lib''' directory that bring together all the qa_<foo> test suites in the directory.<br />
<br />
=== Python unit tests ===<br />
<br />
We use the standard unittest package for unit testing of Python code.<br /><br />
http://docs.python.org/library/unittest.html<br />
<br />
We have added our own interface called '''gr_unittest''' to the unittest to overload and add some functions to make it simpler to test GNU Radio blocks. The Python QA functions are located in the top-level components '''python''' directory. They are all named “qa_<foo>.py”. Any Python file with the “qa_” prefix will be automatically tested.<br />
<br />
A general QA file looks like:<br />
<br />
<syntaxhighlight lang="python"><br />
from gnuradio import gr, gr_unittest<br />
import MODULE_swig as MODULE<br />
<br />
class test_foo(gr_unittest.TestCase):<br />
def setUp (self):<br />
self.tb = gr.top_block()<br />
<br />
def tearDown (self):<br />
self.tb = None<br />
<br />
def test_001(self):<br />
source_data = […]<br />
expected_data = […]<br />
<br />
src = gr.vector_source_X(source_data)<br />
head = gr.head(, )<br />
op = <br />
snk = gr.vector_sink_X()<br />
<br />
self.tb.connect(src, head, op, snk)<br />
self.tb.run()<br />
<br />
result_data = snk.data()<br />
self.assertXXX(expected_data, result_data, )<br />
<br />
if __name__ == '__main__':<br />
gr_unittest.run(test_foo, "test_foo.xml")<br />
</syntaxhighlight><br />
<br />
The '''self.assertXXX''' performs the test between the expected and actual data up. There are many assertions that are already part of the Python unittest module and gr_unittest adds many more, specifically to allow us to test different data types with a set amount of precision that is more appropriate for signal processing tests. These include:<br />
<br />
* assertComplexAlmostEqual(self, first, second, places=7, msg=None)<br />
* assertComplexAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
* assertComplexTuplesAlmostEqual(self, a, b, places=7, msg=None)<br />
* assertComplexTuplesAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
* assertFloatTuplesAlmostEqual(self, a, b, places=7, msg=None)<br />
* assertFloatTuplesAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
<br />
See the definitions of these in “gnuradio-runtime/python/gnuradio/gr_unittest.py” in the source code for details.<br />
<br />
== Setup Recommendations for Standard Editors ==<br />
<br />
Here are a few hints for some of our favorite editors to help you get your code formatted properly to work with our standards. Most of it's straight-forward, and these help make sure spacing and white space issues are kept consistent.<br />
<br />
=== Emacs ===<br />
<br />
<pre>(setq-default show-trailing-whitespace t) ;; show trailing white space as color def. next<br />
(set-face-background 'trailing-whitespace "#72706F")<br />
(setq-default show-tabs t) ;; show tabs; should be replaced by spaces<br />
(setq-default indent-tabs-mode nil) ;; use spaces, not tabs, for indenting<br />
(add-hook 'before-save-hook 'delete-trailing-whitespace) ;; remove trailing white space on save</pre><br />
=== ViM ===<br />
<br />
<pre>" You won't need every setting in this list. The below command is the one that will be most generally <br />
" useful to everyone.<br />
autocmd BufWritePre * :%s/\s\+$//e " Automatically remove trailing whitespace on save<br />
<br />
" In addition to having trailing whitespace automatically removed when you save a file, you can also <br />
" map a key sequence (e.g., `-w`) for the purpose. This is useful for remove trailing whitespace <br />
" without saving, but while editing.<br />
nnoremap w mz:%s/\s\+$//:let @/=''`z<br />
<br />
" If you would also like to visualize whitespace, you can try this out<br />
set listchars=tab:▸\ ,extends:❯,precedes:❮,trail:· " Show special characters for tabs, trailing spaces, and broken indents<br />
<br />
" Alternatively, you can show whitespace characters only while in Insert mode. For example:<br />
augroup trailing<br />
au!<br />
au InsertEnter * :set listchars+=tab:▸\ ,extends:❯,precedes:❮,trail:·,eol:¬<br />
au InsertLeave * :set listchars-=tab:▸\ ,extends:❯,precedes:❮,trail:·,eol:¬,<br />
augroup END</pre></div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Coding_guide_impl&diff=4112Coding guide impl2018-03-12T10:02:10Z<p>M3x1m0m: </p>
<hr />
<div>= Coding Guide =<br />
<br />
'''THIS PAGE IS A WORK IN PROGRESS TO DOCUMENT THE NEW CODING STYLE AS OF v3.7'''<br />
<br />
This document specifies the coding style and structure of blocks in GNU Radio as of version 3.7. The blocks are structured under top-level component (TLC) directories that combine similar blocks/algorithms (e.g., all filtering blocks are found in gr-filter). For purposes of explanation here, we will work with the generic TLC called '''foo''' and the block '''bar'''. Data types for member variables are generically called '''dtype'''.<br />
<br />
== Directory Structure ==<br />
<br />
* apps: any full-fledged applications specific to the component<br />
* doc: place for extra documentation, including Doxygen .dox files to describe the component.<br />
* examples: example code to demonstrate usage of component blocks and algorithms.<br />
* grc: GNU Radio Companion files and block tree.<br />
* include: public header files<br />
** include/foo: actual location of public header files. Headers in included using <code>#include <foo/bar.h></code>.<br />
* lib: location of the implementation source and private header files (bar_impl.h and bar_impl.cc, generally).<br />
* swig: location of SWIG interface file. We use a simplified structure that only includes the public header file for SWIGing.<br />
<br />
== Namespaces ==<br />
<br />
Components are described under the namespace '''gr::foo'''<br />
<br />
In Python, each TLC is a component under the '''gnuradio''' module. So to get access to blocks in '''foo''', do:<br />
<br />
<syntaxhighlight lang="python">from gnuradio import foo</syntaxhighlight><br />
<br />
== Code Format ==<br />
<br />
We want to try and make a consistent look and feel to the code in GNU Radio. Here are a few things to keep in mind while writing code.<br />
<br />
All lines of code should not exceed 70 characters on a width. Often, there is a logical break in the line of code that allows it to easily span two lines, such as between arguments to the function. Sometimes not, though, so make your best judgment here. The reason for this is to make the code format easily readable in both an editor and when printed out.<br />
<br />
As stated elsewhere, no CamelCase. All distinct words in a function or variable name should use underscores:<br />
<br />
<syntaxhighlight lang="C++">int foo_bar</syntaxhighlight><br />
<br />
In the header file, functions should all be written on the same line, up to the 70-character limit.<br />
<br />
<syntaxhighlight lang="C++">int bar(int arg1, int arg2);</syntaxhighlight><br />
<br />
In the C++ implementation file, the function should have its return data type indicated on a separate line.<br />
<br />
<syntaxhighlight lang="C++"><br />
int <br />
foo::bar(int arg1, int arg2)<br />
</syntaxhighlight><br />
<br />
Curly brace convention is as follows. After a function name, the curly brace should follow on the next line, and the ending curly brace should be on its own line. Inside of a function, such as for an '''if''' statement or '''for''' loop, the beginning curly brace should come after the statement, while the end curly brace should be on its own line. Note the spacing used in this example, too.<br />
<br />
<syntaxhighlight lang="C++"><br />
int <br />
foo::bar(int arg1, int arg2)<br />
{<br />
if(arg1 == 0) {<br />
;<br />
}<br />
else {<br />
;<br />
}<br />
return 0;<br />
}</syntaxhighlight><br />
Although C++ allows single-line statements after a '''for''' or '''if''' statement to exist without curly braces surrounding them, it is recommended to ''always'' use curly braces. This may help prevent problems in the future if another line is added to the code block.<br />
<br />
=== Block naming conventions ===<br />
<br />
Blocks should be given a name that indicates its purpose as well as its input and output data types. If a block lives in the GNU Radio core and performs the function FOO that takes complex in and produces floating points outputs, it's name should be:<br />
<br />
<code>foo_cf</code><br />
<br />
This boils down to:<br />
<br />
<code><name>_<input><ouput></code><br />
<br />
This lives in the namespace '''gr::<module>'''.<br />
<br />
The ''name'' indicates the function of the block, such as "add" or "fir_filter".<br />
<br />
The ''input'' indicates the input type while ''output'' indicates the output type. Currently, the types are:<br />
<br />
* c: complex (32-bit floats for both I and Q)<br />
* f: float (32-bit single precision IEEE 754 style)<br />
* i: integer (32-bit signed integers)<br />
* s: short (16-bit signed integers)<br />
* b: bytes (8-bit signed integers)<br />
* TODO: How do we indicate a block has no inputs (a source) or no outputs (a sink)?<br />
<br />
There is no differentiation between bytes and bits, even though some blocks use unpacked format where a single bit is represented in a byte. If this is the case, it should be made clear in the documentation for the block.<br />
<br />
GNU Radio also has a concept of vectors, where the data into or out of the block are vectors of items. The "v" is then used to indicate if this is the case. A block that uses vectors should indicate it by the type:<br />
<br />
<code><vector in><input><vector out><output></code><br />
<br />
So a block that takes vectors in but streams samples out would look like ''vcc'', but a block that does vectors for both input and output will look like ''vcvc''.<br />
<br />
'''NOTE''': this is a new convention and older blocks that use vectors do not follow this. They generally only indicate that vectors are used somewhere by putting a single "v" in front of the data type indicator.<br />
<br />
== Block Coding Guidelines ==<br />
<br />
All GNU Radio blocks have a structure and style that we try to keep consistent. There are also a number of concepts and general practices to understand when building a new block. We have a separate page describing the [[BlocksCodingGuide|block coding guidelines]]:<br />
<br />
== Standard command line options ==<br />
<br />
When writing programs that are executable from the command line, please follow these guidelines for command line argument names (short and long) and types of the arguments. We list them below using the Python optparse syntax. In general, the default value should be coded into the help string using the "... [default=%default]" syntax.<br />
<br />
The following shows the boilerplate text for using the optparse Python module (see http://docs.python.org/library/optparse.html).<br />
<br />
<syntaxhighlight lang="python"><br />
from optparse import OptionParser<br />
….<br />
def :<br />
usage = ””<br />
desc = “”<br />
parser = OptionParser(usage=usage, description=desc, option_class=eng_option)<br />
parser.add_option("-a", "--args", type="string", default="",<br />
help="UHD device address args , [default=%default]")<br />
….<br />
(options, args) = parser.parse_args()<br />
</syntaxhighlight><br />
=== Standard options names by parameter ===<br />
<br />
Whenever you want an integer, use the "intx" type. This allows the user to input decimal, hex or octal numbers. E.g., 10, 012, 0xa.<br />
<br />
Whenever you want a float, use the "eng_float" type. This allows the user to input numbers with SI suffixes. E.g, 10000, 10k, 10M, 10m, 92.1M<br />
<br />
If your program allows the user to specify values for any of the following parameters, please use these options to specify them:<br />
<br />
* To specify a frequency (typically an RF center frequency) use:<br />
<br />
<code>add_option("-f", "--freq", type="eng_float", default=<your-default-here>, help="set frequency to FREQ [default=%default]")</code><br />
<br />
* To specify a sample rate use:<br />
<br />
<code>add_option("-s", "--samp-rate", type="eng_float", default=<your-default-here>, help="set sample rate to DECIM [default=%default]")</code><br />
<br />
* To specify a gain setting use:<br />
<br />
<code>add_option("-g", "--gain", type="eng_float", default=<your-default-here>, help="set gain in dB [default=%default]")</code><br />
<br />
* To specify the number of channels of something use:<br />
<br />
<code>add_option("-n", "--nchannels", type="intx", default=1, help="specify number of channels [default=%default]")</code><br />
<br />
* To specify an output filename use:<br />
<br />
<code>add_option("-o", "--output-filename", type="string", default=<your-default-here>, help="specify output-filename [default=%default]")</code><br />
<br />
* If your application has a verbose option, use:<br />
<br />
<code>add_option('-v', '--verbose', action="store_true", default=False, help="verbose output")</code><br />
<br />
=== Example: Audio source ===<br />
<br />
<code>add_option("-I", "--audio-input", type="string", default="", help="pcm input device name. E.g., hw:0,0 or /dev/dsp")</code><br />
<br />
The default must be "". This allows an audio module-dependent default to be specified in the user preferences file.<br />
<br />
=== Example: Audio sink ===<br />
<br />
<code>add_option("-O", "--audio-output", type="string", default="", help="pcm output device name. E.g., hw:0,0 or /dev/dsp")</code><br />
<br />
The default must be "". This allows an audio module-dependent default to be specified in the user preferences file.<br />
<br />
== C++ ==<br />
<br />
=== Global names ===<br />
<br />
All globally visible names (types, functions, variables, consts, etc) shall begin with a "package prefix", followed by an '_'.<br />
<br />
=== Package prefixes ===<br />
<br />
We have moved away from the concept of package prefixes and use namespaces instead. See the [[BlocksCodingGuide]] page for description and examples of this. For older code, we specified what kind of block it was using a package prefix. For example, we had:<br />
<br />
<code>gr_</code>: Almost everything<br />
<br />
<code>gri_</code>: Implementation primitives. Sometimes we have both a gr_<foo> and a gri_<foo>. In that case, gr_<foo> would be derived from gr_block and gri_<foo> would be the low level guts of the function.<br />
<br />
<code>qa_</code>: Quality Assurance. Test code.<br />
<br />
Now, a block that used to be called '''gr_foo''' that is a general block would now be located in the top-level component '''gr-block'''. The header file would be in '''gr-block/include/block/foo.h'''. It would then be in the namespace '''gr::block'''.<br />
<br />
Implementation files are just named for what they are. For example, the '''fft_vcc_fftw''' block uses the implementation of the FFT found in '''gr::fft::fft'''. Sometimes, when there is a name collisions, we put these under the namespace '''gr::foo::kernel'''. See the '''gr-filter''' blocks to create FIR filters.<br />
<br />
=== Naming conventions ===<br />
<br />
* Functions and variable are underscore_separated_words (No CamelCase).<br />
* All data members of a class use a "d_" prefix to them, such as '''d_bar'''. They should also be private unless there is a very good reason not to.<br />
<br />
All data member should have a settable and gettable accessor function of the form:<br />
<br />
* void set_variable(type var);<br />
* type variable();<br />
<br />
These accessors should perform any required range checking and throw a "std::out_of_range" exceptions when not met. Other conditions that are not met can throw a "std::invalid_argument" exception.<br />
<br />
The big win is when you're staring at a block of code, it's obvious which of the things being assigned to persist outside of the block. This also keeps you from having to be creative with parameter names for methods and constructors. You just use the same name as the instance variable, without the <code>d_</code>.<br />
<br />
Example:<br />
<syntaxhighlight lang="C++"><br />
class gr_wonderfulness {<br />
std::string d_name;<br />
double d_wonderfulness_factor;<br />
<br />
public:<br />
wonderfulness(std::string name, double wonderfulness_factor)<br />
: d_name(name), d_wonderfulness_factor(wonderfulness_factor)<br />
{<br />
...<br />
}<br />
...<br />
};<br />
</syntaxhighlight><br />
* All class static data members shall begin with “s_” as in “s_<bar>”.<br />
<br />
=== Accessor Functions ===<br />
<br />
All signal processing blocks should have accessors to get and set values. For parameter param, the get function is '''param()''' and the set function is '''set_param(type new_value)''' for any variables that can be changed at runtime.<br />
<br />
=== Storage management ===<br />
<br />
Strongly consider using the boost smart pointer templates, scoped_ptr and shared_ptr. scoped_ptr should be used for locals that contain pointers to objects that we need to delete when we exit the current scope. shared_ptr implements transparent reference counting and is a major win. You never have to worry about calling delete. The right thing happens.<br />
<br />
See http://www.boost.org/libs/smart_ptr/smart_ptr.htm<br />
<br />
=== Exceptions ===<br />
<br />
Use the following exceptions:<br />
<br />
* std::runtime_error: when a runtime error has occurred<br />
* std::out_of_range: when a variable to be set is not within a given range<br />
* std::invalid_argument: when an argument is set to an improper value<br />
<br />
=== White Space Removal ===<br />
<br />
While this might sound a little pedantic, we encourage everyone to remove trailing white space on any lines edited as well as extraneous lines at the end of the file. This helps keep our code clean, but more importantly, it helps keep our git logs clean. Removal of trailing white spaces can be done by asking your favorite editor to do it for you on save. See the [[Coding_guide_impl#Setup-Recommendations-for-Standard-Editors|Setup Recommendations for Standard Editors]] guide at the end here for hints about how to get your editor to help you remove any trailing white space.<br />
<br />
You can use a program written for GNU Radio to remove any trailing white spaces from all files in your current directory tree. You can find this file in <source tree>/dtools/bin/remove-whitespace. It's a sed script that does the following:<br />
<br />
<pre>sed -i -e 's/\s\+$//g' $(find . -type f | grep -v '.git' | grep -v 'png' | grep -v 'eps')</pre><br />
Notice that it ignores all .git, .png, and .eps files to preserve their standard formatting.<br />
<br />
We infrequently run this script on the GNU Radio source tree, so depending on when you've last updated the source, you may or may not have a lot of white space that's snuck into the code.<br />
<br />
== Unit testing ==<br />
<br />
Build unit tests for everything non-trivial and run them after every change. Check out Extreme Programming: http://c2.com/cgi/wiki?ExtremeProgrammingRoadmap<br />
<br />
Unit tests should also be written for all examples.<br />
<br />
Unit tests can be written in Python or in C++. If possible, use Python unit tests (e.g., to test the output of signal processing blocks).<br />
<br />
=== C++ unit tests ===<br />
<br />
For C++, we use '''cppunit''', which has some issues, we have it nicely integrated into the build system. http://cppunit.sf.net<br />
<br />
Currently each top-level component <tlc> contains files qa_<tlc>.{h,cc} in the '''lib''' directory that bring together all the qa_<foo> test suites in the directory.<br />
<br />
=== Python unit tests ===<br />
<br />
We use the standard unittest package for unit testing of Python code.<br /><br />
http://docs.python.org/library/unittest.html<br />
<br />
We have added our own interface called '''gr_unittest''' to the unittest to overload and add some functions to make it simpler to test GNU Radio blocks. The Python QA functions are located in the top-level components '''python''' directory. They are all named “qa_<foo>.py”. Any Python file with the “qa_” prefix will be automatically tested.<br />
<br />
A general QA file looks like:<br />
<br />
<syntaxhighlight lang="python"><br />
from gnuradio import gr, gr_unittest<br />
import MODULE_swig as MODULE<br />
<br />
class test_foo(gr_unittest.TestCase):<br />
def setUp (self):<br />
self.tb = gr.top_block()<br />
<br />
def tearDown (self):<br />
self.tb = None<br />
<br />
def test_001(self):<br />
source_data = […]<br />
expected_data = […]<br />
<br />
src = gr.vector_source_X(source_data)<br />
head = gr.head(, )<br />
op = <br />
snk = gr.vector_sink_X()<br />
<br />
self.tb.connect(src, head, op, snk)<br />
self.tb.run()<br />
<br />
result_data = snk.data()<br />
self.assertXXX(expected_data, result_data, )<br />
<br />
if __name__ == '__main__':<br />
gr_unittest.run(test_foo, "test_foo.xml")<br />
</syntaxhighlight><br />
<br />
The '''self.assertXXX''' performs the test between the expected and actual data up. There are many assertions that are already part of the Python unittest module and gr_unittest adds many more, specifically to allow us to test different data types with a set amount of precision that is more appropriate for signal processing tests. These include:<br />
<br />
* assertComplexAlmostEqual(self, first, second, places=7, msg=None)<br />
* assertComplexAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
* assertComplexTuplesAlmostEqual(self, a, b, places=7, msg=None)<br />
* assertComplexTuplesAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
* assertFloatTuplesAlmostEqual(self, a, b, places=7, msg=None)<br />
* assertFloatTuplesAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
<br />
See the definitions of these in “gnuradio-runtime/python/gnuradio/gr_unittest.py” in the source code for details.<br />
<br />
== Setup Recommendations for Standard Editors ==<br />
<br />
Here are a few hints for some of our favorite editors to help you get your code formatted properly to work with our standards. Most of it's straight-forward, and these help make sure spacing and white space issues are kept consistent.<br />
<br />
=== Emacs ===<br />
<br />
<pre>(setq-default show-trailing-whitespace t) ;; show trailing white space as color def. next<br />
(set-face-background 'trailing-whitespace "#72706F")<br />
(setq-default show-tabs t) ;; show tabs; should be replaced by spaces<br />
(setq-default indent-tabs-mode nil) ;; use spaces, not tabs, for indenting<br />
(add-hook 'before-save-hook 'delete-trailing-whitespace) ;; remove trailing white space on save</pre><br />
=== ViM ===<br />
<br />
<pre>" You won't need every setting in this list. The below command is the one that will be most generally <br />
" useful to everyone.<br />
autocmd BufWritePre * :%s/\s\+$//e " Automatically remove trailing whitespace on save<br />
<br />
" In addition to having trailing whitespace automatically removed when you save a file, you can also <br />
" map a key sequence (e.g., `-w`) for the purpose. This is useful for remove trailing whitespace <br />
" without saving, but while editing.<br />
nnoremap w mz:%s/\s\+$//:let @/=''`z<br />
<br />
" If you would also like to visualize whitespace, you can try this out<br />
set listchars=tab:▸\ ,extends:❯,precedes:❮,trail:· " Show special characters for tabs, trailing spaces, and broken indents<br />
<br />
" Alternatively, you can show whitespace characters only while in Insert mode. For example:<br />
augroup trailing<br />
au!<br />
au InsertEnter * :set listchars+=tab:▸\ ,extends:❯,precedes:❮,trail:·,eol:¬<br />
au InsertLeave * :set listchars-=tab:▸\ ,extends:❯,precedes:❮,trail:·,eol:¬,<br />
augroup END</pre></div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Coding_guide_impl&diff=4111Coding guide impl2018-03-12T09:50:23Z<p>M3x1m0m: </p>
<hr />
<div>= Coding Guide =<br />
<br />
'''THIS PAGE IS A WORK IN PROGRESS TO DOCUMENT THE NEW CODING STYLE AS OF v3.7'''<br />
<br />
This document specifies the coding style and structure of blocks in GNU Radio as of version 3.7. The blocks are structured under top-level component (TLC) directories that combine similar blocks/algorithms (e.g., all filtering blocks are found in gr-filter). For purposes of explanation here, we will work with the generic TLC called '''foo''' and the block '''bar'''. Data types for member variables are generically called '''dtype'''.<br />
<br />
== Directory Structure ==<br />
<br />
* apps: any full-fledged applications specific to the component<br />
* doc: place for extra documentation, including Doxygen .dox files to describe the component.<br />
* examples: example code to demonstrate usage of component blocks and algorithms.<br />
* grc: GNU Radio Companion files and block tree.<br />
* include: public header files<br />
** include/foo: actual location of public header files. Headers in included using <code>#include <foo/bar.h></code>.<br />
* lib: location of the implementation source and private header files (bar_impl.h and bar_impl.cc, generally).<br />
* swig: location of SWIG interface file. We use a simplified structure that only includes the public header file for SWIGing.<br />
<br />
== Namespaces ==<br />
<br />
Components are described under the namespace '''gr::foo'''<br />
<br />
In Python, each TLC is a component under the '''gnuradio''' module. So to get access to blocks in '''foo''', do:<br />
<br />
<syntaxhighlight lang="python">from gnuradio import foo</syntaxhighlight><br />
<br />
== Code Format ==<br />
<br />
We want to try and make a consistent look and feel to the code in GNU Radio. Here are a few things to keep in mind while writing code.<br />
<br />
All lines of code should not exceed 70 characters on a width. Often, there is a logical break in the line of code that allows it to easily span two lines, such as between arguments to the function. Sometimes not, though, so make your best judgment here. The reason for this is to make the code format easily readable in both an editor and when printed out.<br />
<br />
As stated elsewhere, no CamelCase. All distinct words in a function or variable name should use underscores:<br />
<br />
<syntaxhighlight lang="C++">int foo_bar</syntaxhighlight><br />
<br />
In the header file, functions should all be written on the same line, up to the 70-character limit.<br />
<br />
<syntaxhighlight lang="C++">int bar(int arg1, int arg2);</syntaxhighlight><br />
<br />
In the C++ implementation file, the function should have its return data type indicated on a separate line.<br />
<br />
<syntaxhighlight lang="C++"><br />
int <br />
foo::bar(int arg1, int arg2)<br />
</syntaxhighlight><br />
<br />
Curly brace convention is as follows. After a function name, the curly brace should follow on the next line, and the ending curly brace should be on its own line. Inside of a function, such as for an '''if''' statement or '''for''' loop, the beginning curly brace should come after the statement, while the end curly brace should be on its own line. Note the spacing used in this example, too.<br />
<br />
<syntaxhighlight lang="C++"><br />
int <br />
foo::bar(int arg1, int arg2)<br />
{<br />
if(arg1 == 0) {<br />
;<br />
}<br />
else {<br />
;<br />
}<br />
return 0;<br />
}</syntaxhighlight><br />
Although C++ allows single-line statements after a '''for''' or '''if''' statement to exist without curly braces surrounding them, it is recommended to ''always'' use curly braces. This may help prevent problems in the future if another line is added to the code block.<br />
<br />
=== Block naming conventions ===<br />
<br />
Blocks should be given a name that indicates its purpose as well as its input and output data types. If a block lives in the GNU Radio core and performs the function FOO that takes complex in and produces floating points outputs, it's name should be:<br />
<br />
<code>foo_cf</code><br />
<br />
This boils down to:<br />
<br />
<code><name>_<input><ouput></code><br />
<br />
This lives in the namespace '''gr::<module>'''.<br />
<br />
The ''name'' indicates the function of the block, such as "add" or "fir_filter".<br />
<br />
The ''input'' indicates the input type while ''output'' indicates the output type. Currently, the types are:<br />
<br />
* c: complex (32-bit floats for both I and Q)<br />
* f: float (32-bit single precision IEEE 754 style)<br />
* i: integer (32-bit signed integers)<br />
* s: short (16-bit signed integers)<br />
* b: bytes (8-bit signed integers)<br />
* TODO: How do we indicate a block has no inputs (a source) or no outputs (a sink)?<br />
<br />
There is no differentiation between bytes and bits, even though some blocks use unpacked format where a single bit is represented in a byte. If this is the case, it should be made clear in the documentation for the block.<br />
<br />
GNU Radio also has a concept of vectors, where the data into or out of the block are vectors of items. The "v" is then used to indicate if this is the case. A block that uses vectors should indicate it by the type:<br />
<br />
<code><vector in><input><vector out><output></code><br />
<br />
So a block that takes vectors in but streams samples out would look like ''vcc'', but a block that does vectors for both input and output will look like ''vcvc''.<br />
<br />
'''NOTE''': this is a new convention and older blocks that use vectors do not follow this. They generally only indicate that vectors are used somewhere by putting a single "v" in front of the data type indicator.<br />
<br />
== Block Coding Guidelines ==<br />
<br />
All GNU Radio blocks have a structure and style that we try to keep consistent. There are also a number of concepts and general practices to understand when building a new block. We have a separate page describing the [[BlocksCodingGuide|block coding guidelines]]:<br />
<br />
== Standard command line options ==<br />
<br />
When writing programs that are executable from the command line, please follow these guidelines for command line argument names (short and long) and types of the arguments. We list them below using the Python optparse syntax. In general, the default value should be coded into the help string using the "... [default=%default]" syntax.<br />
<br />
The following shows the boilerplate text for using the optparse Python module (see http://docs.python.org/library/optparse.html).<br />
<br />
<pre><br />
from optparse import OptionParser<br />
….<br />
def :<br />
usage = ””<br />
desc = “”<br />
parser = OptionParser(usage=usage, description=desc, option_class=eng_option)<br />
parser.add_option("-a", "--args", type="string", default="",<br />
help="UHD device address args , [default=%default]")<br />
….<br />
(options, args) = parser.parse_args()<br />
</pre><br />
=== Standard options names by parameter ===<br />
<br />
Whenever you want an integer, use the "intx" type. This allows the user to input decimal, hex or octal numbers. E.g., 10, 012, 0xa.<br />
<br />
Whenever you want a float, use the "eng_float" type. This allows the user to input numbers with SI suffixes. E.g, 10000, 10k, 10M, 10m, 92.1M<br />
<br />
If your program allows the user to specify values for any of the following parameters, please use these options to specify them:<br />
<br />
* To specify a frequency (typically an RF center frequency) use:<br />
<br />
<code>add_option("-f", "--freq", type="eng_float", default=<your-default-here>, help="set frequency to FREQ [default=%default]")</code><br />
<br />
* To specify a sample rate use:<br />
<br />
<code>add_option("-s", "--samp-rate", type="eng_float", default=<your-default-here>, help="set sample rate to DECIM [default=%default]")</code><br />
<br />
* To specify a gain setting use:<br />
<br />
<code>add_option("-g", "--gain", type="eng_float", default=<your-default-here>, help="set gain in dB [default=%default]")</code><br />
<br />
* To specify the number of channels of something use:<br />
<br />
<code>add_option("-n", "--nchannels", type="intx", default=1, help="specify number of channels [default=%default]")</code><br />
<br />
* To specify an output filename use:<br />
<br />
<code>add_option("-o", "--output-filename", type="string", default=<your-default-here>, help="specify output-filename [default=%default]")</code><br />
<br />
* If your application has a verbose option, use:<br />
<br />
<code>add_option('-v', '--verbose', action="store_true", default=False, help="verbose output")</code><br />
<br />
=== Example: Audio source ===<br />
<br />
<code>add_option("-I", "--audio-input", type="string", default="", help="pcm input device name. E.g., hw:0,0 or /dev/dsp")</code><br />
<br />
The default must be "". This allows an audio module-dependent default to be specified in the user preferences file.<br />
<br />
=== Example: Audio sink ===<br />
<br />
<code>add_option("-O", "--audio-output", type="string", default="", help="pcm output device name. E.g., hw:0,0 or /dev/dsp")</code><br />
<br />
The default must be "". This allows an audio module-dependent default to be specified in the user preferences file.<br />
<br />
== C++ ==<br />
<br />
=== Global names ===<br />
<br />
All globally visible names (types, functions, variables, consts, etc) shall begin with a "package prefix", followed by an '_'.<br />
<br />
=== Package prefixes ===<br />
<br />
We have moved away from the concept of package prefixes and use namespaces instead. See the [[BlocksCodingGuide]] page for description and examples of this. For older code, we specified what kind of block it was using a package prefix. For example, we had:<br />
<br />
<code>gr_</code>: Almost everything<br />
<br />
<code>gri_</code>: Implementation primitives. Sometimes we have both a gr_<foo> and a gri_<foo>. In that case, gr_<foo> would be derived from gr_block and gri_<foo> would be the low level guts of the function.<br />
<br />
<code>qa_</code>: Quality Assurance. Test code.<br />
<br />
Now, a block that used to be called '''gr_foo''' that is a general block would now be located in the top-level component '''gr-block'''. The header file would be in '''gr-block/include/block/foo.h'''. It would then be in the namespace '''gr::block'''.<br />
<br />
Implementation files are just named for what they are. For example, the '''fft_vcc_fftw''' block uses the implementation of the FFT found in '''gr::fft::fft'''. Sometimes, when there is a name collisions, we put these under the namespace '''gr::foo::kernel'''. See the '''gr-filter''' blocks to create FIR filters.<br />
<br />
=== Naming conventions ===<br />
<br />
* Functions and variable are underscore_separated_words (No CamelCase).<br />
* All data members of a class use a "d_" prefix to them, such as '''d_bar'''. They should also be private unless there is a very good reason not to.<br />
<br />
All data member should have a settable and gettable accessor function of the form:<br />
<br />
* void set_variable(type var);<br />
* type variable();<br />
<br />
These accessors should perform any required range checking and throw a "std::out_of_range" exceptions when not met. Other conditions that are not met can throw a "std::invalid_argument" exception.<br />
<br />
The big win is when you're staring at a block of code, it's obvious which of the things being assigned to persist outside of the block. This also keeps you from having to be creative with parameter names for methods and constructors. You just use the same name as the instance variable, without the <code>d_</code>.<br />
<br />
Example:<br />
<br />
<pre>class gr_wonderfulness {<br />
std::string d_name;<br />
double d_wonderfulness_factor;<br />
<br />
public:<br />
wonderfulness(std::string name, double wonderfulness_factor)<br />
: d_name(name), d_wonderfulness_factor(wonderfulness_factor)<br />
{<br />
...<br />
}<br />
...<br />
};</pre><br />
* All class static data members shall begin with “s_” as in “s_<bar>”.<br />
<br />
=== Accessor Functions ===<br />
<br />
All signal processing blocks should have accessors to get and set values. For parameter param, the get function is '''param()''' and the set function is '''set_param(type new_value)''' for any variables that can be changed at runtime.<br />
<br />
=== Storage management ===<br />
<br />
Strongly consider using the boost smart pointer templates, scoped_ptr and shared_ptr. scoped_ptr should be used for locals that contain pointers to objects that we need to delete when we exit the current scope. shared_ptr implements transparent reference counting and is a major win. You never have to worry about calling delete. The right thing happens.<br />
<br />
See http://www.boost.org/libs/smart_ptr/smart_ptr.htm<br />
<br />
=== Exceptions ===<br />
<br />
Use the following exceptions:<br />
<br />
* std::runtime_error: when a runtime error has occurred<br />
* std::out_of_range: when a variable to be set is not within a given range<br />
* std::invalid_argument: when an argument is set to an improper value<br />
<br />
=== White Space Removal ===<br />
<br />
While this might sound a little pedantic, we encourage everyone to remove trailing white space on any lines edited as well as extraneous lines at the end of the file. This helps keep our code clean, but more importantly, it helps keep our git logs clean. Removal of trailing white spaces can be done by asking your favorite editor to do it for you on save. See the [[Coding_guide_impl#Setup-Recommendations-for-Standard-Editors|Setup Recommendations for Standard Editors]] guide at the end here for hints about how to get your editor to help you remove any trailing white space.<br />
<br />
You can use a program written for GNU Radio to remove any trailing white spaces from all files in your current directory tree. You can find this file in <source tree>/dtools/bin/remove-whitespace. It's a sed script that does the following:<br />
<br />
<pre>sed -i -e 's/\s\+$//g' $(find . -type f | grep -v '.git' | grep -v 'png' | grep -v 'eps')</pre><br />
Notice that it ignores all .git, .png, and .eps files to preserve their standard formatting.<br />
<br />
We infrequently run this script on the GNU Radio source tree, so depending on when you've last updated the source, you may or may not have a lot of white space that's snuck into the code.<br />
<br />
== Unit testing ==<br />
<br />
Build unit tests for everything non-trivial and run them after every change. Check out Extreme Programming: http://c2.com/cgi/wiki?ExtremeProgrammingRoadmap<br />
<br />
Unit tests should also be written for all examples.<br />
<br />
Unit tests can be written in Python or in C++. If possible, use Python unit tests (e.g., to test the output of signal processing blocks).<br />
<br />
=== C++ unit tests ===<br />
<br />
For C++, we use '''cppunit''', which has some issues, we have it nicely integrated into the build system. http://cppunit.sf.net<br />
<br />
Currently each top-level component <tlc> contains files qa_<tlc>.{h,cc} in the '''lib''' directory that bring together all the qa_<foo> test suites in the directory.<br />
<br />
=== Python unit tests ===<br />
<br />
We use the standard unittest package for unit testing of Python code.<br /><br />
http://docs.python.org/library/unittest.html<br />
<br />
We have added our own interface called '''gr_unittest''' to the unittest to overload and add some functions to make it simpler to test GNU Radio blocks. The Python QA functions are located in the top-level components '''python''' directory. They are all named “qa_<foo>.py”. Any Python file with the “qa_” prefix will be automatically tested.<br />
<br />
A general QA file looks like:<br />
<br />
<pre>from gnuradio import gr, gr_unittest<br />
import MODULE_swig as MODULE<br />
<br />
class test_foo(gr_unittest.TestCase):<br />
def setUp (self):<br />
self.tb = gr.top_block()<br />
<br />
def tearDown (self):<br />
self.tb = None<br />
<br />
def test_001(self):<br />
source_data = […]<br />
expected_data = […]<br />
<br />
src = gr.vector_source_X(source_data)<br />
head = gr.head(, )<br />
op = <br />
snk = gr.vector_sink_X()<br />
<br />
self.tb.connect(src, head, op, snk)<br />
self.tb.run()<br />
<br />
result_data = snk.data()<br />
self.assertXXX(expected_data, result_data, )<br />
<br />
if __name__ == '__main__':<br />
gr_unittest.run(test_foo, "test_foo.xml")</pre><br />
The '''self.assertXXX''' performs the test between the expected and actual data up. There are many assertions that are already part of the Python unittest module and gr_unittest adds many more, specifically to allow us to test different data types with a set amount of precision that is more appropriate for signal processing tests. These include:<br />
<br />
* assertComplexAlmostEqual(self, first, second, places=7, msg=None)<br />
* assertComplexAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
* assertComplexTuplesAlmostEqual(self, a, b, places=7, msg=None)<br />
* assertComplexTuplesAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
* assertFloatTuplesAlmostEqual(self, a, b, places=7, msg=None)<br />
* assertFloatTuplesAlmostEqual2(self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None)<br />
<br />
See the definitions of these in “gnuradio-runtime/python/gnuradio/gr_unittest.py” in the source code for details.<br />
<br />
== Setup Recommendations for Standard Editors ==<br />
<br />
Here are a few hints for some of our favorite editors to help you get your code formatted properly to work with our standards. Most of it's straight-forward, and these help make sure spacing and white space issues are kept consistent.<br />
<br />
=== Emacs ===<br />
<br />
<pre>(setq-default show-trailing-whitespace t) ;; show trailing white space as color def. next<br />
(set-face-background 'trailing-whitespace "#72706F")<br />
(setq-default show-tabs t) ;; show tabs; should be replaced by spaces<br />
(setq-default indent-tabs-mode nil) ;; use spaces, not tabs, for indenting<br />
(add-hook 'before-save-hook 'delete-trailing-whitespace) ;; remove trailing white space on save</pre><br />
=== ViM ===<br />
<br />
<pre>" You won't need every setting in this list. The below command is the one that will be most generally <br />
" useful to everyone.<br />
autocmd BufWritePre * :%s/\s\+$//e " Automatically remove trailing whitespace on save<br />
<br />
" In addition to having trailing whitespace automatically removed when you save a file, you can also <br />
" map a key sequence (e.g., `-w`) for the purpose. This is useful for remove trailing whitespace <br />
" without saving, but while editing.<br />
nnoremap w mz:%s/\s\+$//:let @/=''`z<br />
<br />
" If you would also like to visualize whitespace, you can try this out<br />
set listchars=tab:▸\ ,extends:❯,precedes:❮,trail:· " Show special characters for tabs, trailing spaces, and broken indents<br />
<br />
" Alternatively, you can show whitespace characters only while in Insert mode. For example:<br />
augroup trailing<br />
au!<br />
au InsertEnter * :set listchars+=tab:▸\ ,extends:❯,precedes:❮,trail:·,eol:¬<br />
au InsertLeave * :set listchars-=tab:▸\ ,extends:❯,precedes:❮,trail:·,eol:¬,<br />
augroup END</pre></div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4109Interfacing Hardware with a C++ OOT Module2018-03-05T15:54:29Z<p>M3x1m0m: /* Using gr::block Facilities */</p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library and corresponding dependencies correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this writing a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module] for every external library. Modules for cmake in GNURadio OOTs are located under ''cmake/Modules''. In this directory create a file and give it a proper name e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<source lang="cmake"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</source><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module: <br />
<source lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</source> <br />
The code above can be placed into the segment where cmake searches for GNURadio dependencies. <br />
<br />
So far so good. We triggered cmake to seek the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed on the system. cmake needs to know, that we want to link this library against our code. Therefore, it provides a macro, which is called in ''lib/CMakeLists.txt''. <br />
<source lang="cmake"><br />
target_link_libraries(gnuradio-limesdr ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${LIMESUITE_LIBRARIES})<br />
</source><br />
The final step is to take the last unused variable and communicate to cmake, that there are header files we want to have added to our project. <br />
<source lang="cmake"><br />
########################################################################<br />
# Setup the include and linker paths<br />
########################################################################<br />
include_directories(<br />
${CMAKE_SOURCE_DIR}/lib<br />
${CMAKE_SOURCE_DIR}/include<br />
${CMAKE_BINARY_DIR}/lib<br />
${CMAKE_BINARY_DIR}/include<br />
${Boost_INCLUDE_DIRS}<br />
${CPPUNIT_INCLUDE_DIRS}<br />
${GNURADIO_ALL_INCLUDE_DIRS}<br />
${LIMESUITE_INCLUDE_DIRS}<br />
)<br />
</source><br />
In case you did not mess up something, you will be able to use the the driver library from now own in your code with a normal include like<br />
<source lang="c++"><br />
#include <LimeSuite.h><br />
</source><br />
If something is wrong, you perhaps will get a quite nasty and really annoying error message similar to ''AttributeError: 'module' object has no attribute 'MODULE_NAME'''. Usually, this can be interpreted as, that you did configure something wrong in the steps described above and you should iterate through the whole procedure again. <br />
<br />
Fortunately, there is a way provided by a program called ''ldd'' ("print shared object dependencies", says man ldd) to find out if you linked the dynamic library properly against your library (produced and deployed when you type sudo make install):<br />
<source><br />
ldd /usr/local/lib/libgnuradio-limesdr.so | grep LimeSuite<br />
</source><br />
The output should be something like<br />
<source><br />
libLimeSuite.so.18.02-1 => /usr/local/lib/libLimeSuite.so.18.02-1 (0x00007f5b86be8000)<br />
</source><br />
and can be interpreted as a success of the previous steps. Hurray! If you can not scream the 'Hurray!' yet, since it does not work, the following might be some helpful piece code for you<br />
<source lang="cmake"><br />
########################################################################<br />
# Debugging<br />
########################################################################<br />
set(PRINT_ALL_VARS 1)<br />
if(PRINT_ALL_VARS)<br />
get_cmake_property(_variableNames VARIABLES)<br />
foreach (_variableName ${_variableNames})<br />
message(STATUS "${_variableName}=${${_variableName}}")<br />
endforeach()<br />
endif(PRINT_ALL_VARS)<br />
</source> <br />
which is borrowed from [https://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script#9328525 here].<br />
<br />
== Using gr::block Facilities ==<br />
But how is the hardware actually correctly initialized and deinitialized within GNURadio? What to do when something goes wrong? This section tries to answer these questions. <br />
<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block], which is the base class for every block, provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. <br />
<br />
''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. <br />
<br />
However, one can not solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One approach for a sane design might be to call ''stop ()'' oneself as part of the exception handling in a catch block. An example implementation of ''start()'' is the following:<br />
<br />
<source lang="c++"><br />
try<br />
{<br />
LOG << "start() called." << std::endl;<br />
// Try to initialize HW. Throws if HW ends up in a faulty state.<br />
init_hw(d_settings);<br />
}<br />
catch(const std::exception& e)<br />
{<br />
LOG << "Problem during initialization: "<< e.what();<br />
stop();<br />
// Tell GNURadio.<br />
throw;<br />
} <br />
</source><br />
<br />
The above example tries to initialize the HW, if it fails, the device will be deinitialized again as ''stop()'' is called.<br />
<br />
It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly, given, that you implemented the ''stop()'' function correctly. <br />
<br />
It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called acc. to standard C++, which is something nobody wants, right? So '''DO NOT THROW EXCEPTIONS IN stop()''' unless you know exactly what you are doing. <br />
<br />
The implementation of the stop function can be much simpler as it avoids exceptions:<br />
<br />
<source lang="c++"><br />
LOG << "stop() called.";<br />
// Deinitialize HW.<br />
deinit_hw();<br />
</source><br />
<br />
One more thing to mention is, that having a signal source with a fixed sample rate, should embed the function ''set_output_multiple(...)'' provided by [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] somewhere in the constructor. The argument of this function should be the size of the buffer for communication between the (hopefully) asynchronous driver and the GNURadio block. This makes sure, that the data size the work function has to output (''noutput_items'') is always a multiple of buffer's size and it is always OK to return less samples. Yet, it is not recommended to produce less samples than specified with ''noutput_items''.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4108Interfacing Hardware with a C++ OOT Module2018-03-05T15:19:35Z<p>M3x1m0m: /* Linking against Driver Library */</p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library and corresponding dependencies correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this writing a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module] for every external library. Modules for cmake in GNURadio OOTs are located under ''cmake/Modules''. In this directory create a file and give it a proper name e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<source lang="cmake"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</source><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module: <br />
<source lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</source> <br />
The code above can be placed into the segment where cmake searches for GNURadio dependencies. <br />
<br />
So far so good. We triggered cmake to seek the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed on the system. cmake needs to know, that we want to link this library against our code. Therefore, it provides a macro, which is called in ''lib/CMakeLists.txt''. <br />
<source lang="cmake"><br />
target_link_libraries(gnuradio-limesdr ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${LIMESUITE_LIBRARIES})<br />
</source><br />
The final step is to take the last unused variable and communicate to cmake, that there are header files we want to have added to our project. <br />
<source lang="cmake"><br />
########################################################################<br />
# Setup the include and linker paths<br />
########################################################################<br />
include_directories(<br />
${CMAKE_SOURCE_DIR}/lib<br />
${CMAKE_SOURCE_DIR}/include<br />
${CMAKE_BINARY_DIR}/lib<br />
${CMAKE_BINARY_DIR}/include<br />
${Boost_INCLUDE_DIRS}<br />
${CPPUNIT_INCLUDE_DIRS}<br />
${GNURADIO_ALL_INCLUDE_DIRS}<br />
${LIMESUITE_INCLUDE_DIRS}<br />
)<br />
</source><br />
In case you did not mess up something, you will be able to use the the driver library from now own in your code with a normal include like<br />
<source lang="c++"><br />
#include <LimeSuite.h><br />
</source><br />
If something is wrong, you perhaps will get a quite nasty and really annoying error message similar to ''AttributeError: 'module' object has no attribute 'MODULE_NAME'''. Usually, this can be interpreted as, that you did configure something wrong in the steps described above and you should iterate through the whole procedure again. <br />
<br />
Fortunately, there is a way provided by a program called ''ldd'' ("print shared object dependencies", says man ldd) to find out if you linked the dynamic library properly against your library (produced and deployed when you type sudo make install):<br />
<source><br />
ldd /usr/local/lib/libgnuradio-limesdr.so | grep LimeSuite<br />
</source><br />
The output should be something like<br />
<source><br />
libLimeSuite.so.18.02-1 => /usr/local/lib/libLimeSuite.so.18.02-1 (0x00007f5b86be8000)<br />
</source><br />
and can be interpreted as a success of the previous steps. Hurray! If you can not scream the 'Hurray!' yet, since it does not work, the following might be some helpful piece code for you<br />
<source lang="cmake"><br />
########################################################################<br />
# Debugging<br />
########################################################################<br />
set(PRINT_ALL_VARS 1)<br />
if(PRINT_ALL_VARS)<br />
get_cmake_property(_variableNames VARIABLES)<br />
foreach (_variableName ${_variableNames})<br />
message(STATUS "${_variableName}=${${_variableName}}")<br />
endforeach()<br />
endif(PRINT_ALL_VARS)<br />
</source> <br />
which is borrowed from [https://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script#9328525 here].<br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. However, one can no solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One work around might be to call ''stop ()'' oneself as part of the exception handling in a catch block. It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly. It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called, which is something nobody wants.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4107Interfacing Hardware with a C++ OOT Module2018-03-05T15:19:09Z<p>M3x1m0m: /* Linking against Driver Library */</p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library and corresponding dependencies correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this writing a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module] for every external library. Modules for cmake in GNURadio OOTs are located under ''cmake/Modules''. In this directory create a file and give it a proper name e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<source lang="cmake"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</source><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module: <br />
<source lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</source> <br />
The code above can be placed into the segment where cmake searches for GNURadio dependencies. <br />
<br />
So far so good. We triggered cmake to seek the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed on the system. cmake needs to know, that we want to link this library against our code. Therefore, it provides a macro, which is called in ''lib/CMakeLists.txt''. <br />
<source lang="cmake"><br />
target_link_libraries(gnuradio-limesdr ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${LIMESUITE_LIBRARIES})<br />
</source><br />
The final step is to take the last unused variable and communicate to cmake, that there are header files we want to have added to our project. <br />
<source lang="cmake"><br />
########################################################################<br />
# Setup the include and linker paths<br />
########################################################################<br />
include_directories(<br />
${CMAKE_SOURCE_DIR}/lib<br />
${CMAKE_SOURCE_DIR}/include<br />
${CMAKE_BINARY_DIR}/lib<br />
${CMAKE_BINARY_DIR}/include<br />
${Boost_INCLUDE_DIRS}<br />
${CPPUNIT_INCLUDE_DIRS}<br />
${GNURADIO_ALL_INCLUDE_DIRS}<br />
${LIMESUITE_INCLUDE_DIRS}<br />
)<br />
</source><br />
In case you did not mess up something, you will be able to use the the driver library from now own in your code with a normal include like<br />
<source lang="c++"><br />
#include <LimeSuite.h><br />
</source><br />
If something is wrong, you perhaps will get a quite nasty and really annoying error message similar to ''AttributeError: 'module' object has no attribute 'MODULE_NAME'''. Usually, this can be interpreted as, that you did configure something wrong in the steps described above and you should iterate through the whole procedure again. <br />
<br />
Fortunately, there is a way provided by a program called ''ldd'' ("print shared object dependencies", says man ldd) to find out if you linked the dynamic library properly against your library (produced and deployed when you type sudo make install):<br />
<source><br />
ldd /usr/local/lib/libgnuradio-limesdr.so | grep LimeSuite<br />
</source><br />
The output should be something like<br />
<source><br />
libLimeSuite.so.18.02-1 => /usr/local/lib/libLimeSuite.so.18.02-1 (0x00007f5b86be8000)<br />
</source><br />
and can be interpreted as a success of the previous steps. Hurray! If you can not scream the 'Hurray!' yet, since it does not work, the following might be some helpful piece code for you<br />
<source lang="c++"><br />
########################################################################<br />
# Debugging<br />
########################################################################<br />
set(PRINT_ALL_VARS 1)<br />
if(PRINT_ALL_VARS)<br />
get_cmake_property(_variableNames VARIABLES)<br />
foreach (_variableName ${_variableNames})<br />
message(STATUS "${_variableName}=${${_variableName}}")<br />
endforeach()<br />
endif(PRINT_ALL_VARS)<br />
</source> <br />
which is borrowed from [https://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script#9328525 here].<br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. However, one can no solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One work around might be to call ''stop ()'' oneself as part of the exception handling in a catch block. It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly. It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called, which is something nobody wants.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4106Interfacing Hardware with a C++ OOT Module2018-03-05T14:46:48Z<p>M3x1m0m: /* Linking against Driver Library */</p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library and corresponding dependencies correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this writing a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module] for every external library. Modules for cmake in GNURadio OOTs are located under ''cmake/Modules''. In this file create a file e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<source lang="cmake"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</source><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module: <br />
<source lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</source> <br />
That piece of code can be placed into the segment where cmake searches for GNURadio dependencies. <br />
<br />
Well. So far we looked for the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed. cmake needs to know, that we want to link this library against our code. Therefore, it provides a macro, which is called in ''lib/CMakeLists.txt''. <br />
<source lang="cmake"><br />
target_link_libraries(gnuradio-limesdr ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${LIMESUITE_LIBRARIES})<br />
</source><br />
The final step to take is to take the last unused variable and communicate to cmake, that there are header files we want to have added to our project. <br />
<source lang="cmake"><br />
########################################################################<br />
# Setup the include and linker paths<br />
########################################################################<br />
include_directories(<br />
${CMAKE_SOURCE_DIR}/lib<br />
${CMAKE_SOURCE_DIR}/include<br />
${CMAKE_BINARY_DIR}/lib<br />
${CMAKE_BINARY_DIR}/include<br />
${Boost_INCLUDE_DIRS}<br />
${CPPUNIT_INCLUDE_DIRS}<br />
${GNURADIO_ALL_INCLUDE_DIRS}<br />
${LIMESUITE_INCLUDE_DIRS}<br />
)<br />
</source><br />
In case you did not mess up anything, you will be able to use the the driver library from now own in your code with a normal include like<br />
<source lang="c++"><br />
#include <LimeSuite.h><br />
</source><br />
You might might get a quite nasty and really annoying error message similar to ''AttributeError: 'module' object has no attribute 'MODULE_NAME'''. This means usually, that you messed something up in the steps described above and you should iterate through everything again. There is a way provided by a program called ''ldd'' ("print shared object dependencies", says man ldd) to find out if you linked the dynamic library properly against your library (produced when you type sudo make install). <br />
<source><br />
ldd /usr/local/lib/libgnuradio-limesdr.so | grep LimeSuite<br />
</source><br />
The output should be something like<br />
<source><br />
libLimeSuite.so.18.02-1 => /usr/local/lib/libLimeSuite.so.18.02-1 (0x00007f5b86be8000)<br />
</source><br />
and can be interpreted as a success of the previous steps. Hurray! If you can not scream the 'Hurray!' yet, since it does not work, the following might be some helpful piece code for you<br />
<source lang="c++"><br />
########################################################################<br />
# Debugging<br />
########################################################################<br />
set(PRINT_ALL_VARS 0)<br />
if(PRINT_ALL_VARS)<br />
get_cmake_property(_variableNames VARIABLES)<br />
foreach (_variableName ${_variableNames})<br />
message(STATUS "${_variableName}=${${_variableName}}")<br />
endforeach()<br />
endif(PRINT_ALL_VARS)<br />
</source> <br />
which is borrowed from [https://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script#9328525 here].<br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. However, one can no solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One work around might be to call ''stop ()'' oneself as part of the exception handling in a catch block. It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly. It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called, which is something nobody wants.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4105Interfacing Hardware with a C++ OOT Module2018-03-05T13:06:07Z<p>M3x1m0m: </p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this using a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module]. Modules for cmake are located under ''cmake/Modules''. In this file create a file e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<source lang="cmake"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</source><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module. <br />
<source lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</source> <br />
That piece of code can be placed into the segment where cmake searches for GNURadio dependencies. So far we looked for the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed. cmake needs to know, that we want to link this library against our code. Therefore, it provides a macro, which is called in ''lib/CMakeLists.txt''. <br />
<source lang="cmake"><br />
target_link_libraries(gnuradio-limesdr ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${LIMESUITE_LIBRARIES})<br />
</source><br />
The final step to take is to take the last unused variable and communicate to cmake, that there are header files we want to have added to our project. <br />
<source lang="cmake"><br />
########################################################################<br />
# Setup the include and linker paths<br />
########################################################################<br />
include_directories(<br />
${CMAKE_SOURCE_DIR}/lib<br />
${CMAKE_SOURCE_DIR}/include<br />
${CMAKE_BINARY_DIR}/lib<br />
${CMAKE_BINARY_DIR}/include<br />
${Boost_INCLUDE_DIRS}<br />
${CPPUNIT_INCLUDE_DIRS}<br />
${GNURADIO_ALL_INCLUDE_DIRS}<br />
${LIMESUITE_INCLUDE_DIRS}<br />
)<br />
</source> <br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. However, one can no solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One work around might be to call ''stop ()'' oneself as part of the exception handling in a catch block. It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly. It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called, which is something nobody wants.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4104Interfacing Hardware with a C++ OOT Module2018-03-05T12:57:56Z<p>M3x1m0m: </p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Linking against Driver Library ==<br />
The first thing you will need when writing a block, that connects your design to hardware, is getting the driver compiled and linked against your module. To do this make sure you have installed the driver library correctly on your system e.g. by compiling a small example application executing some basic routines of the driver. What you need next is to tell cmake where to find the libraries you want to include. It is known as best practice to do this using a [https://cmake.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules find module]. Modules for cmake are located under ''cmake/Modules''. In this file create a file e.g. ''FindLimeSuite.cmake''. The find module can for instance look as follows:<br />
<source lang="cmake"><br />
# - Looking for LimeSuite on the machine<br />
# Once done this will define<br />
#<br />
# LIMESUITE_INCLUDE_DIRS - Where to find the header files.<br />
# LIMESUITE_LIBRARIES - Where to find the dynamically loaded lib. files (.so).<br />
# LIMESUITE_FOUND - True if LIMESUITE found.<br />
<br />
find_path(<br />
LIMESUITE_INCLUDE_DIR<br />
LimeSuite.h<br />
PATHS /usr/include /usr/include/lime /usr/local/include/lime<br />
DOC "LimeSuite include file."<br />
)<br />
<br />
find_library(<br />
LIMESUITE_LIBRARY<br />
LimeSuite<br />
PATHS /usr/lib/x86_64-linux-gnu /usr/lib /usr/local/lib<br />
DOC "LimeSuit shared library obejct file."<br />
)<br />
<br />
if(LIMESUITE_INCLUDE_DIR AND LIMESUITE_LIBRARY)<br />
set(LIMESUITE_FOUND 1)<br />
set(LIMESUITE_LIBRARIES ${LIMESUITE_LIBRARY})<br />
set(LIMESUITE_INCLUDE_DIRS ${LIMESUITE_INCLUDE_DIR})<br />
else()<br />
set(LIMESUITE_FOUND 0)<br />
set(LIMESUITE_LIBRARIES)<br />
set(LIMESUITE_INCLUDE_DIRS)<br />
endif()<br />
<br />
mark_as_advanced(LIMESUITE_INCLUDE_DIR)<br />
mark_as_advanced(LIMESUITE_LIBRARY)<br />
mark_as_advanced(LIMESUITE_FOUND)<br />
<br />
if(NOT LIMESUITE_FOUND)<br />
set(LIMESUITE_DIR_MESSAGE "LimeSuite was not found. Has it been installed?")<br />
if(NOT LIMESUITE_FIND_QUIETLY)<br />
message(STATUS "${LIMESUITE_DIR_MESSAGE}")<br />
else()<br />
if(LIMESUITE_FIND_REQUIRED)<br />
message(FATAL_ERROR "${LIMESUITE_DIR_MESSAGE}")<br />
endif()<br />
endif()<br />
else()<br />
message(STATUS "Found LimeSuite: ${LIMESUITE_LIBRARIES} and ${LIMESUITE_INCLUDE_DIRS}.")<br />
endif()<br />
</source><br />
<br />
As one can see this module, silently assumed it is written correctly, sets three variables: ''LIMESUITE_INCLUDE_DIRS'', ''LIMESUITE_LIBRARIES'' and ''LIMESUITE_FOUND''. These variables are put to use later on. Our module is executed by calling it in the CMakeLists.txt, located in the root of the OOT module. <br />
<source lang="cmake"><br />
# Call the module<br />
find_package(LimeSuite)<br />
if(NOT LIMESUITE_FOUND)<br />
message(FATAL_ERROR "LimeSuite required to compile limesdr.")<br />
endif()<br />
</source> <br />
That piece of code can be placed into the segment where cmake searches for GNURadio dependencies. So far we looked for the library and used ''LIMESUITE_FOUND'' to see if cmake was successful. However, we have not made use of the other two variables yet, right? ''LIMESUITE_LIBRARIES'' holds the directory where the dynamically linked library (a .so file) is installed. cmake needs to know, that we want to link this library against our code. <br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. However, one can no solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One work around might be to call ''stop ()'' oneself as part of the exception handling in a catch block. It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly. It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called, which is something nobody wants.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4080Interfacing Hardware with a C++ OOT Module2018-02-10T12:06:36Z<p>M3x1m0m: </p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. However, one can no solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One work around might be to call ''stop ()'' oneself as part of the exception handling in a catch block. It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly. It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' might be called, which is something nobody wants.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4079Interfacing Hardware with a C++ OOT Module2018-02-08T20:59:46Z<p>M3x1m0m: This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR.</p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls ''stop ()'' when something goes wrong, for instance when a exception is thrown in the ''work ()'' function. However, one can no solely trust on this mechanism as it seems not to be called when ''start ()'' fails. One work around might be to call ''stop ()'' oneself as part of the exception handling in a catch block. It is in any case better to use exceptions combined with these two functions instead of relying on constructor and destructor designing blocks to interface with hardware. When a flowgraph is exited normally (e.g. using your preferred window manager), the blocks destructors are called as well as the ''stop ()'' function of your hardware I/O block, which closes the connection to your device properly. It is a well-known fact, that throwing exceptions in a destructor can be dangerous (cf. [https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor#130123 here]). This is the same with the ''stop ()'' function. If the exception is allowed to escape the function ''std::terminate()'' will be called, which is something nobody wants.</div>M3x1m0mhttps://wiki.gnuradio.org/index.php?title=Interfacing_Hardware_with_a_C%2B%2B_OOT_Module&diff=4078Interfacing Hardware with a C++ OOT Module2018-02-08T20:44:35Z<p>M3x1m0m: Created page with "This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. == Using gr::block..."</p>
<hr />
<div>This page aims to give a rough overview about what needs to be considered developing a GNURadio OOT module, that accesses hardware as for instance a SDR. <br />
<br />
== Using gr::block Facilities ==<br />
Luckily, the class [https://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block] provides functions, that can be used to initialize and deinitialize hardware. These member functions are ''virtual bool start ()'' and ''virtual bool stop ()''. ''start ()'' is generally used to initialize the hardware and is not coupled with the block construction, when the hole flowgraph is instantiated, i.e. the scheduler can take up working without having to wait for the hardware. The constructor of the block in the module is called before ''start ()'' is invoked. ''stop ()'' facilitates hardware deinitialization, such as turning off a transceiver, destroying a stream etc. GNURadio calls stop whenever something goes wrong, for instance when a exception is thrown in the ''work ()'' function.</div>M3x1m0m