Problem applying transfer function to DSP notch filter with Arduino Mega

Hi, if anybody could assist with some issues i am having, whether that be code to try or a useful source, i would be very grateful. Here is the background information.
I am currently designing a DSP notch filter for use as a training aid for military electronics students. The idea is to have a conversation which cannot be heard due to a frequency/noise played at the same time, both audio signals will be input and the noise frequency will be taken out.
The audio signal goes into an anolgue circuit, is amplified and passed through three Low Pass filters to eliminate anything over 4KHz. Using a potential divider this signal is then set on a 2.5V centre.
The signal is read into the Arduino analogue pin 3, stored in a small array for use in the transfer function and output at digital pins 31-38.
The sampling rate has been changed and audio passes through to a speaker without error.
The issue is that the transfer function used does not remove the desired, or any other, frequency and i am unsure of the reason why.
Below is my setup code;

void setup() {
  Serial.begin(115200);              //  setup serial

  DDRC = 255;  // set digital pins 31-38 as outputs

  ADCSRA = 0;             // clear ADCSRA register
  ADCSRB = 0;             // clear ADCSRB register
  ADMUX |= (3 & 0x07);    // set A3 analog input pin
  ADMUX |= (1 << REFS0);  // set reference voltage
  ADMUX |= (1 << ADLAR);  // left align ADC value to 8 bits from ADCH register

  // sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
  // for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles

  ADCSRA |= (1 << ADPS2) | (1 << ADPS0);    // 32 prescaler for 38.5 KHz
 // ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
   //ADCSRA |= (1 << ADPS1) | (1 << ADPS0);    // 8 prescaler for 153.8 KHz

  ADCSRA |= (1 << ADATE); // enable auto trigger
  ADCSRA |= (1 << ADIE);  // enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN);  // enable ADC
  ADCSRA |= (1 << ADSC);  // start ADC measurements

}

And the following code is the interrupt i am using to read the input values and attempt the transfer function.

ISR(ADC_vect)
{

  double in[3]; // Arrays for read values
double out[3];

  numSamples++;
  in[2]=in[1];
  in[1]=in[0];
  in[0] = ADCH;  // read 8 bit value from ADC
  out[2]=out[1];
  out[1]=out[0]; 
  out[0] = ((in[0] * b0) + (in[1] * b1) + (in[2] * b2) - (out[1] * a1) - (out[2] * a2)); //Transfer Function
  //out[0] = in[0];
 PORTC = out[0]; //Outputs digital signals on Port C
}

I have tried several sources which unfortunately as a beginner i do not have the background knowledge to understand or implement what i have seen.
Any help would be appreciated.
Thank you in advance.

PD82,

Thanks for the help.

Matt

There's a very strong smell of sock puppet about this thread - I thought you RAF types were supposed to look after your kit?

Not quite yet.

The DSP book i have out but just confirms the maths i have done to find the multipliers for my transfer function using the z transform method.

Just looking into the line you pointed out ADMUX |= (3 & 0x07); checking against data sheets to check for potential problems although struggling to realise any as audio is sampled and output correctly.

(Apologies if this is difficult to interpret, the first time i touched programming and Arduino was two weeks ago)

thumbnail_IMG_2591.jpg

PORTC = out[0]; //Outputs digital signals on Port C

out[0] is a 32 bit double variable. PORTC is 8 bits.

the first time i touched programming and Arduino was two weeks ago

Where did you get the code from and how did you design the notch filter?
Post ALL your code.

the transfer function used does not remove the desired, or any other, frequency

How do you know that? What are you measuring and where?

audio passes through to a speaker without error

Is this the audio before, or after, the Arduino processes it?

PORTC = out[0]; //Outputs digital signals on Port C

How do you use/interpret what you get out of PortC?

And to add to what @jremington has just posted: The 'in' and 'out' arrays not only aren't volatile, they are local to the ISR.

(3 & 0x07)

This is silly. Stop it.

Pete

First off:-

in[0] = ADCH;  // read 8 bit value from ADC

Is not reading an 8 bit value, it is reading the top two bits from the 10 bit A/D converter.

Next all that floating point multiplication in the DSP function is going to take time, have you ensured that this is not slowing down the real sample rate you are getting? A DSP design needs a specific sample rate to work on.

Finally when implementing a DSP filter it is normal to use a ring buffer with a pointer to it rather than shuffle the numbers about in an array.

Now read the rules here and post ALL your code.

Grumpy_Mike,el_supremo,

PSB the entire code block.

int analogPin = 3;    

#define a1 -1.334517808
#define a2 0.959355045
#define b0 0.980128067
#define b1 -1.335418897
#define b2 0.980128067
//  fixed multipliers for 15.3KHz

volatile int numSamples = 0;
long t, t0;

void setup() {
  Serial.begin(115200);              //  setup serial
  pinMode(13, OUTPUT);

  DDRC = 255;  // set digital pins 31-38 as outputs

  ADCSRA = 0;             // clear ADCSRA register
  ADCSRB = 0;             // clear ADCSRB register
  ADMUX |= (3 & 0x07);    // set A3 analog input pin
  ADMUX |= (1 << REFS0);  // set reference voltage
  ADMUX |= (1 << ADLAR);  // left align ADC value to 8 bits from ADCH register

  // sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
  // for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles

  ADCSRA |= (1 << ADPS2) | (1 << ADPS0);    // 32 prescaler for 38.5 KHz
  // ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
  //ADCSRA |= (1 << ADPS1) | (1 << ADPS0);    // 8 prescaler for 153.8 KHz

  ADCSRA |= (1 << ADATE); // enable auto trigger
  ADCSRA |= (1 << ADIE);  // enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN);  // enable ADC
  ADCSRA |= (1 << ADSC);  // start ADC measurements

}

ISR(ADC_vect)
{

  double in[3]; // Arrays for read values
  double out[3];

  numSamples++;
  in[2] = in[1];
  in[1] = in[0];
  in[0] = ADCH;  // read 8 bit value from ADC
  out[2] = out[1];
  out[1] = out[0];
  //out[0] = ((2*in[0])+(0.2*in[1])+(0.6*out[1])); //Test TF for LPF 
  out[0] = ((in[0] * b0) + (in[1] * b1) + (in[2] * b2) - (out[1] * a1) - (out[2] * a2)); //Transfer Function
  //out[0] = in[0];
  PORTC = out[0]; //Outputs digital signals on Port C
  //digitalWrite(13, !digitalRead(13));
}



void loop() {
  //  if (numSamples >= 1000) {
  //    t = micros() - t0; // calculate elapsed time
  //
  //    Serial.print("Sampling frequency: ");
  //    Serial.print((float)1000000 / t);
  //    Serial.println(" KHz");
  //    delay(1000);
  //
  //    // restart
  //    t0 = micros();
  //    numSamples = 0;
  //  }

}

I used the loop to check the sampling speed which constantly read 15.3kHz. Hopefully this is correct as i found the example along with the code to speed up the sampling rate at the following website. http://yaab-arduino.blogspot.com/2015/02/fast-sampling-from-analog-input.html
The audio passes through a signal conditioning circuit which consists of an amplifier and LPF, this is then passed into the arduino through analogue pin 3. After the Arduino output i have a DAC which is a R2R ladder which feeds into a buffer and then the speaker. We are also monitoring the input and output on an oscilloscope and spectrum analyser. A signal generator is used as an input for testing.
Hopefully this answers all of your questions, apologies for the ambiguity.
Appreciate the help.

Quick update to anybody looking into this question,

I have made progress and now have a functioning notch filter which still needs refinement but is good enough for the current purpose.

Appreciate all help that was given.

Code is shown below for interest.

int analogPin = 3;

#define a1 -0.420911387
#define a2 0.933579994
#define b0 0.966948881
#define b1 -0.421229154
#define b2 0.966948881
// fixed multipliers for 9.3kHz Sampling frequency

volatile int numSamples = 0;
long t, t0; //variables for sampling frequency calculation

byte MyRead;

void setup() {
  Serial.begin(115200);              //  setup serial
  pinMode(13, OUTPUT);

  DDRC = 255;  // set digital pins 31-38 as outputs

  ADCSRA = 0;             // clear ADCSRA register
  ADCSRB = 0;             // clear ADCSRB register
  ADMUX |= (3 & 0x07);    // set A3 analog input pin
  ADMUX |= (1 << REFS0);  // set reference voltage
  ADMUX |= (1 << ADLAR);  // left align ADC value to 8 bits from ADCH register

  // sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
  // for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles

  // ADCSRA |= (1 << ADPS2) | (1 << ADPS0);    // 32 prescaler for 38.5 KHz
  ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
  // ADCSRA |= (1 << ADPS1) | (1 << ADPS0);    // 8 prescaler for 153.8 KHz

  ADCSRA |= (1 << ADATE); // enable auto trigger
  ADCSRA |= (1 << ADIE);  // enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN);  // enable ADC
  ADCSRA |= (1 << ADSC);  // start ADC measurements

}

volatile double in[3]; // Arrays for read values
volatile double out[3];

ISR(ADC_vect)
{

  MyRead = ADCH;  // read 8 bit value from ADC

}

void loop() {

  numSamples++;
  in[2] = in[1];
  in[1] = in[0];
  in[0] = MyRead;  // read 8 bit value from ADC
  out[2] = out[1];
  out[1] = out[0];
  out[0] = ((in[0] * b0) + (in[1] * b1) + (in[2] * b2) - (out[1] * a1) - (out[2] * a2)); //Transfer Function
 // out[0] = in[0];
  PORTC = ((int)out[0]) >> 2; //Outputs digital signals on Port C

//Use the code below to check sampling frequency - this will affect output whilst running

//  if (numSamples >= 1000)  {
//    t = micros() - t0; // calculate elapsed time
//    Serial.print("Sampling frequency: ");
//    Serial.print((float)1000000 / t);
//    Serial.println(" KHz");
//    delay(1000);
//
//    // restart
//    t0 = micros();
//    numSamples = 0;
//  }

}

Astounding that it is "good enough for the current purpose", considering that you are sampling just the top two bits of the audio!

  MyRead = ADCH;  // read 8 bit value from ADC

Try

MyRead = ((ADCH << 6) & 0x3F) | (ADCL >> 2);

Your filter coefficients were calculated for 15.3 KHz but you are doing your filter calculations in the main loop where your sample time is not defined. I would have kept the filter calculations in your ADC ISR and adjusted the ISR period (with the ADPS[0:2] bits) and your filter coefficients to match in frequency. A less elegant way to do it is to do the calculation in the main loop, record the time you do the calculation and constantly check if it is time to do the next calculation using micros().

Grumpy_Mike,

Thanks for the tip, i'll give it a try Monday once i have access to the kit.

MattBaron44:
Grumpy_Mike,

Thanks for the tip, i'll give it a try Monday once i have access to the kit.

Don't worry about ADCH, since you set ADLAR:
ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register

Everything else is wrong, your code is never gonna work as intended. Here is some steps, you need to take:

  1. Sampling must be precise, there are two options - free running adc or adc conversion started by Timer. First option is easier to implement, but minimum frequency is 9.6 kHz, and if arduino can't do a math in the same speed, would be a problems.
    You always can control sampling rate setting couple digitalWrite's in the ISR, than driving high/low any available digital pin and observing on a scope if it's correct.
  2. Measure how long does it take for arduino to do a math. Put some dummy data array, than measure micros() before and after processing lines of code, see what you get. Basically, arduino is very slow with floating point math, better to re-write your filter in integer's.