[ DTMF 雙音辨號 ] 解碼, 到底甚麼是 [ 頻域 Frequency Domain ] 和 [ 時域 Time Domain ]


http://wp.me/ph3BR-QO

nEO_IMG_img158

這個題目, 對於沒有學過的俺, 確實是很大的挑戰, 不過呢, 今日 INTERNET 和 GOOGLE, 比起圖書館厲害太多了.

首先, 學習新的東西, 主要是明白基本原理.

那先來了解一下最基本的

[ DTMF 雙音辨號 ] – 是什麼呢 ? 今天的電話, 大多都是按鍵式, 每按一個鍵, 都會聽到的那個 [ 嘀嘀嘟嘟 ] 的聲音, 就是 DTMF

[ 頻域 Frequency Domain ] – 從頻率分佈的角度看一個(或一堆)訊號  (俺看完後, 理解後劃了一個圖, 後來在 WIKI 上看到, 果然是差不多的意思)

[ 時域 Time Domain ] – 從時間的過程看一個(或一堆)訊號 (理解後劃了一個圖, 後來在 WIKI 上看到, 果然是差不多的意思)

為什麼要學這個 ? 因為需要偵測電話裡面的聲音, 到底有沒有按鍵的指示, 所以要學習如何偵測這個 DTMF 訊號. 所以就學到了一個 名詞 DFT (Discrete Fourier Transform)

有源碼的就兩個地方, 其中一個是祖國的同胞 hendry 在 2008年發表的, 他聲明不能商用, 另外一個是老外用 PIC 寫的, 都是理論結合實際的好材料, 看完這些, 基本上是學會了基本, 而且軟件和硬件的教材都具備了, 不過還是需要時間試驗和加深理解, 類似上次 AT89C2051, software Sigma-Delta ADC 實驗, 神奇, 數學是關鍵, 但到底那些人是怎樣想到這些原理的呢 !?


http://cache.ourdev.cn/bbs_upload782111/files_12/ourdev_421329.mht

可以解碼的,方法如下:
波形-->A/D採樣-->8頻率點傅立葉變換-->選取最大兩個頻率份量組合編碼-->查表,得DTMF解碼結果
我選取的參數如下:

 1) A/D 採樣頻率8KHz,分辨率 8bit
 2) 參與傅立葉變換的樣本個數(轉換窗口寬度) = 50 samples (採樣點)
 3) 8個頻率點:697Hz, 770, 852, 941, 1209, 1336, 1477, 1633Hz
 4) 傅立葉變換所需的三角函數運算變成查表,表格大小 = 50 * 8 = 400 字節
 5) atmega8 @ 8MHz

實測結果:

 1) 信號動態適應範圍 <= 42dB
 2) 頻偏容忍度 <= +/- 10%
 3) 誤碼率 < 1%,結合軟件判斷,誤碼率將為0 對於樓上疑問解釋一下: 1)8KHz 採樣率,50個採樣點,花費時間 = 0.125ms * 50 = 6.25ms 2) 為了保證 50 個點至少採一個完整週期,信號週期必須不大於 6.25ms, 即頻率必須不小於 1/6.25ms = 160Hz 3) 低於 160Hz 的信號,50 個點都採不到一個完整週期,因此無法保證傅立葉計算的正確性 4) 實際上,在 50 個採樣點內,上述 8 個頻率信號分別可以采 4.4, 4.8, 5.3, 5.9, 7.6, 8.4, 9.2, 10.2 個週期,因此可以保證傅立葉計算正確性 5) 實際表明,採到 40 個點時,仍可以工作,採到 30 個點時,就不可靠了(無法適應兩兩頻率的任意相位組合),最後選擇了 50 點,保證了應用的可靠性 6) 當然,採樣點越多,計算就越精確,但是,1是沒有必要,因為只需要相對把 8個頻率點區分開就可以了,沒有必要真正計算其準確頻譜,2是單片機資源有限,採樣點越多,存儲開銷和計算開銷就越大,最後不能實現。 7)對於其他電話信令,例如忙音、撥號音、提示音等等,均是固定頻率信號或固定頻率信號的復合,可以採用相同思路解碼。希望對於做遠程電話控制的朋友有所啟發 4) 實際上,在 50 個採樣點內,上述 8 個頻率信號分別可以采 4.4, 4.8, 5.3, 5.9, 7.6, 8.4, 9.2, 10.2 個週期,因此可以保證傅立葉計算正確性 市面上所售比較便宜的 DTMF 解碼芯片,都是軟解碼,內部就一個小單片機。 實際證明,軟解碼方法比硬解碼(硬件帶通濾波)效果更好,適應性更強,成本也更低。 明白了軟解碼的原理,就不需要外置這些解碼芯片了,從而降低成本,降低功耗,提高集成度和可靠性,採購也簡單了。 10樓:是的。 經我測試, 軟解碼抗干擾能力很強,我試過了在通話過程中,用音箱播放 DTMF 雙音頻錄音,音箱距離電話1米開外,音量中等,DTMF 音從聽筒透進電話,軟解碼能正確解出,這期間我一直在說話。 另外,當 DTMF 波形嚴重削頂失真(幅值飽和)時,也能正確解出號碼,這令我很驚訝(因為我用的 A/D 是 8bit,信號稍強就飽和失真)。後來想明白了,因為軟件解碼是基於信號的頻譜,而不是波形,信號失真後,從頻譜上看只是諧波份量增加了,但是基波份量還是存在的,軟件照樣能解出,如果用基於波形的解碼算法,肯定就不行了!從這點可以看出,軟解碼方法的穩定性。 當然了,要全面測試軟解碼指標,還要用電話測試儀,不過我手頭沒有,所以也沒有做。 下面貼代碼,mega8 @ 8MHz 通過。 注意:1) 採樣率 4000 bps(採樣率很低,降低對系統要求,解碼準確度一點不含糊,(*^__^*) 嘻嘻……) 2) 採樣精度 8bit, signed char, 注意是 「有符號數」啊!! 千萬別搞錯了, 如果輸入無符號數,哭去吧.. 3) 每輸入 50 個採樣點就得到一次 DTMF 解碼,歷時 12.5ms, 國標要求 DTMF 碼持續時間 >= 40ms, 因此連續解碼的話可以得到連續3個相同碼值,實際上,我就是這樣提高抗干擾性的。
 4) 屬於本人原創,請勿用作商用,否則保留追究權利。

by hendry, 2008/11
//
// DTMF 解碼程序
// author: hendry
// date: 2008/11
//

unsigned char s_dtmf_p;
long s_dtmf_real[8];
long s_dtmf_image[8];

const unsigned char g_sincos_len = 50;
const signed char g_cos_table[400] = {(signed char)0x7E,(signed char)0x3A,(signed char)0xB7,(signed char)0x83,(signed char)0xD7,(signed char)0x57,(signed char)0x79,(signed char)0x18,(signed char)0x9D,(signed char)0x8D,(signed char)0xFA,(signed char)0x6D,(signed char)0x6A,(signed char)0xF4,(signed char)0x8B,(signed char)0xA1,(signed char)0x1E,(signed char)0x7A,(signed char)0x52,(signed char)0xD1,(signed char)0x83,(signed char)0xBC,(signed char)0x3F,(signed char)0x7E,(signed char)0x34,(signed char)0xB2,(signed char)0x84,(signed char)0xDD,(signed char)0x5B,(signed char)0x77,(signed char)0x12,(signed char)0x99,(signed char)0x90,(signed char)0x00,(signed char)0x70,(signed char)0x67,(signed char)0xEE,(signed char)0x89,(signed char)0xA5,(signed char)0x24,
(signed char)0x7C,(signed char)0x4E,(signed char)0xCB,(signed char)0x82,(signed char)0xC1,(signed char)0x44,(signed char)0x7D,(signed char)0x2F,(signed char)0xAD,(signed char)0x86,(signed char)0x7E,(signed char)0x2D,(signed char)0xA1,(signed char)0x91,(signed char)0x10,(signed char)0x7B,(signed char)0x47,(signed char)0xB8,(signed char)0x86,(signed char)0xF2,(signed char)0x70,(signed char)0x5D,(signed char)0xD2,(signed char)0x82,(signed char)0xD5,(signed char)0x60,(signed char)0x6E,(signed char)0xEE,(signed char)0x85,(signed char)0xBB,(signed char)0x4A,(signed char)0x7A,(signed char)0x0C,(signed char)0x8F,(signed char)0xA4,(signed char)0x30,(signed char)0x7E,(signed char)0x29,(signed char)0x9F,(signed char)0x93,
(signed char)0x14,(signed char)0x7B,(signed char)0x44,(signed char)0xB4,(signed char)0x87,(signed char)0xF6,(signed char)0x72,(signed char)0x5A,(signed char)0xCE,(signed char)0x82,(signed char)0xD9,(signed char)0x62,(signed char)0x6C,(signed char)0xEA,(signed char)0x84,(signed char)0xBE,(signed char)0x4D,(signed char)0x78,(signed char)0x08,(signed char)0x8D,(signed char)0x7E,(signed char)0x1D,(signed char)0x8F,(signed char)0xAF,(signed char)0x4B,(signed char)0x74,(signed char)0xEA,(signed char)0x82,(signed char)0xDC,(signed char)0x6D,(signed char)0x56,(signed char)0xBA,(signed char)0x8A,(signed char)0x0F,(signed char)0x7D,(signed char)0x2B,(signed char)0x96,(signed char)0xA5,(signed char)0x3F,(signed char)0x79,
(signed char)0xF8,(signed char)0x84,(signed char)0xCF,(signed char)0x65,(signed char)0x60,(signed char)0xC7,(signed char)0x86,(signed char)0x01,(signed char)0x7B,(signed char)0x38,(signed char)0x9F,(signed char)0x9B,(signed char)0x33,(signed char)0x7C,(signed char)0x06,(signed char)0x87,(signed char)0xC2,(signed char)0x5C,(signed char)0x69,(signed char)0xD4,(signed char)0x83,(signed char)0xF3,(signed char)0x77,(signed char)0x44,(signed char)0xA9,(signed char)0x94,(signed char)0x25,(signed char)0x7E,(signed char)0x14,(signed char)0x8C,(signed char)0x7E,(signed char)0x0C,(signed char)0x84,(signed char)0xDD,(signed char)0x75,(signed char)0x38,(signed char)0x95,(signed char)0xB4,(signed char)0x5D,(signed char)0x5D,
(signed char)0xB4,(signed char)0x95,(signed char)0x38,(signed char)0x76,(signed char)0xDE,(signed char)0x84,(signed char)0x0B,(signed char)0x7E,(signed char)0x0C,(signed char)0x84,(signed char)0xDD,(signed char)0x75,(signed char)0x39,(signed char)0x95,(signed char)0xB3,(signed char)0x5D,(signed char)0x5E,(signed char)0xB5,(signed char)0x94,(signed char)0x37,(signed char)0x76,(signed char)0xDF,(signed char)0x84,(signed char)0x0A,(signed char)0x7E,(signed char)0x0D,(signed char)0x84,(signed char)0xDC,(signed char)0x75,(signed char)0x39,(signed char)0x96,(signed char)0xB3,(signed char)0x5C,(signed char)0x5E,(signed char)0xB5,(signed char)0x94,(signed char)0x37,(signed char)0x76,(signed char)0xDF,(signed char)0x84,
(signed char)0x7E,(signed char)0xD7,(signed char)0x9C,(signed char)0x69,(signed char)0x20,(signed char)0x82,(signed char)0x31,(signed char)0x5E,(signed char)0x92,(signed char)0xE9,(signed char)0x7D,(signed char)0xC7,(signed char)0xA8,(signed char)0x72,(signed char)0x0F,(signed char)0x85,(signed char)0x41,(signed char)0x51,(signed char)0x8B,(signed char)0xFA,(signed char)0x79,(signed char)0xB8,(signed char)0xB6,(signed char)0x78,(signed char)0xFD,(signed char)0x8A,(signed char)0x4F,(signed char)0x43,(signed char)0x85,(signed char)0x0C,(signed char)0x73,(signed char)0xAA,(signed char)0xC5,(signed char)0x7C,(signed char)0xEB,(signed char)0x91,(signed char)0x5C,(signed char)0x33,(signed char)0x83,(signed char)0x1E,
(signed char)0x6A,(signed char)0x9E,(signed char)0xD5,(signed char)0x7E,(signed char)0xDA,(signed char)0x9B,(signed char)0x68,(signed char)0x23,(signed char)0x82,(signed char)0x2F,(signed char)0x7E,(signed char)0xC1,(signed char)0xC2,(signed char)0x7E,(signed char)0xBF,(signed char)0xC3,(signed char)0x7E,(signed char)0xBE,(signed char)0xC5,(signed char)0x7E,(signed char)0xBC,(signed char)0xC6,(signed char)0x7E,(signed char)0xBB,(signed char)0xC8,(signed char)0x7E,(signed char)0xBA,(signed char)0xC9,(signed char)0x7E,(signed char)0xB9,(signed char)0xCA,(signed char)0x7E,(signed char)0xB7,(signed char)0xCC,(signed char)0x7D,(signed char)0xB6,(signed char)0xCD,(signed char)0x7D,(signed char)0xB5,(signed char)0xCF,
(signed char)0x7D,(signed char)0xB3,(signed char)0xD0,(signed char)0x7D,(signed char)0xB2,(signed char)0xD2,(signed char)0x7D,(signed char)0xB1,(signed char)0xD3,(signed char)0x7C,(signed char)0xB0,(signed char)0xD5,(signed char)0x7C,(signed char)0xAE,(signed char)0xD6,(signed char)0x7C,(signed char)0xAD,(signed char)0xD8,(signed char)0x7B,(signed char)0xAC,(signed char)0x7E,(signed char)0xAA,(signed char)0xF7,(signed char)0x62,(signed char)0x83,(signed char)0x48,(signed char)0x1B,(signed char)0x93,(signed char)0x79,(signed char)0xC8,(signed char)0xD3,(signed char)0x75,(signed char)0x8E,(signed char)0x27,(signed char)0x3D,(signed char)0x86,(signed char)0x6A,(signed char)0xEB,(signed char)0xB4,(signed char)0x7D,
(signed char)0xA1,(signed char)0x03,(signed char)0x5A,(signed char)0x82,(signed char)0x52,(signed char)0x0F,(signed char)0x9A,(signed char)0x7C,(signed char)0xBD,(signed char)0xDF,(signed char)0x6F,(signed char)0x89,(signed char)0x33,(signed char)0x32,(signed char)0x89,(signed char)0x70,(signed char)0xDE,(signed char)0xBE,(signed char)0x7C,(signed char)0x9A,(signed char)0x10,(signed char)0x51,(signed char)0x82,(signed char)0x5B,(signed char)0x02,(signed char)0xA2,(signed char)0x7D,(signed char)0xB3,(signed char)0xEC,(signed char)0x69,(signed char)0x7E,(signed char)0x96,(signed char)0x33,(signed char)0x14,(signed char)0xAB,(signed char)0x7A,(signed char)0x88,(signed char)0x4F,(signed char)0xF3,(signed char)0xC6,
(signed char)0x6D,(signed char)0x82,(signed char)0x65,(signed char)0xD4,(signed char)0xE5,(signed char)0x5A,(signed char)0x85,(signed char)0x75,(signed char)0xB7,(signed char)0x05,(signed char)0x40,(signed char)0x8F,(signed char)0x7D,(signed char)0x9F,(signed char)0x25,(signed char)0x22,(signed char)0xA1,(signed char)0x7D,(signed char)0x8E,(signed char)0x43,(signed char)0x02,(signed char)0xBA,(signed char)0x74,(signed char)0x84,(signed char)0x5C,(signed char)0xE2,(signed char)0xD7,(signed char)0x63,(signed char)0x82,(signed char)0x6F,(signed char)0xC3,(signed char)0xF7,(signed char)0x4C,(signed char)0x89,(signed char)0x7B,(signed char)0xA9,(signed char)0x17,(signed char)0x30,(signed char)0x98,(signed char)0x7E
};
const signed char g_sin_table[400] = {(signed char)0x00,(signed char)0x70,(signed char)0x67,(signed char)0xEE,(signed char)0x89,(signed char)0xA5,(signed char)0x24,(signed char)0x7C,(signed char)0x4E,(signed char)0xCC,(signed char)0x82,(signed char)0xC1,(signed char)0x44,(signed char)0x7D,(signed char)0x2F,(signed char)0xAD,(signed char)0x86,(signed char)0xE2,(signed char)0x5F,(signed char)0x75,(signed char)0x0C,(signed char)0x96,(signed char)0x93,(signed char)0x06,(signed char)0x73,(signed char)0x63,(signed char)0xE8,(signed char)0x87,(signed char)0xA9,(signed char)0x29,(signed char)0x7D,(signed char)0x49,(signed char)0xC6,(signed char)0x82,(signed char)0xC6,(signed char)0x49,(signed char)0x7D,(signed char)0x29,(signed char)0xA9,(signed char)0x87,
(signed char)0xE8,(signed char)0x63,(signed char)0x73,(signed char)0x06,(signed char)0x93,(signed char)0x96,(signed char)0x0C,(signed char)0x75,(signed char)0x5F,(signed char)0xE2,(signed char)0x00,(signed char)0x76,(signed char)0x53,(signed char)0xC5,(signed char)0x83,(signed char)0xE3,(signed char)0x68,(signed char)0x67,(signed char)0xE1,(signed char)0x83,(signed char)0xC7,(signed char)0x55,(signed char)0x75,(signed char)0xFE,(signed char)0x89,(signed char)0xAE,(signed char)0x3D,(signed char)0x7D,(signed char)0x1B,(signed char)0x97,(signed char)0x9A,(signed char)0x21,(signed char)0x7D,(signed char)0x37,(signed char)0xAA,(signed char)0x8C,(signed char)0x04,(signed char)0x77,(signed char)0x50,(signed char)0xC2,
(signed char)0x84,(signed char)0xE6,(signed char)0x6A,(signed char)0x65,(signed char)0xDD,(signed char)0x82,(signed char)0xCA,(signed char)0x58,(signed char)0x74,(signed char)0xFA,(signed char)0x88,(signed char)0xB1,(signed char)0x40,(signed char)0x7C,(signed char)0x18,(signed char)0x95,(signed char)0x9C,(signed char)0x25,(signed char)0x7E,(signed char)0x34,(signed char)0x00,(signed char)0x7B,(signed char)0x38,(signed char)0x9F,(signed char)0x9B,(signed char)0x32,(signed char)0x7C,(signed char)0x07,(signed char)0x87,(signed char)0xC1,(signed char)0x5C,(signed char)0x69,(signed char)0xD5,(signed char)0x83,(signed char)0xF2,(signed char)0x77,(signed char)0x45,(signed char)0xA9,(signed char)0x93,(signed char)0x25,
(signed char)0x7E,(signed char)0x15,(signed char)0x8C,(signed char)0xB5,(signed char)0x52,(signed char)0x70,(signed char)0xE2,(signed char)0x82,(signed char)0xE4,(signed char)0x71,(signed char)0x50,(signed char)0xB4,(signed char)0x8D,(signed char)0x17,(signed char)0x7E,(signed char)0x23,(signed char)0x92,(signed char)0xAA,(signed char)0x46,(signed char)0x76,(signed char)0xF0,(signed char)0x83,(signed char)0xD6,(signed char)0x6A,(signed char)0x5B,(signed char)0xC0,(signed char)0x88,(signed char)0x09,(signed char)0x7C,(signed char)0x31,(signed char)0x00,(signed char)0x7D,(signed char)0x17,(signed char)0x87,(signed char)0xD2,(signed char)0x71,(signed char)0x43,(signed char)0x9C,(signed char)0xAB,(signed char)0x55,
(signed char)0x65,(signed char)0xBE,(signed char)0x8F,(signed char)0x2D,(signed char)0x79,(signed char)0xE9,(signed char)0x82,(signed char)0xFF,(signed char)0x7D,(signed char)0x18,(signed char)0x87,(signed char)0xD2,(signed char)0x70,(signed char)0x43,(signed char)0x9C,(signed char)0xAA,(signed char)0x54,(signed char)0x65,(signed char)0xBF,(signed char)0x8F,(signed char)0x2D,(signed char)0x79,(signed char)0xEA,(signed char)0x82,(signed char)0xFF,(signed char)0x7D,(signed char)0x18,(signed char)0x87,(signed char)0xD1,(signed char)0x70,(signed char)0x44,(signed char)0x9C,(signed char)0xAA,(signed char)0x54,(signed char)0x65,(signed char)0xBF,(signed char)0x8F,(signed char)0x2C,(signed char)0x7A,(signed char)0xEB,
(signed char)0x00,(signed char)0x77,(signed char)0xB3,(signed char)0xBA,(signed char)0x7A,(signed char)0xF7,(signed char)0x8C,(signed char)0x54,(signed char)0x3E,(signed char)0x84,(signed char)0x12,(signed char)0x70,(signed char)0xA6,(signed char)0xCA,(signed char)0x7D,(signed char)0xE5,(signed char)0x94,(signed char)0x60,(signed char)0x2E,(signed char)0x82,(signed char)0x23,(signed char)0x67,(signed char)0x9A,(signed char)0xDA,(signed char)0x7E,(signed char)0xD4,(signed char)0x9E,(signed char)0x6B,(signed char)0x1D,(signed char)0x83,(signed char)0x34,(signed char)0x5C,(signed char)0x91,(signed char)0xEC,(signed char)0x7C,(signed char)0xC4,(signed char)0xAA,(signed char)0x73,(signed char)0x0B,(signed char)0x86,
(signed char)0x44,(signed char)0x4F,(signed char)0x8A,(signed char)0xFD,(signed char)0x78,(signed char)0xB5,(signed char)0xB8,(signed char)0x79,(signed char)0xFA,(signed char)0x8B,(signed char)0x00,(signed char)0x6D,(signed char)0x92,(signed char)0x02,(signed char)0x6C,(signed char)0x92,(signed char)0x03,(signed char)0x6B,(signed char)0x91,(signed char)0x05,(signed char)0x6A,(signed char)0x90,(signed char)0x06,(signed char)0x6A,(signed char)0x8F,(signed char)0x08,(signed char)0x69,(signed char)0x8F,(signed char)0x09,(signed char)0x68,(signed char)0x8E,(signed char)0x0B,(signed char)0x67,(signed char)0x8D,(signed char)0x0D,(signed char)0x66,(signed char)0x8D,(signed char)0x0E,(signed char)0x65,(signed char)0x8C,
(signed char)0x10,(signed char)0x64,(signed char)0x8B,(signed char)0x11,(signed char)0x63,(signed char)0x8B,(signed char)0x13,(signed char)0x62,(signed char)0x8A,(signed char)0x14,(signed char)0x61,(signed char)0x8A,(signed char)0x16,(signed char)0x60,(signed char)0x89,(signed char)0x18,(signed char)0x5F,(signed char)0x89,(signed char)0x19,(signed char)0x5E,(signed char)0x00,(signed char)0x5C,(signed char)0x82,(signed char)0x4F,(signed char)0x12,(signed char)0x98,(signed char)0x7B,(signed char)0xC0,(signed char)0xDC,(signed char)0x71,(signed char)0x8A,(signed char)0x30,(signed char)0x35,(signed char)0x88,(signed char)0x6E,(signed char)0xE2,(signed char)0xBB,(signed char)0x7C,(signed char)0x9C,(signed char)0x0C,
(signed char)0x53,(signed char)0x82,(signed char)0x58,(signed char)0x06,(signed char)0xA0,(signed char)0x7D,(signed char)0xB6,(signed char)0xE8,(signed char)0x6B,(signed char)0x86,(signed char)0x3B,(signed char)0x29,(signed char)0x8D,(signed char)0x74,(signed char)0xD6,(signed char)0xC6,(signed char)0x79,(signed char)0x95,(signed char)0x19,(signed char)0x4A,(signed char)0x83,(signed char)0x61,(signed char)0xF9,(signed char)0xA8,(signed char)0x7E,(signed char)0xAC,(signed char)0xF5,(signed char)0x64,(signed char)0x84,(signed char)0x46,(signed char)0x00,(signed char)0x45,(signed char)0x8D,(signed char)0x7C,(signed char)0xA3,(signed char)0x20,(signed char)0x27,(signed char)0x9E,(signed char)0x7D,(signed char)0x90,
(signed char)0x3E,(signed char)0x07,(signed char)0xB5,(signed char)0x76,(signed char)0x85,(signed char)0x58,(signed char)0xE7,(signed char)0xD2,(signed char)0x67,(signed char)0x82,(signed char)0x6C,(signed char)0xC8,(signed char)0xF1,(signed char)0x50,(signed char)0x88,(signed char)0x79,(signed char)0xAD,(signed char)0x12,(signed char)0x35,(signed char)0x95,(signed char)0x7E,(signed char)0x97,(signed char)0x31,(signed char)0x16,(signed char)0xAA,(signed char)0x7A,(signed char)0x89,(signed char)0x4D,(signed char)0xF5,(signed char)0xC5,(signed char)0x6E,(signed char)0x82,(signed char)0x64,(signed char)0xD6,(signed char)0xE3,(signed char)0x5B,(signed char)0x84,(signed char)0x74,(signed char)0xB9,(signed char)0x03
};

//
// dtmf decode initiation
//
void init_dtmf_decoder()
{
 s_dtmf_p = 0;
 memset(s_dtmf_real, 0, sizeof(long) * 8);
 memset(s_dtmf_image, 0, sizeof(long) * 8);
}

//
// dtmf decode program
//
// input: sample = DTMF wave sample value,
// sample rate = 4000 point per second
// NOTE!! sample is "SIGNED" char, NOT "UNSIGNED" char !!
// output: 0 = decoded not completed yet
// '0123456789*#ABCD' = dtmf code
//
unsigned char decode_dtmf(signed char sample)
{
 unsigned char i, low_freq, high_freq;
 unsigned short offset;

 for (i = 0, offset = s_dtmf_p; i < 8; i ++, offset += g_sincos_len)
 {
 s_dtmf_real[i] += sample * g_cos_table[offset];
 s_dtmf_image[i] += sample * g_sin_table[offset];
 }

 s_dtmf_p ++;

 if (s_dtmf_p == g_sincos_len) // 50 sample has inputed, let's look at the result...
 {
 // calculate |R| + |I|
 for (i = 0; i < 8; i ++)
 {
 s_dtmf_real[i] = labs(s_dtmf_real[i]) + labs(s_dtmf_image[i]);
 }

 // find max value of low freq
 for (low_freq = 0, i = 1; i < 4; i ++) { if (s_dtmf_real[i] > s_dtmf_real[low_freq])
 {
 low_freq = i;
 }
 }

 // find max value of high freq
 for (high_freq = 4, i = 5; i < 8; i ++) { if (s_dtmf_real[i] > s_dtmf_real[high_freq])
 {
 high_freq = i;
 }
 }

 // clean up for continuous decoding
 s_dtmf_p = 0;
 memset(s_dtmf_real, 0, 32);
 memset(s_dtmf_image, 0, 32);

 // combine low and high frequency and decode out dtmf code
 if ((low_freq == 0) && (high_freq == 4))
 {
 return '1';
 }
 else if ((low_freq == 0) && (high_freq == 5))
 {
 return '2';
 }
 else if ((low_freq == 0) && (high_freq == 6))
 {
 return '3';
 }
 else if ((low_freq == 0) && (high_freq == 7))
 {
 return 'A';
 }
 else if ((low_freq == 1) && (high_freq == 4))
 {
 return '4';
 }
 else if ((low_freq == 1) && (high_freq == 5))
 {
 return '5';
 }
 else if ((low_freq == 1) && (high_freq == 6))
 {
 return '6';
 }
 else if ((low_freq == 1) && (high_freq == 7))
 {
 return 'B';
 }
 else if ((low_freq == 2) && (high_freq == 4))
 {
 return '7';
 }
 else if ((low_freq == 2) && (high_freq == 5))
 {
 return '8';
 }
 else if ((low_freq == 2) && (high_freq == 6))
 {
 return '9';
 }
 else if ((low_freq == 2) && (high_freq == 7))
 {
 return 'C';
 }
 else if ((low_freq == 3) && (high_freq == 4))
 {
 return '*';
 }
 else if ((low_freq == 3) && (high_freq == 5))
 {
 return '0';
 }
 else if ((low_freq == 3) && (high_freq == 6))
 {
 return '#';
 }
 else if ((low_freq == 3) && (high_freq == 7))
 {
 return 'D';
 }
 }

 return 0;
}

 

 

今天先記這些….待繼續

DTMF
http://www.mathworks.com/products/demos/signaltlbx/dtmf/dtmfdemo.html

A DISCRETE FOURIER TRANSFORM BASED DIGITAL DTMF DETECTION ALGORITHM
http://www.rootsecure.net/content/downloads/pdf/paper_dtmf.pdf

DTMF – Decoding with a 1-bit A/D converter
http://www.dattalo.com/technical/theory/dtmf.html
https://web.archive.org/web/20131018125259/http://www.dattalo.com/technical/theory/dtmf.html

Efficiently detecting a frequency using a Goertzel filter
http://netwerkt.wordpress.com/2011/08/25/goertzel-filter/

求AVR用DFT軟解碼DTMF信號的那個帖子(amoBBS 阿莫電子論壇)
http://www.amobbs.com/thread-3213853-1-1.html

DTMF Signal Detection Using Z8 Encore! XP F64xx Series MCUs
http://www.zilog.com/docs/appnotes/an0335.pdf

Black DTMF Decoding Algorithm
http://www.romanblack.com/DTMF/DTMF_alg.htm

.

.

.

2013-04-20

繼續學習以下, 看這篇是比較有趣的 Scott Dattalo, 1999年12月 (DTMF – Decoding with a 1-bit A/D converter, http://www.dattalo.com/technical/theory/dtmf.html), 所以看了第二次, 希望能夠完全理解這個老外所描述的原理, 有些地方看不明白, 用 GOOGLE 的翻譯, 在加上俺的理解, 它的字面意義如下, 須要的話會逐步修正或修改.

http://www.dattalo.com/technical/theory/dtmf.html

2015-09-18, 需要用到再重溫,上面原作者的網頁移除了
所以動用一下 WEBARCHIVE, 找回這個網頁的歷史資料, 趕快存檔日後參考
https://web.archive.org/web/20131018125259/http://www.dattalo.com/technical/theory/dtmf.html

DTMF – 用 1 BIT A/D 轉換器解碼

DTMF 解碼的目的是為了監測聲音訊號裡面有沒有特定頻率的正弦波信號存在. 有很多便宜市售的 IC 有同樣的功能。 通常, DTMF 解碼器 IC 與微控制器拼湊使用. 因此, 為何不直接使用單片機解碼呢 ? 那麼答案是因為典型的基於微控制器的解碼器需要一個 A/D 轉換器. 此外, 信號處理與解碼相關可能會讓微控制器相當吃力.  因此,以往設計師被迫使用專用 DTMF 解碼 IC 或使用更高級的微控制器或更昂貴的數字信號處理器.

但是, 有另一種方法來解碼 DTMF信號, 使用微控制器和一個 1-bit A/D 轉換器(即比較器). 該理論是相當類似上面提到的 "慣用" 的信號處理技術. 所以, 在繼續之前, 讓我們簡要地考慮所涉及的範疇.

用蠻力的方法來檢測 DTMF 信號是使輸入模擬信號數字化, 並計算 DTMF 的 8 個頻率訊號的 DFT (離散傅立葉變換). 選用 DFT 而非 FFT 是因為頻率不等距(事實上, 他們是對數間隔). DFT 的計算式簡單描述如下:

N
—-
DFT(x) = \ x(k) * W(k)
/
—-

其中
x(K)的時間樣本
W(k)是臭名昭著的核函數 :

W(k) = e^(j*2*pi*f*k/N)
= cos(2*pi*f*k/N) + j*sin(2*pi*f*k/N)

這公式的意思是, 時間樣本的數值, 乘弦波和餘弦波, 並將它們加總. 計算完成將得到8個複數. 這些數字的大小告訴我們,對每個頻率的輸入信號存在大約多少能量. 換句話說,我們是計算 8 DTMF 訊號頻率的頻譜.

這個計算方法運作良好的原因是因為 "正交" 的正弦波. 換句話說, 如果您執行兩個正弦波 DFT 變換:

—-
DFT = \ sin(f_1*t) * sin(f_2*t)
/
—-

如果兩個頻率是相同的, 你會得到一個 "大" 計算值, 如果它們是不同會得到一個 "小" 計算值或 0.

俺對以上的數學描述是完全沒聽過的, 所以真的不知道原作者寫的是啥. 但是如果有相關基礎, 可能覺得沒什麼重要的東西.

其實, 原作者的目的是試圖做的是展示如何執行 DFT的計算概念 (原理) 並可以推廣到非正弦信號的計算, 尤其是方波 (square wave)

對方波進行 "DFT" (離散傅立葉變換):

正交的概念同樣適用於方波. 事實上, 它甚至容易說明用 ASCII 字符劃成圖畫! 考慮的兩個例子:

Ex 1
+—-+ +—-+ +—-+ +—-+ +—-+
| | | | | | | | | |
–+ +—-+ +—-+ +—-+ +—-+ +—

+—-+ +—-+ +—-+ +—-+ +—-+
| | | | | | | | | |
–+ +—-+ +—-+ +—-+ +—-+ +—
+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
= > 25

Ex 2
+—-+ +—-+ +—-+ +—-+ +—-+
| | | | | | | | | |
–+ +—-+ +—-+ +—-+ +—-+ +—

+———+ +———+ +——–
| | | | |
–+ +———+ +———+
+1+1+1+1-1-1-1-1-1+1+1+1+1+1-1-1-1-1-1+1+1+1+1+1-1
= > 2

在第一個例子中,兩個方波具有相同的頻率和相位. 當單個樣品相乘加總後, 你會得到一個大數目 25 (這個看俺不明白!)
在第二種情況下, 方波的頻率差了一倍 (2), 和預期的一樣, 單個樣品相乘加總後, 你會得到一個小數目 2 (這個看俺不明白!)

如果你仔細觀察, 你會發現,乘法是一個真正的異或操作 (XOR).

用 PIC 的ASSEMBLY 的寫法,

sum_of_products equ 0x20 ;A register variable
input_sample equ 0x21 ;In LS bit
square_wave_sample equ 0x22 ;In LS bit

movf input_sample,W
xorwf square_wave_sample,W
skpnz
movlw -2
addlw 1
addwf sum_of_products,F

相位差90度 (Quadrature):

在DFT中, 我們使用正弦波和餘弦波. sin / cos 是兩個90度相移明顯的關係的訊號. DTMF 解碼也需要一個類似轉移的方波. 原因是, 兩個頻率相同但是相位差了90度的訊號, DFT 後它可能最終是一個小的數目. 例如,

quadrature1

因此,為了防止這種情況, 我們必須執行兩個乘積加總, 第一個數字化輸入信號,並在檢測頻率的正方形, 另一個數字化輸入信號和方波之間的移相90度的相對於第一方波。

DFT運算是點積操作 (?) 你可以想像的信號和kernals指數的樣本數為載體. 載體可能是非常大的,如4096個樣本.

信號強度 : 1-norm

如果我們計算 sin 和 cosin 的 DFT, 那麼一個特定的頻率的信號強度是很容易確定找到熟悉的幅度:

strength ~ sqrt( real(DFT)^2 + imaginary(DFT)^2)

換句話說, 一個DFT的結果是一個複數, 當使用複雜的內核。 量值和一個複數的實部的平方加上的虛部的平方之和的平方根. 我希望避免這是一個繁瑣的操作. 但是, 如果你需要了解, 可以看看 平方根理論 和 PIC平方根的寫法.

平方的總和歸一化的平方根被稱為 "平方範數". 這是一般類稱為 "p-norm" 賦範線性空間的一個子集. 在我們的例子中,線性空間由兩部分組成:實部和虛部的DFT。 p-norm 是指:

strength ~ (abs(real(DFT))^p + abs(imaginary(DFT))^p) ^ 1/p

同時, 平方範數, 當p為2時, 這是相同的. ??

現在, 如果p是1, 我們會得到一個更簡單的公式, 1-norm :

strength ~ abs(real(DFT)) + abs(imaginary(DFT))

數字化 :+1 和-1, 或者, 1 和 0 ?

到目前為止, 我們已經進行過數字化 +1 和 -1. 然而, 在實際的程序中, 我們可能只會用到 1 和 0, 因為電腦的處理能力之認識這兩個狀態, 如果微控制器的主觀感受(不要動搖你,然後讀上……真正的原因是需要一些效率).

接下來的問題是, 請問這是怎麼影響的DFT計算 ? 假設我們有兩個方波, 數字化成 +1 和 -1.

dot product 會出現甚麼結果? 如果我們數字化他們以 1和0來代替呢 ? 假設 f1 和 f2 是兩個方波.

f1 = -1 或 +1

f2 = -1 或 +1

轉換為 1 和 0 的:</pre>
q1 = (f1 + 1)/2 => f1 = 2*q1 – 1

q2 = (f2 + 1)/2 => f2 = 2*q2 – 1
<pre>

因此,q1和q2表示重新數字化的f1和f2的方波。 現在的 dot product :

—- —-
DFT = \ f1*f2 = \ (2*q1 – 1) * (2*q2 – 1)
/ /
—- —-
—-
= \ (4*q1*q2 – 2*q2 – 2*q1 + 1)
/
—-
—- —- —- —-
= 4 \ q1*q2 – 2 \ q2 – 2 \ q1 + \ 1
/ / / /
—- —- —- —-

的最後一項評估N,換言之,1 +1 +1 + … +1 = N的中間兩個方面需要仔細的檢查。 假設f1和f2不包含直流分量:

N~
—-
\ F1 = 0
/
—-
0

那麼Q1的總和是:

NN
——–
\ Q1 = \(F1 + 1)/ 2
/ /
——–
0 0
= 0 + N / 2 = N / 2

同樣,之q2的總和為N / 2。 結合這些結果產量:

——–
\ F1 * F2 = 4 * \ Q1 * Q2  –  2 * N / 2  –  2 * N / 2 + N
/ /
——–
—-
= 4 \ Q1 * Q2  –  N
/
—-

因此,得出的結論是,用0和1的數字化在本質上是相同的+1和-1的數字化。 唯一的區別是,一個新的“DC”一詞的N已引進和點產品已被縮放。

所有假設問題

我提出的假設,即方波f1和f2沒有DC分量。 不過,仔細想想如何創建論斷方波。 其中之一說f1是數字化的DTMF信號的,另一種是合成的軟件。 當然,我們(應該)有合成的方波控制權。 然而,另一種是現實的錯誤。 首先,有一個問題比較介紹 assymetry 的數字化。 這可以彌補由正確設計的比較器電路。 第二,有問題的樣本窗口寬度有限。 例如,如果採樣窗口寬度為20毫秒,有一個60Hz的成分存在於我們的輸入信號,我們將介紹一個區議會任期。 事實上,如果存在不具有整數個週期在採樣窗口存在的任何頻率的DC誤差項存在。 有兩種方法來解決這個“問題”。 第一種方法是使窗口盡可能的寬。 這不幸減慢檢測下來。 其他的解決辦法是測量的直流分量。

測量的直流分量

方波直流分量是很容易衡量。 不,你不會需要一個 A/D轉換器. 考慮有一個50%佔空比的方波. 如果我們要品嚐這在一個較高的利率(如10倍的方波的頻率),我們應該得到相同數量的高和低的樣品。 在這種情況下的直流分量的方波的振幅的二分之一。 如果更高的樣品比低的樣品,然後累計的直流分量會更大一些。 換句話說,在方波的佔空比成正比的直流分量。

回到 DTMF 檢測應用程序…

在我們的應用程序,其中的q的點積是0/1數字化的輸入。 另一種是軟件產生的方波。 總是會有兩個點的產品每個音;每個正交之一。
應用的1 +1和0的數字化規範:

未完成… 這是很容易申請1範數公式的新的數字化。

誤差分析

方波幾乎相當於一個正弦波。 所以,問題自然就變成了: 這種技術引進多少誤差 ? 可能嘗試回答這個問題的最好辦法是進行諧波分析的數字化進程。

考慮的雙音信號:

g(t) = A1*cos(w1*t) + A2*cos(w2*t)

當然有不同的頻率w1和w2(否則我們沒有一個雙音)。 的振幅也不同,但在許多情況下,它們在彼此的百分之幾。 讓我們考慮振幅的情況下是相同的,現在:

g(t) = A1*cos(w1*t) + A1*cos(w2*t)
= A1*(cos(w1*t) + cos(w2*t))
/w1 + w2 \ /w1 – w2 \
= A1* cos |——- * t| * cos |——- * t|
\ 2 / \ 2 /

由一個比較器,該信號被數字化的1和0的。 在數字化過程的數學描述:

gd(t) = (1 + sign(g(t)))/2

符號()是一個函數,返回其參數的標誌。 徵(+)+1 / -1數字化本身產生。 克(t)為正時的兩個餘弦項具有相同的符號。 現在考慮下面的符號()的身份:

sign(f1(t) * f1(t)) = sign(f1(t)) * sign(f2(t))

並應用此我們的公式

/ /w1 + w2 \\ / /w1 – w2 \\
sign(g(t)) = sign|cos |——- * t|| sign|cos |——- * t||
\ \ 2 // \ \ 2 //

sign() 正弦波函數產生 具有相同的頻率的50%佔空比的方波.
The sign() function with a sinusoidal argument produces a 50% duty cycle square wave with the same frequency as the sinusoid.

未完成…

"方波" 頁面提供了更多的理論。

這裡更多的軟件 。

返回首頁

保持此頁面Scott Dattalo的你可以達到我: scott@dattalo.com
最後修改於17Dec99

廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s