https://wiki.gnuradio.org/api.php?action=feedcontributions&user=N1ai&feedformat=atomGNU Radio - User contributions [en]2024-03-29T10:12:31ZUser contributionsMediaWiki 1.39.5https://wiki.gnuradio.org/index.php?title=ALSAPulseAudio&diff=12798ALSAPulseAudio2022-12-23T00:51:39Z<p>N1ai: Clarify the roles of ALSA and PulseAudio</p>
<hr />
<div><!-- ALSAPulseAudio.mediawiki --><br />
== Working with ALSA and Pulse Audio ==<br />
<br />
* Don't clip: The maximum amplitude in your signal '''must not''' exceed 1.0.<br />
* Set "OK to Block" to "No" when the flowgraph is throttled by another hardware device.<br />
* Sound cards don't support arbitrary sampling rates. If your audio is choppy, check the rate of your audio sink or source: 44100 Hz works under all known audio cards, 48000 Hz on most, others may not.<br />
* If you get lots of <code>aU</code> (audio underruns) on your terminal, try creating or editing ~/.gnuradio/config.conf as follows:<br />
<pre><br />
[audio_alsa]<br />
nperiods = 32<br />
period_time = 0.010<br />
verbose = false<br />
</pre><br />
<br />
== Talking to ALSA ==<br />
<br />
When used on Linux, the GNU Radio Audio Sink and Source blocks typically use the ALSA Application Programming Interface (API), unless ALSA support was disabled during build time. ALSA has been the standard audio API under Linux for a decade or more, so basically all programs that produce audio know how to deal with it. GNU Radio also supports other audio APIs, but this page focuses on ALSA.<br />
<br />
The ALSA system has a plug-in architecture, and one of these plug-ins allows applications that use the ALSA API such as GNU Radio to utilize the [https://en.wikipedia.org/wiki/PulseAudio PulseAudio] ''sound server'', which supports the complex use cases required on a modern computer such as mixing sound from various sources into a single stream that can be controlled by a central volume control, remote/network audio streaming, etc.<br />
<br />
However, PulseAudio's device isn't always perfect. PulseAudio is capable of resampling internally, but the results aren't always predictable. Also, because of its complexity, PulseAudio can sometimes impact performance negatively, causing gaps in the audio being played back. These gaps can be quite long on slower computers. For some GNU Radio applications, it may be desirable to use the ALSA device directly, rather than using the PulseAudio path.<br />
<br />
To do so, you can obtain a list of the ALSA playback devices (for an Audio Sink) using the <code>aplay</code> program.<br />
<br />
* from a terminal window enter:<br />
<code>aplay -L</code><br />
* a long list of options will be displayed, such as:<br />
<pre><br />
default<br />
Playback/recording through the PulseAudio sound server<br />
null<br />
Discard all samples (playback) or generate zero samples (capture)<br />
pulse<br />
PulseAudio Sound Server<br />
hdmi:CARD=HDMI,DEV=0<br />
HDA ATI HDMI, HDMI 0<br />
HDMI Audio Output<br />
hw:CARD=Generic,DEV=0<br />
HD-Audio Generic, ALC662 rev3 Analog<br />
Direct hardware device without any conversions<br />
plughw:CARD=Generic,DEV=0<br />
HD-Audio Generic, ALC662 rev3 Analog<br />
Hardware device with all software conversions<br />
...<br />
</pre><br />
* find the entry such as:<br />
<pre><br />
hw:CARD=Generic,DEV=0<br />
HD-Audio Generic, ALC662 rev3 Analog<br />
Direct hardware device without any conversions<br />
</pre><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in the list which matches your desired device.<br />
* use the first line of that entry (e.g. "hw:CARD=Generic,DEV=0") as the device name (without the quotes). The device name can be set in the Audio Sink block in <code>gnuradio-companion</code>, or in the C++ source code of a GNU Radio C++ application, or in the <code>audio_alsa</code> stanza in the <code>$HOME/.gnuradio/config.conf</code> file if you want to set the default device for all GNU Radio applications running from your Linux login session.<br />
<br />
For audio input devices (an Audio Source), use:<br />
<br />
<pre>arecord -L</pre><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to obtain a similar list.<br />
<br />
== Monitoring the audio input of your system with PulseAudio ==<br />
<br />
<p><b>IMPORTANT: this procedure only applies to an Audio Source block!</b></p><br />
<br />
PulseAudio has its own monitor &quot;ports&quot;. You can list all PulseAudio monitor sources by running:<br />
<br />
<pre>pactl list|grep &quot;Monitor Source&quot;|sed 's/^[[:space:]]*Monitor Source: //g'</pre><br />
<br />
This will give you one or more lines containing something like<br />
<br />
<pre>alsa_output.pci-0000_00_03.0.hdmi-stereo.monitor<br />
alsa_output.pci-0000_00_1b.0.analog-stereo.monitor<br />
alsa_output.pci-0000_06_00.1.hdmi-stereo.monitor</pre><br />
<br />
Select the right name; assuming we use the analog input, the second line would be the right.<br />
<br />
=== Add ALSA Pseudodevice for monitor ===<br />
<br />
Now, we need to edit (or create, if it doesn't already exist) <code>~/.asoundrc</code>.<br /><br />
Add<br />
<pre><br />
pcm.pulse_monitor {<br />
type pulse<br />
device alsa_output.pci-0000_00_1b.0.analog-stereo.monitor<br />
}<br />
<br />
ctl.pulse_monitor {<br />
type pulse<br />
device alsa_output.pci-0000_00_1b.0.analog-stereo.monitor<br />
}<br />
</pre><br />
of course, replacing the <code>device</code> name with the correct one from the previous step.<br />
<br />
=== Using the newly created device ===<br />
<br />
In the Audio Source block, use <code>pulse_monitor</code> as the device name:<br /><br />
<br />
[[File:PulseAudio-ALSA-Monitoring.png|PulseAudio-ALSA-Monitoring.png]]</div>N1aihttps://wiki.gnuradio.org/index.php?title=Talk:UsingVSCode&diff=12795Talk:UsingVSCode2022-12-20T17:13:03Z<p>N1ai: Requesting the vscode extensions to be installed be identified more precisely</p>
<hr />
<div>Comments on the instructions, since it is not working fully for me:<br />
<br />
Re: "In VSCode Extensions Marketplace, install C++ and GDB Extensions (also Python Debugger for stepping through Python Code)"<br />
<br />
This is ambiguous. Are you talking about one extension called "C++ and GDB Extensions" or two different ones?<br />
<br />
When I go to the marketplace I see one extension called "C/C++" by Microsoft.<br />
<br />
In the marketplace when I search for GDB I see "GDB Debug" by DamianKoper -- is this what is being used for GDB support?<br />
<br />
These are the extensions I installed, but would like clarification.<br />
<br />
Maybe the author Mormj could go to his extensions window (Ctrl+Shift+X in vscode) and give the names/authors of the extensions needed?<br />
<br />
The instructions worked for me only partially.<br />
<br />
I was able to get vscode to run my python script which starts my OOT, however it did not hit any breakpoints I placed in my OOT's C++ code.<br />
<br />
I used the Play button to start the debug session, waited till the script was running, then used Pause (||) to pause the code then went to Debug Console tab.<br />
<br />
In debug console I typed '-exec info breakpoints' and saw that the Address field of all breakpoints was <PENDING>.<br />
<br />
This is after I see the console output of my OOT code, so I know it is running.<br />
<br />
Any hints on how to debug this further?</div>N1aihttps://wiki.gnuradio.org/index.php?title=Python_Block_with_Vectors&diff=12395Python Block with Vectors2022-07-27T16:36:49Z<p>N1ai: Add additional information on streams versus vectors</p>
<hr />
<div><div style="float:right"><br />
{{Template:BeginnerTutorials}}<br />
</div><br />
This tutorial describes how the ''Python Embedded Block'' can be modified to accept vector inputs and outputs, and how the ''input_items'' vector indexing is different between vectors and streams.<br />
<br />
The previous tutorial, [[Creating_Your_First_Block|Creating Your First Block]], demonstrates how to create a Python block using the ''Embedded Python Block''. The next tutorial, [[Python_Block_Message_Passing|Python Block Message Passing]] describes how to send and receive messages using the ''Embedded Python Block''.<br />
<br />
== Starting the Flowgraph ==<br />
<br />
This tutorial uses vectors. Please complete the [[Streams_and_Vectors|Streams and Vectors]] tutorial before moving on.<br />
<br />
Add the blocks to the flowgraph:<br />
* Signal Source<br />
* Throttle<br />
* Stream to Vector<br />
* Embedded Python Block<br />
* Vector to Stream<br />
* QT GUI Time Sink<br />
* Virtual Sink<br />
* Virtual Source<br />
* Variable<br />
<br />
Change the block properties:<br />
* Signal Source<br />
** Output Type: float<br />
** Frequency: 100<br />
* Variable<br />
** Id: vectorLength<br />
** Value: 16<br />
* Stream to Vector, Num Items: vectorLength<br />
* Vector to Stream, Num Items: vectorLength<br />
* Virtual Sink, Stream Id: sinusoid<br />
* Virtual Source, Stream Id: sinusoid<br />
* QT GUI Time Sink<br />
** Autoscale: Yes<br />
** Number of Inputs: 2<br />
<br />
Connect the blocks according to the following flowgraph:<br />
<br />
[[File:PythonVectorStartingFlowgraph.png|800px]]<br />
<br />
== Accepting Vector Inputs and Outputs ==<br />
<br />
The ''Embedded Python Block'' needs to be modified to:<br />
* accept vector inputs<br />
* produce vector outputs<br />
* change the data types to ''float''<br />
<br />
With respect to the [[Streams_and_Vectors|Streams and Vectors]] tutorial, from a higher level point of view, ''Streams'' are typically just a special case of ''Vectors'', ones that have just one data element in parallel instead of multiple. This is why we can just change the <code>in_sig</code> and <code>out_sig</code> parameters to use vectors rather than streams. <br />
<br />
Since the case of streams is so common it has its own simple syntax, whereas the syntax for vectors is just a bit more complex. In particular, when using vectors, we must specify the vector's length along with the data type of the elements in the vectors. We do so using a tuple in Python.<br />
<br />
Double-click on the block to edit the source code.<br />
<br />
Change ''example_param'' in the function definition to ''vectorSize'':<br />
<pre>def __init__(self, vectorSize=16):</pre><br />
<br />
Change the ''name'':<br />
<pre>name='Max Hold Block',</pre><br />
<br />
Define the input and output vector signals using a tuple:<br />
<pre>in_sig=[(np.float32,vectorSize)],<br />
out_sig=[(np.float32,vectorSize)]</pre><br />
<br />
Remove the ''self.example_param = example_param'' line.<br />
<br />
Remove the multiplication by ''self.example_param'':<br />
<pre>output_items[0][:] = input_items[0]</pre><br />
<br />
The code should now look like the following:<br />
<br />
[[File:PythonVectorDefineBlock.png|800px]]<br />
<br />
<br />
Save the code (CTRL + S). Connect the ''Max Hold Block'' to the rest of the flowgraph:<br />
<br />
[[File:PythonVectorConnectMaxHold.png|800px]]<br />
<br />
== Warning for Vector Length Mismatches ==<br />
<br />
The ''Embedded Python Block'' has one difference that other [[OutOfTreeModules|Out of Tree Modules]] do not have. Before a flowgraph can be run GRC checks to ensure all of the connected data types and vector sizes match. During this process the default value of ''vectorSize'' in the ''__init()__'' function,<br />
<pre>def __init__(self, vectorSize=16):</pre><br />
<br />
is used to define the size of the vectors for the input and output,<br />
<br />
<pre>in_sig=[(np.float32,vectorSize)],<br />
out_sig=[(np.float32,vectorSize)]</pre><br />
<br />
when determining if the flowgraph is correct. In this case ''vectorSize=16''. GRC assumes the input and output ports are vectors with length 16, <u>even if a different parameter is passed in through the block properties!</u>. The following image shows how a vector size of 128 is passed in as a parameter which GRC does not catch as an error, but the flowgraph will crash once it's run:<br />
<br />
[[File:MismatchVectorSizeOption1.png|800px]]<br />
<br />
<br />
<pre>Traceback (most recent call last):<br />
File "/home/username/vectorinput.py", line 250, in <module><br />
main()<br />
File "/home/username/vectorinput.py", line 226, in main<br />
tb = top_block_cls()<br />
File "/home/username/vectorinput.py", line 188, in __init__<br />
self.connect((self.blocks_stream_to_vector_0, 0), (self.epy_block_0, 0))<br />
File "/usr/lib/python3/dist-packages/gnuradio/gr/hier_block2.py", line 48, in wrapped<br />
func(self, src, src_port, dst, dst_port)<br />
File "/usr/lib/python3/dist-packages/gnuradio/gr/hier_block2.py", line 111, in connect<br />
self.primitive_connect(*args)<br />
File "/usr/lib/python3/dist-packages/gnuradio/gr/runtime_swig.py", line 4531, in primitive_connect<br />
return _runtime_swig.top_block_sptr_primitive_connect(self, *args)<br />
RuntimeError: itemsize mismatch: stream_to_vector0:0 using 64, Embedded Python Block0:0 using 512</pre><br />
<br />
Alternatively, GRC will show an error if the default parameter does not match the vector size of the other blocks. In this case, the default vector length in the code is 128 but the passed-in parameter is 16:<br />
<br />
[[File:MismatchVectorSizeOption2.png|800px]]<br />
<br />
== Indexing Streams ==<br />
<br />
For a stream, inputs and outputs can be indexed using both the port number and the sample index.<br />
<br />
Indexing based on the port number returns all of the input samples for a specific port. For example,<br />
<br />
<pre>input_items[0]</pre><br />
<br />
returns all input samples on port 0.<br />
<br />
The following line returns the 4th input sample on port 0:<br />
<br />
<pre>input_items[0][3]</pre><br />
<br />
The indexing for streams is generalized to:<br />
<br />
<pre>input_items[portIndex][sampleIndex]<br />
output_items[portIndex][sampleIndex]</pre><br />
<br />
The image shows how to visualize indexing streams:<br />
<br />
[[File:IndexingStreams.png|500px]]<br />
<br />
== Indexing Vectors ==<br />
<br />
The input ''input_items'' and output ''output_items'' include an extra dimension when using vectors.<br />
<br />
Vectors add an additional dimension, represented as ''vectorIndex'' below. The ''input_items'' and ''output_items'' are now three-dimensional arrays:<br />
<pre>input_items[portIndex][vectorIndex][sampleIndex]<br />
output_items[portIndex][vectorIndex][sampleIndex]</pre><br />
<br />
Indexing based on the ''portIndex'' returns a two-dimensional array of all vectors and samples, for example:<br />
<br />
<pre>input_items[portIndex]<br />
output_items[portIndex]</pre><br />
<br />
Indexing based on ''portIndex'' and ''vectorIndex'' returns an single-dimensional array of samples, for example:<br />
<br />
<pre>input_items[portIndex][vectorIndex]<br />
input_items[portIndex][vectorIndex]</pre><br />
<br />
Indexing based on ''portIndex'', ''vectorIndex'' and ''sampleIndex'' returns a single sample.<br />
<br />
<pre>input_items[portIndex][vectorIndex][sampleIndex]<br />
input_items[portIndex][vectorIndex][sampleIndex]</pre><br />
<br />
A visual example vector indexing is given below:<br />
<br />
[[File:VectorIndexing.png|700px]]<br />
<br />
== Creating Max Hold Function ==<br />
<br />
The ''work()'' function is modified to include the max hold function. Add a loop over all of the vectors in ''input_items[0]'':<br />
<pre>for vectorIndex in range(len(input_items[0])):</pre><br />
<br />
Calculate the max value of the vector:<br />
<pre>maxValue = np.max(input_items[0][vectorIndex])</pre><br />
<br />
Loop over each of the input samples:<br />
<pre>for sampleIndex in range(len(input_items[0][vectorIndex])):</pre><br />
<br />
Assign each output sample ''maxValue'':<br />
<pre>output_items[0][vectorIndex][sampleIndex] = maxValue</pre><br />
<br />
The code should look like the following:<br />
<br />
[[File:MaxHoldWorkFunction.png|700px]]<br />
<br />
<br />
Save the code (CTRL + S). Run the flowgraph. The output will shows a sinusoid and a sinusoid with a max-hold applied every 16 samples:<br />
<br />
[[File:MaxHoldOutput.png|600px]]<br />
<br />
== Multiple Vector Ports ==<br />
<br />
The ''Max Hold Block'' is modified to add a second vector input and output port.<br />
<br />
Add the following blocks to the workspace:<br />
* Noise Source<br />
* Stream to Vector<br />
* Vector to Stream<br />
* Virtual Sink<br />
* Virtual Source<br />
* QT GUI Time Sink<br />
<br />
Change the following block properties:<br />
* Noise Source, Output Type: float<br />
* Stream to Vector, Num Items: vectorLength<br />
* Vector to Stream, Num Items: vectorLength<br />
* Virtual Sink, Stream Id: noise<br />
* Virtual Source, Stream Id: noise<br />
* QT GUI Time Sink<br />
** Autoscale: Yes<br />
** Number of Inputs: 2<br />
<br />
Connect the blocks:<br />
<br />
[[File:PythonVectorNoiseSource.png|800px]]<br />
<br />
<br />
Edit the code for the ''Max Hold Block''. Add a second vector input and output:<br />
<pre>in_sig=[(np.float32,vectorSize),(np.float32,vectorSize)],<br />
out_sig=[(np.float32,vectorSize),(np.float32,vectorSize)]</pre><br />
<br />
[[File:PythonBlockSecondVector.png|700px]]<br />
<br />
The ''work()'' function is modified to perform the max hold function over both input ports.<br />
<br />
Include an outer loop over all input ports:<br />
<br />
<pre>for portIndex in range(len(input_items)):</pre><br />
<br />
Change all indexing ''[0]'' to ''[portIndex]'':<br />
<br />
<pre>for portIndex in range(len(input_items)):<br />
for vectorIndex in range(len(input_items[portIndex])):<br />
maxValue = np.max(input_items[portIndex][vectorIndex])<br />
for sampleIndex in range(len(input_items[portIndex][vectorIndex])):<br />
output_items[portIndex][vectorIndex][sampleIndex] = maxValue</pre><br />
<br />
The code should now look like the following:<br />
<br />
[[File:PythonVectorFinalCode.png|700px]]<br />
<br />
<br />
Save the code and connect the blocks:<br />
<br />
[[File:PythonVectorFinalFlowgraph.png|800px]]<br />
<br />
<br />
Run the flowgraph. Two max-hold outputs will now be generated, one for the noise source and one for the sinusoid:<br />
<br />
[[File:TwoMaxHoldOutputs.png|700px]]<br />
<br />
<br />
The next tutorial, [[Python_Block_Message_Passing|Python Block Message Passing]] describes how to send and receive messages using the ''Embedded Python Block''.</div>N1aihttps://wiki.gnuradio.org/index.php?title=Creating_Python_OOT_with_gr-modtool&diff=12394Creating Python OOT with gr-modtool2022-07-27T16:26:47Z<p>N1ai: Remove new section, just point to Python_Block_with_Vectors</p>
<hr />
<div>This tutorial describes how to create a custom Python block and use it in a flowgraph with the following steps:<br />
* Create an out-of-tree module using ''gr_modtool''<br />
* Create a new Python block using ''gr_modtool''<br />
* Modify the Python code in a text editor so the block will function<br />
* Modify the YAML file so it can be displayed in GRC<br />
* Install and run the block in a flowgraph<br />
<br />
A tutorial on using the ''Embedded Python Block'' in GRC is on the [[Creating_Your_First_Block|Creating Your First Block]] page. There are also additional tutorials [[Python_Block_Message_Passing|on using tags]], how to do [[Python_Block_Message_Passing|message passing]] and [[Python_Block_with_Vectors|adding vector inputs and outputs]] in Python blocks that will work for both the ''Embedded Python Block'' and OOT Python blocks.<br />
<br />
The next tutorial, [[Creating_c++_OOT_with_gr-modtool|Creating c++ OOT with gr-modtool]], describes how to build a custom c++ OOT module. The c++ OOT tutorial builds upon this Python one, so it is is suggested to at least complete the ''Creating an OOT Module'' portion before moving on.<br />
<br />
== Installation Note ==<br />
<br />
This tutorial was written using GNU Radio v3.10.1.1 on Ubuntu 21.10, installed using the Ubuntu PPA from the [[InstallingGR|Installation Wiki Page]]. The basic GNU Radio install using:<br />
<br />
<pre>$ sudo apt-get install gnuradio</pre><br />
<br />
does not come with the proper libraries needed to compile and install OOT modules. Consider installing the following packages before continuing:<br />
<br />
<pre>$ sudo apt-get install gnuradio-dev cmake libspdlog-dev clang-format</pre><br />
<br />
== Creating an OOT Module ==<br />
<br />
Open a terminal and navigate to an appropriate directory for writing software, such as the home directory:<br />
<pre>$ cd $HOME</pre><br />
<br />
GNU Radio comes packaged with ''gr_modtool'', software used to create out-of-tree (OOT) modules. An OOT module can be thought of as a collection of custom GNU Radio blocks. Create an OOT module named ''customModule'' using ''gr_modtool'':<br />
<br />
<pre>$ gr_modtool newmod customModule</pre><br />
<br />
The directory ''gr-customModule'' is created which contains all of the skeleton code for an OOT module, however it does not yet have any blocks. Move into the ''gr-customModule'' directory:<br />
<pre>$ cd gr-customModule</pre><br />
<br />
List all of the files and directories within the OOT module:<br />
<pre>$ ls</pre><br />
<br />
The directory listing will be as follows:<br />
<br />
<pre>apps/ cmake/ CMakeLists.txt docs/ examples/ grc/ include/ lib/ MANIFEST.md python/</pre><br />
<br />
== Creating an OOT Block ==<br />
<br />
Now a block needs to be created within gr-customModule. The custom block will either add or subtract based on an input parameter, so the block is named ''addSubSelect'':<br />
<br />
<pre>$ gr_modtool add addSubSelect</pre><br />
<br />
The command will start a questionnaire about how to the block is to be defined: what block type, language and parameters:<br />
<br />
<pre>GNU Radio module name identified: customModule<br />
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')</pre><br />
<br />
Select the ''sync'' block, which produces an output for every input:<br />
<pre>Enter block type: sync</pre><br />
<br />
Enter ''python'' as the language:<br />
<pre>Language (python/cpp): python<br />
Language: Python<br />
Block/code identifier: addSubSelect</pre><br />
<br />
Enter the name or organization of the copyright holder:<br />
<pre>Please specify the copyright holder: YourName</pre><br />
<br />
Now enter the argument list:<br />
<pre>Enter valid argument list, including default arguments:</pre><br />
<br />
Enter the argument list as if writing the Python code directly. In this case the ''selector'' will determine whether or not the block performs addition or subtraction. A default argument of ''True'' is given:<br />
<pre>selector=True</pre><br />
<br />
Determine whether or not you want the Python quality assurance (QA) code:<br />
<pre>Add Python QA code? [Y/n] n</pre><br />
<br />
New files will be generated:<br />
<br />
<pre>Adding file 'python/customModule/addSubSelect.py'...<br />
Adding file 'grc/customModule_addSubSelect.block.yml'...<br />
Editing grc/CMakeLists.txt...</pre><br />
<br />
Two new files were created, ''addSubSelect.py'' which defines the operation of the block and ''customModule_addSubSelect.block.yml'' which defines the interface of the block for GNU Radio Companion (GRC). The ''CMakeLists.txt'' file was modified so the two files will be installed when the module is compiled and installed.<br />
<br />
== Modifying the Python .py File ==<br />
<br />
Open the python file with a text editor:<br />
<pre>gedit python/customModule/addSubSelect.py &</pre><br />
<br />
The following code will be listed: <syntaxhighlight lang="python" line><br />
#!/usr/bin/env python<br />
# -*- coding: utf-8 -*-<br />
#<br />
# Copyright 2022 YourName.<br />
#<br />
# SPDX-License-Identifier: GPL-3.0-or-later<br />
#<br />
<br />
<br />
import numpy<br />
from gnuradio import gr<br />
<br />
class addSubSelect(gr.sync_block):<br />
"""<br />
docstring for block addSubSelect<br />
"""<br />
def __init__(self, selector=True):<br />
gr.sync_block.__init__(self,<br />
name="addSubSelect",<br />
in_sig=[<+numpy.float32+>, ],<br />
out_sig=[<+numpy.float32+>, ])<br />
<br />
<br />
def work(self, input_items, output_items):<br />
in0 = input_items[0]<br />
out = output_items[0]<br />
# <+signal processing here+><br />
out[:] = in0<br />
return len(output_items[0])</syntaxhighlight><br />
<br />
Change the ''import'' statement:<br />
<br />
<syntaxhighlight lang="python" line><br />
import numpy as np<br />
</syntaxhighlight><br />
<br />
Both the ''__init__()'' function and ''work()'' function need to be modified. The ''__init__()'' function is modified to define the input type. The ''addSubSelect'' block will accept two complex inputs and produce a complex output, therefore the ''in_sig'' and ''out_sig'' parameters need to be changed. The ''selector'' parameter also needs to be saved as a member variable:<br />
<br />
<syntaxhighlight lang="python" line><br />
def __init__(self, selector=True):<br />
gr.sync_block.__init__(self,<br />
name="addSubSelect",<br />
in_sig=[np.complex64,np.complex64],<br />
out_sig=[np.complex64])<br />
self.selector = selector</syntaxhighlight><br />
<br />
The ''work()'' function is modified to either add or subtract the two inputs based on the ''selector'' parameter:<br />
<br />
<syntaxhighlight lang="python" line><br />
def work(self, input_items, output_items):<br />
in0 = input_items[0]<br />
in1 = input_items[1]<br />
<br />
if (self.selector):<br />
output_items[0][:] = in0 + in1<br />
else:<br />
output_items[0][:] = in0 - in1<br />
<br />
return len(output_items[0])</syntaxhighlight><br />
<br />
Press CTRL + S to save the file.<br />
<br />
== Modifying YAML .yml File ==<br />
<br />
Open the .yml file with a text editor:<br />
<pre>$ gedit grc/customModule_addSubSelect.block.yml &</pre><br />
<br />
The following YAML is displayed: <syntaxhighlight lang="yaml" line><br />
id: customModule_addSubSelect<br />
label: addSubSelect<br />
category: '[customModule]'<br />
<br />
templates:<br />
imports: from gnuradio import customModule<br />
make: customModule.addSubSelect(${selector})<br />
<br />
# Make one 'parameters' list entry for every parameter you want settable from the GUI.<br />
# Keys include:<br />
# * id (makes the value accessible as keyname, e.g. in the make entry)<br />
# * label (label shown in the GUI)<br />
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)<br />
# * default<br />
parameters:<br />
- id: parametername_replace_me<br />
label: FIX ME:<br />
dtype: string<br />
default: You need to fill in your grc/customModule_addSubSelect.block.yaml<br />
#- id: ...<br />
# label: ...<br />
# dtype: ...<br />
<br />
# Make one 'inputs' list entry per input and one 'outputs' list entry per output.<br />
# Keys include:<br />
# * label (an identifier for the GUI)<br />
# * domain (optional - stream or message. Default is stream)<br />
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)<br />
# * vlen (optional - data stream vector length. Default is 1)<br />
# * optional (optional - set to 1 for optional inputs. Default is 0)<br />
inputs:<br />
#- label: ...<br />
# domain: ...<br />
# dtype: ...<br />
# vlen: ...<br />
# optional: ...<br />
<br />
outputs:<br />
#- label: ...<br />
# domain: ...<br />
# dtype: ...<br />
# vlen: ...<br />
# optional: ...<br />
<br />
# 'file_format' specifies the version of the GRC yml format used in the file<br />
# and should usually not be changed.<br />
file_format: 1</syntaxhighlight><br />
<br />
The YAML file needs to be updated to match the ''addSubSelector.py'' file that was just modified. There is a single parameter, ''selector''. Enter the parameter values according to:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
parameters:<br />
- id: selector<br />
label: Add (True) or Subtract (False) Selector<br />
dtype: bool<br />
default: True</syntaxhighlight><br />
<br />
The two inputs need to be defined in the YAML:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
inputs:<br />
- label: in0<br />
domain: stream<br />
dtype: complex<br />
- label: in1<br />
domain: stream<br />
dtype: complex<br />
</syntaxhighlight><br />
<br />
The single output needs to be defined:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
outputs:<br />
- label: out0<br />
domain: stream<br />
dtype: complex<br />
</syntaxhighlight><br />
<br />
Press CTRL + S to save the file.<br />
<br />
== Compiling and Installing the Block ==<br />
<br />
In the top level directory of ''gr_customModule'', create a ''build'' directory:<br />
<pre>$ mkdir build</pre><br />
<br />
The directory should look like the following:<br />
<pre>apps/ build/ cmake/ CMakeLists.txt docs/ examples/ grc/ include/ lib/ MANIFEST.md python/</pre><br />
<br />
Move into the build directory,<br />
<pre>$ cd build</pre><br />
<br />
Then run CMake which will prepare the makefiles:<br />
<pre>cmake ..</pre><br />
<br />
Compile the module:<br />
<pre>$ make</pre><br />
<br />
Install the module with sudo:<br />
<pre>sudo make install</pre><br />
<br />
Multiple files will now be installed:<br />
<br />
<pre>[ 0%] Built target pygen_apps_9a6dd<br />
[ 0%] Built target copy_module_for_tests<br />
[100%] Built target pygen_python_customModule_f524f<br />
Install the project...<br />
-- Install configuration: "Release"<br />
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake<br />
-- Installing: /usr/local/include/gnuradio/customModule/api.h<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.py<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.py<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyc<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyc<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyo<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyo<br />
-- Installing: /usr/local/share/gnuradio/grc/blocks/customModule_addSubSelect.block.yml</pre><br />
<br />
Run ''ldconfig'' to update the linking for the customModule library:<br />
<br />
<pre>$ sudo ldconfig </pre><br />
<br />
== Using the Custom Block in a Flowgraph ==<br />
<br />
Start GNU Radio Companion (GRC):<br />
<pre>$ gnuradio-companion &</pre><br />
<br />
The ''addSubSelect'' block will be available under the ''customModule'' drop down in the block library:<br />
<br />
[[File:AddSubSelectInGRC.png|900px]]<br />
<br />
Drag the block into the workspace:<br />
<br />
[[File:DragInAddSubSelectBlock.png|900px]]<br />
<br />
The block shows the properties as defined in the YAML file:<br />
* Two complex inputs, ''in0'' and ''in1''<br />
* One complex output, ''out0''<br />
* A selector parameter<br />
<br />
Double-click the block to open the properties. The ''selector'' parameter is described by the label ''Add (True) or Subtract (False) Selector'' with ''True'' as the default value:<br />
<br />
[[File:AddSubSelectProperties.png|500px]]<br />
<br />
Click OK.<br />
<br />
Add the following blocks to the workspace:<br />
* Signal Source<br />
* Constant Source<br />
* Throttle<br />
* QT GUI Time Sink<br />
<br />
Update the following parameters<br />
* Signal Source<br />
** Frequency: 100<br />
** Amplitude: 0.5<br />
* Constant Source<br />
** Constant: 0.5+0.5j<br />
<br />
Connect the blocks according to the following flowgraph:<br />
<br />
[[File:ConnectAddSubSelectFlowgraph.png|800px]]<br />
<br />
== Running the Flowgraph ==<br />
<br />
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output, showing the average level of the sinusoid has been raised by ''0.5+0.5j'':<br />
<br />
[[File:AddSubSelectPositiveOffset.png|700px]]<br />
<br />
Edit the properties off the ''addSubSelect'' block and enter False which enables the subtraction mode:<br />
<br />
[[File:AddSubSelectFalse.png|500px]]<br />
<br />
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output, showing the average level of the sinusoid has been reduced by ''0.5+0.5j'':<br />
<br />
[[File:AddSubSelectNegativeOffset.png|700px]]<br />
<br />
== Making Changes ==<br />
<br />
It is suggested to recompile and reinstall the module any time a change is made, followed by reloading the block library in GRC. This includes changes such as:<br />
* Number of parameters<br />
* Type of parameters<br />
* Number of input ports or output ports<br />
* Types of input ports or output ports<br />
* Modifying the YAML .yml file<br />
* Modifying the Python .py file<br />
<br />
Removing and re-creating the ''build/'' directory may be necessary before recompiling and reinstalling the module depending on the scope of the change:<br />
<br />
<pre>$ rm -rf gr-customModule/build<br />
$ mkdir gr-customModule/build</pre><br />
<br />
== Related Tutorial ==<br />
<br />
The tutorial [[Python_Block_with_Vectors]] describes how to work with vectors in a Python block.<br />
<br />
== Next Tutorial ==<br />
<br />
The next tutorial, [[Creating_c++_OOT_with_gr-modtool|Creating c++ OOT with gr-modtool]], describes how to build a custom c++ OOT module.</div>N1aihttps://wiki.gnuradio.org/index.php?title=Creating_Python_OOT_with_gr-modtool&diff=12393Creating Python OOT with gr-modtool2022-07-27T16:05:12Z<p>N1ai: Give an example of how to represent a vector signal rather than stream</p>
<hr />
<div>This tutorial describes how to create a custom Python block and use it in a flowgraph with the following steps:<br />
* Create an out-of-tree module using ''gr_modtool''<br />
* Create a new Python block using ''gr_modtool''<br />
* Modify the Python code in a text editor so the block will function<br />
* Modify the YAML file so it can be displayed in GRC<br />
* Install and run the block in a flowgraph<br />
<br />
A tutorial on using the ''Embedded Python Block'' in GRC is on the [[Creating_Your_First_Block|Creating Your First Block]] page. There are also additional tutorials [[Python_Block_Message_Passing|on using tags]], how to do [[Python_Block_Message_Passing|message passing]] and [[Python_Block_with_Vectors|adding vector inputs and outputs]] in Python blocks that will work for both the ''Embedded Python Block'' and OOT Python blocks.<br />
<br />
The next tutorial, [[Creating_c++_OOT_with_gr-modtool|Creating c++ OOT with gr-modtool]], describes how to build a custom c++ OOT module. The c++ OOT tutorial builds upon this Python one, so it is is suggested to at least complete the ''Creating an OOT Module'' portion before moving on.<br />
<br />
== Installation Note ==<br />
<br />
This tutorial was written using GNU Radio v3.10.1.1 on Ubuntu 21.10, installed using the Ubuntu PPA from the [[InstallingGR|Installation Wiki Page]]. The basic GNU Radio install using:<br />
<br />
<pre>$ sudo apt-get install gnuradio</pre><br />
<br />
does not come with the proper libraries needed to compile and install OOT modules. Consider installing the following packages before continuing:<br />
<br />
<pre>$ sudo apt-get install gnuradio-dev cmake libspdlog-dev clang-format</pre><br />
<br />
== Creating an OOT Module ==<br />
<br />
Open a terminal and navigate to an appropriate directory for writing software, such as the home directory:<br />
<pre>$ cd $HOME</pre><br />
<br />
GNU Radio comes packaged with ''gr_modtool'', software used to create out-of-tree (OOT) modules. An OOT module can be thought of as a collection of custom GNU Radio blocks. Create an OOT module named ''customModule'' using ''gr_modtool'':<br />
<br />
<pre>$ gr_modtool newmod customModule</pre><br />
<br />
The directory ''gr-customModule'' is created which contains all of the skeleton code for an OOT module, however it does not yet have any blocks. Move into the ''gr-customModule'' directory:<br />
<pre>$ cd gr-customModule</pre><br />
<br />
List all of the files and directories within the OOT module:<br />
<pre>$ ls</pre><br />
<br />
The directory listing will be as follows:<br />
<br />
<pre>apps/ cmake/ CMakeLists.txt docs/ examples/ grc/ include/ lib/ MANIFEST.md python/</pre><br />
<br />
== Creating an OOT Block ==<br />
<br />
Now a block needs to be created within gr-customModule. The custom block will either add or subtract based on an input parameter, so the block is named ''addSubSelect'':<br />
<br />
<pre>$ gr_modtool add addSubSelect</pre><br />
<br />
The command will start a questionnaire about how to the block is to be defined: what block type, language and parameters:<br />
<br />
<pre>GNU Radio module name identified: customModule<br />
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')</pre><br />
<br />
Select the ''sync'' block, which produces an output for every input:<br />
<pre>Enter block type: sync</pre><br />
<br />
Enter ''python'' as the language:<br />
<pre>Language (python/cpp): python<br />
Language: Python<br />
Block/code identifier: addSubSelect</pre><br />
<br />
Enter the name or organization of the copyright holder:<br />
<pre>Please specify the copyright holder: YourName</pre><br />
<br />
Now enter the argument list:<br />
<pre>Enter valid argument list, including default arguments:</pre><br />
<br />
Enter the argument list as if writing the Python code directly. In this case the ''selector'' will determine whether or not the block performs addition or subtraction. A default argument of ''True'' is given:<br />
<pre>selector=True</pre><br />
<br />
Determine whether or not you want the Python quality assurance (QA) code:<br />
<pre>Add Python QA code? [Y/n] n</pre><br />
<br />
New files will be generated:<br />
<br />
<pre>Adding file 'python/customModule/addSubSelect.py'...<br />
Adding file 'grc/customModule_addSubSelect.block.yml'...<br />
Editing grc/CMakeLists.txt...</pre><br />
<br />
Two new files were created, ''addSubSelect.py'' which defines the operation of the block and ''customModule_addSubSelect.block.yml'' which defines the interface of the block for GNU Radio Companion (GRC). The ''CMakeLists.txt'' file was modified so the two files will be installed when the module is compiled and installed.<br />
<br />
== Modifying the Python .py File ==<br />
<br />
Open the python file with a text editor:<br />
<pre>gedit python/customModule/addSubSelect.py &</pre><br />
<br />
The following code will be listed: <syntaxhighlight lang="python" line><br />
#!/usr/bin/env python<br />
# -*- coding: utf-8 -*-<br />
#<br />
# Copyright 2022 YourName.<br />
#<br />
# SPDX-License-Identifier: GPL-3.0-or-later<br />
#<br />
<br />
<br />
import numpy<br />
from gnuradio import gr<br />
<br />
class addSubSelect(gr.sync_block):<br />
"""<br />
docstring for block addSubSelect<br />
"""<br />
def __init__(self, selector=True):<br />
gr.sync_block.__init__(self,<br />
name="addSubSelect",<br />
in_sig=[<+numpy.float32+>, ],<br />
out_sig=[<+numpy.float32+>, ])<br />
<br />
<br />
def work(self, input_items, output_items):<br />
in0 = input_items[0]<br />
out = output_items[0]<br />
# <+signal processing here+><br />
out[:] = in0<br />
return len(output_items[0])</syntaxhighlight><br />
<br />
Change the ''import'' statement:<br />
<br />
<syntaxhighlight lang="python" line><br />
import numpy as np<br />
</syntaxhighlight><br />
<br />
Both the ''__init__()'' function and ''work()'' function need to be modified. The ''__init__()'' function is modified to define the input type. The ''addSubSelect'' block will accept two complex inputs and produce a complex output, therefore the ''in_sig'' and ''out_sig'' parameters need to be changed. The ''selector'' parameter also needs to be saved as a member variable:<br />
<br />
<syntaxhighlight lang="python" line><br />
def __init__(self, selector=True):<br />
gr.sync_block.__init__(self,<br />
name="addSubSelect",<br />
in_sig=[np.complex64,np.complex64],<br />
out_sig=[np.complex64])<br />
self.selector = selector</syntaxhighlight><br />
<br />
The ''work()'' function is modified to either add or subtract the two inputs based on the ''selector'' parameter:<br />
<br />
<syntaxhighlight lang="python" line><br />
def work(self, input_items, output_items):<br />
in0 = input_items[0]<br />
in1 = input_items[1]<br />
<br />
if (self.selector):<br />
output_items[0][:] = in0 + in1<br />
else:<br />
output_items[0][:] = in0 - in1<br />
<br />
return len(output_items[0])</syntaxhighlight><br />
<br />
Press CTRL + S to save the file.<br />
<br />
== Modifying YAML .yml File ==<br />
<br />
Open the .yml file with a text editor:<br />
<pre>$ gedit grc/customModule_addSubSelect.block.yml &</pre><br />
<br />
The following YAML is displayed: <syntaxhighlight lang="yaml" line><br />
id: customModule_addSubSelect<br />
label: addSubSelect<br />
category: '[customModule]'<br />
<br />
templates:<br />
imports: from gnuradio import customModule<br />
make: customModule.addSubSelect(${selector})<br />
<br />
# Make one 'parameters' list entry for every parameter you want settable from the GUI.<br />
# Keys include:<br />
# * id (makes the value accessible as keyname, e.g. in the make entry)<br />
# * label (label shown in the GUI)<br />
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)<br />
# * default<br />
parameters:<br />
- id: parametername_replace_me<br />
label: FIX ME:<br />
dtype: string<br />
default: You need to fill in your grc/customModule_addSubSelect.block.yaml<br />
#- id: ...<br />
# label: ...<br />
# dtype: ...<br />
<br />
# Make one 'inputs' list entry per input and one 'outputs' list entry per output.<br />
# Keys include:<br />
# * label (an identifier for the GUI)<br />
# * domain (optional - stream or message. Default is stream)<br />
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)<br />
# * vlen (optional - data stream vector length. Default is 1)<br />
# * optional (optional - set to 1 for optional inputs. Default is 0)<br />
inputs:<br />
#- label: ...<br />
# domain: ...<br />
# dtype: ...<br />
# vlen: ...<br />
# optional: ...<br />
<br />
outputs:<br />
#- label: ...<br />
# domain: ...<br />
# dtype: ...<br />
# vlen: ...<br />
# optional: ...<br />
<br />
# 'file_format' specifies the version of the GRC yml format used in the file<br />
# and should usually not be changed.<br />
file_format: 1</syntaxhighlight><br />
<br />
The YAML file needs to be updated to match the ''addSubSelector.py'' file that was just modified. There is a single parameter, ''selector''. Enter the parameter values according to:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
parameters:<br />
- id: selector<br />
label: Add (True) or Subtract (False) Selector<br />
dtype: bool<br />
default: True</syntaxhighlight><br />
<br />
The two inputs need to be defined in the YAML:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
inputs:<br />
- label: in0<br />
domain: stream<br />
dtype: complex<br />
- label: in1<br />
domain: stream<br />
dtype: complex<br />
</syntaxhighlight><br />
<br />
The single output needs to be defined:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
outputs:<br />
- label: out0<br />
domain: stream<br />
dtype: complex<br />
</syntaxhighlight><br />
<br />
Press CTRL + S to save the file.<br />
<br />
== Compiling and Installing the Block ==<br />
<br />
In the top level directory of ''gr_customModule'', create a ''build'' directory:<br />
<pre>$ mkdir build</pre><br />
<br />
The directory should look like the following:<br />
<pre>apps/ build/ cmake/ CMakeLists.txt docs/ examples/ grc/ include/ lib/ MANIFEST.md python/</pre><br />
<br />
Move into the build directory,<br />
<pre>$ cd build</pre><br />
<br />
Then run CMake which will prepare the makefiles:<br />
<pre>cmake ..</pre><br />
<br />
Compile the module:<br />
<pre>$ make</pre><br />
<br />
Install the module with sudo:<br />
<pre>sudo make install</pre><br />
<br />
Multiple files will now be installed:<br />
<br />
<pre>[ 0%] Built target pygen_apps_9a6dd<br />
[ 0%] Built target copy_module_for_tests<br />
[100%] Built target pygen_python_customModule_f524f<br />
Install the project...<br />
-- Install configuration: "Release"<br />
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake<br />
-- Installing: /usr/local/include/gnuradio/customModule/api.h<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.py<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.py<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyc<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyc<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyo<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyo<br />
-- Installing: /usr/local/share/gnuradio/grc/blocks/customModule_addSubSelect.block.yml</pre><br />
<br />
Run ''ldconfig'' to update the linking for the customModule library:<br />
<br />
<pre>$ sudo ldconfig </pre><br />
<br />
== Using the Custom Block in a Flowgraph ==<br />
<br />
Start GNU Radio Companion (GRC):<br />
<pre>$ gnuradio-companion &</pre><br />
<br />
The ''addSubSelect'' block will be available under the ''customModule'' drop down in the block library:<br />
<br />
[[File:AddSubSelectInGRC.png|900px]]<br />
<br />
Drag the block into the workspace:<br />
<br />
[[File:DragInAddSubSelectBlock.png|900px]]<br />
<br />
The block shows the properties as defined in the YAML file:<br />
* Two complex inputs, ''in0'' and ''in1''<br />
* One complex output, ''out0''<br />
* A selector parameter<br />
<br />
Double-click the block to open the properties. The ''selector'' parameter is described by the label ''Add (True) or Subtract (False) Selector'' with ''True'' as the default value:<br />
<br />
[[File:AddSubSelectProperties.png|500px]]<br />
<br />
Click OK.<br />
<br />
Add the following blocks to the workspace:<br />
* Signal Source<br />
* Constant Source<br />
* Throttle<br />
* QT GUI Time Sink<br />
<br />
Update the following parameters<br />
* Signal Source<br />
** Frequency: 100<br />
** Amplitude: 0.5<br />
* Constant Source<br />
** Constant: 0.5+0.5j<br />
<br />
Connect the blocks according to the following flowgraph:<br />
<br />
[[File:ConnectAddSubSelectFlowgraph.png|800px]]<br />
<br />
== Running the Flowgraph ==<br />
<br />
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output, showing the average level of the sinusoid has been raised by ''0.5+0.5j'':<br />
<br />
[[File:AddSubSelectPositiveOffset.png|700px]]<br />
<br />
Edit the properties off the ''addSubSelect'' block and enter False which enables the subtraction mode:<br />
<br />
[[File:AddSubSelectFalse.png|500px]]<br />
<br />
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output, showing the average level of the sinusoid has been reduced by ''0.5+0.5j'':<br />
<br />
[[File:AddSubSelectNegativeOffset.png|700px]]<br />
<br />
== Making Changes ==<br />
<br />
It is suggested to recompile and reinstall the module any time a change is made, followed by reloading the block library in GRC. This includes changes such as:<br />
* Number of parameters<br />
* Type of parameters<br />
* Number of input ports or output ports<br />
* Types of input ports or output ports<br />
* Modifying the YAML .yml file<br />
* Modifying the Python .py file<br />
<br />
Removing and re-creating the ''build/'' directory may be necessary before recompiling and reinstalling the module depending on the scope of the change:<br />
<br />
<pre>$ rm -rf gr-customModule/build<br />
$ mkdir gr-customModule/build</pre><br />
<br />
== A Note on Streams Versus Vectors ==<br />
<br />
Above, we declared the block's input and output signals as:<br />
<br />
<syntaxhighlight lang="python" line><br />
def __init__(self, selector=True):<br />
gr.sync_block.__init__(self,<br />
name="addSubSelect",<br />
in_sig=[np.complex64,np.complex64],<br />
out_sig=[np.complex64])<br />
self.selector = selector</syntaxhighlight><br />
<br />
The [[Streams_and_Vectors]] tutorial described the difference between streams and vectors.<br />
<br />
In this case, there are two input streams of type <code>np.complex64</code> and one output stream of type <code>np.complex64</code><br />
<br />
What if we wanted to work with vectors rather than streams?<br />
<br />
From a higher level point of view, ''Streams'' are typically just a special case of ''Vectors'', ones that have just one data element in parallel instead of multiple. Since the case of ''Streams'' is so common, it has its own simple syntax, whereas the syntax for ''Vectors'' is just a bit more complex. In particular, when using vectors, we must specify the vector's length.<br />
<br />
So, for example, suppose the two input signals we wanted to use were vectors of seven floating point values and five complex values? We would have to change the <code>in_sig</code> parameter above to be <code>in_sig=[(np.float32, 7), (np.complex64, 5)]</code><br />
<br />
== Next Tutorial ==<br />
<br />
The next tutorial, [[Creating_c++_OOT_with_gr-modtool|Creating c++ OOT with gr-modtool]], describes how to build a custom c++ OOT module.</div>N1aihttps://wiki.gnuradio.org/index.php?title=Variables_in_Flowgraphs&diff=12392Variables in Flowgraphs2022-07-27T15:21:52Z<p>N1ai: Try to clarify the role of GRC in creating Python code since most people use GRC instead of writing Python flowgraphs from scratch</p>
<hr />
<div><div style="float:right"><br />
{{Template:BeginnerTutorials}}<br />
</div><br />
This tutorial describes how to use variables in a flowgraph. The flowgraph from a previous tutorial ([[Your First Flowgraph]]) is used as a starting point for this tutorial. Please complete the [[Your First Flowgraph]] tutorial beforehand.<br />
<br />
The previous tutorial, [[Python Variables in GRC]], describes how GRC uses Python data types and how values are displayed in ''Variable'' blocks. The next tutorial, [[Runtime Updating Variables]], demonstrates how variables are updated while a flowgraph is running.<br />
<br />
==Basic Variables==<br />
<br />
GRC allows a user to interact with GNURadio flowgraphs, either ones they create from scratch interactively, or ones that are read from a ''.grc'' file.<br />
<br />
When the GRC user uses the ''play'' button to execute a flowgraph, GRC creates a ''.py'' Python file that contains the flowgraph's code. <br />
<br />
Python code can have variables, and a GNURadio flowgraph can have variables created by the ''Variable'' block.<br />
<br />
Every new flowgraph starts with the ''samp_rate'' variable:<br />
<br />
[[File:VariableSampRate.png|600px]]<br />
<br />
<br />
GNURadio blocks are implemented as functions. GNU Radio blocks take parameters which modify the behavior. All of the blocks in the flowgraph above use ''samp_rate'' as a parameter. Create a new variable block by dragging and dropping it from the block library on the right:<br />
<br />
[[File:NewVariableBlock.png|800px]]<br />
<br />
<br />
Double-click the ''variable_0'' block to view and modify the parameters.<br />
<br />
[[File:VariableProperties.png|500px]]<br />
<br />
<br />
The ''Id'' field is the name of the variable. The variable will be the frequency of the ''Signal Source'' block. Edit the name to ''frequency''. Now edit the value to ''4000''.<br />
<br />
[[File:FrequencyVariable.png|500px]]<br />
<br />
Click ''OK'' to save.<br />
<br />
Double-click the ''Signal Source'' block to modify the parameters:<br />
<br />
[[File:SignalSourceProperties.png|500px]]<br />
<br />
<br />
The ''Frequency'' is set to ''1000''. Enter ''frequency'' into the Frequency field to use the variable:<br />
<br />
[[File:SignalSourceFrequency.png|500px]]<br />
<br />
<br />
Click ''OK'' to save the properties. The ''frequency'' variable and the value within the ''Signal Source'' block are updated:<br />
<br />
[[File:FlowgraphWithFrequencyVariable.png|600px]]<br />
<br />
<br />
Run the flowgraph:<br />
<br />
[[File:FlowgraphNewFrequencyOutput.png|800px]]<br />
<br />
<br />
The peak of the frequency response has moved to ''4,000'' due to the variable change.<br />
<br />
==Dependent Variables==<br />
<br />
Variables can be dependent on one another. The ''Id'' and ''Value'' fields are converted into a line of Python in the following manner:<br />
<br />
<pre>Id = Value</pre><br />
<br />
The ''frequency'' variable was modified to accept the value ''4000'', which is the same as a line of Python code:<br />
<br />
<pre>frequency = 4000</pre><br />
<br />
The ''frequency'' variable can also be dependent on another variable. Edit ''frequency'' to enter the value ''samp_rate/3'', which for ''samp_rate = 32000'' will be a frequency of ''10,667''.<br />
<br />
[[File:ExampleDependentVariable.png|500px]]<br />
<br />
<br />
The change is displayed in the flowgraph:<br />
<br />
[[File:UpdatedFrequencyFlowgraph.png|600px]]<br />
<br />
<br />
Running the flowgraph shows the frequency is updated:<br />
<br />
[[File:FrequencySinkUpdatedFrequency.png|800px]]<br />
<br />
<br />
The next tutorial, [[Runtime_Updating_Variables|Runtime Updating Variables]], demonstrates how variables are updated while a flowgraph is running.</div>N1aihttps://wiki.gnuradio.org/index.php?title=Embedded_Python_Block&diff=12391Embedded Python Block2022-07-27T15:06:10Z<p>N1ai: Explain difference between embedded block and OOT, point to updated pages</p>
<hr />
<div>[[Category:Block Docs]]<br />
<!-- Embedded_Python_Block.mediawiki --><br />
This block allows you to create a new (custom) block in Python without needing to make and install an Out of Tree (OOT) Module. When you add the block to your flowgraph, the pre-populated code simply takes the input stream and multiplies it by a constant. Note that the structure of this Python block matches the structure of an OOT Python block. It's essentially a Python OOT block built into a grc flowgraph. <br />
<br />
== Initial code in the Python block ==<br />
Here is the pre-populated code:<br />
<syntaxhighlight lang="python"><br />
"""<br />
Embedded Python Blocks:<br />
<br />
Each time this file is saved, GRC will instantiate the first class it finds<br />
to get ports and parameters of your block. The arguments to __init__ will<br />
be the parameters. All of them are required to have default values!<br />
"""<br />
<br />
import numpy as np<br />
from gnuradio import gr<br />
<br />
<br />
class blk(gr.sync_block): # other base classes are basic_block, decim_block, interp_block<br />
"""Embedded Python Block example - a simple multiply const"""<br />
<br />
def __init__(self, example_param=1.0): # only default arguments here<br />
"""arguments to this function show up as parameters in GRC"""<br />
gr.sync_block.__init__(<br />
self,<br />
name='Embedded Python Block', # will show up in GRC<br />
in_sig=[np.complex64],<br />
out_sig=[np.complex64]<br />
)<br />
# if an attribute with the same name as a parameter is found,<br />
# a callback is registered (properties work, too).<br />
self.example_param = example_param<br />
<br />
def work(self, input_items, output_items):<br />
"""example: multiply with constant"""<br />
output_items[0][:] = input_items[0] * self.example_param<br />
return len(output_items[0])<br />
</syntaxhighlight><br />
<br />
== User notes ==<br />
<br />
* To revise the Python code:<br />
** right click on the Embedded Python block<br />
** select <q>Properties</q><br />
** select <q>Open in Editor</q><br />
** select <q>Use Default</q> or <q>Choose Editor</q><br />
** For some platforms the <q>Use Default</q> button may do nothing; see below.<br />
** if you select <q>Choose Editor</q>, the /usr/bin folder will open for you to pick one.<br />
** When you have finished editing, save the file (usually Ctrl-S).<br />
** close the edit window.<br />
** If there are any syntax errors, they are shown in the <q>Properties</q> window.<br />
** select <q>OK</q>. It is not necessary to select <q>Apply</q>.<br />
* to change the default editor (works for Ubuntu and Debian):<br />
** open File Manager<br />
** single right click a file of type .py<br />
** left click on 'Properties'<br />
** click on the pull down for 'Open with:'<br />
** click on the editor of your choice<br />
** click OK<br />
<p><b>Important:</b> For version 3.8, it is recommended that any flowgraph using an Embedded Python Block be placed in a separate directory (folder); particularly so if more than one Embedded Python Block is used in the same directory. This is because the generation process creates a file <q>epy_block_0.py</q> from that flowgraph. If you generate a different flowgraph in the same directory, its Embedded Python Block will overwrite the file. This problem has been resolved with a new naming convention in 3.9 which prepends the flowgraph name. For example "pkt_31_epy_block_0.py"</p><br />
<p><b>Warning:</b> To make changes to the Python code, you <u>must</u> follow the steps above. If you edit the <q>epy_block_0.py</q> file directly and then generate the flowgraph again, your changes will be wiped out! (The most recent edited code is actually stored in the .grc file.)</p><br />
<br />
== Parameters ==<br />
The parameters for this block are simply those of your embedded Python block itself. <br />
<br />
Note that every parameter needs a default value. For example, in the code above, note line: <syntaxhighlight lang="python">def __init__(self, example_param=1.0): # only default arguments here</syntaxhighlight> If you don't have any parameters, change that line of code to <syntaxhighlight lang="python">def __init__(self):</syntaxhighlight><br />
<br />
If you have a vector length equal to a variable of your block, note that the default value must be "hardcoded".<br />
<br />
== Example Flowgraph ==<br />
<br />
This flowgraph shows how an Embedded Python Block can be used to convert a string from a Message Edit Box to a byte string for the output port. The text is sent through a Throttle to a File Sink. The file sink should have Unbuffered = On. In this particular case, the Throttle block is not required because all blocks will wait for a message string to be entered into the Message Edit Box. In this way the Message Edit Box limits the number of samples produced. Text output is in the black screen at the bottom of the image.<br />
<br />
[[File:Epy_demo.png|800px]]<br />
<br />
This is the epy_block_0.py code:<br />
<syntaxhighlight lang="python"><br />
"""<br />
Embedded Python Block demo<br />
"""<br />
<br />
# epy_block_0.py<br />
# created 10/17/2019<br />
<br />
import numpy as np<br />
from gnuradio import gr<br />
<br />
import pmt<br />
<br />
textboxValue = ""<br />
<br />
class my_sync_block(gr.sync_block):<br />
"""<br />
reads input from a message port<br />
outputs text<br />
"""<br />
def __init__(self):<br />
gr.sync_block.__init__(self,<br />
name = "Embedded Python demo",<br />
in_sig = None,<br />
out_sig = [np.byte])<br />
self.message_port_register_in(pmt.intern('msg_in'))<br />
self.message_port_register_out(pmt.intern('clear_input'))<br />
self.set_msg_handler(pmt.intern('msg_in'), self.handle_msg)<br />
<br />
def handle_msg(self, msg):<br />
global textboxValue<br />
<br />
textboxValue = pmt.symbol_to_string (msg)<br />
# print (textboxValue)<br />
<br />
def work(self, input_items, output_items):<br />
global textboxValue<br />
<br />
# get length of string<br />
_len = len(textboxValue)<br />
if (_len > 0):<br />
# terminate with LF<br />
textboxValue += "\n"<br />
_len += 1<br />
# store elements in output array<br />
for x in range(_len):<br />
output_items[0][x] = ord(textboxValue[x])<br />
textboxValue = ""<br />
self.message_port_pub(pmt.intern('clear_input'), pmt.intern(''))<br />
return (_len)<br />
else:<br />
return (0)<br />
</syntaxhighlight><br />
<br />
Another example can be found in https://github.com/duggabe/gr-morse-code-gen<br />
<br />
== Comparison between Embedded Python Block and OOT Python Module ==<br />
<br />
Both the Embedded Python Block and OOT Python Module allow you to use Python code from a GRC flowgraph. The differences between the two are mostly about sharing and maintenance.<br />
<br />
The embedded block is just as the name suggests, a copy of Python code that is embedded into flowgraphs. If you want to use a given block in two flowgraphs, you have to make two copies of that block. Like most pieces of software, once you have two copies they tend to diverge over time. Putting a fix in one doesn't fix the other. <br />
<br />
Using a OOT Python Module to hold your Python code allows there to be one central copy that can be shared between flowgraphs that can be updated independently of the flowgraphs that use it. The cost of these sharing and maintenance benefits is the time needed to set up the OOT-related files that tell GRC how to use the OOT.<br />
<br />
In many cases the Embedded Python Block is used for smaller, simpler pieces of code, but once a larger, more complex code is developed it often makes sense to package that code in a OOT Python Module.<br />
<br />
== Types of Python blocks ==<br />
<br />
The pre-populated code shows the use of a <code>gr.sync_block</code> producing one data item of output for each data item of input (synchronous). Other classes are ''decim_block'' for decimation, ''interp''_block for interpolation, and the generic ''basic_block'' which has no fixed relation between the number of input items consumed and the number of output items produced.<br />
<br />
More information on creating your own blocks can be found on the following pages: <br />
* [[Types_of_Blocks]] <br />
* [[BlocksCodingGuide]]<br />
* [[Creating_Python_OOT_with_gr-modtool]]<br />
* [[Creating_c%2B%2B_OOT_with_gr-modtool]]</div>N1aihttps://wiki.gnuradio.org/index.php?title=File_Source&diff=12383File Source2022-07-24T23:01:07Z<p>N1ai: Improve the description of the "Add Begin Tag" parameter.</p>
<hr />
<div>[[Category:Block Docs]]<br />
Reads raw data values in binary format from the specified file. This file can be a file that was captured using a [[File Sink]] block, generated with a computer program or saved from an audio editor such as Audacity (using suitable RAW format options). <br />
<br />
== Parameters ==<br />
(''R''): <span class="plainlinks">[https://wiki.gnuradio.org/index.php/GNURadioCompanion#Variable_Controls ''Run-time adjustable'']</span><br />
<br />
; File (''R'')<br />
: Filename of binary file. <b>Note:</b> The file MUST be on the local computer. <br />
The file size must not be less than 8 bytes, otherwise it gives the following error: "RuntimeError: file is too small"<br />
<br />
; Output Type<br />
: Because a binary file does not store information about what type of data is in it, we have to tell GNU Radio the format. <br />
<br />
; Repeat (''R'')<br />
: Whether or not to repeat the signal, once the end of the file is reached<br />
<br />
; Add Begin Tag (''R'')<br />
: Key of a tag to add to the first sample of the stream, and if Repeat is true, the first sample of each repeated copy. The provided expression must create a PMT. For example, <code>pmt.intern("example_key")</code> will create a key that represents the string <code>"example_key"</code>. See [[Polymorphic_Types_(PMTs)]] and [[Stream_Tags]] for more information.<br />
<br />
; Offset<br />
: Start offset samples/items after the beginning of the file<br />
<br />
; Length<br />
: Read only this number of items beginning from offset(offset+len). 0 will read until the end of the file.<br />
<br />
== Example Flowgraph ==<br />
<br />
This flowgraph can be found at [https://github.com/gnuradio/gnuradio/blob/master/gr-dtv/examples/dvbt_rx_8k.grc].<br />
<br />
[[File:Dvbt_rx_8k_fg.png|800px]]<br />
<br />
== Source Files ==<br />
<br />
; C++ files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/file_source_impl.cc]<br />
<br />
; Header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/file_source_impl.h]<br />
<br />
; Public header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/include/gnuradio/blocks/file_source.h]<br />
<br />
; Block definition<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/grc/blocks_file_source.block.yml]</div>N1aihttps://wiki.gnuradio.org/index.php?title=Patterned_Interleaver&diff=12382Patterned Interleaver2022-07-24T17:46:42Z<p>N1ai: Edit: typo.</p>
<hr />
<div>[[Category:Block Docs]]<br />
Interleave items based on the provided vector.<br />
<br />
== Parameters ==<br />
<br />
; Pattern <br />
: Vector that represents the interleaving pattern. <br />
: For example, the pattern <code>[ 0, 0, 1, 2 ]</code> means to pass one item of data from port 0 twice, then from port 1 once, then from port 2 once, then repeat.<br />
<br />
== Example Flowgraph ==<br />
<br />
Insert description of flowgraph here, then show a screenshot of the flowgraph and the output if there is an interesting GUI. Currently we have no standard method of uploading the actual flowgraph to the wiki or git repo, unfortunately. The plan is to have an example flowgraph showing how the block might be used, for every block, and the flowgraphs will live in the git repo.<br />
<br />
== Source Files ==<br />
<br />
; C++ files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/patterned_interleaver_impl.cc]<br />
<br />
; Header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/patterned_interleaver_impl.h]<br />
<br />
; Public header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/include/gnuradio/blocks/patterned_interleaver.h]<br />
<br />
; Block definition<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/grc/blocks_patterned_interleaver.block.yml]</div>N1aihttps://wiki.gnuradio.org/index.php?title=Patterned_Interleaver&diff=12381Patterned Interleaver2022-07-24T17:46:20Z<p>N1ai: Updated: "sample" -> "item" to address the case of vector input.</p>
<hr />
<div>[[Category:Block Docs]]<br />
Interleave items based on the provided vector.<br />
<br />
== Parameters ==<br />
<br />
; Pattern <br />
: Vector that represents the interleaving pattern. <br />
: For example, the pattern <code>[ 0, 0, 1, 2 ]</code> means to pass one itemp of data from port 0 twice, then from port 1 once, then from port 2 once, then repeat.<br />
<br />
== Example Flowgraph ==<br />
<br />
Insert description of flowgraph here, then show a screenshot of the flowgraph and the output if there is an interesting GUI. Currently we have no standard method of uploading the actual flowgraph to the wiki or git repo, unfortunately. The plan is to have an example flowgraph showing how the block might be used, for every block, and the flowgraphs will live in the git repo.<br />
<br />
== Source Files ==<br />
<br />
; C++ files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/patterned_interleaver_impl.cc]<br />
<br />
; Header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/patterned_interleaver_impl.h]<br />
<br />
; Public header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/include/gnuradio/blocks/patterned_interleaver.h]<br />
<br />
; Block definition<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/grc/blocks_patterned_interleaver.block.yml]</div>N1aihttps://wiki.gnuradio.org/index.php?title=Patterned_Interleaver&diff=12380Patterned Interleaver2022-07-24T17:41:18Z<p>N1ai: Improve the description of the 'pattern' parameter</p>
<hr />
<div>[[Category:Block Docs]]<br />
Interleave items based on the provided vector.<br />
<br />
== Parameters ==<br />
<br />
; Pattern <br />
: Vector that represents the interleaving pattern. <br />
: For example, the pattern <code>[ 0, 0, 1, 2 ]</code> means to pass one sample of data from port 0 twice, then from port 1 once, then from port 2 once, then repeat.<br />
<br />
== Example Flowgraph ==<br />
<br />
Insert description of flowgraph here, then show a screenshot of the flowgraph and the output if there is an interesting GUI. Currently we have no standard method of uploading the actual flowgraph to the wiki or git repo, unfortunately. The plan is to have an example flowgraph showing how the block might be used, for every block, and the flowgraphs will live in the git repo.<br />
<br />
== Source Files ==<br />
<br />
; C++ files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/patterned_interleaver_impl.cc]<br />
<br />
; Header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/patterned_interleaver_impl.h]<br />
<br />
; Public header files<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/include/gnuradio/blocks/patterned_interleaver.h]<br />
<br />
; Block definition<br />
: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/grc/blocks_patterned_interleaver.block.yml]</div>N1aihttps://wiki.gnuradio.org/index.php?title=QT_GUI_Eye_Sink&diff=12377QT GUI Eye Sink2022-07-19T22:23:51Z<p>N1ai: Improve the description of the samples per symbol value.</p>
<hr />
<div>[[Category:Block Docs]]<br />
A graphical sink to display eye diagrams.<br><br />
<code>Added in 3.9</code><br />
<br />
This is a QT-based graphical sink which takes a set of complex streams and plots them as an eye pattern. For each signal, both the signal's I and Q eye patterns are plotted. Eye patterns are 2 symbol periods long. <br />
<br />
The symbol rate of the signal being provided as input to the eye sink must be an integer multiple of the eye sink's sample rate to obtain the eye pattern. This integer multiple is the samples per symbol value that is provided to the eye sink block.<br />
<br />
The set_title and set_color functions can be used to change the label and color for a given input number.<br />
<br />
Trigger occurs at the beginning of each stream used to plot the eye pattern; whilst a real eye diagram would be triggered with a (recovered) symbol clock. For these reasons, triggering of noisy and/or unsynchronized signals may lead to incorrect eye pattern.<br />
<br />
The sink supports plotting streaming complex data or messages. The message port is named "in". The two modes cannot be used simultaneously, and nconnections should be set to 0 when using the message mode. GRC handles this issue by providing the "Complex Message" type that removes the streaming port(s).<br />
<br />
This sink can plot messages that contain either uniform vectors of complex 32 values (pmt::is_c32vector) or PDUs where the data is a uniform vector of complex 32 values.<br />
<br />
See [[GUI Hint]] for info about how to organize multiple QT GUIs <br />
<br />
=== Triggering Info ===<br />
<br />
Set up a trigger for the sink to know when to start<br />
plotting. Useful to isolate events and avoid noise.<br />
<br />
The trigger modes are Free, Auto, Normal, and Tag (see<br />
gr::qtgui::trigger_mode). The first three are like a normal<br />
oscope trigger function. Free means free running with no<br />
trigger, auto will trigger if the trigger event is seen, but<br />
will still plot otherwise, and normal will hold until the<br />
trigger event is observed. The Tag trigger mode allows us to<br />
trigger off a specific stream tag. The tag trigger is based<br />
only on the name of the tag, so when a tag of the given name<br />
is seen, the trigger is activated.<br />
<br />
In auto and normal mode, we look for the slope of the of the<br />
signal. Given a gr::qtgui::trigger_slope as either Positive<br />
or Negative, if the value between two samples moves in the<br />
given direction (x[1] > x[0] for Positive or x[1] < x[0] for<br />
Negative), then the trigger is activated.<br />
<br />
With the complex eye sink, each input has two eye patterns<br />
drawn for the real and imaginary parts of the signal. When<br />
selecting the \p channel value, channel 0 is the real signal<br />
and channel 1 is the imaginary signal. For more than 1 input<br />
stream, channel 2i is the real part of the ith input and<br />
channel (2i+1) is the imaginary part of the ith input<br />
channel.<br />
<br />
The \p delay value is specified in time based off the sample<br />
rate. If the sample rate of the block is set to 1, the delay<br />
is then also the sample number offset. This is the offset<br />
from the left-hand y-axis of the plot. It delays the signal<br />
to show the trigger event at the given delay along with some<br />
portion of the signal before the event. The delay must be<br />
within 0 - t_max where t_max is the maximum amount of time<br />
displayed on the eye pattern equal to 2 symbol time.<br />
<br />
== Parameters ==<br />
; samp_rate<br />
: sample rate (used to set x-axis labels)<br />
<br />
; name<br />
: title for the plot<br />
<br />
; nconnections<br />
: number of signals connected to sink<br />
<br />
; parent<br />
: a QWidget parent object, if any<br />
<br />
; trigger_mode<br />
: free, auto, normal, or tag.<br />
<br />
; slope<br />
: The trigger_slope: positive or negative. Only used for auto and normal modes.<br />
<br />
; level<br />
: The magnitude of the trigger even for auto or normal modes.<br />
<br />
; delay<br />
: The delay (in units of time) for where the trigger happens.<br />
<br />
; channel<br />
: Which input channel to use for the trigger events.<br />
<br />
; tag_key<br />
: The name (as a string) of the tag to trigger off of if using the tag mode.<br />
<br />
== Example Flowgraph ==<br />
<br />
The flowgraph for this plot can be found in [https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/examples/qtgui_eye_sink_example.grc]<br />
<br />
[[File:eye.png|727px]]</div>N1aihttps://wiki.gnuradio.org/index.php?title=Creating_C%2B%2B_OOT_with_gr-modtool&diff=12376Creating C++ OOT with gr-modtool2022-07-19T21:33:55Z<p>N1ai: Improve text related to block type, arguments, and YAML files.</p>
<hr />
<div>This tutorial describes how to create a custom c++ block and use it in a flowgraph:<br />
* Create a new c++ block using ''gr_modtool''<br />
* Modify the c++ .h and .cc code so the block will function<br />
* Modify the YAML file so it can be read in GRC<br />
* Install and run the block in a flowgraph<br />
<br />
The previous tutorial, [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]], describes how to create a Python block in an OOT module. This c++ OOT tutorial builds upon the previous Python one, so it is is suggested to at least complete the ''Creating an OOT Module'' portion of that tutorial before completing this one.<br />
<br />
== Installation Note ==<br />
<br />
This tutorial was written using GNU Radio v3.10.1.1 on Ubuntu 21.10, installed using the Ubuntu PPA from the [[InstallingGR|Installation Wiki Page]]. The basic GNU Radio install using:<br />
<br />
<pre>$ sudo apt-get install gnuradio</pre><br />
<br />
does not come with the proper libraries needed to compile and install OOT modules. Consider installing the following packages before continuing:<br />
<br />
<pre>$ sudo apt-get install gnuradio-dev cmake libspdlog-dev clang-format</pre><br />
<br />
== Creating an OOT Block ==<br />
<br />
Move to the ''gr-customModule'' directory created in the [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]] tutorial:<br />
<pre>cd your-path/gr-customModule</pre><br />
<br />
Add a new block named ''multDivSelect'':<br />
<pre>$ gr_modtool add multDivSelect</pre><br />
<br />
The types of blocks will be displayed:<br />
<br />
<pre>GNU Radio module name identified: customModule<br />
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')</pre><br />
<br />
Enter ''sync'' as the block type, because the block we're making will produce the same number of output items on the output port for each item it consumes from the input port. See [[Types of Blocks]] for more info on different block types that are available. <br />
<pre>Enter block type: sync</pre><br />
<br />
Enter ''cpp'' as the language:<br />
<pre>Language (python/cpp): cpp<br />
Language: C++<br />
Block/code identifier: multDivSelect</pre><br />
<br />
Enter the name or organization of the copyright holder:<br />
<pre>Please specify the copyright holder: YourName</pre><br />
<br />
Our OOT block allows the ''gnuradio-companion'' user to specify a selector value, which at the C++ level is a boolean (true/false) value. To enable this, we need to enter the C++ expression shown below that declares the ''selector'' variable with a default value of ''true'' as an argument to our OOT block:<br />
<br />
<pre>Enter valid argument list, including default arguments: <br />
bool selector=true</pre><br />
<br />
Select whether or not QA code is desired:<br />
<pre>Add Python QA code? [Y/n] n<br />
Add C++ QA code? [Y/n] n</pre><br />
<br />
Multiple files will then be created or modified:<br />
<pre>Adding file 'lib/multDivSelect_impl.h'...<br />
Adding file 'lib/multDivSelect_impl.cc'...<br />
Adding file 'include/gnuradio/customModule/multDivSelect.h'...<br />
Adding file 'python/customModule/bindings/docstrings/multDivSelect_pydoc_template.h'...<br />
Adding file 'python/customModule/bindings/multDivSelect_python.cc'...<br />
Adding file 'grc/customModule_multDivSelect.block.yml'...<br />
Editing grc/CMakeLists.txt...</pre><br />
<br />
== Modifying the c++ impl.h Header ==<br />
<br />
Many of the files are automatically generated wrapper code that do not need to be modified. However, the ''multDivSelect_impl.h'' and ''multDivSelect_impl.cc'' files defines the operation of the block and must be modified. Open the file with a text editor:<br />
<br />
<pre>$ gedit lib/multDivSelect_impl.h &</pre><br />
<br />
The following code will be displayed:<br />
<br />
<syntaxhighlight lang="cpp" line>/* -*- c++ -*- */<br />
/*<br />
* Copyright 2022 YourName.<br />
*<br />
* SPDX-License-Identifier: GPL-3.0-or-later<br />
*/<br />
<br />
#ifndef INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H<br />
#define INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H<br />
<br />
#include <gnuradio/customModule/multDivSelect.h><br />
<br />
namespace gr {<br />
namespace customModule {<br />
<br />
class multDivSelect_impl : public multDivSelect<br />
{<br />
private:<br />
// Nothing to declare in this block.<br />
<br />
public:<br />
multDivSelect_impl(bool selector);<br />
~multDivSelect_impl();<br />
<br />
// Where all the action really happens<br />
int work(int noutput_items,<br />
gr_vector_const_void_star& input_items,<br />
gr_vector_void_star& output_items);<br />
};<br />
<br />
} // namespace customModule<br />
} // namespace gr<br />
<br />
#endif /* INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H */</syntaxhighlight><br />
<br />
Create a boolean private member ''_selector'' which will hold the value of the ''selector'' parameter:<br />
<br />
<syntaxhighlight lang="cpp" line>class multDivSelect_impl : public multDivSelect<br />
{<br />
private:<br />
bool _selector;<br />
</syntaxhighlight><br />
<br />
Press CTRL + S to save the file.<br />
<br />
== Modifying the c++ impl.cc File ==<br />
<br />
The ''.cc'' file needs to be modified to define the desired operation of the block. Open the file with a text editor:<br />
<br />
<pre>$ gedit lib/multDivSelect_impl.cc &</pre><br />
<br />
The code will be as displayed:<br />
<br />
<syntaxhighlight lang="cpp" line>/* -*- c++ -*- */<br />
/*<br />
* Copyright 2022 YourName.<br />
*<br />
* SPDX-License-Identifier: GPL-3.0-or-later<br />
*/<br />
<br />
#include "multDivSelect_impl.h"<br />
#include <gnuradio/io_signature.h><br />
<br />
namespace gr {<br />
namespace customModule {<br />
<br />
#pragma message("set the following appropriately and remove this warning")<br />
using input_type = float;<br />
#pragma message("set the following appropriately and remove this warning")<br />
using output_type = float;<br />
multDivSelect::sptr multDivSelect::make(bool selector)<br />
{<br />
return gnuradio::make_block_sptr<multDivSelect_impl>(selector);<br />
}<br />
<br />
<br />
/*<br />
* The private constructor<br />
*/<br />
multDivSelect_impl::multDivSelect_impl(bool selector)<br />
: gr::sync_block("multDivSelect",<br />
gr::io_signature::make(<br />
1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),<br />
gr::io_signature::make(<br />
1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))<br />
{<br />
}<br />
<br />
/*<br />
* Our virtual destructor.<br />
*/<br />
multDivSelect_impl::~multDivSelect_impl() {}<br />
<br />
int multDivSelect_impl::work(int noutput_items,<br />
gr_vector_const_void_star& input_items,<br />
gr_vector_void_star& output_items)<br />
{<br />
auto in = static_cast<const input_type*>(input_items[0]);<br />
auto out = static_cast<output_type*>(output_items[0]);<br />
<br />
#pragma message("Implement the signal processing in your block and remove this warning")<br />
// Do <+signal processing+><br />
<br />
// Tell runtime system how many output items we produced.<br />
return noutput_items;<br />
}<br />
<br />
} /* namespace customModule */<br />
} /* namespace gr */</syntaxhighlight><br />
<br />
Remove the ''pragma'' messages and define the input and output type to be ''gr_complex'':<br />
<br />
<syntaxhighlight lang="cpp" line><br />
using input_type = gr_complex;<br />
using output_type = gr_complex;<br />
</syntaxhighlight><br />
<br />
Update to two inputs and store the value of the ''selector'' parameter using the private member ''_selector'' as defined in ''multDivSelector_impl.h'':<br />
<br />
<syntaxhighlight lang="cpp" line><br />
/*<br />
* The private constructor<br />
*/<br />
multDivSelect_impl::multDivSelect_impl(bool selector)<br />
: gr::sync_block("multDivSelect",<br />
gr::io_signature::make(<br />
2 /* min inputs */, 2 /* max inputs */, sizeof(input_type)),<br />
gr::io_signature::make(<br />
1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))<br />
{<br />
_selector = selector;<br />
}</syntaxhighlight><br />
<br />
Modify the ''work()'' function by removing the ''pragma'' message, defining the variables ''in0'' and ''in1'' corresponding to the two input ports, and multiply the two inputs if ''_selector'' is true and divide them if ''_selector'' is false:<br />
<br />
<syntaxhighlight lang="cpp" line><br />
int multDivSelect_impl::work(int noutput_items,<br />
gr_vector_const_void_star& input_items,<br />
gr_vector_void_star& output_items)<br />
{<br />
auto in0 = static_cast<const input_type*>(input_items[0]);<br />
auto in1 = static_cast<const input_type*>(input_items[1]);<br />
auto out = static_cast<output_type*>(output_items[0]);<br />
<br />
for (int index = 0; index < noutput_items; index++) {<br />
if (_selector) { out[index] = in0[index] * in1[index]; }<br />
else{ out[index] = in0[index] / in1[index]; }<br />
}<br />
<br />
<br />
// Tell runtime system how many output items we produced.<br />
return noutput_items;<br />
}</syntaxhighlight><br />
<br />
Press CTRL + S to save the file.<br />
<br />
== Modifying the YAML .yml File ==<br />
<br />
''gnuradio-companion'' uses files in the YAML (''yet another markup language'') format to learn about our OOT block and how to call it. More information about this kind of file may be found in the [[YAML GRC]] page.<br />
<br />
Open our block's YAML file using a text editor:<br />
<br />
<pre>$ gedit gedit grc/customModule_multDivSelect.block.yml &</pre><br />
<br />
The code will be displayed:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
id: customModule_multDivSelect<br />
label: multDivSelect<br />
category: '[customModule]'<br />
<br />
templates:<br />
imports: from gnuradio import customModule<br />
make: customModule.multDivSelect(${selector})<br />
<br />
# Make one 'parameters' list entry for every parameter you want settable from the GUI.<br />
# Keys include:<br />
# * id (makes the value accessible as keyname, e.g. in the make entry)<br />
# * label (label shown in the GUI)<br />
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)<br />
# * default<br />
parameters:<br />
- id: parametername_replace_me<br />
label: FIX ME:<br />
dtype: string<br />
default: You need to fill in your grc/customModule_multDivSelect.block.yaml<br />
#- id: ...<br />
# label: ...<br />
# dtype: ...<br />
<br />
# Make one 'inputs' list entry per input and one 'outputs' list entry per output.<br />
# Keys include:<br />
# * label (an identifier for the GUI)<br />
# * domain (optional - stream or message. Default is stream)<br />
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)<br />
# * vlen (optional - data stream vector length. Default is 1)<br />
# * optional (optional - set to 1 for optional inputs. Default is 0)<br />
inputs:<br />
#- label: ...<br />
# domain: ...<br />
# dtype: ...<br />
# vlen: ...<br />
# optional: ...<br />
<br />
outputs:<br />
#- label: ...<br />
# domain: ...<br />
# dtype: ...<br />
# vlen: ...<br />
# optional: ...<br />
<br />
# 'file_format' specifies the version of the GRC yml format used in the file<br />
# and should usually not be changed.<br />
file_format: 1</syntaxhighlight><br />
<br />
Update the parameter definition with the information for ''selector'':<br />
<br />
<syntaxhighlight lang="yaml" line><br />
parameters:<br />
- id: selector<br />
label: Selector, Multiply (true) or Divide (false)<br />
dtype: bool<br />
default: true</syntaxhighlight><br />
<br />
Update the input port and output port definitions:<br />
<br />
<syntaxhighlight lang="yaml" line><br />
inputs:<br />
- label: in0<br />
domain: stream<br />
dtype: complex<br />
- label: in1<br />
domain: stream<br />
dtype: complex<br />
outputs:<br />
- label: out0<br />
domain: stream<br />
dtype: complex</syntaxhighlight><br />
<br />
Press CTRL + S to save the file.<br />
<br />
== Compiling and Installing the Block ==<br />
<br />
The block needs to be compiled and installed. Ensure you are in the ''gr-customModule'' directory:<br />
<br />
<pre>$ cd your-path/gr-customModule</pre><br />
<br />
If the ''build/'' directory already exists, remove it:<br />
<br />
<pre>rm -rf build/</pre><br />
<br />
Create the build directory:<br />
<br />
<pre>$ mkdir build</pre><br />
<br />
Move into the build directory:<br />
<br />
<pre>cd build</pre><br />
<br />
Run cmake to build the makefiles:<br />
<br />
<pre>cmake ..</pre><br />
<br />
Compile the module:<br />
<br />
<pre>make</pre><br />
<br />
Install the module:<br />
<br />
<pre>sudo make install</pre><br />
<br />
The new files will then be installed:<br />
<br />
<pre>-- Install configuration: "Release"<br />
-- Up-to-date: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake<br />
-- Up-to-date: /usr/local/include/gnuradio/customModule/api.h<br />
-- Installing: /usr/local/include/gnuradio/customModule/multDivSelect.h<br />
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so.1.0.0.0<br />
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so.1.0.0<br />
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so<br />
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleTargets.cmake<br />
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleTargets-release.cmake<br />
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake<br />
-- Up-to-date: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.py<br />
-- Up-to-date: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.py<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyc<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyc<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyo<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyo<br />
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/customModule_python.cpython-39-x86_64-linux-gnu.so<br />
-- Set runtime path of "/usr/local/lib/python3.9/dist-packages/gnuradio/customModule/customModule_python.cpython-39-x86_64-linux-gnu.so" to ""<br />
-- Up-to-date: /usr/local/share/gnuradio/grc/blocks/customModule_addSubSelect.block.yml<br />
-- Installing: /usr/local/share/gnuradio/grc/blocks/customModule_multDivSelect.block.yml</pre><br />
<br />
Some of the files are listed as ''Up-to-date'' because they correspond to the ''addSubSelect'' block created in the [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]] tutorial. All of the files corresponding to ''multDivSelect'' are now installed to ''/usr/local/''.<br />
<br />
Run ''ldconfig'' to update the linking for the customModule library:<br />
<br />
<pre>$ sudo ldconfig </pre><br />
<br />
== Using the Custom Block in a Flowgraph ==<br />
<br />
If GRC is already running, press the ''Reload'' button to refresh the list of blocks in the library:<br />
<br />
[[File:ReloadBlockLibrary.png|800px]]<br />
<br />
Otherwise, start GRC from the command line:<br />
<pre>$ gnuradio-companion &</pre><br />
<br />
The ''multDivBlock'' can now be seen under the ''customModule'' tab. The ''addSubSelect'' is a Python block created in the [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]] tutorial.<br />
<br />
[[File:MultDivBlockLibrary.png|800px]]<br />
<br />
Drag the ''multDivSelect'' block into the flowgraph:<br />
<br />
[[File:AddMultDivToWorkspace.png|800px]]<br />
<br />
Now drag in the following blocks and update their properties:<br />
* Signal Source<br />
** Frequency: 100<br />
* Constant Source<br />
** Constant: 2<br />
* Throttle<br />
* QT GUI Time Sink<br />
** Autoscale: Yes<br />
<br />
Connect the flowgraph accordingly:<br />
<br />
[[File:MultDivFlowgraph.png|800px]]<br />
<br />
== Running the Flowgraph ==<br />
<br />
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output. Notice that the amplitude of the sinusoid is 2, due to the multiplication by 2 in the ''multDivSelect'' block.<br />
<br />
[[File:MultDivMultOutput.png|700px]]<br />
<br />
Edit the properties of the ''multDivSelect'' block and enter ''False'' for the ''selector'':<br />
<br />
[[File:MultDivProperties.png|500px]]<br />
<br />
Click ''OK'' to save the property.<br />
<br />
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output. Notice that the amplitude of the sinusoid is 0.5, due to the division by 2 in the ''multDivSelect'' block.<br />
<br />
[[File:MultDiv_DivOutput.png|700px]]<br />
<br />
== Making Changes ==<br />
<br />
It is suggested to recompile and reinstall the module any time a change is made, followed by reloading the block library in GRC. This includes changes such as:<br />
* Number of parameters<br />
* Type of parameters<br />
* Number of input ports or output ports<br />
* Types of input ports or output ports<br />
* Modifying the YAML .yml file<br />
* Modifying any c++ .h or .cc files<br />
<br />
Removing and re-creating the ''build/'' directory may be necessary before recompiling and reinstalling the module depending on the scope of the change:<br />
<br />
<pre>$ rm -rf gr-customModule/build<br />
$ mkdir gr-customModule/build</pre><br />
<br />
The previous tutorial, [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]], describes how to build a custom Python OOT module.</div>N1aihttps://wiki.gnuradio.org/index.php?title=OutOfTreeModules&diff=12375OutOfTreeModules2022-07-19T21:09:41Z<p>N1ai: Put in a deprecation warning that points people to the newer tutorials.</p>
<hr />
<div>[[Category:Guide]]<br />
'''Extending GNU Radio with own functionality and blocks'''<br />
<br />
'''Deprecation Warning''': This article has been superseded by newer tutorials for C++ ( [[Creating c++ OOT with gr-modtool]] ) and Python ( [[Creating Python OOT with gr-modtool]] ). This article is being kept only as a reference. Please use the newer tutorials instead of this aricle. In turn, this article superseded the original &quot;How to write a block?&quot; article written by Eric Blossom.<br />
<br />
== What is an out-of-tree module? ==<br />
<br />
An out-of-tree module is a GNU Radio component that does not live within the GNU Radio source tree. Typically, if you want to extend GNU Radio with your own functions and blocks, such a module is what you create (i.e. you wouldn't usually add stuff to the actual GNU Radio source tree unless you're planning to submit it to the devs for upstream integration). This allows you to maintain the code yourself and have additional functionality alongside the main code.<br />
<br />
A lot of OOT projects are hosted at [http://cgran.org CGRAN] -- the Comprehensive GNU Radio Archive Network. CGRAN projects are all available through our tool [https://www.gnuradio.org/blog/2016-06-19-pybombs-the-what-the-how-and-the-why/ PyBOMBS]. In fact, when you add your project to the [https://github.com/gnuradio/gr-etcetera PyBOMBS recipe repo], it will automatically update the CGRAN website.<br />
<br />
The example of such a module is the [https://www.cgran.org/14680/ GNU Radio Digital Audio Broadcasting module], which extends GNU Radio with everything needed to get audio from DAB and DAB+. When installed, you have more blocks available (i.e. in the [[GNURadioCompanion|GNU Radio companion]]) which behave just like the rest of GNU Radio; however, the developers are different people.<br />
<br />
== Tools and resources at my disposal ==<br />
<br />
There are a couple of tools, scripts, and documents that are available as 3rd-party programs or as part of GNU Radio.<br />
<br />
=== gr_modtool - The swiss army knife of module editing ===<br />
<br />
When developing a module, there's a lot of boring, monotonous work involved: boilerplate code, makefile editing, etc. gr_modtool is a script which aims to help with all these things by automatically editing makefiles, using templates, and doing as much work as possible for the developer such that you can jump straight into the DSP coding.<br />
<br />
Note that gr_modtool makes a lot of assumptions on what the code looks like. The more your module is custom and has specific changes, the less useful gr_modtool will be, but it is probably the best place to start with any new module or block.<br />
<br />
gr_modtool is now available in the GNU Radio source tree and is installed by default.<br />
<br />
=== Developer resources on the wiki ===<br />
<br />
Most important is definitely the [[BlocksCodingGuide|block coding guide]]. While this is written for the GNU Radio main tree, this should also be applied to all modules. Specifically, have a look at the naming conventions!<br />
<br />
=== CMake, make, etc. ===<br />
<br />
GNU Radio uses CMake as a build system. Building a module, therefore, requires you to have cmake installed, and whatever build manager you prefer (most often this is 'make', but you could also be using Eclipse or MS Visual Studio).<br />
<br />
== Tutorial 1: Creating an out-of-tree module ==<br />
<br />
In the following tutorials, we will use an out-of-tree module called '''howto'''. The first step is to create this module.<br />
<br />
With gr_modtool, this is dead easy. Just point your command line wherever you want your new module directory (this should be outside the GNU Radio source tree!), and go:<br />
<br />
<pre>$ gr_modtool newmod howto<br />
Creating out-of-tree module in ./gr-howto... Done.<br />
Use 'gr_modtool add' to add a new block to this currently empty module.</pre><br />
If all went well, you now have a new directory called <code>gr-howto</code> in which we will work for the other tutorials.<br />
<br />
== Structure of a module ==<br />
<br />
Let's jump straight into the gr-howto module and see what it's made up of:<br />
<pre>$ cd gr-howto<br />
$ ls<br />
apps cmake CMakeLists.txt docs examples grc include lib python swig</pre><br />
It consists of several subdirectories. Anything that will be written in C++ (or C, or any language that is not Python) is put into <code>lib/</code>. For C++ files, we usually have headers which are put into <code>include/</code> (if they are to be exported) or also in <code>lib/</code> (if they're only relevant during compile time, but are not installed later, such as <code>_impl.h</code> files. You'll see what that is in the next tutorial).<br />
<br />
Of course, Python stuff goes into the <code>python/</code> directory. This includes unit tests (which are not installed) and parts of the Python module which are installed.<br />
<br />
You probably know already that GNU Radio blocks are available in Python even if they were written in C++. This is done by the help of the Simplified Wrapper and Interface Generator (SWIG), which automatically creates glue code to make this possible. SWIG needs some instructions on how to do this, which are put into the <code>swig/</code> subdirectory. Unless doing something extra clever with your block, you will not need to go into the <code>swig/</code> directory; gr_modtool handles all of that for us.<br />
<br />
If you want your blocks to be available in the [[GNURadioCompanion|GNU Radio companion]], the graphical UI for GNU Radio, you need to add descriptions of the blocks and put them into <code>grc/</code>. Prior to version 3.8 these descriptions were XML files, but from 3.8 onward they use [[YAML_GRC|YAML instead]].<br />
<br />
For documentation, <code>docs/</code> contains some instructions on how to extract documentation from the C++ files and Python files (we use Doxygen and Sphinx for this) and also make sure they're available as docstrings in Python. Of course, you can add custom documentation here as well.<br />
<br />
The <code>apps/</code> subdir contains any complete applications (both for GRC and standalone executables) which are installed to the system alongside with the blocks.<br />
<br />
The directory, <code>examples/</code> can be used to save (guess what) examples, which are a great addendum to documentation because other developers can simply look straight at the code to see how your blocks are used.<br />
<br />
The build system brings some baggage along, as well: the <code>CMakeLists.txt</code> file (one of which is present in every subdirectory) and the <code>cmake/</code> folder. You can ignore the latter for now, as it brings along mainly instructions for CMake on how to find GNU Radio libraries etc. The CMakeLists.txt files need to be edited a lot in order to make sure your module builds correctly.<br />
<br />
But one step at a time! Now, let's move on to the next step.<br />
<br />
== Tutorial 2: Writing a block (square_ff) in C++ ==<br />
<br />
For our first example, we'll 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, i.e., for every incoming float item, we output one float item which is the square of that input item.<br />
<br />
Following the naming conventions, the block will be called <code>square_ff</code> because it has float inputs, float outputs.<br />
<br />
We are going to arrange that this block, as well as the others that we write in this article, end up in the <code>howto</code> Python module. This will allow us to access it from Python like this:<br />
<br />
<pre>import howto<br />
sqr = howto.square_ff()</pre><br />
=== Creating the files ===<br />
<br />
First step is to create empty files for the block and edit the CMakeLists.txt files.<br /><br />
Again, <code>gr_modtool</code> does the job. On the command line, go to the <code>gr-howto</code> directory and enter:<br />
<br />
<pre>gr-howto$ gr_modtool add -t general -l cpp square_ff<br />
GNU Radio module name identified: howto<br />
Language: C++<br />
Block/code identifier: square_ff<br />
Enter valid argument list, including default arguments: <br />
Add Python QA code? [Y/n] Y<br />
Add C++ QA code? [y/N] N<br />
Adding file 'lib/square_ff_impl.h'...<br />
Adding file 'lib/square_ff_impl.cc'...<br />
Adding file 'include/howto/square_ff.h'...<br />
Editing swig/howto_swig.i...<br />
Adding file 'python/qa_square_ff.py'...<br />
Editing python/CMakeLists.txt...<br />
Adding file 'grc/howto_square_ff.xml'...<br />
Editing grc/CMakeLists.txt...</pre><br />
On the command line, we specify that we're adding a block, its type is 'general' (because we don't know what block types are, yet) and it is called <code>square_ff</code>. The block should be created in C++ and it currently has no specified copyright holder (by default, gr-module author is the copyright holder). <code>gr_modtool</code> then asks you if your block takes any arguments (it doesn't, so we leave that empty), whether or not we want QA code for Python (yes, we do) and for C++ (no, we don't right now).<br />
<br />
Now, have another look at the different CMakeLists.txt files and see what <code>gr_modtool</code> did. You can also see a lot of new files, which now have to be edited if we want the block to work.<br />
<br />
=== Test Driven Programming ===<br />
<br />
We could just start banging out the C++ code, but being highly evolved modern programmers, we're 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.<br />
<br />
How hard could this be? Turns out that this is easy! So, we open <code>python/qa_square_ff.py</code>, which we edit to look like this:<br />
<br />
<syntaxhighlight lang="python"><br />
from gnuradio import gr, gr_unittest<br />
from gnuradio import blocks<br />
import howto_swig as howto<br />
<br />
class qa_square_ff (gr_unittest.TestCase):<br />
<br />
def setUp (self):<br />
self.tb = gr.top_block ()<br />
<br />
def tearDown (self):<br />
self.tb = None<br />
<br />
def test_001_square_ff(self):<br />
src_data = (-3, 4, -5.5, 2, 3)<br />
expected_result = (9, 16, 30.25, 4, 9)<br />
src = blocks.vector_source_f(src_data)<br />
sqr = howto.square_ff()<br />
dst = blocks.vector_sink_f()<br />
self.tb.connect(src, sqr)<br />
self.tb.connect(sqr, dst)<br />
self.tb.run()<br />
result_data = dst.data()<br />
self.assertFloatTuplesAlmostEqual(expected_result, result_data, 6)<br />
<br />
if __name__ == '__main__':<br />
gr_unittest.run(qa_square_ff, &quot;qa_square_ff.xml&quot;)</pre><br />
</syntaxhighlight><br />
<br />
For GNU Radio v3.9 and up, use the following:<br />
<br />
<syntaxhighlight lang="python"><br />
from gnuradio import gr, gr_unittest<br />
from gnuradio import blocks<br />
<br />
try:<br />
from howto import square_ff<br />
except ImportError:<br />
import os<br />
import sys<br />
dirname, filename = os.path.split(os.path.abspath(__file__))<br />
sys.path.append(os.path.join(dirname, "bindings"))<br />
from howto import square_ff<br />
<br />
class qa_square_ff(gr_unittest.TestCase):<br />
<br />
def setUp(self):<br />
self.tb = gr.top_block()<br />
<br />
def tearDown(self):<br />
self.tb = None<br />
<br />
def test_instance(self):<br />
# FIXME: Test will fail until you pass sensible arguments to the constructor<br />
instance = square_ff()<br />
<br />
def test_001_square_ff(self): <br />
src_data = (-3, 4, -5.5, 2, 3)<br />
expected_result = (9, 16, 30.25, 4, 9)<br />
src = blocks.vector_source_f(src_data)<br />
sqr = square_ff()<br />
dst = blocks.vector_sink_f()<br />
self.tb.connect(src, sqr)<br />
self.tb.connect(sqr,dst)<br />
self.tb.run()<br />
result_data = dst.data()<br />
self.assertFloatTuplesAlmostEqual(expected_result, result_data, 6)<br />
# check data<br />
<br />
if __name__ == '__main__':<br />
gr_unittest.run(qa_square_ff, "qa_square_ff.yaml")<br />
</syntaxhighlight><br />
<br />
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 [http://docs.python.org/2/library/unittest.html Python unittest documentation] for details.<br />
<br />
When we run the test, gr_unittest.main is going to invoke setUp, test_001_square_ff, and tearDown, in that order.<br />
<br />
<code>test_001_square_ff</code> builds a small graph that contains three nodes. <code>blocks.vector_source_f(src_data)</code> will source the elements of <code>src_data</code> and then say that it's finished. <code>howto.square_ff</code> is the block we're testing. <code>blocks.vector_sink_f</code> gathers the output of <code>howto.square_ff</code>.<br />
<br />
The <code>run()</code> method runs the graph until all the blocks indicate they are finished. Finally, we check that the result of executing <code>square_ff</code> on <code>src_data</code> matches what we expect.<br />
<br />
Note that such a test is usually called before installing the module. This means that we need some trickery to be able to load the blocks when testing. CMake takes care of most things by changing PYTHONPATH appropriately. Also, we import <code>howto_swig</code> instead of <code>howto</code> in this file.<br />
<br />
In order for CMake to actually know this test exists, <code>gr_modtool</code> modified <code>python/CMakeLists.txt</code> with these lines:<br />
<br />
<pre>########################################################################<br />
# Handle the unit tests<br />
########################################################################<br />
include(GrTest)<br />
<br />
set(GR_TEST_TARGET_DEPS gnuradio-howto)<br />
set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig)<br />
GR_ADD_TEST(qa_square_ff ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_square_ff.py)</pre><br />
<br />
For GR v3.9+, remove the following line from <code>python/CMakeLists.txt</code>:<br />
<pre>set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig) </pre><br />
<br />
=== The C++ code (part 1) ===<br />
<br />
Now that we've got a test case, let's write the C++ code. All signal processing blocks are derived from <code>gr::block</code> or one of its subclasses. Go check out the [http://gnuradio.org/doc/doxygen/classgr_1_1block.html block documentation] on the Doxygen-generated manual.<br />
<br />
<code>gr_modtool</code> already provided us with three files that define the block: <code>lib/square_ff_impl.h</code>, <code>lib/square_ff_impl.cc</code> and <code>include/howto/square_ff.h</code>. All we have to do is modify them to do our bidding. After you've finished with this tutorial ''please'' read and understand the [https://wiki.gnuradio.org/index.php/BlocksCodingGuide Blocks Coding Guide] to find out how these files are structured and why!<br />
<br />
First of all, we have a look at our header files. Because the block we're writing is so simple, we don't have to actually change them (the header file in <code>include/</code> is often quite complete after running <code>gr_modtool</code>, unless we need to add some public methods such as mutator methods, i.e., getters and setters). That leaves us with <code>lib/square_ff_impl.cc</code>.<br />
<br />
<code>gr_modtool</code> hints at where you have to change code by adding <code>&lt;++&gt;</code> symbols.<br /><br />
<br />
The following is the constructor of the module:<br />
<br />
<pre> square_ff_impl::square_ff_impl()<br />
: gr::block("square_ff",<br />
gr::io_signature::make(<+MIN_IN+>, <+MAX_IN+>, sizeof(<+ITYPE+>)),<br />
gr::io_signature::make(<+MIN_OUT+>, <+MAX_OUT+>, sizeof(<+OTYPE+>)))<br />
{}<br />
{<br />
// empty constructor<br />
}</pre><br />
For this tutorial, we replace <+MIN_IN+>, <+MAX_IN+>, <+MIN_OUT+>, <+MAX_OUT+> by 1 and set input type (<+ITYPE+>) and output type (<+OTYPE+>) as float.<br />
Replace the above constructor with the following one:<br />
<br />
<pre> square_ff_impl::square_ff_impl()<br />
: gr::block(&quot;square_ff&quot;,<br />
gr::io_signature::make(1, 1, sizeof (float)), // input signature<br />
gr::io_signature::make(1, 1, sizeof (float))) // output signature<br />
{<br />
// empty constructor<br />
}</pre><br />
<br />
The constructor itself is empty, as the squaring block has no need to set up anything.<br />
<br />
The only interesting portion is the definition of the input and output signatures: At the input, we have 1 port that allows float inputs. The output port is the same.<br />
<br />
Replace the forecast function with the following one:<br />
<pre> void<br />
square_ff_impl::forecast (int noutput_items, gr_vector_int &amp;ninput_items_required)<br />
{<br />
ninput_items_required[0] = noutput_items;<br />
}</pre><br />
<code>forecast()</code> is a function which tells the scheduler how many input items are required to produce <code>noutput_items</code> output items. In this case, they're the same. The index 0 indicates that this is for the first port, but we only have one anyway. This is generally the case for <code>forecast</code> in a lot of blocks. For examples, you can look at how <code>gr::block</code>, <code>gr::sync_block</code>, <code>gr::sync_decimator</code>, and <code>gr::sync_interpolator</code> define the default forecast functions to account for things like rate changes and history.<br />
<br />
Finally, there's <code>general_work()</code>, which is pure virtual in <code>gr::block</code>, so we definitely need to override that. <code>general_work()</code> is the method that does the actual signal processing:<br />
<br />
<pre> int<br />
square_ff_impl::general_work (int noutput_items,<br />
gr_vector_int &amp;ninput_items,<br />
gr_vector_const_void_star &amp;input_items,<br />
gr_vector_void_star &amp;output_items)<br />
{<br />
const float *in = (const float *) input_items[0];<br />
float *out = (float *) output_items[0];<br />
<br />
for(int i = 0; i &lt; noutput_items; i++) {<br />
out[i] = in[i] * in[i];<br />
}<br />
<br />
// Tell runtime system how many input items we consumed on<br />
// each input stream.<br />
consume_each (noutput_items);<br />
<br />
// Tell runtime system how many output items we produced.<br />
return noutput_items;<br />
}</pre><br />
There is one pointer to the input- and one pointer to the output buffer, respectively, and a for-loop which copies the square of the input buffer to the output buffer.<br />
<br />
=== Using CMake ===<br />
<br />
If you've never used CMake before, this is a good time to give it a try. The typical workflow of a CMake-based project as seen from the command line is this (if using PyBOMBS, first read '''Build Tree vs. Install Tree'''):<br />
<br />
<pre>gr-howto$ mkdir build <br />
gr-howto$ cd build/<br />
gr-howto/build$ cmake ../ # Tell CMake that all its config files are one dir up<br />
gr-howto/build$ make # And start building (should work after the previous section)</pre><br />
<br />
If using GR v3.9 you will need to run:<br />
<pre>gr-howto/build$ sudo make install </pre><br />
<br />
===== Build Tree vs. Install Tree =====<br />
<br />
When you run cmake, you usually run it in a separate directory (e.g. <code>build/</code>). This is the build tree. The path to the install tree is <code>$prefix/lib/$pythonversion/dist-packages</code>, where <code>$prefix</code> is whatever you specified to CMake during configuration (usually <code>/usr/local/</code>) with the <code>-DCMAKE_INSTALL_PREFIX</code> switch. (Note: different versions of Python will either use site-packages or dist-packages; dist-packages is the newer way and most likely for newer OSes and installations.)<br />
<br />
If you installed GNU Radio using PyBOMBS, the install tree is located in the <code>target/</code> directory set during the initial PyBOMBS configuration. Make sure to add the -DCMAKE_INSTALL_PREFIX switch for CMake, so that it will correctly locate your GNU Radio installation. The command should look similar to this:<br />
<br />
<pre>$ cmake -DCMAKE_INSTALL_PREFIX=~/prefix-3.8 ../ # should be the configured PyBOMBS target</pre><br />
Now we have a new directory <code>build/</code> in our module's directory. All the compiling etc. is done in here, so the actual source tree is not littered with temporary files. If we change any CMakeLists.txt files, we should re-run <code>cmake ../</code> (although in truth, cmake detects these changes and reruns automatically when you next run <code>make</code>). During compilation, the libraries are copied into the build tree. Only during installation, files are installed to the install tree, thus making our blocks available to GNU Radio apps.<br />
<br />
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.<br />
<br />
=== Let's try that -- running <code>make test</code> ===<br />
<br />
Because we wrote the QA code before the C++ code, we can immediately see if what we did was correct.<br />
<br />
We use <code>make test</code> to run our tests (run this from the <code>build/</code> subdirectory, after calling <code>cmake</code> and <code>make</code>). This invokes a 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.<br />
<br />
There is quite a bit of behind-the-scenes action required to use the non-installed versions of our code (look at the <code>cmake/</code> directory for a cheap thrill.)<br />
<br />
If you completed the <code>square_ff</code> block, this should work fine:<br />
<br />
<pre>gr-howto/build % make test<br />
Running tests...<br />
Test project /home/braun/tmp/gr-howto/build<br />
Start 1: test_howto<br />
1/2 Test #1: test_howto ....................... Passed 0.01 sec<br />
Start 2: qa_square_ff<br />
2/2 Test #2: qa_square_ff ..................... Passed 0.38 sec<br />
<br />
100% tests passed, 0 tests failed out of 2<br />
<br />
Total Test time (real) = 0.39 sec</pre><br />
If something fails during the tests, we can dig a little deeper. When we run <code>make test</code>, we're actually invoking the CMake program <code>ctest</code>, which has a number of options we can pass to it for more detailed information. Say we forgot to multiply <code>in[i] * in[i]</code> and so aren't actually squaring the signal. If we just run <code>make test</code> or even just <code>ctest</code>, we would get this:<br />
<br />
<pre>gr-howto/build $ ctest<br />
Test project /home/braun/tmp/gr-howto/build<br />
Start 1: test_howto<br />
1/2 Test #1: test_howto ....................... Passed 0.02 sec<br />
Start 2: qa_square_ff<br />
2/2 Test #2: qa_square_ff .....................***Failed 0.21 sec<br />
<br />
50% tests passed, 1 tests failed out of 2<br />
<br />
Total Test time (real) = 0.23 sec<br />
<br />
The following tests FAILED:<br />
2 - qa_square_ff (Failed)<br />
Errors while running CTest</pre><br />
To find out what happened with our qa_square_ff test, we run <code>ctest -V -R square</code>. The '-V' flag gives us verbose output and the '-R' flag is a regex expression to only run those tests which match.<br />
<br />
<pre>gr-howto/build $ ctest -V -R square<br />
UpdateCTestConfiguration from :/home/braun/tmp/gr-howto/build/DartConfiguration.tcl<br />
UpdateCTestConfiguration from :/home/bruan/tmp/gr-howto/build/DartConfiguration.tcl<br />
Test project /home/braun/tmp/gr-howto/build<br />
Constructing a list of tests<br />
Done constructing a list of tests<br />
Checking test dependency graph...<br />
Checking test dependency graph end<br />
test 2<br />
Start 2: qa_square_ff<br />
<br />
2: Test command: /bin/sh &quot;/home/bruan/tmp/gr-howto/build/python/qa_square_ff_test.sh&quot;<br />
2: Test timeout computed to be: 9.99988e+06<br />
2: F<br />
2: ======================================================================<br />
2: FAIL: test_001_t (__main__.qa_square_ff)<br />
2: ----------------------------------------------------------------------<br />
2: Traceback (most recent call last):<br />
2: File &quot;/home/braun/tmp/gr-howto/python/qa_square_ff.py&quot;, line 44, in test_001_t<br />
2: self.assertFloatTuplesAlmostEqual(expected_result, result_data, 6)<br />
2: File &quot;/opt/gr/lib/python2.7/dist-packages/gnuradio/gr_unittest.py&quot;, line 90, in assertFloatTuplesAlmostEqual<br />
2: self.assertAlmostEqual (a[i], b[i], places, msg)<br />
2: AssertionError: 9 != -3.0 within 6 places<br />
2: <br />
2: ----------------------------------------------------------------------<br />
2: Ran 1 test in 0.002s<br />
2: <br />
2: FAILED (failures=1)<br />
1/1 Test #2: qa_square_ff .....................***Failed 0.21 sec<br />
<br />
0% tests passed, 1 tests failed out of 1<br />
<br />
Total Test time (real) = 0.21 sec<br />
<br />
The following tests FAILED:<br />
2 - qa_square_ff (Failed)<br />
Errors while running CTest</pre><br />
This tells us that &quot;9 != -3.0&quot; because we expected the output to be (-3)^2 = 9 but really got the input of -3. We can use this information to go back and fix our block until the tests pass.<br />
<br />
We can also put in debug print statements into our QA code on failures, like printing out <code>expected_result</code> and <code>result_data</code> to compare them to better understand the problem.<br />
<br />
=== More C++ code (but better) - Subclasses for common patterns ===<br />
<br />
<code>gr::block</code> allows tremendous flexibility with regard to the consumption of input streams and the production of output streams. Adroit use of <code>forecast()</code> and <code>consume()</code> (see below) 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.<br />
<br />
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. You must have thought the same thing in the <code>general_work()</code> function of the previous block: if the number of items consumed is identical the number of items produced, why do I have to tell GNU Radio the exact same number twice?<br />
<br />
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 &quot;history&quot;, but you could also think of it as &quot;look-ahead&quot;.<br />
<br />
* <code>gr::sync_block</code><br />
<br />
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 <code>work()</code> method instead of <code>general_work()</code>. <code>work()</code> has a slightly different calling sequence; it omits the unnecessary <code>ninput_items</code> parameter, and arranges for <code>consume_each()</code> to be called on our behalf.<br />
<br />
Let's add another block which derives from <code>gr::sync_block</code> and call it <code>square2_ff</code>. First, we edit <code>qa_square_ff.py</code> to add another test:<br />
<br />
<pre> def test_002_square2_ff(self):<br />
src_data = (-3, 4, -5.5, 2, 3)<br />
expected_result = (9, 16, 30.25, 4, 9)<br />
src = blocks.vector_source_f(src_data)<br />
sqr = howto.square2_ff()<br />
dst = blocks.vector_sink_f()<br />
self.tb.connect(src, sqr, dst)<br />
self.tb.run()<br />
result_data = dst.data()<br />
self.assertFloatTuplesAlmostEqual(expected_result, result_data, 6)</pre><br />
You can see it's the exact same test as before except for the use of <code>square2_ff</code>.<br />
<br />
Then, we use <code>gr_modtool</code> to add the block files, skipping the QA code (because we already have that):<br />
<br />
<pre>gr-howto$ gr_modtool add -t sync -l cpp square2_ff<br />
GNU Radio module name identified: howto<br />
Block/code identifier: square2_ff<br />
Language: C++<br />
Please specify the copyright holder:<br />
Enter valid argument list, including default arguments: <br />
Add Python QA code? [Y/n] n<br />
Add C++ QA code? [Y/n] n<br />
Adding file 'square2_ff_impl.h'...<br />
Adding file 'square2_ff_impl.cc'...<br />
Adding file 'square2_ff.h'...<br />
Editing swig/howto_swig.i...<br />
Adding file 'howto_square2_ff.xml'...<br />
Editing grc/CMakeLists.txt...</pre><br />
<br />
If running GR v3.9+ you will need to bind the new <code>square2_ff</code> files to the existing files.<br />
<pre>gr-howto$ gr_modtool bind square2_ff<br />
GNU Radio module name identifiedL howto<br />
Writing binding code to ./python/bindings/square2_ff_python.cc<br />
Writing binding code to ./python/bindings/docstrings/square2_ff_pydoc_template.h</pre><br />
<br />
Whenever howto/square2_ff.h is changed, <code> gr_modtool bind square2_ff </code> needs to be run.<br />
<br />
The constructor in <code>square2_ff_impl.cc</code> is done the same way as before, except for the parent class being <code>gr::sync_block</code>.<br />
<br />
<pre> square2_ff_impl::square2_ff_impl()<br />
: gr::sync_block(&quot;square2_ff&quot;,<br />
gr::io_signature::make(1, 1, sizeof (float)),<br />
gr::io_signature::make(1, 1, sizeof (float)))<br />
{}<br />
<br />
// [...] skip some lines ...<br />
<br />
int<br />
square2_ff_impl::work(int noutput_items,<br />
gr_vector_const_void_star &amp;input_items,<br />
gr_vector_void_star &amp;output_items)<br />
{<br />
const float *in = (const float *) input_items[0];<br />
float *out = (float *) output_items[0];<br />
<br />
for(int i = 0; i &lt; noutput_items; i++) {<br />
out[i] = in[i] * in[i];<br />
}<br />
<br />
// Tell runtime system how many output items we produced.<br />
return noutput_items;<br />
}</pre><br />
The <code>work</code> function is the real difference (also, we don't have a <code>forecast()</code> function any more). We'll look at it in greater detail in the next section.<br />
<br />
This gives us fewer things to worry about and less code to write. If the block requires history greater than 1, call <code>set_history()</code> in the constructor or any time the requirement changes.<br />
<br />
<code>gr::sync_block</code> provides a version of <code>forecast</code> that handles the history requirement.<br />
<br />
* <code>gr::sync_decimator</code><br />
<br />
<code>gr::sync_decimator</code> is derived from <code>gr::sync_block</code> and implements a N:1 block with optional history.<br />
<br />
* <code>gr::sync_interpolator</code><br />
<br />
<code>gr::sync_interpolator</code> is derived from <code>gr::sync_block</code> and implements a 1:N block with optional history.<br />
<br />
With this knowledge it should be clear that <code>howto_square_ff</code> should be a <code>gr::sync_block</code> with no history.<br />
<br />
Now, go back into our build directory and run <code>make</code>. Because <code>gr_modtool</code> added the <code>square2_ff</code> block to the necessary CMakeLists.txt files, <code>cmake</code> is automatically rerun for us and followed by <code>make</code>.<br />
<br />
<pre>gr-howto$ cd build<br />
gr-howto/build$ make<br />
gr-howto/build$ make test</pre><br />
<br />
Again, running <code>make test</code> will spawn a test run with of <code>qa_square_ff.py</code> which should not fail.<br />
<br />
=== Inside the <code>work()</code> function ===<br />
<br />
If you're using a sync block (including decimator and interpolator), this is how the skeleton code looks like produced by gr_modtool:<br />
<br />
<pre> int<br />
my_block_name::work(int noutput_items,<br />
gr_vector_const_void_star &amp;input_items,<br />
gr_vector_void_star &amp;output_items)<br />
{<br />
const float *in = (const float *) input_items[0];<br />
float *out = (float *) output_items[0];<br />
<br />
// Do &lt;+signal processing+&gt;<br />
<br />
// Tell runtime system how many output items we produced.<br />
return noutput_items;<br />
}</pre><br />
So, given history, vectors, multiple input ports etc., is this really all you need? Yes, it is! Because sync blocks have a fixed output to input rate, all you need to know is the number of output items, and you can calculate how many input items are available.<br />
<br />
Example - the adder block: source:gr-blocks/lib/add_XX_impl.cc.t<br />
<br />
This block has an unknown number of inputs and variable vector lengths. The number of connected ports can be checked by <code>input_items.size()</code> and <code>output_items.size()</code>. The outer <code>for</code> loop, which goes over all the available items, goes up to <code>noutput_items*d_vlen</code>. The number of output items is identical to the number of input items because it is a sync block, and you can trust GNU Radio to have this number of items available. In this case, one item is a vector of samples, but we want to add the individual samples, so the for loop considers that.<br />
<br />
Example - interpolation in gr::blocks::unpack_k_bits_bb: source:gr-blocks/lib/unpack_k_bits_bb_impl.cc [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/unpack_k_bits_bb_impl.cc source]<br />
<br />
This is a block which picks apart bytes and produces the individual bits. Again, it is unknown at compile time how many bits per byte there are. However, there's a fixed number of output items per input item, so we can simply divide <code>noutput_items/d_k</code> to get the correct number of input items. It will always be correct because GNU Radio knows the input to output ratio and will make sure that <code>noutput_items</code> is always a multiple of this integer ratio.<br />
<br />
Example - history in source:gr-digital/lib/diff_phasor_cc_impl.cc<br />
<br />
If you use history of length k, GNU Radio will keep k-1 entries of the input buffer instead of discarding them. This means that if GNU Radio tells you the input buffer has N items, it actually has N+k-1 items you may use.<br />
<br />
Consider the example above. We need one previous item, so history is set to k=2. If you inspect the for loop closely, you'll find that out of <code>noutput_items</code> items, <code>noutput_items+1</code> items are actually read. This is possible because there is an extra item in the input buffer from the history.<br />
<br />
After consuming <code>noutput_items</code> items, the last entry is not discarded and will be available for the next call of <code>work()</code>.<br />
<br />
=== Help! My test fails! ===<br />
<br />
Congratulations! If your test fails, your QA code has already paid for itself. Obviously, you want to fix everything before you continue.<br />
<br />
You can use the command <code>ctest -V</code> (instead of <code>make test</code>, again, all in your <code>build/</code> subdirectory) to get all the output from the tests. You can also use <code>ctest -V -R REGEX</code> to only run tests that match REGEX, if you have many tests and want to narrow it down. If you can't figure out the problem from the output of your QA code, put in <code>print</code> statements and show intermediary results.<br />
<br />
=== Making your blocks available in GRC ===<br />
<br />
You can now install your module, but it will not be available in GRC. That's because <code>gr_modtool</code> can't create valid XML files before you've even written a block. The XML code generated when you call <code>gr_modtool add</code> is just some skeleton code.<br />
<br />
Once you've finished writing the block, <code>gr_modtool</code> has a function to help you create the XML code for you. For the howto example, you can invoke it on the <code>square2_ff</code> block by calling<br />
For GNU Radio 3.8 and up:<br />
<pre>gr-howto$ gr_modtool makeyaml square2_ff<br />
GNU Radio module name identified: howto<br />
Warning: This is an experimental feature. Don't expect any magic.<br />
Searching for matching files in lib/:<br />
Making GRC bindings for lib/square2_ff_impl.cc...<br />
Overwrite existing GRC file? [y/N] y</pre><br />
<br />
For GNU Radio 3.7:<br />
<pre>gr-howto$ gr_modtool makexml square2_ff<br />
GNU Radio module name identified: howto<br />
Warning: This is an experimental feature. Don't expect any magic.<br />
Searching for matching files in lib/:<br />
Making GRC bindings for lib/square2_ff_impl.cc...<br />
Overwrite existing GRC file? [y/N] y</pre><br />
Note that <code>gr_modtool add</code> creates an invalid GRC file, so we can overwrite that.<br />
<br />
In most cases, <code>gr_modtool</code> can't figure out all the parameters by itself and you will have to edit the appropriate XML or YML file by hand. See [[XML_GRC|Writing the XML file for a block (for 3.7)]] or [[YAML_GRC|Writing the YAML file for a block (for 3.8+)]] for details.<br />
<br />
In this case, because the block is so simple, the XML is actually valid. Have a look at <code>grc/howto_square2_ff.xml</code>:<br />
<br />
<pre><br />
<block> <br />
<name>Square ff</name><br />
<key>howto_square_ff</key><br />
<category>[HOWTO]</category><br />
<import>import howto</import><br />
<make>howto.square_ff()</make><br />
<sink><br />
<name>in</name><br />
<type>float</type><br />
</sink><br />
<source><br />
<name>out</name><br />
<type>float</type><br />
</source><br />
</block><br />
</pre><br />
Perhaps you want to change the autogenerated name to something nicer.<br />
<br />
<b>Note:</b> The category name <b>must</b> be enclosed in square brackets to work!<br />
<br />
If you do a <code>make install</code> from the build directory, you can use the block in GRC. If GRC is already running, you can hit the &quot;Reload Blocks&quot; button in the GRC toolbar; it's a blue circular arrow on the right-hand side. You should now see a &quot;HOWTO&quot; category in the block tree.<br />
<br />
=== There's more: additional <code>gr::block</code>-methods ===<br />
<br />
If you've read the [http://gnuradio.org/doc/doxygen/classgr_1_1block.html gr::block documentation] (which you should have), you'll have noticed there are a great number of methods available to configure your block.<br />
<br />
Here's some of the more important ones:<br />
<br />
==== <code>set_history()</code> ====<br />
<br />
If your block needs a history (i.e., something like an FIR filter), call this in the constructor. GNU Radio then makes sure you have the given number of 'old' items available.<br />
<br />
The smallest history you can have is 1, i.e., for every output item, you need 1 input item. If you choose a larger value, N, this means your output item is calculated from the current input item and from the N-1 previous input items.<br />
<br />
The scheduler takes care of this for you. If you set the history to length N, the first N items in the input buffer include the N-1 previous ones (even though you've already consumed them).<br />
<br />
==== <code>forecast()</code> ====<br />
<br />
The system needs to know how much data is required to ensure validity in each of the input arrays. As stated before, the <code>forecast()</code> method provides this information, and you must therefore override it anytime you write a <code>gr::block</code> derivative (for sync blocks, this is implicit).<br />
<br />
The default implementation of <code>forecast()</code> says there is a 1:1 relationship between <code>noutput_items</code> and the requirements for each input stream. The size of the items is defined by <code>gr::io_signature::make</code> in the constructor of <code>gr::block</code>. The sizes of the input and output items can of course differ; this still qualifies as a 1:1 relationship. Of course, if you had this relationship, you wouldn't want to use a <code>gr::block</code>!<br />
<br />
<pre> // default implementation: 1:1<br />
void<br />
gr::block::forecast(int noutput_items,<br />
gr_vector_int &amp;ninput_items_required)<br />
{<br />
unsigned ninputs = ninput_items_required.size ();<br />
for(unsigned i = 0; i &lt; ninputs; i++)<br />
ninput_items_required[i] = noutput_items;<br />
}</pre><br />
Although the 1:1 implementation worked for <code>square_ff</code>, it wouldn't be appropriate for interpolators, decimators, or blocks with a more complicated relationship between <code>noutput_items</code> 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.<br />
<br />
==== <code>set_output_multiple()</code> ====<br />
<br />
When implementing your <code>general_work()</code> routine, it's 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 <code>set_output_multiple</code> in your constructor to specify this requirement. The default output multiple is 1.<br />
<br />
=== Finalizing your work and installing ===<br />
<br />
First, go through this checklist:<br />
<br />
* Have you written one or more blocks, including QA codes?<br />
* Does <code>make test</code> pass?<br />
* Are there GRC bindings available (if that's what you want)?<br />
<br />
In that case, you can go ahead and install your module. On a Linux machine, this would mean going back to the build directory and calling <code>make install</code>:<br />
<br />
<pre>$ cd build/<br />
$ make install # or sudo make install</pre><br />
With Ubuntu, you may have to call <code>ldconfig</code> as well:<br />
<br />
<pre>$ sudo ldconfig</pre><br />
Otherwise, you'll get an error message that the library you just installed cannot be found.<br />
<br />
== Other types of blocks ==<br />
<br />
=== Sources and sinks ===<br />
<br />
Sources and sinks are derived from <code>gr::sync_block</code>. The only thing different about them is that sources have no inputs and sinks have no outputs. This is reflected in the <code>gr::io_signature::make</code> that are passed to the <code>gr::sync_block</code> constructor. Take a look at [source:gr-blocks/lib/file_source_impl.cc file_source.{h,cc}] and file_sink_impl.{h,cc} for some very straight-forward examples.<br />
<br />
=== Hierarchical blocks ===<br />
<br />
<code>gr_modtool</code> supports skeleton code for hierarchical blocks both in Python and C''++.<br />
<br />
<pre>~/gr-howto % gr_modtool.py add -t hier -l cpp hierblockcpp_ff<br />
GNU Radio module name identified: howto<br />
Block/code identifier: hierblockcpp_ff<br />
Language: C++<br />
Please specify the copyright holder:<br />
Enter valid argument list, including default arguments:<br />
Add Python QA code? [Y/n]<br />
Add C++ QA code? [y/N]<br />
Adding file 'hierblockcpp_ff_impl.h'...<br />
Adding file 'hierblockcpp_ff_impl.cc'...<br />
Adding file 'hierblockcpp_ff.h'...<br />
Editing swig/howto_swig.i...<br />
Adding file 'howto_hierblockcpp_ff.xml'...<br />
Editing grc/CMakeLists.txt...</pre><br />
Using the <code>-l python</code> switch creates such a block in Python.<br />
<br />
== Everything at one glance: Cheat sheet for editing modules/components: ==<br />
<br />
Here's a quick list for all the steps necessary to build blocks and out-of-tree modules:<br />
<br />
# Create (do this once per module): <code>gr_modtool newmod MODULENAME</code><br />
# Add a block to the module: <code>gr_modtool add BLOCKNAME</code><br />
# Create a build directory: <code>mkdir build/</code><br />
# Invoke the make process: <code>cd build &amp;&amp; cmake &lt;OPTIONS&gt; ../ &amp;&amp; make</code> (Note that you only have to call cmake if you've changed the CMake files)<br />
# Invoke the testing: <code>make test</code> or <code>ctest</code> or <code>ctest -V</code> for more verbosity<br />
# Call <code>gr_modtool makexml BLOCKNAME</code> or <code>gr_modtool makeyaml BLOCKNAME</code> to generate the xml or yaml file for your blocks. Correct manually if needed.<br />
# Install (only when everything works and no tests fail): <code>sudo make install</code><br />
# Ubuntu users: reload the libs: <code>sudo ldconfig</code><br />
# Rename a block in the module: <code>gr_modtool rename BLOCKNAME</code>. The command will then prompt the user to enter the new name for the block.<br />
# Delete blocks from the source tree: <code>gr_modtool rm REGEX</code><br />
# Disable blocks by removing them from the CMake files: <code>gr_modtool disable REGEX</code><br />
<br />
== Tutorial 3: Writing a signal processing block in Python ==<br />
<br />
'''Note:''' Writing signal processing blocks in Python comes with a performance penalty. The most common cause for using Python to write blocks is because you want to quickly prototype something without having to argue with C++.<br />
<br />
From the previous tutorials, you already know about blocks and how they work. Lets go through things a bit quicker, and code another squaring block in pure Python, which shall be called <code>square3_ff()</code>.<br />
<br />
=== Adding the test case ===<br />
<br />
So, first of all, we add another test case by editing <code>qa_square_ff.py</code>. Leaving out the test cases for the other two blocks, the QA file now looks like this:<br />
<br />
<pre>from gnuradio import gr, gr_unittest<br />
from gnuradio import blocks<br />
import howto_swig<br />
from square3_ff import square3_ff<br />
<br />
class qa_square_ff (gr_unittest.TestCase):<br />
<br />
def setUp (self):<br />
self.tb = gr.top_block ()<br />
<br />
def tearDown (self):<br />
self.tb = None<br />
<br />
# [...] Skipped the other test cases<br />
<br />
def test_003_square3_ff (self):<br />
src_data = (-3, 4, -5.5, 2, 3)<br />
expected_result = (9, 16, 30.25, 4, 9)<br />
src = blocks.vector_source_f (src_data)<br />
sqr = square3_ff ()<br />
dst = blocks.vector_sink_f ()<br />
self.tb.connect (src, sqr)<br />
self.tb.connect (sqr, dst)<br />
self.tb.run ()<br />
result_data = dst.data ()<br />
self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)<br />
<br />
if __name__ == '__main__':<br />
gr_unittest.main ()</pre><br />
The actual test case looks '''exactly''' like the previous ones did, only replacing the block definition with <code>square3_ff()</code>. The only other difference is in the import statements: We are now importing a module called <code>square3_ff</code> from which we pull the new block.<br />
<br />
=== Adding the block code ===<br />
<br />
Having put the unit test in place, we add a file called <code>square3_ff.py</code> into the <code>python/</code> directory using <code>gr_modtool</code>:<br />
<br />
<pre>gr-howto % gr_modtool add -t sync -l python square3_ff<br />
GNU Radio module name identified: howto<br />
Block/code identifier: square3_ff<br />
Language: Python<br />
Please specify the copyright holder:<br />
Enter valid argument list, including default arguments: <br />
Add Python QA code? [Y/n] n<br />
Adding file 'square3_ff.py'...<br />
Adding file 'howto_square3_ff.xml'...<br />
Editing grc/CMakeLists.txt...</pre><br />
Remember not to add any QA files as we're using the existing one. Next, edit the new file <code>python/square3_ff.py</code>. It should look a bit like this:<br />
<br />
<pre>import numpy<br />
from gnuradio import gr<br />
<br />
class square3_ff(gr.sync_block):<br />
&quot; Squaring block &quot;<br />
def __init__(self):<br />
gr.sync_block.__init__(<br />
self,<br />
name = &quot;square3_ff&quot;,<br />
in_sig = [numpy.float32], # Input signature: 1 float at a time<br />
out_sig = [numpy.float32], # Output signature: 1 float at a time<br />
)<br />
<br />
def work(self, input_items, output_items):<br />
output_items[0][:] = input_items[0] * input_items[0] # Only works because numpy.array<br />
return len(output_items[0])</pre><br />
Some things should immediately stick out:<br />
<br />
* The block class is derived from <code>gr.sync_block</code>, just like the C++ version was derived from gr::sync_block<br />
* It has a constructor where the name and input/output signatures are set and a <code>work()</code> function<br />
<br />
However, there are some major differences to the C++ version:<br />
<br />
* The input and output signatures are simply defined as a list. Every element contains the item size of that port. So in this case, there is one port per input and one port per output and each has an item size of <code>numpy.float32</code> (a single-precision float). If you want a port to operate on vectors, define a tuple, e.g. [(numpy.float32, 4), numpy.float32] means there are two ports: The first one is for vectors of 4 floats, the second is for scalar floats.<br />
* When assigning vectors to <code>output_items</code>, remember to use the <code>[:]</code> operator. This makes sure Python doesn't rebind the variables or does something clever but guarantees that the data is properly copied<br />
* <code>input_items</code> and <code>output_items</code> are numpy arrays, which is why we can do the very simple element-wise multiplication the way it's done here (instead of a list comprehension)<br />
* No recompiling is necessary for the <code>make test</code> (faster development cycles, yay!)<br />
* For Python code, you must modify the XML or YAML file yourself. See [[XML_GRC|Writing the XML file for a block (for 3.7)]] or [[YAML_GRC|Writing the YAML file for a block (for 3.8+)]] for details.<br />
<br />
=== Other types of Python blocks ===<br />
<br />
Just like the C++ variant, there are four types of blocks in Python:<br />
<br />
* <code>gr.sync_block</code><br />
* <code>gr.decim_block</code><br />
* <code>gr.interp_block</code><br />
* <code>gr.basic_block</code> - The Python version of <code>gr::block</code><br />
<br />
Like their C++ versions, these blocks have <code>forecast()</code>, <code>work()</code>, and <code>general_work()</code> methods you can override. The difference is, the argument list for the work functions is always as shown in the previous example:<br />
<br />
<pre> def work(self, input_items, output_items):<br />
# Do stuff<br />
<br />
def general_work(self, input_items, output_items):<br />
# Do stuff</pre><br />
The number of input/output items is obtained through <code>len(input_items[PORT_NUM])</code>.<br />
<br />
=== More examples ===<br />
<br />
Check out the QA code for the Python blocks for some good examples:<br />
<br />
* [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/python/blocks/qa_block_gateway.py gr-blocks/python/blocks/qa_block_gateway.py]<br />
<br />
=== Troubleshooting ===<br />
<br />
<code>ValueError: invalid literal for int() with base 10: '...'</code> This occurs in GRC when attempting to drag a block into a flowgraph. It usually means the <code>.yml</code> file cooresponding to the block located in the <code>grc/</code> folder is not filled out. ("..." is a placeholder).<br />
<br />
<code>Error: invalid literal for int() with base 10: '1 /* max inputs */'</code> This occurs in GRC when attempting to drag a block into the flowgraph. This is caused by <code>multiplicity: 1 /* max inputs */</code> in your <code>.yml</code> file. Removing <code>/* max inputs */</code> will fix the error.</div>N1aihttps://wiki.gnuradio.org/index.php?title=Correlate_Access_Code_-_Tag&diff=12374Correlate Access Code - Tag2022-07-19T20:41:31Z<p>N1ai: Brought over parameter descriptions from deprecated 'Correlate Access Code' block.</p>
<hr />
<div>Examine input for specified access code, one bit at a time.<br />
<br />
This block replaces [[Correlate_Access_Code]].<br />
<br />
input: stream of bits, 1 bit per input byte (data in LSB) output: unaltered stream of bits (plus tags)<br />
<br />
This block annotates the input stream with tags. The tags have key name [tag_name], specified in the constructor. Used for searching an input data stream for preambles, etc.<br />
<br />
== Parameters ==<br />
<b>(''R''):</b> <span class="plainlinks">[https://wiki.gnuradio.org/index.php/GNURadioCompanion#Variable_Controls ''Run-time adjustable'']</span><br />
<br />
; Access Code<br />
: is represented with 1 byte per bit, e.g., <code>010101010111000100</code><br />
<br />
:It is important to choose an access code that is not "cyclical". A cyclical code contains a repetition of itself within the length of the code. This can cause false access code detections, and will cause byte boundaries (and hence valid packet length and payload recovery) to be recovered in error, with bizarre results!<br />
<br />
:For example, an access code of <code>11111111</code> is a poor choice because it is cyclical. Let's look at why this is. <br />
<br />
:The first eight <code>1</code> bits in a row from the block's input stream will correctly detect the first access code. The first packet (in standard form <code><access code> <length1> <length2> <payload></code>) will be recovered nominally. <br />
<br />
:However, when the correlate access code block's state machine goes back into scanning/detection mode after the first packet, the first bit of the NEXT access code (which forms the <code>11111111</code> preamble of the next packet) will cause an immediate access code detection. This is because the access code detection routine within the block always shifts the last detected access code one bit to the left, and ORs in the current input bit.<br />
<br />
:In our example, the valid bits <code>11111111</code> from the first access code detected will get shifted left one bit (to give <code>11111110</code>), and then the next input bit (the first bit after the end of the first packet) will be ORed into the value (<code>11111110 OR 1</code>, giving <code>11111111</code>) resulting in an erroneous detection of the next access code, because <code>11111111</code> will of course match the access code that the block is searching for. <br />
<br />
:This will falsely detect the start of the next packet (alignment off by 7 bits) and your flowgraph will explode with subsequent bad headers and invalid data. Not a pretty sight.<br />
<br />
:So, when choosing an access code, select one that is not cyclical!<br />
<br />
:A possible (and historically interesting) non-cyclical 32-bit access code might be 0xe15ae893:<br />
<br />
:<code>11100001010110101110100010010011</code><br />
<br />
:which was the "unique word" used on several early communications satellites.<br />
<br />
; Threshold (R)<br />
: dtype: int<br />
: maximum number of bits that may be wrong when deciding if the access code matches.<br />
<br />
; Tag Name (R)<br />
: dtype: string<br />
<br />
== Example Flowgraph ==<br />
<br />
== Source Files ==<br />
<br />
; C++ files<br />
: [https://github.com/gnuradio/gnuradio TODO]<br />
<br />
; Header files<br />
: [https://github.com/gnuradio/gnuradio TODO]<br />
<br />
; Public header files<br />
: [https://github.com/gnuradio/gnuradio TODO]<br />
<br />
; Block definition<br />
: [https://github.com/gnuradio/gnuradio TODO]<br />
<br />
[[Category:Block Docs]]</div>N1aihttps://wiki.gnuradio.org/index.php?title=User:N1ai&diff=12373User:N1ai2022-07-19T18:25:55Z<p>N1ai: </p>
<hr />
<div>[https://www.mediawiki.org/wiki/Help:Formatting external formatting help]<br />
<br />
[[Tutorials|internal tutorials]]<br />
<br />
<br />
* Start each line<br />
* with an [[Wikipedia:asterisk|asterisk]] (*).<br />
** More asterisks give deeper<br />
*** and deeper levels.<br />
* Line breaks <br />don't break levels.<br />
*** But jumping levels creates empty space.<br />
Any other start ends the list.<br />
<br />
* combine bullet list<br />
** with definition <br />
::- definition<br />
** creates empty space<br />
<br />
* combine bullet list<br />
** with definition <br />
*:- definition<br />
** without empty spaces<br />
<br />
*bullet list<br />
:- definition<br />
:* sublist that doesn't create empty<br />
:* spaces after definition</div>N1aihttps://wiki.gnuradio.org/index.php?title=User:N1ai&diff=12372User:N1ai2022-07-19T18:07:57Z<p>N1ai: Created page with "* Start each line * with an asterisk (*). ** More asterisks give deeper *** and deeper levels. * Line breaks <br />don't break levels. *** But jumping levels creates empty space. Any other start ends the list. * combine bullet list ** with definition ::- definition ** creates empty space * combine bullet list ** with definition *:- definition ** without empty spaces *bullet list :- definition :* sublist that doesn't create empty :* spaces afte..."</p>
<hr />
<div>* Start each line<br />
* with an [[Wikipedia:asterisk|asterisk]] (*).<br />
** More asterisks give deeper<br />
*** and deeper levels.<br />
* Line breaks <br />don't break levels.<br />
*** But jumping levels creates empty space.<br />
Any other start ends the list.<br />
<br />
* combine bullet list<br />
** with definition <br />
::- definition<br />
** creates empty space<br />
<br />
* combine bullet list<br />
** with definition <br />
*:- definition<br />
** without empty spaces<br />
<br />
*bullet list<br />
:- definition<br />
:* sublist that doesn't create empty<br />
:* spaces after definition</div>N1ai