Creating Your First Block: Difference between revisions

From GNU Radio
Jump to navigation Jump to search
No edit summary
(typo)
 
(28 intermediate revisions by 4 users not shown)
Line 2: Line 2:
{{Template:BeginnerTutorials}}
{{Template:BeginnerTutorials}}
</div>
</div>
This tutorial shows how to create a signal processing block with the ''Embedded Python Block''. The example block will either add or multiply the two inputs based on a parameter.
This tutorial shows how to create a signal processing block with the ''Embedded Python Block''. The example block either adds or multiplys the two inputs based on a parameter.
 
This tutorial uses the ''Embedded Python Block''' which can only be used in the flowgraph it was created in. The tutorial [[Creating Python OOT with gr-modtool|Creating Python OOT with gr-modtool]] demonstrates how to create a custom Python block as an out-of-tree (OOT) module which can be installed and used in any flowgraph.


The previous tutorial, [[Hier_Blocks_and_Parameters|Hier Blocks and Parameters]], describes how to create a ''hierarchical block'' and how to use ''parameters''. The next tutorial, [[Python_Block_with_Vectors|Python Block with Vectors]], demonstrates how to write an ''Embedded Python Block'' with vector inputs and outputs.
The previous tutorial, [[Hier_Blocks_and_Parameters|Hier Blocks and Parameters]], describes how to create a ''hierarchical block'' and how to use ''parameters''. The next tutorial, [[Python_Block_with_Vectors|Python Block with Vectors]], demonstrates how to write an ''Embedded Python Block'' with vector inputs and outputs.
Line 12: Line 14:




Double-click the box to edit the properties. The ''Embedded Python Block'' has two properties,
Double-click the block to edit the properties. The ''Embedded Python Block'' has two properties,
# ''Code'', a click-box which contains a link to the Python code for the block and  
# ''Code'', a click-box which contains a link to the Python code for the block and  
# ''Example_Param'', an input parameter to the block.
# ''Example_Param'', an input parameter to the block.
Line 22: Line 24:




You will be prompted with another choice for which editor to use to write the Python code. Click ''Use Default'':
A prompt is displayed with a choice of which text editor to use to write the Python code. Click ''Use Default'':


[[File:ClickUseDefault.png|500px]]
[[File:ClickUseDefault.png|500px]]




An editor window will then display the Python code for the ''Embedded Python Block'':
An editor window displays the Python code for the ''Embedded Python Block'':


[[File:PythonCodeGedit.png|500px]]
[[File:PythonCodeGedit.png|500px]]


== Components of a Python Block ==
== Components of a Python Block ==


There are three important sections in the Python block code:
There are three important sections in the Python block code:
# ''import'' statements in <span style="color:green">green</span>
# ''import'' statements in a '''<span style="color:green">green box</span>'''
# ''__init__'' function in <span style="color:blue">blue</span>
# ''__init__'' method in a '''<span style="color:aqua">blue box </span>'''
# ''work'' function in <span style="color:red">red</span>
# ''work'' method in a '''<span style="color:red">red box</span>'''
 
{|
! Screenshot of editor window
! Program code from editor window
|-
|[[File:PythonBlockCodeFunctions.png|height=500px]]
|<syntaxhighlight lang="python" highlight="9-10,16-26,28-31">
"""
Embedded Python Blocks:
 
Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""


[[File:PythonBlockCodeFunctions.png|500px]]
import numpy as np
from gnuradio import gr




The ''import'' statement includes the NumPy and GNU Radio libraries.
class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""


The ''__init__'' statement:
    def __init__(self, example_param=1.0):  # only default arguments here
# Accepts the ''example_param'' parameter with a default argument of 1.0
        """arguments to this function show up as parameters in GRC"""
# Declares the block to have a ''np.complex64'' input and output, which is the GNU Radio ''Complex Float 32'' data type
        gr.sync_block.__init__(
# Stores the ''self.example_param'' variable from the input parameter
            self,
            name='Embedded Python Block',  # will show up in GRC
            in_sig=[np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.example_param = example_param


The ''work'' function:
    def work(self, input_items, output_items):
# Has the input ''input_items'' and output ''output_items'' parameters
        """example: multiply with constant"""
# Applies a mathematical operation to ''input_items'' and stores the result in ''output_items''
        output_items[0][:] = input_items[0] * self.example_param
        return len(output_items[0])
 
</syntaxhighlight>
|}
 
 
The <syntaxhighlight lang="python" inline>import</syntaxhighlight> statement includes the NumPy and GNU Radio libraries.
 
The <syntaxhighlight lang="python" inline>__init__</syntaxhighlight> method:
# Accepts the <syntaxhighlight lang="python" inline>example_param</syntaxhighlight> parameter with a default argument of 1.0
# Declares the block to have a <syntaxhighlight lang="python" inline>np.complex64</syntaxhighlight> input and output, which is the GNU Radio <syntaxhighlight lang="text" inline>Complex Float 32</syntaxhighlight> data type
# Stores the <syntaxhighlight lang="python" inline>self.example_param</syntaxhighlight> variable from the input parameter
 
The <syntaxhighlight lang="python" inline>work</syntaxhighlight> method:
# Has the input <syntaxhighlight lang="python" inline>input_items</syntaxhighlight> and output <syntaxhighlight lang="python" inline>output_items</syntaxhighlight> parameters
# Applies a mathematical operation to <syntaxhighlight lang="python" inline>input_items</syntaxhighlight> and stores the result in <syntaxhighlight lang="python" inline>output_items</syntaxhighlight>
# Returns the number of samples produced
# Returns the number of samples produced
The remainder of this tutorial will describe modifications to both the ''init()'' and ''work()'' functions to demonstrate how to provide custom functionality. The ''init()'' and ''work()'' functions cannot be changed arbitrarily as they must conform to the rules and expectations of the broader GNU Radio software framework which controls transferring data between block inputs and outputs. For example, the number of input parameters to the ''init()'' function can be changed to include the different number of variables being passed into the block, however the ''work()'' function must use the pre-existing function definition which includes ''input_items'' and ''output_items'' and in the correct order. Additionally, the '''Embedded Python Block''' must return the number of output samples produced which must be equivalent to the number of input samples produced. If you are creating your first block the suggested path is to follow this tutorial exactly, step by step, and then afterward attempt to modify the working block in small incremental ways as you build up new functionality.
More sophisticated blocks can be produced such as those which incorporate sampling rate change (produce more or less output samples than the number of input samples), blocks which do not have an input or output, or blocks which only produce or consume messages. These blocks can be created through the use of gr_modtool in both [[Creating_C%2B%2B_OOT_with_gr-modtool|c++]] and [[Creating_Python_OOT_with_gr-modtool|Python]]. It is also recommended to review the [[Types_of_Blocks|Types of Blocks]] and [[BlocksCodingGuide|Blocks Coding Guide]] pages.


== Changing Parameter Name ==
== Changing Parameter Name ==


The first step will be to rename ''example_param'' to ''additionFlag'' to be more descriptive. From the editor menu select ''Find and Replace'':
The code is modified to add the custom behavior.
 
The first step is to rename ''example_param'' to ''additionFlag'' to be more descriptive. Assuming your editor is a bit like the GNOME <syntaxhighlight lang="text" inline>gedit</syntaxhighlight> program shown in the screenshots here, from the editor menu select ''Find and Replace'':


[[File:SelectFindAndReplace.png|700px]]
[[File:SelectFindAndReplace.png|700px]]


Enter:
* ''Find'' >  ''example_param''
* ''Replace with'' > ''additionFlag''
* Click ''Replace All''
[[File:FindReplaceExampleParam.png|500px]]
The parameter is changed. The Python code is updated:
<syntaxhighlight lang="python" highlight="16,26,30">
"""
Embedded Python Blocks:
Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""
import numpy as np
from gnuradio import gr
class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""
    def __init__(self, additionFlag=1.0):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Embedded Python Block',  # will show up in GRC
            in_sig=[np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag
    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])
</syntaxhighlight>


Enter ''example_param'' under ''Find'', and ''additionFlag'' under ''Replace with'', and then click ''Replace All'':
Change the default value to be <syntaxhighlight lang="python" inline>True</syntaxhighlight> (so a truth value instead of the floating point number <syntaxhighlight lang="python" inline>1.0</syntaxhighlight>):
<syntaxhighlight lang="python" highlight="16">
"""
Embedded Python Blocks:


[[File:FindReplaceExampleParam.png|500px]]
Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""


import numpy as np
from gnuradio import gr


The parameter will be changed and the Python code will then look like this:


[[File:PythonBlockAdditionFlag.png|700px]]
class blk(gr.sync_block): # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""


    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Embedded Python Block',  # will show up in GRC
            in_sig=[np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag


Now change the default value to be ''True'':
    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])
</syntaxhighlight>


[[File:AdditionFlagDefaultParam.png|700px]]




Line 81: Line 192:




Return back to the GRC window and it will show that the block now displays the ''Additionflag'' parameter instead of ''example_param'':
Return back to the GRC window.
 
The ''Embedded Python Block'' displays the ''Additionflag'' parameter instead of ''example_param'':


[[File:AdditionFlagUpdatedBlock.png|500px]]
[[File:AdditionFlagUpdatedBlock.png|500px]]
Line 87: Line 200:
== Editing Block Inputs ==
== Editing Block Inputs ==


The default block has a single input and a single output, however we need two inputs for the block. To add an input, add a second ''np.complex64'' to the ''in_sig'' list:
The default block has a single input and a single output, however we need two inputs for the block. To add an input, add a second <syntaxhighlight lang="python" inline>np.complex64</syntaxhighlight>np.complex64 to the <syntaxhighlight lang="python" inline>in_sig</syntaxhighlight> list:
 
<syntaxhighlight lang="python" highlight="21">
"""
Embedded Python Blocks:
 
Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""
 
import numpy as np
from gnuradio import gr
 
 
class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""
 
    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Embedded Python Block',  # will show up in GRC
            in_sig=[np.complex64, np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag
 
    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])
</syntaxhighlight>
 
Change the block name to ''Add or Multiply Block'':
 
<syntaxhighlight lang="python" highlight="20">
"""
Embedded Python Blocks:
 
Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""
 
import numpy as np
from gnuradio import gr


[[File:AddSecondInput.png|700px]]


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""


Now change the block name to ''Add or Multiply Block'':
    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Add or Multiply Block',  # will show up in GRC
            in_sig=[np.complex64, np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag


[[File:EditBlockName.png|700px]]
    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])
</syntaxhighlight>




Save the file. GRC will now display the block with a second input and the block name will be updated:
Save the file. GRC displays the block with a second input and the block name is updated:


[[File:SecondInputOnBlockGRC.png|500px]]
[[File:SecondInputOnBlockGRC.png|500px]]
Line 103: Line 279:
== Editing Work Function ==
== Editing Work Function ==


Now that the variable name has been changed the ''work'' function needs to be re-written to provide the desired functionality.
The ''work'' function needs to be modified.


The pseudo code for the Python block is:
The pseudo-code for the Python block is:


<pre>if (additionFlag is True)
<pre>if (additionFlag is True)
Line 114: Line 290:
Modify the ''work'' function so it has the following code:
Modify the ''work'' function so it has the following code:


[[File:EditWorkFunction.png|700px]]
<syntaxhighlight lang="python" highlight="28-34">
"""
Embedded Python Blocks:
 
Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""


import numpy as np
from gnuradio import gr
class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""
    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Add or Multiply Block',  # will show up in GRC
            in_sig=[np.complex64, np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag
    def work(self, input_items, output_items):
        """example: add or multiply based on flag"""
        if self.additionFlag:
            output_items[0][:] = input_items[0][:] + input_items[1][:]
        else:
            output_items[0][:] = input_items[0][:] * input_items[1][:]
        return len(output_items[0])
</syntaxhighlight>


Remember to indent with multiples of 4 spaces (4, 8, 12, etc.) when starting new lines in Python!
Remember to indent with multiples of 4 spaces (4, 8, 12, etc.) when starting new lines in Python!


Save the the code.
Save the the code.


== Connecting the Flowgraph ==
== Connecting the Flowgraph ==
Line 136: Line 345:
== Running the Flowgraph ==
== Running the Flowgraph ==


Selecting ''True'' in the ''Add or Multiply Block'' will perform the addition of the two ''Signal Sources''. Running the flowgraph gives the following two plots:
Selecting ''True'' in the ''Add or Multiply Block'' performs the addition of the two ''Signal Sources''. Running the flowgraph gives the following two plots:


[[File:SinusoidAddition.png|700px]]
[[File:SinusoidAddition.png|700px]]
Line 149: Line 358:


[[File:TimeSinkFullAmplitude.png|700px]]
[[File:TimeSinkFullAmplitude.png|700px]]
Stop the flowgraph by closing the ''QT GUI Time Sink'' or by pressing the square button in GRC:
[[File:StopFlowgraphButtons.png]]




Line 157: Line 370:
Click ''OK'' to save.
Click ''OK'' to save.


By definition, the multiplication of two complex sinusoids produces a sinusoid at the summation of the two frequencies. Therefore, the multiplication of the ''Signal Source'' of frequency 1,000 and frequency 3,000 will be a complex sinusoid of frequency 4,000. This complex sinusoid is seen when running the flowgraph:
By definition, the multiplication of two complex sinusoids produces a sinusoid at the summation of the two frequencies. Therefore, the multiplication of the ''Signal Source'' of frequency 1,000 and frequency 3,000 is a complex sinusoid of frequency 4,000. This complex sinusoid is seen when running the flowgraph:


[[File:MultiplicationSinusoids.png|700px]]
[[File:MultiplicationSinusoids.png|700px]]

Latest revision as of 14:13, 4 May 2024

Beginner Tutorials

Introducing GNU Radio

  1. What is GNU Radio?
  2. Installing GNU Radio
  3. Your First Flowgraph

Flowgraph Fundamentals

  1. Python Variables in GRC
  2. Variables in Flowgraphs
  3. Runtime Updating Variables
  4. Signal Data Types
  5. Converting Data Types
  6. Packing Bits
  7. Streams and Vectors
  8. Hier Blocks and Parameters

Creating and Modifying Python Blocks

  1. Creating Your First Block
  2. Python Block With Vectors
  3. Python Block Message Passing
  4. Python Block Tags

DSP Blocks

  1. Low Pass Filter Example
  2. Designing Filter Taps
  3. Sample Rate Change
  4. Frequency Shifting
  5. Reading and Writing Binary Files

SDR Hardware

  1. RTL-SDR FM Receiver
  2. B200-B205mini FM Receiver

This tutorial shows how to create a signal processing block with the Embedded Python Block. The example block either adds or multiplys the two inputs based on a parameter.

This tutorial uses the Embedded Python Block' which can only be used in the flowgraph it was created in. The tutorial Creating Python OOT with gr-modtool demonstrates how to create a custom Python block as an out-of-tree (OOT) module which can be installed and used in any flowgraph.

The previous tutorial, Hier Blocks and Parameters, describes how to create a hierarchical block and how to use parameters. The next tutorial, Python Block with Vectors, demonstrates how to write an Embedded Python Block with vector inputs and outputs.

Opening Code Editor

The Embedded Python Block is a tool to quickly prototype a block within a flowgraph. Search for the Python Block and add it to the workspace:

AddPythonBlockToWorkspace.png


Double-click the block to edit the properties. The Embedded Python Block has two properties,

  1. Code, a click-box which contains a link to the Python code for the block and
  2. Example_Param, an input parameter to the block.


Click on Open in Editor to edit the Python code:

EmbeddedPythonBlockProperties.png


A prompt is displayed with a choice of which text editor to use to write the Python code. Click Use Default:

ClickUseDefault.png


An editor window displays the Python code for the Embedded Python Block:

PythonCodeGedit.png

Components of a Python Block

There are three important sections in the Python block code:

  1. import statements in a green box
  2. __init__ method in a blue box
  3. work method in a red box
Screenshot of editor window Program code from editor window
height=500px
"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""

import numpy as np
from gnuradio import gr


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""

    def __init__(self, example_param=1.0):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Embedded Python Block',   # will show up in GRC
            in_sig=[np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.example_param = example_param

    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.example_param
        return len(output_items[0])


The import statement includes the NumPy and GNU Radio libraries.

The __init__ method:

  1. Accepts the example_param parameter with a default argument of 1.0
  2. Declares the block to have a np.complex64 input and output, which is the GNU Radio Complex Float 32 data type
  3. Stores the self.example_param variable from the input parameter

The work method:

  1. Has the input input_items and output output_items parameters
  2. Applies a mathematical operation to input_items and stores the result in output_items
  3. Returns the number of samples produced

The remainder of this tutorial will describe modifications to both the init() and work() functions to demonstrate how to provide custom functionality. The init() and work() functions cannot be changed arbitrarily as they must conform to the rules and expectations of the broader GNU Radio software framework which controls transferring data between block inputs and outputs. For example, the number of input parameters to the init() function can be changed to include the different number of variables being passed into the block, however the work() function must use the pre-existing function definition which includes input_items and output_items and in the correct order. Additionally, the Embedded Python Block must return the number of output samples produced which must be equivalent to the number of input samples produced. If you are creating your first block the suggested path is to follow this tutorial exactly, step by step, and then afterward attempt to modify the working block in small incremental ways as you build up new functionality.

More sophisticated blocks can be produced such as those which incorporate sampling rate change (produce more or less output samples than the number of input samples), blocks which do not have an input or output, or blocks which only produce or consume messages. These blocks can be created through the use of gr_modtool in both c++ and Python. It is also recommended to review the Types of Blocks and Blocks Coding Guide pages.

Changing Parameter Name

The code is modified to add the custom behavior.

The first step is to rename example_param to additionFlag to be more descriptive. Assuming your editor is a bit like the GNOME gedit program shown in the screenshots here, from the editor menu select Find and Replace:

SelectFindAndReplace.png

Enter:

  • Find > example_param
  • Replace with > additionFlag
  • Click Replace All

FindReplaceExampleParam.png


The parameter is changed. The Python code is updated:

"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""

import numpy as np
from gnuradio import gr


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""

    def __init__(self, additionFlag=1.0):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Embedded Python Block',   # will show up in GRC
            in_sig=[np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag

    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])

Change the default value to be True (so a truth value instead of the floating point number 1.0):

"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""

import numpy as np
from gnuradio import gr


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""

    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Embedded Python Block',   # will show up in GRC
            in_sig=[np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag

    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])


Save the file:

SaveButtonGedit.png


Return back to the GRC window.

The Embedded Python Block displays the Additionflag parameter instead of example_param:

AdditionFlagUpdatedBlock.png

Editing Block Inputs

The default block has a single input and a single output, however we need two inputs for the block. To add an input, add a second np.complex64np.complex64 to the in_sig list:

"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""

import numpy as np
from gnuradio import gr


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""

    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Embedded Python Block',   # will show up in GRC
            in_sig=[np.complex64, np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag

    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])

Change the block name to Add or Multiply Block:

"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""

import numpy as np
from gnuradio import gr


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""

    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Add or Multiply Block',   # will show up in GRC
            in_sig=[np.complex64, np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag

    def work(self, input_items, output_items):
        """example: multiply with constant"""
        output_items[0][:] = input_items[0] * self.additionFlag
        return len(output_items[0])


Save the file. GRC displays the block with a second input and the block name is updated:

SecondInputOnBlockGRC.png

Editing Work Function

The work function needs to be modified.

The pseudo-code for the Python block is:

if (additionFlag is True)
    then add the two inputs
else
    then multiply the two inputs

Modify the work function so it has the following code:

"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds
to get ports and parameters of your block. The arguments to __init__  will
be the parameters. All of them are required to have default values!
"""

import numpy as np
from gnuradio import gr


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block example - a simple multiply const"""

    def __init__(self, additionFlag=True):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Add or Multiply Block',   # will show up in GRC
            in_sig=[np.complex64, np.complex64],
            out_sig=[np.complex64]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).
        self.additionFlag = additionFlag

    def work(self, input_items, output_items):
        """example: add or multiply based on flag"""
        if self.additionFlag:
            output_items[0][:] = input_items[0][:] + input_items[1][:]
        else:
            output_items[0][:] = input_items[0][:] * input_items[1][:]
        return len(output_items[0])

Remember to indent with multiples of 4 spaces (4, 8, 12, etc.) when starting new lines in Python!

Save the the code.

Connecting the Flowgraph

Return to GRC. Double-click the Add or Multiply Block. Enter True for the Additionflag property:

SetAdditionFlagProperty.png

Click OK to save.

Drag and drop two Signal Source blocks, a Throttle block, a QT GUI Time Sink and a QT GUI Frequency Sink block into the GRC workspace and connect them according to the following flowgraph. Set the frequency of the second Signal Source to 3000:

ConnectAddMultiplyFlowgraph.png

Running the Flowgraph

Selecting True in the Add or Multiply Block performs the addition of the two Signal Sources. Running the flowgraph gives the following two plots:

SinusoidAddition.png


The plots show the summation of the two sinusoids, one at a frequency of 1,000 and another at 3,000. The y-axis in the QT GUI Time Sink plot is partially cutting off the amplitude of the sinusoids. Click the scroll-wheel button to bring up the display menu and select Auto Scale:

SelectAutoScaleTimeSink.png


The full amplitude of the two sinusoids can then be seen:

TimeSinkFullAmplitude.png

Stop the flowgraph by closing the QT GUI Time Sink or by pressing the square button in GRC:

StopFlowgraphButtons.png


Enter False for the Additionflag property:

SetFalseAdditionFlag.png

Click OK to save.

By definition, the multiplication of two complex sinusoids produces a sinusoid at the summation of the two frequencies. Therefore, the multiplication of the Signal Source of frequency 1,000 and frequency 3,000 is a complex sinusoid of frequency 4,000. This complex sinusoid is seen when running the flowgraph:

MultiplicationSinusoids.png


The next tutorial, Python Block With Vectors describes how the Python Embedded Block can be modified to accept vector inputs and outputs, and how the input_items vector indexing is different between vectors and streams.