Forums

Adaptive Notch filtering using FxLMS

Started by lightbearer November 5, 2015
I've been coding up a working MATLAB simulation on a Blackfin 533 using
the 32 bit fractional data type (fract32). I have used FIR filters to
implement convolution to get outputs from different filter stages.  I'm
using look up tables created from MATLAB to simulate the algorithm
internally on the processor. The LUTs consist of secondary path filtered
white noise and a 30Hz sine wave also filtered through the same impulse
response as the white noise. I run the system identification mode for 5
seconds and switch to active mode filtering. 
During development, I've been using only a fraction of the look up
tables(which doesn't contain the full period of the sine wave) which fills
a buffer of 512 bytes. This was the case for both the filtered 30Hz input
signal as well as the reference sine/cosine waves. This works as I can
verify this by plotting the signals on every iteration. 
However, when i start to input the signals in their full periods, my
adaptive coefficients (fract32) starts to saturate  and the filter starts
to misbehave. I've been reading up on scaling at different filter nodes
but to no avail. 
Any help on this will be greatly appreciated. 

Here's some pseudo code as to what's going on in active mode. 

#define ANC_SAMPLE_FREQ_HZ  48000
#define ANC_SAMPLE_SIZE 512
#define ANC_FILTER_ORDER 128. 
#define ANC_MU 0.00021

#define TEST_FREQ 30.0
 
/* temp buffers  */
static fract32 references[2][ANC_SAMPLE_SIZE];
static fract32 weightedreference[2][ANC_SAMPLE_SIZE];
static fract32 filteredreference[2][ANC_SAMPLE_SIZE];
static fract32 filteredOut[ANC_SAMPLE_SIZE];
static fract32 desiredResponse[ANC_SAMPLE_SIZE];

// output buffer
static fract32 _output[ANC_SAMPLE_SIZE];


/* FIR filters that are initialized at startup before DMA fires */
 
static fir_state_fr32 weightFilter[2];        // Adaptive filters 
static fir_state_fr32 secondaryFilter[2]; // Secondary path filtering for
reference waves 
static fir_state_fr32 secondarypath;      // Secondary path filtering for
the output  


 for(n = 0; n < ANC_SAMPLE_SIZE; n++)
 {
         references[0][n] = generate_SINE(TEST_FREQ);    // Sin
         references[1][n]  = generate_COSINE(TEST_FREQ ;  // Cos 
  }

     // Weighted Sine reference 
    fir_fr32(references[0], weightedreference[0], 
                  ANC_SAMPLE_SIZE, 
                  &weightFilter[0]);
	 					  			  
    // Weighted Cosine reference 
    fir_fr32(references[1], weightedreference[1], 
                  ANC_SAMPLE_SIZE, 
                  &weightFilter[1]);
	 				  
	 	
    // Secondary path filtered sine reference	
    fir_fr32(references[0], filteredreference[0], 
                  ANC_SAMPLE_SIZE, 
                  &secondaryFilter[0]);	
 			 		  
             
    // Secondary path filtered cosine reference			 		  
    fir_fr32(references[1], filteredreference[1], 
                  ANC_SAMPLE_SIZE, 
                  &secondaryFilter[0]);

    // Compute output
   for(n = 0; n < ANC_SAMPLE_SIZE; n++)
  {
   sData->outputvector[n] = add_fr1x32(weightedreference[0][n],        
weightedreference[1][n]);
  }
    

    // Secondary path filtered controller output 
     fir_fr32(_output, filteredOut, ANC_SAMPLE_SIZE, 
           &secondarypath);



   // Compute noise estimate 
   for(n = 0; n < ANC_SAMPLE_SIZE; n++)
  {   
     debug = generate_debugInput(); // get filtered 30Hz input from LUT
     desiredResponse[n] = sub_fr1x32(debug, filteredOut[n]);
  }	


  // Finally, Adapt coefficiants




---------------------------------------
Posted through http://www.DSPRelated.com
lightbearer <110036@DSPRelated> wrote:

>However, when i start to input the signals in their full periods, my >adaptive coefficients (fract32) starts to saturate and the filter starts >to misbehave.
[snip]
>Here's some pseudo code as to what's going on in active mode.
>[snip]
> // Finally, Adapt coefficiants
You did not give any pseudo-code for your "adapt coefficients" block, even though this is likely where your problem is. I have not implemented an adaptive notch, but colleagues tell me it is pretty routine... however, in my experience "adaptive anything" is at least potentially far from routine... My only advice is to perfect the algorithm first in high precision before moving to target precisions (in your case, fract32). Good luck Steve
Sorry about not adding code for the potential trouble section lol. 
  
  // Adapting coefficiants
  int i,j;
  fract32 temp;
  fract32 MU = float_to_fr32(ANC_MU); // step size
  static fract32 state[2][ANC_FILTER_ORDER]; // temp delay buffer
  static fract32 wvector[2]; // adaptive coeffs

  for(i = 0; i < ANC_SAMPLE_SIZE)
  {
    
    /* Sine coeffs */
    memmove(&state[0][1], &state[0][0], sizeof(fract32) *
ANC_FILTER_ORDER-1); //     shift left
    state[0][0] = sample; // Add new  value to beginning 

    for(i = 0; i < ANC_FILTER_ORDER; i++)
    {
      temp = mult_fr1x32x32(reference->w[i],desired);
      wvector[0][i] = add_fr1x32(wvector[0][i], mult_fr1x32x32(temp,
MU));
    }


    /* Cosine coeffs */
    memmove(&state[1][1], &state[1][0], sizeof(fract32) *
ANC_FILTER_ORDER-1); //     shift left
    state[1][0] = sample; // Add new  value to beginning 

    for(i = 0; i < ANC_FILTER_ORDER; i++)
    {
      temp = mult_fr1x32x32(reference->w[i],desired);
      wvector[1][i] = add_fr1x32(wvector[1][i], mult_fr1x32x32(temp,
MU));
    }

  }

   I Have circular buffer implementation to achieve this but this is
essentially whats happening. 


---------------------------------------
Posted through http://www.DSPRelated.com