Editing Guided Tutorial GNU Radio in Python

Jump to: navigation, search

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
[[Category:Guided Tutorials]]
+
<. [[Guided_Tutorial_GRC|Previous: Working with GRC]]
 +
>. [[Guided_Tutorial_GNU_Radio_in_C++|Next: Programming GNU Radio in C++]]
 +
 
  
== <b>NOTE:</b> This tutorial has been deprecated in GR 3.8. ==
+
= Tutorial: Working with GNU Radio in Python =
  
 
== Objectives ==
 
== Objectives ==
Line 14: Line 16:
 
== Prerequisites ==
 
== Prerequisites ==
  
* Working Installation of GNU Radio 3.7
+
* Working Installation of GNU Radio 3.7.4 or later
 
* [[Guided_Tutorial_GRC|GRC Tutorial]] (Recommended)
 
* [[Guided_Tutorial_GRC|GRC Tutorial]] (Recommended)
 
* Familiar with Python
 
* Familiar with Python
 +
  
 
-----
 
-----
Line 22: Line 25:
 
== 3.1. Intro to Using GNU Radio with Python ==
 
== 3.1. Intro to Using GNU Radio with Python ==
  
This tutorial goes through three parts. The first is how to modify, create, or simply understand the Python generated files GRC produces for us. The second is how to create our own custom out-of-tree (OOT) modules from the ground up. Lastly we go through an actual project to get more practice and build intuition on how we can use GNU Radio in our own project. As with the last tutorial, all the content - pictures, source code, and grc files - is included in the [https://github.com/gnuradio/gr-tutorial gr-tutorial repository] which we should have a local copy if we followed the directions from the [[Guided_Tutorial_GRC|GRC Tutorial]]
+
This tutorial goes through three parts. The first is how to modify, create, or simply understand the Python generated files GRC produces for us. The second is how to create our own custom out-of-tree (OOT) modules from the ground up. Lastly we go through an actual project to get more practice and build intuition on how we can use GNU Radio in our own project. As with the last tutorial, all the content - pictures, source code, and grc files - is included in the [https://github.com/gnuradio/gr-tutorial gr-tutorial repository] which we should have a local copy if we followed the directions from the [http://gnuradio.org/redmine/projects/gnuradio/wiki/NewTutorials2#Part-I-Setting-up-the-Tutorials GRC Tutorial]
  
 
Again we should have a directory with the solutions and a directory with our work as below:
 
Again we should have a directory with the solutions and a directory with our work as below:
Line 31: Line 34:
 
/home/user/gnuradio/tutorials/work
 
/home/user/gnuradio/tutorials/work
 
</syntaxhighlight>
 
</syntaxhighlight>
 
 
As a rule, if we hover over the GRC flowgraph images, we will be able to see the corresponding filename. Same applies for other images. Full code files are collapsed with the filename in the collapsed handle.
 
As a rule, if we hover over the GRC flowgraph images, we will be able to see the corresponding filename. Same applies for other images. Full code files are collapsed with the filename in the collapsed handle.
  
Line 44: Line 46:
  
 
<syntaxhighlight lang="python" line="line">
 
<syntaxhighlight lang="python" line="line">
#!/usr/bin/env python3
+
#!/usr/bin/env Python
# -*- coding: utf-8 -*-
+
##################################################
 
+
# Gnuradio Python Flow Graph
#
+
# Title: Tutorial Three
# SPDX-License-Identifier: GPL-3.0
+
# Generated: Wed Mar 12 15:35:18 2014
#
+
##################################################
# GNU Radio Python Flow Graph
 
# Title: tutorial_three_1
 
# GNU Radio version: 3.8.0.0
 
 
 
from distutils.version import StrictVersion
 
 
 
if __name__ == '__main__':
 
    import ctypes
 
    import sys
 
    if sys.platform.startswith('linux'):
 
        try:
 
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
 
            x11.XInitThreads()
 
        except:
 
            print("Warning: failed to XInitThreads()")
 
  
 
from gnuradio import analog
 
from gnuradio import analog
 
from gnuradio import audio
 
from gnuradio import audio
 +
from gnuradio import eng_notation
 
from gnuradio import gr
 
from gnuradio import gr
 +
from gnuradio.eng_option import eng_option
 
from gnuradio.filter import firdes
 
from gnuradio.filter import firdes
import sys
+
from optparse import OptionParser
import signal
 
from PyQt5 import Qt
 
from argparse import ArgumentParser
 
from gnuradio.eng_arg import eng_float, intx
 
from gnuradio import eng_notation
 
from gnuradio import qtgui
 
  
class tutorial_three_1(gr.top_block, Qt.QWidget):
+
class tutorial_three(gr.top_block):
  
 
     def __init__(self):
 
     def __init__(self):
         gr.top_block.__init__(self, "tutorial_three_1")
+
         gr.top_block.__init__(self, &quot;Tutorial Three&quot;)
        Qt.QWidget.__init__(self)
 
        self.setWindowTitle("tutorial_three_1")
 
        qtgui.util.check_set_qss()
 
        try:
 
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
 
        except:
 
            pass
 
        self.top_scroll_layout = Qt.QVBoxLayout()
 
        self.setLayout(self.top_scroll_layout)
 
        self.top_scroll = Qt.QScrollArea()
 
        self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
 
        self.top_scroll_layout.addWidget(self.top_scroll)
 
        self.top_scroll.setWidgetResizable(True)
 
        self.top_widget = Qt.QWidget()
 
        self.top_scroll.setWidget(self.top_widget)
 
        self.top_layout = Qt.QVBoxLayout(self.top_widget)
 
        self.top_grid_layout = Qt.QGridLayout()
 
        self.top_layout.addLayout(self.top_grid_layout)
 
 
 
        self.settings = Qt.QSettings("GNU Radio", "tutorial_three_1")
 
 
 
        try:
 
            if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
 
                self.restoreGeometry(self.settings.value("geometry").toByteArray())
 
            else:
 
                self.restoreGeometry(self.settings.value("geometry"))
 
        except:
 
            pass
 
  
 
         ##################################################
 
         ##################################################
Line 119: Line 74:
 
         # Blocks
 
         # Blocks
 
         ##################################################
 
         ##################################################
         self.audio_sink_0 = audio.sink(samp_rate, '', True)
+
         self.audio_sink_0 = audio.sink(samp_rate, &quot;&quot;, True)
         self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 350, 0.1, 0, 0)
+
         self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_SIN_WAVE, 350, .1, 0)
         self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 440, 0.1, 0, 0)
+
         self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_SIN_WAVE, 440, .1, 0)
 
 
 
 
  
 
         ##################################################
 
         ##################################################
 
         # Connections
 
         # Connections
 
         ##################################################
 
         ##################################################
 +
        self.connect((self.analog_sig_source_x_1, 0), (self.audio_sink_0, 1))
 
         self.connect((self.analog_sig_source_x_0, 0), (self.audio_sink_0, 0))
 
         self.connect((self.analog_sig_source_x_0, 0), (self.audio_sink_0, 0))
        self.connect((self.analog_sig_source_x_1, 0), (self.audio_sink_0, 1))
 
  
    def closeEvent(self, event):
+
 
        self.settings = Qt.QSettings("GNU Radio", "tutorial_three_1")
+
# QT sink close method reimplementation
        self.settings.setValue("geometry", self.saveGeometry())
 
        event.accept()
 
  
 
     def get_samp_rate(self):
 
     def get_samp_rate(self):
Line 144: Line 95:
 
         self.analog_sig_source_x_1.set_sampling_freq(self.samp_rate)
 
         self.analog_sig_source_x_1.set_sampling_freq(self.samp_rate)
  
 
+
if __name__ == '__main__':
 
+
     parser = OptionParser(option_class=eng_option, usage=&quot;%prog: [options]&quot;)
def main(top_block_cls=tutorial_three_1, options=None):
+
    (options, args) = parser.parse_args()
 
+
     tb = tutorial_three()
     if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
 
        style = gr.prefs().get_string('qtgui', 'style', 'raster')
 
        Qt.QApplication.setGraphicsSystem(style)
 
    qapp = Qt.QApplication(sys.argv)
 
 
 
     tb = top_block_cls()
 
 
     tb.start()
 
     tb.start()
     tb.show()
+
     raw_input('Press Enter to quit: ')
 
+
     tb.stop()
    def sig_handler(sig=None, frame=None):
+
    tb.wait()
        Qt.QApplication.quit()
 
 
 
    signal.signal(signal.SIGINT, sig_handler)
 
    signal.signal(signal.SIGTERM, sig_handler)
 
 
 
    timer = Qt.QTimer()
 
    timer.start(500)
 
    timer.timeout.connect(lambda: None)
 
 
 
     def quitting():
 
        tb.stop()
 
        tb.wait()
 
    qapp.aboutToQuit.connect(quitting)
 
    qapp.exec_()
 
 
 
 
 
if __name__ == '__main__':
 
    main()
 
 
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The first thing for us to realize is that the GRC can generate Python files that we can then modify to do things we wouldn't be able to do in GNU Radio Companion such as perform [[TutorialsSimulations|simulations]]. The libraries available in Python open up a whole new realm of possibilities! For now, we will explore the structure of the GRC Python files so we are comfortable creating more interesting applications.
+
The first thing for us to realize is that the GRC can generate Python files that we can then modify to do things we wouldn't be able to do in GNU Radio Companion such as perform [http://gnuradio.org/redmine/projects/gnuradio/wiki/Simulations simulations]. The libraries available in Python open up a whole new realm of possibilities! For now, we will explore the structure of the GRC Python files so we are comfortable creating more interesting applications.
  
 
=== 3.1.2. Hello World Dissected ===
 
=== 3.1.2. Hello World Dissected ===
  
 
While examining the code, we need to get familiar with documentation. GNU Radio uses Doxygen (the software) for the [http://gnuradio.org/doc/doxygen/ GNU Radio Manual]. The easiest way to go through the documentation is to go through the functions that we use so let us simplify our code by only including the bare bones needed to run the dial-tone example.
 
While examining the code, we need to get familiar with documentation. GNU Radio uses Doxygen (the software) for the [http://gnuradio.org/doc/doxygen/ GNU Radio Manual]. The easiest way to go through the documentation is to go through the functions that we use so let us simplify our code by only including the bare bones needed to run the dial-tone example.
 +
 +
{{collapse(hello_world.py)
  
 
<syntaxhighlight lang="python" line="line">
 
<syntaxhighlight lang="python" line="line">
#!/usr/bin/env Python3
+
#!/usr/bin/env Python
 
from gnuradio import gr
 
from gnuradio import gr
 
from gnuradio import audio
 
from gnuradio import audio
Line 200: Line 128:
 
         src0 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 350, ampl)
 
         src0 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 350, ampl)
 
         src1 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 440, ampl)
 
         src1 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 440, ampl)
         dst = audio.sink(sample_rate, "")
+
         dst = audio.sink(sample_rate, &quot;&quot;)
 
         self.connect(src0, (dst, 0))
 
         self.connect(src0, (dst, 0))
 
         self.connect(src1, (dst, 1))
 
         self.connect(src1, (dst, 1))
Line 209: Line 137:
 
     except [[KeyboardInterrupt]]:
 
     except [[KeyboardInterrupt]]:
 
         pass
 
         pass
 
+
<\syntaxhighlight>
</syntaxhighlight>
+
}}
  
 
Let us examine this line by line:
 
Let us examine this line by line:
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
#!/usr/bin/env python3
+
#!/usr/bin/env python
</syntaxhighlight>
+
#<\syntaxhighlight>
 
Tells the shell that this file is a Python file and to use the Python interpreter to run this file. Should always be included at the top to run from the terminal.
 
Tells the shell that this file is a Python file and to use the Python interpreter to run this file. Should always be included at the top to run from the terminal.
  
Line 223: Line 151:
 
from gnuradio import audio
 
from gnuradio import audio
 
from gnuradio import analog
 
from gnuradio import analog
</syntaxhighlight>
+
<\syntaxhighlight>
  
 
Tells Python the modules to include. We must always have '''gr''' to run GNU Radio applications. The audio sink is included in the audio module and the signal_source_f is included in the analog module which is why we include them. [http://legacy.python.org/dev/peps/pep-0008/ PEP8] tells us we should import every module on its own line.
 
Tells Python the modules to include. We must always have '''gr''' to run GNU Radio applications. The audio sink is included in the audio module and the signal_source_f is included in the analog module which is why we include them. [http://legacy.python.org/dev/peps/pep-0008/ PEP8] tells us we should import every module on its own line.
Line 229: Line 157:
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
class my_top_block(gr.top_block):
 
class my_top_block(gr.top_block):
</syntaxhighlight>
+
<\syntaxhighlight>
Define a class called "my_top_block" which is derived from another class, '''gr.top_block'''. This class is basically a container for the flow graph. By deriving from gr.top_block, we get all the hooks and functions we need to add blocks and interconnect them.
+
Define a class called &quot;my_top_block&quot; which is derived from another class, '''gr.top_block'''. This class is basically a container for the flow graph. By deriving from gr.top_block, we get all the hooks and functions we need to add blocks and interconnect them.
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
def __init__(self):
 
def __init__(self):
</syntaxhighlight>
+
<\syntaxhighlight>
Only one member function is defined for this class: the function "''init''()", which is the constructor of this class.
+
Only one member function is defined for this class: the function &quot;''init''()&quot;, which is the constructor of this class.
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
gr.top_block.__init__(self)
 
gr.top_block.__init__(self)
</syntaxhighlight>
+
<\syntaxhighlight>
 
The parent constructor is called (in Python, this needs to be done explicitly. Most things in Python need to be done explicitly; in fact, this is one main Python principle).
 
The parent constructor is called (in Python, this needs to be done explicitly. Most things in Python need to be done explicitly; in fact, this is one main Python principle).
  
Line 245: Line 173:
 
sample_rate = 32000
 
sample_rate = 32000
 
ampl = 0.1
 
ampl = 0.1
</syntaxhighlight>
+
<\syntaxhighlight>
 
Variable declarations for sampling rate and amplitude that we will later use.
 
Variable declarations for sampling rate and amplitude that we will later use.
  
Line 253: Line 181:
 
src0 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 350, ampl)
 
src0 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 350, ampl)
 
src1 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 440, ampl)
 
src1 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 440, ampl)
</syntaxhighlight>
+
<\syntaxhighlight>
 
Here we are using functions from GNU Radio so let's have a look at the documentation for '''analog.sig_source_f''' which is available in [http://gnuradio.org/doc/doxygen/classgr_1_1analog_1_1sig__source__f.html the GNU Radio manual]. We can find it easily by using the search function as below:
 
Here we are using functions from GNU Radio so let's have a look at the documentation for '''analog.sig_source_f''' which is available in [http://gnuradio.org/doc/doxygen/classgr_1_1analog_1_1sig__source__f.html the GNU Radio manual]. We can find it easily by using the search function as below:
  
 
+
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_search.png|400px|hello_search.png]]
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_search.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_search.png
  
 
We can then scroll down to '''Member Function Documentation''' to see how the function is used and the parameters it accepts as below:
 
We can then scroll down to '''Member Function Documentation''' to see how the function is used and the parameters it accepts as below:
  
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_source.png|400px|hello_source.png]]
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_source.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_source.png
  
We can see that our function '''analog.sig_source_f''' takes in 5 parameters but in our code we are only using 4. There is no error because the last input '''offset''' is set to "0" by default as shown in the documentation. The first input is the '''sampling_freq''' which we defined as '''sample_rate''' in our code. The second input is asking for a '''gr::analog::gr_waveform_t''' waveform so let's click that [http://gnuradio.org/doc/doxygen/group__waveform__generators__blk.html#gac97c0f42ffb63f1265decceaaeab9177 link] to find out more.
+
We can see that our function '''analog.sig_source_f''' takes in 5 parameters but in our code we are only using 4. There is no error because the last input '''offset''' is set to &quot;0&quot; by default as shown in the documentation. The first input is the '''sampling_freq''' which we defined as '''sample_rate''' in our code. The second input is asking for a '''gr::analog::gr_waveform_t''' waveform so let's click that [http://gnuradio.org/doc/doxygen/group__waveform__generators__blk.html#gac97c0f42ffb63f1265decceaaeab9177 link] to find out more.
  
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_waveform.png|200px|hello_waveform.png]]
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_waveform.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_waveform.png
  
We can see that there are a couple of options to choose from. In this case we chose '''analog.GR_SIN_WAVE'''. The third input is '''wave_freq''' which we input "350" or "440". The fourth input is '''ampl''' which we defined as '''ampl'''.
+
We can see that there are a couple of options to choose from. In this case we chose '''analog.GR_SIN_WAVE'''. The third input is '''wave_freq''' which we input &quot;350&quot; or &quot;440&quot;. The fourth input is '''ampl''' which we defined as '''ampl'''.
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
dst = audio.sink(sample_rate, "")
+
dst = audio.sink(sample_rate, &quot;&quot;)
</syntaxhighlight>
+
<\syntaxhighlight>
 
Because documentation is so important, let's look at another example. Again, we can look at the documentation for '''audio.sink''' which is available on the GNU Radio Manual through the search function:
 
Because documentation is so important, let's look at another example. Again, we can look at the documentation for '''audio.sink''' which is available on the GNU Radio Manual through the search function:
  
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_sink.png|400px|hello_sink.png]]
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_sink.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_sink.png
  
Line 280: Line 211:
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_sink_detail.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/hello_sink_detail.png
  
This time we have 3 inputs with the last being optional. In our code, for the first input '''sampling_rate''' we used are '''sample_rate''' variable. In the second input, we have a choice for what device to use for audio output. If we leave it alone as "" then it'll choose the default on our machine.
+
This time we have 3 inputs with the last being optional. In our code, for the first input '''sampling_rate''' we used are '''sample_rate''' variable. In the second input, we have a choice for what device to use for audio output. If we leave it alone as &quot;&quot; then it'll choose the default on our machine.
  
 
=== 3.1.4. Connecting the Block Together ===
 
=== 3.1.4. Connecting the Block Together ===
Line 287: Line 218:
 
self.connect(src0, (dst, 0))
 
self.connect(src0, (dst, 0))
 
self.connect(src1, (dst, 1))
 
self.connect(src1, (dst, 1))
</syntaxhighlight>
+
<\syntaxhighlight>
 
The general syntax for connecting blocks is self.connect(block1, block2, block3, ...) which would connect the output of block1 with the input of block2, the output of block2 with the input of block3 and so on. We can connect as many blocks as we wish with one connect() call. However this only work when there is a one-to-one correspondence. If we go back to our initial flowgraph, there are 2 inputs to the '''Audio Sink''' block. The way to connect them is by using the syntax above. The first line connects the only output of src0 (350 Hz waveform) to the first input of dst (Audio Sink). The second line connects the only output of src1 (440 Hz waveform) to the second input of dst (Audio Sink). The code so far is equivalent to the flowgraph we have created in the beginning; the rest of the lines simply start the flowgraph and provide a keyboard interrupt.
 
The general syntax for connecting blocks is self.connect(block1, block2, block3, ...) which would connect the output of block1 with the input of block2, the output of block2 with the input of block3 and so on. We can connect as many blocks as we wish with one connect() call. However this only work when there is a one-to-one correspondence. If we go back to our initial flowgraph, there are 2 inputs to the '''Audio Sink''' block. The way to connect them is by using the syntax above. The first line connects the only output of src0 (350 Hz waveform) to the first input of dst (Audio Sink). The second line connects the only output of src1 (440 Hz waveform) to the second input of dst (Audio Sink). The code so far is equivalent to the flowgraph we have created in the beginning; the rest of the lines simply start the flowgraph and provide a keyboard interrupt.
  
Line 296: Line 227:
 
     except KeyboardInterrupt:
 
     except KeyboardInterrupt:
 
         pass
 
         pass
</syntaxhighlight>
+
<\syntaxhighlight>
  
 
Luckily we are past the early years of GNU Radio when there was no GRC to make the Python files for us. Nowadays we can simply click things together in GRC instead of having to write code in Python to build flowgraphs. Still, a good understanding of what is going on every time we run GRC is good to know as it gives us more control of what we want the program to do.
 
Luckily we are past the early years of GNU Radio when there was no GRC to make the Python files for us. Nowadays we can simply click things together in GRC instead of having to write code in Python to build flowgraphs. Still, a good understanding of what is going on every time we run GRC is good to know as it gives us more control of what we want the program to do.
Line 306: Line 237:
 
Our friend heard we were into RF so being cheap he has asked us to power his house using RF. He wants us to give him high power during the day so he can watch TV and play video games while at night give him low power so he power his alarm clock to wake up for work in the morning. We first need to setup the clock which keeps track of the day and gives us 1 for day or 0 for night. Once we get the time we can send him power through a sine wave using our massive terawatt amplifier and massive dish in our backyard. We did the calculations and we want to pulse at 1kHz, 1 amplitude during the day and 100Hz, 0.3 amplitude at night. Here's what we came up with in GRC:
 
Our friend heard we were into RF so being cheap he has asked us to power his house using RF. He wants us to give him high power during the day so he can watch TV and play video games while at night give him low power so he power his alarm clock to wake up for work in the morning. We first need to setup the clock which keeps track of the day and gives us 1 for day or 0 for night. Once we get the time we can send him power through a sine wave using our massive terawatt amplifier and massive dish in our backyard. We did the calculations and we want to pulse at 1kHz, 1 amplitude during the day and 100Hz, 0.3 amplitude at night. Here's what we came up with in GRC:
  
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/if_else.png|400px|if_else.grc]]
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/if_else.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/if_else.png
  
<span style="color:blue">- '''Frequency''' to "freq", '''Amplitude''' to "ampl"</span><br />
+
<span style="color:blue">- '''Frequency''' to &quot;freq&quot;, '''Amplitude''' to &quot;ampl&quot;</span><br />
<span style="color:red">- '''ID''' to "probe"</span><br />
+
<span style="color:red">- '''ID''' to &quot;probe&quot;</span><br />
 
- Everything else is visible
 
- Everything else is visible
  
Line 317: Line 249:
  
 
Now we can open up the GRC generated python file if_else.py which is copied below:
 
Now we can open up the GRC generated python file if_else.py which is copied below:
 +
 +
{{collapse(if_else.py)
  
 
<syntaxhighlight lang="python" line="line">
 
<syntaxhighlight lang="python" line="line">
#!/usr/bin/env python3
+
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+
##################################################
 +
# Gnuradio Python Flow Graph
 +
# Title: Top Block
 +
# Generated: Wed Jul  2 01:39:19 2014
 +
##################################################
  
#
+
from PyQt4 import Qt
# SPDX-License-Identifier: GPL-3.0
+
from gnuradio import analog
#
+
from gnuradio import blocks
# GNU Radio Python Flow Graph
 
# Title: if_else
 
# GNU Radio version: 3.8.0.0
 
 
 
from distutils.version import StrictVersion
 
 
 
if __name__ == '__main__':
 
    import ctypes
 
    import sys
 
    if sys.platform.startswith('linux'):
 
        try:
 
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
 
            x11.XInitThreads()
 
        except:
 
            print("Warning: failed to XInitThreads()")
 
 
 
from PyQt5 import Qt
 
 
from gnuradio import eng_notation
 
from gnuradio import eng_notation
 +
from gnuradio import gr
 
from gnuradio import qtgui
 
from gnuradio import qtgui
 +
from gnuradio.eng_option import eng_option
 
from gnuradio.filter import firdes
 
from gnuradio.filter import firdes
 +
from optparse import OptionParser
 
import sip
 
import sip
from gnuradio import analog
 
from gnuradio import blocks
 
from gnuradio import gr
 
 
import sys
 
import sys
import signal
+
import threading
from argparse import ArgumentParser
 
from gnuradio.eng_arg import eng_float, intx
 
 
import time
 
import time
import threading
 
from gnuradio import qtgui
 
  
class if_else(gr.top_block, Qt.QWidget):
+
class top_block(gr.top_block, Qt.QWidget):
  
 
     def __init__(self):
 
     def __init__(self):
         gr.top_block.__init__(self, "if_else")
+
         gr.top_block.__init__(self, &quot;Top Block&quot;)
 
         Qt.QWidget.__init__(self)
 
         Qt.QWidget.__init__(self)
         self.setWindowTitle("if_else")
+
         self.setWindowTitle(&quot;Top Block&quot;)
        qtgui.util.check_set_qss()
 
 
         try:
 
         try:
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
+
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
 
         except:
 
         except:
            pass
+
            pass
 
         self.top_scroll_layout = Qt.QVBoxLayout()
 
         self.top_scroll_layout = Qt.QVBoxLayout()
 
         self.setLayout(self.top_scroll_layout)
 
         self.setLayout(self.top_scroll_layout)
Line 380: Line 296:
 
         self.top_layout.addLayout(self.top_grid_layout)
 
         self.top_layout.addLayout(self.top_grid_layout)
  
         self.settings = Qt.QSettings("GNU Radio", "if_else")
+
         self.settings = Qt.QSettings(&quot;GNU Radio&quot;, &quot;top_block&quot;)
 +
        self.restoreGeometry(self.settings.value(&quot;geometry&quot;).toByteArray())
  
        try:
 
            if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
 
                self.restoreGeometry(self.settings.value("geometry").toByteArray())
 
            else:
 
                self.restoreGeometry(self.settings.value("geometry"))
 
        except:
 
            pass
 
  
 
         ##################################################
 
         ##################################################
Line 403: Line 313:
 
         self.probe = blocks.probe_signal_f()
 
         self.probe = blocks.probe_signal_f()
 
         self._freq_tool_bar = Qt.QToolBar(self)
 
         self._freq_tool_bar = Qt.QToolBar(self)
         self._freq_tool_bar.addWidget(Qt.QLabel('freq' + ": "))
+
         self._freq_tool_bar.addWidget(Qt.QLabel(&quot;freq&quot;+&quot;: &quot;))
 
         self._freq_line_edit = Qt.QLineEdit(str(self.freq))
 
         self._freq_line_edit = Qt.QLineEdit(str(self.freq))
 
         self._freq_tool_bar.addWidget(self._freq_line_edit)
 
         self._freq_tool_bar.addWidget(self._freq_line_edit)
 
         self._freq_line_edit.returnPressed.connect(
 
         self._freq_line_edit.returnPressed.connect(
             lambda: self.set_freq(int(str(self._freq_line_edit.text()))))
+
             lambda: self.set_freq(int(self._freq_line_edit.text().toAscii())))
         self.top_grid_layout.addWidget(self._freq_tool_bar)
+
         self.top_layout.addWidget(self._freq_tool_bar)
 
         self._ampl_tool_bar = Qt.QToolBar(self)
 
         self._ampl_tool_bar = Qt.QToolBar(self)
         self._ampl_tool_bar.addWidget(Qt.QLabel('ampl' + ": "))
+
         self._ampl_tool_bar.addWidget(Qt.QLabel(&quot;ampl&quot;+&quot;: &quot;))
 
         self._ampl_line_edit = Qt.QLineEdit(str(self.ampl))
 
         self._ampl_line_edit = Qt.QLineEdit(str(self.ampl))
 
         self._ampl_tool_bar.addWidget(self._ampl_line_edit)
 
         self._ampl_tool_bar.addWidget(self._ampl_line_edit)
 
         self._ampl_line_edit.returnPressed.connect(
 
         self._ampl_line_edit.returnPressed.connect(
             lambda: self.set_ampl(int(str(self._ampl_line_edit.text()))))
+
             lambda: self.set_ampl(int(self._ampl_line_edit.text().toAscii())))
         self.top_grid_layout.addWidget(self._ampl_tool_bar)
+
         self.top_layout.addWidget(self._ampl_tool_bar)
 
         def _variable_function_probe_0_probe():
 
         def _variable_function_probe_0_probe():
 
             while True:
 
             while True:
 
+
                 val = self.probe.level()
                 val = self.probe.get_number()
 
 
                 try:
 
                 try:
 
                     self.set_variable_function_probe_0(val)
 
                     self.set_variable_function_probe_0(val)
Line 428: Line 337:
 
         _variable_function_probe_0_thread.daemon = True
 
         _variable_function_probe_0_thread.daemon = True
 
         _variable_function_probe_0_thread.start()
 
         _variable_function_probe_0_thread.start()
 
 
         self.qtgui_time_sink_x_0 = qtgui.time_sink_f(
 
         self.qtgui_time_sink_x_0 = qtgui.time_sink_f(
 
             1024, #size
 
             1024, #size
 
             samp_rate, #samp_rate
 
             samp_rate, #samp_rate
             "", #name
+
             &quot;QT GUI Plot&quot;, #name
 
             1 #number of inputs
 
             1 #number of inputs
 
         )
 
         )
 
         self.qtgui_time_sink_x_0.set_update_time(0.10)
 
         self.qtgui_time_sink_x_0.set_update_time(0.10)
 
         self.qtgui_time_sink_x_0.set_y_axis(-1, 1)
 
         self.qtgui_time_sink_x_0.set_y_axis(-1, 1)
 
+
         self.qtgui_time_sink_x_0.enable_tags(-1, True)
         self.qtgui_time_sink_x_0.set_y_label('Amplitude', "")
+
         self.qtgui_time_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, &quot;&quot;)
 
 
        self.qtgui_time_sink_x_0.enable_tags(True)
 
         self.qtgui_time_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "")
 
 
         self.qtgui_time_sink_x_0.enable_autoscale(False)
 
         self.qtgui_time_sink_x_0.enable_autoscale(False)
        self.qtgui_time_sink_x_0.enable_grid(False)
 
        self.qtgui_time_sink_x_0.enable_axis_labels(True)
 
        self.qtgui_time_sink_x_0.enable_control_panel(False)
 
        self.qtgui_time_sink_x_0.enable_stem_plot(False)
 
  
 
+
         labels = [&quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;,
         labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5',
+
                  &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;]
            'Signal 6', 'Signal 7', 'Signal 8', 'Signal 9', 'Signal 10']
 
 
         widths = [1, 1, 1, 1, 1,
 
         widths = [1, 1, 1, 1, 1,
            1, 1, 1, 1, 1]
+
                  1, 1, 1, 1, 1]
         colors = ['blue', 'red', 'green', 'black', 'cyan',
+
         colors = [&quot;blue&quot;, &quot;red&quot;, &quot;green&quot;, &quot;black&quot;, &quot;cyan&quot;,
            'magenta', 'yellow', 'dark red', 'dark green', 'dark blue']
+
                  &quot;magenta&quot;, &quot;yellow&quot;, &quot;dark red&quot;, &quot;dark green&quot;, &quot;blue&quot;]
        alphas = [1.0, 1.0, 1.0, 1.0, 1.0,
 
            1.0, 1.0, 1.0, 1.0, 1.0]
 
 
         styles = [1, 1, 1, 1, 1,
 
         styles = [1, 1, 1, 1, 1,
            1, 1, 1, 1, 1]
+
                  1, 1, 1, 1, 1]
 
         markers = [-1, -1, -1, -1, -1,
 
         markers = [-1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1]
+
                  -1, -1, -1, -1, -1]
 +
        alphas = [1.0, 1.0, 1.0, 1.0, 1.0,
 +
                  1.0, 1.0, 1.0, 1.0, 1.0]
  
 
+
         for i in xrange(1):
         for i in range(1):
 
 
             if len(labels[i]) == 0:
 
             if len(labels[i]) == 0:
                 self.qtgui_time_sink_x_0.set_line_label(i, "Data {0}".format(i))
+
                 self.qtgui_time_sink_x_0.set_line_label(i, &quot;Data {0}&quot;.format(i))
 
             else:
 
             else:
 
                 self.qtgui_time_sink_x_0.set_line_label(i, labels[i])
 
                 self.qtgui_time_sink_x_0.set_line_label(i, labels[i])
Line 475: Line 374:
  
 
         self._qtgui_time_sink_x_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0.pyqwidget(), Qt.QWidget)
 
         self._qtgui_time_sink_x_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0.pyqwidget(), Qt.QWidget)
         self.top_grid_layout.addWidget(self._qtgui_time_sink_x_0_win)
+
         self.top_layout.addWidget(self._qtgui_time_sink_x_0_win)
 
         self.blocks_throttle_0 = blocks.throttle(gr.sizeof_float*1, samp_rate,True)
 
         self.blocks_throttle_0 = blocks.throttle(gr.sizeof_float*1, samp_rate,True)
         self.analog_sig_source_x_0_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, freq, ampl, 0, 0)
+
         self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_SIN_WAVE, freq, ampl, 0)
         self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_SQR_WAVE, 0.1, 1, 0, 0)
+
         self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_SQR_WAVE, 0.1, 1, 0)
 
 
 
 
  
 
         ##################################################
 
         ##################################################
Line 486: Line 383:
 
         ##################################################
 
         ##################################################
 
         self.connect((self.analog_sig_source_x_0, 0), (self.blocks_throttle_0, 0))
 
         self.connect((self.analog_sig_source_x_0, 0), (self.blocks_throttle_0, 0))
        self.connect((self.analog_sig_source_x_0_0, 0), (self.qtgui_time_sink_x_0, 0))
 
 
         self.connect((self.blocks_throttle_0, 0), (self.probe, 0))
 
         self.connect((self.blocks_throttle_0, 0), (self.probe, 0))
 +
        self.connect((self.analog_sig_source_x_1, 0), (self.qtgui_time_sink_x_0, 0))
  
 +
 +
# QT sink close method reimplementation
 
     def closeEvent(self, event):
 
     def closeEvent(self, event):
         self.settings = Qt.QSettings("GNU Radio", "if_else")
+
         self.settings = Qt.QSettings(&quot;GNU Radio&quot;, &quot;top_block&quot;)
         self.settings.setValue("geometry", self.saveGeometry())
+
         self.settings.setValue(&quot;geometry&quot;, self.saveGeometry())
 
         event.accept()
 
         event.accept()
  
Line 505: Line 404:
 
     def set_samp_rate(self, samp_rate):
 
     def set_samp_rate(self, samp_rate):
 
         self.samp_rate = samp_rate
 
         self.samp_rate = samp_rate
 +
        self.blocks_throttle_0.set_sample_rate(self.samp_rate)
 
         self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate)
 
         self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate)
        self.analog_sig_source_x_0_0.set_sampling_freq(self.samp_rate)
 
        self.blocks_throttle_0.set_sample_rate(self.samp_rate)
 
 
         self.qtgui_time_sink_x_0.set_samp_rate(self.samp_rate)
 
         self.qtgui_time_sink_x_0.set_samp_rate(self.samp_rate)
 +
        self.analog_sig_source_x_1.set_sampling_freq(self.samp_rate)
  
 
     def get_freq(self):
 
     def get_freq(self):
Line 515: Line 414:
 
     def set_freq(self, freq):
 
     def set_freq(self, freq):
 
         self.freq = freq
 
         self.freq = freq
         Qt.QMetaObject.invokeMethod(self._freq_line_edit, "setText", Qt.Q_ARG("QString", str(self.freq)))
+
         Qt.QMetaObject.invokeMethod(self._freq_line_edit, &quot;setText&quot;, Qt.Q_ARG(&quot;QString&quot;, str(self.freq)))
         self.analog_sig_source_x_0_0.set_frequency(self.freq)
+
         self.analog_sig_source_x_1.set_frequency(self.freq)
  
 
     def get_ampl(self):
 
     def get_ampl(self):
Line 523: Line 422:
 
     def set_ampl(self, ampl):
 
     def set_ampl(self, ampl):
 
         self.ampl = ampl
 
         self.ampl = ampl
         Qt.QMetaObject.invokeMethod(self._ampl_line_edit, "setText", Qt.Q_ARG("QString", str(self.ampl)))
+
         Qt.QMetaObject.invokeMethod(self._ampl_line_edit, &quot;setText&quot;, Qt.Q_ARG(&quot;QString&quot;, str(self.ampl)))
         self.analog_sig_source_x_0_0.set_amplitude(self.ampl)
+
         self.analog_sig_source_x_1.set_amplitude(self.ampl)
  
 
+
if __name__ == '__main__':
 
+
    import ctypes
def main(top_block_cls=if_else, options=None):
+
    import sys
 
+
     if sys.platform.startswith('linux'):
     if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
+
        try:
        style = gr.prefs().get_string('qtgui', 'style', 'raster')
+
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
        Qt.QApplication.setGraphicsSystem(style)
+
            x11.XInitThreads()
 +
        except:
 +
            print &quot;Warning: failed to XInitThreads()&quot;
 +
    parser = OptionParser(option_class=eng_option, usage=&quot;%prog: [options]&quot;)
 +
    (options, args) = parser.parse_args()
 +
    Qt.QApplication.setGraphicsSystem(gr.prefs().get_string('qtgui','style','raster'))
 
     qapp = Qt.QApplication(sys.argv)
 
     qapp = Qt.QApplication(sys.argv)
 
+
     tb = top_block()
     tb = top_block_cls()
 
 
     tb.start()
 
     tb.start()
 
     tb.show()
 
     tb.show()
 
    def sig_handler(sig=None, frame=None):
 
        Qt.QApplication.quit()
 
 
    signal.signal(signal.SIGINT, sig_handler)
 
    signal.signal(signal.SIGTERM, sig_handler)
 
 
    timer = Qt.QTimer()
 
    timer.start(500)
 
    timer.timeout.connect(lambda: None)
 
 
 
     def quitting():
 
     def quitting():
 
         tb.stop()
 
         tb.stop()
 
         tb.wait()
 
         tb.wait()
     qapp.aboutToQuit.connect(quitting)
+
     qapp.connect(qapp, Qt.SIGNAL(&quot;aboutToQuit()&quot;), quitting)
 
     qapp.exec_()
 
     qapp.exec_()
 
+
    tb = None #to clean up Qt widgets
 
+
<\syntaxhighlight>
if __name__ == '__main__':
+
}}
    main()
 
 
 
</syntaxhighlight>
 
  
 
We are only concerned about a couple of parts namely the part where the probe is being read:
 
We are only concerned about a couple of parts namely the part where the probe is being read:
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
        def _variable_function_probe_0_probe():
+
      def _variable_function_probe_0_probe():
 
             while True:
 
             while True:
 
 
                 val = self.probe.level()
 
                 val = self.probe.level()
 
                 try:
 
                 try:
Line 573: Line 461:
 
                     pass
 
                     pass
 
                 time.sleep(1.0 / (10))
 
                 time.sleep(1.0 / (10))
</syntaxhighlight>
+
<\syntaxhighlight>
 
We can see that the variable '''val''' is obtaining the value of the probe block. We can write our if-else statement here based on the value of '''val''' to change the amplitude and frequency of our sine wave. But how do we change the frequency and amplitude? We can use the part where the '''QT GUI Entry''' updates the flowgraph. For the variable freq:
 
We can see that the variable '''val''' is obtaining the value of the probe block. We can write our if-else statement here based on the value of '''val''' to change the amplitude and frequency of our sine wave. But how do we change the frequency and amplitude? We can use the part where the '''QT GUI Entry''' updates the flowgraph. For the variable freq:
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
    def set_freq(self, freq):
+
  def set_freq(self, freq):
 
         self.freq = freq
 
         self.freq = freq
         Qt.QMetaObject.invokeMethod(self._freq_line_edit, "setText", Qt.Q_ARG("QString", str(self.freq)))
+
         Qt.QMetaObject.invokeMethod(self._freq_line_edit, &quot;setText&quot;, Qt.Q_ARG(&quot;QString&quot;, str(self.freq)))
         self.analog_sig_source_x_0_0.set_frequency(self.freq)
+
         self.analog_sig_source_x_1.set_frequency(self.freq)
</syntaxhighlight>
+
<\syntaxhighlight>
  
 
and for the variable ampl:
 
and for the variable ampl:
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
    def set_ampl(self, ampl):
+
  def set_ampl(self, ampl):
 
         self.ampl = ampl
 
         self.ampl = ampl
         Qt.QMetaObject.invokeMethod(self._ampl_line_edit, "setText", Qt.Q_ARG("QString", str(self.ampl)))
+
         Qt.QMetaObject.invokeMethod(self._ampl_line_edit, &quot;setText&quot;, Qt.Q_ARG(&quot;QString&quot;, str(self.ampl)))
         self.analog_sig_source_x_0_0.set_amplitude(self.ampl)
+
         self.analog_sig_source_x_1.set_amplitude(self.ampl)
</syntaxhighlight>
+
<\syntaxhighlight>
 
We can see that the functions set_ampl and set_freq can be used for just that - setting the amplitude and the frequency. Thus we can go back and modify our probe function with the if-else statement to give power to our friend.
 
We can see that the functions set_ampl and set_freq can be used for just that - setting the amplitude and the frequency. Thus we can go back and modify our probe function with the if-else statement to give power to our friend.
  
 +
{{collapse(Changes Made to Probe)
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
        def _variable_funct ion_probe_0_probe():
+
      def _variable_function_probe_0_probe():
 
             while True:
 
             while True:
 
 
                 val = self.probe.level()
 
                 val = self.probe.level()
 
                 print val
 
                 print val
Line 611: Line 499:
 
                     pass
 
                     pass
 
                 time.sleep(1.0 / (10))
 
                 time.sleep(1.0 / (10))
</syntaxhighlight>
+
<\syntaxhighlight>
 
+
}}
Now there is one more thing we need to take care of. GRC has compiled the python file in the order of creation of the elements, which was okay as long as there were no crossreferences. With the introduced adaptation (calling set_ampl and set_freq inside the _variable_function_probe_0_probe()) we need to fix the order of declarations. As set_ampl and set_freq both modify parameters of analog_sig_source_x_0_0 but analog_sig_source_x_0_0 is not instantiated before line  150, we have to move the declaration of the _variable_function_probe_0_probe() and everything related below that.
 
 
 
<syntaxhighlight lang="python">
 
        self._qtgui_time_sink_x_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0.pyqwidget(), Qt.QWidget)
 
        self.top_grid_layout.addWidget(self._qtgui_time_sink_x_0_win)
 
        self.blocks_throttle_0 = blocks.throttle(gr.sizeof_float*1, samp_rate,True)
 
        self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_SIN_WAVE, freq, ampl, 0)
 
        self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_SQR_WAVE, 0.1, 1, 0)
 
</syntaxhighlight>
 
  
 
Full code copied below:
 
Full code copied below:
  
 +
{{collapse(if_else_mod.py)
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
#!/usr/bin/env python3
+
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+
##################################################
 +
# Gnuradio Python Flow Graph
 +
# Title: Top Block
 +
# Generated: Wed Jul  2 01:39:19 2014
 +
##################################################
  
#
+
from PyQt4 import Qt
# SPDX-License-Identifier: GPL-3.0
+
from gnuradio import analog
#
+
from gnuradio import blocks
# GNU Radio Python Flow Graph
 
# Title: if_else
 
# GNU Radio version: 3.8.0.0
 
 
 
from distutils.version import StrictVersion
 
 
 
if __name__ == '__main__':
 
    import ctypes
 
    import sys
 
    if sys.platform.startswith('linux'):
 
        try:
 
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
 
            x11.XInitThreads()
 
        except:
 
            print("Warning: failed to XInitThreads()")
 
 
 
from PyQt5 import Qt
 
 
from gnuradio import eng_notation
 
from gnuradio import eng_notation
 +
from gnuradio import gr
 
from gnuradio import qtgui
 
from gnuradio import qtgui
 +
from gnuradio.eng_option import eng_option
 
from gnuradio.filter import firdes
 
from gnuradio.filter import firdes
 +
from optparse import OptionParser
 
import sip
 
import sip
from gnuradio import analog
 
from gnuradio import blocks
 
from gnuradio import gr
 
 
import sys
 
import sys
import signal
+
import threading
from argparse import ArgumentParser
 
from gnuradio.eng_arg import eng_float, intx
 
 
import time
 
import time
import threading
 
from gnuradio import qtgui
 
  
class if_else(gr.top_block, Qt.QWidget):
+
class top_block(gr.top_block, Qt.QWidget):
  
 
     def __init__(self):
 
     def __init__(self):
         gr.top_block.__init__(self, "if_else")
+
         gr.top_block.__init__(self, &quot;Top Block&quot;)
 
         Qt.QWidget.__init__(self)
 
         Qt.QWidget.__init__(self)
         self.setWindowTitle("if_else")
+
         self.setWindowTitle(&quot;Top Block&quot;)
        qtgui.util.check_set_qss()
 
 
         try:
 
         try:
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
+
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
 
         except:
 
         except:
            pass
+
            pass
 
         self.top_scroll_layout = Qt.QVBoxLayout()
 
         self.top_scroll_layout = Qt.QVBoxLayout()
 
         self.setLayout(self.top_scroll_layout)
 
         self.setLayout(self.top_scroll_layout)
Line 688: Line 550:
 
         self.top_layout.addLayout(self.top_grid_layout)
 
         self.top_layout.addLayout(self.top_grid_layout)
  
         self.settings = Qt.QSettings("GNU Radio", "if_else")
+
         self.settings = Qt.QSettings(&quot;GNU Radio&quot;, &quot;top_block&quot;)
 +
        self.restoreGeometry(self.settings.value(&quot;geometry&quot;).toByteArray())
  
        try:
 
            if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
 
                self.restoreGeometry(self.settings.value("geometry").toByteArray())
 
            else:
 
                self.restoreGeometry(self.settings.value("geometry"))
 
        except:
 
            pass
 
  
 
         ##################################################
 
         ##################################################
Line 711: Line 567:
 
         self.probe = blocks.probe_signal_f()
 
         self.probe = blocks.probe_signal_f()
 
         self._freq_tool_bar = Qt.QToolBar(self)
 
         self._freq_tool_bar = Qt.QToolBar(self)
         self._freq_tool_bar.addWidget(Qt.QLabel('freq' + ": "))
+
         self._freq_tool_bar.addWidget(Qt.QLabel(&quot;freq&quot;+&quot;: &quot;))
 
         self._freq_line_edit = Qt.QLineEdit(str(self.freq))
 
         self._freq_line_edit = Qt.QLineEdit(str(self.freq))
 
         self._freq_tool_bar.addWidget(self._freq_line_edit)
 
         self._freq_tool_bar.addWidget(self._freq_line_edit)
 
         self._freq_line_edit.returnPressed.connect(
 
         self._freq_line_edit.returnPressed.connect(
             lambda: self.set_freq(int(str(self._freq_line_edit.text()))))
+
             lambda: self.set_freq(int(self._freq_line_edit.text().toAscii())))
         self.top_grid_layout.addWidget(self._freq_tool_bar)
+
         self.top_layout.addWidget(self._freq_tool_bar)
 
         self._ampl_tool_bar = Qt.QToolBar(self)
 
         self._ampl_tool_bar = Qt.QToolBar(self)
         self._ampl_tool_bar.addWidget(Qt.QLabel('ampl' + ": "))
+
         self._ampl_tool_bar.addWidget(Qt.QLabel(&quot;ampl&quot;+&quot;: &quot;))
 
         self._ampl_line_edit = Qt.QLineEdit(str(self.ampl))
 
         self._ampl_line_edit = Qt.QLineEdit(str(self.ampl))
 
         self._ampl_tool_bar.addWidget(self._ampl_line_edit)
 
         self._ampl_tool_bar.addWidget(self._ampl_line_edit)
 
         self._ampl_line_edit.returnPressed.connect(
 
         self._ampl_line_edit.returnPressed.connect(
             lambda: self.set_ampl(int(str(self._ampl_line_edit.text()))))
+
             lambda: self.set_ampl(int(self._ampl_line_edit.text().toAscii())))
         self.top_grid_layout.addWidget(self._ampl_tool_bar)
+
         self.top_layout.addWidget(self._ampl_tool_bar)
 
+
        def _variable_function_probe_0_probe():
 +
            while True:
 +
                val = self.probe.level()
 +
                if val == 1:
 +
                    self.set_ampl(1)
 +
                    self.set_freq(1000)
 +
                else:
 +
                    self.set_ampl(.3)
 +
                    self.set_freq(100)
 +
                try:
 +
                    self.set_variable_function_probe_0(val)
 +
                except AttributeError:
 +
                    pass
 +
                time.sleep(1.0 / (10))
 +
        _variable_function_probe_0_thread = threading.Thread(target=_variable_function_probe_0_probe)
 +
        _variable_function_probe_0_thread.daemon = True
 +
        _variable_function_probe_0_thread.start()
 
         self.qtgui_time_sink_x_0 = qtgui.time_sink_f(
 
         self.qtgui_time_sink_x_0 = qtgui.time_sink_f(
 
             1024, #size
 
             1024, #size
 
             samp_rate, #samp_rate
 
             samp_rate, #samp_rate
             "", #name
+
             &quot;QT GUI Plot&quot;, #name
 
             1 #number of inputs
 
             1 #number of inputs
 
         )
 
         )
 
         self.qtgui_time_sink_x_0.set_update_time(0.10)
 
         self.qtgui_time_sink_x_0.set_update_time(0.10)
 
         self.qtgui_time_sink_x_0.set_y_axis(-1, 1)
 
         self.qtgui_time_sink_x_0.set_y_axis(-1, 1)
 
+
         self.qtgui_time_sink_x_0.enable_tags(-1, True)
         self.qtgui_time_sink_x_0.set_y_label('Amplitude', "")
+
         self.qtgui_time_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, &quot;&quot;)
 
 
        self.qtgui_time_sink_x_0.enable_tags(True)
 
         self.qtgui_time_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "")
 
 
         self.qtgui_time_sink_x_0.enable_autoscale(False)
 
         self.qtgui_time_sink_x_0.enable_autoscale(False)
        self.qtgui_time_sink_x_0.enable_grid(False)
 
        self.qtgui_time_sink_x_0.enable_axis_labels(True)
 
        self.qtgui_time_sink_x_0.enable_control_panel(False)
 
        self.qtgui_time_sink_x_0.enable_stem_plot(False)
 
  
 
+
         labels = [&quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;,
         labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5',
+
                  &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;]
            'Signal 6', 'Signal 7', 'Signal 8', 'Signal 9', 'Signal 10']
 
 
         widths = [1, 1, 1, 1, 1,
 
         widths = [1, 1, 1, 1, 1,
            1, 1, 1, 1, 1]
+
                  1, 1, 1, 1, 1]
         colors = ['blue', 'red', 'green', 'black', 'cyan',
+
         colors = [&quot;blue&quot;, &quot;red&quot;, &quot;green&quot;, &quot;black&quot;, &quot;cyan&quot;,
            'magenta', 'yellow', 'dark red', 'dark green', 'dark blue']
+
                  &quot;magenta&quot;, &quot;yellow&quot;, &quot;dark red&quot;, &quot;dark green&quot;, &quot;blue&quot;]
        alphas = [1.0, 1.0, 1.0, 1.0, 1.0,
 
            1.0, 1.0, 1.0, 1.0, 1.0]
 
 
         styles = [1, 1, 1, 1, 1,
 
         styles = [1, 1, 1, 1, 1,
            1, 1, 1, 1, 1]
+
                  1, 1, 1, 1, 1]
 
         markers = [-1, -1, -1, -1, -1,
 
         markers = [-1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1]
+
                  -1, -1, -1, -1, -1]
 +
        alphas = [1.0, 1.0, 1.0, 1.0, 1.0,
 +
                  1.0, 1.0, 1.0, 1.0, 1.0]
  
 
+
         for i in xrange(1):
         for i in range(1):
 
 
             if len(labels[i]) == 0:
 
             if len(labels[i]) == 0:
                 self.qtgui_time_sink_x_0.set_line_label(i, "Data {0}".format(i))
+
                 self.qtgui_time_sink_x_0.set_line_label(i, &quot;Data {0}&quot;.format(i))
 
             else:
 
             else:
 
                 self.qtgui_time_sink_x_0.set_line_label(i, labels[i])
 
                 self.qtgui_time_sink_x_0.set_line_label(i, labels[i])
Line 771: Line 634:
  
 
         self._qtgui_time_sink_x_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0.pyqwidget(), Qt.QWidget)
 
         self._qtgui_time_sink_x_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0.pyqwidget(), Qt.QWidget)
         self.top_grid_layout.addWidget(self._qtgui_time_sink_x_0_win)
+
         self.top_layout.addWidget(self._qtgui_time_sink_x_0_win)
 
         self.blocks_throttle_0 = blocks.throttle(gr.sizeof_float*1, samp_rate,True)
 
         self.blocks_throttle_0 = blocks.throttle(gr.sizeof_float*1, samp_rate,True)
         self.analog_sig_source_x_0_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, freq, ampl, 0, 0)
+
         self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_SIN_WAVE, freq, ampl, 0)
         self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_SQR_WAVE, 0.1, 1, 0, 0)
+
         self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_SQR_WAVE, 0.1, 1, 0)
 
 
        def _variable_function_probe_0_probe():
 
            while True:
 
 
 
                val = self.probe.level()
 
                print (val)
 
                if val == 1:
 
                    self.set_ampl(1)
 
                    self.set_freq(1000)
 
                else:
 
                    self.set_ampl(.3)
 
                    self.set_freq(100)
 
                try:
 
                    self.set_variable_function_probe_0(val)
 
                except AttributeError:
 
                    pass
 
                time.sleep(1.0 / (10))
 
        _variable_function_probe_0_thread = threading.Thread(target=_variable_function_probe_0_probe)
 
        _variable_function_probe_0_thread.daemon = True
 
        _variable_function_probe_0_thread.start()
 
  
 
         ##################################################
 
         ##################################################
Line 800: Line 643:
 
         ##################################################
 
         ##################################################
 
         self.connect((self.analog_sig_source_x_0, 0), (self.blocks_throttle_0, 0))
 
         self.connect((self.analog_sig_source_x_0, 0), (self.blocks_throttle_0, 0))
        self.connect((self.analog_sig_source_x_0_0, 0), (self.qtgui_time_sink_x_0, 0))
 
 
         self.connect((self.blocks_throttle_0, 0), (self.probe, 0))
 
         self.connect((self.blocks_throttle_0, 0), (self.probe, 0))
 +
        self.connect((self.analog_sig_source_x_1, 0), (self.qtgui_time_sink_x_0, 0))
  
 +
 +
# QT sink close method reimplementation
 
     def closeEvent(self, event):
 
     def closeEvent(self, event):
         self.settings = Qt.QSettings("GNU Radio", "if_else")
+
         self.settings = Qt.QSettings(&quot;GNU Radio&quot;, &quot;top_block&quot;)
         self.settings.setValue("geometry", self.saveGeometry())
+
         self.settings.setValue(&quot;geometry&quot;, self.saveGeometry())
 
         event.accept()
 
         event.accept()
  
Line 819: Line 664:
 
     def set_samp_rate(self, samp_rate):
 
     def set_samp_rate(self, samp_rate):
 
         self.samp_rate = samp_rate
 
         self.samp_rate = samp_rate
 +
        self.blocks_throttle_0.set_sample_rate(self.samp_rate)
 
         self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate)
 
         self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate)
        self.analog_sig_source_x_0_0.set_sampling_freq(self.samp_rate)
 
        self.blocks_throttle_0.set_sample_rate(self.samp_rate)
 
 
         self.qtgui_time_sink_x_0.set_samp_rate(self.samp_rate)
 
         self.qtgui_time_sink_x_0.set_samp_rate(self.samp_rate)
 +
        self.analog_sig_source_x_1.set_sampling_freq(self.samp_rate)
  
 
     def get_freq(self):
 
     def get_freq(self):
Line 829: Line 674:
 
     def set_freq(self, freq):
 
     def set_freq(self, freq):
 
         self.freq = freq
 
         self.freq = freq
         Qt.QMetaObject.invokeMethod(self._freq_line_edit, "setText", Qt.Q_ARG("QString", str(self.freq)))
+
         Qt.QMetaObject.invokeMethod(self._freq_line_edit, &quot;setText&quot;, Qt.Q_ARG(&quot;QString&quot;, str(self.freq)))
         self.analog_sig_source_x_0_0.set_frequency(self.freq)
+
         self.analog_sig_source_x_1.set_frequency(self.freq)
  
 
     def get_ampl(self):
 
     def get_ampl(self):
Line 837: Line 682:
 
     def set_ampl(self, ampl):
 
     def set_ampl(self, ampl):
 
         self.ampl = ampl
 
         self.ampl = ampl
         Qt.QMetaObject.invokeMethod(self._ampl_line_edit, "setText", Qt.Q_ARG("QString", str(self.ampl)))
+
         Qt.QMetaObject.invokeMethod(self._ampl_line_edit, &quot;setText&quot;, Qt.Q_ARG(&quot;QString&quot;, str(self.ampl)))
         self.analog_sig_source_x_0_0.set_amplitude(self.ampl)
+
         self.analog_sig_source_x_1.set_amplitude(self.ampl)
  
 
+
if __name__ == '__main__':
 
+
    import ctypes
def main(top_block_cls=if_else, options=None):
+
    import sys
 
+
     if sys.platform.startswith('linux'):
     if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
+
        try:
        style = gr.prefs().get_string('qtgui', 'style', 'raster')
+
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
        Qt.QApplication.setGraphicsSystem(style)
+
            x11.XInitThreads()
 +
        except:
 +
            print &quot;Warning: failed to XInitThreads()&quot;
 +
    parser = OptionParser(option_class=eng_option, usage=&quot;%prog: [options]&quot;)
 +
    (options, args) = parser.parse_args()
 +
    Qt.QApplication.setGraphicsSystem(gr.prefs().get_string('qtgui','style','raster'))
 
     qapp = Qt.QApplication(sys.argv)
 
     qapp = Qt.QApplication(sys.argv)
 
+
     tb = top_block()
     tb = top_block_cls()
 
 
     tb.start()
 
     tb.start()
 
     tb.show()
 
     tb.show()
 
    def sig_handler(sig=None, frame=None):
 
        Qt.QApplication.quit()
 
 
    signal.signal(signal.SIGINT, sig_handler)
 
    signal.signal(signal.SIGTERM, sig_handler)
 
 
    timer = Qt.QTimer()
 
    timer.start(500)
 
    timer.timeout.connect(lambda: None)
 
 
 
     def quitting():
 
     def quitting():
 
         tb.stop()
 
         tb.stop()
 
         tb.wait()
 
         tb.wait()
     qapp.aboutToQuit.connect(quitting)
+
     qapp.connect(qapp, Qt.SIGNAL(&quot;aboutToQuit()&quot;), quitting)
 
     qapp.exec_()
 
     qapp.exec_()
 
+
    tb = None #to clean up Qt widgets
 
+
<\syntaxhighlight>
if __name__ == '__main__':
+
}}
    main()
 
 
 
</syntaxhighlight>
 
  
 
We can then simply run our flowgraph from outside of GRC using
 
We can then simply run our flowgraph from outside of GRC using
  
 
<pre>
 
<pre>
$ python3 if_else_mod.py
+
$ python if_else_mod.py
 
</pre>
 
</pre>
 
We should be able to see the numbers 0 and 1 on the terminal and the sine wave changing amplitude and frequency as the numbers change.
 
We should be able to see the numbers 0 and 1 on the terminal and the sine wave changing amplitude and frequency as the numbers change.
 +
 +
This tutorial is merely an introduction on using python in GNU Radio, for a more advanced tutorial see [http://gnuradio.org/redmine/projects/gnuradio/wiki/TutorialsWritePythonApplications TutorialsWritePythonApplications]
  
 
== 3.2. Where Do Blocks Come From? ==
 
== 3.2. Where Do Blocks Come From? ==
Line 890: Line 727:
 
Before we begin, we need to figure out what the commands for gr_modtool are so let's ask for help.
 
Before we begin, we need to figure out what the commands for gr_modtool are so let's ask for help.
  
<pre>$ gr_modtool --help
+
<pre>$ gr_modtool help
Usage: gr_modtool [OPTIONS] COMMAND [ARGS]...
+
Usage:
 
+
gr_modtool [options] -- Run  with the given options.
  A tool for editing GNU Radio out-of-tree modules.
+
gr_modtool help -- Show a list of commands.
 
+
gr_modtool help -- Shows the help for a given command.
Options:
 
  --help  Show this message and exit.
 
 
 
Commands:
 
  add      Adds a block to the out-of-tree module.
 
  disable  Disable selected block in module.
 
  info      Return information about a given module
 
  makeyaml Generate YAML files for GRC block bindings.
 
  newmod    Create new empty module, use add to add blocks.
 
  rename    Rename a block inside a module.
 
  rm        Remove a block from a module.
 
  update    Update the grc bindings for a block
 
  
  Manipulate with GNU Radio modules source code tree. Call it without
+
List of possible commands:
  options to run specified command interactively
 
  
 +
Name      Aliases          Description
 +
=====================================================================
 +
disable  dis              Disable block (comments out CMake entries for files)
 +
info      getinfo,inf      Return information about a given module
 +
remove    rm,del          Remove block (delete files and remove Makefile entries)
 +
makexml  mx              Make XML file for GRC block bindings
 +
add      insert          Add block to the out-of-tree module.
 +
newmod    nm,create        Create a new out-of-tree module
 
</pre>
 
</pre>
 
We immediately see there are many commands available. In this tutorial we will only cover '''newmod''' and '''add'''; however, the thorough explanation should enable smooth usage of the other gr_modtool commands without guidance.
 
We immediately see there are many commands available. In this tutorial we will only cover '''newmod''' and '''add'''; however, the thorough explanation should enable smooth usage of the other gr_modtool commands without guidance.
Line 917: Line 749:
  
 
<pre>
 
<pre>
$ gr_modtool newmod --help
+
$ gr_modtool help newmod
Usage: gr_modtool newmod [OPTIONS] MODULE-NAME
+
Usage: gr_modtool nm [options].
 
+
Call gr_modtool without any options to run it interactively.
  Create a new out-of-tree module
 
 
 
  The argument MODULE-NAME is the name of the module to be added.
 
  
 
Options:
 
Options:
   --srcdir TEXT            Source directory for the module template.
+
   General options:
  -d, --directory TEXT      Base directory of the module. Defaults to the cwd.
+
    -h, --help          Displays this help message.
  --skip-lib               Don't do anything in the lib/ subdirectory.
+
    -d DIRECTORY, --directory=DIRECTORY
  --skip-swig               Don't do anything in the swig/ subdirectory.
+
                        Base directory of the module. Defaults to the cwd.
  --skip-python            Don't do anything in the python/ subdirectory.
+
    -n MODULE_NAME, --module-name=MODULE_NAME
  --skip-grc               Don't do anything in the grc/ subdirectory.
+
                        Use this to override the current module's name (is
  --scm-mode [yes|no|auto]  Use source control management [ yes | no | auto ]).
+
                        normally autodetected).
  -y, --yes                 Answer all questions with 'yes'. This can
+
    -N BLOCK_NAME, --block-name=BLOCK_NAME
                            overwrite and delete your files, so be careful.
+
                        Name of the block, where applicable.
  --help                    Show this message and exit.
+
    --skip-lib         Don't do anything in the lib/ subdirectory.
 +
    --skip-swig         Don't do anything in the swig/ subdirectory.
 +
    --skip-Python      Don't do anything in the Python/ subdirectory.
 +
    --skip-grc         Don't do anything in the grc/ subdirectory.
 +
    --scm-mode=SCM_MODE
 +
                        Use source control management (yes, no or auto).
 +
    -y, --yes           Answer all questions with 'yes'. This can overwrite
 +
                        and delete your files, so be careful.
  
 +
  New out-of-tree module options:
 +
    --srcdir=SRCDIR    Source directory for the module template
 
</pre>
 
</pre>
Now that we have read over the list of commands for newmod, we can deduce that the one we want to pick is -n which is the default so we can simply type the MODULE_NAME after newmod. It is actually advised to avoid using "-n" as for other commands it can override the auto-detected name. For now, let's ignore the other options.
+
Now that we have read over the list of commands for newmod, we can deduce that the one we want to pick is -n which is the default so we can simply type the MODULE_NAME after newmod. It is actually advised to avoid using &quot;-n&quot; as for other commands it can override the auto-detected name. For now, let's ignore the other options.
  
 
=== 3.2.2. Setting up a new block ===
 
=== 3.2.2. Setting up a new block ===
Line 978: Line 816:
 
                         Language (cpp or Python)
 
                         Language (cpp or Python)
 
</pre>
 
</pre>
We can see the '''-l LANG''' and the '''-t BLOCK_TYPE''' are relevant for our example. Thus when creating our new block, we know the command. When prompted for a name simply enter "multiply_py_ff", when prompted for an argument list enter "multiple", and when prompted for Python QA (Quality Assurance) code type "y", or just hit enter (the capital letter is the default value).
+
We can see the '''-l LANG''' and the '''-t BLOCK_TYPE''' are relevant for our example. Thus when creating our new block, we know the command. When prompted for a name simply enter &quot;multiply_py_ff&quot;, when prompted for an argument list enter &quot;multiple&quot;, and when prompted for Python QA (Quality Assurance) code type &quot;y&quot;, or just hit enter (the capital letter is the default value).
  
 
<pre>gr-tutorial$ gr_modtool add -t sync -l python
 
<pre>gr-tutorial$ gr_modtool add -t sync -l python
Line 990: Line 828:
 
Adding file 'Python/qa_multiply_py_ff.py'...
 
Adding file 'Python/qa_multiply_py_ff.py'...
 
Editing Python/CMakeLists.txt...
 
Editing Python/CMakeLists.txt...
Adding file 'grc/tutorial_multiply_py_ff.yml'...
+
Adding file 'grc/tutorial_multiply_py_ff.xml'...
 
Editing grc/CMakeLists.txt...
 
Editing grc/CMakeLists.txt...
 
</pre>
 
</pre>
Line 1,008: Line 846:
  
 
class multiply_py_ff(gr.sync_block):
 
class multiply_py_ff(gr.sync_block):
     """
+
     &quot;&quot;&quot;
 
     docstring for block multiply_py_ff
 
     docstring for block multiply_py_ff
     """
+
     &quot;&quot;&quot;
 
     def __init__(self, multiple):
 
     def __init__(self, multiple):
 
         gr.sync_block.__init__(self,
 
         gr.sync_block.__init__(self,
             name="multiply_py_ff",
+
             name=&quot;multiply_py_ff&quot;,
             in_sig=[<+numpy.float+>],
+
             in_sig=[&lt;+numpy.float+&gt;],
             out_sig=[<+numpy.float+>])
+
             out_sig=[&lt;+numpy.float+&gt;])
 
         self.multiple = multiple
 
         self.multiple = multiple
  
Line 1,022: Line 860:
 
         in0 = input_items[0]
 
         in0 = input_items[0]
 
         out = output_items[0]
 
         out = output_items[0]
         # <+signal processing here+>
+
         # &lt;+signal processing here+&gt;
 
         out[:] = in0
 
         out[:] = in0
 
         return len(output_items[0])
 
         return len(output_items[0])
</syntaxhighlight>
+
<syntaxhighlight>
Let's take this one line by line as our first Python examples. We are already familiar with the imports so we will skip those lines. We are familiar with the constructor (init) of Python so can immediately see that if we want to use our variable "multiple", we need to add another line. Let us not forget to preserve those spaces as some code editors like to add tabs to new lines. How do we use the variable multiple?
+
Let's take this one line by line as our first Python examples. We are already familiar with the imports so we will skip those lines. We are familiar with the constructor (init) of Python so can immediately see that if we want to use our variable &quot;multiple&quot;, we need to add another line. Let us not forget to preserve those spaces as some code editors like to add tabs to new lines. How do we use the variable multiple?
  
How to use variable multiple...
+
{{collapse(View How to use variable multiple...)
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
Line 1,034: Line 872:
 
         self.multiple = multiple
 
         self.multiple = multiple
 
         gr.sync_block.__init__(self,
 
         gr.sync_block.__init__(self,
</syntaxhighlight>
+
<syntaxhighlight>
 
+
}}
  
We notice that there are "&lt;''...''&gt;" scattered in many places. These placeholders are from gr_modtool and tell us where we need to alter things
+
We notice that there are &quot;&lt;''...''&gt;&quot; scattered in many places. These placeholders are from gr_modtool and tell us where we need to alter things
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
in_sig=[<+numpy.float+>]
+
in_sig=[&lt;+numpy.float+&gt;]
out_sig=[<+numpy.float+>]
+
out_sig=[&lt;+numpy.float+&gt;]
</syntaxhighlight>
+
<\syntaxhighlight>
The '''gr.sync_block.''init''''' takes in 4 inputs: self, name, and the size/type of the input and output vectors. First, we want to make the item size a single precision float or numpy.float32 by removing the "&lt;''" and the "''&gt;". If we wanted vectors, we could define those as in_sig=[(numpy.float32,4),numpy.float32]. This means there are two input ports, one for vectors of 4 floats and the other for scalars. It is worth noting that if in_sig contains nothing then it becomes a source block, and if out_sig contains nothing it becomes a sink block (provided we change return <code>len(output_items[0])</code> to return <code>len(input_items[0])</code> since output_items is empty). Our changes to the first placeholders should appear as follows:
+
The '''gr.sync_block.''init''''' takes in 4 inputs: self, name, and the size/type of the input and output vectors. First, we want to make the item size a single precision float or numpy.float32 by removing the &quot;&lt;''&quot; and the &quot;''&gt;&quot;. If we wanted vectors, we could define those as in_sig=[(numpy.float32,4),numpy.float32]. This means there are two input ports, one for vectors of 4 floats and the other for scalars. It is worth noting that if in_sig contains nothing then it becomes a source block, and if out_sig contains nothing it becomes a sink block (provided we change return <code>len(output_items[0])</code> to return <code>len(input_items[0])</code> since output_items is empty). Our changes to the first placeholders should appear as follows:
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
in_sig=[numpy.float32]
 
in_sig=[numpy.float32]
 
out_sig=[numpy.float32]
 
out_sig=[numpy.float32]
</syntaxhighlight>
+
<\syntaxhighlight>
 
The other piece of code that has the placeholders is in the work function but let us first get a better understanding of the work function:
 
The other piece of code that has the placeholders is in the work function but let us first get a better understanding of the work function:
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
def work(self, input_items, output_items)
 
def work(self, input_items, output_items)
</syntaxhighlight>
+
<\syntaxhighlight>
 
The work function is where the actual processing happens, where we want our code to be. Because this is a sync block, the number of input items always equals the number of output items because synchronous block ensures a fixed output to input rate. There are also decimation and interpolation blocks where the number of output items are a user specified multiple of the number of input items. We will further discuss when to use what block type in the third section of this tutorial. For now we look at the placeholder:
 
The work function is where the actual processing happens, where we want our code to be. Because this is a sync block, the number of input items always equals the number of output items because synchronous block ensures a fixed output to input rate. There are also decimation and interpolation blocks where the number of output items are a user specified multiple of the number of input items. We will further discuss when to use what block type in the third section of this tutorial. For now we look at the placeholder:
  
Line 1,059: Line 897:
 
in0 = input_items[0]
 
in0 = input_items[0]
 
out = output_items[0]
 
out = output_items[0]
# <+signal processing here+>
+
# &lt;+signal processing here+&gt;
 
out[:] = in0
 
out[:] = in0
 
return len(output_items[0])
 
return len(output_items[0])
</syntaxhighlight>
+
<syntaxhighlight>
The "in0" and "out" simply store the input and output in a variable to make the block easier to write. The signal processing can be anything including if statements, loops, function calls but for this example we only need to modify the out[:] = in0 line so that our input signal is multiplied by our variable multiple. What do we need to add to make the in0 multiply by our multiple?
+
The &quot;in0&quot; and &quot;out&quot; simply store the input and output in a variable to make the block easier to write. The signal processing can be anything including if statements, loops, function calls but for this example we only need to modify the out[:] = in0 line so that our input signal is multiplied by our variable multiple. What do we need to add to make the in0 multiply by our multiple?
  
How to Multiple...
+
{{collapse(How to Multiple...)
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
out[:] = in0*self.multiple
 
out[:] = in0*self.multiple
</syntaxhighlight>
+
<\syntaxhighlight>
 +
}}
  
 
That's it! Our block should now be able to multiply but to be sure we have these things called Quality Assurance tests!
 
That's it! Our block should now be able to multiply but to be sure we have these things called Quality Assurance tests!
Line 1,097: Line 936:
  
 
if __name__ == '__main__':
 
if __name__ == '__main__':
     gr_unittest.run(qa_multiply_py_ff)
+
     gr_unittest.run(qa_multiply_py_ff, &quot;qa_multiply_py_ff.xml&quot;)
</syntaxhighlight>
+
<\syntaxhighlight>
 
gr_unittest adds support for checking approximate equality of tuples of float and complex numbers. The only part we need to worry about is the def test_001_t function. We know we need input data so let us create data. We want it to be in the form of a vector so that we can test multiple values at once. Let us create a vector of floats
 
gr_unittest adds support for checking approximate equality of tuples of float and complex numbers. The only part we need to worry about is the def test_001_t function. We know we need input data so let us create data. We want it to be in the form of a vector so that we can test multiple values at once. Let us create a vector of floats
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
src_data = (0, 1, -2, 5.5, -0.5)
 
src_data = (0, 1, -2, 5.5, -0.5)
</syntaxhighlight>
+
<\syntaxhighlight>
 
We also need output data so we can compare the input of the block to ensure that it is doing what we expect it to do. Let us multiply by 2 for simplicity.
 
We also need output data so we can compare the input of the block to ensure that it is doing what we expect it to do. Let us multiply by 2 for simplicity.
  
 
<syntaxhighlight lang="python">expected_result = (0, 2, -4, 11, -1)
 
<syntaxhighlight lang="python">expected_result = (0, 2, -4, 11, -1)
</syntaxhighlight>
+
<syntaxhighlight>
Now we can create a flowgraph as we have when we first introduced using Python in GNU Radio. We can use the blocks library specifically the [http://gnuradio.org/doc/doxygen/classgr_1_1blocks_1_1vector__source__f.html vector_source_f] function and the [http://gnuradio.org/doc/doxygen/classgr_1_1blocks_1_1vector__sink__f.html vector_sink_f] function which are linked to the doxygen manual which we should be able to read and understand. Let us assign three variables "src", "mult", and "snk" to the blocks. The first is shown below:
+
Now we can create a flowgraph as we have when we first introduced using Python in GNU Radio. We can use the blocks library specifically the [http://gnuradio.org/doc/doxygen/classgr_1_1blocks_1_1vector__source__f.html vector_source_f] function and the [http://gnuradio.org/doc/doxygen/classgr_1_1blocks_1_1vector__sink__f.html vector_sink_f] function which are linked to the doxygen manual which we should be able to read and understand. Let us assign three variables &quot;src&quot;, &quot;mult&quot;, and &quot;snk&quot; to the blocks. The first is shown below:
  
<syntaxhighlight lang="python">src = blocks.vector_source_f(src_data)</syntaxhighlight>
+
<syntaxhighlight lang="python">src = blocks.vector_source_f(src_data)<\syntaxhighlight>
 
The rest are hidden below as an exercise:
 
The rest are hidden below as an exercise:
  
What do we assign snk and mult?
+
{{collapse(What do we assign snk and mult?)
  
 
<syntaxhighlight lang="python">mult = multiply_py_ff(2)
 
<syntaxhighlight lang="python">mult = multiply_py_ff(2)
 
snk = blocks.vector_sink_f()
 
snk = blocks.vector_sink_f()
</syntaxhighlight>
+
<syntaxhighlight>
 
+
}}
  
 
Now we need to connect everything as src <s>&gt; mult</s>&gt; snk. Instead of using self.connect as we did in our other blocks we need to use self.tb.connect because of the setUp function. Below is how we would connect the src block to the mult block.
 
Now we need to connect everything as src <s>&gt; mult</s>&gt; snk. Instead of using self.connect as we did in our other blocks we need to use self.tb.connect because of the setUp function. Below is how we would connect the src block to the mult block.
  
<syntaxhighlight lang="python">
+
<syntaxhighlight lang="python" line="line">self.tb.connect (src, mult)
self.tb.connect (src, mult)
+
<syntaxhighlight>
</syntaxhighlight>
+
{{collapse(How would we connect the other blocks together?)
 
 
How would we connect the other blocks together?
 
 
 
<syntaxhighlight lang="python">
 
self.tb.connect (mult, snk)
 
</syntaxhighlight>
 
  
 +
<syntaxhighlight lang="python" line="line">self.tb.connect (mult, snk)
 +
<syntaxhighlight>
 +
}}
  
 
Then we can run the graph and store the data from the sink as below:
 
Then we can run the graph and store the data from the sink as below:
  
<syntaxhighlight lang="python">self.tb.run ()
+
<syntaxhighlight lang="python" line="line">self.tb.run ()
 
result_data = snk.data ()
 
result_data = snk.data ()
</syntaxhighlight>
+
<syntaxhighlight>
Lastly we can run our comparison function that will tell us whether the numbers match up to 6 decimal places. We are using the assertFloatTuplesAlmostEqual instead of the "regular assert functions"https://docs.python.org/2/library/unittest.html#assert-methods included in python's unittest because there may be situations where we cannot get a=b due to rounding in floating point numbers.
+
Lastly we can run our comparison function that will tell us whether the numbers match up to 6 decimal places. We are using the assertFloatTuplesAlmostEqual instead of the &quot;regular assert functions&quot;https://docs.python.org/2/library/unittest.html#assert-methods included in python's unittest because there may be situations where we cannot get a=b due to rounding in floating point numbers.
  
<syntaxhighlight lang="python">
+
<syntaxhighlight lang="python" line="line">self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)
self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)
+
<syntaxhighlight>
</syntaxhighlight>
 
 
All together the new test_001_t function should appear as below:
 
All together the new test_001_t function should appear as below:
  
<syntaxhighlight lang="python">
+
<syntaxhighlight lang="python" line="line">src_data = (0, 1, -2, 5.5, -0.5)
src_data = (0, 1, -2, 5.5, -0.5)
 
 
expected_result = (0, 2, -4, 11, -1)
 
expected_result = (0, 2, -4, 11, -1)
 
src = blocks.vector_source_f (src_data)
 
src = blocks.vector_source_f (src_data)
Line 1,156: Line 990:
 
result_data = snk.data ()
 
result_data = snk.data ()
 
self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)
 
self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)
</syntaxhighlight>
+
<syntaxhighlight>
 
 
 
We can then go to the python directory and run:
 
We can then go to the python directory and run:
  
<pre>
+
<syntaxhighlight lang="python" line="line">gr-tutorial/python$ python qa_multiply_py_ff.py
gr-tutorial/python$ python qa_multiply_py_ff.py
 
 
.
 
.
 
----------------------------------------------------------------------
 
----------------------------------------------------------------------
Line 1,167: Line 999:
  
 
OK
 
OK
</pre>
+
<syntaxhighlight>
 
 
 
While we are here, we should take a break to change one of the numbers in the src_data to ensure that the block is actually checking the values and to simply see what an error looks like. Python allows for really quick testing of blocks without having to compile anything; simply change something and re-run the QA test.
 
While we are here, we should take a break to change one of the numbers in the src_data to ensure that the block is actually checking the values and to simply see what an error looks like. Python allows for really quick testing of blocks without having to compile anything; simply change something and re-run the QA test.
  
=== 3.2.5. YAML Files ===
+
=== 3.2.5. XML Files ===
  
At this point we should have written a Python block and a QA test for that block. The next thing to do is edit the YAML file in the grc folder so that we can get another step closer to using it in GRC. GRC uses the YAML files for all the options we see. We actually don't need to write any Python or C++ code to have a block display in GRC but of course if we would connect it, it wouldn't do anything but give errors. We should get out of the python folder and go to the grc folder where all the YAML files reside. There is a tool in gr_modtool called makexml but it is only available for C++ blocks. Let us open the tutorial_multiply_py_ff.xml file copied below:
+
At this point we should have written a Python block and a QA test for that block. The next thing to do is edit the XML file in the grc folder so that we can get another step closer to using it in GRC. GRC uses the XML files for all the options we see. We actually don't need to write any Python or C++ code to have a block display in GRC but of course if we would connect it, it wouldn't do anything but give errors. We should get out of the python folder and go to the grc folder where all the XML files reside. There is a tool in gr_modtool called makexml but it is only available for C++ blocks. Let us open the tutorial_multiply_py_ff.xml file copied below:
  
<syntaxhighlight lang="YAML" line="line">
+
<syntaxhighlight lang="python" line="line">
id: tutorial_multiply_py_ff
+
  multiply_py_ff
label: multiply_py_ff
+
  tutorial_multiply_py_ff
category: '[tutorial]'
+
  tutorial
 +
  import tutorial
 +
  tutorial.multiply_py_ff($multiple)
 +
 
 +
 
 +
    ...
 +
    ...
 +
    ...
 +
 
  
templates:
+
 
   imports: import tutorial
+
    
   make: tutorial.multiply_py_ff(${multiple})
+
    in
 +
   
 +
    
  
#  Make one 'parameters' list entry for every Parameter you want settable from the GUI.
+
 
#    Sub-entries of dictionary:
+
 
#    * id (makes the value accessible as \$keyname, e.g. in the make entry)
+
     out
#     * label
+
      
#     * dtype
+
    
parameters:
+
<syntaxhighlight>
- id: ...
+
</code>
   label: ...
 
  dtype: ...
 
- id: ...
 
  label: ...
 
  dtype: ...
 
 
 
#  Make one 'inputs' list entry per input. Sub-entries of dictionary:
 
#      * label (an identifier for the GUI)
 
#      * domain
 
#      * dtype
 
#      * vlen
 
#      * optional (set to 1 for optional inputs)
 
inputs:
 
- label: ...
 
  domain: ...
 
  dtype: ...
 
  vlen: ...
 
 
 
#  Make one 'outputs' list entry per output. Sub-entries of dictionary:
 
#      * label (an identifier for the GUI)
 
#      * dtype
 
#      * vlen
 
#      * optional (set to 1 for optional inputs)
 
- label: ...
 
  domain: ...
 
  dtype: ... #!-- e.g. int, float, complex, byte, short, xxx_vector, ...--
 
 
 
file_format: 1
 
</syntaxhighlight>
 
  
 
We can change the name that appears and the category it will appear in GRC. The category is where the block will be found in GRC. Examples of categories tag are '''Audio''' and '''Waveform Generators''' used in previous examples. Examples of names tag are the '''QT GUI Time Sink''' or the '''Audio Sink'''. Again, we can go through the file and find the modtool place holders. The first is copied below:
 
We can change the name that appears and the category it will appear in GRC. The category is where the block will be found in GRC. Examples of categories tag are '''Audio''' and '''Waveform Generators''' used in previous examples. Examples of names tag are the '''QT GUI Time Sink''' or the '''Audio Sink'''. Again, we can go through the file and find the modtool place holders. The first is copied below:
  
<syntaxhighlight lang="YAML" line="line">
+
<syntaxhighlight lang="python" line="line"><\syntaxhighlight>
#  Make one 'parameters' list entry for every Parameter you want settable from the GUI.
+
This is referring to the parameter that we used in the very beginning when creating our block: the variable called &quot;multiple&quot;. We can fill it in as below:
#    Sub-entries of dictionary:
 
#    * id (makes the value accessible as \$keyname, e.g. in the make entry)
 
#    * label
 
#    * dtype
 
</syntaxhighlight>
 
This is referring to the parameter that we used in the very beginning when creating our block: the variable called "multiple". We can fill it in as below:
 
 
 
<syntaxhighlight lang="YAML" line="line"> 
 
parameters:
 
- id: multiple
 
  label: Multiple
 
  dtype: float
 
</syntaxhighlight>
 
The next placeholder can be found in the inputs and outputs tags:
 
  
<syntaxhighlight lang="YAML" line="line">
+
<syntaxhighlight lang="python" line="line">  
inputs:
+
    Multiple
- label: in
+
    multiple
   dtype: float
+
    float
 +
    
 +
<syntaxhighlight>
 +
The next placeholder can be found in the sink and source tags:
  
outputs:
+
<syntaxhighlight lang="python" line="line"> 
- label: out
+
    in
   dtype: float #!-- e.g. int, float, complex, byte, short, xxx_vector, ...--
+
   
</syntaxhighlight>
+
    
We can see that it is asking for a type so we can simply erase everything in the tag and replace it with "float" for both the source and the sink blocks. That should do it for this block. The best way to get more experience writing YAML files is to look at the source code of previously made blocks such as the existing multiple block. Let's go back to this and use the documentation tag!
+
<syntaxhighlight>
 +
We can see that it is asking for a type so we can simply erase everything in the tag and replace it with &quot;float&quot; for both the source and the sink blocks. That should do it for this block. The best way to get more experience writing xml files is to look at the source code of previously made blocks such as the existing multiple block. Let's go back to this and use the documentation tag!
  
 
=== 3.2.6. Installing Python Blocks ===
 
=== 3.2.6. Installing Python Blocks ===
  
Now that we have edited the XML file; we are ready to install the block into the GRC. First, we need to get out of the /grc directory and create a build directory called "build". Inside the build directory, we can then run a series of commands:
+
Now that we have edited the XML file; we are ready to install the block into the GRC. First, we need to get out of the /grc directory and create a build directory called &quot;build&quot;. Inside the build directory, we can then run a series of commands:
 
 
cmake ../
 
make
 
sudo make install
 
sudo ldconfig
 
  
 +
<syntaxhighlight lang="python" line="line">cmake ../
 +
make
 +
sudo make install
 +
sudo ldconfig
 +
<syntaxhighlight>
 +
<\syntaxhighlight>
 
We should then open up the GRC and take a look at the new block.
 
We should then open up the GRC and take a look at the new block.
  
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/xml_blockname.png|xml_blockname.png]]
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/xml_blockname.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/xml_blockname.png
  
Line 1,270: Line 1,074:
 
Now that we can see our block, let us test it to make sure it works. Below is an example of one of the many ways to check whether it is actually multiplying.
 
Now that we can see our block, let us test it to make sure it works. Below is an example of one of the many ways to check whether it is actually multiplying.
  
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_2.png|600px|tutorial_three_2.png]]
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_2.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_2.png
  
 
== 3.3. My QPSK Demodulator for Satellites ==
 
== 3.3. My QPSK Demodulator for Satellites ==
  
Now that we have a better understanding on how to add new blocks to GNU Radio for use in the GRC, we can do another example. This time we will be creating a QPSK demodulator block using the same process as before in the same module. Let's first setup the scenario. There is a "noise source" that outputs data in complex float format but let's pretend it comes from a satellite being aimed at our computer. Our secret agent insider tells us this particular satellite encodes digital data using QPSK modulation. We can decode this using a QPSK demodulator that outputs data into bytes. Our insider tells us the space manual doesn't specify whether it's gray code or not. We want to read the bytes using a time sink. What would our flowgraph look like?
+
Now that we have a better understanding on how to add new blocks to GNU Radio for use in the GRC, we can do another example. This time we will be creating a QPSK demodulator block using the same process as before in the same module. Let's first setup the scenario. There is a &quot;noise source&quot; that outputs data in complex float format but let's pretend it comes from a satellite being aimed at our computer. Our secret agent insider tells us this particular satellite encodes digital data using QPSK modulation. We can decode this using a QPSK demodulator that outputs data into bytes. Our insider tells us the space manual doesn't specify whether it's gray code or not. We want to read the bytes using a time sink. What would our flowgraph look like?
  
Incomplete Flowgraph...
+
{{collapse(Incomplete Flowgraph...)<br />
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_3.png|600px|tutorial_three_3.png]]<br />
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_3.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_3.png
 
+
}}
  
 
Now that we know the input type, output type, and parameters, we can ask the question we skipped with our multiply_py_ff block. What type of block do we want?
 
Now that we know the input type, output type, and parameters, we can ask the question we skipped with our multiply_py_ff block. What type of block do we want?
Line 1,293: Line 1,099:
 
When we insert data into our block, do we need more samples or less samples? Or put another way, should our sample rate change?
 
When we insert data into our block, do we need more samples or less samples? Or put another way, should our sample rate change?
  
What Type of Block Should we Use?
+
{{collapse(What Type of Block Should we Use?)
* Sync Block or Basic Block
 
  
 +
<syntaxhighlight lang="python" line="line">Sync Block or Basic Block
 +
<syntaxhighlight>
 +
}}
  
 
=== 3.3.2. Adding Another Block to our OOT Module ===
 
=== 3.3.2. Adding Another Block to our OOT Module ===
  
Now we know everything we need to know to create the block in gr_modtool. As a refresher, what would our gr_modtool command be?
+
Now we know everything we need to know to create the block in gr_modtool. As a refresher, what would our gr_modtool command be?g
  
gr_modtool command...
+
{{collapse(gr_modtool command...)
<pre>
+
 
gr-tutorial$ gr_modtool add -t sync -l python
+
<syntaxhighlight lang="python" line="line">gr-tutorial$ gr_modtool add -t sync -l python
+
<syntaxhighlight>
name: qpsk_demod_py_cb (c for complex input and b for byte/char/int8 output)<br />
+
}}
args: gray_code<br />
 
QA Code: y
 
</pre>
 
  
 +
name: qpsk_demod_py_cb (c for complex input and b for byte/char/int8 output)<br />
 +
args: gray_code<br />
 +
QA Code: y
  
 
Now we have our files setup so we can begin with editing the qpsk_demod_py.py file in the /python folder
 
Now we have our files setup so we can begin with editing the qpsk_demod_py.py file in the /python folder
Line 1,319: Line 1,127:
 
With [http://en.wikipedia.org/wiki/Phase-shift_keying#Quadrature_phase-shift_keying_.28QPSK.29 Gray coding] (adjacent only flips by 1). Note that the mapping we use here is different from the mapping on wikipedia:
 
With [http://en.wikipedia.org/wiki/Phase-shift_keying#Quadrature_phase-shift_keying_.28QPSK.29 Gray coding] (adjacent only flips by 1). Note that the mapping we use here is different from the mapping on wikipedia:
  
 
+
<syntaxhighlight lang="python" line="line">(-1+1j) 10 | 00 (1+1j)
(-1+1j) 10 | 00 (1+1j)
+
-----------+-----------
-----------+-----------
+
(-1-1j) 11 | 01 (1-1j)
(-1-1j) 11 | 01 (1-1j)
+
<syntaxhighlight>
 
 
 
Without Gray coding:
 
Without Gray coding:
  
(-1+1j) 11 | 00 (1+1j)
+
<syntaxhighlight lang="python" line="line">(-1+1j) 11 | 00 (1+1j)
-----------+-----------
+
-----------+-----------
(-1-1j) 10 | 01 (1-1j)
+
(-1-1j) 10 | 01 (1-1j)
 
+
<syntaxhighlight>
 
We can see that we will need to write an if-else statement to take into account the gray_code variable. We will also need four other if-else statements inside the main if-else so that we can pick the mapping. Our pseudocode will look as follows:
 
We can see that we will need to write an if-else statement to take into account the gray_code variable. We will also need four other if-else statements inside the main if-else so that we can pick the mapping. Our pseudocode will look as follows:
  
if gray_code
+
<syntaxhighlight lang="python" line="line">if gray_code
    if 1+1j
+
    if 1+1j
        binary "00" = 0
+
        binary &quot;00&quot; = 0
    ....
+
    ....
    elif -1-1j
+
    elif -1-1j
        binary "11" = 3
+
        binary &quot;11&quot; = 3
else
+
else
    if 1+1j
+
    if 1+1j
        binary "00" = 0
+
        binary &quot;00&quot; = 0
    ....
+
    ....
    elif -1-1j
+
    elif -1-1j
        binary "10" = 2
+
        binary &quot;10&quot; = 2
 
+
<syntaxhighlight>
 
So we have everything we need to implement. Let's go ahead and fill in our gr_modtoool placeholders. We can begin with def init. There are three changes. How do we use the variable gray_code outside the function (similar to what we did with multiple in the last example)? What are the input and output types in [http://docs.scipy.org/doc/numpy/user/basics.types.html? numpy]
 
So we have everything we need to implement. Let's go ahead and fill in our gr_modtoool placeholders. We can begin with def init. There are three changes. How do we use the variable gray_code outside the function (similar to what we did with multiple in the last example)? What are the input and output types in [http://docs.scipy.org/doc/numpy/user/basics.types.html? numpy]
  
Changes to def ''init''...
+
{{collapse(Changes to def ''init''...)
  
<syntaxhighlight lang="python">
+
<syntaxhighlight lang="python" line="line">   def __init__(self, gray_code):
    def __init__(self, gray_code):
 
 
         self.gray_code=gray_code
 
         self.gray_code=gray_code
 
         gr.sync_block.__init__(self,
 
         gr.sync_block.__init__(self,
             name="qpsk_demod_py",
+
             name=&quot;qpsk_demod_py&quot;,
 
             in_sig=[numpy.complex64],
 
             in_sig=[numpy.complex64],
 
             out_sig=[numpy.uint8])
 
             out_sig=[numpy.uint8])
</syntaxhighlight>
+
<syntaxhighlight>
 +
</code><br />
 +
}}
  
Once we have our constructor setup, we can go onto the work function. For simplicity and beauty, let us call the pseudocode we made above a function "get_minimum_distance" that takes samples as input arguments. In our multiply_py_ff example, we took all the samples and multiplied them with with out[:] = in0*self.multiple. The in0 is actually a vector so contains many samples within it. The multiply example required the same operation for each sample so it was okay to simply operate on the entire vector but now we need to have different operations per sample so what do we do?
+
Once we have our constructor setup, we can go onto the work function. For simplicity and beauty, let us call the pseudocode we made above a function &quot;get_minimum_distance&quot; that takes samples as input arguments. In our multiply_py_ff example, we took all the samples and multiplied them with with out[:] = in0*self.multiple. The in0 is actually a vector so contains many samples within it. The multiply example required the same operation for each sample so it was okay to simply operate on the entire vector but now we need to have different operations per sample so what do we do?
  
How can we operate on samples in a vector?
+
{{collapse(How can we operate on samples in a vector?)<br />
* loops!
+
loops!
  
<syntaxhighlight lang="python">
+
<syntaxhighlight lang="python" line="line">       for i in range(0, len(output_items[0])):
        for i in range(0, len(output_items[0])):
 
 
             sample = in0[i]
 
             sample = in0[i]
 
             out[i] = self.get_minimum_distances(sample)
 
             out[i] = self.get_minimum_distances(sample)
</syntaxhighlight>
+
<syntaxhighlight>
 
+
</code><br />
 +
}}
  
 
Now we can move onto the get_minimum_distances(self, sample) function. We already have pseudo code so the next step is translating to Python. Below is a snip of what the code can look like. Again there are multiple ways to do this
 
Now we can move onto the get_minimum_distances(self, sample) function. We already have pseudo code so the next step is translating to Python. Below is a snip of what the code can look like. Again there are multiple ways to do this
  
<syntaxhighlight lang="python" line="line">
+
<syntaxhighlight lang="python" line="line">   def get_minimum_distances(self, sample):
    def get_minimum_distances(self, sample):
 
 
         if self.gray_code == 1:
 
         if self.gray_code == 1:
             if (sample.imag >= 0 and sample.real >= 0):
+
             if (sample.imag &gt;= 0 and sample.real &gt;= 0):
 
                 return 0 # 1+1j
 
                 return 0 # 1+1j
             elif (sample.imag >= 0 and sample.real < 0):
+
             elif (sample.imag &gt;= 0 and sample.real &lt; 0):
 
                 return 2 # -1+1j
 
                 return 2 # -1+1j
</syntaxhighlight>
+
<syntaxhighlight>
 
Let us try to fill in the other cases for gray code and non-gray code. Below is what the entire file Python file can look like once complete:
 
Let us try to fill in the other cases for gray code and non-gray code. Below is what the entire file Python file can look like once complete:
  
qpsk_demod_py_cb.py
+
{{collapse(qpsk_demod_py_cb.py)
  
<syntaxhighlight lang="python" line="line">
+
<syntaxhighlight lang="python" line="line">import numpy
import numpy
 
 
from gnuradio import gr
 
from gnuradio import gr
  
 
class qpsk_demod_py(gr.sync_block):
 
class qpsk_demod_py(gr.sync_block):
     """
+
     &quot;&quot;&quot;
 
     docstring for block qpsk_demod_py
 
     docstring for block qpsk_demod_py
     """
+
     &quot;&quot;&quot;
 
     def __init__(self, gray_code):
 
     def __init__(self, gray_code):
 
         self.gray_code=gray_code
 
         self.gray_code=gray_code
 
         gr.sync_block.__init__(self,
 
         gr.sync_block.__init__(self,
             name="qpsk_demod_py",
+
             name=&quot;qpsk_demod_py&quot;,
 
             in_sig=[numpy.complex64],
 
             in_sig=[numpy.complex64],
 
             out_sig=[numpy.uint8])
 
             out_sig=[numpy.uint8])
Line 1,401: Line 1,207:
 
     def get_minimum_distances(self, sample):
 
     def get_minimum_distances(self, sample):
 
         if self.gray_code == 1:
 
         if self.gray_code == 1:
             if (sample.imag >= 0 and sample.real >= 0):
+
             if (sample.imag &gt;= 0 and sample.real &gt;= 0):
 
                 return 0 # 1+1j
 
                 return 0 # 1+1j
             elif (sample.imag >= 0 and sample.real < 0):
+
             elif (sample.imag &gt;= 0 and sample.real &lt; 0):
 
                 return 2 # -1+1j
 
                 return 2 # -1+1j
             elif (sample.imag < 0 and sample.real < 0):
+
             elif (sample.imag &lt; 0 and sample.real &lt; 0):
 
                 return 3 # -1-1j
 
                 return 3 # -1-1j
             elif (sample.imag < 0 and sample.real >= 0):
+
             elif (sample.imag &lt; 0 and sample.real &gt;= 0):
 
                 return 1 # 1-1j
 
                 return 1 # 1-1j
 
         else:
 
         else:
             if (sample.imag >= 0 and sample.real >= 0):
+
             if (sample.imag &gt;= 0 and sample.real &gt;= 0):
 
                 return 0 # 1+1j
 
                 return 0 # 1+1j
             elif (sample.imag >= 0 and sample.real < 0):
+
             elif (sample.imag &gt;= 0 and sample.real &lt; 0):
 
                 return 3 # -1+1j
 
                 return 3 # -1+1j
             elif (sample.imag < 0 and sample.real < 0):
+
             elif (sample.imag &lt; 0 and sample.real &lt; 0):
 
                 return 2 # -1-1j
 
                 return 2 # -1-1j
             elif (sample.imag < 0 and sample.real >= 0):
+
             elif (sample.imag &lt; 0 and sample.real &gt;= 0):
 
                 return 1 # 1-1j
 
                 return 1 # 1-1j
  
Line 1,428: Line 1,234:
  
 
         return len(output_items[0])
 
         return len(output_items[0])
</syntaxhighlight>
+
<syntaxhighlight>
 
+
}}
  
 
Now that we have code, we know what's next!
 
Now that we have code, we know what's next!
Line 1,439: Line 1,245:
 
We can copy the def test_001_t from the qa_multiply_py_ff code which is copied below:
 
We can copy the def test_001_t from the qa_multiply_py_ff code which is copied below:
  
<syntaxhighlight lang="python" line="line">
+
<syntaxhighlight lang="python" line="line">   def test_001_t (self):
    def test_001_t (self):
 
 
         src_data = (-3, 4, -5.5, 2, 3)
 
         src_data = (-3, 4, -5.5, 2, 3)
 
         expected_result = (-6, 8, -11, 4, 6)
 
         expected_result = (-6, 8, -11, 4, 6)
Line 1,451: Line 1,256:
 
         result_data = dst.data ()
 
         result_data = dst.data ()
 
         self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)
 
         self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)
</syntaxhighlight>
+
<syntaxhighlight>
 
This time we are working with a complex input so our src = blocks.vector_source_f must change. If we use the search bar in the manual we can find the other options:
 
This time we are working with a complex input so our src = blocks.vector_source_f must change. If we use the search bar in the manual we can find the other options:
  
Line 1,462: Line 1,267:
 
s - short
 
s - short
  
What do we change our source and sink vectors to?<br />
+
{{collapse(What do we change our source and sink vectors to?)<br />
 
src = blocks.vector_source_c (src_data)<br />
 
src = blocks.vector_source_c (src_data)<br />
 
dst = blocks.vector_sink_b ()<br />
 
dst = blocks.vector_sink_b ()<br />
 
+
}}
  
 
Before we move onto actual test cases, let us decide which mode we are testing for the test_001_t. We can create a new variable and assign it False (translates to 0) to test non-Gray code
 
Before we move onto actual test cases, let us decide which mode we are testing for the test_001_t. We can create a new variable and assign it False (translates to 0) to test non-Gray code
  
<syntaxhighlight lang="python">
+
<syntaxhighlight lang="python" line="line">gray_code = False
gray_code = False
+
<syntaxhighlight>
</syntaxhighlight>
 
 
Once we know we want to test non gray_code mappings, we can refer to our chart above and start placing in the proper inputs and outputs into the src_data and the expected_results. For instance if we were testing only two cases for non gray_code, we would do:
 
Once we know we want to test non gray_code mappings, we can refer to our chart above and start placing in the proper inputs and outputs into the src_data and the expected_results. For instance if we were testing only two cases for non gray_code, we would do:
  
 
<syntaxhighlight lang="python" line="line">src_data = ((-1-1j), (-1+1j))
 
<syntaxhighlight lang="python" line="line">src_data = ((-1-1j), (-1+1j))
 
expected_result = (2, 3)
 
expected_result = (2, 3)
</syntaxhighlight>
+
<syntaxhighlight>
Last thing to do is call upon our new block in the "qpsk =" line and pass it the gray_code parameter
+
Last thing to do is call upon our new block in the &quot;qpsk =&quot; line and pass it the gray_code parameter
  
qpsk = ?
+
{{collapse(qpsk = ?)<br />
* qpsk = qpsk_demod_py_cb (gray_code)
+
qpsk = qpsk_demod_py_cb (gray_code)<br />
 +
}}
  
Now that we are done with the non gray_code test, we can simply create another test "def test_002_t (self):" and copy the contents underneath making sure that for this test we set gray_code = True and change the expected_result so it matches gray_code mapping. The full test is copied below:
+
Now that we are done with the non gray_code test, we can simply create another test &quot;def test_002_t (self):&quot; and copy the contents underneath making sure that for this test we set gray_code = True and change the expected_result so it matches gray_code mapping. The full test is copied below:
  
Full QA QPSK Demod Code...
+
{{collapse(Full QA QPSK Demod Code...)
  
<syntaxhighlight lang="python" line="line">
+
<syntaxhighlight lang="python" line="line">from gnuradio import gr, gr_unittest
from gnuradio import gr, gr_unittest
 
 
from gnuradio import blocks
 
from gnuradio import blocks
 
import numpy
 
import numpy
Line 1,527: Line 1,331:
  
 
if __name__ == '__main__':
 
if __name__ == '__main__':
     gr_unittest.run(qa_qpsk_demod, "qpsk_demod_py_cb.xml")
+
     gr_unittest.run(qa_qpsk_demod, &quot;qpsk_demod_py_cb.xml&quot;)
</syntaxhighlight>
+
<syntaxhighlight>
 
+
}}
  
 
We can then run the test in Python and all should say something similar to:
 
We can then run the test in Python and all should say something similar to:
  
Ran 2 tests in 0.005s
+
<syntaxhighlight lang="python" line="line">Ran 2 tests in 0.005s
 
OK
 
  
 +
OK
 +
<syntaxhighlight>
 
So once we verify it works as expected, we can then edit our XML file so that it is usable inside GRC.
 
So once we verify it works as expected, we can then edit our XML file so that it is usable inside GRC.
  
Line 1,543: Line 1,347:
 
This XML is very similar to the XML file for the multiply_py_ff block so all we need to do is set the gray_code parameter and pick the correct input (complex) and output (byte) types. A copy of the full XML file is below:
 
This XML is very similar to the XML file for the multiply_py_ff block so all we need to do is set the gray_code parameter and pick the correct input (complex) and output (byte) types. A copy of the full XML file is below:
  
XML File for QPSK Demod
+
{{collapse(XML File for QPSK Demod)
  
 
<syntaxhighlight lang="python" line="line">
 
<syntaxhighlight lang="python" line="line">
<?xml version="1.0"?>
 
<block>
 
  <name>qpsk_demod_py</name>
 
  <key>tutorial_qpsk_demod_py</key>
 
  <category>tutorial</category>
 
  <import>import tutorial</import>
 
  <make>tutorial.qpsk_demod_py($gray_code)</make>
 
  <!-- Make one 'param' node for every Parameter you want settable from the GUI.
 
      Sub-nodes:
 
      * name
 
      * key (makes the value accessible as $keyname, e.g. in the make node)
 
      * type -->
 
  <param>
 
    <name>Gray Code</name>
 
    <key>gray_code</key>
 
    <type>int</type>
 
  </param>
 
  
   <!-- Make one 'sink' node per input. Sub-nodes:
+
   qpsk_demod_py
      * name (an identifier for the GUI)
+
  tutorial_qpsk_demod_py
      * type
+
  tutorial
      * vlen
+
  import tutorial
      * optional (set to 1 for optional inputs) -->
+
  tutorial.qpsk_demod_py($gray_code)
   <sink>
+
    
     <name>in</name>
+
 
     <type>complex</type>
+
    Gray Code
   </sink>
+
     gray_code
 +
     int
 +
    
  
   <!-- Make one 'source' node per output. Sub-nodes:
+
    
      * name (an identifier for the GUI)
+
    
      * type
+
     in
      * vlen
+
     complex
      * optional (set to 1 for optional inputs) -->
+
    
      <!-- e.g. int, float, complex, byte, short, xxx_vector, ...-->
 
   <source>
 
     <name>out</name>
 
     <type>byte</type>
 
   </source>
 
</block>
 
  
</syntaxhighlight>
+
 
 +
     
 +
 
 +
    out
 +
    byte
 +
 
 +
<\syntaxhighlight>
 +
}}
  
 +
We can then install as we did for the multiply block however we need to rerun cmake in order to take into account the new block:
  
We can then install as we did for the multiply block however we need to rerun cmake in order to take into account the new block:
+
<syntaxhighlight lang="bash" line="line">
<pre>
+
cd build
cd build
+
cmake ../
cmake ../
+
make
make
+
sudo make install
sudo make install
+
sudo ldconfig
sudo ldconfig
+
<\syntaxhighlight>
</pre>
 
 
Then we can open up our GRC file from the beginning and place our missing block we just made.
 
Then we can open up our GRC file from the beginning and place our missing block we just made.
  
What is the Expected Output?
+
{{collapse(What is the Expected Output?)<br />
 +
[[File:https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_4.png|600px|tutorial_three_4.png]]<br />
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_4.png
 
https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_4.png
 +
}}
  
 
== 3.4. Conclusion ==
 
== 3.4. Conclusion ==
  
And that is it for now with Python. Let us know your thoughts before going on to the [[Guided_Tutorial_GNU_Radio_in_C++|C++ tutorial]].
+
And that is it for now with Python. Let us know your thoughts before going on to the [http://gnuradio.org/redmine/projects/gnuradio/wiki/NewTutorials4 C++ tutorial].
  
 
=== 3.4.1. Questions We Should Now be Able to Answer ===
 
=== 3.4.1. Questions We Should Now be Able to Answer ===
Line 1,610: Line 1,402:
 
1. How do we set input- and output signatures in Python blocks?<br />
 
1. How do we set input- and output signatures in Python blocks?<br />
 
2. Consider this I/O signature: (FIXME). Which input types and output types does it take?
 
2. Consider this I/O signature: (FIXME). Which input types and output types does it take?
 +
 +
=== 3.4.2. Links to Further Information ===
 +
 +
* [http://gnuradio.org/redmine/projects/gnuradio/wiki/BlocksCodingGuide Blocks Coding Guide]
 +
* [http://gnuradio.org/redmine/projects/gnuradio/wiki/OutOfTreeModules Out-of-Tree Modules]
 +
* [http://gnuradio.org/redmine/projects/gnuradio/wiki/TutorialsWritePythonApplications Writing Python Applications]
  
 
== 3.5. Candidates for Future Sections ==
 
== 3.5. Candidates for Future Sections ==
Line 1,616: Line 1,414:
 
- How to add documentation to blocks<br />
 
- How to add documentation to blocks<br />
 
- Constructor arguments, History, Output Multiple
 
- Constructor arguments, History, Output Multiple
 +
 +
 +
-----
 +
 +
&lt;. [[Guided_Tutorial_GRC|Previous: Working with GRC]]
 +
&gt;. [[Guided_Tutorial_GNU_Radio_in_C++|Next: Programming GNU Radio in C++]]
 +
 +
[[Category:Guided Tutorials]]

Please note that all contributions to GNU Radio are considered to be released under the Creative Commons Attribution-ShareAlike (see GNU Radio:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To edit this page, please answer the question that appears below (more info):

Cancel | Editing help (opens in new window)