Go Down

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

pito

BTW - where in your code you do sampling of the signal at the required sampling frequency?

SexualMoose

I'm not 100% sure what you are asking, I believe the sampling code is in the goertzel.cpp and it happens at the analogRead(0);

el_supremo

I think I have figured out what's happening in that library. As I mentioned, the sampling loop just goes as fast as possible. Your code sets the sampling rate to 9000Hz but I've measured that loop on my Duemilanove with a frequency counter and the loop is more like 8300Hz.
Change your sampling frequency to 8300 and give it a try. On my Arduino it shows a definite peak at 440Hz now.

Pete
Don't send me technical questions via Private Message.

SexualMoose

Are you using the modified code or the original?

Can you post your code for recording the frequency?

Also I don't believe you can just change the sampling rate without changing "N".

Docedison

A Mitel MT8870 will give you 4 bit binary outputs and a data ready signal. The device contains the filters for the high and kw frequencies and decode @ < 100mV. I included thee data sheet and I've seen them on Ebay for $3 - $4.00. Much easier than devoting such a large piece of the code space for decoding... another note is that there are DTMF generators that use the same crystal... If you choose another time base only you could easily decode them. This was a common method for semi secure radio based remote control. I used it when building my first amateur radio repeater for shut down and phone patch control.

Doc
--> WA7EMS <--
"The solution of every problem is another problem." -Johann Wolfgang von Goethe
I do answer technical questions PM'd to me with whatever is in my clipboard

SexualMoose

#20
Sep 03, 2012, 01:58 am Last Edit: Sep 03, 2012, 02:16 am by SexualMoose Reason: 1


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!



I appreciate the help, but I really just want to get a functional Goertzel algorithm coded. Finalized, the code shouldn't take more than 50 lines of code. I just don't understand the math well enough to lay it out. I understand the steps, the flowchart, and a small amount of the math. I just need some one, who understands the math, to copy the equations and the lay them out like shown in the flow chart as seen here http://www.ti.com/lit/an/spra066/spra066.pdf. Even if the arduino can't sample at 9000, I believe I can adjust the sample rate and "N" later.

Also, Pete, I would really like to know how you monitored the frequency at which it took a sample. That would help immensely, as i would need to adjust it after I implemented it into another batch of code I'm currently working on. The other code shouldn't consume much and I only need to sample at >3300Hz.

el_supremo

I tested it with a slightly modified version of the library which I've attached to this post. I've added code to toggle the output of digital pin 4 so that you can test it yourself. When you do the test, you should modify your code so that it does:
Code: [Select]
  while(1)goertzel.sample(sensorPin); //Will take n samples
This will force it to loop in the sample routine all the time, otherwise you won't get an accurate frequency reading. Then whatever frequency you get on your frequency counter, you use double that value as the sampling frequency.
It is best to leave the counter code in there all the time because removing it will speed up the loop a bit which knocks the sampling frequency out of whack again.

Changes to the library:
- added code to initialize and toggle pin 4 so that a frequency counter can be used to determine the sampling rate
- the library was storing the sampled values as bytes - not integers!
- offset the sample by 512 so that the Goertzel algorithm then uses values in the range -512 to 511



Quote
Also I don't believe you can just change the sampling rate without changing "N".

Yes you can. The two values are independent. N determines the bandwidth of the detector. Larger N gives smaller bandwidth. For example with a sampling frequency of 9000 and N=45 the bandwidth will be 200Hz (9000/45). Change N to 90 and the bandwidth will be 100Hz.
NOTE that the maximum value of N allowed by the library is 160. If you exceed that value your sketch will just crash in mysterious ways.

Pete
Don't send me technical questions via Private Message.

el_supremo

BTW. This, of course, only decodes one tone. To do DTMF it would have to be modified to decode 8 tones and then compare the amplitudes to figure out whether there's a DTMF tone there and if so which one it is.

Pete
Don't send me technical questions via Private Message.

SexualMoose

Alright thank you, I'll take a whack at it tonight. I'm at work currently so I won't be able to test it until around midnight.


pito


Change your sampling frequency to 8300 and give it a try. On my Arduino it shows a definite peak at 440Hz now.

That is why I asked :).

el_supremo

Don't send me technical questions via Private Message.

jjrosent

Pete,

Thanks for the fixes, I rolled them back into my repository
https://github.com/jacobrosenthal/Goertzel

Mines still a general frequency detection library though. If you're looking for a DTMF, Petes new library is still going to be your best bet.

Also. Remember, you can bring bugs or feature requests to a github authors attention by clicking 'issues' so it doesn't take me a year to hear about this :)


rickso234

Know this is an old posting but it's very appropriate.

Tried Pete's library files in Post #21 and although it detects a tone, the "Serial.println(magnitude);" statement doesn't print anything. It's getting a value for "magnitude" because the LED lights when a 100Hz tone is played. A different Goertzel library, which also detects the tone but uses much more memory, prints fine.

What in a library would disable the "println", or is my brain the thing that's disabled?


Code: [Select]
#include <Goertzel.h>

int sensorPin = A0;
int led = 4;

// ideally an integer of SAMPLING_FREQUENCY/N to center the bins around your content so if you're
// looking for 700hz, frequencies below and above it equally contribute. Read up on Kevin's article
// for more info.
// Nyquist says the highest frequency we can target is SAMPLING_FREQUENCY/2
const float TARGET_FREQUENCY = 100;

// if you're trying to detect several different drum hits all within low frequency like
// ~100-200hz you'll need a small bin size like 25 or 50 to distinguish them.
// If however you're just trying to find ANY bass hit you might want something
// basically equal to frequency youre looking for like ~100
 
// If Im detecting a frequency much higher with no care about nearby tones, like 2000hz
// Ill set to a round divisor like 200 So 1900 to 2100 could trigger, but not less or more
// Max is 200 as we have limited ram in the Arduino, and sampling longer would make us less
// responsive anyway
const int N = 160;

// This is what will trigger the led. Its INCREDIBLY squishy based on volume of your source,
// frequency, etc. You'll just need to get in your environment and look at the serial console
// to start. Then pick something that triggers pleasantly to your eye.
const int THRESHOLD = 1000;

// Again, the highest frequency we can target is SAMPLING_FREQUENCY/2. So Since Arduino is
// relatively slow in terms of audio, we sample literally as fast as we can
// This is generally around ~8900hz for a 16mhz Arduino and 4400hz for an 8mhz Arduino.
// User nicola points out these rates are for stock arduino firmware and that on a board
// by board basis you can juice the adc rates. For Arduino Uno you could move that rate up to
// 22khz by adding somthing like this to your setup:
//  _SFR_BYTE(ADCSRA) |=  _BV(ADPS2); // Set ADPS2
//  _SFR_BYTE(ADCSRA) &= ~_BV(ADPS1); // Clear ADPS1
//  _SFR_BYTE(ADCSRA) &= ~_BV(ADPS0); // Clear ADPS0
const int SAMPLING_FREQUENCY = 8900;

Goertzel goertzel = Goertzel(TARGET_FREQUENCY, N, SAMPLING_FREQUENCY);

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

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

jremington

Quote
What in a library would disable the "println", or is my brain the thing that's disabled?
That version contains extra code to test the sampling frequency, and should not be used.


Go Up