基于gnuradio的自适应陷波滤波器OOT模块(notch filter)

基于gnuradio的自适应陷波滤波器OOT模块

  • Introduction
  • 模型工具gr_modtool
  • 创建OOT Module
    • 创建OOT block
    • 修改impl.h
    • 修改impl.cc
    • 修改adaptiveNotch.h
    • 修改.yml
  • 安装模块和block
  • 测试
  • git 源码
  • 后面的工作:

Introduction

使用的gnuraido版本是3.9,在ubuntu20.04的操作系统下开发。
创建gnuradio的OOT的过程主要参考官方的OOT制作教程c++ OOT with gr-modtool

本文将按照官方教程,演示如何制作一个gnuradio的OOT,除了官方教程中的内容,还将涉及到很多官方教程中没有提到的注意点

模型工具gr_modtool

在建模前肯定要下载安装gnuradio的本体库。通过命令可以轻松安装:

sudo apt-get install gnuradio

然而要注意的是,不同的ubuntu版本能够安装的gnuradio版本是不同的,ubuntu18只能安装gnuradio3.8,ubuntu20.4可以安装gnuradio3.9,ubuntu对应的gnuradio版本
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第1张图片
3.8.2.0-0~gnuradio~bionic 中3.8.2指的是gnuradio版本,bionic是ubuntu版本,指的是ubuntu18,请根据自己的ubuntu版本安装gnuradio,想要使用高版本的gnuradio只能升级ubuntu

仅仅有本体是不够的,还需要安装mod的依赖:

sudo apt-get install gnuradio-dev cmake libspdlog-dev clang-format

创建OOT Module

在任意一个目录下打开终端,在终端中输入:

gr_modtool newmod notchFilter

gr_modtool会在当前目录下新建一个目录,目录名为gr-notchFilter

创建OOT block

gnuradio的模块的逻辑是一个模块下有多个block,比如在gnuradio-companion可视化界面中,math模块下有add,abs等block:
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第2张图片
创建block时要先进入module的文件夹中,可以cd进去,也可以打开文件夹后,然后在文件夹中打开终端,这个文件夹中应该有这些(除了build):
在这里插入图片描述
然后在终端中输入:

gr_modtool add adaptiveNotch

意思是给模块增加一个adaptiveNotch的block,我这里因为已经有了adaptivenotch,所以就随便输入了lalala,后面的例子和代码仍以adaptiveNotch为例
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第3张图片
这是我另外作为例子演示以下添加block的效果,gr_modtool提供了添加block的快速配置,首先选block类型,sync是同步的意思,换言之就是block的输入和输出数量是一样的,‘sink’只有输入,‘source’只有输出,‘decimator’,离散器,就是输出比输入少,‘interpolator’插值器,就是输出比输入多,其他的查一下gnuradio的官网吧。
接下来选语言:
在这里插入图片描述
python和c++都可以
在这里插入图片描述
然后输入版权持有者,我填的是我的github名称mortarboard-H。
这个会在代码的顶端自动生成一个注释,注明版权。

接下来就是输入block的参数列表,就像是函数的参数列表一样。
在这里插入图片描述
接下来好像是询问需不需要自动的测试代码,全部no就行
在这里插入图片描述
然后就会自动添加和修改这些文件:
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第4张图片
那么用visual code打开对应的文件夹,
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第5张图片
就会发现多了一些东西。

在这些文件中,为了实现我们需要的功能,主要需要修改的是impl.h,impl.cc,对应的yaml文件。

修改impl.h

/* -*- c++ -*- */
/*
 * Copyright 2022 mortarboard-H.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#ifndef INCLUDED_NOTCHFILTER_ADAPTIVENOTCH_IMPL_H
#define INCLUDED_NOTCHFILTER_ADAPTIVENOTCH_IMPL_H

#include 

namespace gr {
namespace notchFilter {

class adaptiveNotch_impl : public adaptiveNotch
{
private:
    double sampRate;
    double targetFreq;
    double curPhase;
    double radPerSample;

public:
    adaptiveNotch_impl(double sampRate, double targetFreq);
    ~adaptiveNotch_impl();

    // Where all the action really happens
    int work(int noutput_items,
             gr_vector_const_void_star& input_items,
             gr_vector_void_star& output_items);
    void update_sample_rate(double rate) override;
    void set_target_freq(double rate) override;
};

} // namespace notchFilter
} // namespace gr

#endif /* INCLUDED_NOTCHFILTER_ADAPTIVENOTCH_IMPL_H */

在调用gr_modtool 时输入的parameter list会作为构造函数的参数自动地生成,除此以外整体的框架代码是自动生成的,private中的字段是自行添加的,work函数的声明是自动的,该声明不能更改,update_sample_rate和set_target_freq是自己添加的。

修改impl.cc

这个文件存放了h文件中定义的类的实现

/* -*- c++ -*- */
/*
 * Copyright 2022 mortarboard-H.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#include "adaptiveNotch_impl.h"
#include 

namespace gr {
namespace notchFilter {

#pragma message("set the following appropriately and remove this warning")
using input_type = float;//定义输入信号的数据类型
#pragma message("set the following appropriately and remove this warning")
using output_type = float;//定义输出信号的数据类型
adaptiveNotch::sptr adaptiveNotch::make(double sampRate, double targetFreq)
{
    return gnuradio::make_block_sptr<adaptiveNotch_impl>(sampRate, targetFreq);
}


/*
 * The private constructor
 */
adaptiveNotch_impl::adaptiveNotch_impl(double sampRate, double targetFreq)
    : gr::sync_block("adaptiveNotch",
                     gr::io_signature::make(
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),
                     gr::io_signature::make(
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))
{
    this->sampRate=sampRate;
    this->targetFreq=targetFreq;
    this->curPhase=0;
    this->radPerSample=2*M_PI*targetFreq/sampRate;
}

/*
 * Our virtual destructor.
 */
adaptiveNotch_impl::~adaptiveNotch_impl() {}

void adaptiveNotch_impl::update_sample_rate(double rate)
{
    this->sampRate=rate;
}

void adaptiveNotch_impl::set_target_freq(double rate)
{
    this->targetFreq=rate;
    this->radPerSample=2*M_PI*targetFreq/this->sampRate;
}


int adaptiveNotch_impl::work(int noutput_items,
                             gr_vector_const_void_star& input_items,
                             gr_vector_void_star& output_items)
{
    auto in = static_cast<const input_type*>(input_items[0]);
    auto out = static_cast<output_type*>(output_items[0]);

#pragma message("Implement the signal processing in your block and remove this warning")
    // Do <+signal processing+>
    //自适应陷波滤波的具体实现
    float w0=0.1,w1=0.1;
    double updateRate=0.01;
    for(int index=0;index<noutput_items;index++)
    {
        float in0=cos(curPhase);
        float in1=sin(curPhase);
        
        float outLMS=w0*in0+w1*in0;
        out[index]=in[index];
        out[index]-=outLMS;
        //out[index]=in0;
        auto error=out[index];
        w0+=(updateRate*error*in0);
        w1+=(updateRate*error*in1);
        curPhase+=radPerSample;
    }
    // Tell runtime system how many output items we produced.
    return noutput_items;
}

} /* namespace notchFilter */
} /* namespace gr */

至于自适应陷波滤波的原理可以自行百度。

修改adaptiveNotch.h

/* -*- c++ -*- */
/*
 * Copyright 2022 mortarboard-H.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#ifndef INCLUDED_NOTCHFILTER_ADAPTIVENOTCH_H
#define INCLUDED_NOTCHFILTER_ADAPTIVENOTCH_H

#include 
#include 

namespace gr {
namespace notchFilter {

/*!
 * \brief <+description of block+>
 * \ingroup notchFilter
 *
 */
class NOTCHFILTER_API adaptiveNotch : virtual public gr::sync_block
{
public:
    typedef std::shared_ptr<adaptiveNotch> sptr;

    /*!
     * \brief Return a shared_ptr to a new instance of notchFilter::adaptiveNotch.
     *
     * To avoid accidental use of raw pointers, notchFilter::adaptiveNotch's
     * constructor is in a private implementation
     * class. notchFilter::adaptiveNotch::make is the public interface for
     * creating new instances.
     */
    static sptr make(double sampRate, double targetFreq);
    /*!
     * \brief update sample rate
    */
    virtual void update_sample_rate(double rate)=0;

    /*!
     * \brief update target frequency
    */
    virtual void set_target_freq(double rate)=0;
};

} // namespace notchFilter
} // namespace gr

#endif /* INCLUDED_NOTCHFILTER_ADAPTIVENOTCH_H */

这是官方教程中没有提到的地方,这里主要是添加了update_sample_rate和set_target_freq的纯虚函数的声明,这两个函数主要充当了回调函数,用于在每次执行完work之后自动地调用,两个函数的功能分别是设置陷波的中心频率和设置采样率。
注意:
要注意的是,每次更改完这个头文件后,要在module的文件夹下打开terminal,然后输入:

gr_modtool bind <your block name>

意思是要重新绑定你的block,这个很重要,如果重新绑定,在后面cmake的时候,就会出现out of sync的错误,这个错误主要是因为每次cmake的时候,会将该头文件的内容与camke中的缓存进行比较,如果有任何的不同,都会出现out of sync 的错误。

修改.yml

id: notchFilter_adaptiveNotch
label: adaptiveNotch
category: '[notchFilter]'

templates:
  imports: import notchFilter
  make: |-
    notchFilter.adaptiveNotch(${sampRate}, ${targetFreq})
  #  self.${id}.set_target_freq(${targetFreq})
  callbacks:
  - self.${id}.set_target_freq(${targetFreq})
  # -update_sample_rate(${sampRate})
  #-self.set_target_freq(${targetFreq})
#  Make one 'parameters' list entry for every parameter you want settable from the GUI.
#     Keys include:
#     * id (makes the value accessible as keyname, e.g. in the make entry)
#     * label (label shown in the GUI)
#     * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#     * default
parameters:
- id: sampRate
  label: sample rate
  dtype: real
  default: samp_rate

- id: targetFreq
  label: target frequency
  dtype: real
  default: 0
#- id: ...
#  label: ...
#  dtype: ...

#  Make one 'inputs' list entry per input and one 'outputs' list entry per output.
#  Keys include:
#      * label (an identifier for the GUI)
#      * domain (optional - stream or message. Default is stream)
#      * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#      * vlen (optional - data stream vector length. Default is 1)
#      * optional (optional - set to 1 for optional inputs. Default is 0)
inputs:
- label: in0
  domain: stream
  dtype: float
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...

outputs:
- label: out0
  domain: stream
  dtype: float
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...

#  'file_format' specifies the version of the GRC yml format used in the file
#  and should usually not be changed.
file_format: 1

注意在template字段的make与callback,make是该block被创建时调用的函数,callback是block的work执行完一次之后自动调用的回调函数,要注意这里的语法。 callback的写法官网并没有给出一个教程,此处参考的其他module的源码。

注意parameters中是在使用block时会出现的参数表:

parameters:
- id: sampRate
  label: sample rate
  dtype: real
  default: samp_rate

- id: targetFreq
  label: target frequency
  dtype: real
  default: 0

基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第6张图片
其中id必须与make中的参数名完全一致,label会出现在block的界面中,double类型的参数在这里要写为real,default就是默认值。

注意outputs和inputs字段,label是端口名,dtype是输入数据类型,必须与.cc文件中的一致(不过不知道double是否需要写为real):

inputs:
- label: in0
  domain: stream
  dtype: float


outputs:
- label: out0
  domain: stream
  dtype: float

如果有多个输入,则应变为:

inputs:
- label: in0
  domain: stream
  dtype: float
 - label: in1
  domain: stream
  dtype: float


outputs:
- label: out0
  domain: stream
  dtype: float

对于多输入的情况在.cc中使用时,所有的输入都会放入一个in中,in0输入的用in[0]来访问。
完成这一系列操作后,进入安装模块的过程。

安装模块和block

首先进入module所在的文件夹,创建一个build文件夹,在build文件夹下进入terminal,依次执行以下内容:

cmake ..
make
sudo make install
sudo ldconfig

全部成功后,打开gnuradio-companion,刷新模块:
在这里插入图片描述
(在搜索标志旁边的刷新标志)
接着就会在右侧的模块中出现我们的自定义模块:
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第7张图片
注意
如果没有出现,一定是yml文件没有写对。

测试

创建一个流图:
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第8张图片
将2Khz的信号视为有用信号,1KHz的信号看作是要滤除的干扰,下图中1KHz的信号被完全滤除了。
基于gnuradio的自适应陷波滤波器OOT模块(notch filter)_第9张图片

git 源码

mortarboard-H的notchfilter

后面的工作:

1、完善陷波滤波器,加入更多block
2、写一个OOT的导航卫星信号(GNSS)接收机

对后续工作感兴趣的朋友请联系我:[email protected]

你可能感兴趣的:(gnurado,模块编程,gnu,c++)