From: Johnathan Corgan Date: Tue, 3 Nov 2009 21:42:28 +0000 (-0800) Subject: howto: moved osbsolete docs directory into limbo X-Git-Url: https://git.gag.com/?a=commitdiff_plain;ds=sidebyside;h=26d9beb0b51945915798e75ab24051adaa46d083;p=debian%2Fgnuradio howto: moved osbsolete docs directory into limbo --- diff --git a/gr-howto-write-a-block/Makefile.am b/gr-howto-write-a-block/Makefile.am index 96bf053b..ce2e4e8c 100644 --- a/gr-howto-write-a-block/Makefile.am +++ b/gr-howto-write-a-block/Makefile.am @@ -32,7 +32,6 @@ EXTRA_DIST = \ version.sh SUBDIRS = config lib swig python -DIST_SUBDIRS = config lib swig python doc pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = diff --git a/gr-howto-write-a-block/configure.ac b/gr-howto-write-a-block/configure.ac index 6af39b76..8241f7ec 100644 --- a/gr-howto-write-a-block/configure.ac +++ b/gr-howto-write-a-block/configure.ac @@ -72,7 +72,6 @@ dnl AX_BOOST_WSERIALIZATION AC_CONFIG_FILES([\ Makefile \ config/Makefile \ - doc/Makefile \ lib/Makefile \ python/Makefile \ python/run_tests \ diff --git a/gr-howto-write-a-block/doc/.gitignore b/gr-howto-write-a-block/doc/.gitignore deleted file mode 100644 index f65ab6cf..00000000 --- a/gr-howto-write-a-block/doc/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -/Makefile -/Makefile.in -/.deps -/.libs -/*.la -/*.lo -/autom4te.cache -/*.cache -/howto-write-a-block.html -/gr_block.h.xml -/howto_1.i.xml -/howto_square_ff.cc.xml -/howto_square_ff.h.xml -/qa_howto_1.py.xml -/src_lib_Makefile_1.am.xml -/src_lib_Makefile_2.am.xml -/howto_square2_ff.cc.xml -/howto_square2_ff.h.xml diff --git a/gr-howto-write-a-block/doc/Makefile.am b/gr-howto-write-a-block/doc/Makefile.am deleted file mode 100644 index 0152ecad..00000000 --- a/gr-howto-write-a-block/doc/Makefile.am +++ /dev/null @@ -1,81 +0,0 @@ -# -# Copyright 2004,2005,2007 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, -# Boston, MA 02110-1301, USA. -# - -TARGETS = howto-write-a-block.html - - -# To avoid build problems for folks who don't have xmlto installed, we -# don't build the docs by default. - -# html: $(TARGETS) -all: $(TARGETS) - - -EXTRA_DIST = \ - howto-write-a-block.xml \ - howto_1.i \ - make_numbered_listing.py \ - qa_howto_1.py \ - src_lib_Makefile_1.am \ - src_lib_Makefile_2.am - - -BUILT_XML_FILES = \ - gr_block.h.xml \ - howto_1.i.xml \ - howto_square_ff.cc.xml \ - howto_square_ff.h.xml \ - howto_square2_ff.cc.xml \ - howto_square2_ff.h.xml \ - qa_howto_1.py.xml \ - src_lib_Makefile_1.am.xml \ - src_lib_Makefile_2.am.xml - - -howto-write-a-block.html : howto-write-a-block.xml $(BUILT_XML_FILES) - - -gr_block.h.xml: $(GNURADIO_CORE_INCLUDEDIR)/gr_block.h make_numbered_listing.py - $(PYTHON) ./make_numbered_listing.py $(GNURADIO_CORE_INCLUDEDIR)/gr_block.h - -howto_square_ff.cc.xml: $(top_srcdir)/src/lib/howto_square_ff.cc make_numbered_listing.py - $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square_ff.cc - -howto_square_ff.h.xml: $(top_srcdir)/src/lib/howto_square_ff.h make_numbered_listing.py - $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square_ff.h - -howto_square2_ff.cc.xml: $(top_srcdir)/src/lib/howto_square2_ff.cc make_numbered_listing.py - $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square2_ff.cc - -howto_square2_ff.h.xml: $(top_srcdir)/src/lib/howto_square2_ff.h make_numbered_listing.py - $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square2_ff.h - - -# ---------------------------------------------------------------- - -clean: - -${RM} -f $(TARGETS) $(BUILT_XML_FILES) - -%.html : %.xml - xmlto html-nochunks $< - -%.xml : % make_numbered_listing.py - $(PYTHON) ./make_numbered_listing.py $< diff --git a/gr-howto-write-a-block/doc/howto-write-a-block.xml b/gr-howto-write-a-block/doc/howto-write-a-block.xml deleted file mode 100644 index f8027b45..00000000 --- a/gr-howto-write-a-block/doc/howto-write-a-block.xml +++ /dev/null @@ -1,959 +0,0 @@ - -GNU Radio"> - SWIG"> - gr_block"> - howto_square_ff"> - - - - - - - - - - - - - - - - - - -]> - -
- - -How to Write a Signal Processing Block - - Eric - Blossom - -
- eb@comsec.com -
-
-
- - - - 0.1 - 2005-01-20 - - - 0.2 - 2005-02-02 - Updated for SWIG 1.3.24 - - - 0.3 - 2006-07-21 - Clarification of 1:1 fixed rate vs item size - - - - - 2004 - 2005 - Free Software Foundation, Inc. - - -This article explains how to write signal -processing blocks for GNU Radio. - - -
- -Prerequisites -This article assumes that the reader has basic familiarity with -GNU Radio and has read and understood - -Exploring GNU Radio. - - -There is a tarball of files that accompany this article. It -includes the examples, DocBook source for the article and all the -Makefiles etc it takes to make it work. Grab it at -ftp://ftp.gnu.org/gnu/gnuradio or one of the mirrors. The -file you want is -gr-howto-write-a-block-X.Y.tar.gz. Pick the one -with the highest version number. -See -http://comsec.com/wiki?CvsAccess for CVS Access. - - - - - -Introduction -&gnuradio; provides a framework for building software radios. -Waveforms -- signal processing applications -- are built using a -combination of Python code for high level organization, policy, GUI and -other non performance-critical functions, while performance critical -signal processing blocks are written in C++. - -From the Python point of view, &gnuradio; provides a data flow -abstraction. The fundamental concepts are signal processing -blocks and the connections between them. This abstraction is -implemented by the Python gr.flow_graph class. -Each block has a set of input ports and output ports. Each port has -an associated data type. The most common port types are -float and gr_complex -(equivalent to std::complex<float>), though other types are used, -including those representing structures, arrays or other types of -packetized data. - -From the high level point-of-view, infinite streams of data flow -through the ports. At the C++ level, streams are dealt with in -convenient sized pieces, represented as contiguous arrays of the -underlying type. - - - -The View from 30,000 Feet - -This article will walk through the construction of several -simple signal processing blocks, and explain the techniques and idioms -used. Later sections cover debugging signal processing blocks in the -mixed Python/C++ environment and performance measurement and -optimization. - -The example blocks will be built in the style of all &gnuradio; -extensions. That is, they are built outside of the gnuradio-core build -tree, and are constructed as shared libraries that may be dynamically -loaded into Python using the "import" mechanism. &SWIG;, the -Simplified Wrapper and Interface Generator, is used to generate the -glue that allows our code to be used from Python. - - - - - - -The C++ class &gr_block; is the base of all signal processing -blocks in &gnuradio;. Writing a new signal processing block involves -creating 3 files: The .h and .cc files that define the new class and -the .i file that tells &SWIG; how to generate the glue that binds the -class into Python. The new class must derive from &gr_block; or -one of it's subclasses. - -Our first examples will derive directly from &gr_block;. Later -we will look at some other subclasses that simplify the process for -common cases. - - - - - - - -Autotools, Makefiles, and Directory Layout - -Before we dive into the code, &lets; talk a bit about the -overall build environment and the directory structure that &well; -be using. - -To reduce the amount of Makefile hacking that we have to do, and -to facilitate portability across a variety of systems, we use the GNU -autoconf, -automake, and -libtool tools. These are collectively -referred to as the autotools, and once you get over the initial -shock, they will become your friends. (The good news is that we -provide boilerplate that can be used pretty much as-is.) - - - -automake - -automake and configure work together to generate GNU -compliant Makefiles from a much higher level description contained in -the corresponding Makefile.am file. Makefile.am -specifies the libraries and programs to build and the source files -that compose each. Automake reads Makefile.am -and produces Makefile.in. Configure reads -Makefile.in and produces -Makefile. The resulting Makefile contains a -zillion rules that do the right right thing to build, check and -install your code. It is not uncommon for the the resulting -Makefile to be 5 or 6 times larger than -Makefile.am. - - - - -autoconf -autoconf reads configure.ac -and produces the configure shell -script. configure automatically tests for -features of the underlying system and sets a bunch of variables and -defines that can be used in the Makefiles and your C++ code to -conditionalize the build. If features are required but not found, -configure will output an error message and stop. - - - -libtool -libtool works behind the scenes and provides the magic -to construct shared libraries on a wide variety of systems. - - - - - - shows the directory layout and -common files &well; be using. After renaming the -topdir directory, use it in your projects -too. We'll talk about particular files as they come up later. - - -Directory Layout - - - -File/Dir Name -Comment - - - - - - -topdir/Makefile.am -Top level Makefile.am - - -topdir/Makefile.common -Common fragment included in sub-Makefiles - - -topdir/bootstrap -Runs autoconf, automake, libtool first time through - - -topdir/config -Directory of m4 macros used by configure.ac - - -topdir/configure.ac -Input to autoconf - - -topdir/src - - -topdir/src/lib -C++ code goes here - - -topdir/src/lib/Makefile.am - - -topdir/src/python -Python code goes here - - -topdir/src/python/Makefile.am - - -topdir/src/python/run_tests -Script to run tests in the build tree - - - - -
- -
- - - -Naming Conventions - -&gnuradio; uses a set of naming conventions to assist in -comprehending the code base and gluing C++ and Python together. -Please follow them. - -<emphasis>Death to CamelCaseNames!</emphasis> - -We've returned to a kinder, gentler era. We're now using the -"STL style" naming convention with a couple of modifications -since we're not using namespaces. - -With the exception of macros and other constant values, all -identifiers shall be lower case with words_separated_like_this. - -Macros and constant values (e.g., enumerated values, -static const int FOO = 23) shall be in UPPER_CASE. - - - -Global Names - -All globally visible names (types, functions, variables, consts, etc) -shall begin with a "package prefix", followed by an underscore. The bulk of -the code in GNU Radio belongs to the "gr" package, hence -names look like gr_open_file (...). - -Large coherent bodies of code may use other package prefixes, but -let's try to keep them to a well thought out list. See the list -below. - - - -Package Prefixes - -These are the current package prefixes: - - - -gr_ -Almost everything. - - -gri_ - -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. - - -atsc_ -Code related to the Advanced Television Standards Committee HDTV implementation - - - -usrp_ -Universal Software Radio Peripheral. - - -qa_ -Quality Assurance (Test code.) - - - - - - - -Class Data Members (instance variables) - -All class data members shall begin with d_foo. - -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 d_. - - -class gr_wonderfulness { - std::string d_name; - double d_wonderfulness_factor; - -public: - gr_wonderfulness (std::string name, double wonderfulness_factor) - : d_name (name), d_wonderfulness_factor (wonderfulness_factor) - { - ... - } - ... -}; - - - - -Class Static Data Members (class variables) - - -All class static data members shall begin with s_foo. - - - - -File Names - -Each significant class shall be contained in its own file. The -declaration of class gr_foo shall be in -gr_foo.h and the definition in -gr_foo.cc. - - - -Suffixes - -By convention, we encode the input and output types of signal -processing blocks in their name using suffixes. The suffix is -typically one or two characters long. Source and sinks have single -character suffixes. Regular blocks that have both inputs and outputs -have two character suffixes. The first character indicates the type -of the input streams, the second indicates the type of the output -streams. FIR filter blocks have a three character suffix, indicating -the type of the inputs, outputs and taps, respectively. - -These are the suffix characters and their interpretations: - -f - single precision floating point -c - complex<float> -s - short (16-bit integer) -i - integer (32-bit integer) - - - -In addition, for those cases where the block deals with streams -of vectors, we use the character 'v' as the first character of the -suffix. An example of this usage is -gr_fft_vcc. The FFT block takes a vector of -complex numbers on its input and produces a vector of complex -numbers on its output. - - - - - - - - -First Block: □ - -For our first example &well; create a block that computes -the square of its single float input. This block will accept a single -float input stream and produce a single float output stream. - -Following the naming conventions, &well; use -howto as our package prefix, and the block will -be called howto_square_ff. - -We are going to arrange that this block, as well as the others -that we write in this article, end up in the -gnuradio.howto Python module. This will allow us -to access it from Python like this: - -from gnuradio import howto -sqr = howto.square_ff () - - - - -Test Driven Programming - -We could just start banging out the C++ code, but being highly -evolved modern programmers, &were; going to write the test code first. -After all, we do have a good spec for the behavior: take a single -stream of floats as the input and produce a single stream of floats as -the output. The output should be the square of the input. - -How hard could this be? Turns out that this is easy! Check out -. - - -<filename>qa_howto.py</filename> (first version) -&qa_howto_1_listing; - - - -gr_unittest is an extension to the standard -python module unittest. -gr_unittest adds support for checking -approximate equality of tuples of float and complex numbers. -Unittest uses Python's reflection mechanism to find all methods that start with -test_ and runs them. Unittest wraps each call -to test_* with matching calls to -setUp and tearDown. -See the python -unittest documentation for details. - - -When we run the test, -gr_unittest.main is going to invoke -setUp, -test_001_square_ff, and -tearDown. - -test_001_square_ff builds a small graph that -contains three nodes. gr.vector_source_f(src_data) will source the -elements of src_data and then say that &its; finished. howto.square_ff is the block -&were; testing. gr.vector_sink_f gathers the output of -howto.square_ff. - -The run method runs the graph until all -the blocks indicate they are finished. Finally, we check that the -result of executing square_ff on src_data matches what we expect. - - - - -Build Tree vs. Install Tree - -The build tree is everything from topdir -(the one containing configure.ac) down. The path to the install tree is - -prefix/lib/pythonversion/site-packages, -where prefix is the --prefix -argument to configure (default /usr/local) and -version is the installed version of -python. A typical value is -/usr/local/lib/python2.3/site-packages. - - -We normally set our PYTHONPATH environment variable to point at -the install tree, and do this in ~/.bash_profile -or ~/.profile. -This allows our python apps to access all the standard python -libraries, plus our locally installed stuff like GNU Radio. - -We write our applications such that they access the code and -libraries in the install tree. On the other hand, we want our test -code to run on the build tree, where we can detect problems before -installation. - - - -make check - - -We use make check to run our tests. -Make check invokes the run_tests shell script which -sets up the PYTHONPATH environment variable so that -our tests use the build tree versions of our code and libraries. -It then runs all files -which have names of the form qa_*.py and reports -the overall success or failure. - -There is quite a bit of behind-the-scenes action required to use -the non-installed versions of our code (look at -runtest for a cheap thrill.) - -Finally, running make check in the python -directory produces this result: - - [eb@bufo python]$ make check - make check-TESTS - make[1]: Entering directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' - Traceback (most recent call last): - File "./qa_howto.py", line 24, in ? - import howto - ImportError: No module named howto - Traceback (most recent call last): - File "./qa_howto_1.py", line 24, in ? - import howto - ImportError: No module named howto - FAIL: run_tests - =================== - 1 of 1 tests failed - =================== - make[1]: *** [check-TESTS] Error 1 - make[1]: Leaving directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' - make: *** [check-am] Error 2 - [eb@bufo python]$ - -Excellent! Our test failed, just as we expected. The ImportError -indicates that it can't find the module named -howto. No surprise, since we haven't written it yet. - - - - -The C++ code -Now that we've got a test case written that successfully fails, -let's write the C++ code. As we mentioned earlier, all signal -processing blocks are derived from gr_block or -one of its subclasses. Let's take a look at -. - - -<filename>gr_block.h</filename> -&gr_block_listing; - - -A quick scan of gr_block.h reveals that -since general_work is pure virtual, we -definitely need to override that. -general_work is the method that does the -actual signal processing. For our squaring example we'll -need to override general_work and provide a -constructor and destructor and a bit of stuff to take advantage of -the boost - -shared_ptrs. - - - - - -and are the header and c++ -source. - - -<filename>howto_square_ff.h</filename> -&howto_square_ff_h_listing; - - - -<filename>howto_square_ff.cc</filename> -&howto_square_ff_cc_listing; - - -Now we need a Makefile.am to get all this to build. - -is enough to build a shared library from our source file. We'll be -adding additional rules to use &SWIG; in just a bit. If you haven't -already, this is a good time to browse all the Makefile.am's in -the build tree and get an idea for how it all hangs together. - - -<filename>src/lib/Makefile.am</filename> (no &SWIG;) -&src_lib_Makefile_1_am_listing; - - - - - - - - -The &SWIG; .i file - -Now that we've got something that will compile, we need to write -the &SWIG; .i file. This is a pared-down version of the .h file, plus -a bit of magic that has python work with the boost shared_ptr's. -To reduce code bloat, we only declare methods that &well; want to -access from Python. - -We're going to call the .i file -howto.i, and use it to hold the &SWIG; -declarations for all classes from howto that will -be accessible from python. It's quite small: -&howto_1_i_listing; - - - - -Putting it all together - -Now we need to modify src/lib/Makefile.am -to run &SWIG; and to add the glue it generates to the shared library. - - -<filename>src/lib/Makefile.am</filename> (with &SWIG;) -&src_lib_Makefile_2_am_listing; - - -make now builds everything successfully. We get a -few warnings, but &thats; OK. - -Changing directories back to the python directory we try -make check again: - - [eb@bufo python]$ make check - make check-TESTS - make[1]: Entering directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' - . - ---------------------------------------------------------------------- - Ran 1 test in 0.004s - - OK - PASS: run_tests - ================== - All 1 tests passed - ================== - make[1]: Leaving directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' - [eb@bufo python]$ - -Victory! Our new block works! - - - - - - -Additional gr_block methods - -In our howto_square_ff example above, we only -had to override the general_work method to -accomplish our goal. gr_block provides a few other -methods that are sometimes useful. - -forecast - -Looking at general_work you may -have wondered how the system knows how much data it needs to -ensure is valid in each of the input arrays. The -forecast method provides this -information. - -The default implementation of forecast -says there is a 1:1 relationship between noutput_items and the -requirements for each input stream. The size of the items is defined by -gr_io_signatures in the constructor of -gr_block. The sizes of the input and output items -can of course differ; this still qualifies as a 1:1 relationship. - - // default implementation: 1:1 - - void - gr_block::forecast (int noutput_items, - gr_vector_int &ninput_items_required) - { - unsigned ninputs = ninput_items_required.size (); - for (unsigned i = 0; i < ninputs; i++) - ninput_items_required[i] = noutput_items; - } - - - -Although the 1:1 implementation worked for howto_square_ff, it -wouldn't be appropriate for interpolators, decimators, or blocks -with a more complicated relationship between noutput_items and the -input requirements. That said, by deriving your classes from -gr_sync_block, -gr_sync_interpolator or -gr_sync_decimator instead of -gr_block, you can often avoid -implementing forecast. - - - -set_output_multiple - -When implementing your general_work -routine, &its; occasionally convenient to have the run time system -ensure that you are only asked to produce a number of output items -that is a multiple of some particular value. This might occur if your -algorithm naturally applies to a fixed sized block of data. -Call set_output_multiple in your constructor -to specify this requirement. The default output multiple is 1. - - - - - - - -Subclasses for common patterns - -gr_block allows tremendous flexibility -with regard to the consumption of input streams and the production of -output streams. Adroit use of forecast and -consume allows variable rate blocks to be -built. It is possible to construct blocks that consume data at -different rates on each input, and produce output at a rate that -is a function of the contents of the input data. - -On the other hand, it is very common for signal processing -blocks to have a fixed relationship between the input rate and the -output rate. Many are 1:1, while others have 1:N or N:1 -relationships. - -Another common requirement is the need to examine more than one -input sample to produce a single output sample. This is orthogonal to -the relationship between input and output rate. For example, a -non-decimating, non-interpolating FIR filter needs to examine N input -samples for each output sample it produces, where N is the number of -taps in the filter. However, it only consumes a single input sample -to produce a single output. We call this concept "history", but you -could also think of it as "look-ahead". - -<classname>gr_sync_block</classname> - - - -gr_sync_block -is derived from - -gr_block -and implements a 1:1 block with -optional history. Given that we know the input to output rate, -certain simplifications are possible. From the implementor's -point-of-view, the primary change is that we define a -work method instead of -general_work. work -has a slightly different calling sequence; -It omits the unnecessary ninput_items parameter, and arranges for -consume_each to be called on our -behalf. - - /*! - * \brief Just like gr_block::general_work, only this arranges to - * call consume_each for you. - * - * The user must override work to define the signal processing code - */ - virtual int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) = 0; - - -This gives us fewer things to worry about, and less code to -write. If the block requires history greater than 1, call -set_history in the constructor, or any time -the requirement changes. - -gr_sync_block provides a -version of forecast that handles the -history requirement. - - - -<classname>gr_sync_decimator</classname> - - - -gr_sync_decimator -is derived from - -gr_sync_block -and implements a N:1 block with optional history. - - - - -<classname>gr_sync_interpolator</classname> - - - -gr_sync_interpolator -is derived from - -gr_sync_block -and implements a 1:N block with optional history. - - - - - - - - -Second Block: <classname>howto_square2_ff</classname> - -Given that we now know about -gr_sync_block, the way -howto_square_ff should really be implemented is -by subclassing gr_sync_block. - -Here are the revised sources: , -. -The accompanying files contain the additional test code. - - - -<filename>howto_square2_ff.h</filename> -&howto_square2_ff_h_listing; - - - -<filename>howto_square2_ff.cc</filename> -&howto_square2_ff_cc_listing; - - - - -Where to from Here? - -At this point, we've got a basic overview of how the system -goes together. For more insight, I suggest that you look at the code -of the system. The doxygen generated class -hierarchy is a useful way to find things that might interest -you. - - - - -Miscellaneous Tips - -Sources and Sinks - -Sources and sinks are derived from -gr_sync_block. The only thing different about -them is that sources have no inputs and sinks have no outputs. This -is reflected in the gr_io_signatures that are -passed to the gr_sync_block constructor. -Take a look at gr_file_source.{h,cc} and -gr_file_sink.{h,cc} for some very straight-forward examples. - - - - - -Debugging with <application>gdb</application> - -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. The trick of course -is that all of &gnuradio;, including your new block, is dynamically -loaded into python for execution. - -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. - - - #!/usr/bin/env python - from gnuradio import gr - from gnuradio import my_buggy_module - - # insert this in your test code... - import os - print 'Blocked waiting for GDB attach (pid = %d)' % (os.getpid(),) - raw_input ('Press Enter to continue: ') - # remainder of your test code follows... - - -Another SNAFU you might run into is that gdb 6.2 isn't -able to set breakpoints in the constructors or destructors generated -by g++ 3.4. In this case, insert a call to the nop function -gri_debugger_hook in the constructor and recompile. Load the code as -before and set a break point on gri_debugger_hook. - - - - -Performance Measurement with <application>oprofile</application> -Oprofile is your friend. -See http://oprofile.sourceforge.net. - - - - - -Coming Attractions - - -Improved Type System - - - -Hierarchical Blocks - - - - - -
diff --git a/gr-howto-write-a-block/doc/howto_1.i b/gr-howto-write-a-block/doc/howto_1.i deleted file mode 100644 index 640d0897..00000000 --- a/gr-howto-write-a-block/doc/howto_1.i +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- c++ -*- */ - -%include "exception.i" -%import "gnuradio.i" // the common stuff - -%{ -#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix -#include "howto_square_ff.h" -#include -%} - -// ---------------------------------------------------------------- - -/* - * First arg is the package prefix. - * Second arg is the name of the class minus the prefix. - * - * This does some behind-the-scenes magic so we can - * access howto_square_ff from python as howto.square_ff - */ -GR_SWIG_BLOCK_MAGIC(howto,square_ff); - -howto_square_ff_sptr howto_make_square_ff (); - -class howto_square_ff : public gr_block -{ -private: - howto_square_ff (); -}; diff --git a/gr-howto-write-a-block/doc/make_numbered_listing.py b/gr-howto-write-a-block/doc/make_numbered_listing.py deleted file mode 100755 index 889c2d78..00000000 --- a/gr-howto-write-a-block/doc/make_numbered_listing.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - -import sys -import os, os.path -from optparse import OptionParser - -def quote_line (line): - line = line.replace ('&', '&') - line = line.replace ('<', '<') - line = line.replace ('>', '>') - line = line.replace ("'", ''') - line = line.replace ('"', '"') - return line - -def generate_listing (input_filename, title=None): - inf = open (input_filename, "r") - output_filename = os.path.basename (input_filename) + '.xml' - outf = open (output_filename, "w") - outf.write ('\n') - # outf.write ('\n' % (input_filename,)) - # if not title: - # title = input_filename - # outf.write ('') - # outf.write (title) - # outf.write ('\n') - outf.write ('\n'); - - lineno = 0 - for line in inf: - line = line.expandtabs (8) - line = quote_line (line) - lineno = lineno + 1 - outf.write ('%3d %s' % (lineno, line)) - - outf.write ('\n') - # outf.write ('\n') - - -def main (): - for file in sys.argv[1:]: - generate_listing (file) - -if __name__ == '__main__': - main () - diff --git a/gr-howto-write-a-block/doc/qa_howto_1.py b/gr-howto-write-a-block/doc/qa_howto_1.py deleted file mode 100755 index 3173110f..00000000 --- a/gr-howto-write-a-block/doc/qa_howto_1.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -from gnuradio import gr, gr_unittest -import howto - -class qa_howto (gr_unittest.TestCase): - - def setUp (self): - self.tb = gr.top_block () - - def tearDown (self): - self.tb = None - - def test_001_square_ff (self): - src_data = (-3, 4, -5.5, 2, 3) - expected_result = (9, 16, 30.25, 4, 9) - src = gr.vector_source_f (src_data) - sqr = howto.square_ff () - dst = gr.vector_sink_f () - self.tb.connect (src, sqr) - self.tb.connect (sqr, dst) - self.tb.run () - result_data = dst.data () - self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6) - -if __name__ == '__main__': - gr_unittest.main () diff --git a/gr-howto-write-a-block/doc/src_lib_Makefile_1.am b/gr-howto-write-a-block/doc/src_lib_Makefile_1.am deleted file mode 100644 index e97d70d1..00000000 --- a/gr-howto-write-a-block/doc/src_lib_Makefile_1.am +++ /dev/null @@ -1,25 +0,0 @@ -include $(top_srcdir)/Makefile.common - -# Install this stuff so that it ends up as the gnuradio.howto module -# This usually ends up at: -# ${prefix}/lib/python${python_version}/site-packages/gnuradio - -ourpythondir = $(grpythondir) -ourlibdir = $(grpyexecdir) - -INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) - -ourlib_LTLIBRARIES = _howto.la - -# These are the source files that go into the shared library -_howto_la_SOURCES = \ - howto_square_ff.cc - -# magic flags -_howto_la_LDFLAGS = -module -avoid-version - -# These headers get installed in ${prefix}/include/gnuradio -grinclude_HEADERS = \ - howto_square_ff.h - -MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc diff --git a/gr-howto-write-a-block/doc/src_lib_Makefile_2.am b/gr-howto-write-a-block/doc/src_lib_Makefile_2.am deleted file mode 100644 index dca236e2..00000000 --- a/gr-howto-write-a-block/doc/src_lib_Makefile_2.am +++ /dev/null @@ -1,86 +0,0 @@ -# -# Copyright 2004,2008 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, -# Boston, MA 02110-1301, USA. -# - -include $(top_srcdir)/Makefile.common - -# Install this stuff so that it ends up as the gnuradio.howto module -# This usually ends up at: -# ${prefix}/lib/python${python_version}/site-packages/gnuradio - -ourpythondir = $(grpythondir) -ourlibdir = $(grpyexecdir) - -INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) - -SWIGCPPPYTHONARGS = -noruntime -c++ -python $(PYTHON_CPPFLAGS) \ - -I$(swigincludedir) -I$(grincludedir) $(WITH_SWIG_INCLUDES) - -ALL_IFILES = \ - $(LOCAL_IFILES) \ - $(NON_LOCAL_IFILES) - -NON_LOCAL_IFILES = \ - $(GNURADIO_CORE_INCLUDEDIR)/swig/gnuradio.i - - -LOCAL_IFILES = \ - howto.i - -# These files are built by SWIG. The first is the C++ glue. -# The second is the python wrapper that loads the _howto shared library -# and knows how to call our extensions. - -BUILT_SOURCES = \ - howto.cc \ - howto.py - -# This gets howto.py installed in the right place -ourpython_PYTHON = \ - howto.py - -ourlib_LTLIBRARIES = _howto.la - -# These are the source files that go into the shared library -_howto_la_SOURCES = \ - howto.cc \ - howto_square_ff.cc - -# magic flags -_howto_la_LDFLAGS = -module -avoid-version - -# link the library against some comon swig runtime code and the -# c++ standard library -_howto_la_LIBADD = \ - -lgrswigrunpy \ - -lstdc++ - -howto.cc howto.py: howto.i $(ALL_IFILES) - $(SWIG) $(SWIGCPPPYTHONARGS) -module howto -o howto.cc $< - -# These headers get installed in ${prefix}/include/gnuradio -grinclude_HEADERS = \ - howto_square_ff.h - -# These swig headers get installed in ${prefix}/include/gnuradio/swig -swiginclude_HEADERS = \ - $(LOCAL_IFILES) - -MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc diff --git a/gr-howto-write-a-block/limbo/doc/.gitignore b/gr-howto-write-a-block/limbo/doc/.gitignore new file mode 100644 index 00000000..f65ab6cf --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/.gitignore @@ -0,0 +1,18 @@ +/Makefile +/Makefile.in +/.deps +/.libs +/*.la +/*.lo +/autom4te.cache +/*.cache +/howto-write-a-block.html +/gr_block.h.xml +/howto_1.i.xml +/howto_square_ff.cc.xml +/howto_square_ff.h.xml +/qa_howto_1.py.xml +/src_lib_Makefile_1.am.xml +/src_lib_Makefile_2.am.xml +/howto_square2_ff.cc.xml +/howto_square2_ff.h.xml diff --git a/gr-howto-write-a-block/limbo/doc/Makefile.am b/gr-howto-write-a-block/limbo/doc/Makefile.am new file mode 100644 index 00000000..5f58a21e --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/Makefile.am @@ -0,0 +1,81 @@ +# +# Copyright 2004,2005,2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +TARGETS = howto-write-a-block.html + +# To avoid build problems for folks who don't have xmlto installed, we +# don't build the docs by default. + +# html: $(TARGETS) +all: $(TARGETS) + + +EXTRA_DIST = \ + README \ + howto-write-a-block.xml \ + howto_1.i \ + make_numbered_listing.py \ + qa_howto_1.py \ + src_lib_Makefile_1.am \ + src_lib_Makefile_2.am + + +BUILT_XML_FILES = \ + gr_block.h.xml \ + howto_1.i.xml \ + howto_square_ff.cc.xml \ + howto_square_ff.h.xml \ + howto_square2_ff.cc.xml \ + howto_square2_ff.h.xml \ + qa_howto_1.py.xml \ + src_lib_Makefile_1.am.xml \ + src_lib_Makefile_2.am.xml + + +howto-write-a-block.html : howto-write-a-block.xml $(BUILT_XML_FILES) + + +gr_block.h.xml: $(GNURADIO_CORE_INCLUDEDIR)/gr_block.h make_numbered_listing.py + $(PYTHON) ./make_numbered_listing.py $(GNURADIO_CORE_INCLUDEDIR)/gr_block.h + +howto_square_ff.cc.xml: $(top_srcdir)/src/lib/howto_square_ff.cc make_numbered_listing.py + $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square_ff.cc + +howto_square_ff.h.xml: $(top_srcdir)/src/lib/howto_square_ff.h make_numbered_listing.py + $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square_ff.h + +howto_square2_ff.cc.xml: $(top_srcdir)/src/lib/howto_square2_ff.cc make_numbered_listing.py + $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square2_ff.cc + +howto_square2_ff.h.xml: $(top_srcdir)/src/lib/howto_square2_ff.h make_numbered_listing.py + $(PYTHON) ./make_numbered_listing.py $(top_srcdir)/src/lib/howto_square2_ff.h + + +# ---------------------------------------------------------------- + +clean: + -${RM} -f $(TARGETS) $(BUILT_XML_FILES) + +%.html : %.xml + xmlto html-nochunks $< + +%.xml : % make_numbered_listing.py + $(PYTHON) ./make_numbered_listing.py $< diff --git a/gr-howto-write-a-block/limbo/doc/README b/gr-howto-write-a-block/limbo/doc/README new file mode 100644 index 00000000..ff3b75e5 --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/README @@ -0,0 +1 @@ +The contents of this directory are obsolete. diff --git a/gr-howto-write-a-block/limbo/doc/howto-write-a-block.xml b/gr-howto-write-a-block/limbo/doc/howto-write-a-block.xml new file mode 100644 index 00000000..f8027b45 --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/howto-write-a-block.xml @@ -0,0 +1,959 @@ + +GNU Radio"> + SWIG"> + gr_block"> + howto_square_ff"> + + + + + + + + + + + + + + + + + + +]> + +
+ + +How to Write a Signal Processing Block + + Eric + Blossom + +
+ eb@comsec.com +
+
+
+ + + + 0.1 + 2005-01-20 + + + 0.2 + 2005-02-02 + Updated for SWIG 1.3.24 + + + 0.3 + 2006-07-21 + Clarification of 1:1 fixed rate vs item size + + + + + 2004 + 2005 + Free Software Foundation, Inc. + + +This article explains how to write signal +processing blocks for GNU Radio. + + +
+ +Prerequisites +This article assumes that the reader has basic familiarity with +GNU Radio and has read and understood + +Exploring GNU Radio. + + +There is a tarball of files that accompany this article. It +includes the examples, DocBook source for the article and all the +Makefiles etc it takes to make it work. Grab it at +ftp://ftp.gnu.org/gnu/gnuradio or one of the mirrors. The +file you want is +gr-howto-write-a-block-X.Y.tar.gz. Pick the one +with the highest version number. +See +http://comsec.com/wiki?CvsAccess for CVS Access. + + + + + +Introduction +&gnuradio; provides a framework for building software radios. +Waveforms -- signal processing applications -- are built using a +combination of Python code for high level organization, policy, GUI and +other non performance-critical functions, while performance critical +signal processing blocks are written in C++. + +From the Python point of view, &gnuradio; provides a data flow +abstraction. The fundamental concepts are signal processing +blocks and the connections between them. This abstraction is +implemented by the Python gr.flow_graph class. +Each block has a set of input ports and output ports. Each port has +an associated data type. The most common port types are +float and gr_complex +(equivalent to std::complex<float>), though other types are used, +including those representing structures, arrays or other types of +packetized data. + +From the high level point-of-view, infinite streams of data flow +through the ports. At the C++ level, streams are dealt with in +convenient sized pieces, represented as contiguous arrays of the +underlying type. + + + +The View from 30,000 Feet + +This article will walk through the construction of several +simple signal processing blocks, and explain the techniques and idioms +used. Later sections cover debugging signal processing blocks in the +mixed Python/C++ environment and performance measurement and +optimization. + +The example blocks will be built in the style of all &gnuradio; +extensions. That is, they are built outside of the gnuradio-core build +tree, and are constructed as shared libraries that may be dynamically +loaded into Python using the "import" mechanism. &SWIG;, the +Simplified Wrapper and Interface Generator, is used to generate the +glue that allows our code to be used from Python. + + + + + + +The C++ class &gr_block; is the base of all signal processing +blocks in &gnuradio;. Writing a new signal processing block involves +creating 3 files: The .h and .cc files that define the new class and +the .i file that tells &SWIG; how to generate the glue that binds the +class into Python. The new class must derive from &gr_block; or +one of it's subclasses. + +Our first examples will derive directly from &gr_block;. Later +we will look at some other subclasses that simplify the process for +common cases. + + + + + + + +Autotools, Makefiles, and Directory Layout + +Before we dive into the code, &lets; talk a bit about the +overall build environment and the directory structure that &well; +be using. + +To reduce the amount of Makefile hacking that we have to do, and +to facilitate portability across a variety of systems, we use the GNU +autoconf, +automake, and +libtool tools. These are collectively +referred to as the autotools, and once you get over the initial +shock, they will become your friends. (The good news is that we +provide boilerplate that can be used pretty much as-is.) + + + +automake + +automake and configure work together to generate GNU +compliant Makefiles from a much higher level description contained in +the corresponding Makefile.am file. Makefile.am +specifies the libraries and programs to build and the source files +that compose each. Automake reads Makefile.am +and produces Makefile.in. Configure reads +Makefile.in and produces +Makefile. The resulting Makefile contains a +zillion rules that do the right right thing to build, check and +install your code. It is not uncommon for the the resulting +Makefile to be 5 or 6 times larger than +Makefile.am. + + + + +autoconf +autoconf reads configure.ac +and produces the configure shell +script. configure automatically tests for +features of the underlying system and sets a bunch of variables and +defines that can be used in the Makefiles and your C++ code to +conditionalize the build. If features are required but not found, +configure will output an error message and stop. + + + +libtool +libtool works behind the scenes and provides the magic +to construct shared libraries on a wide variety of systems. + + + + + + shows the directory layout and +common files &well; be using. After renaming the +topdir directory, use it in your projects +too. We'll talk about particular files as they come up later. + + +Directory Layout + + + +File/Dir Name +Comment + + + + + + +topdir/Makefile.am +Top level Makefile.am + + +topdir/Makefile.common +Common fragment included in sub-Makefiles + + +topdir/bootstrap +Runs autoconf, automake, libtool first time through + + +topdir/config +Directory of m4 macros used by configure.ac + + +topdir/configure.ac +Input to autoconf + + +topdir/src + + +topdir/src/lib +C++ code goes here + + +topdir/src/lib/Makefile.am + + +topdir/src/python +Python code goes here + + +topdir/src/python/Makefile.am + + +topdir/src/python/run_tests +Script to run tests in the build tree + + + + +
+ +
+ + + +Naming Conventions + +&gnuradio; uses a set of naming conventions to assist in +comprehending the code base and gluing C++ and Python together. +Please follow them. + +<emphasis>Death to CamelCaseNames!</emphasis> + +We've returned to a kinder, gentler era. We're now using the +"STL style" naming convention with a couple of modifications +since we're not using namespaces. + +With the exception of macros and other constant values, all +identifiers shall be lower case with words_separated_like_this. + +Macros and constant values (e.g., enumerated values, +static const int FOO = 23) shall be in UPPER_CASE. + + + +Global Names + +All globally visible names (types, functions, variables, consts, etc) +shall begin with a "package prefix", followed by an underscore. The bulk of +the code in GNU Radio belongs to the "gr" package, hence +names look like gr_open_file (...). + +Large coherent bodies of code may use other package prefixes, but +let's try to keep them to a well thought out list. See the list +below. + + + +Package Prefixes + +These are the current package prefixes: + + + +gr_ +Almost everything. + + +gri_ + +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. + + +atsc_ +Code related to the Advanced Television Standards Committee HDTV implementation + + + +usrp_ +Universal Software Radio Peripheral. + + +qa_ +Quality Assurance (Test code.) + + + + + + + +Class Data Members (instance variables) + +All class data members shall begin with d_foo. + +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 d_. + + +class gr_wonderfulness { + std::string d_name; + double d_wonderfulness_factor; + +public: + gr_wonderfulness (std::string name, double wonderfulness_factor) + : d_name (name), d_wonderfulness_factor (wonderfulness_factor) + { + ... + } + ... +}; + + + + +Class Static Data Members (class variables) + + +All class static data members shall begin with s_foo. + + + + +File Names + +Each significant class shall be contained in its own file. The +declaration of class gr_foo shall be in +gr_foo.h and the definition in +gr_foo.cc. + + + +Suffixes + +By convention, we encode the input and output types of signal +processing blocks in their name using suffixes. The suffix is +typically one or two characters long. Source and sinks have single +character suffixes. Regular blocks that have both inputs and outputs +have two character suffixes. The first character indicates the type +of the input streams, the second indicates the type of the output +streams. FIR filter blocks have a three character suffix, indicating +the type of the inputs, outputs and taps, respectively. + +These are the suffix characters and their interpretations: + +f - single precision floating point +c - complex<float> +s - short (16-bit integer) +i - integer (32-bit integer) + + + +In addition, for those cases where the block deals with streams +of vectors, we use the character 'v' as the first character of the +suffix. An example of this usage is +gr_fft_vcc. The FFT block takes a vector of +complex numbers on its input and produces a vector of complex +numbers on its output. + + + + + + + + +First Block: □ + +For our first example &well; create a block that computes +the square of its single float input. This block will accept a single +float input stream and produce a single float output stream. + +Following the naming conventions, &well; use +howto as our package prefix, and the block will +be called howto_square_ff. + +We are going to arrange that this block, as well as the others +that we write in this article, end up in the +gnuradio.howto Python module. This will allow us +to access it from Python like this: + +from gnuradio import howto +sqr = howto.square_ff () + + + + +Test Driven Programming + +We could just start banging out the C++ code, but being highly +evolved modern programmers, &were; going to write the test code first. +After all, we do have a good spec for the behavior: take a single +stream of floats as the input and produce a single stream of floats as +the output. The output should be the square of the input. + +How hard could this be? Turns out that this is easy! Check out +. + + +<filename>qa_howto.py</filename> (first version) +&qa_howto_1_listing; + + + +gr_unittest is an extension to the standard +python module unittest. +gr_unittest adds support for checking +approximate equality of tuples of float and complex numbers. +Unittest uses Python's reflection mechanism to find all methods that start with +test_ and runs them. Unittest wraps each call +to test_* with matching calls to +setUp and tearDown. +See the python +unittest documentation for details. + + +When we run the test, +gr_unittest.main is going to invoke +setUp, +test_001_square_ff, and +tearDown. + +test_001_square_ff builds a small graph that +contains three nodes. gr.vector_source_f(src_data) will source the +elements of src_data and then say that &its; finished. howto.square_ff is the block +&were; testing. gr.vector_sink_f gathers the output of +howto.square_ff. + +The run method runs the graph until all +the blocks indicate they are finished. Finally, we check that the +result of executing square_ff on src_data matches what we expect. + + + + +Build Tree vs. Install Tree + +The build tree is everything from topdir +(the one containing configure.ac) down. The path to the install tree is + +prefix/lib/pythonversion/site-packages, +where prefix is the --prefix +argument to configure (default /usr/local) and +version is the installed version of +python. A typical value is +/usr/local/lib/python2.3/site-packages. + + +We normally set our PYTHONPATH environment variable to point at +the install tree, and do this in ~/.bash_profile +or ~/.profile. +This allows our python apps to access all the standard python +libraries, plus our locally installed stuff like GNU Radio. + +We write our applications such that they access the code and +libraries in the install tree. On the other hand, we want our test +code to run on the build tree, where we can detect problems before +installation. + + + +make check + + +We use make check to run our tests. +Make check invokes the run_tests shell script which +sets up the PYTHONPATH environment variable so that +our tests use the build tree versions of our code and libraries. +It then runs all files +which have names of the form qa_*.py and reports +the overall success or failure. + +There is quite a bit of behind-the-scenes action required to use +the non-installed versions of our code (look at +runtest for a cheap thrill.) + +Finally, running make check in the python +directory produces this result: + + [eb@bufo python]$ make check + make check-TESTS + make[1]: Entering directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' + Traceback (most recent call last): + File "./qa_howto.py", line 24, in ? + import howto + ImportError: No module named howto + Traceback (most recent call last): + File "./qa_howto_1.py", line 24, in ? + import howto + ImportError: No module named howto + FAIL: run_tests + =================== + 1 of 1 tests failed + =================== + make[1]: *** [check-TESTS] Error 1 + make[1]: Leaving directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' + make: *** [check-am] Error 2 + [eb@bufo python]$ + +Excellent! Our test failed, just as we expected. The ImportError +indicates that it can't find the module named +howto. No surprise, since we haven't written it yet. + + + + +The C++ code +Now that we've got a test case written that successfully fails, +let's write the C++ code. As we mentioned earlier, all signal +processing blocks are derived from gr_block or +one of its subclasses. Let's take a look at +. + + +<filename>gr_block.h</filename> +&gr_block_listing; + + +A quick scan of gr_block.h reveals that +since general_work is pure virtual, we +definitely need to override that. +general_work is the method that does the +actual signal processing. For our squaring example we'll +need to override general_work and provide a +constructor and destructor and a bit of stuff to take advantage of +the boost + +shared_ptrs. + + + + + +and are the header and c++ +source. + + +<filename>howto_square_ff.h</filename> +&howto_square_ff_h_listing; + + + +<filename>howto_square_ff.cc</filename> +&howto_square_ff_cc_listing; + + +Now we need a Makefile.am to get all this to build. + +is enough to build a shared library from our source file. We'll be +adding additional rules to use &SWIG; in just a bit. If you haven't +already, this is a good time to browse all the Makefile.am's in +the build tree and get an idea for how it all hangs together. + + +<filename>src/lib/Makefile.am</filename> (no &SWIG;) +&src_lib_Makefile_1_am_listing; + + + + + + + + +The &SWIG; .i file + +Now that we've got something that will compile, we need to write +the &SWIG; .i file. This is a pared-down version of the .h file, plus +a bit of magic that has python work with the boost shared_ptr's. +To reduce code bloat, we only declare methods that &well; want to +access from Python. + +We're going to call the .i file +howto.i, and use it to hold the &SWIG; +declarations for all classes from howto that will +be accessible from python. It's quite small: +&howto_1_i_listing; + + + + +Putting it all together + +Now we need to modify src/lib/Makefile.am +to run &SWIG; and to add the glue it generates to the shared library. + + +<filename>src/lib/Makefile.am</filename> (with &SWIG;) +&src_lib_Makefile_2_am_listing; + + +make now builds everything successfully. We get a +few warnings, but &thats; OK. + +Changing directories back to the python directory we try +make check again: + + [eb@bufo python]$ make check + make check-TESTS + make[1]: Entering directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' + . + ---------------------------------------------------------------------- + Ran 1 test in 0.004s + + OK + PASS: run_tests + ================== + All 1 tests passed + ================== + make[1]: Leaving directory `/home/eb/gr-build/gr-howto-write-a-block/src/python' + [eb@bufo python]$ + +Victory! Our new block works! + + + + + + +Additional gr_block methods + +In our howto_square_ff example above, we only +had to override the general_work method to +accomplish our goal. gr_block provides a few other +methods that are sometimes useful. + +forecast + +Looking at general_work you may +have wondered how the system knows how much data it needs to +ensure is valid in each of the input arrays. The +forecast method provides this +information. + +The default implementation of forecast +says there is a 1:1 relationship between noutput_items and the +requirements for each input stream. The size of the items is defined by +gr_io_signatures in the constructor of +gr_block. The sizes of the input and output items +can of course differ; this still qualifies as a 1:1 relationship. + + // default implementation: 1:1 + + void + gr_block::forecast (int noutput_items, + gr_vector_int &ninput_items_required) + { + unsigned ninputs = ninput_items_required.size (); + for (unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = noutput_items; + } + + + +Although the 1:1 implementation worked for howto_square_ff, it +wouldn't be appropriate for interpolators, decimators, or blocks +with a more complicated relationship between noutput_items and the +input requirements. That said, by deriving your classes from +gr_sync_block, +gr_sync_interpolator or +gr_sync_decimator instead of +gr_block, you can often avoid +implementing forecast. + + + +set_output_multiple + +When implementing your general_work +routine, &its; occasionally convenient to have the run time system +ensure that you are only asked to produce a number of output items +that is a multiple of some particular value. This might occur if your +algorithm naturally applies to a fixed sized block of data. +Call set_output_multiple in your constructor +to specify this requirement. The default output multiple is 1. + + + + + + + +Subclasses for common patterns + +gr_block allows tremendous flexibility +with regard to the consumption of input streams and the production of +output streams. Adroit use of forecast and +consume allows variable rate blocks to be +built. It is possible to construct blocks that consume data at +different rates on each input, and produce output at a rate that +is a function of the contents of the input data. + +On the other hand, it is very common for signal processing +blocks to have a fixed relationship between the input rate and the +output rate. Many are 1:1, while others have 1:N or N:1 +relationships. + +Another common requirement is the need to examine more than one +input sample to produce a single output sample. This is orthogonal to +the relationship between input and output rate. For example, a +non-decimating, non-interpolating FIR filter needs to examine N input +samples for each output sample it produces, where N is the number of +taps in the filter. However, it only consumes a single input sample +to produce a single output. We call this concept "history", but you +could also think of it as "look-ahead". + +<classname>gr_sync_block</classname> + + + +gr_sync_block +is derived from + +gr_block +and implements a 1:1 block with +optional history. Given that we know the input to output rate, +certain simplifications are possible. From the implementor's +point-of-view, the primary change is that we define a +work method instead of +general_work. work +has a slightly different calling sequence; +It omits the unnecessary ninput_items parameter, and arranges for +consume_each to be called on our +behalf. + + /*! + * \brief Just like gr_block::general_work, only this arranges to + * call consume_each for you. + * + * The user must override work to define the signal processing code + */ + virtual int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) = 0; + + +This gives us fewer things to worry about, and less code to +write. If the block requires history greater than 1, call +set_history in the constructor, or any time +the requirement changes. + +gr_sync_block provides a +version of forecast that handles the +history requirement. + + + +<classname>gr_sync_decimator</classname> + + + +gr_sync_decimator +is derived from + +gr_sync_block +and implements a N:1 block with optional history. + + + + +<classname>gr_sync_interpolator</classname> + + + +gr_sync_interpolator +is derived from + +gr_sync_block +and implements a 1:N block with optional history. + + + + + + + + +Second Block: <classname>howto_square2_ff</classname> + +Given that we now know about +gr_sync_block, the way +howto_square_ff should really be implemented is +by subclassing gr_sync_block. + +Here are the revised sources: , +. +The accompanying files contain the additional test code. + + + +<filename>howto_square2_ff.h</filename> +&howto_square2_ff_h_listing; + + + +<filename>howto_square2_ff.cc</filename> +&howto_square2_ff_cc_listing; + + + + +Where to from Here? + +At this point, we've got a basic overview of how the system +goes together. For more insight, I suggest that you look at the code +of the system. The doxygen generated class +hierarchy is a useful way to find things that might interest +you. + + + + +Miscellaneous Tips + +Sources and Sinks + +Sources and sinks are derived from +gr_sync_block. The only thing different about +them is that sources have no inputs and sinks have no outputs. This +is reflected in the gr_io_signatures that are +passed to the gr_sync_block constructor. +Take a look at gr_file_source.{h,cc} and +gr_file_sink.{h,cc} for some very straight-forward examples. + + + + + +Debugging with <application>gdb</application> + +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. The trick of course +is that all of &gnuradio;, including your new block, is dynamically +loaded into python for execution. + +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. + + + #!/usr/bin/env python + from gnuradio import gr + from gnuradio import my_buggy_module + + # insert this in your test code... + import os + print 'Blocked waiting for GDB attach (pid = %d)' % (os.getpid(),) + raw_input ('Press Enter to continue: ') + # remainder of your test code follows... + + +Another SNAFU you might run into is that gdb 6.2 isn't +able to set breakpoints in the constructors or destructors generated +by g++ 3.4. In this case, insert a call to the nop function +gri_debugger_hook in the constructor and recompile. Load the code as +before and set a break point on gri_debugger_hook. + + + + +Performance Measurement with <application>oprofile</application> +Oprofile is your friend. +See http://oprofile.sourceforge.net. + + + + + +Coming Attractions + + +Improved Type System + + + +Hierarchical Blocks + + + + + +
diff --git a/gr-howto-write-a-block/limbo/doc/howto_1.i b/gr-howto-write-a-block/limbo/doc/howto_1.i new file mode 100644 index 00000000..640d0897 --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/howto_1.i @@ -0,0 +1,29 @@ +/* -*- c++ -*- */ + +%include "exception.i" +%import "gnuradio.i" // the common stuff + +%{ +#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix +#include "howto_square_ff.h" +#include +%} + +// ---------------------------------------------------------------- + +/* + * First arg is the package prefix. + * Second arg is the name of the class minus the prefix. + * + * This does some behind-the-scenes magic so we can + * access howto_square_ff from python as howto.square_ff + */ +GR_SWIG_BLOCK_MAGIC(howto,square_ff); + +howto_square_ff_sptr howto_make_square_ff (); + +class howto_square_ff : public gr_block +{ +private: + howto_square_ff (); +}; diff --git a/gr-howto-write-a-block/limbo/doc/make_numbered_listing.py b/gr-howto-write-a-block/limbo/doc/make_numbered_listing.py new file mode 100755 index 00000000..889c2d78 --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/make_numbered_listing.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +import sys +import os, os.path +from optparse import OptionParser + +def quote_line (line): + line = line.replace ('&', '&') + line = line.replace ('<', '<') + line = line.replace ('>', '>') + line = line.replace ("'", ''') + line = line.replace ('"', '"') + return line + +def generate_listing (input_filename, title=None): + inf = open (input_filename, "r") + output_filename = os.path.basename (input_filename) + '.xml' + outf = open (output_filename, "w") + outf.write ('\n') + # outf.write ('\n' % (input_filename,)) + # if not title: + # title = input_filename + # outf.write ('') + # outf.write (title) + # outf.write ('\n') + outf.write ('\n'); + + lineno = 0 + for line in inf: + line = line.expandtabs (8) + line = quote_line (line) + lineno = lineno + 1 + outf.write ('%3d %s' % (lineno, line)) + + outf.write ('\n') + # outf.write ('\n') + + +def main (): + for file in sys.argv[1:]: + generate_listing (file) + +if __name__ == '__main__': + main () + diff --git a/gr-howto-write-a-block/limbo/doc/qa_howto_1.py b/gr-howto-write-a-block/limbo/doc/qa_howto_1.py new file mode 100755 index 00000000..3173110f --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/qa_howto_1.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +from gnuradio import gr, gr_unittest +import howto + +class qa_howto (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_001_square_ff (self): + src_data = (-3, 4, -5.5, 2, 3) + expected_result = (9, 16, 30.25, 4, 9) + src = gr.vector_source_f (src_data) + sqr = howto.square_ff () + dst = gr.vector_sink_f () + self.tb.connect (src, sqr) + self.tb.connect (sqr, dst) + self.tb.run () + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6) + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gr-howto-write-a-block/limbo/doc/src_lib_Makefile_1.am b/gr-howto-write-a-block/limbo/doc/src_lib_Makefile_1.am new file mode 100644 index 00000000..e97d70d1 --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/src_lib_Makefile_1.am @@ -0,0 +1,25 @@ +include $(top_srcdir)/Makefile.common + +# Install this stuff so that it ends up as the gnuradio.howto module +# This usually ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio + +ourpythondir = $(grpythondir) +ourlibdir = $(grpyexecdir) + +INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) + +ourlib_LTLIBRARIES = _howto.la + +# These are the source files that go into the shared library +_howto_la_SOURCES = \ + howto_square_ff.cc + +# magic flags +_howto_la_LDFLAGS = -module -avoid-version + +# These headers get installed in ${prefix}/include/gnuradio +grinclude_HEADERS = \ + howto_square_ff.h + +MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc diff --git a/gr-howto-write-a-block/limbo/doc/src_lib_Makefile_2.am b/gr-howto-write-a-block/limbo/doc/src_lib_Makefile_2.am new file mode 100644 index 00000000..dca236e2 --- /dev/null +++ b/gr-howto-write-a-block/limbo/doc/src_lib_Makefile_2.am @@ -0,0 +1,86 @@ +# +# Copyright 2004,2008 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +# Install this stuff so that it ends up as the gnuradio.howto module +# This usually ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio + +ourpythondir = $(grpythondir) +ourlibdir = $(grpyexecdir) + +INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) + +SWIGCPPPYTHONARGS = -noruntime -c++ -python $(PYTHON_CPPFLAGS) \ + -I$(swigincludedir) -I$(grincludedir) $(WITH_SWIG_INCLUDES) + +ALL_IFILES = \ + $(LOCAL_IFILES) \ + $(NON_LOCAL_IFILES) + +NON_LOCAL_IFILES = \ + $(GNURADIO_CORE_INCLUDEDIR)/swig/gnuradio.i + + +LOCAL_IFILES = \ + howto.i + +# These files are built by SWIG. The first is the C++ glue. +# The second is the python wrapper that loads the _howto shared library +# and knows how to call our extensions. + +BUILT_SOURCES = \ + howto.cc \ + howto.py + +# This gets howto.py installed in the right place +ourpython_PYTHON = \ + howto.py + +ourlib_LTLIBRARIES = _howto.la + +# These are the source files that go into the shared library +_howto_la_SOURCES = \ + howto.cc \ + howto_square_ff.cc + +# magic flags +_howto_la_LDFLAGS = -module -avoid-version + +# link the library against some comon swig runtime code and the +# c++ standard library +_howto_la_LIBADD = \ + -lgrswigrunpy \ + -lstdc++ + +howto.cc howto.py: howto.i $(ALL_IFILES) + $(SWIG) $(SWIGCPPPYTHONARGS) -module howto -o howto.cc $< + +# These headers get installed in ${prefix}/include/gnuradio +grinclude_HEADERS = \ + howto_square_ff.h + +# These swig headers get installed in ${prefix}/include/gnuradio/swig +swiginclude_HEADERS = \ + $(LOCAL_IFILES) + +MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc