GNURadio入门教程-(3)自定义Hier和Python Block

3、自定义块

3.1 如何创建Hier 块(自定义封装块)

(参考实验 A05_Create_Hier_block.grc 和 Self_FrequencyShifter.grc)

一个Hier块被用作封装器,将多个GNU无线电块封装为一个块,重新加载块列表,使其出现在块列表中。

下面示例是一个封装的的移频器,它将信号源与输入信号相乘。

 

第一步是创建流程图。将以下块拖放到工作区中,并连接:

1、 Signal Source

2、 Multiply

3、 Noise Source

4、 Low Pass Filter

5、 Throttle

6、 QT GUI Frequency Sink

7、 QT GUI Range

 

 

更新 QT GUI Range属性:

l Id: frequency

l Default Value: 0

l Start: -samp_rate/2

l Stop: samp_rate/2

更新Low Pass Filter属性:

l 截止频率Cutoff Freq (Hz): samp_rate/4

l 过渡宽度Transition Width (Hz): samp_rate/8

 

3.1.1 创建 Hier 块

在工作区窗口中框选Signal SourceMultiply块,包括它们之间的连接:

 

如下图,右键选中部分,然后选择More->Create Hier,一个新的GRC流图将被创建。

 

新创建的流图如下:

 

其中,Pad用于指定Hier块上的输入和输出端口。Pad Source 和 Pad Sink是Hier的输入和输出端口。 Parameter块将成为未来定义好的Hier block的参数属性,几个Parameter将有几个属性。

双击Options块并编辑属性:

  • Id: Self_FrequencyShifter (.grc和对应.py的文件名)
  • Title: Self_FrequencyShifter Block (将成为自定义block的块名)
  • Generate Options: Hier Block

 

然后,Category属性将更改,以显示Hier块所属类别

Category是可以在GRC右侧的块库中找到块的位置。Hier 块将位于 GRC Hier 块下,而不是 GNU Radio 其它块所在处。

 

3.1.2 Variables 与 Parameters 块

Variables块不同于 GNU Radio 中的ParametersParameters块为Hier创建一个接口以接受来自外部的值,仅对当前block有效(相当于局部变量),几个Parameter块就是Hier block的几个参数属性。而Variables仅存在于 Hier 块或某流图文件的内部,对整个流图有效(相当于全局变量),如下图所示:

 

例如,samp_rate Variables只能在 hier 块内访问:

 

samp_rate Variables需要替换为Parameters(新版本创建Hier块是自动变为Parameters块),以便可以从较大流图中的另一个块更新该数据。删除samp_rate变量并将Parameters块添加到 GRC 工作区中:

 

编辑Parameters属性:

 

添加第二个Parameters,并设置属性:

 

将频率参数添加到Signal Source 块的频率属性:

 

最终Hier块的流程图应如下所示,保存:

 

3.1.3 生成 Hier 块的代码

单击生成流图 以创建Hier源代码:

 

将创建一个 .py 文件和 .yml 文件。对于 GNU Radio v3.10,文件目录在您的主目录中:

/home/$USER/.grc_gnuradio/

需要单击Reload按钮,以更新模块的内部列表,然后才能在GRC块库中找到自定义的移频器FrequencyShifter模块:

 

在块库中将出现一个新的类别GRC Hier block,而自定义的移频器模块就在其中

 

3.1.4 使用Hier块

现在可以在流程图中使用Hier块。返回起始流程图并删除信号源乘法块:

移频器模块添加到工作区,并将其连接到流程图的其余部分:

 

通过添加samp_rate和frequency参数来编辑移频器块属性:

 

运行流程图将打开带有 QT QUI Range的 QT GUI 频率接收器窗口:

 

拖动频率滑块将通过移频器块参数传递该值,从而导致信号的中心频率被修改:

 

3.1.5 删除Hier块

可以从通过从/home/$USER/.grc_gnuradio目录中删除文件以从磁盘中清除 hier 块。

cd /home/$USER/.grc_gnuradio
rm FrequencyShifter.py FrequencyShifter.block.yml

单击“重新加载块”按钮以更新GRC的块库:

 

3.2 用 Python Block 自定义块原型

(参考实验 A06_Create_First_Python_Block.grc)

使用Embedded Python Block定义原型,它只能在创建它的流程图中使用(而Hier块是添加到了块库中,所以可以在别的流程图中使用)。

Embedded Python块是一种在流程图中快速制作块原型的工具。搜索 Python Block并将其添加到工作区:

 

 

双击块以编辑属性。嵌入式 Python 块有两个属性,

1、 Code,一个点击框Open in Editor,点击跳转到指向块的 Python 代码。

2、 Example_Param,块的输入参数。

 

 

单击“Open in Editor”以编辑 Python 代码:

将显示一个提示,选择使用哪个文本编辑器来编写 Python 代码。单击使用默认编辑器

 

编辑器窗口显示嵌入式 Python 块的 Python 代码:

 

3.2.1 Python Block的代码构成

 

代码中有 import、__init__、work 三个部分,解析当前代码如下:

1、import语句

包括 NumPy 和 GNU Radio 库。

2__init__声明:

① 接受默认参数为 1.0 的 example_param 参数

② 声明块具有 np.complex64 输入和输出端口,即 GNU Radio Complex Float 32 数据类型

③ 存储输入参数中的self.example_param变量

3、work功能:

① 具有输入input_items和输出output_items参数

② 将数学运算应用于input_items并将结果存储在output_items

③ 返回生成的样本数

3.2.2 更改work函数代码以自定义行为

注意:在修改py代码时,混合使用制表符和空格会引发语法错误。

1、 更改参数名称:将example_param重命名为 additionFlag 以使其更具描述性。从编辑器菜单中选择查找和替换并将默认值从1.0改为True;将块名称改为“Add or Multiply Block”,保存文件:(注意:init函数参数必须有默认值)

 

返回到 GRC 窗口。Embedded Python 块显示 Additionflag 参数,而不是example_param;且块名变为“Add or Multiply Block”

 

2、 编辑块输入输出:默认块具有单个输入和单个输出,但是我们需要两个输入。若要添加输入,请将第二个 np.complex64 添加到in_sig列表中:,保存文件。

 

返回GRC,块变为如下:

 

3、 修改work函数:自定义其功能。

 

连接流程图如下,并调整属性:

 

运行

 

将“Add or Multiply Block”的AdditionFlag属性改为 False再次运行。

根据定义,两个复正弦曲线的乘法在两个频率的总和处产生一个正弦曲线。因此,频率为1000和频率为3000的信号源的乘法是频率为4000的复正弦曲线。运行流程图时可以看到这个复正弦曲线:

 cc

 

3.2.3 支持向量输入输出的Python Block

(实验A07_Create_Python_Block_With_Vector.grc)

修改Python 嵌入式Block,使其输入和输出类型为向量格式。

首先,将Signal Source、Throttle、Stream to Vector、Embedded Python Block、Vector to Stream、QT GUI Time Sink、Virtual Sink、Virtual Source、Variable 加入工作区,并修改参数:

  • Signal Source
    • Output Type: float
    • Frequency: 100
  • Variable
    • Id: vectorLength
    • Value: 16
  • Stream to Vector, Num Items: vectorLength
  • Vector to Stream, Num Items: vectorLength
  • Virtual Sink, Stream Id: sinusoid
  • Virtual Source, Stream Id: sinusoid
  • QT GUI Time Sink
    • Autoscale: Yes
    • Number of Inputs: 2

 

Virtual Source与Virtual Sink成对出现时,必须设置相同的Stream ID 值,作用其实相当于一根导线直连。

 

Embedded Python Block需要修改为:

a) 接受向量输入

b) 产生向量输出

c) 将数据类型更改为浮动

在使用向量时,必须指定向量的长度以及向量中元素的数据类型。我们使用Python中的元组来实现这一点。双击该块以编辑源代码。

 

运行得出结果:

 

3.2.4 向量长度不匹配两种报错情况

Embed Python Block有一个不同于其他树外模块的地方。在流图可以运行之前,GRC检查以确保所有连接的数据类型和向量大小匹配。

a) 代码中默认参数与块属性的向量大小不匹配:代码中默认参数的向量大小与流图中前后块的向量大小匹配,但该Python Block块参数属性中向量大小不匹配,GRC检查不出错误,只有运行后才能报Error。

在上述案例中,__init__()函数中vectorSize的默认值

def __init__(self, vectorSize=16):
用于定义输入和输出的向量大小,
in_sig=[(np.float32,vectorSize)], 
out_sig=[(np.float32,vectorSize)]
向量大小 = 16。GRC 假定输入和输出端口是长度为 16 的向量,但即使通过块属性传入不同的参数,(如通过属性将vectorSize大小设为 128 ),GRC 不会将其捕获为错误,但流图在运行后将崩溃:

 

Traceback (most recent call last):

File “/home/username/vectorinput.py”, line 250, in <module>

main()

File “/home/username/vectorinput.py”, line 226, in main

tb </span>=<span style="color: #000000;"> top_block_cls()

File “/home/username/vectorinput.py”, line 188, in init

self.connect((self.blocks_stream_to_vector_0, 0), (self.epy_block_0, 0))

File “/usr/lib/python3/dist-packages/gnuradio/gr/hier_block2.py”, line 48, in wrapped

func(self, src, src_port, dst, dst_port)

File “/usr/lib/python3/dist-packages/gnuradio/gr/hier_block2.py”, line 111, in connect

self.primitive_connect(</span>*<span style="color: #000000;">args)

File “/usr/lib/python3/dist-packages/gnuradio/gr/runtime_swig.py”, line 4531, in primitive_connect

</span><span style="color: #0000ff;">return</span> _runtime_swig.top_block_sptr_primitive_connect(self, *<span style="color: #000000;">args)

RuntimeError: itemsize mismatch: stream_to_vector0:0 using 64, Embedded Python Block0:0 using 512

 

b) 代码中默认参数与块属性的向量大小不匹配:代码中默认参数的向量大小与流图中前后块的向量大小不匹配,但该Python Block块参数属性中向量大小与前后块匹配,GRC 将检出错误。如下示例这种情况,代码中的默认向量长度为 128,但传入的参数为 16,GRC能直接检出错误:

 

3.2.5 Python Block的 流索引

对于流,可以使用 端口号和样本索引 对输入和输出进行索引。

基于端口号的索引将返回特定端口的所有输入样本。例如

input_items[0] # 返回端口 0 上的所有输入样
input_items[0][3]  # 返回端口 0 上的第 4 个输入示例

流的索引一般化为:

input_items[portIndex][sampleIndex]
output_items[portIndex][sampleIndex]

下图显示了如何可视化流索引:

 

3.2.6 Python Block的 向量索引

使用向量时,输入input_items和输出output_items包含额外的维度。

向量增加了一个额外的维度,在下面表示为向量索引input_itemsoutput_items现在是三维数组:

input_items[portIndex][vectorIndex][sampleIndex]
output_items[portIndex][vectorIndex][sampleIndex]

基于 portIndex 端口索引返回某端口所有向量和样本的二维数组,例如:

input_items[portIndex]
output_items[portIndex]

基于 portIndex 和 vectorIndex 的索引返回样本的一维数组,例如:

input_items[portIndex][vectorIndex]
input_items[portIndex][vectorIndex]

基于 portIndex、vectorIndex 和 sampleIndex 的索引返回单个样本。

input_items[portIndex][vectorIndex][sampleIndex]
input_items[portIndex][vectorIndex][sampleIndex]

下面给出了一个可视化的向量索引示例:(此处认为官网写反了,已订正如下)

 

 

例子:创建最大保持函数(A07_Create_Python_Block_With_Vector.grc)

在 input_items[0] 中的所有向量上添加一个循环:

for vectorIndex in range(len(input_items[0])):

计算向量的最大值:

maxValue = np.max(input_items[0][vectorIndex])

遍历每个输入样本:

for sampleIndex in range(len(input_items[0][vectorIndex])):

分配每个输出样本最大值

output_items[0][vectorIndex][sampleIndex] = maxValue

代码应如下所示:

 

3.2.7 多端口Python Block向量输入输出

(实验A08_Create_Python_Block_With_MultipleVectorPorts.grc)

修改Max Hold Block以添加第二个向量输入和输出端口。

将Noise Source、Stream to Vector、Vector to Stream、Virtual Sink、Virtual Source、QT GUI Time Sink 添加到工作区:

更改以下块属性:

  • Noise Source, Output Type: float
  • Stream to Vector, Num Items: vectorLength
  • Vector to Stream, Num Items: vectorLength
  • Virtual Sink, Stream Id: noise
  • Virtual Source, Stream Id: noise
  • QT GUI Time Sink
    • Autoscale: Yes
    • Number of Inputs: 2 

连接模块:

 

 

编辑Max Hold Block的代码。添加两个向量输入和输出:

in_sig=[(np.float32,vectorSize),(np.float32,vectorSize)],
out_sig=[(np.float32,vectorSize),(np.float32,vectorSize)]

 

work()  函数基于两个输入端口输入的数据执行某些运算,更改如下:

for portIndex in range(len(input_items)):
</span><span style="color: #0000ff;">for</span> vectorIndex <span style="color: #0000ff;">in</span><span style="color: #000000;"> range(len(input_items[portIndex])):

    maxValue </span>=<span style="color: #000000;"> np.max(input_items[portIndex][vectorIndex])

        </span><span style="color: #0000ff;">for</span> sampleIndex <span style="color: #0000ff;">in</span><span style="color: #000000;"> range(len(input_items[portIndex][vectorIndex])):

            output_items[portIndex][vectorIndex][sampleIndex] </span>= maxValue</pre>

代码现在应如下所示:

 

保存代码并连接块:

 

运行流程图。现在将生成两个最大保持输出,一个用于噪声源,一个用于正弦波:

 

3.2.8 Python Block的消息传递

  可以先看中级教程中消息传递机制。

(实验 A09_Python_Block_Message_Pasing.grc)

消息端口(message port)之间的连线是虚线,而流数据端口(streaming port)之间的连线是实线。

 

 

本次实验以流程图演示了如何:

a) 将消息发送和接收端口添加到 Python Block

b) 发送消息

c) 接收和处理消息

d) 根据收到的消息调整 work() 函数中的块行为

创建两个自定义Embedded Python Block是为了:

a) 根据接收到的消息选择或多路复用两个输入信号之一

b) 计算样本数并向多路复用模块发送消息以切换输入

 

先创建以下流程图

 

打开Python Block编辑代码:不需要example_param,因此请从 __init__() 函数中删除变量example_param,以及与之相关的语句。

将块的名称更改为多路复用器

name='Multiplexer',

向块添加第二个输入:

in_sig=[np.complex64, np.complex64],

(1) 为Multiplexer定义消息的输入端口

返回到代码编辑器。需要添加输入消息端口。

创建一个变量来存储消息端口名称:

self.selectPortName = 'selectPort'

添加一行以创建或注册消息输入端口:

self.message_port_register_in(pmt.intern(self.selectPortName))  # 调用了pmt,需要先导包 import pmt

添加一行以将输入端口与消息处理程序连接。

self.set_msg_handler(pmt.intern(self.selectPortName), self.handle_msg)

(2) 给Multiplexer创建消息处理程序

消息处理程序是在收到消息时调用的函数。

必须定义消息处理程序函数。此消息处理程序根据收到的消息在两个输入端口之间切换。收到的消息是真或假的布尔值。在 __init__() 下定义一个新变量,它是输入选择器,

self.selector = True

再定义 handle_msg() 函数:

def handle_msg(self, msg):
    self.selector = pmt.to_bool(msg)

函数 pmt.to_bool() 获取消息 PMT,然后将数据类型转换为 Python 的布尔数据类型。PMT 用于将消息传递给抽象数据类型。例如,消息可用于发送和接收字符串、浮点数、整数甚至列表。有关 PMT 的更多信息,请访问多态类型 (PMT) 维基页面

 

(3) 在该work()中使用消息

多路复用器的外部接口已完成。修改块的 work() 函数以添加多路复用操作。将以下代码添加到 work() 函数中:

 

如果 self.selector = True,则多路复用器块选择端口 0,如果 self.selector = False,则选择端口 1self.selector 的默认值在 __init__() 函数中定义。

保存代码 (CTRL+S) 并返回到 GRC。多路复用器块具有消息端口选择端口

 

 

 

运行流程图以确保一切正确,然后再继续。如简介中所述,不必连接消息端口即可运行流图。由于 self.selector 的默认值为 True,因此多路复用器的 work() 函数将选择端口 0 并将其发送到输出。QT GUI 时间接收器显示噪音:

 

 

(4) 定义另一个Python Block:Selector Control进行控制多路复用器

另一个嵌入式 Python 模块用于计算它收到的样本数量,然后将控制消息发送到多路复用器块以切换选择器。

 

编辑 Python 块的代码。更改 __init__() 函数中的参数example_param:

def __init__(self, Num_Samples_To_Count=128):

更改块的名称:

name='Selector Control',

将Num_Samples_To_Count存储为私有变量:

self.Num_Samples_To_Count = Num_Samples_To_Count

删除 work() 函数中的example_param乘法:

 

(5) 定义Selector Control的消息输出端口

 

(6) 在其work()函数中发送消息

不需要为输出端口定义消息处理程序。但是,需要修改 work() 函数以创建发送消息的逻辑。

首先在 __init__() 中创建两个变量:

self.state = True
self.counter = 0

添加该行以增加每次调用 work() 的计数样本数:

self.counter = self.counter + len(output_items[0])

添加逻辑以在超过计数器时发送消息:

if (self.counter > self.Num_Samples_To_Count):
PMT_msg </span>=<span style="color: #000000;"> pmt.from_bool(self.state)

self.message_port_pub(pmt.intern(self.portName), PMT_msg)

self.state </span>= <span style="color: #0000ff;">not</span><span style="color: #000000;">(self.state)

self.counter </span>= 0</pre>

 

该逻辑使用 pmt.from_bool() 函数调用将 self.state 的 Python 布尔数据类型转换为 PMT,然后在输出消息端口上发送或发布消息。self.state 变量切换为其相反的值,计数器被重置。

 

保存代码并返回到 GRC。在选择器控制块的属性中输入 32000 作为Num_Samples_To_Count:

 

 

添加Message Debug块并将msg_out端口连接到该块。运行流图显示消息以每秒一次的速率发送,在#t(真)和#f(假)之间交替:

 

 

但是,Selector Control的消息输出端口尚未连接到Multiplexer的输入消息端口,因此QT GUI Time Sink仅显示噪声。

Virtual SinkVirtual Source可用于简洁连接,并使流图更易于理解;将其Stream ID 更改为相同message,如下:

 

运行流程图。QT GUI Time Sink显示Noise SourceSignal Source之间的交替输出:

 

 

3.3 Message Passing示例2

(实验:A10_Message_Passing_demo.grc 将字符串转为字节流输出并存储在文件中 )

拖入嵌入Python块(Embedded Python Block),并将该块重命名为 Message Python Block。作用是将由 QT GUI Message Edit Box 中输入的字符串转换为字节流输出。如下图所示。图中的embedded Python block命名为"Message Passing Demo"。在File Sink中,File为输出字符的保存文件的路径,可根据实际情况自行设定。注意,图中的虚线连接的是消息端口(message ports),实线连接的是数据流端口(streaming ports)。

 

QT GUI Message Edit Box的val为输入,msg为输出。其中,val:接受PMT消息以更新输入框中的值:首先检查消息的类型是否正确(整型、浮点型、字符串型或复数型),然后将其转换为字符串显示在输入框中。在使用is_pair时,会检查PMT以确保它是一个有效的PMT对。然后将键提取为字符串,并根据数据类型处理值。 msg:将输入框中的数据生成PMT消息。如果数据类型不正确且转换失败,则该块产生日志警告消息,但不输出数据。

 

Embedded Python Block的代码如下:(使out输出字节流,clear_input为消息输出端口用于清空输入框)import numpy as np

from gnuradio import gr

“”“ 目的: 将 QT GUI Message Edit Box 中输入的字符串转换为字节流输出 ”“”

import pmt
textboxValue = “”

class blk(gr.sync_block): # other base classes are basic_block, decim_block, interp_block
def init(self): # only default arguments here
“”“arguments to this function show up as parameters in GRC”“”
gr.sync_block.init(
self,
name=‘Message Passing Demo’, # 改名字
in_sig=None, # 本例不需要 stream 数据输入端口
out_sig=[np.byte] # 设置 stream 数据输出端口
)

    self.message_port_register_in(pmt.intern(</span><span style="color: #800000;">'</span><span style="color: #800000;">msg_in</span><span style="color: #800000;">'</span>))  <span style="color: #008000;">#</span><span style="color: #008000;"> 设置消息输入端口</span>
self.message_port_register_out(pmt.intern( ' clear_input ')) # 设置消息输出端口 (注意:消息输出端口和输出端口是两个端口) self.set_msg_handler(pmt.intern( ' msg_in '), self.handle_msg) # 为msg_in端口的输入数据绑定消息处理函数函数
<span style="color: #0000ff;">def</span><span style="color: #000000;"> handle_msg(self, msg):

    </span><span style="color: #0000ff;">global</span> textboxValue  <span style="color: #008000;">#</span><span style="color: #008000;"> 声明全局变量</span>
textboxValue = pmt.symbol_to_string (msg)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> work(self, input_items, output_items):

    </span><span style="color: #0000ff;">global</span> textboxValue  <span style="color: #008000;">#</span><span style="color: #008000;"> 声明全局变量</span>
_len = len(textboxValue) # 获取字符串的长度
    <span style="color: #0000ff;">if</span> (_len &gt;<span style="color: #000000;"> 0):

        </span><span style="color: #008000;">#</span><span style="color: #008000;"> 增加换行符</span>
textboxValue += " \n "
        _len </span>+= 1

        <span style="color: #008000;">#</span><span style="color: #008000;"> 再输出数组中存储字符串</span>

        <span style="color: #0000ff;">for</span> x <span style="color: #0000ff;">in</span><span style="color: #000000;"> range(_len):

            output_items[0][x] </span>= ord(textboxValue[x])  <span style="color: #008000;">#</span><span style="color: #008000;"> ord() 函数以一个字符(长度为1的字符串)作为参数,返回字符对应的 ASCII 数值,或者 Unicode 数值</span>
textboxValue = ""
        self.message_port_pub(pmt.intern(</span><span style="color: #800000;">'</span><span style="color: #800000;">clear_input</span><span style="color: #800000;">'</span>), pmt.intern(<span style="color: #800000;">''</span>)) <span style="color: #008000;">#</span><span style="color: #008000;"> 消息输出端口,输出消息为空,(清空Message Edit Box)</span>

        <span style="color: #0000ff;">return</span><span style="color: #000000;"> (_len)

    </span><span style="color: #0000ff;">else</span><span style="color: #000000;">:

        </span><span style="color: #0000ff;">return</span> (0)</pre>

 

运行后,输入字符按回车,如下图,稍后可以在File Sink 中的文件中看到写入的内容,同时编辑框被清空。

 

观察流图可以发现,图中的Message Passing Demo输入是消息输出是数据流,只要消息的PMTs数据类型与数据流端口的数据类型相匹配,就可以在消息传递和流端口之间切换(在这个demo中,流端口的粉红色表示字节,而在Message Passing Demo中将message消息处理成ASCII码(字节)后输出,两者相匹配)。

 

3.4 Python Block Tags标签/标记

标签是一种以时间同步方式与数字化射频样本一起传递信息的方法。可以在时间轴上对采样数据进行打标签,使下游块可以方便的获取样本的时间戳和一些附属信息。消息以异步方式传达信息,和采样时钟没有关系,而标签是与特定RF样本相关的。

当下游块需要知道接收器是在哪个样本上调谐到一个新频率,或者用于包含特定样本的时间戳时,标签特别有用。

如下图,三角号为tag,可以记录时间戳等信息,并传递到下一个模块。

 

3.4.1 添加和获取标记

包括 Complex Float 32, Float 32, Byte和所有其他格式。

(1) 使用以下api添加标记:

self.add_item_tag(outputPortNumber, absoluteIndex, key, value)

outputPortNumber输出端口号确定将标记添加到哪个输出流。absoluteIndex绝对索引是添加标记(Tags)的样本索引。流程图对每个样本进行计数,产生的第一个样本处于绝对样本索引 0key是包含要存储的变量名的 PMT 类型,value是包含要存储的信息的另一种 PMT 类型。

 

(2) 读取标记可以使用以下函数完成:

tagTuple = self.get_tags_in_window(inputPortNumber, relativeIndexStart, relativeIndexStop))

上图窗口中读取标签会根据当前input_items向量中的相对索引读取它们。获取与当前input_items样本对应的所有标记的最简单方法是使用如下函数调用:

tagTuple = self.get_tags_in_window(inputPortNumber, 0, len(input_items[inputPortNumber]))) 

有关标签的更多信息,请参阅:Stream Tags

3.4.2 示例:阈值检测

(阈值检测实验:A11_Python_Block_Tags.grc,实验创建了两个Python 块,用于检测输入信号何时超过阈值,并为其写入标签,然后再单独的块中读取标签,并用之前的时间戳输出)

(1) 创建测试信号

需要有一个测试信号。拖入以下块:GLFSR SourceRepeatMultiply ConstAdd ConstSingle Pole IIR FilterThrottleQT GUI Time Sink

更改以下参数:

  • GLFSR Source, Degree: 32 (产生-1~1的随机数)
  • Repeat, Interpolation: 128
  • Multiply Const, Constant: 0.5
  • Add Const, Constant: 0.5
  • Single Pole IIR Filter, Alpha: 0.05
  • QT GUI Time Sink
    • Number of Points: 2048
    • Autoscale: Yes
  • samp_rate Variable, Value: 3200

 

将所有块更改为float输入和输出。将它们全部连接起来如下:

 

运行流程图。生成过滤后的 0~1的伪随机序列:

 

(2) 定义Python Block - Threshold Detector(阈值检测器)

拖入一个Python Block,Virtual Source,Virtual Sink,更改代码:

 

更改参数

  • Threshold Detector Threshold: 0.75
  • Report Period: 128
  • Virtual Sink, Stream ID: signal
  • Virtual Source, Stream ID: signal
  • QT GUI Time Sink, name: "Threshold Detector"

连接如下:

 

(3) 写入标签

编写Threshold Detector的代码:导入 pmt 库;在 __init__() 函数下添加两个新变量 self.timer 和 self.readyForTag

self.timer = 0
self.readyForTag = True

work() 函数需要修改:创建一个 for 循环以循环访问所有输入样本,当样本值超过阈值,记录并添加标签。仅当 self.readyForTag 状态变量为 True 时,才会写入标记。写入标记后,状态变量 self.readyForTag 将设置为 False。此后只有 self.timer 达到report_period,状态才会重置。

 

 

运行流程图。超过阈值进行打标签,显示在 QT GUI Time Sink中:

 

(4) 定义检测计数器块

创建一个新的嵌入式 Python 块来读取标签,计算自最后一个标签以来的所有样本数,并将该数字生成为输出。双击嵌入式 Python 块并编辑代码。

 

添加另一个 QT GUI Time Sink并更改属性:

  • QT GUI Time Sink
    • Name: "Detection Counter"
    • Number of Points: 2048
    • Autoscale: Yes

 

(5) 读取标签,以使检测计数器 算出样本数

需要修改检测计数器块才能读取标记。

先导入 pmt 库; 在 __init__() 下添加一个新变量:

self.samplesSinceDetection = 0

修改 work() 函数以读取标签:

# 获取与input_items[0]相关的所有标签
tagTuple = self.get_tags_in_window(0, 0, len(input_items[0]))
#tags = self.get_tags_in_window(which_input, rel_start, rel_end)

遍历所有标签,计算key=“detect”标签的相对偏移量并将其存储在列表中:

# 声明一个列表
relativeOffsetList = []

# 遍历所有“detect”标签并存储它们的相对偏移量
for tag in tagTuple:
if (pmt.to_python(tag.key) == ‘detect’):
relativeOffsetList.append( tag.offset - self.nitems_read(0) )

将偏移量从最低到最高排序:

relativeOffsetList.sort()

遍历所有输出样本:

for index in range(len(output_items[0])):

生成一个输出样本,其中包含自上一次detect标签以来到当前样本的样本计数:

 

output_items[0][index] = self.samplesSinceDetection

 

如果当前输出样本索引大于或等于当前detect标签的索引,说明该索引处对应下一个detect标签,则重新计数,从列表中删除偏移值并重置样本计数器 self.samplesSinceDetection。否则,将样本计数器增加 1。

if (len(relativeOffsetList) > 0 and index >= relativeOffsetList[0]):
    relativeOffsetList.pop(0)   # 删除偏移值
    self.samplesSinceDetection = 0  # 重置输出计数器
else:
    self.samplesSinceDetection = self.samplesSinceDetection + 1

最后,work()函数如下所示:

 

保存代码 (CTRL + S)。运行流程图。输出如下所示:

 

请注意,Detection Counter块输入中的所有标记都会自动传送到其输出。

3.4.3 标签传播

默认情况下,所有输入标签都会传播到所有输出标签。有时不需要标签,需要从某些流中减少或完全删除标签。Tag Gate块可以实现该需求,属性Propagate Tags:No。在Detection Counter块之后连接即可:

 

运行流程图。标签将在对应 QT GUI Time Sink中删除:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值