Детектирование DTMF

Предыдущая  Содержание  Следующая  V*D*V

Для детектирования DTMF сигналов применяют два метода.

Первый основан на алгоритме детектирования сигналов с неизвестной начальной фазой. По этому методу вычисляются скалярные произведения входного сигнала с 2-мя образцами, один из которых сдвинут по фазе на 90 град. относительно другого. В результате получаются 2 проекции на взаимно перпендикулярные оси. Далее по формуле прямоугольного треугольника вычисляется полная амплитуда взаимного совпадения. Недостатком является необходимость иметь два массива с предварительно рассчитанными коэффициентами для синуса и косинуса для каждой частоты. Данный метод используется в АОН. Его использование там обусловлено простотой расчёта: в АОН используется только 2 значения сигнала 1 - больше нуля, 0 - меньше нуля. Таким образом, вместо умножения достаточно сделать логическое И или ИСКЛЮЧАЮЩЕЕ ИЛИ между отсчётом входного сигнала и коэффициентами.

 

Второй метод использует БИХ-фильтры 2-го порядка, настроенные на необходимые частоты. Вычисление мощности сигнала заменено на вычисление квази-мощности. Алгоритм требует меньше памяти, так как для каждой частоты используется только один наперёд рассчитанный коэффициент.

 

Ниже приведены алгоритмы, использующие оба метода для детектирования DTMF и отдельных частот. Для устранения чувствительности к амплитуде входного сигнала используется нормировка по мощности. Подобный метод можно применять, если амплитуда сигнала изменяется в широких пределах.

 

Хорошие результаты также достигаются другим способом: с помощью порога отсекаются низкие уровни, а дальше за счёт финальной проверки убеждаемся в наличии именно DTMF:

проверяем, что амплитуды сигналов  с максимальными уровнями не сильно отличаются

проверяем, что на остальных частотах сигналы существенно слабее

 

Реализация корреляционного алгоритма

/*******************************************************

* детектирование DTMF и однотональных сигналов

*

--------------

использование:

 ToneDetector td = new ToneDetector();

 if( td.initDTMF( ... ) )

 {

         td.setThreshold( 0.5f );

         ...

         for( int i = 0; i < sampleCount; i++ )

         {

                 byte code = td.process( sample[i] );

                 if( code > 0 )

                 {

                         ...

                 }

         }

 }

********************************************************

tones:

350 Hz                dialtone

440 Hz                ring, dialtone

480 Hz                ring, busy

620 Hz                busy

 

697 Hz                dtmf row 1

770 Hz                dtmf row 2

852 Hz                dtmf row 3

941 Hz                dtmf row 4

1209 Hz                dtmf col 1

1336 Hz                dtmf col 2

1477 Hz                dtmf col 3

1633 Hz                dtmf col 4

 

700 Hz                blue box 1

900 Hz                blue box 2

1100 Hz                blue box 3

1300 Hz                blue box 4

1500 Hz                blue box 5

1700 Hz                blue box 6

2400 Hz                blue box 7

2600 Hz                blue box 8

*******************************************************/

public class ToneDetector {

 /**

  * Период времени, в течение которого производится оценка.

  * Длительность символов в последовательности должна быть

  * как минимум в 2 раза больше.

  * Приемлемый диапазон: 10 ... 100 мс

  */

 private static final byte BUFFER_MS = 20;

 /**

  * мощность сигнала. параметр и вычисления с ним не нужны,

  * если минимальный уровень отсекается порогом.

  */

 private float m_inPower = 0.0f;

 //

 private float m_threshold = 0.5f;

 private boolean m_bDualTone = false;

 private int m_counter = 0;

 private int m_decodeLength = 0;

 private float[][] m_pSin = null;

 private float[][] m_pCos = null;

 private float[] m_pSinAccum = null;

 private float[] m_pCosAccum = null;

 

 public ToneDetector() {

 }

 

 boolean initDTMF( int sampleRate ) {

         int[] pFrequency = new int[8];

         pFrequency[0] = 697;

         pFrequency[1] = 770;

         pFrequency[2] = 852;

         pFrequency[3] = 941;

         pFrequency[4] = 1209;

         pFrequency[5] = 1336;

         pFrequency[6] = 1477;

         pFrequency[7] = 1633;

         boolean res = initSTMF( sampleRate, pFrequency );

         m_bDualTone = true;

         return res;

 }

 

 boolean initSTMF( int sampleRate, final int[] pFrequency ) {

         m_bDualTone = false;

         m_pSin = new float[pFrequency.length][];

         m_pCos = new float[pFrequency.length][];

         m_pSinAccum = new float[pFrequency.length];

         m_pCosAccum = new float[pFrequency.length];

         m_inPower = 0.0f;

         m_decodeLength = (int)(BUFFER_MS * sampleRate / 1000);

         m_counter = 0;

         for( int f = pFrequency.length; f-- != 0; ) {

                 m_pSin[f] = new float[m_decodeLength];

                 m_pCos[f] = new float[m_decodeLength];

                 double step = 2.0 * Math.PI * pFrequency[f] / sampleRate;

                 double t = 0;

                 for( int i = 0; i < m_decodeLength; i++ ) {

                         double angle = i * step;

                         m_pSin[f][i] = (float)Math.sin( angle );

                         m_pCos[f][i] = (float)Math.cos( angle );

                         t += m_pSin[f][i] * m_pSin[f][i];

                 }

                 m_pSinAccum[f] = 0.0f;

                 m_pCosAccum[f] = 0.0f;

         }

         return true;

 }

 /************************************************

 установить порог

 0.00 < threshold < 1.00

 ************************************************/

 void setThreshold( float threshold ) {

         // расчитываем квадрат амплитуды, поэтому

         // используем квадрат порога

         m_threshold = threshold * threshold;

 }

 //frhigh   1209  1336  1477  1633

 //frlow

 //    697   1     2     3     A

 //    770   4     5     6     B

 //    852   7     8     9     C

 //    941   *     0     #     D

 private static final byte decodeDTMF[] = {

                 '1', '2', '3', 'A',

                 '4', '5', '6', 'B',

                 '7', '8', '9', 'C',

                 '*', '0', '#', 'D' };

 

 byte process(short sample) {

         for( int f = m_pSinAccum.length; f-- != 0; ) {

                 m_pSinAccum[f] += (float)sample * m_pSin[f][m_counter];

                 m_pCosAccum[f] += (float)sample * m_pCos[f][m_counter];

                 m_inPower += (float)sample * (float)sample;// если используется мощность

         }

         if( ++m_counter < m_decodeLength ) return 0;

         m_counter = 0;

         byte code = 0;

         if( m_inPower > 0.0f ) {

                 int firstFr = 0;

                 int secondFr = 0;

                 float max = -1.0f;

                 int f = 0;

                 for( ; f < (m_pSinAccum.length / 2); f++ ) {

                         float res = m_pSinAccum[f] * m_pSinAccum[f] + m_pCosAccum[f] * m_pCosAccum[f];

                         if( max < res ) {

                                 max = res;

                                 firstFr = f;

                         }

                         m_pSinAccum[f] = 0.0f;

                         m_pCosAccum[f] = 0.0f;

                 }

                 // нормировка для сравнения с порогом

                 /// если регулировка уровня порогом

                 // 32767 - максимальное значение для 16-ти разрядного звука

                 // max = max / m_decodeLength / m_decodeLength * 4.0f / (32767 * 32767);

                 /// если используется мощность

                 // мощность образца = (sqrt(2)/2)^2 * m_decodeLength

                 max = max / m_inPower / m_decodeLength * 16.0f;

                 if( max < m_threshold ) firstFr = m_pSinAccum.length;

                 if( m_bDualTone ) max = -1.0f;

                 for( ; f < m_pSinAccum.length; f++ ) {

                         float res = m_pSinAccum[f] * m_pSinAccum[f] + m_pCosAccum[f] * m_pCosAccum[f];

                         if( max < res ) {

                                 max = res;

                                 secondFr = f;

                         }

                         m_pSinAccum[f] = 0.0f;

                         m_pCosAccum[f] = 0.0f;

                 }

                 // нормировка для сравнения с порогом

                 /// если регулировка уровня порогом

                 // 32767 - максимальное значение для 16-ти разрядного звука

                 //max = max / m_decodeLength / m_decodeLength * 4.0f / (32767 * 32767);

                 /// если используется мощность

                 // мощность образца = (sqrt(2)/2)^2 * m_decodeLength

                 max = max / m_inPower / m_decodeLength * 16.0f;

                 if( max < m_threshold ) secondFr = m_pSinAccum.length;

                 if( m_bDualTone ) {

                         /**

                          * для уменьшения вероятности ложных срабатываний здесь имеет смысл добавить

                          * дополнительные проверки:

                          * - уровни обнаруженных сигналов не должны сильно отличаться

                          * - остальные сигналы должны быть по уровню сильно меньше основных

                          */

                         if( (firstFr < m_pSinAccum.length) && (secondFr < m_pSinAccum.length) )

                                 code = decodeDTMF[secondFr - (m_pSinAccum.length / 2) +

                                                   firstFr * (m_pSinAccum.length / 2)];

                 } else {

                         /**

                          * для уменьшения вероятности ложных срабатываний здесь имеет смысл добавить

                          * дополнительную проверку:

                          * - уровень обнаруженного сигнала должен сильно превосходить остальные

                          */

                         if( secondFr < m_pSinAccum.length )

                                 code = (byte)('1' + secondFr);

                         else if( firstFr < m_pSinAccum.length )

                                 code = (byte)('1' + firstFr);

                 }

                 

                 m_inPower = 0.0f;

         }

         return code;

 }

}

Реализация алгоритма Герцеля

/*******************************************************

* детектирование DTMF и однотональных сигналов

*

* используется алгоритм Герцеля

* алгоритм модифицирован с целью уменьшения

* чувствительности к амплитуде входного сигнала

*

* использовано:

* Texas Instruments

* DTMF Tone Generation and Detection

* An Implementation Using the TMS320C54X

--------------

использование:

 ToneDetector td = new ToneDetector();

 if( td.initDTMF( ... ) )

 {

         td.setThreshold( 0.5f );

         ...

         for( int i = 0; i < sampleCount; i++ )

         {

                 byte code = td.process( sample[i] );

                 if( code > 0 )

                 {

                         ...

                 }

         }

 }

********************************************************

tones:

350 Hz                dialtone

440 Hz                ring, dialtone

480 Hz                ring, busy

620 Hz                busy

 

697 Hz                dtmf row 1

770 Hz                dtmf row 2

852 Hz                dtmf row 3

941 Hz                dtmf row 4

1209 Hz                dtmf col 1

1336 Hz                dtmf col 2

1477 Hz                dtmf col 3

1633 Hz                dtmf col 4

 

700 Hz                blue box 1

900 Hz                blue box 2

1100 Hz                blue box 3

1300 Hz                blue box 4

1500 Hz                blue box 5

1700 Hz                blue box 6

2400 Hz                blue box 7

2600 Hz                blue box 8

*******************************************************/

public class ToneDetector {

 /**

  * Период времени, в течение которого производится оценка.

  * Длительность символов в последовательности должна быть

  * как минимум в 2 раза больше.

  * Приемлемый диапазон: 10 ... 100 мс

  */

 private static final byte BUFFER_MS = 20;

 /**

  * мощность сигнала. параметр и вычисления с ним не нужны,

  * если минимальный уровень отсекается порогом.

  */

 private float m_inPower = 0.0f;

 private float m_threshold = 0.5f;// порог отсечения

 private boolean m_bSingleTone = false;// однотональный/двутональный детектор

 private int m_counter = 0;// счётчик обработанных сэмплов

 private int m_decodeLength = 0;// число сэмплов на заданную длину символа

 private float[] m_pV1 = null;// первый шаг задержки

 private float[] m_pV2 = null;// второй шаг задержки

 private float[] m_pCoeff = null;// коэффициенты Герцеля

 

 public ToneDetector() {

 }

 

 boolean initDTMF( int sampleRate ) {

         int[] pFrequency = new int[8];

         pFrequency[0] = 697;

         pFrequency[1] = 770;

         pFrequency[2] = 852;

         pFrequency[3] = 941;

         pFrequency[4] = 1209;

         pFrequency[5] = 1336;

         pFrequency[6] = 1477;

         pFrequency[7] = 1633;

         boolean res = initSTMF( sampleRate, pFrequency );

         m_bSingleTone = false;

         return res;

 }

 

 boolean initSTMF( int sampleRate, final int[] pFrequency ) {

         m_bSingleTone = true;

         m_pV1 = new float[pFrequency.length];

         m_pV2 = new float[pFrequency.length];

         m_pCoeff = new float[pFrequency.length];

         m_inPower = 0.0f;

         m_decodeLength = (int)(BUFFER_MS * sampleRate / 1000);

         m_counter = 0;

         for( int f = pFrequency.length; f-- != 0; ) {

                 m_pCoeff[f] = (float)(2.0 * Math.cos( 2.0 * Math.PI * pFrequency[f] / sampleRate));

                 m_pV1[f] = 0.0f;

                 m_pV2[f] = 0.0f;

         }

         return true;

 }

 /************************************************

 установить порог

 0.00 < threshold < 1.00

 ************************************************/

 void setThreshold( float threshold ) {

         // расчитываем квадрат амплитуды, поэтому

         // используем квадрат порога

         m_threshold = threshold * threshold;

 }

 

 //frhigh   1209  1336  1477  1633

 //frlow

 //    697   1     2     3     A

 //    770   4     5     6     B

 //    852   7     8     9     C

 //    941   *     0     #     D

 private static final byte decodeDTMF[] = {

                 '1', '2', '3', 'A',

                 '4', '5', '6', 'B',

                 '7', '8', '9', 'C',

                 '*', '0', '#', 'D' };

 

 byte process(short sample) {

         for( int f = m_pCoeff.length; f-- != 0; ) {

                 float t = m_pV1[f];

                 m_pV1[f] = (float)sample + m_pCoeff[f] * m_pV1[f] - m_pV2[f];

                 m_pV2[f] = t;

                 m_inPower += (float)sample * (float)sample;// если используется

         }

         if( ++m_counter < m_decodeLength ) return 0;

         m_counter = 0;

         byte code = 0;

         if( m_inPower > 0.0f ) {

                 int firstFr = 0;

                 int secondFr = 0;

                 float max = -1.0f;

                 int f = 0;

                 for( ; f < (m_pCoeff.length / 2); f++ ) {

                         float res = m_pV1[f] * m_pV1[f] + m_pV2[f] * m_pV2[f] -

                                                 m_pCoeff[f] * m_pV1[f] * m_pV2[f];

                         if( max < res ) {

                                 max = res;

                                 firstFr = f;

                         }

                         m_pV1[f] = 0.0f;

                         m_pV2[f] = 0.0f;

                 }

                 // нормировка для сравнения с порогом

                 /// если регулировка уровня порогом

                 // 32767 - максимальное значение для 16-ти разрядного звука

                 // max = max / m_decodeLength / m_decodeLength * 4.0f / (32767 * 32767);

                 /// если используется мощность

                 // мощность образца = (sqrt(2)/2)^2 * m_decodeLength

                 max = max / m_inPower / m_decodeLength * 16.0f;

                 if( max < m_threshold ) firstFr = m_pCoeff.length;

                 if( !m_bSingleTone ) max = -1.0f;

                 for( ; f < m_pCoeff.length; f++ ) {

                         float res = m_pV1[f] * m_pV1[f] + m_pV2[f] * m_pV2[f] -

                                                 m_pCoeff[f] * m_pV1[f] * m_pV2[f];

                         if( max < res ) {

                                 max = res;

                                 secondFr = f;

                         }

                         m_pV1[f] = 0.0f;

                         m_pV2[f] = 0.0f;

                 }

                 // нормировка для сравнения с порогом

                 /// если регулировка уровня порогом

                 // 32767 - максимальное значение для 16-ти разрядного звука

                 // max = max / m_decodeLength / m_decodeLength * 4.0f / (32767 * 32767);

                 /// если используется мощность

                 // мощность образца = (sqrt(2)/2)^2 * m_decodeLength

                 max = max / m_inPower / m_decodeLength * 16.0f;

                 if( max < m_threshold ) secondFr = m_pCoeff.length;

                 if( m_bSingleTone ) {

                         /**

                          * для уменьшения вероятности ложных срабатываний здесь имеет смысл добавить

                          * дополнительную проверку:

                          * - уровень обнаруженного сигнала должен сильно превосходить остальные

                          */

                         if( secondFr < m_pCoeff.length )

                                 code = (byte)('1' + secondFr);

                         else if( firstFr < m_pCoeff.length )

                                 code = (byte)('1' + firstFr);

                 } else {

                         /**

                          * для уменьшения вероятности ложных срабатываний здесь имеет смысл добавить

                          * дополнительные проверки:

                          * - уровни обнаруженных сигналов не должны сильно отличаться

                          * - остальные сигналы должны быть по уровню сильно меньше основных

                          */

                         if( (firstFr < m_pCoeff.length) && (secondFr < m_pCoeff.length) )

                                 code = decodeDTMF[secondFr - (m_pCoeff.length / 2) +

                                                   firstFr * (m_pCoeff.length / 2)];

                 }

                 m_inPower = 0.0f;

         }

         return code;

 }

}

 

Предыдущая  Содержание  Следующая