QT版本5.9.6,详细代码和完整工程文件见资料,下载即可运行,适合QT新手以及学习IIR、FIR滤波、FFT的C语言算法实现,受益绝对匪浅。
(1)在上位机,使用QT软件编写完成两种或三种频率信号的合成,在图形界面上显示合成的时域波形;
(2)使用FFT计算合成信号的频率,在界面上绘制频谱图;
(3)使用matlab的Filter Designer设计FIR和IIR滤波器,分别完成一种频率成分的去除,并且在界面上显示滤除后的信号时域波形。
1、三种频率信号的合成,正弦信号a、正弦信号b、正弦信号c,并在UI界面上显示合成信号的时域波形:
通过在界面输入三个信号的频率,然后在程序里读取三个频率,转换为角度,生成三个正弦信号,绘制三个波形。下面这段程序实现了从输入框读取频率,生成三个正弦信号,并在图上绘制三个正弦波合成的信号的功能。
//三种频率的正弦波信号合成
void Widget::on_com_clicked()
{
QLayoutItem *child;
while ((child = ui->layout->takeAt(0)) != 0)
{
if(child->widget())
{
child->widget()->setParent(NULL);
}
delete child;
}
Widget::Chart_Init();
series->clear();
chart->removeSeries(series);
series->setName("合成");
//读取文本框中的频率
QString fa = ui->fa->toPlainText();
double fa1=fa.toDouble();
QString fb = ui->fb->toPlainText();
double fb1=fb.toDouble();
QString fc = ui->fc->toPlainText();
double fc1=fc.toDouble();
for(double i=0.0;i<10.0;i+=0.0001){
double angle1 = i * fa1;
double angle2 = i * fb1;
double angle3 = i * fc1;
series->append(i,(sin(2*3.14159265*angle1)+sin(2*3.14159265*angle2)+sin(2*3.14159265*angle3)));
}
chart->addSeries(series);//把折线添加到图表
series->attachAxis(axisX);
series->attachAxis(axisY);
chart->axisY()->setRange(-3, 3);
}
2、使用FFT计算合成信号的频率,在界面绘制频谱图。
1)下面这段是主程序,调用了FFT.h头文件,头文件中又调用了FFT.c函数进行计算FFT,然后对得到的结果画幅频曲线:
//对合成信号做FFT,绘制幅频曲线
void Widget::on_FFT_clicked()
{
QLayoutItem *child;
while ((child = ui->layout->takeAt(0)) != 0)
{
if(child->widget())
{
child->widget()->setParent(NULL);
}
delete child;
}
Widget::Chart_Init();
series->clear();
series->setName("IIR滤波");
//获得文本输入框中a波形的频率
QString fa = ui->fa->toPlainText();
double freq_a=fa.toDouble();
double freq_a_radians=freq_a*2*3.1415926;
//获得文本输入框中b波形的频率
QString fb = ui->fb->toPlainText();
double freq_b=fb.toDouble();
double freq_b_radians=freq_b*2*3.1415926;
QString fc = ui->fc->toPlainText();
double freq_c=fc.toDouble();
double freq_c_radians=freq_c*2*3.1415926;
double max_freq=freq_a;
if(max_freq<=freq_b)
max_freq=freq_b;
else if(max_freq<=freq_c)
max_freq=freq_c;
//获得FFT数据,用于显示
double real_freq[1024];
double amp[1024];
get_FFT_data(real_freq,amp,freq_a_radians,freq_b_radians,freq_c_radians);
//显示FFT变换后计算得到的频谱图
for(int i=0;i<max_freq+50;i++)
{
series->append(real_freq[i],amp[i]);
}
chart->addSeries(series);//把折线添加到图表
series->setName("FFT");
chart->createDefaultAxes();
chart->axisY()->setRange(-1, 1);
}
3、FIR滤波
采用了Matlab的Filter Designer设计FIR滤波器,通带频率为100Hz,截止频率为200Hz,通带波纹0.5dB,阻带波纹80dB,信号采样率为800Sa/s,按照奈奎斯特采样定律,所以理论上来说能够处理的信号在400Hz以内,设计出的FIR滤波器阶数为21阶。
FIR滤波程序:
//FIR滤波
void Widget::on_FIRfilter_clicked()
{
QLayoutItem *child;
while ((child = ui->layout->takeAt(0)) != 0)
{
if(child->widget())
{
child->widget()->setParent(NULL);
}
delete child;
}
Widget::Chart_Init();
series->clear();
series->setName("FIR滤波"); //添加波形名字
chart->removeSeries(series);
double data_in[10000];
double data_out[10000];
QString fa = ui->fa->toPlainText();
double fa1=fa.toDouble();
QString fb = ui->fb->toPlainText();
double fb1=fb.toDouble();
QString fc = ui->fc->toPlainText();
double fc1=fc.toDouble();
for(double i=0.0;i<10.0;i+=0.001){
double angle1 = 2*3.14159*i * fa1;
double angle2 = 2*3.14159*i * fb1;
double angle3 = 2*3.14159*i * fc1;
int j = 1000 * i;
data_in[j]=sin(angle1)+sin(angle2)+sin(angle3);
}
int i=0,j=0,k=0;
double state[FIR_FILTER_LENGTH];
double temp=0;
double FIR_COFFES[FIR_FILTER_LENGTH]= {
0.000828859699301925110029309884396298003, 0.005491897013585840710281349430488262442,0.014807416606805706704719227673194836825, 0.021389125806650765432292971013339411002,
0.010554414190420358110600318468641489744, -0.022661508756789315588431321657481021248,
-0.053911115172153287189438231052918126807, -0.0357090761579505269751599882965820143,
0.061219089267199357229376488476191298105, 0.205966149469584192122084687071037478745,
0.31493515071552402595500552706653252244, 0.31493515071552402595500552706653252244 ,
0.205966149469584192122084687071037478745, 0.061219089267199357229376488476191298105,
-0.0357090761579505269751599882965820143, -0.053911115172153287189438231052918126807,
-0.022661508756789315588431321657481021248, 0.010554414190420358110600318468641489744,
0.021389125806650765432292971013339411002, 0.014807416606805706704719227673194836825,
0.005491897013585840710281349430488262442, 0.000828859699301925110029309884396298003};
for (k = 0; k < 10000; k++){
state[0] = data_in[k];
for (i = 0, temp = 0; i < FIR_FILTER_LENGTH-1; i++)
temp += FIR_COFFES[i] * state[i];
data_out[k] = temp;
for (j = FIR_FILTER_LENGTH - 2; j > -1 ; j--)
state[j+1] = state[j];
}
for(double i=0.0;i<10.0;i+=0.001){
series->append(i,data_out[int (1000 * i)]);
}
chart->addSeries(series);//把折线添加到图表
series->attachAxis(axisX);
series->attachAxis(axisY);
chart->axisY()->setRange(-1.5, 1.5);
}
在界面分别设置3组三个信号的频率,验证FIR滤波的效果:
组2:用50Hz、220Hz和280Hz三个频率的信号波形进行滤波验证:
组3:用20Hz、220Hz和340Hz三个频率的信号波形进行滤波验证:
4、IIR滤波
采用了Matlab的Filter Designer工具箱设计IIR滤波器,为了对比和FIR滤波器的滤波效果,大多数参数和FIR滤波器阶数保持一致,所以也设置通带频率为100Hz,截止频率为200Hz,通带波纹0.5dB,阻带波纹80dB,信号采样率为800Sa/s,按照奈奎斯特采样定律,所以理论上来说也能够处理的信号在400Hz以内,设计出的滤波器阶数为12阶。
下面是IIR滤波的代码部分,调用了IIR参数设置和IIR滤波两个函数,2型的IIR滤波器函数并没有列出,详细的可以看程序。
//点击IIR滤波按钮,进行IIR滤波
void Widget::on_IIRfilter_clicked()
{
QLayoutItem *child;
while ((child = ui->layout->takeAt(0)) != 0)
{
if(child->widget())
{
child->widget()->setParent(NULL);
}
delete child;
}
Widget::Chart_Init();
series->clear();
series->setName("IIR滤波"); //添加波形名字
int N=13;
double b[N]={
3.09525176187779e-06, 3.71430211425335e-05, 0.000204286616283934,
0.000680955387613114, 0.00153214962212951, 0.00245143939540721,
0.00286001262797508, 0.00245143939540721, 0.00153214962212951,
0.000680955387613114, 0.000204286616283934, 3.71430211425335e-05, 3.09525176187779e-06};
double a[N]={
1, -5.34962036107987, 14.1411241339418, -23.9072313155838,
28.4874436778036, -25.0265817923911, 16.5375193873507,
8.25031849575782, 3.07476132941233, -0.832796574455573,
0.155288255056261, -0.0178681514268133, 0.000958058346940756};
IIR_II filter;
filter.setPara(b, N-1, a, N-1);//设置IIR滤波器参数,b是分子,a是分母,分母,
//N-1是滤波器阶数,由于从0开始,所以是N-1,而不是N
double data_in[10000];
double data_out[10000];
//读取三个输入框中的频率数字
QString fa = ui->fa->toPlainText();
double fa1=fa.toDouble();
QString fb = ui->fb->toPlainText();
double fb1=fb.toDouble();
QString fc = ui->fc->toPlainText();
double fc1=fc.toDouble();
//将频率转换为角度
for(double i=0.0;i<10.0;i+=0.001){
double angle1 = 2*3.14159*i * fa1;
double angle2 = 2*3.14159*i * fb1;
double angle3 = 2*3.14159*i * fc1;
int j = 1000 * i;
data_in[j]=sin(angle1)+sin(angle2)+sin(angle3);
}
//调用IIR滤波器进行滤波
//data_in是待滤波信号,data_out是滤波后的信号,10000是信号长度
filter.filter(data_in,data_out,10000);
//在图上画滤波后的波形
for(double i=0.0;i<10.0;i+=0.001)
{
series->append(i,data_out[int (1000 * i)]);
}
chart->addSeries(series);//把折线添加到图表
series->attachAxis(axisX);
series->attachAxis(axisY);
chart->axisY()->setRange(-1.5, 1.5);
}
为了验证设计的IIR滤波器的滤波性能,我们采用了三组信号,这个和FIR滤波部分仍然保持一致。
组1:用100Hz、250Hz和380Hz三个频率的信号波形进行滤波验证:
组2:用50Hz、220Hz和280Hz三个频率的信号波形进行滤波验证:
组3:用20Hz、220Hz和340Hz三个频率的信号波形进行滤波验证:
IIR和FIR滤波的对比:
由于在本次实验设计滤波器指标阶段时,我刻意将滤波器的滤波指标保持一致,设计出的FIR滤波器阶数为21阶,IIR滤波器阶数为12阶,可见当计算资源非常宝贵时,IIR滤波器非常有用,即以最小的滤波器滤波器阶数实现滤波,另外对比IIR和FIR滤波器的滤波器系数的位数可以发现,IIR的位数比FIR的位数少很多,几乎少一半,这对于处理器如FPGA来说是一大好事,因为处理器的浮点数的位数是固定的,本身容易造成有限字长效应,不得不截断滤波器系数的长度,导致误差积累。单纯的就滤波效果来说,两种滤波器都能有效滤波,滤波效果相差不大,但是需要注意的是设计IIR滤波器一定要保持其稳定性,及分母的极点在单位圆以内。