高性能数字滤波器与C++实用工具
高性能数字滤波器
17阶低通FIR滤波器
有一个17阶低通FIR滤波器,也被称为18抽头滤波器,因为它有18个滤波器系数。其系数总和为128,分辨率比例为64。该滤波器使用
std::uint32_t
来表示内部算法值,因为这些值并不总是能适应
std::uint16_t
。它可以过滤
std::uint16_t
整个范围内的输入值。
以下是该滤波器的定义和使用示例:
typedef fir_order_n<17U,
64U,
std::uint16_t,
std::uint32_t> filter_type;
filter_type f(4U);
void do_something()
{
f.new_sample<-2, -2, -2, -1, 3, 9, 15, 20, 24,
24, 20, 15, 9, 3, -1, -2, -2, -2>(100U);
}
这个17阶滤波器比之前的5阶滤波器需要更多的代码和运行时间。这不仅是因为它有更多的系数,还因为延迟线值是32位宽而不是16位。对于8位或16位目标,这个17阶滤波器明显过大,更适合32位目标。不过,可以使用两个或更多级联的低阶滤波器以更少的代码和运行时间成本获得相同的滤波质量。
滤波器设计示例
考虑一个未滤波的原始信号,其主要成分是频率为60Hz、振幅为150、偏移为250的正弦波,还添加了一个强异步噪声成分,噪声频率为600Hz,振幅为30,偏移为0.317ms。该信号的数学表示为:
$S = 250 + 150 \cdot \sin(0.12\pi t) + \frac{1}{5} \sin(0.317 + 1.2\pi t)$
我们将使用一个N阶低通FIR滤波器对该信号进行滤波,使600Hz的噪声成分得到强烈抑制,60Hz的主要成分尽可能少地衰减。
设计步骤
-
确定采样频率
:假设每个噪声半波应采集约3 - 4个样本,噪声频率为600Hz,若每个噪声半波采集3.5个样本,则采样频率$T_s$为:
$T_s = 3.5 \cdot (2 \cdot 600 Hz) = 4200 Hz \approx 4000 Hz$
相应的采样周期为250μs。 - 选择滤波器参数 :选择通带上限频率为200Hz,阻带下限频率为600Hz,阻带衰减为40dB,通带允许1dB的波纹。
- 生成滤波器系数 :将这些滤波器参数提供给滤波器设计工具,计算出18个双精度系数,对应于18抽头、17阶滤波器的缩放整数系数。
测试数据
使用一个单独的程序以4kHz的采样频率从上述信号方程生成101个数字化点作为测试数据:
#include <cstdint>
#include <array>
const std::array<std::uint16_t, 101U> data =
{
{
250U, 288U, 306U, 301U, 287U, 288U, 312U, 351U,
381U, 386U, 371U, 354U, 357U, 381U, 412U, 428U,
417U, 390U, 370U, 372U, 392U, 411U, 409U, 383U,
347U, 326U, 328U, 343U, 350U, 333U, 296U, 258U,
241U, 246U, 258U, 256U, 231U, 190U, 158U, 150U,
162U, 176U, 170U, 141U, 106U,
87U,
93U, 116U,
132U, 125U, 100U,
77U,
75U,
97U, 129U, 147U,
141U, 123U, 113U, 127U, 162U, 198U, 215U, 209U,
195U, 197U, 224U, 264U, 297U, 306U, 296U, 285U,
293U, 325U, 363U, 386U, 383U, 364U, 352U, 363U,
392U, 420U, 427U, 409U, 381U, 368U, 377U, 400U,
414U, 403U, 371U, 338U, 324U, 332U, 348U, 348U,
322U, 282U, 250U, 240U, 250U
}
};
滤波操作
使用17阶滤波器对测试数据进行滤波:
#include <iostream>
#include <math/filters/fir_order_n.h>
typedef fir_order_n<17U,
64U,
std::uint16_t,
std::uint32_t> filter_type;
void do_something()
{
filter_type f(data[0U]);
std::cout << f.get_result() << "\n";
std::for_each(
data.begin() + 1U,
data.end(),
[&f](const std::uint16_t& s)
{
f.new_sample
<-2, -2, -2, -1, 3, 9, 15, 20, 24,
24, 20, 15, 9, 3, -1, -2, -2, -2>(s);
std::cout << f.get_result() << "\n";
});
}
滤波结果显示,滤波器质量非常好,60Hz的主要成分基本无衰减通过,600Hz的噪声基本被消除。该滤波器在32位目标上运行快速,在8位目标上运行时间较长,CPU负载过高。
级联低阶滤波器
可以使用两个级联的16位5阶滤波器来替代17阶滤波器,以降低8位目标的CPU负载。5阶滤波器的系数为(5, 5, 6, 6, 5, 5)。
#include <iostream>
typedef fir_order_n<5U> filter_type;
void do_something()
{
filter_type f1(data[0U]);
filter_type f2(f1.get_result());
std::cout << f2.get_result() << std::endl;
std::for_each(
data.begin() + 1U,
data.end(),
[&f1, &f2](const std::uint16_t& s)
{
f1.new_sample<5, 5, 6, 6, 5, 5>(s);
filter_type::result_type r = f1.get_result();
f2.new_sample<5, 5, 6, 6, 5, 5>(r);
std::cout << f2.get_result() << std::endl;
});
}
级联滤波器的滤波质量与17阶滤波器相当,但CPU负载显著降低。
高通滤波器
使用高通滤波器对测试数据进行滤波,选择阻带上限频率为80Hz,衰减为40dB,通带下限频率为600Hz,通带波纹为1dB。得到一个10阶、11抽头的高通滤波器,系数为(1, 2, 4, 6, 8, -40, 8, 6, 4, 2, 1)。
滤波器测试结果对比
| 滤波器类型 | 滤波效果 | 运行时间(8位目标) | CPU负载(8位目标,4kHz采样率) |
|---|---|---|---|
| 17阶低通 | 噪声基本消除,60Hz成分无衰减 | 约56μs | 约22% |
| 级联5阶低通 | 滤波质量与17阶相当 | 约22μs | 约9% |
| 10阶高通 | 衰减60Hz成分,保留600Hz波纹 | - | - |
滤波器设计流程mermaid图
graph LR
A[确定信号特征] --> B[选择滤波器类型]
B --> C[确定采样频率]
C --> D[选择滤波器参数]
D --> E[生成滤波器系数]
E --> F[滤波测试]
F --> G{是否满足要求}
G -- 是 --> H[应用滤波器]
G -- 否 --> D
C++实用工具
nothing结构
nothing
结构不包含任何成员,也没有任何功能,但它可以作为其他函数和模板参数的占位符。
示例:固定点类
// A simplified Q7.8 fixed-point representation.
class fixed_point
{
public:
// Construct from integer with left-shift.
fixed_point(std::uint16_t u) : value(u << 8) { }
// Create pi with the special constructor.
static fixed_point value_pi()
{
return fixed_point(nothing(), 0x0324U);
}
private:
std::uint16_t value;
// Constructor from integer without left-shift.
fixed_point(const nothing&,
std::uint16_t u) : value(u) { }
};
nothing
结构用于区分从
std::uint16_t
进行左移的普通构造函数和不进行左移的私有构造函数。
示例:triple类
struct nothing {};
template <typename first_type
= nothing,
typename second_type = nothing,
typename third_type
= nothing>
class triple
{
public:
// Constructor with default values.
triple(const first_type&
t1_ = first_type(),
const second_type& t2_ = second_type(),
const third_type&
t3_ = third_type())
: t1(t1_),
t2(t2_),
t3(t3_)
{
}
// Element access.
first_type&
first()
{ return t1; }
second_type& second() { return t2; }
third_type&
third()
{ return t3; }
private:
first_type
t1;
second_type t2;
third_type
t3;
};
triple
类类似于标准库中的
std::pair
类,但有三个元素。
noncopyable类
noncopyable
类用于禁止类对象的有意和无意复制。
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
// Emphasize: The following members are private.
private:
noncopyable(const noncopyable&) = delete;
const noncopyable& operator=(const noncopyable&)
= delete;
};
示例:LED类
class led : private noncopyable
{
// ...
};
通过私有继承
noncopyable
类,
led
类变得不可复制,简化了代码编写和维护。
模板timer类
模板
timer
类可用于实时C++中的各种定时应用。
template<typename unsigned_tick>
class timer
{
public:
// A class-specific tick type.
typedef unsigned_tick tick_type;
// Utility functions for creating timespans.
template<typename other>
static tick_type microseconds(const other&);
template<typename other>
static tick_type milliseconds(const other&);
template<typename other>
static tick_type seconds(const other&);
template<typename other>
static tick_type minutes(const other&);
template<typename other>
static tick_type hours(const other&);
// Constructors.
timer();
explicit timer(const tick_type&);
timer(const timer&);
// Copy assignment operator.
timer& operator=(const timer&);
// Interval and relative timeout functions.
void start_interval(const tick_type&);
void start_relative(const tick_type&);
// The timeout, now, and delay functions.
bool timeout() const;
static tick_type now();
static void blocking_delay(const tick_type&);
};
该类提供了以下操作:
- 使用
now()
查询当前时间点。
- 使用
start_relative()
设置相对超时。
- 使用
start_interval()
设置间隔超时。
- 使用
blocking_delay()
进行阻塞延迟。
timer类使用流程mermaid图
graph LR
A[创建timer对象] --> B[设置时间间隔或相对时间]
B --> C{是否超时}
C -- 否 --> B
C -- 是 --> D[执行操作]
D --> E{是否继续计时}
E -- 是 --> B
E -- 否 --> F[结束]
滤波器设计与应用总结
在滤波器设计与应用中,我们需要根据具体的应用场景和硬件平台来选择合适的滤波器。对于不同的信号特征和滤波要求,我们可以通过以下步骤进行滤波器的设计和应用:
1.
分析信号特征
:确定信号的主要成分和噪声成分,包括频率、振幅和偏移等参数。
2.
选择滤波器类型
:根据滤波要求,选择低通、高通或其他类型的滤波器。
3.
确定采样频率
:根据信号的频率特征,确定合适的采样频率,以保证信号的有效采集。
4.
选择滤波器参数
:如通带频率、阻带频率、衰减和波纹等参数,以满足滤波要求。
5.
生成滤波器系数
:使用滤波器设计工具,根据选择的参数生成滤波器系数。
6.
滤波测试
:使用生成的滤波器系数对测试数据进行滤波,评估滤波效果。
7.
优化调整
:如果滤波效果不满足要求,返回步骤4进行参数调整,直到满足要求为止。
不同滤波器的应用场景
| 滤波器类型 | 应用场景 |
|---|---|
| 17阶低通 | 对32位目标系统,需要高精度滤波,且对CPU负载要求不高的场景 |
| 级联5阶低通 | 8位或16位目标系统,对CPU负载敏感,同时需要较好滤波效果的场景 |
| 10阶高通 | 需要去除低频信号,保留高频信号的场景,如信号的高频特征提取 |
C++实用工具的综合应用
在实际的C++编程中,
nothing
结构、
noncopyable
类和模板
timer
类可以结合使用,以提高代码的可读性、可维护性和性能。
示例:结合使用的代码
#include <iostream>
#include <array>
// nothing结构
struct nothing {};
// noncopyable类
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private:
noncopyable(const noncopyable&) = delete;
const noncopyable& operator=(const noncopyable&) = delete;
};
// 简化的固定点类
class fixed_point
{
public:
fixed_point(std::uint16_t u) : value(u << 8) { }
static fixed_point value_pi()
{
return fixed_point(nothing(), 0x0324U);
}
private:
std::uint16_t value;
fixed_point(const nothing&, std::uint16_t u) : value(u) { }
};
// triple类
template <typename first_type = nothing, typename second_type = nothing, typename third_type = nothing>
class triple
{
public:
triple(const first_type& t1_ = first_type(), const second_type& t2_ = second_type(), const third_type& t3_ = third_type())
: t1(t1_), t2(t2_), t3(t3_)
{
}
first_type& first() { return t1; }
second_type& second() { return t2; }
third_type& third() { return t3; }
private:
first_type t1;
second_type t2;
third_type t3;
};
// 模板timer类
template<typename unsigned_tick>
class timer
{
public:
typedef unsigned_tick tick_type;
template<typename other>
static tick_type microseconds(const other&);
template<typename other>
static tick_type milliseconds(const other&);
template<typename other>
static tick_type seconds(const other&);
template<typename other>
static tick_type minutes(const other&);
template<typename other>
static tick_type hours(const other&);
timer();
explicit timer(const tick_type&);
timer(const timer&);
timer& operator=(const timer&);
void start_interval(const tick_type&);
void start_relative(const tick_type&);
bool timeout() const;
static tick_type now();
static void blocking_delay(const tick_type&);
};
// 不可复制的LED类
class led : private noncopyable
{
public:
led() {}
void toggle() const
{
std::cout << "LED toggled." << std::endl;
}
};
int main()
{
// 使用triple类
triple<char, int, led> things('a', 123, led());
// 使用timer类
timer<std::uint32_t> t;
t.start_interval(t.milliseconds(1000));
while (!t.timeout())
{
// 等待超时
}
things.third().toggle();
return 0;
}
代码解释
-
nothing结构 :作为固定点类构造函数的区分标志,避免构造函数的歧义。 -
noncopyable类 :使led类不可复制,防止不必要的对象复制。 -
triple类 :类似于std::pair,但包含三个元素,方便数据的组织。 -
模板
timer类 :用于定时操作,设置时间间隔并等待超时。
综合应用流程mermaid图
graph LR
A[定义实用工具类] --> B[创建对象]
B --> C[使用对象功能]
C --> D{是否完成任务}
D -- 否 --> C
D -- 是 --> E[结束]
总结
通过对高性能数字滤波器和C++实用工具的介绍,我们了解了不同类型滤波器的设计、应用和性能特点,以及如何使用
nothing
结构、
noncopyable
类和模板
timer
类来提高C++代码的质量和效率。在实际应用中,我们可以根据具体需求选择合适的滤波器和实用工具,以实现高效、可靠的系统设计。
注意事项
- 在滤波器设计中,要根据硬件平台的特点选择合适的滤波器阶数和类型,避免资源浪费和性能瓶颈。
- 在使用C++实用工具时,要注意类的继承和成员函数的使用,确保代码的正确性和可维护性。
超级会员免费看
1946

被折叠的 条评论
为什么被折叠?



