Go Down

Topic: DTMF decoder library (Read 96758 times) previous topic - next topic

el_supremo

That has already been covered earlier in this thread. See message #28 and a few of the earlier ones.

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

AquaRovGuy


aleksandr-zh

hi all, hi el_supremo!

I want to do caller ID. But I live in Russia. We have most of the telephone exchanges of old, they were made in the USSR. In the old telephone exchanges not used for FSK or DTMF, but own Soviet standard transmission of the calling subscriber.
The old standard differs by the algorithm, and frequencies. For example, here's the frequencies, they are transmitted without a pause between digits.

I found the code for Goertzel algorithm, but I can't figure out how to obtain the necessary data from this program and how to connect the signal to the ADC input.

code "2 of 6".

f, Hz   The level dB
700   -6.5 To 27.4
900   -6.5 To 29.0
1100   -6.5 To 31.0
1300   -6.5 To 32.6
1500   -6.5 To 34.3
1700   -6.5 To 36.0

Combinations of two of the six frequencies above means:
f1 + f2, Hz The code "2 6"

The number
1 700 + 900 000011
2 700 + 1100 000101
3 900 + 1100 000110
4 700 + 1300 001001
5 900 + 1300 001010
6 1100 + 1300 001100
7 700 + 1500 010001
8 900 + 1500 010010
9 1100 + 1500 010100
0 1300+ 1500 011000
"Start" 1100 + 1700 100100
"Repeat" 1300+ 1700 101000

"Start" - represents the start and end of the two-frequency chip package (multi-frequency sequence is repeated several times a packet of information, the beginning and end of which points to this combination).
"Repeat" - it means that another figure repeats the previous (in the absence of the combination of decoding two series following the same numbers would be extremely difficult).
information package contains 10 two-frequency packages for 38-42 ms


I want to try to use your library to determine the desired frequencies me. But I do not understand how you carded these values?

DTMF.cpp

const unsigned char dtmf_map[16] = {
 0x11,
 0x21,
 0x41,
 0x12,
 0x22,
 0x42,
 0x14,
 0x24,
 0x44,
 0x28,
 0x81,
 0x82,
 0x84,
 0x88,
 0x18,
 0x48
};

Info:
The telephone exchange transmits the calling number after the subscriber request - 500 Hz, 100-200 milliseconds. To improve the reliability of the signals can be transmitted 2-9 times.
https://upload.wikimedia.org/wikipedia/commons/3/38/ANI-signals.wav

info: https://translate.google.ru/translate?sl=ru&tl=en&js=y&prev=_t&hl=ru&ie=UTF-8&u=https%3A%2F%2Fru.wikipedia.org%2Fwiki%2F%25D0%2590%25D0%259E%25D0%259D&edit-text=

https://translate.google.ru/translate?sl=ru&tl=en&js=y&prev=_t&hl=ru&ie=UTF-8&u=http%3A%2F%2Fradio.cybernet.name%2Fshem%2Ftel%2Faon.html&edit-text=

el_supremo

#48
Jul 16, 2016, 06:47 pm Last Edit: Jul 16, 2016, 06:57 pm by el_supremo
With DTMF, the code is 2 of 8 consisting of one tone from the "row" frequencies and one tone from the "column" frequencies. The code uses the Goertzel algorithm to detect each of the 8 frequencies. The DTMF frequencies are arranged like this on a touchtone phone:
Code: [Select]

          1209  1336  1477  1633
 697,       1     2     3     A
 770,       4     5     6     B
 852,       7     8     9     C
 941,       *     0     #     D

So hitting the number '8' will send tones 852 and 1366 together. When receiving these tones, the code looks for them in the order they are listed in the dtmf_tones array. The code then maps them into two hex digits, one for the rows and one for the columns.
Code: [Select]

int dtmf_tones[8] = {
// Row tones (low order hex digit)
 697,     //1
 770,     //2
 852,     //4
 941,     //8
// Column tones (high order hex digit)
1209,    //1
1336,    //2
1477,    //4
1633     //8
};

If tones 852 and 1366 are received together, they will be converted into hex 24. This is then looked up in the dtmf_map array and the index where it is found (7) is then used to map into the dtmf_char array which translates it to the character '8'.

In your case, there are only six tones which aren't in a convenient row/column arrangement but it shouldn't be too hard to modify the code to read it provided that the individual tones are long enough for the Goertzel algorithm to reliably detect them.

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

aleksandr-zh

#49
Jul 16, 2016, 08:35 pm Last Edit: Jul 16, 2016, 09:06 pm by aleksandr-zh
Pete, I understand!!! :)
terrible not to know English...

but then I don't understand how the program is allocated a desired frequency... :(

el_supremo

The easiest way to add the frequencies would be to change the array like this:
Code: [Select]
int dtmf_tones[8] = {
 700,
 900,
1100,
1300,
1500,
1700,
0,
0
};


The code will still look for eight tones but two of them will be zero and you can ignore them. You should only see pairs of tones with an index of 0 to 5. Anything else is not valid.
Then you will have to change the code which translates these into their corresponding digits.
But you'll also have to handle the start and repeat codes which don't occur in DTMF.

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

aleksandr-zh

#51
Jul 16, 2016, 09:29 pm Last Edit: Jul 16, 2016, 09:38 pm by aleksandr-zh
I read discovery method by sines and cosines (tabular correlation method) and the program in assembler before, so I waited a difficult code.

Well, Pete. I will try this option this week, and then report the results.
I use the Bascom compiler. Arduino used a little, experience not have. And always Arduino proved to be very positive. Probably will have to learn this IDE also :)

Thank you for help and detailed explanations!

aleksandr-zh

the problem is solved, but not completely: I took the program from Arduino for DTMF receiving and remade it for its task. Signals are received, but there are some unpleasant moments.

the device is not fast enough: I successfully received signals with a duration of 40-45 ms, but bad receive or not taking all signals shorter than 40 ms

I'm writing a program for Arduino for the first time...

el_supremo

Can you post the code you have so far (in code tags please) and I'll have a look at it?

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

aleksandr-zh

#54
Jul 18, 2016, 08:29 am Last Edit: Jul 18, 2016, 08:37 am by aleksandr-zh
Yes, of course!
I sped up the algorithm by removing the two "useless for me" dimension. The algorithm is written to the 8 frequencies. I reduced the measurement of up to 6 frequencies.
after these improvements, the program is well receive signals from the telephone station. Telephone station 3 times transmits data about the subscriber number. And I at least 2 times successfully to receive the data. if the program is incorrectly received symbol, then I see "-"

some parts of the code is bad - the first time I write on Arduino... I don't know the language, so doing everything by trial and error

The signal of russian CID:
https://upload.wikimedia.org/wikipedia/commons/3/38/ANI-signals.wav




el_supremo

First just a "cosmetic" change to your code. Replace the entire DTMF::button method with this:
Code: [Select]

// The low order bit corresponds to the lowest frequency (700Hz)
unsigned char dtmf_map[12] = {
  B000011, //1
  B000101, //2
  B000110, //3
  B001001, //4
  B001010, //5
  B001100, //6
  B010001, //7
  B010010, //8
  B010100, //9
  B011000, //0
  B100100, //start
  B101000, //repeat
};

char dtmf_char[12] = {
  '1',
  '2',
  '3',
  '4',
  '5',
  '6',
  '7',
  '8',
  '9',
  '0',
  'S',
  'R'
};

char last_dtmf = 0;
char DTMF::button(float mags[], float magnitude)
{
  int bit = 1;
  int j;
  int dtmf = 0;

  for (int i = 0; i < 6; i++) {
    if (mags[i] > magnitude) {
      dtmf |= bit;
    }
    bit <<= 1;
  }

  if (dtmf > 0)  {
    for(j = 0;j < 12;j++) {
      if(dtmf == dtmf_map[j]) {      
        return(dtmf_char[j]);
      }
    }
    return('-');
  }
  return(0);
}


This will remove the "goto" statements :) It also returns the detected character, which your version wasn't doing.

The button method still needs some fixing though. In the Russian CID system, the same digit can't appear twice in a row. Your code needs to be able to detect whether the currently detected digit is the same as the last one and, if it is, ignore it (return zero instead). This will remove the occasional duplicated digits that show up in your example output. You will also have to handle the Repeat code properly - if 'R' is detect, return the last valid digit instead of 'R'.

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

aleksandr-zh

thanks! I'll do it tomorrow

SHTIRLITZ

Hello, i try to launch scecth on IDE 1.16.11, compilation was not complete:
Arduino: 1.6.11 (Windows 10), Плата:"Arduino/Genuino Uno"

C:\Users\oberw\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.15\cores\arduino\main.cpp: In function 'main':

C:\Users\oberw\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.15\cores\arduino\main.cpp:51:1: error: unable to find a register to spill in class 'POINTER_REGS'

 }

 ^

C:\Users\oberw\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.15\cores\arduino\main.cpp:51:1: error: this is the insn:

(insn 262 260 264 16 (set (reg:SF 144 [ D.2206 ])

        (mem:SF (post_inc:HI (reg:HI 164 [ ivtmp.110 ])) [10 MEM[base: _142, offset: 0B]+0 S4 A8])) C:\Users\oberw\Documents\Arduino\libraries\DTMF\DTMF.cpp:175 100 {*movsf}

     (expr_list:REG_INC (reg:HI 164 [ ivtmp.110 ])

        (nil)))

C:\Users\oberw\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.15\cores\arduino\main.cpp:51: confused by earlier errors, bailing out

lto-wrapper: C:\Users\oberw\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\4.9.2-atmel3.5.3-arduino2/bin/avr-gcc returned 1 exit status

c:/users/oberw/appdata/local/arduino15/packages/arduino/tools/avr-gcc/4.9.2-atmel3.5.3-arduino2/bin/../lib/gcc/avr/4.9.2/../../../../avr/bin/ld.exe: lto-wrapper failed

collect2.exe: error: ld returned 1 exit status

exit status 1


el_supremo

Post your code (in code tags </>)

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

SHTIRLITZ

Post your code (in code tags </>)

Pete
Code the original from your library:

Code: [Select]

// The library toggles digital pin 4 in the sampling loop which allows
// measurement of the actual sampling frequency.
// If you call .sample() continuously like this:
// while(1)dtmf.sample(sensorPin);
// you can put a frequency counter on pin 4 to determine what the
// sampling frequency is on your Arduino. Note that the frequency
// counter will show half the actual rate. My meter showed 4.463kHz
// so the sampling rate is 8926Hz
#include <DTMF.h>

int sensorPin = A0;
int led = 13;


// NOTE that N MUST NOT exceed 160
// This is the number of samples which are taken in a call to
// .sample. The smaller the value of N the wider the bandwidth.
// For example, with N=128 at a sample rate of 8926Hz the tone
// detection bandwidth will be 8926/128 = 70Hz. If you make N
// smaller, the bandwidth increases which makes it harder to detect
// the tones because some of the energy of one tone can cross into
// an adjacent (in frequency) tone. But a larger value of N also means
// that it takes longer to collect the samples.
// A value of 64 works just fine, as does 128.
// NOTE that the value of N does NOT have to be a power of 2.
float n=128.0;
// sampling rate in Hz
float sampling_rate=8926.0;

// Instantiate the dtmf library with the number of samples to be taken
// and the sampling rate.
DTMF dtmf = DTMF(n,sampling_rate);

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

int nochar_count = 0;
float d_mags[8];
void loop()
{
  char thischar;
 
  // This reads N samples from sensorpin (must be an analog input)
  // and stores them in an array within the library. Use while(1)
  // to determine the actual sampling frequency as described in the
  // comment at the top of this file
  /* while(1) */dtmf.sample(sensorPin);
 
  // The first argument is the address of a user-supplied array
  // of 8 floats in which the function will return the magnitudes
  // of the eight tones.
  // The second argument is the value read by the ADC when there
  // is no signal present. A voltage divider with precisely equal
  // resistors will presumably give a value of 511 or 512.
  // My divider gives a value of 506.
  // If you aren't sure what to use, set this to 512
  dtmf.detect(d_mags,506);

  // detect the button
  // If it is recognized, returns one of 0123456789ABCD*#
  // If unrecognized, returns binary zero

  // Pass it the magnitude array used when calling .sample
  // and specify a magnitude which is used as the threshold
  // for determining whether a tone is present or not
  //
  // If N=64 magnitude needs to be around 1200
  // If N=128 the magnitude can be set to 1800
  // but you will need to play with it to get the right value
  thischar = dtmf.button(d_mags,1800.);
  if(thischar) {
    Serial.print(thischar);
    nochar_count = 0;
    // Print the magnitudes for debugging
//#define DEBUG_PRINT
#ifdef DEBUG_PRINT
    for(int i = 0;i < 8;i++) {
      Serial.print("  ");
      Serial.print(d_mags[i]);
    }
    Serial.println("");
#endif
  } else {
    // print a newline
    if(++nochar_count == 50)Serial.println("");
    // don't let it wrap around
    if(nochar_count > 30000)nochar_count = 51;
  }
}

Go Up