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 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, "Tutorial Three")
        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, "", 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="%prog: [options]")
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 [[Tutorials/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 ===
Line 186: Line 113:
  
 
<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 215: Line 142:
  
 
<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 317: Line 244:
  
 
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, "Top Block")
 
         Qt.QWidget.__init__(self)
 
         Qt.QWidget.__init__(self)
         self.setWindowTitle("if_else")
+
         self.setWindowTitle("Top Block")
        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 291:
 
         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("GNU Radio", "top_block")
 +
        self.restoreGeometry(self.settings.value("geometry").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 308:
 
         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("freq"+": "))
 
         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("ampl"+": "))
 
         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 332:
 
         _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
+
             "QT GUI Plot", #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.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.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 = ["", "", "", "", "",
         labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5',
+
                  "", "", "", "", ""]
            '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 = ["blue", "red", "green", "black", "cyan",
            'magenta', 'yellow', 'dark red', 'dark green', 'dark blue']
+
                  "magenta", "yellow", "dark red", "dark green", "blue"]
        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 range(1):
+
         for i in xrange(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, "Data {0}".format(i))
Line 475: Line 369:
  
 
         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 378:
 
         ##################################################
 
         ##################################################
 
         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("GNU Radio", "top_block")
 
         self.settings.setValue("geometry", self.saveGeometry())
 
         self.settings.setValue("geometry", self.saveGeometry())
 
         event.accept()
 
         event.accept()
Line 505: Line 399:
 
     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 516: Line 410:
 
         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, "setText", Qt.Q_ARG("QString", 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 524: Line 418:
 
         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, "setText", Qt.Q_ARG("QString", 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 "Warning: failed to XInitThreads()"
 +
    parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
 +
    (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("aboutToQuit()"), quitting)
 
     qapp.exec_()
 
     qapp.exec_()
 
+
    tb = None #to clean up Qt widgets
 
 
if __name__ == '__main__':
 
    main()
 
 
 
 
</syntaxhighlight>
 
</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 577: Line 460:
  
 
<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, "setText", Qt.Q_ARG("QString", 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>
  
Line 586: Line 469:
  
 
<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, "setText", Qt.Q_ARG("QString", 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.
Line 595: Line 478:
  
 
<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 493:
 
                     pass
 
                     pass
 
                 time.sleep(1.0 / (10))
 
                 time.sleep(1.0 / (10))
</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>
 
</syntaxhighlight>
  
Line 627: Line 499:
  
 
<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, "Top Block")
 
         Qt.QWidget.__init__(self)
 
         Qt.QWidget.__init__(self)
         self.setWindowTitle("if_else")
+
         self.setWindowTitle("Top Block")
        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 542:
 
         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("GNU Radio", "top_block")
 +
        self.restoreGeometry(self.settings.value("geometry").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 559:
 
         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("freq"+": "))
 
         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("ampl"+": "))
 
         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
+
             "QT GUI Plot", #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.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.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 = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5',
+
         labels = ["", "", "", "", "",
            '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 = ["blue", "red", "green", "black", "cyan",
            'magenta', 'yellow', 'dark red', 'dark green', 'dark blue']
+
                  "magenta", "yellow", "dark red", "dark green", "blue"]
        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, "Data {0}".format(i))
Line 771: Line 626:
  
 
         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 635:
 
         ##################################################
 
         ##################################################
 
         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("GNU Radio", "top_block")
 
         self.settings.setValue("geometry", self.saveGeometry())
 
         self.settings.setValue("geometry", self.saveGeometry())
 
         event.accept()
 
         event.accept()
Line 819: Line 656:
 
     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 830: Line 667:
 
         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, "setText", Qt.Q_ARG("QString", 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 838: Line 675:
 
         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, "setText", Qt.Q_ARG("QString", 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 "Warning: failed to XInitThreads()"
 +
    parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
 +
    (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("aboutToQuit()"), quitting)
 
     qapp.exec_()
 
     qapp.exec_()
 
+
    tb = None #to clean up Qt widgets
 
 
if __name__ == '__main__':
 
    main()
 
 
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 878: Line 704:
  
 
<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 718:
 
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.
 +
gr_modtool help -- Show a list of commands.
 +
gr_modtool help  -- Shows the help for a given command.
  
  A tool for editing GNU Radio out-of-tree modules.
+
List of possible commands:
 
 
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
 
  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 740:
  
 
<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 "-n" as for other commands it can override the auto-detected name. For now, let's ignore the other options.
Line 990: Line 819:
 
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,014: Line 843:
 
         gr.sync_block.__init__(self,
 
         gr.sync_block.__init__(self,
 
             name="multiply_py_ff",
 
             name="multiply_py_ff",
             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 851:
 
         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])
Line 1,028: Line 857:
 
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 "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?
  
How to use variable multiple...
+
{{collapse(View How to use variable multiple...)
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
Line 1,035: Line 864:
 
         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 "&lt;''...''&gt;" 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 "&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:
Line 1,059: Line 888:
 
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 "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?
  
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 927:
  
 
if __name__ == '__main__':
 
if __name__ == '__main__':
     gr_unittest.run(qa_multiply_py_ff)
+
     gr_unittest.run(qa_multiply_py_ff, "qa_multiply_py_ff.xml")
 
</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
Line 1,107: Line 937:
  
 
<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 "src", "mult", and "snk" to the blocks. The first is shown below:
  
Line 1,113: Line 943:
 
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.
Line 1,126: Line 956:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
How would we connect the other blocks together?
+
{{collapse(How would we connect the other blocks together?)
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
self.tb.connect (mult, snk)
 
self.tb.connect (mult, snk)
 
</syntaxhighlight>
 
</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:
Line 1,171: Line 1,001:
 
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
templates:
+
   tutorial.multiply_py_ff($multiple)
   imports: import tutorial
+
 
   make: tutorial.multiply_py_ff(${multiple})
+
 
 +
    ...
 +
    ...
 +
    ...
 +
 
  
#  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)
+
     in
#     * label
+
      
#    * dtype
+
    
parameters:
 
- id: ...
 
  label: ...
 
  dtype: ...
 
- id: ...
 
  label: ...
 
   dtype: ...
 
  
#  Make one 'inputs' list entry per input. Sub-entries of dictionary:
+
    
#      * label (an identifier for the GUI)
+
    
#      * domain
+
    out
#      * 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>
 
</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.
 
#    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:
 
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">   
+
<syntaxhighlight lang="python" line="line">   
parameters:
+
    Multiple
- id: multiple
+
    multiple
  label: Multiple
+
    float
   dtype: float
+
    
 
</syntaxhighlight>
 
</syntaxhighlight>
The next placeholder can be found in the inputs and outputs tags:
+
The next placeholder can be found in the sink and source tags:
  
<syntaxhighlight lang="YAML" line="line">
+
<syntaxhighlight lang="python" line="line">  
inputs:
+
    in
- label: in
+
   
  dtype: float
+
    
 
 
outputs:
 
- label: out
 
   dtype: float #!-- e.g. int, float, complex, byte, short, xxx_vector, ...--
 
 
</syntaxhighlight>
 
</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!
+
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 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 ===
Line 1,270: Line 1,070:
 
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
  
Line 1,276: Line 1,077:
 
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 "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?
  
Incomplete Flowgraph...
+
{{collapse(Incomplete Flowgraph...)
 
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,094:
 
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
+
Sync Block or Basic Block
 
+
}}
  
 
=== 3.3.2. Adding Another Block to our OOT Module ===
 
=== 3.3.2. Adding Another Block to our OOT Module ===
Line 1,301: Line 1,102:
 
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?
  
gr_modtool command...
+
{{collapse(gr_modtool command...)
<pre>
 
 
  gr-tutorial$ gr_modtool add -t sync -l python
 
  gr-tutorial$ gr_modtool add -t sync -l python
 
   
 
   
Line 1,308: Line 1,108:
 
  args: gray_code<br />
 
  args: gray_code<br />
 
  QA Code: y
 
  QA Code: y
</pre>
+
}}
  
  
Line 1,347: Line 1,147:
 
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">
 
     def __init__(self, gray_code):
 
     def __init__(self, gray_code):
Line 1,356: Line 1,155:
 
             in_sig=[numpy.complex64],
 
             in_sig=[numpy.complex64],
 
             out_sig=[numpy.uint8])
 
             out_sig=[numpy.uint8])
</syntaxhighlight>
+
<syntaxhighlight>
 +
}}
  
 
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 "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?
  
How can we operate on samples in a vector?
+
{{collapse(How can we operate on samples in a vector?)
* loops!
+
loops!
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
Line 1,367: Line 1,167:
 
             sample = in0[i]
 
             sample = in0[i]
 
             out[i] = self.get_minimum_distances(sample)
 
             out[i] = self.get_minimum_distances(sample)
</syntaxhighlight>
+
<syntaxhighlight>
 
+
}}
  
 
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
Line 1,375: Line 1,175:
 
     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">
Line 1,401: Line 1,201:
 
     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,228:
  
 
         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,451: Line 1,251:
 
         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,262:
 
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
Line 1,471: Line 1,271:
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
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 "qpsk =" line and pass it the gray_code parameter
  
qpsk = ?
+
{{collapse(qpsk = ?)
* qpsk = qpsk_demod_py_cb (gray_code)
+
qpsk = qpsk_demod_py_cb (gray_code)
 +
}}
  
 
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 "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:
  
Full QA QPSK Demod Code...
+
{{collapse(Full QA QPSK Demod Code...)
  
 
<syntaxhighlight lang="python" line="line">
 
<syntaxhighlight lang="python" line="line">
Line 1,529: Line 1,330:
 
     gr_unittest.run(qa_qpsk_demod, "qpsk_demod_py_cb.xml")
 
     gr_unittest.run(qa_qpsk_demod, "qpsk_demod_py_cb.xml")
 
</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:
Line 1,543: Line 1,344:
 
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>
 
  
 +
 
 +
     
 +
 
 +
    out
 +
    byte
 +
 
 
</syntaxhighlight>
 
</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:
 
<pre>
 
 
  cd build
 
  cd build
 
  cmake ../
 
  cmake ../
Line 1,596: Line 1,382:
 
  sudo make install
 
  sudo make install
 
  sudo ldconfig
 
  sudo ldconfig
</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?)
 +
[[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,398:
 
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,410:
 
- 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)