Go Down

Topic: Goertzel for reliable DTMF Decoding (Read 6217 times) previous topic - next topic

SexualMoose

Sep 01, 2012, 08:37 am Last Edit: Sep 03, 2012, 01:24 am by SexualMoose Reason: 1
I've been looking all over the web for a simple Arduino library for the implementation of either Goertzel or FFT. My goal is to use something like this to decode DTMF tones reliably. The closest thing I could find was this https://github.com/jacobrosenthal/Goertzel. I attempted to use the library example, but the results were less than perfect. I put an LED between digital 13 and the ground right above it. Then I plugged the headphone out from my iphone into A0 and ground. Volume was set at full. The default target frequency is 440.

First test:
No matter the tone or shape generated the LED did not light.

Second test:
I changed the "magnitude" to 500 and the led triggered from around 350 to 670 which is hugely unacceptable

Third test:
I spent some time changing the magnitude and target frequencies with no avail. Either the LED never triggered or it would trigger in +/-200 range of the target frequency.

As a side note, I am not very mathematically inclined. Otherwise i would've just taken apart the algorithm myself and made an Arduino library.

Anyway, if anyone has any good Arduino software solutions for decoding DTMF or any suggestions on how to make this or another library work, it would be greatly appreciated. I do not want a hardware solution. I already have a perfectly working hardware solution and I am now looking to eliminate it all together. Thanks for your help!

pito

you may try
http://arduino.cc/forum/index.php/topic,119273.0/topicseen.html

Magician

Quote
Then I plugged the headphone out from my iphone into A0 and ground.
You shouldn't, A0 probably already damaged.
http://coolarduino.wordpress.com/2012/06/22/audio-input-to-arduino/

SexualMoose

#3
Sep 01, 2012, 09:53 pm Last Edit: Sep 01, 2012, 10:02 pm by SexualMoose Reason: 1
I did, indeed, forget that you cannot simply wire up ground to analog an expect results, but I fail to see how I may have cause damage. Analog is a 0 to 5 volt input. In a rather oversimplified way, if it accepts a +5v input then connecting it to a ground or audio out shouldn't harm it. Analog is an input. So, its not going to short out with ground and a headphone jack is only going to put out a maximum ~3volts and a couple milliamps. So, unless I'm missing something (which is totally possible), no damage should occur.

Also after wiring it up correctly, with a different arduino(in case I damaged it :P), the results were exactly the same. Nothing happens unless you lower the magnitude and then the LED fires in +/-100hz ranges that are no where near the target range.

Also, I'm confused on how to use the FFT Library you linked me to. It only provides 2 examples and one requires a codec shield. The other one only outputs data in binary form. How would I go about getting a frequency as a number or getting it to recognize certain frequencies?

Again, thanks for you help.

el_supremo

Quote
I'm confused on how to use the FFT Library you linked me to.

For single tone detection stick with Goertzel's algorithm. It's much faster.

Quote
I put an LED between digital 13 and the ground right above it.

Not a good idea. You must have a resistor (220 ohms or higher) in series with the LED otherwise you'll probably damage pin 13 and even the whole Arduino.
But which Arduino are you using? Arduino boards have an LED on Pin 13 anyway.

Quote
Then I plugged the headphone out from my iphone into A0 and ground.

As has been pointed out, this is also not a good idea. You should connect the headphone output through a capactor (about 0.1uF) to the centre of a 1:1 voltage divider. Then the analog input pin is also connected to the centre of the divider. The headphone output should also have a resistor to ground.

Quote
Volume was set at full

You might have been overloading the input and all it was seeing was 1023, 1023, 1023, .....

Quote
Either the LED never triggered or it would trigger in +/-200 range of the target frequency.

Post your code. There's no way to know what's wrong without seeing it - all of it.


Pete



SexualMoose

On a Duemilanove or UNO pin 13 has a resistor and led on the board, but I added a brighter LED because the one on the board is rather dim and hard to see. The pin directly above it is ground. http://www.arduino.cc/en/Tutorial/BlinkingLED

As stated in my last post I already fixed the audio connection to follow this suggested schematic http://coolarduino.wordpress.com/2012/06/22/audio-input-to-arduino/ You'd be hard pressed to damage an analog input with audio out.

Even with the volume at max an iphone/ipod wont put out more than ~3volts which would max out the reading somewhere around 613.8.

The code and the library were posted in the first post via a link. I figured that would be more appropriate because you need the library to test the code anyway.
Here is the code anyway:
Code: [Select]
/*
  Blinks a light on a 16mhz Arduino when it detects an A4 (440 hz), tone
the tuning fork pitch and something easily generated by the Tone library
  or a google search.

  The Goertzel algorithm is long standing so see
  http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
  It is often used in DTMF tone detection as an alternative to the Fast
  Fourier Transform because it is quick with low overheard because it
  is only searching for a single frequency rather than showing the
  occurrence of all frequencies.
 
  This work is entirely based on the Kevin Banks code found at
  http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
  so full credit to him for his generic implementation and breakdown. I've
  simply massaged it into an Arduino library. I recommend reading his article
  for a full description of whats going on behind the scenes.

  Created by Jacob Rosenthal, June 20, 2012.
  Released into the public domain.
*/
#include <Goertzel.h>

int sensorPin = A0;
int led = 13;

float target_freq=440.0; //must be an integer of 9000/N and be less than
                         //sampling_frequency/2 (thanks to Nyquist)
float n=20.0;
float sampling_freq=9000.0;

Goertzel goertzel = Goertzel(target_freq,n,sampling_freq);

void setup(){
  pinMode(led, OUTPUT);     
  Serial.begin(9600);
}

void loop()
{
  goertzel.sample(sensorPin); //Will take n samples
 
  float magnitude = goertzel.detect();  //check them for target_freq
 
  if(magnitude>1000) //if you're getting false hits or no hits adjust this
    digitalWrite(led, HIGH); //if found, enable led
  else
    digitalWrite(led, LOW); //if not found, or lost, disable led
   
  Serial.println(magnitude);
}

el_supremo

I don't see how that library can possible work. The initialization of the sampling rate, sample size and target tone looks fine. But Goertzel.sample just reads N samples as fast as it can without any attempt to do the sampling at the rate you've specified. It would need to have the correct delay between each sample for this to work.

Pete

SexualMoose

Any suggestions on how to modify it to work? This is obviously not my code and the math is beyond me. So, if you have any suggestions or alternatives, let me know.

Thank you

retrolefty

#8
Sep 02, 2012, 06:43 am Last Edit: Sep 02, 2012, 06:56 am by retrolefty Reason: 1
Quote
On a Duemilanove or UNO pin 13 has a resistor and led on the board, but I added a brighter LED because the one on the board is rather dim and hard to see. The pin directly above it is ground. http://www.arduino.cc/en/Tutorial/BlinkingLED


That is bad and outdated information on that linked page. The present arduino boards do not have a resistor wired in series between the chip pin and the shield pin 13, and so if you simply wire a led like in that picture there is no current control that limits current to less then maximum safe value for the chip's output pin.

If you wire an led directly between the shield pin 13 and the shield ground pin, the led will have no series resistor to limit it's current flow and puts the 328p chip and led at great risk. The external led requires, as in you must add, a series resistor to prevent damage.

Lefty

el_supremo

I tried your code with that library. After fixing a few problems with the library, with code to recognize a 440 Hz tone, it was responding much stronger to tones up around 550z and I couldn't figure out why so I gave up.

Pete

SexualMoose

Yup that's my problem too. Well, if anyone else has any ideas or even another library, let me know. Thanks for giving it a try pete !

SexualMoose

Alright so I made a few changes and made a little progress. It provides predictable results and is accurate to some degree. I found a couple errors in his code and then started making equation adjustments along the way. Since I'm rather poor at math I used this document http://www.ti.com/lit/an/spra066/spra066.pdf to help a tad and just tweaked things to as close of a reading as possible.

Here is the .ino
Code: [Select]
/*
  Blinks a light on a 16mhz Arduino when it detects an A4 (440 hz), tone
the tuning fork pitch and something easily generated by the Tone library
  or a google search.

  The Goertzel algorithm is long standing so see
  http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
  It is often used in DTMF tone detection as an alternative to the Fast
  Fourier Transform because it is quick with low overheard because it
  is only searching for a single frequency rather than showing the
  occurrence of all frequencies.
 
  This work is entirely based on the Kevin Banks code found at
  http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
  so full credit to him for his generic implementation and breakdown. I've
  simply massaged it into an Arduino library. I recommend reading his article
  for a full description of whats going on behind the scenes.

  Created by Jacob Rosenthal, June 20, 2012.
  Released into the public domain.
*/


/*
Crudely Revised By: SexualMoose
Frequencies generated by iPhone/iPod at full volume
Frequencies are found in a 80Hz range proportional to the target.
When looking for 697 Frequency recognition starts ~70Hz below target and stop about ~10Hz above
this range shifts down proportional to the increase in the target frequency
i.e. when looking for 1633 its starts ~1480Hz and ends ~1560Hz
*/
#include <Goertzel.h>

int sensorPin = 0;
int led = 13;

float target_freq=1633.0; //must be an integer of 9000/N and be less than
                         //sampling_frequency/2 (thanks to Nyquist)
float n=115.0;
float sampling_freq=9000;

Goertzel goertzel = Goertzel(target_freq,n,sampling_freq);

void setup(){
  pinMode(led, OUTPUT);     
  Serial.begin(9600);
}

void loop()
{
  delay(10);
  sensorPin = analogRead(0);
  Serial.println(sensorPin);
  goertzel.sample(sensorPin); //Will take n samples
 
  float magnitude = goertzel.detect();  //check them for target_freq
 
  if(magnitude>1000) //if you're getting false hits or no hits adjust this
    digitalWrite(led, HIGH); //if found, enable led
  else
    digitalWrite(led, LOW); //if not found, or lost, disable led
   
  Serial.println(magnitude);
}


Here is the Goertzel.cpp
Code: [Select]
/*
  The Goertzel algorithm is long standing so see
  http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
  It is often used in DTMF tone detection as an alternative to the Fast
  Fourier Transform because it is quick with low overheard because it
  is only searching for a single frequency rather than showing the
  occurrence of all frequencies.

  This work is entirely based on the Kevin Banks code found at
  http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
  so full credit to him for his generic implementation and breakdown. I've
  simply massaged it into an Arduino library. I recommend reading his article
  for a full description of whats going on behind the scenes.

  Created by Jacob Rosenthal, June 20, 2012.
  Released into the public domain.
*/
// include core Wiring API
#include "Arduino.h"

// include this library's description file
#include "Goertzel.h"

float SAMPLING_RATE;
float TARGET;
float N;
float coeff;
float Q1;
float Q2;
float sine;
float cosine;

byte testData[160];

Goertzel::Goertzel(float TARGET_FREQUENCY, float BLOCK)
{
#if F_CPU == 16000000L
Goertzel(TARGET_FREQUENCY, BLOCK, 9000.0);
#else
Goertzel(TARGET_FREQUENCY, BLOCK, 9000.0);
#endif
}

Goertzel::Goertzel(float TARGET_FREQUENCY,float BLOCK,float SAMPLING_FREQ)
{

  SAMPLING_RATE=SAMPLING_FREQ; //on 16mhz, ~8928.57142857143, on 8mhz ~44444
  TARGET=TARGET_FREQUENCY; //must be integer of SAMPLING_RATE/N
  N=BLOCK; //Block size
  int k;
  float omega;

  k = (int) (N * (TARGET_FREQUENCY / SAMPLING_RATE) + 0.93);
  omega = (2.0 * PI * k) / N;
  sine = sin(omega);
  cosine = cos(omega);
  coeff = 2.0 * cosine;

  ResetGoertzel();
}


/* Call this routine before every "block" (size=N) of samples. */
void Goertzel::ResetGoertzel(void)
{
  Q2 = 0;
  Q1 = 0;
}


/* Call this routine for every sample. */
void Goertzel::ProcessSample(byte sample)
{
  float Q0;
  Q0 = coeff * Q1 - Q2 + (float) sample;
  Q2 = Q1;
  Q1 = Q0;
}


/* Basic Goertzel */
/* Call this routine after every block to get the complex result. */
void Goertzel::GetRealImag(float *realPart, float *imagPart)
{
  *realPart = (Q1 - Q2 * cosine);
  *imagPart = (Q2 * sine);
}


/* Sample some test data. */
void Goertzel::sample(int sensorPin)
{
  for (int index = 0; index < N; index++)
  {
    testData[index] = (byte) analogRead(0);
  }
}


float Goertzel::detect()
{

  int index;

  float magnitudeSquared;
  float magnitude;
  float real;
  float imag;

  /* Process the samples. */
  for (index = 0; index < N; index++)
  {
    ProcessSample(testData[index]);
  }

  /* Do the "standard Goertzel" processing. */
  GetRealImag(&real, &imag);

  magnitudeSquared = real*real + imag*imag;
  magnitude = (sqrt(magnitudeSquared)/2);

  ResetGoertzel();
  return magnitude;
}



And here is a link to test out the modified library:
https://www.dropbox.com/sh/9lmbik1v6k1tgt3/Wl2xlBTypU


Here's what I'd like to do:
I need help turning the equations in this document into arduino  code http://www.ti.com/lit/an/spra066/spra066.pdf. If anyone could help me out with that I'd super appreciate it. It doesn't have to be much, just a frame work so I know where to put the variables. Thanks for your help!

pito

TMS320 is a special DSP processor, you can hardly apply that equations to atmega, though..

SexualMoose

#13
Sep 03, 2012, 12:08 am Last Edit: Sep 03, 2012, 12:15 am by SexualMoose Reason: 1
While yes, the formula was modified for that chip, the formula should still be sound if you change the sample rate to 9000 and "N" to 115

SexualMoose

Literally all I need is the formula(s) in order and the inputs labeled so I know where to put them. I am able to modify parts but understanding the whole thing is beyond me. I do know that the basic modified equations still apply if you change N to 115 and the sample rate to 9000

Go Up