gnu radio学习(三)Message Passing消息传递详解

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).
GNU Radio最初是一个流系统,没有其他机制可以在块之间传递数据。数据流是一种适用于样本,位等等的一个模型,但是对于控制数据,元数据或者数据包结构来说并不是一个真正正确的机制(至少在传输链上的一些点来说是这样)

We solved part of this problem by introducing the tag stream (see 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.
出于一些原因,我们想要一个更通用的消息传递系统。首先是要允许下游块与上游块通信。其次是允许我们以一种更简单的方式在外部应用与GNU Radio之间来回通信。GNU Radio的消息传递接口处理这些情况,尽管它是在异步基础上进行的。

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).
消息传递接口非常依赖GNU Radio中的多态类型(PMTs)。关于这些数据类型的更多介绍可以参考多态类型页面。
在这里插入图片描述

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.
消息传递的接口设计在gr::basic_block中,这是GNU Radio中所有块的父类。每一个块都有一个消息队列来存放输入的消息同时还可以传递消息给其他块的消息队列。这些块还区分输入和输出端口。

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 (i.e., an interned string). The API calls to register a new port are:
一个块需要在它的构造函数中声明它的消息输入和输出端口。消息端口通过一个名字来描述,这个名字其实是一个PMT符号(例如一个内部字符串)。注册新端口的API调用如下所示:

 void message_port_register_in(pmt::pmt_t port_id)
 void message_port_register_out(pmt::pmt_t port_id)

In Python:

 self.message_port_register_in(pmt.intern("port name"))
 self.message_port_register_out(pmt.intern("port name"))

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:
现在可以通过端口的名字来区分端口。一个块想在一个端口上传输或者接收信息就必须得先订阅它。当一个块有一个消息要传递,它们将会使用下面的API来发送到一个特定的端口:

 void message_port_pub(pmt::pmt_t port_id, pmt::pmt_t msg);

In Python:(python实现方式)

 self.message_port_pub(pmt.intern("port name"), <pmt message>)

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.
订阅通常以连接消息端口的形式作为流图的一部分,稍后在讨论。在内部,当消息端口被连接时,gr::basic_block::message_port_sub方法将被调用。

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.
任何块在订阅了其他块消息输出端口后都将收到它发布的消息。在内部,当一个块发布了一个消息,它只是简单的遍历所有订阅过它的块并使用gr::basic_block::_post方法来传递消息到那些块的消息队列。

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.
订阅者必须声明一个消息处理函数来处理发布给它的消息。在使用gr::basic_block::message_port_register_in声明一个订阅端口后,我们必须将这个端口绑定到消息处理程序上。

Starting in GNU Radio 3.8¹ using C++11 we do that using a lambda function:
从使用c++11的GNU Radio3.8开始我们开始使用下面的lambda函数

 set_msg_handler(pmt::pmt_t port_id, 
   [this](const pmt::pmt_t& msg) { message_handler_function(msg); });

In Python:

 self.set_msg_handler(pmt.intern("port name"), <msg handler function>)

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.
当一个新的消息被放到一个端口的消息队列中后,就是用这个函数来处理消息的。‘port_id’与注册输入端口时使用的PMT相同。‘block_class::message_handler_function’是被指定用来处理端口消息的类的成员函数。

The prototype for all message handling functions is:
所有消息处理函数的原型是:

 void block_class::message_handler_function(const pmt::pmt_t& msg);

In Python the equivalent function would be:
python中的函数类型如下:

 def handle_msg(self, msg):

We give examples of using this below.
我们在下面给出了使用它的实例。

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 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:
从流图层面,我们检测到一个gr::hier_block2::msg_connect方法使得更容易让一个块订阅其他块的消息。假设块src有一个输出消息端口叫做pdus另一个块dbg有一个输入端口叫做print。在python中流图中消息的传递像如下这样:

  self.tb.msg_connect(src, "pdus", dbg, "print")

All messages published by the src block on port pdus will be received by dbg on port 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”)).
所有由src块在pdus端口产生的消息都会被dbg块在print端口接收。注意这里我们是如何仅仅通过字符串来定义端口,而不是PMT符号。这跟方便用户更简单的输入端口名(提示下,你可以在python中你可以像c++中使用pmt::intern函数一样使用pmt.inter(”string“)函数来创建一个PMT标签)。

Users can also query blocks for the names of their input and output ports using the following API calls:
用户 还可以使用以下的API来查询块的输入输出端口名称。

   pmt::pmt_t message_ports_in();
   pmt::pmt_t message_ports_out();

The return value for these are a PMT vector filled with PMT symbols, so PMT operators must be used to manipulate them.
它们的返回值是一个内容为PMT符号的向量组,因此必须使用PMT操作符来操作它们。

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.
每个块都有一个内部方法来处理发送和接收到的消息。gr::basic_block::post方法接收一个消息并把它放入自己消息队列中。所以相应的消息队列中将会有一个新的消息。发送消息同样有唤醒一个在等待状态的块的线程。因此,如果在空闲状态,同时发布一个消息,那么它将唤醒并调动消息处理程序。

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.
消息传递架构的一个重要特点是如何使用它从一个外部的资源中接收消息。我们可以直接使用块的gr::basic_block::post方法传递消息。所以任何有消息输入端口的块可以通过这个方法从外部接收到消息。

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.
以下示例使用gr::blocks::pdu_to_tagged_stream块作为流程图的源块。它的目的是等待一个消息作为PDUs发送给它并将它们转化为普通流,有效载荷将作为一个普通流发送,而元数据将被解码为标签并被标记在流上发送。

So if we have created a src block as a PDU to stream, it has 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.
所以如果我们创建了一个src块作为流式传输的PDU,它有一个pdus输入端口,这就是我们将PDU消息注入流程图的方式。这些PDU可以来自其他块或者流图,但是在这里,我们将手动创建并插入它们。

 port = pmt.intern("pdus")
 msg = pmt.cons(pmt.PMT_NIL, pmt.make_u8vector(16, 0xFF))
 src.to_basic_block()._post(port, msg)

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.
PDU的元数据部分都是空的,因此是pmt::PMT_NIL对象。有效载荷现在只是一个16字节的全1向量。要传递消息,我们必须访问块的gr::basic_block类,我们使用gr::basic_block::to_basic_block方法同时使用gr::basic_block::_post方法将PDU传输到正确的端口。

All of these mechanisms are explored and tested in the QA code of the file qa_pdu.py.
所有的这些机制都在qa_pdu.py文件中的QA代码中进行了探索和检测。

There are some examples of using the message passing infrastructure through GRC in:
这里有一些通过GRC使用消息传输基础设施的例子:

gr-blocks/examples/msg_passing

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(许多收发器相关的设置可以通过命令消息来修改,比如频率,增益和LO偏移)

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类型来编码命令,然而,非常建议使用下面的格式:

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.(这个格式非常适用于使用单个值的命令。KEY和VALUE分别为参数名和值。对于QT GUI Frequency Sink案例,KEY将是”freq“而VALUE为新的中心频率(单位为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).(这和前面的格式基本一样,但是你可以使用多个键值对。这对一个使用多个参数而不能分解为多个命令消息的命令非常有用(例如,USRP块在命令消息中有时间戳和中心频率,它们密切相关))

In both cases, all KEYs should be pmt::symbols (i.e. strings). VALUEs can be whatever the block requires.
在这些案例中,所有的KEY都应该是pmt::symbols(即字符串)。VALUE可以是块中需要的任何值。

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:
偏离这种格式可能很诱人,例如,QT Frequency sink可以简单的使用浮点数值作为一个消息命令,它仍然可以正常工作。但是坚持使用这种格式有很多的好处:

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.(这种格式非常通用,不太可能造成一个不适用的情况(特别是考虑到值本身就是PMT)。作为一个反例,使用位置参数(比如说”第一个参数是频率,第二个参数是增益“)非常容易被遗忘, 或者在一个地方而不是另一个地方改变,等等)。

Code Examples(代码样例)

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::blocks::tagged_stream_to_pdu below to show setting up both input and output message passing capabilities.
以下是当前GNU Radio中利用消息传递的块的代码片段。我们将在下面使用gr::blocks::message_debug和gr::blocks::tagged_stream_to_pdu来显示设置输入和输出消息传递功能。

The gr::blocks::message_debug block is used for debugging the message 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 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.
gr::blocks::message_debug块被用来调试消息传输系统。它描述了两个输入消息端口:print和store。print端口把所有的消息简单的打印到标准输出,但是store端口存放了被传输过来的所有消息列表。store端口与gr::block::message_debug::get_message(size_t i)一同调用,允许我们后面检索消息i。

The constructor of this block looks like this:
这个块的构造如下所示:

 {
   message_port_register_in(pmt::mp("print"));
   set_msg_handler(pmt::mp("print"),
     [this](const pmt::pmt_t& msg) { print(msg); });
 
   message_port_register_in(pmt::mp("store"));
   set_msg_handler(pmt::mp("store"),
     [this](const pmt::pmt_t& msg) { store(msg); });
 }

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.
三个消息输入端口按照它们各自的名字来注册。然后,我们使用gr::basic_block::set_msg_handler函数通过回调函数来识别特定端口的名称。

So now the functions in the block’s private implementation class, 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.
所以现在块的私有实现类中的函数gr::blocks::message_debug_impl::print和gr::blocks::message_debug_impl::store函数被用来处理分配给他们的消息。参考下面的输出函数。

void
 message_debug_impl::print(const pmt::pmt_t& msg)
 {
   std::cout << "***** MESSAGE DEBUG PRINT ********\n";
   pmt::print(msg);
   std::cout << "**********************************\n";
 }

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.
函数只是简单的接收了PMT消息并输出了他们。PMT库中的pmt::print方法是一个更友好和漂亮的方式来输出一个PMT。

The gr::blocks::tagged_stream_to_pdu block only defines a single output message port. In this case, its constructor contains the line:
gr::blocks::tagged_stream_to_pdu块只是定义了单个的输出消息端口。这种情况下,它的构造函数包括了一下行:

{
   message_port_register_out(pdu_port_id);
}

So we are only creating a single output port where pdu_port_id is defined in the file pdu.h as pdus.
所以我们只是创建了一个输出端口,同时pdu_prot_id作为pdus被定义在pdu.h头文件里面。

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.
块的目的是接收样本流和流标签同时从从它们之中构建一个一个预定义PDU消息。在GNU Radio中,我们将一个PDU定义为一个PMT对(元数据,数据)。元数据描述了在该对数据部分找到的样本。特别的是,该元数据能够包含数据段的长度以及任何其他信息(样本率等等)。PMT向量组知道它们自己的长度,所以长度不是必须的,除非在后面的代码中有用。元数据是一个PMT数据字典而数据段是字节、浮点数或者复数值的一个PMT的统一向量。

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.
最后,当一个PDU消息准备好的时候,块会调用它的gr::blocks::tagged_stream_to_pdu_impl::send_message函数,像下面这样:

 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;
 }

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.
此函数会进行一系列的检查来确保PDU是正确的以及最后的一些清理工作。但是发布消息的行对于这项工作来说非常重要。在此,块通过调用gr::basic_block::message_port_pub函数的发布方法来向任意它的订阅者发布消息。

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::blocks::pdu_to_tagged_stream block.
类似的还有一个gr::blocks::pdu_to_tagged_stream块做了一些本质上相反的事情。它充当一个流图的源,并等待发送给它的输入端口pdus的PDU消息。它提取元数据和数据然后处理它们。元数据字典被拆分为键值对并从中创建标签。然后将数据转换为一个项目的输出流并随后传递它们。下一节将描述如何使用gr::blocks::pdu_to_tagged_stream块将PDUs传递到流图中。

Python

A Python Block example:
一个python块样例:

 class msg_block(gr.basic_block):
     def __init__(self):
         gr.basic_block.__init__(
             self,
             name="msg_block",
             in_sig=None,
             out_sig=None)
 
         self.message_port_register_out(pmt.intern('msg_out'))
         self.message_port_register_in(pmt.intern('msg_in'))
         self.set_msg_handler(pmt.intern('msg_in'), self.handle_msg)
 
     def handle_msg(self, msg):
         self.message_port_pub(pmt.intern('msg_out'), pmt.intern('message received!'))

Flowgraph Example(流图示例)

Here’s a simple example of a flow graph using both streaming and messages:
这里是一个同时使用流和消息的流图样例:
在这里插入图片描述
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).
这里有一些有趣的事情值得被指出。首先,这里有两个源块,它们都在一个固定的间隔输出项目,一个以没1000毫秒的间隔输入另一个则以750毫秒的间隔输入。虚线表示连接消息端口,而实线表示连接流端口。在流图的上半部分我们可以看到,实际上,可以实现消息端口和流端口之间的转换,但是只有在PMT的类型与流端口相匹配的时候才可以实现(在本样例中,粉色的流端口表示字节,意味着如果我们想要以流传输我们以PMT形式发送的相同的数据,PMT的格式应该是u8vector的形式)。

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.
    1)一个消息处理器被调用,立刻处理消息。
  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.
    2)消息被写入一个FIFO缓冲区,块可以在任何时候处理它们,这通常在工作函数内实现。

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.
对于一个同时拥有消息端口 和流端口的块来说,这两种选择都可以,取决于应用程序。当时,我们强烈反对在一个工作函数内来处理消息,我们推荐通过使用消息处理器来处理消息。在工作函数内使用消息会使得我们在等待消息的过程中形成阻塞。这对于永远不能被阻塞的工作函数来说是一个非常不好的行为。如果依赖一个消息来操作,使用消息处理程序来接收消息,然后在调用工作函数时来使用消息来告知块的操作。只有在啊特殊明确的场合我们才应该使用上面的方法2。

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:
在之前的流图中,我们有一个叫做PDU to Tagged Stream的块。一个在GNU Radio中的PDU(协议数据单元)有一个特殊的数据类型,它是一对字典(在CAR上)和统一向量类型。所以我们将会生成一个有效的PDU,没有元数据和10个0作为流数据。

pdu = pmt.cons(pmt.make_dict(), pmt.make_u8vector(10, 0))

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.
让我们来创建一个传递消息的应用程序,聊天程序是一个非常理想的用例,因为它等待用户输入一个消息,然后发送它。因此它不需要Throttle块。

Create the following flowgraph and save it as ‘chat_app2.grc’:
创建下面的流图并保存命名为’chat_app2.grc’:
在这里插入图片描述
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.
ZMQ消息块有一个地址为’tcp://127.0.0.1:50261’。一但按下回车键,QT消息编辑框中输入的消息就会发送出去。输出在运行gnuradio-companion的终端窗上。

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_app3.grc’。然后像下面这样改变ZMQ的端口名:

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.
当使用GRC时,执行生成同时/或者运行命令会创建一个同名.grc文件的python文件。你可以在不再次运行GRC的情况下执行python文件。

For testing this system we will use two processes, so we will need two terminal windows.
我们将使用两个进程来测试这个系统,因此我们需要两个终端窗口。

Terminal 1:(终端1)

since you just finished building the chat_app3 flowgraph, you can just do a Run.
因为我们刚刚完成了chat_app3流图的构建,因此我们只需要运行即可。

Terminal 2: Open another terminal window.(终端2:打开另一个终端窗口。)

change to whatever directory you used to generate the flowgraph for chat_app2.
更改为你用于为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.
在chat_app2消息编辑框中输入的消息将会出现在在终端1的屏幕上(chat_app3),反之亦然。

To terminate each of the processes cleanly, click on the ‘X’ in the upper corner of the GUI rather than using Control-C.
要干净利落的终止整个程序,应该点击GUI窗口上的’X’关闭而不是输入Control-C指令。

¹ In old GNU Radio 3.7, we used Boost’s ‘bind’ function:
在老版本的GNU Radio 3.7中我们使用了Boost的’bind‘函数:

 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.
’this‘和’_1‘是使用Boost bind函数传递第一个’this‘指针参数到类(标准OOP实践)中以及_1是函数需要一个附加参数的指示符。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值