Creating OOT Module in GR 4.0
Creating the OOT
In place of modtool, there is a script in the gnuradio
source tree that will perform the same function
python3 $GR_PREFIX/src/gnuradio/utils/modtool/create_mod.py myOOT
This will create a skeleton OOT with the following structure:
gr4-myOOT/ ├── blocklib │ └── myOOT │ ├── include │ │ └── gnuradio │ │ └── myOOT │ │ └── meson.build │ ├── lib │ │ └── meson.build │ ├── python │ │ └── myOOT │ │ └── __init__.py │ └── test │ └── meson.build ├── meson.build └── meson_options.txt
Creating a Block
To create a block, there is a helper scripts in the gnuradio
source tree:
cd gr4-myOOT python3 $GR_PREFIX/src/gnuradio/utils/modtool/create_block.py myblock
This will create the necessary files for compiling a block by placing a minimal set of files in the following structure:
├── blocklib │ └── myOOT │ ├── myblock │ │ ├── myblock_cpu.cc │ │ ├── myblock_cpu.h │ │ └── myblock.yml
All of the files needed to create the block are located in one place, and all of the normal boilerplate will be automatically generated.
From here let's start with the .yml
file
YML File
Top level properties
module: myOOT
block: myblock
label: myblock
blocktype: sync_block
The first section contains the top level properties. Module and block need to match the module/filename.
Label corresponds to how it would be displayed in GRC or other interface.
Blocktype currently can be block
, sync_block
, or grc
and corresponds to the type of block that will be implemented
Parameters
# Example Parameters
parameters:
- id: itemsize
label: Item Size
dtype: size
settable: false
default: 0
grc:
hide: part
Next we declare the parameters in the block. These correspond to the constructor of the block, as well as setters and getters if desired. The example parameter that is given is for an itemsize parameter, which is by default set to 0 - this can be used to attach an untyped port to an arbitrarily typed port, in the case of e.g. a head or throttle block that doesn't really consider the type of the incoming/outgoing data
dtype
dtype
uses sigmf data type names, and should be from the set (listed as c++, python, and grc type)
type_lookup = {
'cf64': ['std::complex<double>', 'complex', 'complex'],
'cf32': ['std::complex<float>', 'complex', 'complex'],
'rf64': ['double', 'float', 'float'],
'rf32': ['float', 'float', 'float'],
'ri64': ['int64_t', 'int', 'int'],
'ri32': ['int32_t', 'int', 'int'],
'ri16': ['int16_t', 'int', 'short'],
'ri8': ['int8_t', 'int', 'byte'],
'ru64': ['uint64_t', 'int', 'int'],
'ru32': ['uint32_t', 'int', 'int'],
'ru16': ['uint16_t', 'int', 'short'],
'ru8': ['uint8_t', 'int', 'byte'],
'size': ['size_t', 'int', 'int'],
'string': ['std::string', 'str', 'string'],
'bool': ['bool', 'bool', 'bool'],
}
id/label
This gives the parameter a name, which will show up in the constructor arguments as well as the pmt
object instantiated to represent this parameter in the base block
settable/gettable
If the settable
is set true
(default false), the parameter can be changed at runtime via an autogenerated callback method
settable=true
also enables a getter method - but settable=false/gettable=true will only provide the getter
default
The value set for default is a hard default in the constructor arguments. All parameters after this must also have a default set. Parameters with a default value are given keyword arguments in the python bindings
grc
The grc section is passed directly to the generated grc bindings as these pertain specifically to grc fields. These include: - category - hide - default (a soft default when a block is placed on the canvas)
Ports
# Example Ports
ports:
- domain: stream
id: in
direction: input
type: untyped
size: parameters/itemsize
- domain: stream
id: out
direction: output
type: untyped
size: parameters/itemsize
The ports section includes both message and streaming ports, which can be of different types
domain
stream
or message
id
The unique (for this block) id for the port
direction
input
or output
type
untyped
- This is a sized port where the block knows nothing about the type of the data being consumed.
{cf32, rf32, ri32, ...}
- The sigmf types described above. Field not used for message ports
size
For untyped
ports, this fixes the size of the port so it is checked at flowgraph initialization. If set to 0, that check is bypassed and it is up to the block to infer the size of the data from the incoming buffer.
Implementations
implementations:
- id: cpu
# - id: cuda
The Implementations section allows multiple implementations for each block. cpu
is the default, which would then automatically generate the compile targets for myblock_cpu.cc
.
If an implementation is python, this needs to be indicated as follows
implementations:
- id: cpu
- id: numpy
lang: python
domain: cpu