Детектирование DTMF |
Предыдущая Содержание Следующая |
|
Для детектирования 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; } }
|
Предыдущая Содержание Следующая |