1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
|
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbookx.dtd" [
<!ENTITY gnuradio "<application>GNU Radio</application>">
<!ENTITY SWIG "<application>SWIG</application>">
<!ENTITY gr_block "<classname>gr_block</classname>">
<!ENTITY square "<classname>howto_square_ff</classname>">
<!ENTITY were "we're">
<!ENTITY well "we'll">
<!ENTITY thats "that's">
<!ENTITY its "it's">
<!ENTITY lets "let's">
<!ENTITY youre "you're">
<!ENTITY gr_block_listing SYSTEM "gr_block.h.xml">
<!ENTITY qa_howto_1_listing SYSTEM "qa_howto_1.py.xml">
<!ENTITY howto_square_ff_h_listing SYSTEM "howto_square_ff.h.xml">
<!ENTITY howto_square_ff_cc_listing SYSTEM "howto_square_ff.cc.xml">
<!ENTITY howto_square2_ff_h_listing SYSTEM "howto_square2_ff.h.xml">
<!ENTITY howto_square2_ff_cc_listing SYSTEM "howto_square2_ff.cc.xml">
<!ENTITY howto_1_i_listing SYSTEM "howto_1.i.xml">
<!ENTITY src_lib_Makefile_1_am_listing SYSTEM "src_lib_Makefile_1.am.xml">
<!ENTITY src_lib_Makefile_2_am_listing SYSTEM "src_lib_Makefile_2.am.xml">
]>
<article>
<articleinfo>
<title>How to Write a Signal Processing Block</title>
<author>
<firstname>Eric</firstname>
<surname>Blossom</surname>
<affiliation>
<address>
<email>eb@comsec.com</email>
</address>
</affiliation>
</author>
<revhistory>
<revision>
<revnumber>0.1</revnumber>
<date>2005-01-20</date>
</revision>
<revision>
<revnumber>0.2</revnumber>
<date>2005-02-02</date>
<revremark>Updated for SWIG 1.3.24</revremark>
</revision>
<revision>
<revnumber>0.3</revnumber>
<date>2006-07-21</date>
<revremark>Clarification of 1:1 fixed rate vs item size</revremark>
</revision>
</revhistory>
<copyright>
<year>2004</year>
<year>2005</year>
<holder>Free Software Foundation, Inc.</holder>
</copyright>
<abstract><para>This article explains how to write signal
processing blocks for <application>GNU Radio</application>.
</para></abstract>
</articleinfo>
<sect1 id="prereqs"><title>Prerequisites</title>
<para>This article assumes that the reader has basic familiarity with
GNU Radio and has read and understood
<ulink url="http://www.gnu.org/software/gnuradio/doc/exploring-gnuradio.html">
<citetitle>Exploring GNU Radio</citetitle></ulink>.
</para>
<para>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 <ulink
url="ftp://ftp.gnu.org/gnu/gnuradio">
ftp://ftp.gnu.org/gnu/gnuradio</ulink> or one of the mirrors. The
file you want is
<filename>gr-howto-write-a-block-X.Y.tar.gz</filename>. Pick the one
with the highest version number.
See <ulink url="http://comsec.com/wiki?CvsAccess">
http://comsec.com/wiki?CvsAccess</ulink> for CVS Access.
</para>
</sect1>
<sect1 id="intro"><title>Introduction</title>
<para>&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++.</para>
<para>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 <classname>gr.flow_graph</classname> 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
<classname>float</classname> and <classname>gr_complex</classname>
(equivalent to std::complex<float>), though other types are used,
including those representing structures, arrays or other types of
packetized data.</para>
<para>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.</para>
</sect1>
<sect1 id="overview"><title>The View from 30,000 Feet</title>
<para>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.</para>
<para>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.</para>
</sect1>
<sect1 id="gr_block"><title></title>
<para>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.</para>
<para>Our first examples will derive directly from &gr_block;. Later
we will look at some other subclasses that simplify the process for
common cases.</para>
</sect1><!-- end gr_block sect1 -->
<!-- ================================================================ -->
<sect1 id="autotools"><title>Autotools, Makefiles, and Directory Layout</title>
<para>Before we dive into the code, &lets; talk a bit about the
overall build environment and the directory structure that &well;
be using.</para>
<para>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
<application>autoconf</application>,
<application>automake</application>, and
<application>libtool</application> 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.)</para>
<variablelist>
<varlistentry><term>automake</term>
<listitem><para>automake and configure work together to generate GNU
compliant Makefiles from a much higher level description contained in
the corresponding Makefile.am file. <filename>Makefile.am</filename>
specifies the libraries and programs to build and the source files
that compose each. Automake reads <filename>Makefile.am</filename>
and produces <filename>Makefile.in</filename>. Configure reads
<filename>Makefile.in</filename> and produces
<filename>Makefile</filename>. 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
<filename>Makefile</filename> to be 5 or 6 times larger than
<filename>Makefile.am</filename>.</para>
</listitem>
</varlistentry>
<varlistentry><term>autoconf</term>
<listitem><para>autoconf reads <filename>configure.ac</filename>
and produces the <filename>configure</filename> shell
script. <filename>configure</filename> 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.</para>
</listitem>
</varlistentry>
<varlistentry><term>libtool</term>
<listitem><para>libtool works behind the scenes and provides the magic
to construct shared libraries on a wide variety of systems.</para>
</listitem>
</varlistentry>
</variablelist>
<para><xref linkend="dir-layout"/> shows the directory layout and
common files &well; be using. After renaming the
<replaceable>topdir</replaceable> directory, use it in your projects
too. We'll talk about particular files as they come up later.</para>
<table id="dir-layout"><title>Directory Layout</title>
<tgroup cols="2">
<thead><row>
<entry>File/Dir Name</entry>
<entry>Comment</entry>
</row>
</thead>
<tbody>
<row>
<entry><replaceable>topdir</replaceable>/Makefile.am</entry>
<entry>Top level Makefile.am</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/Makefile.common</entry>
<entry>Common fragment included in sub-Makefiles</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/bootstrap</entry>
<entry>Runs autoconf, automake, libtool first time through</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/config</entry>
<entry>Directory of m4 macros used by configure.ac</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/configure.ac</entry>
<entry>Input to autoconf</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/src</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/src/lib</entry>
<entry>C++ code goes here</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/src/lib/Makefile.am</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/src/python</entry>
<entry>Python code goes here</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/src/python/Makefile.am</entry>
</row>
<row>
<entry><replaceable>topdir</replaceable>/src/python/run_tests</entry>
<entry>Script to run tests in the build tree</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<!-- ================================================================ -->
<sect1 id="naming"><title>Naming Conventions</title>
<para>&gnuradio; uses a set of naming conventions to assist in
comprehending the code base and gluing C++ and Python together.
Please follow them.</para>
<sect2 id="camel-case"><title><emphasis>Death to CamelCaseNames!</emphasis></title>
<para>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.</para>
<para>With the exception of macros and other constant values, all
identifiers shall be lower case with <literal>words_separated_like_this</literal>.</para>
<para>Macros and constant values (e.g., enumerated values,
<literal>static const int FOO = 23</literal>) shall be in <literal>UPPER_CASE</literal>.</para>
</sect2>
<sect2 id="global_names"><title>Global Names</title>
<para>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 <literal>gr_open_file (...)</literal>.</para>
<para>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.</para>
</sect2>
<sect2 id="package_prefixes"><title>Package Prefixes</title>
<para>These are the current package prefixes:
<variablelist>
<varlistentry><term>gr_</term>
<listitem><para>Almost everything.</para></listitem>
</varlistentry>
<varlistentry><term>gri_</term>
<listitem><para>
Implementation primitives. Sometimes we
have both a gr_<replaceable>foo</replaceable> and a gri_<replaceable>foo</replaceable>. In that case,
gr_<replaceable>foo</replaceable> would be derived from gr_block and gri_<replaceable>foo</replaceable>
would be the low level guts of the function.</para></listitem>
</varlistentry>
<varlistentry><term>atsc_</term>
<listitem><para>Code related to the Advanced Television Standards Committee HDTV implementation
</para></listitem>
</varlistentry>
<varlistentry><term>usrp_</term>
<listitem><para>Universal Software Radio Peripheral.</para></listitem>
</varlistentry>
<varlistentry><term>qa_</term>
<listitem><para>Quality Assurance (Test code.)</para></listitem>
</varlistentry>
</variablelist>
</para>
</sect2>
<sect2 id="class-data-members"><title>Class Data Members (instance variables)</title>
<para>All class data members shall begin with d_<replaceable>foo</replaceable>.</para>
<para>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_. </para>
<literallayout>
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)
{
...
}
...
};
</literallayout>
</sect2>
<sect2 id="static-data-members"><title>Class Static Data Members (class variables)</title>
<para>
All class static data members shall begin with s_<replaceable>foo</replaceable>.
</para>
</sect2>
<sect2 id="file-names"><title>File Names</title>
<para>Each significant class shall be contained in its own file. The
declaration of class <classname>gr_foo</classname> shall be in
<filename>gr_foo.h</filename> and the definition in
<filename>gr_foo.cc</filename>.</para>
</sect2>
<sect2><title>Suffixes</title>
<para>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.</para>
<para>These are the suffix characters and their interpretations:
<itemizedlist>
<listitem><para>f - single precision floating point</para></listitem>
<listitem><para>c - complex<float></para></listitem>
<listitem><para>s - short (16-bit integer)</para></listitem>
<listitem><para>i - integer (32-bit integer)</para></listitem>
</itemizedlist>
</para>
<para>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
<classname>gr_fft_vcc</classname>. The FFT block takes a vector of
complex numbers on its input and produces a vector of complex
numbers on its output.</para>
</sect2>
</sect1>
<sect1 id="square"><title>First Block: □</title>
<para>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.</para>
<para>Following the naming conventions, &well; use
<literal>howto</literal> as our package prefix, and the block will
be called <classname>howto_square_ff</classname>.</para>
<para>We are going to arrange that this block, as well as the others
that we write in this article, end up in the
<literal>gnuradio.howto</literal> Python module. This will allow us
to access it from Python like this:
<programlisting>
from gnuradio import howto
sqr = howto.square_ff ()
</programlisting>
</para>
<sect2 id="test_driven"><title>Test Driven Programming</title>
<para>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.</para>
<para>How hard could this be? Turns out that this is easy! Check out
<xref linkend="qa_howto_1.py"/>.</para>
<example id="qa_howto_1.py">
<title><filename>qa_howto.py</filename> (first version)</title>
&qa_howto_1_listing;
</example>
<para>
<classname>gr_unittest</classname> is an extension to the standard
python module <classname>unittest</classname>.
<classname>gr_unittest</classname> 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
<methodname>test_</methodname> and runs them. Unittest wraps each call
to <methodname>test_*</methodname> with matching calls to
<methodname>setUp</methodname> and <methodname>tearDown</methodname>.
See the python <ulink url="http://docs.python.org/lib/module-unittest.html">
unittest</ulink> documentation for details.
</para>
<para>When we run the test,
gr_unittest.main is going to invoke
<methodname>setUp</methodname>,
<methodname>test_001_square_ff</methodname>, and
<methodname>tearDown</methodname>.</para>
<para>
<methodname>test_001_square_ff</methodname> 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.</para>
<para>The <methodname>run</methodname> 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.
</para>
</sect2>
<sect2 id="build_vs_install"><title>Build Tree vs. Install Tree</title>
<para>The build tree is everything from <replaceable>topdir</replaceable>
(the one containing configure.ac) down. The path to the install tree is
<filename>
<replaceable>prefix</replaceable>/lib/python<replaceable>version</replaceable>/site-packages</filename>,
where <replaceable>prefix</replaceable> is the <literal>--prefix</literal>
argument to configure (default <filename>/usr/local</filename>) and
<replaceable>version</replaceable> is the installed version of
python. A typical value is
<filename>/usr/local/lib/python2.3/site-packages</filename>.</para>
<para>We normally set our PYTHONPATH environment variable to point at
the install tree, and do this in <filename>~/.bash_profile</filename>
or <filename>~/.profile</filename>.
This allows our python apps to access all the standard python
libraries, plus our locally installed stuff like GNU Radio.</para>
<para>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.</para>
</sect2>
<sect2 id="make_check"><title>make check</title>
<para>We use <command>make check</command> to run our tests.
Make check invokes the <command>run_tests</command> 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 <filename>qa_*.py</filename> and reports
the overall success or failure.</para>
<para>There is quite a bit of behind-the-scenes action required to use
the non-installed versions of our code (look at
<filename>runtest</filename> for a cheap thrill.)</para>
<para>Finally, running <command>make check</command> in the python
directory produces this result:
<literallayout>
[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]$
</literallayout>
Excellent! Our test failed, just as we expected. The ImportError
indicates that it can't find the module named
<classname>howto</classname>. No surprise, since we haven't written it yet.
</para>
</sect2>
<sect2><title>The C++ code</title>
<para>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 <classname>gr_block</classname> or
one of its subclasses. Let's take a look at
<xref linkend="gr_block.h"/>.</para>
<example id="gr_block.h">
<title><filename>gr_block.h</filename></title>
&gr_block_listing;
</example>
<para>A quick scan of <filename>gr_block.h</filename> reveals that
since <methodname>general_work</methodname> is pure virtual, we
definitely need to override that.
<methodname>general_work</methodname> is the method that does the
actual signal processing. For our squaring example we'll
need to override <methodname>general_work</methodname> and provide a
constructor and destructor and a bit of stuff to take advantage of
the <ulink url="http://www.boost.org">boost</ulink>
<ulink url="http://www.boost.org/libs/smart_ptr/smart_ptr.htm">
<classname>shared_ptr</classname>s.</ulink>
</para>
<para><xref linkend="howto_square_ff.h"/>
and <xref linkend="howto_square_ff.cc"/> are the header and c++
source.</para>
<example id="howto_square_ff.h">
<title><filename>howto_square_ff.h</filename></title>
&howto_square_ff_h_listing;
</example>
<example id="howto_square_ff.cc">
<title><filename>howto_square_ff.cc</filename></title>
&howto_square_ff_cc_listing;
</example>
<para>Now we need a Makefile.am to get all this to build.
<xref linkend="src_lib_Makefile_1"/>
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.</para>
<example id="src_lib_Makefile_1">
<title><filename>src/lib/Makefile.am</filename> (no &SWIG;)</title>
&src_lib_Makefile_1_am_listing;
</example>
</sect2>
<!-- ==============================
<sect2 id="io_sig"><title><classname>gr_io_signature</classname></title>
<para></para>
</sect2>
<sect2 id="forecast"><title><methodname>forecast</methodname></title>
<para></para>
</sect2>
<sect2 id="output_multiple">
<title><methodname>set_output_multiple</methodname></title>
<para></para>
</sect2>
============================== -->
<sect2 id="swig"><title>The &SWIG; .i file</title>
<para>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.</para>
<para>We're going to call the .i file
<filename>howto.i</filename>, and use it to hold the &SWIG;
declarations for all classes from <literal>howto</literal> that will
be accessible from python. It's quite small:
&howto_1_i_listing;
</para>
</sect2>
<sect2><title>Putting it all together</title>
<para>
Now we need to modify <filename>src/lib/Makefile.am</filename>
to run &SWIG; and to add the glue it generates to the shared library.</para>
<example id="src_lib_Makefile_2">
<title><filename>src/lib/Makefile.am</filename> (with &SWIG;)</title>
&src_lib_Makefile_2_am_listing;
</example>
<para><command>make</command> now builds everything successfully. We get a
few warnings, but &thats; OK.</para>
<para>Changing directories back to the python directory we try
<command>make check</command> again:
<literallayout>
[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]$
</literallayout>
<emphasis>Victory! Our new block works!</emphasis>
</para>
</sect2>
</sect1><!-- end First Block: square -->
<sect1 id="additional_methods"><title>Additional gr_block methods</title>
<para>In our <classname>howto_square_ff</classname> example above, we only
had to override the <methodname>general_work</methodname> method to
accomplish our goal. <classname>gr_block</classname> provides a few other
methods that are sometimes useful.</para>
<sect2 id="forecast"><title>forecast</title>
<para>Looking at <methodname>general_work</methodname> you may
have wondered how the system knows how much data it needs to
ensure is valid in each of the input arrays. The
<methodname>forecast</methodname> method provides this
information.</para>
<para>The default implementation of <methodname>forecast</methodname>
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
<classname>gr_io_signature</classname>s in the constructor of
<classname>gr_block</classname>. The sizes of the input and output items
can of course differ; this still qualifies as a 1:1 relationship.
<programlisting>
// 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;
}
</programlisting>
</para>
<para>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
<classname>gr_sync_block</classname>,
<classname>gr_sync_interpolator</classname> or
<classname>gr_sync_decimator</classname> instead of
<classname>gr_block</classname>, you can often avoid
implementing <methodname>forecast</methodname>.</para>
</sect2>
<sect2 id="set_output_multiple"><title>set_output_multiple</title>
<para>When implementing your <methodname>general_work</methodname>
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 <methodname>set_output_multiple</methodname> in your constructor
to specify this requirement. The default output multiple is 1.</para>
</sect2>
</sect1>
<sect1 id="common_patterns">
<title>Subclasses for common patterns</title>
<para><classname>gr_block</classname> allows tremendous flexibility
with regard to the consumption of input streams and the production of
output streams. Adroit use of <methodname>forecast</methodname> and
<methodname>consume</methodname> 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.</para>
<para>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.</para>
<para>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".</para>
<sect2 id="gr_sync_block"><title><classname>gr_sync_block</classname></title>
<para>
<ulink url="http://www.gnu.org/software/gnuradio/doc/classgr__sync__block.html">
<classname>gr_sync_block</classname></ulink>
is derived from
<ulink url="http://www.gnu.org/software/gnuradio/doc/classgr__block.html">
<classname>gr_block</classname></ulink>
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
<methodname>work</methodname> method instead of
<methodname>general_work</methodname>. <methodname>work</methodname>
has a slightly different calling sequence;
It omits the unnecessary ninput_items parameter, and arranges for
<methodname>consume_each</methodname> to be called on our
behalf.</para>
<programlisting>
/*!
* \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;
</programlisting>
<para>This gives us fewer things to worry about, and less code to
write. If the block requires history greater than 1, call
<methodname>set_history</methodname> in the constructor, or any time
the requirement changes.</para>
<para><classname>gr_sync_block</classname> provides a
version of <methodname>forecast</methodname> that handles the
history requirement.</para>
</sect2>
<sect2 id="gr_sync_decimator"><title><classname>gr_sync_decimator</classname></title>
<para>
<ulink url="http://www.gnu.org/software/gnuradio/doc/classgr__sync__decimator.html">
<classname>gr_sync_decimator</classname></ulink>
is derived from
<ulink url="http://www.gnu.org/software/gnuradio/doc/classgr__sync__block.html">
<classname>gr_sync_block</classname></ulink>
and implements a N:1 block with optional history.
</para>
</sect2>
<sect2 id="gr_sync_interpolator"><title><classname>gr_sync_interpolator</classname></title>
<para>
<ulink url="http://www.gnu.org/software/gnuradio/doc/classgr__sync__interpolator.html">
<classname>gr_sync_interpolator</classname></ulink>
is derived from
<ulink url="http://www.gnu.org/software/gnuradio/doc/classgr__sync__block.html">
<classname>gr_sync_block</classname></ulink>
and implements a 1:N block with optional history.
</para>
</sect2>
</sect1>
<sect1 id="square2">
<title>Second Block: <classname>howto_square2_ff</classname></title>
<para>Given that we now know about
<classname>gr_sync_block</classname>, the way
<classname>howto_square_ff</classname> should really be implemented is
by subclassing <classname>gr_sync_block</classname>.</para>
<para>Here are the revised sources: <xref
linkend="howto_square2_ff.h"/>,
<xref linkend="howto_square2_ff.cc"/>.
The accompanying files contain the additional test code.
</para>
<example id="howto_square2_ff.h">
<title><filename>howto_square2_ff.h</filename></title>
&howto_square2_ff_h_listing;
</example>
<example id="howto_square2_ff.cc">
<title><filename>howto_square2_ff.cc</filename></title>
&howto_square2_ff_cc_listing;
</example>
</sect1>
<sect1 id="where_to"><title>Where to from Here?</title>
<para>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 <ulink
url="http://www.gnu.org/software/gnuradio/doc/hierarchy.html"> class
hierarchy</ulink> is a useful way to find things that might interest
you.</para>
</sect1>
<sect1 id="tips"><title>Miscellaneous Tips</title>
<sect2 id="sources_and_sinks"><title>Sources and Sinks</title>
<para>Sources and sinks are derived from
<classname>gr_sync_block</classname>. The only thing different about
them is that sources have no inputs and sinks have no outputs. This
is reflected in the <classname>gr_io_signature</classname>s that are
passed to the <classname>gr_sync_block</classname> constructor.
Take a look at <filename>gr_file_source.{h,cc}</filename> and
<filename>gr_file_sink.{h,cc}</filename> for some very straight-forward examples.
</para>
</sect2>
<sect2 id="debugging">
<title>Debugging with <application>gdb</application></title>
<para>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 <application>gdb</application> to debug it. The trick of course
is that all of &gnuradio;, including your new block, is dynamically
loaded into python for execution.</para>
<para>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.</para>
<programlisting>
#!/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...
</programlisting>
<para>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.</para>
</sect2>
<sect2 id="oprofile">
<title>Performance Measurement with <application>oprofile</application></title>
<para>Oprofile is your friend.
See <ulink url="http://oprofile.sourceforge.net">http://oprofile.sourceforge.net</ulink>.
</para>
</sect2>
</sect1><!-- end tips -->
<sect1 id="futures"><title>Coming Attractions</title>
<para></para>
<sect2 id="types"><title>Improved Type System</title>
<para></para>
</sect2>
<sect2 id="hierarchy"><title>Hierarchical Blocks</title>
<para></para>
</sect2>
</sect1><!-- end Coming Attractions -->
</article>
|