Embedded Python Block: Difference between revisions

From GNU Radio
Jump to navigation Jump to search
No edit summary
m (Add new path to config.conf on Windows for versions 3.10.10 and later)
 
(23 intermediate revisions by 8 users not shown)
Line 1: Line 1:
[[Category:Block Docs]]
[[Category:Block Docs]]
Allows you to create a new (custom) block, in Python, without needing to make and install an Out of Tree Module (OOT).  The example block simply takes the input stream and multiplies it by a constant.   
<!-- Embedded_Python_Block.mediawiki -->
This block allows you to create a new (custom) block in Python without needing to make and install an Out of Tree (OOT) ModuleWhen you add the block to your flowgraph,  the pre-populated code simply takes the input stream and multiplies it by a constant.  Note that the structure of this Python block matches the structure of an OOT Python block. It's essentially a Python OOT block built into a grc flowgraph.


For some platforms the open with Default Text Editor button may do nothing, in which case you have to manually choose the text editor.
== Initial code in the Python block ==
Here is the pre-populated code:
<syntaxhighlight lang="python">
"""
Embedded Python Blocks:


When you update the Python code, as long as there are no errors the block will automatically update. If you are adding new code and it seems like nothing is changing, there is probably an error in your code.
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])
</syntaxhighlight>
 
== User notes ==
 
=== To revise the Python code ===
* right click on the Embedded Python block
* select <q>Properties</q>
* select <q>Open in Editor</q>
* select <q>Use Default</q> or <q>Choose Editor</q>
* For some platforms the <q>Use Default</q> button may do nothing; see below.
* if you select <q>Choose Editor</q>, the /usr/bin folder will open for you to pick one.
* When you have finished editing, save the file (usually Ctrl-S).
* close the edit window.
* If there are any syntax errors, they are shown in the <q>Properties</q> window.
* select <q>OK</q>. It is not necessary to select <q>Apply</q>.
 
=== To change the default editor (works for Ubuntu and Debian) ===
* open File Manager
* single right click a file of type .py
* left click on 'Properties'
* click on the pull down for 'Open with:'
* click on the editor of your choice
* click OK
 
=== To specify a default editor in Windows ===
* open C:\Users\{Username}\AppData\Roaming\.config\gnuradio\config.conf ('''version 3.10.10 or later''') or C:\Users\{Username}\AppData\Roaming\.gnuradio\config.conf ('''prior to 3.10.10''')
* create that file if it doesn't exist
* add the following to the file and save it
::<syntaxhighlight lang="ini">
[grc]
editor = C:\path\to\your\favorite\editor.exe
</syntaxhighlight>
 
<p>For example, if you want to use Notepad++, download that program from their website. Then:</p>
* Find the path to notepad++.exe, e.g. <q>C:\Program Files\Notepad++\notepad++.exe</q>.
* Use that path for the <q>config.conf</q> file above.
* Terminate and restart GRC.
* When editing the Embedded Python Block, select <q>Open in Editor</q> rather than <q>Choose Editor</q>. GRC will use the editor specified in the <q>config.conf</q> file.
 
=== Important ===
<p>For version 3.8, it is recommended that any flowgraph using an Embedded Python Block be placed in a separate directory (folder); particularly so if more than one Embedded Python Block is used in the same directory. This is because the generation process creates a file <q>epy_block_0.py</q> from that flowgraph. If you generate a different flowgraph in the same directory, its Embedded Python Block will overwrite the file. This problem has been resolved with a new naming convention in 3.9 which prepends the flowgraph name. For example "pkt_31_epy_block_0.py"</p>
 
=== Warning ===
<p>To make changes to the Python code, you <u>must</u> follow the steps above. If you edit the <q>epy_block_0.py</q> file directly and then generate the flowgraph again, your changes will be wiped out! (The most recent edited code is actually stored in the .grc file.)</p>


== Parameters ==
== Parameters ==
You decide the parameters of your custom block =)
The parameters for this block are simply those of your embedded Python block itself. 
 
Note that every parameter needs a default value.  For example, in the code above, note line: <syntaxhighlight lang="python">def __init__(self, example_param=1.0):  # only default arguments here</syntaxhighlight>  If you don't have any parameters, change that line of code to <syntaxhighlight lang="python">def __init__(self):</syntaxhighlight>
 
If you have a vector length equal to a variable of your block, note that the default value must be "hardcoded".


== Example Flowgraph ==
== Example Flowgraph ==


Insert description of flowgraph here, then show a screenshot of the flowgraph and the output if there is an interesting GUICurrently we have no standard method of uploading the actual flowgraph to the wiki or git repo, unfortunately.  The plan is to have an example flowgraph showing how the block might be used, for every block, and the flowgraphs will live in the git repo.
This flowgraph shows how an Embedded Python Block can be used to convert a string from a Message Edit Box to a byte string for the output port. The text is sent through a Throttle to a File Sink. The file sink should have Unbuffered = On. In this particular case, the Throttle block is not required because all blocks will wait for a message string to be entered into the Message Edit Box. In this way the Message Edit Box limits the number of samples produced. Text output is in the black screen at the bottom of the image.
 
[[File:Epy_demo.png|800px]]
 
This is the epy_block_0.py code:
<syntaxhighlight lang="python">
"""
Embedded Python Block demo
"""
 
#  epy_block_0.py
#  created 10/17/2019
 
import numpy as np
from gnuradio import gr
 
import pmt
 
textboxValue = ""
 
class my_sync_block(gr.sync_block):
    """
    reads input from a message port
    outputs text
    """
    def __init__(self):
        gr.sync_block.__init__(self,
            name = "Embedded Python demo",
            in_sig = None,
            out_sig = [np.byte])
        self.message_port_register_in(pmt.intern('msg_in'))
        self.message_port_register_out(pmt.intern('clear_input'))
        self.set_msg_handler(pmt.intern('msg_in'), self.handle_msg)
 
    def handle_msg(self, msg):
        global textboxValue
 
        textboxValue = pmt.symbol_to_string (msg)
        # print (textboxValue)
   
    def work(self, input_items, output_items):
        global textboxValue
 
        # get length of string
        _len = len(textboxValue)
        if (_len > 0):
            # terminate with LF
            textboxValue += "\n"
            _len += 1
            # store elements in output array
            for x in range(_len):
                output_items[0][x] = ord(textboxValue[x])
            textboxValue = ""
            self.message_port_pub(pmt.intern('clear_input'), pmt.intern(''))
            return (_len)
        else:
            return (0)
</syntaxhighlight>
 
Another example can be found in https://github.com/duggabe/gr-morse-code-gen
 
== Comparison between Embedded Python Block and OOT Python Module ==
 
Both the Embedded Python Block and OOT Python Module allow you to use Python code from a GRC flowgraph. The differences between the two are mostly about sharing and maintenance.
 
The embedded block is just as the name suggests, a copy of Python code that is embedded into flowgraphs.  If you want to use a given block in two flowgraphs, you have to make two copies of that blockLike most pieces of software, once you have two copies they tend to diverge over time.  Putting a fix in one doesn't fix the other. 
 
Using a OOT Python Module to hold your Python code allows there to be one central copy that can be shared between flowgraphs that can be updated independently of the flowgraphs that use it.  The cost of these sharing and maintenance benefits is the time needed to set up the OOT-related files that tell GRC how to use the OOT.
 
In many cases the Embedded Python Block is used for smaller, simpler pieces of code, but once a larger, more complex code is developed it often makes sense to package that code in a OOT Python Module.
 
== Types of Python blocks ==
 
The pre-populated code shows the use of a <code>gr.sync_block</code> producing one data item of output for each data item of input (synchronous). Other classes are ''decim_block'' for decimation, ''interp''_block for interpolation, and the generic ''basic_block'' which has no fixed relation between the number of input items consumed and the number of output items produced.
 
More information on creating your own blocks can be found on the following pages:
* [[Types_of_Blocks]] 
* [[BlocksCodingGuide]]
* [[Creating_Python_OOT_with_gr-modtool]]
* [[Creating_C%2B%2B_OOT_with_gr-modtool]]

Latest revision as of 17:21, 1 July 2024

This block allows you to create a new (custom) block in Python without needing to make and install an Out of Tree (OOT) Module. When you add the block to your flowgraph, the pre-populated code simply takes the input stream and multiplies it by a constant. Note that the structure of this Python block matches the structure of an OOT Python block. It's essentially a Python OOT block built into a grc flowgraph.

Initial code in the Python block

Here is the pre-populated 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, 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])

User notes

To revise the Python code

  • right click on the Embedded Python block
  • select Properties
  • select Open in Editor
  • select Use Default or Choose Editor
  • For some platforms the Use Default button may do nothing; see below.
  • if you select Choose Editor, the /usr/bin folder will open for you to pick one.
  • When you have finished editing, save the file (usually Ctrl-S).
  • close the edit window.
  • If there are any syntax errors, they are shown in the Properties window.
  • select OK. It is not necessary to select Apply.

To change the default editor (works for Ubuntu and Debian)

  • open File Manager
  • single right click a file of type .py
  • left click on 'Properties'
  • click on the pull down for 'Open with:'
  • click on the editor of your choice
  • click OK

To specify a default editor in Windows

  • open C:\Users\{Username}\AppData\Roaming\.config\gnuradio\config.conf (version 3.10.10 or later) or C:\Users\{Username}\AppData\Roaming\.gnuradio\config.conf (prior to 3.10.10)
  • create that file if it doesn't exist
  • add the following to the file and save it
[grc]
editor = C:\path\to\your\favorite\editor.exe

For example, if you want to use Notepad++, download that program from their website. Then:

  • Find the path to notepad++.exe, e.g. C:\Program Files\Notepad++\notepad++.exe.
  • Use that path for the config.conf file above.
  • Terminate and restart GRC.
  • When editing the Embedded Python Block, select Open in Editor rather than Choose Editor. GRC will use the editor specified in the config.conf file.

Important

For version 3.8, it is recommended that any flowgraph using an Embedded Python Block be placed in a separate directory (folder); particularly so if more than one Embedded Python Block is used in the same directory. This is because the generation process creates a file epy_block_0.py from that flowgraph. If you generate a different flowgraph in the same directory, its Embedded Python Block will overwrite the file. This problem has been resolved with a new naming convention in 3.9 which prepends the flowgraph name. For example "pkt_31_epy_block_0.py"

Warning

To make changes to the Python code, you must follow the steps above. If you edit the epy_block_0.py file directly and then generate the flowgraph again, your changes will be wiped out! (The most recent edited code is actually stored in the .grc file.)

Parameters

The parameters for this block are simply those of your embedded Python block itself.

Note that every parameter needs a default value. For example, in the code above, note line:

def __init__(self, example_param=1.0):  # only default arguments here

If you don't have any parameters, change that line of code to

def __init__(self):

If you have a vector length equal to a variable of your block, note that the default value must be "hardcoded".

Example Flowgraph

This flowgraph shows how an Embedded Python Block can be used to convert a string from a Message Edit Box to a byte string for the output port. The text is sent through a Throttle to a File Sink. The file sink should have Unbuffered = On. In this particular case, the Throttle block is not required because all blocks will wait for a message string to be entered into the Message Edit Box. In this way the Message Edit Box limits the number of samples produced. Text output is in the black screen at the bottom of the image.

Epy demo.png

This is the epy_block_0.py code:

"""
Embedded Python Block demo
"""

#  epy_block_0.py
#  created 10/17/2019

import numpy as np
from gnuradio import gr

import pmt

textboxValue = ""

class my_sync_block(gr.sync_block):
    """
    reads input from a message port
    outputs text
    """
    def __init__(self):
        gr.sync_block.__init__(self,
            name = "Embedded Python demo",
            in_sig = None,
            out_sig = [np.byte])
        self.message_port_register_in(pmt.intern('msg_in'))
        self.message_port_register_out(pmt.intern('clear_input'))
        self.set_msg_handler(pmt.intern('msg_in'), self.handle_msg)

    def handle_msg(self, msg):
        global textboxValue

        textboxValue = pmt.symbol_to_string (msg)
        # print (textboxValue)
    
    def work(self, input_items, output_items):
        global textboxValue

        # get length of string
        _len = len(textboxValue)
        if (_len > 0):
            # terminate with LF
            textboxValue += "\n"
            _len += 1
            # store elements in output array
            for x in range(_len):
                output_items[0][x] = ord(textboxValue[x])
            textboxValue = ""
            self.message_port_pub(pmt.intern('clear_input'), pmt.intern(''))
            return (_len)
        else:
            return (0)

Another example can be found in https://github.com/duggabe/gr-morse-code-gen

Comparison between Embedded Python Block and OOT Python Module

Both the Embedded Python Block and OOT Python Module allow you to use Python code from a GRC flowgraph. The differences between the two are mostly about sharing and maintenance.

The embedded block is just as the name suggests, a copy of Python code that is embedded into flowgraphs. If you want to use a given block in two flowgraphs, you have to make two copies of that block. Like most pieces of software, once you have two copies they tend to diverge over time. Putting a fix in one doesn't fix the other.

Using a OOT Python Module to hold your Python code allows there to be one central copy that can be shared between flowgraphs that can be updated independently of the flowgraphs that use it. The cost of these sharing and maintenance benefits is the time needed to set up the OOT-related files that tell GRC how to use the OOT.

In many cases the Embedded Python Block is used for smaller, simpler pieces of code, but once a larger, more complex code is developed it often makes sense to package that code in a OOT Python Module.

Types of Python blocks

The pre-populated code shows the use of a gr.sync_block producing one data item of output for each data item of input (synchronous). Other classes are decim_block for decimation, interp_block for interpolation, and the generic basic_block which has no fixed relation between the number of input items consumed and the number of output items produced.

More information on creating your own blocks can be found on the following pages: