Stream Tags
Introduction
GNURadio原本是一个流媒体系统,没有其他的机制在块之间传递数据。数据流是一个可以很好地处理采样值、比特等的模型,但是缺乏控制手段和元数据。
其中的一部分是通过消息传递接口来解决的,它允许块认同flowgraph中任何其他块发布的消息。消息传递系统的主要缺点是异步工作,这意味着当消息相对于数据流到达时,没有保证。
流标签是与主数据流并行运行的等同步数据流。流标签是由一个块的工作函数生成的,并且从那里流向下游的一个特定的采样值,直到它到达一个接收器,或者被迫停止另一个块的传播。
流标签是为数据流中的特定项目定义的,并以键:值(key:vaule)对组成。键识别值是什么,而值保存标签所包含的数据。键和值都是PMTs(多态类型),其中键是PMT符号,而值是任何类型的PMT,因此可以处理我们希望传递的任何数据。标签的第四个部分是srcid,它是PMT符号,用于识别创建标记的块(通常是block的别名())。
API Extensions to the gr::block
要启用流标签,我们已经拓展了gr::block的API来理解absolute条目数字。在数据流模型中,每个块的工作函数在从0到N-1的数据流中都有一个缓冲区。这是数据流的相对偏移量。绝对引用从流程图的开始开始,并继续与任何条目一起计数。每个输入流都与“读取的条目数量”的概念相关联,并且每个输出流都有一个“被写入的条目数量”。这些都是通过两个API调用来实现的:
unsigned long int nitems_read(unsigned int which_input);
unsigned long int nitems_written(unsigned int which_output);
每个标记都与这个绝对时间范围内的某个条目相关联,这些项是使用这些函数计算的。
和其他数据流一样,在调用工作函数时,读/写的条目数只更新一次。因此,在一个工作函数中,读/写的条目数将在工作函数开始时引用数据流的状态。因此,我们必须在数据流中增加当前相对偏移量。因此,如果我们对所有的输出项迭代i次,我们将把流标签写在nitems_written(0)+i的输出端口上,为第0个输出端口。
Stream Tags API
流标签API由4个函数组成,2个用来添加,2个用来获取流标签。这些函数只在调用通用工作/工作函数时被访问。虽然可以通过一个块在其他地方调用它们,但是由于没有对缓冲区中条目计数的确切了解,工作函数之外的行为没有定义。
Adding a Tag to a Stream
这里定义了两个函数调用来添加item标签。我们将一个标记添加到块的特定输出流中。如果需要的话,我们可以将它们输出到多个输出流中,但是要这样做,就意味着每个端口都调用一个函数。
同样,标签被定义为:
offset: 在绝对项目时间内,数据流中的标记的偏移量。
key: ***:***PMT符号,标识标签的类型。
value: PMT类型数据,保存标签的数据。.
srcid: (可选的)PMT符号,标识创建标签的块。
我们可以建立一个gr::tagt结构来保存所有的标签信息,这可能是最简单的方法。gr:tagt struct被定义为拥有与上面列表相同的成员。要向流添加gr::tagt标签,请使用该函数:
void add_item_tag(unsigned int which_output, consttag_t &tag);
二级API允许我们通过显式列出函数调用中的所有标记信息来创建标记:
void add_item_tag(unsigned int which_output,
uint64_t abs_offset,
const pmt::pmt_t &key,
const pmt::pmt_t &value,
const pmt::pmt_t &srcid=pmt::PMT_F);
Getting tags from a Stream
为了从一个特定的输入流中获得标记,我们有两个可以使用的函数。这两个都传回了gr::tagt向量。第二个函数允许我们指定一个特殊的键(作为PMT符号),它会滤除我们感兴趣的键以外的所有键,这减少了工作函数中获取正确标签数据的工作量。
第一个调用只返回给定范围项之间的任何标记:
void get_tags_in_range(std::vector &v,
unsigned int which_input,
uint64_t abs_start,
uint64_t abs_end);
向这个函数添加第五个参数允许我们对键key进行过滤。
void get_tags_in_range(std::vector &v,
unsigned int which_input,
uint64_t abs_start,
uint64_t abs_end,
const pmt::pmt_t &key);
Tag Propagation
标签像正常的数据流一样,从一个块传播到另一个块。如何实际移动块取决于具体的传播策略。我们定义了三种类型的政策:
All-to-All: 来自任何输入端口的所有标签都被复制到所有输出端口
One-to-One: 输入端口i的标签只被复制到输出端口i(依赖于num inputs = num outputs)。
Dont: 不传播标记。标签要么在这里停止,要么工作函数传播它们本身。
块的默认行为是“All-to-All”的传播方法。
要设置不同的传播策略,请使用该函数:
void set_tag_propagation_policy(tag_propagation_policy_tp);
请参阅gr::block::tag_propagation_policy_t文档,以了解该枚举类型的详细信息。
Tag Propagation through Rate Changes
当一个标记通过一个具有速率变化的块传播时,数据流中的条目的偏移量将发生变化。调度器使用block的gr::block::relative_rate来执行标签的偏移值的更新。块的相对变化率决定了输入速率和输出速率之间的关系。下抽数值为D的下抽器的相对变化率是1/D。
同步块(gr::syncblock)、下抽器(gr::sync_decimator)和插值器(gr::sync_interpolator)都具有预定义的和易于理解的相对速率。一个标准的gr::block的默认相对速率是1.0,但是如果它不这样工作,那么它必须被设置。通常,我们使用一个gr::block,因为我们没有预先设想的输出项的输入数量。如果重要的是通过这些块传递标签来考虑项目值的变化,那么我们就必须使用TPP_DONT标记传播策略并在内部处理传播。
在任何情况下,标签的值都是通过块传播的。当使用TaggedStream Blocks.时,这就变得相关了。
Notes on How to Use Tags
标签对应用程序非常有用,而且它们的应用也在扩展。如果有任何变化,USRP源会生成关于时间、采样率和频率的标签信息。我们有一个元数据文件source/sink,它使用标记来存储关于数据流的信息。但是,当在块中使用标记时,有一些事情需要考虑。
首先,当没有使用标记时,调度程序几乎没有任何影响。但是,当我们使用标记时,我们通过从数据流中获取和提取标记来增加开销。我们还使用开销来传播标记。对于每一个标签,每个块必须将一个标记向量从一个块的输出端口(s)复制到下一个块(s)的输入端口(s)。这些复制操作可以加起来。
关键是要尽量减少标签的使用。在必要时使用它们,并尝试提供一些控制,以控制如何生成标签来控制它们的频率。一个很好的例子是USRP source,,它生成一个时间标记。如果每个采样值都生成一个标记,那么每秒钟就会有数千个标记,这将增加大量的开销。相反,如果我们以t0开始,采样率为sr,那么在N个样本之后,我们知道我们现在是在t0+N/sr。因此不断产生新标签不会增加信息。
在上述情况下,我们需要处理的主要问题是,从USRP收到的数据包中出现了不连续点。由于我们无法在流程图中知道有多少样本可能丢失,我们已经失去了对计时信息的跟踪。USRP驱动程序识别数据包何时被丢弃,并使用它来排队等候另一个标签,这允许我们重新同步。同样地,每当采样率或频率发生变化时,都会发出一个新的标记。