diff options
Diffstat (limited to 'docs/doxygen/other/msg_passing.dox')
-rw-r--r-- | docs/doxygen/other/msg_passing.dox | 381 |
1 files changed, 0 insertions, 381 deletions
diff --git a/docs/doxygen/other/msg_passing.dox b/docs/doxygen/other/msg_passing.dox deleted file mode 100644 index 14de7bae4c..0000000000 --- a/docs/doxygen/other/msg_passing.dox +++ /dev/null @@ -1,381 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_msg_passing Message Passing - -\section msg_passing_introduction Introduction - -GNU Radio was originally a streaming system with no other mechanism to -pass data between blocks. Streams of data are a model that work well -for samples, bits, etc., but are not really the right mechanism for -control data, metadata, or packet structures (at least at -some point in the processing chain). - -We solved part of this problem by introducing the tag stream (see \ref -page_stream_tags). This is a parallel stream to the data -streaming. The difference is that tags are designed to hold metadata -and control information. Tags are specifically associated with a -particular sample in the data stream and flow downstream alongside the -data. This model allows other blocks to identify that an event or -action has occurred or should occur on a particular item. The major -limitation is that the tag stream is really only accessible inside a -work function and only flows in one direction. Its benefit is that it -is isosynchronous with the data. - -We want a more general message passing system for a couple of -reasons. The first is to allow blocks downstream to communicate back -to blocks upstream. The second is to allow an easier way for us to -communicate back and forth between external applications and GNU -Radio. GNU Radio's message passing interface handles these cases, although -it does so on an asynchronous basis. - -The message passing interface heavily relies on Polymorphic Types -(PMTs) in GNU Radio. For further information about these data -structures, see the page \ref page_pmt. - -\section msg_passing_api Message Passing API - -The message passing interface is designed into the gr::basic_block, -which is the parent class for all blocks in GNU Radio. Each block has -a set of message queues to hold incoming messages and can post -messages to the message queues of other blocks. The blocks also -distinguish between input and output ports. - -A block has to declare its input and output message ports in its -constructor. The message ports are described by a name, which is in -practice a PMT symbol (<em>i.e.</em>, an interned string). The API calls -to register a new port are: - -\code - void message_port_register_in(pmt::pmt_t port_id) - void message_port_register_out(pmt::pmt_t port_id) -\endcode - -In Python: - -\code - self.message_port_register_in(pmt.intern("port name")) - self.message_port_register_out(pmt.intern("port name")) -\endcode - -The ports are now identifiable by that port name. Other blocks who may -want to post or receive messages on a port must subscribe to it. When -a block has a message to send, they are published on a particular -port using the following API: - -\code - void message_port_pub(pmt::pmt_t port_id, pmt::pmt_t msg); -\endcode - -In Python: - -\code - self.message_port_pub(pmt.intern("port name"), <pmt message>) -\endcode - -Subscribing is usually done in the form of connecting message ports -as part of the flowgraph, as discussed later. Internally, when message -ports are connected, the gr::basic_block::message_port_sub method is -called. - -Any block that has a subscription to another block's output message -port will receive the message when it is published. Internally, when a -block publishes a message, it simply iterates through all blocks that -have subscribed and uses the gr::basic_block::_post method to send the -message to that block's message queue. - - -\subsection msg_passing_msg_handler Message Handler Functions - -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: - -\code - set_msg_handler(pmt::pmt_t port_id, - boost::bind(&block_class::message_handler_function, this, _1)); -\endcode - -In Python: - -\code - self.set_msg_handler(pmt.intern("port name"), <msg handler function>) -\endcode - -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 -is: - -\code - void block_class::message_handler_function(pmt::pmt_t msg); -\endcode - -In Python the equivalent function would be: - -\code - def handle_msg(self, msg): -\endcode - -We give examples of using this below. - - -\subsection msg_passing_fg_connect Connecting Messages through the Flowgraph - -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 -connection in the flowgraph (in Python) looks like the following: - -\code - self.tb.msg_connect(src, "pdus", dbg, "print") -\endcode - -All messages published by the \b src block on port \a pdus will be -received by \b dbg on port \a print. Note here how we are just using -strings to define the ports, not PMT symbols. This is a convenience to -the user to be able to more easily type in the port names (for -reference, you can create a PMT symbol in Python using the -pmt::intern function as pmt.intern("string")). - -Users can also query blocks for the names of their input and output -ports using the following API calls: - -\code - pmt::pmt_t message_ports_in(); - pmt::pmt_t message_ports_out(); -\endcode - -The return value for these are a PMT vector filled with PMT symbols, -so PMT operators must be used to manipulate them. - -Each block has internal methods to handle posting and receiving of -messages. The gr::basic_block::_post method takes in a message and -places it into its queue. The publishing model uses the -gr::basic_block::_post method of the blocks as the way to access the -message queue. So the message queue of the right name will have a new -message. Posting messages also has the benefit of waking up the -block's thread if it is in a wait state. So if idle, as soon as a -message is posted, it will wake up and call the message handler. - - -\section msg_passing_posting Posting from External Sources - -An important feature of the message passing architecture -is how it can be used to take in messages from an external source. We -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::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 -decoded into tags and sent on the tagged stream. - -So if we have created a \b src block as a PDU to stream, it has a \a -pdus input port, which is how we will inject PDU messages into the -flowgraph. These PDUs could come from another block or flowgraph, but -here, we will create and insert them by hand. - -\code - port = pmt.intern("pdus") - msg = pmt.cons(pmt.PMT_NIL, pmt.make_u8vector(16, 0xFF)) - src.to_basic_block()._post(port, msg) -\endcode - -The PDU's metadata section is empty, hence the pmt::PMT_NIL -object. The payload is now just a simple vector of 16 bytes of all -1's. To post the message, we have to access the block's gr::basic_block -class, which we do using the gr::basic_block::to_basic_block method and -then call the gr::basic_block::_post method to pass the PDU to the -right port. - -All of these mechanisms are explored and tested in the QA code of the -file qa_pdu.py. - -There are some examples of using the message passing infrastructure -through GRC in gr-blocks/examples/msg_passing. - - -\section msg_passing_commands Using messages as commands - -One important use of messages is to send commands to blocks. Examples for this include: - -- gr::qtgui::freq_sink_c: The scaling of the frequency axis can be changed by messages -- gr::uhd::usrp_source and gr::uhd::usrp_sink: Many transceiver-related settings can - be manipulated through command messages, such as frequency, gain and LO offset -- gr::digital::header_payload_demux, which receives an acknowledgement from a header parser - block on how many payload items there are to process - -There is no special PMT type to encode commands, however, it is strongly recommended -to use one of the following formats: - -- pmt::cons(KEY, VALUE): This format is useful for commands that take a single value. - Think of KEY and VALUE as the argument name and value, respectively. For the case of - the QT GUI Frequency Sink, KEY would be "freq" and VALUE would be the new center frequency - in Hz. -- pmt::dict((KEY1: VALUE1), (KEY2: VALUE2), ...): This is basically the same as the - previous format, but you can provide multiple key/value pairs. This is particularly - useful when a single command takes multiple arguments which can't be broken into - multiple command messages (e.g., the USRP blocks might have both a timestamp and a - center frequency in a command message, which are closely associated). - -In both cases, all KEYs should be pmt::symbols (i.e. strings). VALUEs can be -whatever the block requires. - -It might be tempting to deviate from this format, e.g. the QT Frequency sink could -simply take a float value as a command message, and it would still work fine. -However, there are some very good reasons to stick to this format: - -- Interoperability: The more people use the standard format, the more likely it - is that blocks from different sources can work together -- Inspectability: A message debug block will display more useful information about - a message if it's containing both a value and a key -- Intuition: This format is pretty versatile and unlikely to create situations - where it is not sufficient (especially considering that values are PMTs themselves). - As a counterexample, using positional arguments (something like "the first argument - is the frequency, the second the gain") is easily forgotten, or changed in one place - and not another, etc. - - -\section msg_passing_examples Code Examples - -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::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: \a print, \a -store, and \a pdu_print. The \a print port simply prints out all -messages to standard out while the \a store port keeps a list of all -messages posted to it. The \a pdu_print port specially formats PDU -messages for printing to standard out. The \a store port works in -conjunction with a gr::blocks::message_debug::get_message(int i) call -that allows us to retrieve message \p i afterward. - -The constructor of this block looks like this: - -\code -{ - message_port_register_in(pmt::mp("print")); - set_msg_handler(pmt::mp("print"), - boost::bind(&message_debug_impl::print, this, _1)); - - 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)); -} -\endcode - -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 \a bind function (<a target="_blank" -href="http://www.boost.org/doc/libs/1_52_0/libs/bind/bind.html">Boost::bind</a>) -here binds the callback to a function of this block's class. 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 -messages passed to them. Below is the \a print function for reference. - -\code -void -message_debug_impl::print(pmt::pmt_t msg) -{ - std::cout << "***** MESSAGE DEBUG PRINT ********\n"; - pmt::print(msg); - std::cout << "**********************************\n"; -} -\endcode - -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::blocks::tagged_stream_to_pdu block only defines a single -output message port. In this case, its constructor contains the line: - -\code -{ - message_port_register_out(pdu_port_id); -} -\endcode - -So we are only creating a single output port where \a pdu_port_id -is defined in the file pdu.h as \a pdus. - -This block's purpose is to take in a stream of samples along with -stream tags and construct a predefined PDU message from it. In GNU -Radio, we define a PDU as a PMT pair of (metadata, data). The metadata -describes the samples found in the data portion of the -pair. Specifically, the metadata can contain the length of the data -segment and any other information (sample rate, etc.). The PMT vectors -know their own length, so the length value is not actually necessary -unless useful for purposes down the line. The metadata is a PMT -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::blocks::tagged_stream_to_pdu_impl::send_message function that is -shown below. - -\code -void -tagged_stream_to_pdu_impl::send_message() -{ - if(pmt::length(d_pdu_vector) != d_pdu_length) { - throw std::runtime_error("msg length not correct"); - } - - pmt::pmt_t msg = pmt::cons(d_pdu_meta, - d_pdu_vector); - message_port_pub(pdu_port_id, msg); - - d_pdu_meta = pmt::PMT_NIL; - d_pdu_vector = pmt::PMT_NIL; - d_pdu_length = 0; - d_pdu_remain = 0; - d_inpdu = false; -} -\endcode - -This function does a bit of checking to make sure the PDU is OK as -well as some cleanup in the end. But it is the line where the message -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::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 \a 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::blocks::pdu_to_tagged_stream block. - -For a Python block example, see \ref pyblocks_msgs. - -*/ |