使用的gnuraido版本是3.9,在ubuntu20.04的操作系统下开发。
创建gnuradio的OOT的过程主要参考官方的OOT制作教程c++ OOT with gr-modtool
本文将按照官方教程,演示如何制作一个gnuradio的OOT,除了官方教程中的内容,还将涉及到很多官方教程中没有提到的注意点。
在建模前肯定要下载安装gnuradio的本体库。通过命令可以轻松安装:
sudo apt-get install gnuradio
然而要注意的是,不同的ubuntu版本能够安装的gnuradio版本是不同的,ubuntu18只能安装gnuradio3.8,ubuntu20.4可以安装gnuradio3.9,ubuntu对应的gnuradio版本
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
在任意一个目录下打开终端,在终端中输入:
gr_modtool newmod notchFilter
gr_modtool会在当前目录下新建一个目录,目录名为gr-notchFilter
gnuradio的模块的逻辑是一个模块下有多个block,比如在gnuradio-companion可视化界面中,math模块下有add,abs等block:
创建block时要先进入module的文件夹中,可以cd进去,也可以打开文件夹后,然后在文件夹中打开终端,这个文件夹中应该有这些(除了build):
然后在终端中输入:
gr_modtool add adaptiveNotch
意思是给模块增加一个adaptiveNotch的block,我这里因为已经有了adaptivenotch,所以就随便输入了lalala,后面的例子和代码仍以adaptiveNotch为例
这是我另外作为例子演示以下添加block的效果,gr_modtool提供了添加block的快速配置,首先选block类型,sync是同步的意思,换言之就是block的输入和输出数量是一样的,‘sink’只有输入,‘source’只有输出,‘decimator’,离散器,就是输出比输入少,‘interpolator’插值器,就是输出比输入多,其他的查一下gnuradio的官网吧。
接下来选语言:
python和c++都可以
然后输入版权持有者,我填的是我的github名称mortarboard-H。
这个会在代码的顶端自动生成一个注释,注明版权。
接下来就是输入block的参数列表,就像是函数的参数列表一样。
接下来好像是询问需不需要自动的测试代码,全部no就行
然后就会自动添加和修改这些文件:
那么用visual code打开对应的文件夹,
就会发现多了一些东西。
在这些文件中,为了实现我们需要的功能,主要需要修改的是impl.h,impl.cc,对应的yaml文件。
/* -*- 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是自己添加的。
这个文件存放了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 */
至于自适应陷波滤波的原理可以自行百度。
/* -*- 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 的错误。
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
其中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]来访问。
完成这一系列操作后,进入安装模块的过程。
首先进入module所在的文件夹,创建一个build文件夹,在build文件夹下进入terminal,依次执行以下内容:
cmake ..
make
sudo make install
sudo ldconfig
全部成功后,打开gnuradio-companion,刷新模块:
(在搜索标志旁边的刷新标志)
接着就会在右侧的模块中出现我们的自定义模块:
注意:
如果没有出现,一定是yml文件没有写对。
创建一个流图:
将2Khz的信号视为有用信号,1KHz的信号看作是要滤除的干扰,下图中1KHz的信号被完全滤除了。
mortarboard-H的notchfilter
1、完善陷波滤波器,加入更多block
2、写一个OOT的导航卫星信号(GNSS)接收机
对后续工作感兴趣的朋友请联系我:[email protected]