Mise en oeuvre d'un filtre IIR

Mise en oeuvre d'un filtre IIR

Théorie des Filtres IIR : Filtrage Numérique IIR


L’Objectif est de réaliser le filtre d’ordre 4 ( 2 Stages ) évoqué dans Filtrage Numérique IIR

Après avoir déterminé les coefficients du filtre IIR, l’algorithme à appliquer au sein du processeur est du type :

y[n]=i=0Nb[i].x[ni]i=1Na[i].y[ni] y[n]=\sum_{i=0}^{N}b[i].x[n-i]-\sum_{i=1}^{N}a[i].y[n-i]

Pour un Filtre d’ordre 2 :

y[n]=b0.x[n]+b1.x[n1]+b2.x[n2]a1.y[n1]a2.y[n2] y[n]=b_{0}.x[n]+b_{1}.x[n-1]+b_{2}.x[n-2]-a_{1}.y[n-1]-a_{2}.y[n-2]

iir_algo_direct_form_1.svg

iir_algo_direct_form_2.svg

Mise en Oeuvre

arm_math.h
typedef struct
  {
    uint16_t numStages;
    float32_t *pState;
    float32_t *coef;
  } arm_iir_instance_f32;
IIR_filter.c
int IIR_filt_f32(arm_iir_instance_f32 *S, float* x, float* y)
{
    unsigned int i;
    float *wn_1_ptr,*wn_2_ptr,*coef_ptr;
    float output=0.0;
    float wn,wn_1,wn_2;

    coef_ptr = S->coef;                /* coefficient pointer */

    wn_1_ptr = S->pState;            /* first history */
    wn_2_ptr = wn_1_ptr + 1;           /* next history */

    output = *x * (*coef_ptr++);

    for (i = 0 ; i < S->numStages; i++)
        {
        wn_1 = *wn_1_ptr;           /* history values */
        wn_2 = *wn_2_ptr;

        output = output - wn_1 * (*coef_ptr++);
        wn = output - wn_2 * (*coef_ptr++);    /* poles */

        output = wn + wn_1 * (*coef_ptr++);
        output = output + wn_2 * (*coef_ptr++);      /* zeros */

        *wn_2_ptr++ = *wn_1_ptr;
        *wn_1_ptr++ = wn;
        wn_1_ptr++;
        wn_2_ptr++;
    }
    *y = output;
    return 0;
}

Pour un filtre IIR d’ordre 4 ( numStage = 2 ), on rangera les coefficients dans un tableau de la manière suivante :

H(z)=k.1+S0α1.z1+S0α2.z21+S0β1.z1+S0β2.z2.1+S1α1.z1+S1α2.z21+S1β1.z1+S1β2.z2 H(z)=k.\frac{1 + S_0\alpha_1 . z^{-1} + S_0\alpha_2 . z^{-2}}{1 + S_0\beta_1 . z^{-1} + S_0\beta_2 . z^{-2}}.\frac{1 + S_1\alpha_1 . z^{-1} + S_1\alpha_2 . z^{-2}}{1 + S_1\beta_1 . z^{-1} + S_1\beta_2 . z^{-2}}

H(z)=k.z2+S0α1.z+S0α2z2+S0β1.z+S0β2.z2+S1α1.z+S1α2z2+S1β1.z+S1β2 H(z)=k.\frac{z^2 + S_0\alpha_1 . z + S_0\alpha_2 }{z^2 + S_0\beta_1 . z + S_0\beta_2 }.\frac{z^2 + S_1\alpha_1 . z + S_1\alpha_2 }{z^2 + S_1\beta_1 . z + S_1\beta_2 }

tab_coeff[0] : k
tab_coeff[1] : S0_beta1
tab_coeff[2] : S0_beta2
tab_coeff[3] : S0_alpha1
tab_coeff[4] : S0_alpha2
tab_coeff[5] : S1_beta1
tab_coeff[6] : S1_beta2
tab_coeff[7] : S1_alpha1
tab_coeff[8] : S1_alpha2

L’initialisation du filtre IIR se fait avec :
IIR_init_f32( &ARM_IIR_F32, 2, tab_coeff, tab_history, 1);

  • ARM_IIR_F32 : structure de type arm_iir_instance_f32
  • 2 : nombre de Stages
  • tab_coeff : tableau de 9 éléments contenant les coefficients du filtre
  • tab_history : tableau contenant les échantillons précédents pour le calcul du filtre ( de taille 4 )

Le calcul de l’échantillon de sortie du filtre pour chaque nouvel échantillon d’entrée se fait avec la fonction IIR_filt_f32(arm_iir_instance_f32 *S, float* x, float* y)

x_sample_f et y_sample_f sont de type float32_t, ainsi on aura :

main.c
void BSP_AUDIO_SAI_Interrupt_CallBack()
{
 float32_t x_sample_f;
 float32_t y_sample_f;

  BSP_LED_On(LED1);

  x_sample_f=(float32_t)rx_sample_L;

  // SIGNAL PROCESSING ALGORITHM
  IIR_filt_f32(&ARM_IIR_F32, &x_sample_f, &y_sample_f);

  tx_sample_L = (int16_t)y_sample_f ;
  tx_sample_R = tx_sample_L;

  BSP_LED_Off(LED1);

  return;
}

Q1. Tester la fonction IIR_filt_f32() avec des coefficients de filtres calculés par le script python synthese_iir_ordre4.py

REMARQUE : la fonction signal.iirfilter permet de calculer les coefficients de tout type de filtre IIR. ( cf gene_coeffs_IIR.py )

Q2. Compléter et tester la fonction IIR_calc_coeff_f32() afin de calculer les coefficients du filtre IIR d’ordre 4 pour une fréquence de coupure souhaitée.

change_coeff_iir.svg

Q3. Réaliser un synthétiseur à synthèse soustractive à base de filtre IIR. Les paramètres k et Q doivent pouvoir être réglables avec un potentiomètre du clavier maître.