diff options
author | Marc <marcll@vt.edu> | 2021-11-19 23:48:03 -0500 |
---|---|---|
committer | mormj <34754695+mormj@users.noreply.github.com> | 2021-11-21 16:21:26 -0500 |
commit | 5d7430508a4c98baa53531442196023a602419a2 (patch) | |
tree | a2646f41ac8d232911ad4e24ac44f314a7601e8f /docs | |
parent | 1fb203afd0dc3ab5681210c94bfbefe26c8e22f5 (diff) |
Docs: updated usage manual export
Diffstat (limited to 'docs')
11 files changed, 516 insertions, 627 deletions
diff --git a/docs/usage-manual/(exported from wiki) GNURadioCompanion.txt b/docs/usage-manual/(exported from wiki) GNURadioCompanion.txt deleted file mode 100644 index 414fff03e0..0000000000 --- a/docs/usage-manual/(exported from wiki) GNURadioCompanion.txt +++ /dev/null @@ -1,439 +0,0 @@ -<page> - <title>GNURadioCompanion</title> - <ns>0</ns> - <id>47</id> - <revision> - <id>6247</id> - <parentid>6246</parentid> - <timestamp>2019-10-22T15:57:56Z</timestamp> - <contributor> - <username>Duggabe</username> - <id>846</id> - </contributor> - <minor/> - <comment>/* Filter Design correct grammer */</comment> - <model>wikitext</model> - <format>text/x-wiki</format> - <text xml:space="preserve" bytes="22162">[[Category:Usage Manual]] -= GNU Radio Companion = - -GNU Radio Companion (GRC) is a graphical tool for creating signal flow graphs and generating flow-graph source code. There is a [[WorkingGroups|GNU Radio Companion Working Group]] handling the development. - -== New Features == - -What has changed in GRC since the stable 0.70 release? - -* Bundled - GRC is now bundled with the gnuradio source. If all dependencies are met, grc will be installed with gnuradio. *See the Installation and Execution section. - -* Desktop Integration - GRC can be fully integrated into a desktop environment that supports freedesktop standards (courtesy of xdg-utils): icons, mime type, and menu items - -* Code Generation - GRC no longer uses a single executable to load a *.grc.xml file and dynamically build the flow graph. Rather, GRC uses Cheetah templates to generate the python source code for the flow graph. GRC can generate source code for WX GUI and non-GUI flow graphs as well as hierarchical blocks. - -* Documentation - GRC can extract documentation for GNU Radio blocks directly from the doxygen-generated xml files. You will need to configure your gnuradio installation with doxygen support to use this feature. - -* Variables - The variable editor window of past releases has been replaced with variable blocks. Variable blocks show up in the flow graph and act like any other block, except that they have no IO ports. The variable block maps a unique id (variable name) to a particular value. GRC also has several graphical variable blocks that allow one to create WX GUI flow graphs with graphical controls using sliders, text boxes, buttons, drop downs, and radio buttons. - -* Block Definitions - Every block in GRC has a corresponding xml file that contains parameters, IO ports, and a template for code generation. The id key and file name of each xml file matches up exactly with the name of the GNU Radio block to ensure future portability. GRC validates all blocks definitions upon execution, and will exit with error if any definitions fail the validation. - -* File Format - Given that the variables and block definitions have changed, the internal structure of the saved flow graph files has also changed. Fortunately, GRC can automatically convert older saved flow graph files to the new format. Unfortunately, the conversion cannot always be 100%. - -* Block Manipulation - Ever wish that you could take a block out of your transmit/receive chain, but not delete it? Blocks now have an enabled/disabled state. By default, a block is enabled. When disabled, a block is grayed out in the flow graph and will be ignored by the flow graph validator and by the source code generator. In addition, blocks may be cut, copied, and pasted within flow graphs and into other flow graphs. - -* Hierarchical Blocks - GRC can create hierarchical blocks out of the built-in blocks. *See the section on hierarchical blocks. - -== Requirements == - -=== GRC Requirements === - -Most (if not all) of these requirements can be found in the package manager of you linux distribution. - -* [Python 2.5 (or above) http://www.python.org/download/] - -* [Python-LXML 2.0 (or above) http://codespeak.net/lxml/installation.html] - -* [Cheetah Template Engine 2.0 (or above) http://www.cheetahtemplate.org/download.html] - -* [Python-GTK 2.10 (or above) http://www.pygtk.org/downloads.html] - -=== GNU Radio Requirements === - -GRC is bundled with the current gnuradio trunk and will be included in the 3.2 release. I recommend configuring your GNU Radio installation with wx-python, usrp, and audio support. However, any configuration will work (see note). Follow the [http://gnuradio.org/trac/wiki/BuildGuide build-guide] - -Note: GRC will let you generate flow graphs with components that are not included in your local install. For example, you can generate a flow graph with a usrp source, but the code will error when executed unless GNU Radio is configured with usrp support. - -== Installation == - -=== Installing GRC === - -GRC is bundled with gnuradio, so following the [[InstallingGR|installation guide]] should be enough to install GRC. However, GRC will not be installed if you were missing any of the required components. Install any missing components and re-run configure/install: - -<pre> -cmake .. -make -sudo make install -</pre> -GRC should appear in the list of configured components; if not, read the configure verbose for errors. - -=== Installing Documentation === - -To view the blocks' documentation from inside GRC, install doxygen and configure gnuradio with doxygen support. - -=== Installing Icons, Mime Type, and Menu Items === - -If you have an operating system that supports the freedesktop.org standards (Gnome, KDE, XFCE), then you may install the icons, mime type, and menu items bundled with GRC. After installing GRC, run the following command: - -<pre> -cd /libexec/gnuradio/ -sudo ./grc_setup_freedesktop install -</pre> - -== Execution == - -GRC installs several executable python files into your system's path. - -=== Executing the Flow Graph Editor === - -Open a terminal and enter the following: - -<pre> -gnuradio-companion -</pre> - -== Usage Tips == - -* Add a block: double click on a block in the block selection window. - -* Connect blocks: click on the port of one block, then click on the port of another block. - -* Remove a connection: click on the connection, press delete, or drag the connection to remove. - -* Edit block parameters: double click on a block in the flow graph. - -* Select a block, hit up/down for quick type change. - -* For more short cuts, see the hot keys in the menu. - -* Flow graphs that are completely simulation (without audio or usrp blocks) will consume 100% CPU when executed, and the GUI elements will be unresponsive. These flow graphs must include a Misc->Throttle block to throttle the rate of the streaming data. - -== Variables == - -Variables map symbolic names to values. In GRC, a variable can define a global constant or, a variable can be used in conjunction with a GUI to control a running flow graph. - -=== Variable Block === - -The variable block is the most basic way to use a variable in GRC. The ID parameter of the variable block is the "symbolic name". The symbolic name must be alpha-numeric (underscores allowed) and begin with a letter. To use the variable, simply enter the symbolic name into a parameter of another block. - -=== Variable Controls === - -Certain blocks have callback methods that allow their parameters to be changed while executing flow graph. Variable controls in GRC use variables in combination with callback methods to modify these parameters. If a parameter has a callback method, the parameter will be underlined in the block-properties dialog. The variable slider, variable text box, and the variable chooser block provide graphical widgets such as sliders, text boxes, radio buttons, and drop downs as variable controls. In addition, the [[Probe Signal]] combined with [[Function Probe]] blocks takes samples from a gnuradio stream and writes the samples to a variable. - -=== String Evaluation === - -String parameters have a two-phase evaluation. First, GRC evaluates the parameter as-is. If the parameter does not evaluate to a string data type or the evaluation fails, then it is understood that the parameter had implied quotation. In this case, GRC will evaluate the parameter again with quotation marks; which will return a string with the exact code that was typed into the parameter window. - -To use a variable inside a string simply type the name of the variable into the parameter: ''my_var''. If the variable is not a string, cast the variable with python's str function: ''str(my_var)''. Standard python string functionality applies: ''"My Var = " + str(my_var)''. - -*Note: String parameter types also include the file open and file save types. - -== Filter Design == - -Many blocks in gnuradio take an array of complex or real taps as a parameter. GNU Radio provides a package to generate all kinds of filter and window taps. See the [https://www.gnuradio.org/doc/doxygen/classgr_1_1filter_1_1firdes.html firdes package] from the gnuradio documentation. - -=== Firdes Taps Generators === - -* low_pass(gain, samp_rate, cutoff_freq, width, [window], [beta]) - -* high_pass(gain, samp_rate, cutoff_freq, width, [window], [beta]) - -* band_pass(gain, samp_rate, low_cutoff_freq, high_cutoff_freq, width, [window], [beta]) - -* complex_band_pass(gain, samp_rate, low_cutoff_freq, high_cutoff_freq, width, [window], [beta]) - -* band_reject(gain, samp_rate, low_cutoff_freq, high_cutoff_freq, width, [window], [beta]) - -* gaussian(gain, spb, bt, int ntaps) - -* hilbert(int ntaps, window, beta) - -* root_raised_cosine(gain, samp_rate, symbol_rate, alpha, int ntaps) - -* window(window, int ntaps, beta) - -=== Firdes Window Types === - -* WIN_HAMMING - -* WIN_HANN - -* WIN_BLACKMAN - -* WIN_RECTANGULAR - -* WIN_KAISER - -=== Firdes Notes === - -For the pass band functions, window defaults to the Hamming window. The beta parameter defaults to 6.76, and only applies to the Kaiser window. - -=== Firdes Usage Example === - -Create a new "import" block, and enter the following for the import parameter: - -<pre>from gnuradio.filter import firdes</pre> -Note: Most blocks with a taps parameter automatically import the firdes module. You only need to use the import block when firdes will not evaluate. - -Enter the following into the taps parameter of a filter block: - -<pre> -firdes.low_pass(1.0, samp_rate, 1000, 100, fft.window.WIN_HAMMING) -</pre> -=== Use Taps from a File === - -Suppose that you have an array of filter taps stored in a file that you would like to use inside GRC: - -Create a new "import" block, and enter the following for the import parameter: - -<pre> -import numpy -</pre> -Enter the following into the taps parameter of a filter block: - -<pre> -numpy.fromfile('taps file path', numpy.complex64).tolist() -</pre> -This will read an entire binary file and parse every 64 bytes into a complex number. Use numpy.float32 for real taps. See the numpy.fromfile help for more advanced usage: - -<pre> -Help on built-in function fromfile in numpy: - -numpy.fromfile = fromfile(...) - fromfile(file=, dtype=float, count=-1, sep=_) -> array. - - Required arguments: - file -- open file object or string containing file name. - - Keyword arguments: - dtype -- type and order of the returned array (default float) - count -- number of items to input (default all) - sep -- separater between items if file is a text file (default "") - - Return an array of the given data type from a text or binary file. The - 'file' argument can be an open file or a string with the name of a file to - read from. If 'count' == -1 the entire file is read, otherwise count is the - number of items of the given type to read in. If 'sep' is "" it means to - read binary data from the file using the specified dtype, otherwise it gives - the separator between elements in a text file. The 'dtype' value is also - used to determine the size and order of the items in binary files. - -</pre> - -== Grid Positioning == - -GRC offers several graphical sinks and graphical controls for creating wx-gui flow graphs. (scope sink, fft sink, number sink, waterfall sink, constellation sink, slider control, and chooser control) Each of these graphical elements have a grid position parameter for precise positioning. - -A grid position parameter is a list of 4 integers of the form (row, column, row span, column span). The row and column specify the position of the upper-left corner of the graphical element. The smallest position, the (0, 0) position, specifies the upper-left corner of the grid. - -If left blank, the grid parameter specifies that the graphical element will be automatically stacked into a vertical sizer. The vertical sizer is positioned directly above the grid sizer. If you do not want any elements to be added to the vertical sizer, leave no grid parameters blank. - -The row and column span specify the stretch, or the number of rows and columns that the graphical element can occupy. The row span specifies the number of rows down from the row positon, and the column span specifies the number of columns right of the column position. Therefore, the span must be at least (1, 1) to occupy the minimum of 1 grid cell. - -=== Example === - -The user wishes to place a slider, centered directly above a graphical sink. The slider will be positioned at the 2nd column of the top row and with a column span of 2. The sink will be positioned on the 2nd row, and with a row span of 2 and a column span of 4. Notice the grid parameters below, and the resulting gui layout: - -'''The Elements:''' - -'''''' '''Slider Control: (0, 1, 1, 2)''' - -'''''' Graphical Sink: (1, 0, 2, 4) - -'''The Resulting GUI:''' - -{| -| - -| 0,0 -| - -| '''0,1''' -| - -| '''0,2''' -| - -| 0,3 -| - -|- -| - -| 1,0 -| - -| 1,1 -| - -| 1,2 -| - -| 1,3 -| - -|- -| - -| 2,0 -| - -| 2,1 -| - -| 2,2 -| - -| 2,3 -| - -|} - -== Hierarchical Blocks == - -GRC can create hierarchical blocks out of the built-in blocks. Hierarchical blocks can be instantiated inside of other grc flow graphs. The python code generated from a hierarchical block can itself be used in non-GRC flow graphs. Four important blocks are used in the creation of a hierarchical block: The options block, parameter blocks, and the pad source and pad sink. - -=== The Options Block === - -In order to make a hierarchical block, the parameters in the options block must be set properly. The id of the options block sets the module name, and must be unique among the entire library of blocks (built-in and custom). The title parameter sets the display name for the block. The generate options must be set to "Hier Block". The category parameter sets the category for the new block. This category can be an existing category in the block selection window or a new category. Categories may be nested by specifying a name with slashes, ex: Custom/Filters. To put blocks into the root category, specify a single slash "/" (a blank category will hide your block). - -=== Parameter Blocks === - -Parameter blocks specify variables in your hierarchical block that should be configurable in the top level block. Parameter blocks work much like variable blocks with a few exceptions: Parameters blocks cannot depend on variable blocks or other parameter blocks. Parameter blocks have a label parameter for display purposes. Parameter blocks take the place of a variable block, do not try to create a variable block with the same id as your parameter block. - -=== Pad Source and Sink Blocks === - -The pad source and sink blocks create inputs and outputs for the hierarchical block. The pad blocks have configurable data types, vector lengths, and number of ports. A flow graph can have at most, one pad source, and one pad sink. A hierarchical block may have one pad sink and no pad source or no pad sink and one pad source, but it must have at least one pad block. - -=== Creating and Instantiating === - -* Start with a blank slate and create a new (empty) flow graph. - -* Setup the options block as described above, with the id, title, generate options, and category. - -* Add parameter blocks for all variables you wish to configure/control outside of the block. - -* Create at most one pad source and one pad sink to match the IO type and connect them. - -* When finished, click the generate button, and close/reopen GRC. - -* The hierarchical block will appear in the block selection window. - -* Add the hierarchical block to a flow graph as your would any other block. - -=== Notes === - -* After making changes to your hierarchical block, make sure to regenerate, and reopen GRC before usage. - -* The ID parameter of the block must be unique. If two blocks share the same ID, the last one to be generated will overwrite the other. - -* Custom hierarchical blocks may instantiate other custom hierarchical blocks. Just don't have a block instantiate itself! - -== Adding Custom Blocks == - -Every block in GRC corresponds to an XML file that describes the block's parameters, inputs, outputs, and other attributes. Adding a custom block into GRC is simply a matter of creating one of these XML block definition files. A few caveats: - -* The block should be accessible from the python path. Meaning that the block can be accessed via an import statement. -* The block follows the block diagram model: it has parameters, inputs, and outputs. If the block requires some kind of listening thread, or special callback methods to move the data (as in the blks2 packet stuff), it cannot be used in GRC (unless this "special" functionality can be encapsulated into a block that is block-diagram-safe). -* If GRC is missing a block definition for a block that is currently in the trunk, or one of the block definitions is missing functionality, please mail the list. The block definitions in the GRC trunk must stay in sync with the actual GNU Radio blocks. - -=== Creating the XML Block Definition === - -The best way to learn how to create the xml file is to learn by example. See the block definitions (source:grc/blocks) packaged with GRC, and read through a few files. Essentially, all block definitions are structured as follows: - -<pre> - My Block Name - my_package_my_block_ff - Filters - from gnuradio import my_package - my_package.my_block_ff($param1, $param2) - set_param1($param1) - - Parameter 1 - param1 - real - - - Parameter 2 - param2 - 1 - int - - - in - float - part - - - out - float - - - out - float -</pre> - -* The example above will make a block with 2 parameters, 1 input, and 2 outputs. -* The ordering of the tags is important, if tags are not ordered properly, the block will fail validation. See [https://github.com/gnuradio/gnuradio/blob/master/grc/core/block.dtd block.dtd] for specifics. -* The '''name''' tags dictate the label text for the block, parameters, and ports. -* The '''key''' tags are unique identifiers, they may not contain spaces. The block key must be globally unique among all blocks in GRC. The parameter keys must be unique only within the block. -* The '''category''' tag is a unix-style path that represents the location of the block inside the block selection window. The path can be a new category (Custom), or represent a sub-category (Filters/Custom). To put a block into the root category, just use a single slash (/) for the root path. -* The '''import''' tag (there can be multiple) must be a valid python import statement to the module containing your block. -* The '''make''' tag contains the code necessary to construct your block. This code is essentially a cheetah template nested inside an xml tag. Upon code generation, the template performs a text substitution on the "$" parameters. For more advanced capabilities, see the [http://cheetahtemplate.org/users_guide/index.html cheetah template documentation.] -* The '''callback''' tag registers a set-method from your custom block. Once the set-method is registered, the set-method can be called at runtime when a variable is changed. There can be any number of '''callback''' tags, one for each set-method of your block. Or no '''callback''' tags if this is not applicable. -* For the '''param''' tags, the commonly used values for the '''type''' tags are: complex, real, int, complex_vector, real_vector, int_vector, string, and raw. The ''raw'' type allows any value to be used without performing type checking. The ''real'' type should be used for single and double precision floating point numbers. The ''int'' type should be used for longs, ints, shorts, and chars. -* The hide tag controls how the parameter is displayed in GRC. It's either none, part (show in the prop dialog, not in the block on the canvas) or all. -* The '''sink''' tag represents an input port, and the '''source''' tag represents an output port. The allowed values for the '''type''' tags are: complex, float, int, short, and byte. For ports with a vector length, specify a '''vlen''' tag after the '''type''' tag. -* In case you want to create a block definition as done for the FECAPI, the '''key''' tag has to start with '''variable_''' in order to work correctly in GRC. - -=== Some Example Definitions === - -* Simple Example: "Complex to Real" source:[https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/grc/blocks_complex_to_real.xml|gr-blocks/grc/blocks_complex_to_real.xml] -* Multiple Callbacks: "Costas Loop" source:[https://github.com/gnuradio/gnuradio/blob/master/gr-digital/grc/digital_costas_loop_cc.xml|gr-digital/grc/digital_costas_loop_cc.xml] -* Vlen Example: "Throttle" source:[https://github.com/gnuradio/gnuradio/blob/master/gr-digital/grc/blocks_throttle.xml|gr-digital/grc/blocks_throttle.xml] -* Advanced Make: "FFT" source:[https://github.com/gnuradio/gnuradio/blob/master/gr-fft/grc/fft_fft_vxx.xml|gr-fft/grc/fft_fft_vxx.xml] - -=== Installing the XML Block Definition === - -There are many methods to tell grc about your new xml file. Choose one of the following methods... - -==== Method 1: Default Hier Block Location ==== - -Create the _'''.xml_ file inside'''<sub>/.grc_gnuradio/* where</sub> is your home directory. If the directory does not exist, create it: ''mkdir ~/.grc_gnuradio/'' - -==== Method 2: Configuration File ==== - -Create or edit '''~/.gnuradio/config.conf''' and add the following lines: - -<pre>[grc] -local_blocks_path=/path/to/my/blocks</pre> -The local_blocks_path can contain multiple paths separated by colons: local_blocks_path=/path/to/blocks1:/path/to/blocks2 - -==== Method 3: Environment Variable ==== - -Set the '''GRC_BLOCKS_PATH''' environment variable to a path that contains your custom block wrapper. The GRC_BLOCKS_PATH can contain multiple paths separated by colons: GRC_BLOCKS_PATH=/path/to/blocks1:/path/to/blocks2 - -== Special Thanks == - -* '''[http://www.cer.jhu.edu/ CER Technology Fellowship]:''' initial funding -* '''[http://www.ece.jhu.edu/~cooper/ A. Brinton Cooper]:''' starting the project -* '''Patrick Mulligan:''' starting the project -* '''William R. Kenan Jr. Fund:''' usrp & computers -* '''Patrick Strasser:''' the GRC icon - -== Screen Shots == - -[http://www.joshknows.com/grc#screenshots Screen Shots] - -Feel free to submit your own screen shots or flow graphs.</text> - <sha1>e27hwprs9rgxfxv6iiy7zns4v9kmde5</sha1> - </revision> - </page> -</mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Logging.txt b/docs/usage-manual/(exported from wiki) Logging.txt index cd49e4ff54..2a9366b560 100644 --- a/docs/usage-manual/(exported from wiki) Logging.txt +++ b/docs/usage-manual/(exported from wiki) Logging.txt @@ -3,16 +3,16 @@ <ns>0</ns> <id>3484</id> <revision> - <id>4871</id> - <parentid>4183</parentid> - <timestamp>2019-03-12T22:43:41Z</timestamp> + <id>8632</id> + <parentid>8605</parentid> + <timestamp>2021-05-31T14:39:04Z</timestamp> <contributor> - <username>777arc</username> - <id>632</id> + <ip>172.18.0.3</ip> </contributor> + <comment>/* Logging from Python */ small typo fix</comment> <model>wikitext</model> <format>text/x-wiki</format> - <text xml:space="preserve" bytes="9393">[[Category:Usage Manual]] + <text xml:space="preserve" bytes="10203">[[Category:Usage Manual]] GNU Radio has a logging interface to enable various levels of logging information to be printed to the console or a file. The logger derives from [http://log4cpp.sourceforge.net log4cpp] which is readily @@ -38,8 +38,8 @@ to disable a logger. === Logging Configuration === -The logging configuration can be found in the gnuradio-runtime.conf file -under the [LOG] section. This allows us fairly complete control over +The logging configuration can be found in the '''<prefix>/etc/gnuradio/conf.d/gnuradio-runtime.conf''' file +under the '''[LOG]''' section. This allows us fairly complete control over the logging facilities. The main configuration functions are to set up the level of the loggers and set the default output behavior of the loggers. @@ -148,27 +148,27 @@ For the following examples, we will assume that our local debug_level = Off Inside of the default configuration file, we define the parameters -for the two logger's, the standard logger the separate debug logger. +for the two loggers, the standard logger and the separate debug logger. If the levels of the two loggers are specified in our configuration file, as in the above example, these levels override any levels -specified in the XML file. Here, we have turned on the standard logger +specified in the log_config file. Here, we have turned on the standard logger (d_logger) to all levels and turned off the debug logger (d_debug_logger). So even if the debug logger is used in the code, it will not actually output any information. Conversely, any level of output passed to the standard logger will output because we have turned this value to the lowest level "debug." -If both an XML configuration file is set and the "log_file" or +If both an log_config configuration file is set and the "log_file" or "debug_file" options are set at the same time, both systems are actually used. So you can configure file access and the pattern -through the XML file while also still outputting to stdout or stderr. +through the log_config file while also still outputting to stdout or stderr. == Advanced Usage == The description above for using the logging facilities is specific to GNU Radio blocks. We have put the code necessary to access the -debugger into the gr_block parent class to simplify access and make +logger into the gr_block parent class to simplify access and make sure all blocks have the ability to quickly and easily use the logger. For non gr_block-based code, we have to get some information about the @@ -208,9 +208,29 @@ directory of the OOT module. Once these CMake changes are made, the GR logging interface will function as documented on this page. + + +==== Current GNU Radio 3.9 March 2020 ==== +With the current ''master'' branch, logging got way easier. In your OOT module, just write: +<syntaxhighlight lang="c++"> +GR_LOG_INFO(this->d_logger, "My INFO message"); +</syntaxhighlight> +for logging to the standard logger with level 'INFO'. More generally, we'd use: +<syntaxhighlight lang="c++"> +GR_LOG_<LOGLEVEL>(this->d_logger, "My <LOGLEVEL> message"); +</syntaxhighlight> + +It is not necessary to apply any CMake changes or to include additional headers. +Be aware that +<syntaxhighlight lang="c++" inline> +this->d_logger +</syntaxhighlight> +implies that you call the logger within a class inherited from +<syntaxhighlight lang="c++" inline>gr::block</syntaxhighlight>. + == Logging from Python == -The logging capability has been brought out python via swig. The configuration +The logging capability has been brought out python via swig (<=v3.8) or pybind11 (>=v3.9). The configuration of the logger can be manipulated via the following calls: from gnuradio import gr @@ -219,13 +239,13 @@ of the logger can be manipulated via the following calls: gr.logger_reset_config() # Resets logger config by removing all appenders Once the logger is configured you can manipulate a logger via a wrapper class gr.logger(). -You can isntantiate this by the following. (Reference logger.h for list of methods) +You can instantiate this by the following. (Reference logger.h for list of methods) from gnuradio import gr log=gr.logger("nameOfLogger") log.debug("Log a debug message") log.set_level("INFO");</text> - <sha1>iihlmg3bam2b9a6q1yy1inpzt8az1ve</sha1> + <sha1>s1zlqhtu3momfl66ryql76uguo9567g</sha1> </revision> </page> </mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Message Passing.txt b/docs/usage-manual/(exported from wiki) Message Passing.txt index 18dd28407e..37168fff64 100644 --- a/docs/usage-manual/(exported from wiki) Message Passing.txt +++ b/docs/usage-manual/(exported from wiki) Message Passing.txt @@ -3,17 +3,17 @@ <ns>0</ns> <id>3481</id> <revision> - <id>6529</id> - <parentid>4868</parentid> - <timestamp>2020-01-13T17:35:27Z</timestamp> + <id>8794</id> + <parentid>8615</parentid> + <timestamp>2021-10-05T23:22:38Z</timestamp> <contributor> - <username>Mormj</username> - <id>802</id> + <username>777arc</username> + <id>632</id> </contributor> - <comment>/* Message Handler Functions */</comment> + <comment>Added blurb about having to edit yaml</comment> <model>wikitext</model> <format>text/x-wiki</format> - <text xml:space="preserve" bytes="15759">[[Category:Usage Manual]] + <text xml:space="preserve" bytes="20661">[[Category:Usage Manual]] == Introduction == GNU Radio was originally a streaming system with no other mechanism to @@ -43,6 +43,8 @@ The message passing interface heavily relies on Polymorphic Types (PMTs) in GNU Radio. For further information about these data structures, see the page [[Polymorphic Types (PMTs)]]. +[[File:Gnuradio_pdu_illustration.png|550px|Gnuradio_pdu_illustration.png]] + == Message Passing API == The message passing interface is designed into the gr::basic_block, @@ -91,16 +93,12 @@ message to that block's message queue. A subscriber block must declare a message handler function to process the messages that are posted to it. After using the gr::basic_block::message_port_register_in to declare a subscriber port, we -must then bind this port to the message handler. For this, we use -Boost's 'bind' function: - - set_msg_handler(pmt::pmt_t port_id, - boost::bind(&block_class::message_handler_function, this, _1)); +must then bind this port to the message handler. -Or starting in GNU Radio 3.8 using C++11 we can avoid Boost and express the binding as a lambda function (preferred) +Starting in GNU Radio 3.8ยน using C++11 we do that using a lambda function: set_msg_handler(pmt::pmt_t port_id, - [this](pmt::pmt_t msg) { this->message_handler_function(msg); }); + [this](const pmt::pmt_t& msg) { message_handler_function(msg); }); In Python: @@ -110,14 +108,12 @@ When a new message is pushed onto a port's message queue, it is this function that is used to process the message. The 'port_id' is the same PMT as used when registering the input port. The 'block_class::message_handler_function' is the member -function of the class designated to handle messages to this port. The -'this' and '_1' are standard ways of using the Boost bind function to -pass the 'this' pointer as the first argument to the class (standard -OOP practice) and the _1 is an indicator that the function expects 1 -additional argument. The prototype for all message handling functions +function of the class designated to handle messages to this port. + +The prototype for all message handling functions is: - void block_class::message_handler_function(pmt::pmt_t msg); + void block_class::message_handler_function(const pmt::pmt_t& msg); In Python the equivalent function would be: @@ -129,8 +125,8 @@ We give examples of using this below. From the flowgraph level, we have instrumented a gr::hier_block2::msg_connect method to make it easy to subscribe blocks to other blocks' -messages. Assume that the block \b src has an output message port named -\a pdus and the block \b dbg has an input port named \a print. The message +messages. Assume that the block '''src''' has an output message port named +''pdus'' and the block '''dbg''' has an input port named ''print''. The message connection in the flowgraph (in Python) looks like the following: self.tb.msg_connect(src, "pdus", dbg, "print") @@ -168,7 +164,7 @@ can call a block's gr::basic_block::_post method directly and pass it a message. So any block with an input message port can receive messages from the outside in this way. -The following example uses a gr::pdu::pdu_to_tagged_stream block +The following example uses a gr::blocks::pdu_to_tagged_stream block as the source block to a flowgraph. Its purpose is to wait for messages as PDUs posted to it and convert them to a normal stream. The payload will be sent on as a normal stream while the meta data will be @@ -225,20 +221,21 @@ However, there are some very good reasons to stick to this format: == Code Examples == +Note, in addition to the C++ or Python code below, if adding message passing to a block, you need to edit the block's YAML file as well. Add the corresponding input or output entries (ensuring the domain is set to "message"). You do not need to specify a dtype. You should also ensure that the label value corresponds to the registered port name. See [[YAML_GRC#Inputs_and_Outputs_.28optional.29 | here]] for more details. + === C++ === The following is snippets of code from blocks currently in GNU Radio that take advantage of message passing. We will be using -gr::blocks::message_debug and gr::pdu::tagged_stream_to_pdu below +gr::blocks::message_debug and gr::blocks::tagged_stream_to_pdu below to show setting up both input and output message passing capabilities. The gr::blocks::message_debug block is used for debugging the message -passing system. It describes three input message ports: ''print'', -''store'', and ''pdu_print''. The ''print'' port simply prints out all +passing system. It describes two input message ports: ''print'' and +''store''. The ''print'' port simply prints out all messages to standard out while the ''store'' port keeps a list of all -messages posted to it. The ''pdu_print'' port specially formats PDU -messages for printing to standard out. The ''store'' port works in -conjunction with a gr::blocks::message_debug::get_message(int i) call +messages posted to it. The ''store'' port works in +conjunction with a gr::blocks::message_debug::get_message(size_t i) call that allows us to retrieve message i afterward. The constructor of this block looks like this: @@ -247,32 +244,27 @@ The constructor of this block looks like this: { message_port_register_in(pmt::mp("print")); set_msg_handler(pmt::mp("print"), - boost::bind(&message_debug_impl::print, this, _1)); + [this](const pmt::pmt_t& msg) { print(msg); }); message_port_register_in(pmt::mp("store")); set_msg_handler(pmt::mp("store"), - boost::bind(&message_debug_impl::store, this, _1)); - - message_port_register_in(pmt::mp("print_pdu")); - set_msg_handler(pmt::mp("print_pdu"), - boost::bind(&message_debug_impl::print_pdu, this, _1)); + [this](const pmt::pmt_t& msg) { store(msg); }); } </syntaxhighlight> The three message input ports are registered by their respective names. We then use the gr::basic_block::set_msg_handler function to -identify this particular port name with a callback function. The -Boost ''bind'' function ([http://www.boost.org/doc/libs/1_52_0/libs/bind/bind.html Boost::bind]) -here binds the callback to a function of this block's class. So now +identify this particular port name with a callback function. + +So now the functions in the block's private implementation class, -gr::blocks::message_debug_impl::print, -gr::blocks::message_debug_impl::store, and -gr::blocks::message_debug_impl::print_pdu, are assigned to handle +gr::blocks::message_debug_impl::print and +gr::blocks::message_debug_impl::store are assigned to handle messages passed to them. Below is the ''print'' function for reference. <syntaxhighlight lang="cpp"> void - message_debug_impl::print(pmt::pmt_t msg) + message_debug_impl::print(const pmt::pmt_t& msg) { std::cout << "***** MESSAGE DEBUG PRINT ********\n"; pmt::print(msg); @@ -284,12 +276,14 @@ The function simply takes in the PMT message and prints it. The method pmt::print is a function in the PMT library to print the PMT in a friendly and (mostly) pretty manner. -The gr::pdu::tagged_stream_to_pdu block only defines a single +The gr::blocks::tagged_stream_to_pdu block only defines a single output message port. In this case, its constructor contains the line: - { +<syntaxhighlight lang="cpp"> +{ message_port_register_out(pdu_port_id); - } +} +</syntaxhighlight> So we are only creating a single output port where ''pdu_port_id'' is defined in the file pdu.h as ''pdus''. @@ -306,7 +300,7 @@ dictionary while the data segment is a PMT uniform vector of either bytes, floats, or complex values. In the end, when a PDU message is ready, the block calls its -gr::pdu::tagged_stream_to_pdu_impl::send_message function that is +gr::blocks::tagged_stream_to_pdu_impl::send_message function that is shown below. <syntaxhighlight lang="cpp"> @@ -335,14 +329,14 @@ is published that is important to this discussion. Here, the block posts the PDU message to any subscribers by calling gr::basic_block::message_port_pub publishing method. -There is similarly a gr::pdu::pdu_to_tagged_stream block that essentially +There is similarly a gr::blocks::pdu_to_tagged_stream block that essentially does the opposite. It acts as a source to a flowgraph and waits for PDU messages to be posted to it on its input port ''pdus''. It extracts the metadata and data and processes them. The metadata dictionary is split up into key:value pairs and stream tags are created out of them. The data is then converted into an output stream of items and passed along. The next section describes how PDUs can be passed into a -flowgraph using the gr::pdu::pdu_to_tagged_stream block. +flowgraph using the gr::blocks::pdu_to_tagged_stream block. === Python === @@ -363,8 +357,85 @@ A Python Block example: def handle_msg(self, msg): self.message_port_pub(pmt.intern('msg_out'), pmt.intern('message received!')) -</syntaxhighlight></text> - <sha1>es5ic6domqmdirvs8232ksas04tdf0n</sha1> +</syntaxhighlight> + +== Flowgraph Example == + +Here's a simple example of a flow graph using both streaming and messages: + +[[File:strobe.grc.png|550px|strobe.grc.png]] + +There are several interesting things to point out. First, there are two source blocks, which both output items at regular intervals, one every 1000 and one every 750 milliseconds. Dotted lines denote connected message ports, as opposed to solid lines, which denote connected streaming ports. In the top half of the flow graph, we can see that it is, in fact, possible to switch between message passing and streaming ports, but only if the type of the PMTs matches the type of the streaming ports (in this example, the pink color of the streaming ports denotes bytes, which means the PMT should be a u8vector if we want to stream the same data we sent as PMT). + +Another interesting fact is that we can connect more than one message output port to a single message input port, which is not possible with streaming ports. This is due to the '''asynchronous''' nature of messages: The receiving block will process all messages whenever it has a chance to do so, and not necessarily in any specific order. Receiving messages from multiple blocks simply means that there might be more messages to process. + +What happens to a message once it was posted to a block? This depends on the actual block implementation, but there are two possibilities: + +1) A message handler is called, which processes the message immediately.<br /> +2) The message is written to a FIFO buffer, and the block can make use of it whenever it likes, usually in the work function. + +For a block that has both message ports and streaming ports, any of these two options is OK, depending on the application. However, we strongly discourage the processing of messages inside of a work function and instead recommend the use of message handlers. Using messages in the work function encourages us to block in work waiting for a message to arrive. This is bad behavior for a work function, which should never block. If a block depends upon a message to operate, use the message handler concept to receive the message, which may then be used to inform the block's actions when the work function is called. Only on specially, well-identified occasions should we use method 2 above in a block. + +With a message passing interface, we can write blocks that don't have streaming ports, and then the work function becomes useless, since it's a function that is designed to work on streaming items. In fact, blocks that don't have streaming ports usually don't even have a work function. + +=== PDUs === + +In the previous flow graph, we have a block called ''PDU to Tagged Stream''. A PDU (protocol data unit) in GNU Radio has a special PMT type, it is a pair of a dictionary (on CAR) and a uniform vector type. So, this would yield a valid PDU, with no metadata and 10 zeros as stream data: + +<pre>pdu = pmt.cons(pmt.make_dict(), pmt.make_u8vector(10, 0))</pre> +The key/value pairs in the dictionary are then interpreted as key/value pairs of stream tags. + +== Flowgraph Example: Chat Application == + +Let's build an application that uses message passing. A chat program is an ideal use case, since it waits for the user to type a message, and then sends it. Because of that, no [[Throttle]] block is needed. + +Create the following flowgraph and save it as 'chat_app2.grc': + +[[File:Chat_app2_fg.png]] + +The ZMQ Message blocks have an Address of 'tcp://127.0.0.1:50261'. Typing in the [[QT GUI Message Edit Box]] will send the text once the Enter key is pressed. Output is on the terminal screen where gnuradio-companion was started. + +If you want to talk to another user (instead of just yourself), you can create an additional flowgraph with a different name such as 'chat_app3.grc'. Then change the ZMQ port numbers as follows: +* chat_app2 +** ZMQ PUSH Sink: tcp://127.0.0.1:50261 +** ZMQ PULL Source: tcp://127.0.0.1:50262 + +* chat_app3 +** ZMQ PUSH Sink: tcp://127.0.0.1:50262 +** ZMQ PULL Source: tcp://127.0.0.1:50261 + +When using GRC, doing a Generate and/or Run creates a Python file with the same name as the .grc file. You can execute the Python file without running GRC again. + +For testing this system we will use two processes, so we will need two terminal windows. + +Terminal 1: +* since you just finished building the chat_app3 flowgraph, you can just do a Run. + +Terminal 2: +Open another terminal window. +* change to whatever directory you used to generate the flowgraph for chat_app2. +* execute the following command: + python3 -u chat_app2.py + +Typing in the Message Edit Box for chat_app2 should be displayed on the Terminal 1 screen (chat_app3) and vice versa. + +To terminate each of the processes cleanly, click on the 'X' in the upper corner of the GUI rather than using Control-C. + + +---- + +ยน In old GNU Radio 3.7, we used Boost's 'bind' function: + + set_msg_handler(pmt::pmt_t port_id, + boost::bind(&block_class::message_handler_function, this, _1));}} + +The 'this' and '_1' are standard ways of using the Boost bind function to +pass the 'this' pointer as the first argument to the class (standard +OOP practice) and the _1 is an indicator that the function expects 1 +additional argument. + +[[Category:Guided Tutorials]]</text> + <sha1>s8jjo2g4fgrlyymzfhuhti4v98hz2pj</sha1> </revision> </page> -</mediawiki> +</mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Metadata Information.txt b/docs/usage-manual/(exported from wiki) Metadata Information.txt index 289d035534..f45a57a449 100644 --- a/docs/usage-manual/(exported from wiki) Metadata Information.txt +++ b/docs/usage-manual/(exported from wiki) Metadata Information.txt @@ -3,16 +3,18 @@ <ns>0</ns> <id>3479</id> <revision> - <id>4867</id> - <parentid>4293</parentid> - <timestamp>2019-03-12T22:43:19Z</timestamp> + <id>7025</id> + <parentid>4867</parentid> + <timestamp>2020-05-11T23:07:18Z</timestamp> <contributor> - <username>777arc</username> - <id>632</id> + <username>L0uisc</username> + <id>891</id> </contributor> + <minor/> + <comment>fixed "extra_str" in call to blocks.file_meta_sink() in final code block to read "extras_str"</comment> <model>wikitext</model> <format>text/x-wiki</format> - <text xml:space="preserve" bytes="14484">[[Category:Usage Manual]] + <text xml:space="preserve" bytes="14485">[[Category:Usage Manual]] == Introduction == Metadata files have extra information in the form of headers that @@ -336,9 +338,9 @@ encoded as a vector of uint16 with [day, month, year]. "/tmp/metadat_file.out", samp_rate, 1, blocks.GR_FILE_FLOAT, True, - 1000000, extra_str, False) + 1000000, extras_str, False) </syntaxhighlight></text> - <sha1>swg5klnvnjj6ehkn82c6078mgq3mnaz</sha1> + <sha1>79bbsfgjz078sumck0ea80hkoe1kuow</sha1> </revision> </page> </mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Polymorphic Types (PMTs).txt b/docs/usage-manual/(exported from wiki) Polymorphic Types (PMTs).txt index fac9831702..5d3019c760 100644 --- a/docs/usage-manual/(exported from wiki) Polymorphic Types (PMTs).txt +++ b/docs/usage-manual/(exported from wiki) Polymorphic Types (PMTs).txt @@ -3,27 +3,29 @@ <ns>0</ns> <id>3478</id> <revision> - <id>6781</id> - <parentid>6780</parentid> - <timestamp>2020-03-20T01:28:28Z</timestamp> + <id>8534</id> + <parentid>8533</parentid> + <timestamp>2021-05-08T15:28:22Z</timestamp> <contributor> - <username>777arc</username> - <id>632</id> + <username>Solomonbstoner</username> + <id>892</id> </contributor> - <comment>/* Vectors */</comment> + <minor/> + <comment>Fixed example code</comment> <model>wikitext</model> <format>text/x-wiki</format> - <text xml:space="preserve" bytes="17533">[[Category:Usage Manual]] + <text xml:space="preserve" bytes="24944">[[Category:Usage Manual]] == Introduction == -Polymorphic Types are opaque data types that are designed as generic -containers of data that can be safely passed around between blocks and -threads in GNU Radio. They are heavily used in the stream tags and -message passing interfaces. In a sense, PMTs are a way to extend C++' strict typing with something more flexible. The most complete list of PMT function is, -of course, the source code, specifically the header file [https://gnuradio.org/doc/doxygen/pmt_8h.html pmt.h]. This page summarizes the most important features and points of PMTs. +Polymorphic Types are used as the carrier of data from one block/thread to another such as stream tags and message passing interfaces. +PMT data types can represent a variety of data ranging from the Booleans to dictionaries. +They are heavily used in the stream tags and message passing interfaces. +In a sense, PMTs are a way to extend C++' strict typing with something more flexible. +The most complete list of PMT function is, of course, the source code, specifically the header file [https://gnuradio.org/doc/doxygen/pmt_8h.html pmt.h]. +This page summarizes the most important features and points of PMTs. + +Let's dive straight into some Python code and see how we can use PMTs: -Let's dive straight into some Python code and see how we can use -PMTs: <syntaxhighlight lang="python"> >>> import pmt >>> P = pmt.from_long(23) @@ -39,11 +41,7 @@ PMTs: >>> pmt.is_complex(P2) True </syntaxhighlight> -First, the pmt module is imported. We assign two values (P and P2) -with PMTs using the from_long() and from_complex() calls, -respectively. As we can see, they are both of the same type! This -means we can pass these variables to C++ through SWIG, and C++ can -handle this type accordingly. +First, the <code>pmt</code> module is imported. We assign two values (<code>P</code> and <code>P2</code>) with PMTs using the <code>from_long()</code> and <code>from_complex()</code> calls, respectively. As we can see, they are both of the same type! This means we can pass these variables to C++ through SWIG, and C++ can handle this type accordingly. The same code as above in C++ would look like this: <syntaxhighlight lang="c++"> @@ -51,28 +49,29 @@ The same code as above in C++ would look like this: // [...] pmt::pmt_t P = pmt::from_long(23); std::cout << P << std::endl; - pmt::pmt_t P2 = pmt::from_complex(gr_complex(0, 1)); // - Alternatively: pmt::from_complex(0, 1) + pmt::pmt_t P2 = pmt::from_complex(gr_complex(0, 1)); + // Alternatively: pmt::from_complex(0, 1) std::cout << P2 << std::endl; std::cout << pmt::is_complex(P2) << std::endl; </syntaxhighlight> -Two things stand out in both Python and C++. First, we can simply print -the contents of a PMT. How is this possible? Well, the PMTs have -in-built capability to cast their value to a string (this is not -possible with all types, though). Second, PMTs must obviously know -their type, so we can query that, e.g. by calling the is_complex() -method. - -When assigning a non-PMT value to a PMT, we can use the from_* -methods, and use the to_* methods to convert back: +Two things stand out in both Python and C++: First, we can simply print the contents of a PMT. How is this possible? Well, the PMTs have in-built capability to cast their value to a string (this is not possible with all types, though). Second, PMTs must obviously know their type, so we can query that, e.g. by calling the <code>is_complex()</code> method. + +Note: When running the above as a standalone, the compiler command will look something like <code>g++ pmt_tutorial.cpp -I$(gnuradio-config-info --prefix)/include -lgnuradio-pmt -o pmt_tutorial</code> + +When assigning a non-PMT value to a PMT, we can use the <code>from_*</code> methods, and use the <code>to_*</code> methods to convert back: <syntaxhighlight lang="c++"> pmt::pmt_t P_int = pmt::from_long(42); int i = pmt::to_long(P_int); pmt::pmt_t P_double = pmt::from_double(0.2); double d = pmt::to_double(P_double); + pmt::pmt_t P_double = pmt::mp(0.2); </syntaxhighlight> + +The last row shows the [http://gnuradio.org/doc/doxygen/namespacepmt.html#a90faad6086ac00280e0cfd8bb541bd64 pmt::mp()] shorthand function. It basically saves some typing, as it infers the correct <code>from_</code> function from the given type. + String types play a bit of a special role in PMTs, as we will see later, and have their own converter: + <syntaxhighlight lang="c++"> pmt::pmt_t P_str = pmt::string_to_symbol("spam"); pmt::pmt_t P_str2 = pmt::intern("spam"); @@ -80,35 +79,44 @@ later, and have their own converter: </syntaxhighlight> The pmt::intern is another way of saying pmt::string_to_symbol. +See the [https://www.gnuradio.org/doc/doxygen/namespacepmt.html PMT docs] and the header file [http://gnuradio.org/doc/doxygen/pmt_8h.html pmt.h] for a full list of conversion functions. + In Python, we can make use of the dynamic typing, and there's actually a helper function to do these conversions (C++ also has a helper function for converting to PMTs called pmt::mp(), but it's less powerful, and not quite as useful, because types are always strictly known in C++): + <syntaxhighlight lang="python"> P_int = pmt.to_pmt(42) i = pmt.to_python(P_int) P_double = pmt.to_pmt(0.2) d = pmt.to_double(P_double) </syntaxhighlight> + On a side note, there are three useful PMT constants, which can be used in both Python and C++ domains. In C++, these can be used as such: + <syntaxhighlight lang="c++"> pmt::pmt_t P_true = pmt::PMT_T; pmt::pmt_t P_false = pmt::PMT_F; pmt::pmt_t P_nil = pmt::PMT_NIL; </syntaxhighlight> + In Python: + <syntaxhighlight lang="python"> P_true = pmt.PMT_T P_false = pmt.PMT_F P_nil = pmt.PMT_NIL </syntaxhighlight> -pmt.PMT_T and pmt.PMT_F are boolean PMT types. + +<code>pmt.PMT_T</code> and <code>pmt.PMT_F</code> are boolean PMT types representing True and False, respectively. The PMT_NIL is like a NULL or None and can be used for default arguments or return values, often indicating an error has occurred. To be able to go back to C++ data types, we need to be able to find -out the type from a PMT. The family of is_* methods helps us do that: +out the type from a PMT. The family of <code>is_*</code> methods helps us do that: + <syntaxhighlight lang="c++"> double d; if (pmt::is_integer(P)) { @@ -120,18 +128,70 @@ out the type from a PMT. The family of is_* methods helps us do that: throw std::runtime_error("expected an integer!"); } </syntaxhighlight> + It is important to do type checking since we cannot unpack a PMT of the wrong data type. -We can compare PMTs without knowing their type by using the -pmt::equal() function: +We can compare PMTs without knowing their type by using the <code>pmt::equal()</code> function: + <syntaxhighlight lang="c++"> if (pmt::eq(P_int, P_double)) { std::cout << "Equal!" << std::endl; // This line will never be reached </syntaxhighlight> + +There are more equality functions, which compare different things: [http://gnuradio.org/doc/doxygen/namespacepmt.html#a5c28635e14287cc0e2f762841c11032f <code>pmt::eq()</code>] and [http://gnuradio.org/doc/doxygen/namespacepmt.html#a25467c81e1c5f4619a9cabad7a88eed5 <code>pmt::eqv()</code>]. We won't need these for this tutorial. + The rest of this page provides more depth into how to handle different data types with the PMT library. +=== More Complex Types === + +PMTs can hold a variety of types. Using the Python method <code>pmt.to_pmt()</code>, we can convert most of Pythons standard types out-of-the-box: + +<pre>P_tuple = pmt.to_pmt((1, 2, 3, 'spam', 'eggs')) +P_dict = pmt.to_pmt({'spam': 42, 'eggs': 23})</pre> +But what does this mean in the C++ domain? Well, there are PMT types that define [http://gnuradio.org/doc/doxygen/namespacepmt.html#a32895cc5a614a46b66b869c4a7bd283c tuples] and [http://gnuradio.org/doc/doxygen/namespacepmt.html#aba10563e3ab43b8d52f9cb13132047cf dictionaries], keys and values being PMTs, again.<br /> +So, to create the tuple from the Python example, the C++ code would look like this: + +<pre>pmt::pmt_t P_tuple = pmt::make_tuple(pmt::from_long(1), pmt::from_long(2), pmt::from_long(3), pmt::string_to_symbol("spam"), pmt::string_to_symbol("eggs"))</pre> +For the dictionary, it's a bit more complex: + +<pre>pmt::pmt_t P_dict = pmt::make_dict(); +P_dict = pmt::dict_add(P_dict, pmt::string_to_symbol("spam"), pmt::from_long(42)); +P_dict = pmt::dict_add(P_dict, pmt::string_to_symbol("eggs"), pmt::from_long(23));</pre> +As you can see, we first need to create a dictionary, then assign every key/value pair individually. + +A variant of tuples are ''vectors''. Like Python's tuples and lists, PMT vectors are mutable, whereas PMT tuples are not. In fact, PMT vectors are the only PMT data types that are mutable. When changing the value or adding an item to a dictionary, we are actually creating a new PMT. + +To create a vector, we can initialize it to a certain lengths, and fill all elements with an initial value. We can then change items or reference them: + +<pre>pmt::pmt_t P_vector = pmt::make_vector(5, pmt::from_long(23)); // Creates a vector with 5 23's as PMTs +pmt::vector_set(P_vector, 0, pmt::from_long(42)); // Change the first element to a 42 +std::cout << pmt::vector_ref(P_vector, 0); // Will print 42</pre> +In Python, we can do all these steps (using <code>pmt.make_vector()</code> etc.), or convert a list: + +<pre>P_vector = pmt.to_pmt([42, 23, 23, 23, 23])</pre> +Vectors are also different from tuples in a sense that we can '''directly''' load data types into the elements, which don't have to be PMTs.<br /> +Say we want to pass a series of 8 float values to another block (these might be characteristics of a filter, for example). It would be cumbersome to convert every single element to and from PMTs, since all elements of the vector are the same type. + +We can use special vector types for this case: + +<pre>pmt::pmt_t P_f32vector = pmt::make_f32vector(8, 5.0); // Creates a vector with 8 5.0s's as floats +pmt::f32vector_set(P_f32vector, 0, 2.0); // Change the first element to a 2.0 +float f = f32vector_ref(P_f32vector, 0); +std::cout << f << std::endl; // Prints 2.0 +size_t len; +float *fp = pmt::f32vector_elements(P_f32vector, len); +for (size_t i = 0; i < len; i++) + std::cout << fp[i] << std::endl; // Prints all elements from P_f32vector, one after another</pre> +Python has a similar concept: [http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html Numpy arrays]. As usual, the PMT library understands this and converts as expected: + +<pre>P_f32vector = pmt.to_pmt(numpy.array([2.0, 5.0, 5.0, 5.0, 5.0], dtype=numpy.float32)) +print pmt.is_f32vector(P_f32vector) # Prints 'True'</pre> +Here, 'f32' stands for 'float, 32 bits'. PMTs know about most typical fixed-width data types, such as 'u8' (unsigned 8-bit character) or 'c32' (complex with 32-bit floats for each I and Q). Consult the [http://gnuradio.org/doc/doxygen/namespacepmt.html manual] for a full list of types. + +The most generic PMT type is probably the blob (binary large object). Use this with care - it allows us to pass around anything that can be represented in memory. + == PMT Data Type == All PMTs are of the type pmt::pmt_t. This is an opaque container and @@ -202,7 +262,7 @@ PMT, we use a call like "pmt::to_<type>". For example: As a side-note, making a PMT from a complex number is not obvious: <syntaxhighlight lang="c++"> std::complex<double> a(1.2, 3.4); - pmt::pmt_t pmt_a = pmt::make_rectangular(a.real(), b.imag()); + pmt::pmt_t pmt_a = pmt::make_rectangular(a.real(), a.imag()); std::complex<double> b = pmt::to_complex(pmt_a); </syntaxhighlight> Pairs, dictionaries, and vectors have different constructors and ways @@ -373,6 +433,22 @@ u8, u16, u32, u64, s8, s16, s32, s64, f32, f64, c32, c64). * const dtype* pmt::(dtype)vector_elements(pmt_t vector, size_t &len) * dtype* pmt::(dtype)vector_writable_elements(pmt_t vector, size_t &len) +List of available pmt::is_(dtype)vector(pmt_t x) methods from <code>pmt.h</code>:<br> + + bool pmt::is_uniform_vector() + bool pmt::is_u8vector() + bool pmt::is_s8vector() + bool pmt::is_u16vector() + bool pmt::is_s16vector() + bool pmt::is_u32vector() + bool pmt::is_s32vector() + bool pmt::is_u64vector() + bool pmt::is_s64vector() + bool pmt::is_f32vector() + bool pmt::is_f64vector() + bool pmt::is_c32vector() + bool pmt::is_c64vector() + '''Note:''' We break the contract with vectors. The 'set' functions actually change the data underneath. It is important to keep track of the implications of setting a new value as well as accessing the @@ -388,9 +464,21 @@ just a thin wrapper around a u8vector. == Pairs == -Pairs are inspired by LISP 'cons' data types, so you will find the -language here comes from LISP. A pair is just a pair of PMT -objects. They are manipulated using the following functions: +A concept that originates in Lisp dialects are [http://en.wikipedia.org/wiki/Cons ''pairs'' and ''cons'']. The simplest explanation is just that: If you combine two PMTs, they form a new PMT, which is a pair (or cons) of those two PMTs (don't worry about the weird name, a lot of things originating in Lisp have weird names. Think of a 'construct'). + +Similarly to vectors or tuples, pairs are a useful way of packing several components of a message into a single PMT. Using the PMTs generated in the previous section, we can combine two of these to form a pair, here in Python: + +<pre>P_pair = pmt.cons(pmt.string_to_symbol("taps"), P_f32vector) +print pmt.is_pair(P_pair) # Prints 'true'</pre> +You can combine PMTs as tuples, dictionaries, vectors, or pairs, it's just a matter of taste. This construct is well-established though, and as such used in GNU Radio quite often. + +So how do we deconstruct a pair? That's what the <code>car</code> and <code>cdr</code> functions do. Let's deconstruct that previous pair in C++: + +<pre>pmt::pmt_t P_key = pmt::car(P_pair); +pmt::pmt_t P_f32vector2 = pmt::cdr(P_pair); +std::cout << P_key << std::endl; // Will print 'taps' using the PMT automatic conversion to strings</pre> + +Here is a summary of the pairs-related functions in C++ and Python: * bool pmt::is_pair(const pmt_t &obj): Return true if obj is a pair, else false * pmt_t pmt::cons(const pmt_t &x, const pmt_t &y): construct new pair @@ -409,6 +497,8 @@ And in Python we have: pmt.set_cdr(pair_obj, value) # Store value in cdr field </syntaxhighlight> +For more advanced pair manipulation, refer to the [http://gnuradio.org/doc/doxygen/namespacepmt.html#a7ab95721db5cbda1852f13a92eee5362 documentation] and the [https://en.wikipedia.org/wiki/Car_and_cdr Wikipedia page for car and cdr]. + == Serializing and Deserializing == It is often important to hide the fact that we are working with PMTs @@ -468,10 +558,26 @@ contents is using the overloaded "<<" operator with a stream buffer object. In C++, we can inline print the contents of a PMT like: <syntaxhighlight lang="c++"> - pmt::pmt_t a pmt::from_double(1.0); + pmt::pmt_t a = pmt::from_double(1.0); std::cout << "The PMT a contains " << a << std::endl; </syntaxhighlight> +=== Collection Notation === +PMTs use a different bracket notation from what one might be use to in Python. + +<syntaxhighlight lang="plaintext"> +>>> my_dict +((meaning . 42)) +>>> my_vector +#[1 2 3 4] +>>> pmt.make_tuple(pmt.from_long(321), pmt.from_float(3.14)) +{321 3.14} +>>> pmt.cons(pmt.from_long(1), pmt.from_long(2)) +(1 . 2) +>>> my_pdu +(() . #[1 2 3 4]) +</syntaxhighlight> + == Conversion between Python Objects and PMTs == Although PMTs can be manipulated in Python using the Python versions @@ -487,7 +593,7 @@ Two functions capture most of this functionality: pmt.to_pmt # Converts a python object to a PMT. pmt.to_python # Converts a PMT into a python object. </syntaxhighlight></text> - <sha1>6hbhl1y9zc4tlse9yp8hlf6n0m519m6</sha1> + <sha1>mox7is6oymllqhrkqtc39dpmlvu1r2d</sha1> </revision> </page> </mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Polyphase Filterbanks.txt b/docs/usage-manual/(exported from wiki) Polyphase Filterbanks.txt index f15235170e..1fb8526825 100644 --- a/docs/usage-manual/(exported from wiki) Polyphase Filterbanks.txt +++ b/docs/usage-manual/(exported from wiki) Polyphase Filterbanks.txt @@ -49,7 +49,7 @@ filter that is then split up between the N channels of the PFB. self._M = 9 # Number of channels to channelize self._taps = filter.firdes.low_pass_2(1, self._fs, 475.50, 50, attenuation_dB=100, - window=fft.window.WIN_BLACKMAN_hARRIS) + window=filter.firdes.WIN_BLACKMAN_hARRIS) In this example, the signal into the channelizer is sampled at 9 ksps (complex, so 9 kHz of bandwidth). The filter uses 9 channels, so each diff --git a/docs/usage-manual/(exported from wiki) QT GUI.txt b/docs/usage-manual/(exported from wiki) QT GUI.txt index 6cc5d8955c..fab784567c 100644 --- a/docs/usage-manual/(exported from wiki) QT GUI.txt +++ b/docs/usage-manual/(exported from wiki) QT GUI.txt @@ -154,7 +154,7 @@ import sys, sip samp_rate, #bw "QT GUI Plot") #name - self.snk_win = sip.wrapinstance(self.snk.qwidget(), Qt.QWidget) + self.snk_win = sip.wrapinstance(self.snk.pyqwidget(), Qt.QWidget) self.snk_win.show() def main(): @@ -279,4 +279,4 @@ share/gnuradio/examples/qt-gui/dark.qss.</text> <sha1>6p0evs1odganpw5pf993ifhtdncao90</sha1> </revision> </page> -</mediawiki> +</mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Stream Tags.txt b/docs/usage-manual/(exported from wiki) Stream Tags.txt index 88ca4937a0..da4fb23312 100644 --- a/docs/usage-manual/(exported from wiki) Stream Tags.txt +++ b/docs/usage-manual/(exported from wiki) Stream Tags.txt @@ -3,16 +3,17 @@ <ns>0</ns> <id>3482</id> <revision> - <id>6198</id> - <parentid>6191</parentid> - <timestamp>2019-09-30T20:17:37Z</timestamp> + <id>8713</id> + <parentid>8602</parentid> + <timestamp>2021-06-30T11:10:32Z</timestamp> <contributor> - <username>777arc</username> - <id>632</id> + <username>Marcindsp</username> + <id>1033</id> </contributor> + <minor/> <model>wikitext</model> <format>text/x-wiki</format> - <text xml:space="preserve" bytes="12354">[[Category:Usage Manual]] + <text xml:space="preserve" bytes="21769">[[Category:Usage Manual]] == Introduction == GNU Radio was originally a streaming system with no other mechanism to @@ -22,7 +23,7 @@ for samples, bits, etc., but can lack for control and meta data. Part of this is solved using the existing message passing interface, which allows blocks to subscribe to messages published by any other block in the flowgraph (see [[Message Passing]]). The main drawback to the -message passing system is that is works asynchronously, meaning that +message passing system is that it works asynchronously, meaning that there is no guarantee when a message may arrive relative to the data stream. @@ -114,9 +115,14 @@ In Python, we can add a tag to a stream using one of the following: add_item_tag(which_output, abs_offset, key, value) add_item_tag(which_output, abs_offset, key, value, srcid) -Note that the key and value are both PMTs. To create a string type PMT you can use pmt.intern("example_key") +Note that the key and value are both PMTs. To create a string type PMT you can use pmt.intern("example_key"). + +Consider the following flowgraph as an example. We will have an embedded python block insert stream tags at random intervals, and view these tags on the QT GUI Time sink. + +[[File:Epy_stream_tags_example_flowchart.png|800px]] + +Add the following code into the embedded python block. This code outputs the same signal as in the input, except with tags on randomly selected input samples: -Here is an example of a Python block that loops through samples and puts tags on random ones, and outputs the same signal: <syntaxhighlight lang="python"> import numpy as np from gnuradio import gr @@ -127,21 +133,29 @@ Here is an example of a Python block that loops through samples and puts tags on gr.sync_block.__init__( self, name='Embedded Python Block', - in_sig=[np.complex64], - out_sig=[np.complex64] + in_sig=[np.float32], + out_sig=[np.float32] ) def work(self, input_items, output_items): - for indx, sample in enumerate(input_items[0]): + for indx, sample in enumerate(input_items[0]): # Enumerate through the input samples in port in0 if np.random.rand() > 0.95: # 5% chance this sample is chosen key = pmt.intern("example_key") value = pmt.intern("example_value") - self.add_item_tag(0, self.nitems_written(0) + indx, key, value) + self.add_item_tag(0, # Write to output port 0 + self.nitems_written(0) + indx, # Index of the tag in absolute terms + key, # Key of the tag + value # Value of the tag + ) # note: (self.nitems_written(0) + indx) is our current sample, in absolute time output_items[0][:] = input_items[0] # copy input to output return len(output_items[0]) </syntaxhighlight> +You may expect an output similar to the screenshot of the time sink below. + +[[File:Epy_stream_tags_example_timesink.png|800px]] + === Getting tags from a Stream === To get tags from a particular input stream, we have two @@ -210,9 +224,10 @@ If you want to grab all tags on the samples currently being processed by work(), == Tag Propagation == -Tags are propagated downstream from block to block like the normal -data streams. How tags are actually moved depends on a specific -propagation policy. We defined three types of policies: +We now know how to add tags to streams, and how to read them. But what happens to tags after they were read? And what happens to tags that aren't used? After all, there are many blocks that don't care about tags at all. + +The answer is: It depends on the ''tag propagation policy'' of a block what happens to tags that enter it.<br /> +There are [http://gnuradio.org/doc/doxygen/classgr_1_1block.html#abc40fd4d514724a5446a2b34b2352b4e three policies] to choose from: # All-to-All: all tags from any input port are replicated to all output ports # One-to-One: tags from input port ''i'' are only copied to output port ''i'' (depends on num inputs = num outputs). @@ -221,14 +236,14 @@ propagation policy. We defined three types of policies: The default behavior of a block is the 'All-to-All' method of propagation. -To set a different propagation policy, use the function: +We generally set the tag propagation policy in the block's constructor using [http://gnuradio.org/doc/doxygen/classgr_1_1block.html#a476e218927e426ac88c26431cbf086cd @set_tag_propagation_policy] void set_tag_propagation_policy(tag_propagation_policy_t p); See the gr::block::tag_propagation_policy_t documentation for details on this enum type. -=== Tag Propagation through Rate Changes === +When the tag propagation policy is set to TPP_ALL_TO_ALL or TPP_ONE_TO_ONE, the GNU Radio scheduler uses any information available to figure out which output item corresponds to which input item. The block may read them and add new tags, but existing tags are automatically moved downstream in a manner deemed appropriate. When a tag is propagated through a block that has a rate change, the item's offset in the data stream will change. The scheduler uses the @@ -250,12 +265,44 @@ propagation internally. In no case is the value of the tag modified when propagating through a block. This becomes relevant when using [[Tagged Stream Blocks]]. +As an example, consider an interpolating block. See the following flow graph: + +[[File:tags_interp.grc.png|700px|tags_interp.grc.png]] + +[[File:tags_interp.png|500px|tags_interp.png]]<br /> + +As you can tell, we produce tags on every 10th sample, and then pass them through a block that repeats every sample 100 times. Tags do not get repeated with the standard tag propagation policies (after all, they're not tag ''manipulation'' policies), so the scheduler takes care that every tag is put on the output item that corresponds to the input item it was on before. Here, the scheduler makes an educated guess and puts the tag on the '''first''' of 100 items. + +Note: We can't use one QT GUI Time Sink for both signals here, because they are running at a different rate. Note the time difference on the x-axis! + +On decimating blocks, the behavior is similar. Consider this very simple flow graph, and the position of the samples: + +[[File:tags_decim.grc.png|400px|tags_decim.grc.png]] + +[[File:tags_decim.png|500px|tags_decim.png]]<br /> + + +We can see that no tags are destroyed, and tags are indeed spaced at one-tenth of the original spacing of 100 items. Of course, the actual item that was passed through the block might be destroyed, or modified (think of a decimating FIR filter). + +In fact, this works with any rate-changing block. Note that there are cases where the relation of tag positions of in- and output are ambiguous, the GNU Radio scheduler will then try and get as close as possible. + +Here's another interesting example: Consider this flow graph, which has a delay block, and the position of the tags after it: + +[[File:tags_ramp_delay.grc.png|500px|tags_ramp_delay.grc.png]] + +[[File:tags_ramp_delay.png|500px|tags_ramp_delay.png]]<br /> + + +Before the delay block, tags were positioned at the beginning of the ramp. After the delay, they're still in the same position! Would we inspect the source code of the delay block, we'd find that there is absolutely no tag handling code. Instead, the block [http://gnuradio.org/doc/doxygen/classgr_1_1block.html#acad5d6e62ea885cb77d19f72451581c2 declares a delay] to the scheduler, which then propagates tags with this delay. + +Using these mechanisms, we can let GNU Radio handle tag propagation for a large set of cases. For specialized or corner cases, there is no option than to set the tag propagation policy to <code>TPP_DONT</code> and manually propagate tags (actually, there's another way: Say we want most tags to propagate normally, but a select few should be treated differently. We can use [http://gnuradio.org/doc/doxygen/classgr_1_1block.html#a461f6cc92174e83b10c3ec5336036768 <code>remove_item_tag()</code>] (DEPRECATED. Will be removed in 3.8.) to remove these tags from the input; they will then not be propagated any more even if the tag propagation policy is set to something other than <code>TPP_DONT</code>. But that's more advanced use and will not be elaborated on here). + == Notes on How to Use Tags == Tags can be very useful to an application, and their use is spreading. USRP sources generate tag information on the time, sample rate, and frequency of the board if anything changes. We have a meta -data file source/sink (see [[Metadata]]) that use tags to store +data file source/sink (see [[Metadata_Information]]) that use tags to store information about the data stream. But there are things to think about when using tags in a block. @@ -282,8 +329,85 @@ we have no way of knowing in the flowgraph how many samples were potentially lost, we have lost track of the timing information. The USRP driver recognizes when packets have been dropped and uses this to queue another tag, which allows us to resync. Likewise, any point the -sample rate or frequency changes, a new tag is issued.</text> - <sha1>lh9drcohakf8o2pksgy2wpounqwfo3h</sha1> +sample rate or frequency changes, a new tag is issued. + +== Example Flowgraph == + +Let's have a look at a simple example: + +[[File:tut5_tagstest_fg.png|600px|tut5_tagstest_fg.png]] + +In this flow graph, we have two sources: A sinusoid and a tag strobe. A tag strobe is a block that will output a constant tag, in this case, on every 1000th item (the actual value of the items is always zero). Those sources get added up. The signal after the adder is identical to the sine wave we produced, because we are always adding zeros. However, the tags stay attached to the same position as they were coming from the tag strobe! This means every 1000th sample of the sinusoid now has a tag. The QT scope can display tags, and even trigger on them. + +[[File:tut5_tagstest_scope.png|500px|tut5_tagstest_scope.png]]<br /> + +We now have a mechanism to randomly attach any metadata to specific items. There are several blocks that use tags. One of them is the UHD Sink block, the driver used for transmitting with USRP devices. It will react to tags with certain keys, one of them being <code>tx_freq</code>, which can be used to set the transmit frequency of a USRP while streaming. + +=== Adding tags to the QPSK demodulator === + +Going back to our QPSK demodulation example, we might want to add a feature to tell downstream blocks that the demodulation is not going well. Remember the output of our block is always hard-decision, and we have to output something. So we could use tags to notify that the input is not well formed, and that the output is not reliable. + +As a failure criterion, we discuss the case where the input amplitude is too small, say smaller than 0.01. When the amplitude drops below this value, we output one tag. Another tag is only sent when the amplitude has recovered, and falls back below the threshold. We extend our work function like this: + +<pre>if (std::abs(in[i]) < 0.01 and not d_low_ampl_state) { + add_item_tag(0, // Port number + nitems_written(0) + i, // Offset + pmt::mp("amplitude_warning"), // Key + pmt::from_double(std::abs(in[i])) // Value + ); + d_low_ampl_state = true; +} +else if (std::abs(in[i]) >= 0.01 and d_low_ampl_state) { + add_item_tag(0, // Port number + nitems_written(0) + i, // Offset + pmt::mp("amplitude_recovered"), // Key + pmt::PMT_T // Value + ); + d_low_ampl_state = false; // Reset state +}</pre> +In Python, the code would look like this (assuming we have a member of our block class called <code>d_low_ampl_state</code>): + +<pre># The vector 'in' is called 'in0' here because 'in' is a Python keyword +if abs(in0[i]) < 0.01 and not d_low_ampl_state: + self.add_item_tag(0, # Port number + self.nitems_written(0) + i, # Offset + pmt.intern("amplitude_warning"), # Key + pmt.from_double(numpy.double(abs(in0[i]))) # Value + # Note: We need to explicitly create a 'double' here, + # because in0[i] is an explicit 32-bit float here + ) + self.d_low_ampl_state = True +elif abs(in0[i]) >= 0.01 and d_low_ampl_state: + self.add_item_tag(0, # Port number + self.nitems_written(0) + i, # Offset + pmt.intern("amplitude_recovered"), # Key + pmt.PMT_T # Value + ) + self.d_low_ampl_state = False; // Reset state</pre> +We can also create a tag data type [http://gnuradio.org/doc/doxygen/structgr_1_1tag__t.html tag_t] and directly pass this along: + +<pre>if (std::abs(in[i]) < 0.01 and not d_low_ampl_state) { + tag_t tag; + tag.offset = nitems_written(0) + i; + tag.key = pmt::mp("amplitude_warning"); + tag.value = pmt::from_double(std::abs(in[i])); + add_item_tag(0, tag); + d_low_ampl_state = true; +}</pre> +Here's a flow graph that uses the tagged version of the demodulator. We input 20 valid QPSK symbols, then 10 zeros. Since the output of this block is always either 0, 1, 2 or 3, we normally have no way to see if the input was not clearly one of these values. + +[[File:Demod_with_tags.grc.png|750px|Demod_with_tags.grc.png]] + +Here's the output. You can see we have tags on those values which were not from a valid QPSK symbol, but from something unreliable. + +[[File:Demod_with_tags.png|500px|Demod_with_tags.png]]<br /> + +== Use case: FIR filters == + +Assume we have a block that is actually an FIR filter. We want to let GNU Radio handle the tag propagation. How do we configure the block? + +Now, an FIR filter has one input and one output. So, it doesn't matter if we set the propagation policy to <code>TPP_ALL_TO_ALL</code> or <code>TPP_ONE_TO_ONE</code>, and we can leave it as the default. The items going in and those coming out are different, so how do we match input to output? Since we want to preserve the timing of the tag position, we need to use the filter's '''group delay''' as a delay for tags (which for this symmetric FIR filter is <code>(N-1)/2</code>, where <code>N</code> is the number of filter taps). Finally, we might be interpolating, decimating or both (say, for sample rate changes) and we need to tell the scheduler about this as well.</text> + <sha1>laoj1l0sm4zk80obf1h7rdq4htbsg3v</sha1> </revision> </page> </mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Tagged Stream Blocks.txt b/docs/usage-manual/(exported from wiki) Tagged Stream Blocks.txt index 9c9158547f..9bee17eabb 100644 --- a/docs/usage-manual/(exported from wiki) Tagged Stream Blocks.txt +++ b/docs/usage-manual/(exported from wiki) Tagged Stream Blocks.txt @@ -3,28 +3,28 @@ <ns>0</ns> <id>3483</id> <revision> - <id>4870</id> - <parentid>4295</parentid> - <timestamp>2019-03-12T22:43:37Z</timestamp> + <id>8735</id> + <parentid>8603</parentid> + <timestamp>2021-07-23T15:10:11Z</timestamp> <contributor> - <username>777arc</username> - <id>632</id> + <ip>172.18.0.3</ip> </contributor> + <comment>/* CRC32 */ github changed the method to access the actual file content. url with 'blob' now goes to decorated file listing (cut-n-paste no bueno) not the bare file content (cut-n-paste ready!)</comment> <model>wikitext</model> <format>text/x-wiki</format> - <text xml:space="preserve" bytes="13379">[[Category:Usage Manual]] + <text xml:space="preserve" bytes="13506">[[Category:Usage Manual]] == Introduction == A tagged stream block is a block that works on streamed, but packetized input data. Think of packet data transmission: A data packet consists of N bytes. However, in traditional GNU Radio blocks, if we stream N bytes into a block, there's no way of knowing the packet boundary. This might be relevant: Perhaps the modulator has to -prepend a synchronisation word before every packet, or append a CRC. So while some +prepend a synchronization word before every packet or append a CRC. So while some blocks don't care about packet boundaries, other blocks do: These are tagged stream blocks. These blocks are different from all the other GNU Radio block types (gr::block, -gr::sync_block etc.) in that they are driven by the input: The PDU length tag tells the block how to operate, whereas other blocks are output-driven (the scheduler tries to fill up the output buffer are much as possible). +gr::sync_block etc.) in that they are driven by the input: The PDU length tag tells the block how to operate, whereas other blocks are output-driven (the scheduler tries to fill up the output buffer as much as possible). === How do they work? === @@ -276,30 +276,30 @@ of the PDU. === CRC32 === -Block: [https://github.com/gnuradio/gnuradio/blob/master/gr-digital/lib/crc32_bb_impl.cc crc32_bb] +Block: [https://raw.githubusercontent.com/gnuradio/gnuradio/master/gr-digital/lib/crc32_bb_impl.cc gr-digital/lib/crc32_bb_impl.cc] This is a very simple block, and a good example to start with. === OFDM Frame Equalizer === -Block: [https://github.com/gnuradio/gnuradio/blob/master/gr-digital/lib/ofdm_frame_equalizer_vcvc_impl.cc ofdm_frame_equalizer_vcvc] +Block: [https://raw.githubusercontent.com/gnuradio/gnuradio/master/gr-digital/lib/ofdm_frame_equalizer_vcvc_impl.cc gr-digital/lib/ofdm_frame_equalizer_vcvc_impl.cc] This block would be a sync block if tagged stream blocks didn't exist. It also uses more than one tag to determine the output. === Tagged Stream Muxer === -Block: [https://github.com/gnuradio/gnuradio/blob/master/gr-blocks/lib/tagged_stream_mux_impl.cc tagged_stream_mux] +Block: [https://raw.githubusercontent.com/gnuradio/gnuradio/master/gr-blocks/lib/tagged_stream_mux_impl.cc gr-blocks/lib/tagged_stream_mux_impl.cc] Use this to multiplex any number of tagged streams. === Cyclic Prefixer (OFDM) === -Block: [https://github.com/gnuradio/gnuradio/blob/master/gr-digital/lib/ofdm_cyclic_prefixer_impl.cc ofdm_cyclic_prefixer] +Block: [https://raw.githubusercontent.com/gnuradio/gnuradio/master/gr-digital/lib/ofdm_cyclic_prefixer_impl.cc gr-digital/lib/ofdm_cyclic_prefixer_impl.cc] This block uses the gr::block behavior fallback. -=== Troubleshooting === +== Troubleshooting == '''My flow graph crashes with the error message "Missing length tag".''' @@ -307,7 +307,7 @@ This means the input of a tagged stream block was not correctly tagged. The most common cause is when connecting a regular streaming block to a tagged stream block. You can check the log output for the item number and port where this happened.</text> - <sha1>79nex80832w5rj7e2dnhz7i9le1fono</sha1> + <sha1>7zjm0zyisjndz7o8pih62qwhn0r6ctg</sha1> </revision> </page> </mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/(exported from wiki) Types of Blocks.txt b/docs/usage-manual/(exported from wiki) Types of Blocks.txt index a827f2ddb6..1f9e4fbc21 100644 --- a/docs/usage-manual/(exported from wiki) Types of Blocks.txt +++ b/docs/usage-manual/(exported from wiki) Types of Blocks.txt @@ -3,18 +3,17 @@ <ns>0</ns> <id>3495</id> <revision> - <id>6500</id> - <parentid>4864</parentid> - <timestamp>2020-01-05T18:46:42Z</timestamp> + <id>9094</id> + <parentid>8843</parentid> + <timestamp>2021-11-17T16:00:00Z</timestamp> <contributor> - <username>Duggabe</username> - <id>846</id> + <username>777arc</username> + <id>632</id> </contributor> - <minor/> - <comment>correct Python examples</comment> + <comment>/* Basic Block */</comment> <model>wikitext</model> <format>text/x-wiki</format> - <text xml:space="preserve" bytes="8123">[[Category:Usage Manual]] + <text xml:space="preserve" bytes="8027">[[Category:Usage Manual]] == Introduction == To take advantage of the gnuradio framework, users will create various blocks to implement the desired data processing. There are several types of blocks to choose from: @@ -121,14 +120,18 @@ An example decimation block in Python: <syntaxhighlight lang="python"> class my_decim_block(gr.decim_block): - def __init__(self, args): + def __init__(self, decim_rate): gr.decim_block.__init__(self, name="my block", in_sig=[numpy.float32], - out_sig=[numpy.float32]) - self.set_relative_rate(1.0/decimation) + out_sig=[numpy.float32], + decim = decim_rate) + self.set_relative_rate(1.0/decim_rate) + self.decimation = decim_rate - #work function here... + def work(self, input_items, output_items): + output_items[0][:] = input_items[0][0::self.decimation] + return len(output_items[0]) </syntaxhighlight> Some observations: @@ -233,28 +236,22 @@ Some observations: ** ninput_items is a vector describing the length of each input buffer * Before return, general_work must manually consume the used inputs * The number of items in the input buffers is assumed to be noutput_items -** Users may alter this behaviour by overloading the forecast() method +** This behaviour can be altered by overloading the forecast() method but is not mandatory The adder revisited as a basic block in Python: <syntaxhighlight lang="python"> +import numpy as np from gnuradio import gr -import gnuradio.extras class my_basic_adder_block(gr.basic_block): - def __init__(self, args): + def __init__(self): gr.basic_block.__init__(self, name="another_adder_block", - in_sig=[...], - out_sig=[...]) - self.set_auto_consume(False) - - def forecast(self, noutput_items, ninput_items_required): - #setup size of input_items[i] for work call - for i in range(len(ninput_items_required)): - ninput_items_required[i] = noutput_items + in_sig=[np.float32, np.float32], + out_sig=[np.float32]) - def work(self, input_items, output_items): + def general_work(self, input_items, output_items): #buffer references in0 = input_items[0][:len(output_items[0])] in1 = input_items[1][:len(output_items[0])] @@ -263,15 +260,15 @@ class my_basic_adder_block(gr.basic_block): #process data out[:] = in0 + in1 - //consume the inputs - self.consume(0, len(in0)) //consume port 0 input - self.consume(1, len(in1)) //consume port 1 input - #self.consume_each(len(out)) //or shortcut to consume on all inputs + #consume the inputs + self.consume(0, len(in0)) #consume port 0 input + self.consume(1, len(in1)) #consume port 1 input + #return produced return len(out) </syntaxhighlight></text> - <sha1>45ab6553658lbupm4hdgnjnvx9bl7dj</sha1> + <sha1>jlgt0ofen1ui79z2lk7vhsyffax6jr5</sha1> </revision> </page> </mediawiki>
\ No newline at end of file diff --git a/docs/usage-manual/export-usage-manual.py b/docs/usage-manual/export-usage-manual.py index 02d3fb620a..74f3d4d30c 100644 --- a/docs/usage-manual/export-usage-manual.py +++ b/docs/usage-manual/export-usage-manual.py @@ -1,12 +1,17 @@ -from selenium import webdriver +# pip install selenium +# I had to also do pip install --upgrade requests +# Download geckodriver from here https://github.com/mozilla/geckodriver/releases and put it in Downloads +# sudo chmod +x geckodriver +# export PATH=$PATH:/home/marc/Downloads (or wherever you put it) + +from selenium import webdriver from selenium.webdriver.common.keys import Keys import time -import HTMLParser +from html.parser import HTMLParser import os # Settings -pages_to_save = ['GNURadioCompanion', - 'Handling Flowgraphs', +pages_to_save = ['Handling Flowgraphs', 'Types of Blocks', 'Metadata Information', 'Stream Tags', @@ -22,8 +27,10 @@ pages_to_save = ['GNURadioCompanion', 'Polyphase Filterbanks'] # set up web driver -driver = webdriver.Firefox() +driver = webdriver.Firefox('/home/marc/Downloads/geckodriver') # dir that contains geckodriver +print("STARTING") for page_name in pages_to_save: + print("Processing", page_name) driver.get("https://wiki.gnuradio.org/index.php/Special:Export") # fill in text box @@ -44,10 +51,11 @@ for page_name in pages_to_save: cropped_html = raw_html[start_index:] # save text to file - h = HTMLParser.HTMLParser() + h = HTMLParser() cropped_html_text = h.unescape(cropped_html) # makes it so stuff like > shows up as a greater than sign text_file = open("(exported from wiki) " + page_name + ".txt", "w") text_file.write(cropped_html_text) text_file.close() driver.close() +print("DONE") |