IIR数字滤波器实现(mantlab+C语言)

题目:16k采样率音频数据下采样到8k采样率

求解方案分析:直接每隔一个取一个采样值,这样就可以得到8k采样率的数据。但是这样明显会有问题。按照采样率变换理论,首先应该通过一个低通滤波器,滤掉[pi/2, pi]这个区间上的频率,以防止下采样造成的频率混叠。这个低通滤波器在很多书上都用FIR滤波去实现,并且可以用FIR滤波的多相结构去实现。这样滤波和下采样过程可以互换位置。即先下采样再进行多相FIR滤波。

在嵌入式设备上FIR滤波会占用较长的时间,为此,我们可以采用IIR滤波器来做。滤波器的设计采用MATLAB的FDATool工具。相关的参数如下:

 

 

采用最小P阶范数设计IIR滤波器的幅频特性较好。滤波器阶数设定为6阶,需要滤除4k到8k的频率段,才能保证无混叠失真。实际由于滤波器的特性没法做到理想的状态,选择滤波器截止频率为3800hz,3600到3800为过渡带宽。其它选项采用默认设置。设计的滤波器幅度响应如下图:

 

生成的滤波器系数文件如下:

/*

 * Filter Coefficients (C Source) generated by the Filter Design and Analysis Tool

 *

 * Generated by MATLAB(R) 7.6 and the Signal Processing Toolbox 6.9.

 *

 * Generated on: 03-Dec-2010 10:41:03

 *

 */

 

/*

 * Discrete-Time IIR Filter (real)

 * -------------------------------

 * Filter Structure    : Direct-Form II, Second-Order Sections

 * Number of Sections  : 3

 * Stable              : Yes

 * Linear Phase        : No

 */

 

/* General type conversion for MATLAB generated C-code  */

#include "tmwtypes.h"

/*

 * Expected path to tmwtypes.h

 * D:/MATLAB/R2008a/extern/include/tmwtypes.h

 */

#define MWSPT_NSEC 7

const int NL[MWSPT_NSEC] = { 1,3,1,3,1,3,1 };

const real64_T NUM[MWSPT_NSEC][3] = {

  {

    0.09065504059673,                 0,                 0

  },

  {

                   1,  -0.1323122149853,   0.9999674089086

  },

  {

                   1,                 0,                 0

  },

  {

                   1,   0.1670351201308,   0.9999889247428

  },

  {

                   1,                 0,                 0

  },

  {

                   1,    1.417032671609,   0.9978019623105

  },

  {

                   1,                 0,                 0

  }

};

const int DL[MWSPT_NSEC] = { 1,3,1,3,1,3,1 };

const real64_T DEN[MWSPT_NSEC][3] = {

  {

                   1,                 0,                 0

  },

  {

                   1,    -0.40321085647,   0.7334254033056

  },

  {

                   1,                 0,                 0

  },

  {

                   1,  -0.6868636040216,   0.2670185171768

  },

  {

                   1,                 0,                 0

  },

  {

                   1,  -0.2880720042256,   0.9480010462991

  },

  {

                   1,                 0,                 0

  }

};

 

上述系数是3个2阶节IIR结构的级联。可以转换为我们熟悉的b/a的形式如下:

 

double a[3][3] = {

     {1,  -0.6868636040216,   0.2670185171768},

     {1,    -0.40321085647,   0.7334254033056},

     {1,  -0.2880720042256,   0.9480010462991}

 

};

 

double b[3][3] = {

     {1,   1.417032671609,   0.9978019623105},

     {1,   0.1670351201308,   0.9999889247428},

     {1,  -0.1323122149853,   0.9999674089086},

};

 

注意上面系数文件中还有一个增益:

double gain =  0.09065504059673;

这个增益最好在第一级实现以后加入运算。这样可以减小误差,保证数据 动态范围不被溢出。尤其是在定点计算的时候尤为如此。

 

 

2阶节IIR滤波的直接实现

一个2阶节结构是下面这样一个表达式:

 

 

实现上面这个表达式需要4个过去的历史值,把它定义在结构体

typedef struct tag_ IIR_State_2order

{

     float y2;

     float y1;

     float x1;

     float x0;

} IIR_State_2order;

 

调用下面函数之前需要把上述结构体所有值初始化为零。滤波按一帧一帧数据进行。

#define  ONE_FRAME_SAMPLE_SIZE   1024

void cy_signal_filter_by_iir(signed short* pcmIn, IIR_State_2order* filter_state, float a[], float b[], signed short*pcmOut)

{

   int i;

   float x2;

   float tmp;

   for ( i = 0; i < ONE_FRAME_SAMPLE_SIZE; i++ )

   {

      x2 = filter_state->x1;

      filter_state->x1 = filter_state->x0;

      filter_state->x0 = pcmIn[i];

      tmp = ( float )(b[0]* filter_state->x0 + b[1]* filter_state->x1 +

b[2] * x2 - a[1] * filter_state->y1 - a[2] * filter_state->y2);

       if(tmp >= 32767)

       {

           tmp = 32767;

       }

       if(tmp <= -32768)

       {

          tmp = -32768;

       }

      pcmOut[i] = (signed short)tmp;

      filter_state->y2 = filter_state->y1;

      filter_state->y1 = tmp;

   }

}

有一个简单的技巧可以把上面的计算简化,使得历史状态数由4减少为2。定义下面的表达式:

 

 

 

 

 

结构体定义如下:

typedef struct tag_ IIR_State_2order

{

     float st1;

     float st2;

IIR_State_2order;

 

void cy_signal_filter_by_iir(signed short* pcmIn, IIR_State_2order* filter_state, float a[], float b[], signed short*pcmOut)

 

{

   int i;

   float st;

   float Tmp_fl;

   for ( i = 0; i < ONE_FRAME_SAMPLE_SIZE; i++ )

   {

        st = (float)(pcmIn[i] -  a[1] * filter_state->st1  - a[2] * filter_state-> st2);

        Tmp_fl = b[0] *  st + b[1] * filter_state->st1 + b[2] * filter_state->st2;

        filter_state->st2= filter_state->st1;

        filter_state->st1 = st;

        if(Tmp_fl >= 32767.0)

        {

            Tmp_fl = 32767;

        }

        if(Tmp_fl <= -32768)

        {

           Tmp_fl = -32768;

        }

 

         pcmOut[i] = (signed short)Tmp_fl;

   }

}

 

6阶节IIR滤波的实现

有个上面的基础,我们来实现上面设计的6阶IIR滤波器。

6阶节分解为3个2阶节级联实现。每个2阶节需要2个历史状态,总共需要6个历史状态。结构体定义如下:

typedef struct tag_IIR_State_3Order

{

     double w01;

     double w02;

     double w11;

     double w12;

     double w21;

     double w22;

 

}IIR_State_6order;

 

代码中数组a,b,还有gain的定义见第一部分。

void cy_signal_filter_by_6th_iir(signed short* pcmIn, IIR_State_6order* filter_state, int sample_size)

{

     double x1, x2, x3, Tmp_f00, Tmp_f10, Tmp_f20;

     int i;

     double Tmp_pcm;

     for (i = 0; i < sample_size; i++)

     {

         Tmp_pcm = pcmIn[i];

         Tmp_f00 = Tmp_pcm - a[0][1] * filter_state->w01 - a[0][2] * filter_state->w02;

         x1 = Tmp_f00 + b[0][1] * filter_state->w01 + b[0][2] * filter_state->w02;

         filter_state->w02 = filter_state->w01;

         filter_state->w01 = Tmp_f00;

 

         x1 = gain * x1;

 

         Tmp_f10 = x1 - a[1][1] * filter_state->w11 - a[1][2] * filter_state->w12;

         x2 = Tmp_f10 + b[1][1] * filter_state->w11 + b[1][2] * filter_state->w12;

         filter_state->w12 = filter_state->w11;

         filter_state->w11 = Tmp_f10;

 

 

         Tmp_f20 = x2 - a[2][1] * filter_state->w21 - a[2][2] * filter_state->w22;

         x3 = Tmp_f20 * b[2][0] + b[2][1] * filter_state->w21 + b[2][2] * filter_state->w22;

 

         filter_state->w22 = filter_state->w21;

         filter_state->w21 = Tmp_f20;

 

         if (x3 >= 32767)

         {

              x3 = 32767;

         }

         if (x3 <= -32768)

         {

              x3 = -32768;

         }

         pcmIn[i] = (signed short)x3;

 

     }

}

 最后看下滤波的效果:

 

 

滤波之后的频谱:

滤波效果不错,下面可以进行下采样了。

你可能感兴趣的:(matlab,数字信号处理)