雙音多頻

雙音多頻訊號，電話系統中電話機與交換機之間的一種信令，最常用於撥號時傳送被叫號碼。不過雙音多頻的發明，除了縮短撥號時間，也擴充套件了撥號之外的功能，例如自動總機、互動式語音應答。在雙音多頻訊號普及之前，電話系統中使用一連串的斷續脈衝來傳送被叫號碼，稱為脈衝撥號。脈衝撥號需要電信局中的操作員手工完成長途接續。由脈衝撥號到雙音複頻這幾年轉換期當中，新設計的電話機為了能相容於兩種系統，大都設計為複頻/脈衝(TONE / PULSE)兩用。雙音多頻訊號是貝爾實驗室發明的，其目的是為了自動完成長途呼叫。因為傳統的脈衝撥號，只能把訊號送到電話接上的這一臺電信交換機，但長距通話時，往往必須經由多臺電信交換機才能完成，雙音多頻訊號可以克服這個障礙，利用本來就用來傳送聲音的電話線傳送使用者的按鍵訊號。因為雙音多頻的發明，讓自動總機與互動式語音應答得以發展，此類裝置可以在接聽電話後自動宣讀預錄的語音，再依據發話端的按鍵訊號做相對回應。按鍵.雙音多頻的撥號鍵盤是formula_1的矩陣，有formula_2個數字鍵和formula_3個字元鍵，每個數字或字元都是由兩個單頻訊號的組合來進行傳輸，因此鍵盤上每個按鍵所對應的訊號都可以表示為formula_4，其中formula_5和formula_6分別表示按鍵所在的行和列對應的頻率值。換句話說每一行代表一個"高"頻，每一列代表一個"低"頻。也就是說每按一個鍵就傳送一個"高"頻和"低"頻的正弦訊號組合，比如'1'相當於697和1209赫茲。交換機可以解碼這些頻率組合並確定所對應的按鍵。AT&amp;T貝爾實驗室提出用DTMF訊號作為音訊電話的撥號訊號，因為這種方式可以提供更高的撥號速率，且容易被自動檢測和識別。但反之，DTMF訊號的這個優點也很容易變成致命的缺點，因為容易被交換機檢測和識別，也就意味著容易被意圖為之第三方破解。破解的原理很簡單，只要能估計出DTMF訊號中兩個單頻訊號的頻率值，再根據底下電話機鍵盤的頻率陣列表格的對應關係就可以反推出按鍵值。注意：ABCD四個字母並不使用於撥號鍵盤，但常做為機器對機器的控制訊號，例如DTMF格式的來話顯示。按鍵識別實驗.根據撥號音識別號碼的關鍵就在於準確估計出DTMF訊號的頻率值。有很多種方法可以估計DTMF訊號的頻率值，在此舉例兩種實現方法，一種是基於"帶通濾波器"的方法，另一種方法為基於"格策爾演演算法(Goertzel algorithm)"。1.帶通濾波器演算法:濾波器演算法識別按鍵的關鍵是設計8個帶通濾波器，每個帶通濾波器的中心頻率對應著低/高頻組的各個頻率點。將待識別的撥號音也就是DTMF的訊號波型依次透過這8個帶通濾波器。理論上只有頻率成分與濾波器中心頻率一致的訊號才會透過，在濾波器輸出端檢測能量最大者即可判斷出低/高頻序號，最後再透過電話機鍵盤的頻率陣列表格的對應關係即可反推出按鍵值。以下展示使用帶通濾波器演算法應用於MATLAB程式碼:clearclcfs=8000;t=(0:800)/fs;fcolumns1 = 697;fcolumns2 = 770;fcolumns3 = 852;fcolumns4 = 941;frow1 = 1209;frow2 = 1336;frow3 = 1477;num0 = sin(2*pi*fcolumns4*t)+sin(2*pi*frow2*t); %數字0num1 = sin(2*pi*fcolumns1*t)+sin(2*pi*frow1*t); %數字1num2 = sin(2*pi*fcolumns1*t)+sin(2*pi*frow2*t); %數字2num3 = sin(2*pi*fcolumns1*t)+sin(2*pi*frow3*t); %數字3num4 = sin(2*pi*fcolumns2*t)+sin(2*pi*frow1*t); %數字4num5 = sin(2*pi*fcolumns2*t)+sin(2*pi*frow2*t); %數字5num6 = sin(2*pi*fcolumns2*t)+sin(2*pi*frow3*t); %數字6num7 = sin(2*pi*fcolumns3*t)+sin(2*pi*frow1*t); %數字7num8 = sin(2*pi*fcolumns3*t)+sin(2*pi*frow2*t); %數字8num9 = sin(2*pi*fcolumns3*t)+sin(2*pi*frow3*t); %數字9numStar = sin(2*pi*fcolumns4*t)+sin(2*pi*frow1*t); %符號*numJin = sin(2*pi*fcolumns4*t)+sin(2*pi*frow3*t); %符號#blank = zeros(size(num1)); %間隔f = [fcolumns1 fcolumns2 fcolumns3 fcolumns4 frow1 frow2 frow3];f_Low = [fcolumns1 fcolumns2 fcolumns3 fcolumns4];f_High = [frow1 frow2 frow3];%設計帶通濾波器(low frequency)N = 400;Bandwidth = 70;B_Low = zeros(4,N+1); %存放低頻組的濾波器係數for i = 1:4Wo = f_Low(i);wc1 = ( Wo - Bandwidth/2 ) * 2* pi/fs;wc2 = ( Wo + Bandwidth/2 ) * 2* pi/fs;B_Low(i, :) = fir2(N,[0,wc1/pi,wc2/pi,1],[0,1,1,0]);end%設計帶通濾波器(high frequency)N = 200;Bandwidth = 110;B_High = zeros(3,N+1); %存放高頻組的濾波器係數for i = 1:3Wo = f_High(i);wc1 = ( Wo - Bandwidth/2 ) * 2* pi/fs;wc2 = ( Wo + Bandwidth/2 ) * 2* pi/fs;B_High(i, :) = fir2(N,[0,wc1/pi,wc2/pi,1],[0,1,1,0]);endDialNum = num1; %DTMF訊號sound(DialNum);%計算當前訊號與各個頻率點的距離(low frequency)Diatance_Low = zeros(1,4);for i = 1:4Output = filter(B_Low(i, :),1,DialNum);Diatance_Low(1,i) = max(abs(fft(Output)));end[maxnum_low,index_low] = max(Diatance_Low(1, :));%計算當前訊號與各個頻率點的距離(high frequency)Diatance_High = zeros(1,3);for i = 1:3Output = filter(B_High(i, :),1,DialNum);Diatance_High(1,i) = max(abs(fft(Output)));end[maxnum_high,index_high] = max(Diatance_High(1, :));%判斷按鍵if index_low == 1 &amp;&amp; index_high == 1keynum = '1';elseif index_low == 1 &amp;&amp; index_high == 2keynum = '2';elseif index_low == 1 &amp;&amp; index_high == 3keynum = '3';elseif index_low == 2 &amp;&amp; index_high == 1keynum = '4';elseif index_low == 2 &amp;&amp; index_high == 2keynum = '5';elseif index_low == 2 &amp;&amp; index_high == 3keynum = '6';elseif index_low == 3 &amp;&amp; index_high == 1keynum = '7';elseif index_low == 3 &amp;&amp; index_high == 2keynum = '8';elseif index_low == 3 &amp;&amp; index_high == 3keynum = '9';elseif index_low == 4 &amp;&amp; index_high == 2keynum = '0';elseif index_low == 4 &amp;&amp; index_high == 1keynum = '*';elseif index_low == 4 &amp;&amp; index_high == 3keynum = '#';endkeynum2.格策爾演演算法(Goertzel algorithm):理論上DTMF訊號只會在兩個固定的頻率點上出現能量，如何準確又高效地估計這兩個頻率值(低/高頻組)是識別撥號音的關鍵。傳統的頻譜估計方法得到的是一個頻率區間內所有頻率點的估計結果，而對於DTMF訊號我們只在乎那8個固定頻率點上的功率譜估計值。而Goertzel演算法就是估計DTMF訊號功率譜最經典又實用的方法，這個演算法只估計DTMF訊號特定頻率點上的功率譜。以下展示MATLAB利用Goertzel演演算法估計DTMF訊號的範例:clearclcfs=8000;t=(0:2000)/fs;fcolumns1=697;fcolumns2=770;fcolumns3=852;fcolumns4=941;frow1=1209;frow2=1336;frow3=1477;num0=sin(2*pi*fcolumns4*t)+sin(2*pi*frow2*t); %數字0num1=sin(2*pi*fcolumns1*t)+sin(2*pi*frow1*t); %數字1num2=sin(2*pi*fcolumns1*t)+sin(2*pi*frow2*t); %數字2num3=sin(2*pi*fcolumns1*t)+sin(2*pi*frow3*t); %數字3num4=sin(2*pi*fcolumns2*t)+sin(2*pi*frow1*t); %數字4num5=sin(2*pi*fcolumns2*t)+sin(2*pi*frow2*t); %數字5num6=sin(2*pi*fcolumns2*t)+sin(2*pi*frow3*t); %數字6num7=sin(2*pi*fcolumns3*t)+sin(2*pi*frow1*t); %數字7num8=sin(2*pi*fcolumns3*t)+sin(2*pi*frow2*t); %數字8num9=sin(2*pi*fcolumns3*t)+sin(2*pi*frow3*t); %數字9numStar=sin(2*pi*fcolumns4*t)+sin(2*pi*frow1*t); %符號*numJin=sin(2*pi*fcolumns4*t)+sin(2*pi*frow3*t); %符號#blank=zeros(size(num1)); %間隔f=[fcolumns1 fcolumns2 fcolumns3 fcolumns4 frow1 frow2 frow3];freq_indices=round(f/fs*length(t))+1;%DTMF訊號%CellPhoneNum=[num1 blank num3 blank num9 blank num8 blank num0 blank num7 blank num6 blank num6 blank num5 blank num2 blank num4];CellPhoneNum=0.2*randn(size(CellPhoneNum))+CellPhoneNum;sound(CellPhoneNum);for i=1:2:21DialNumber=CellPhoneNum(((i-1)*length(t)+1):i*length(t));dft_data=goertzel(DialNumber,freq_indices);temp = sort(abs(dft_data),'descend'); %找最大的兩個頻率點數值temp_index1 = find(abs(dft_data) == temp(1));temp_inedx2 = find(abs(dft_data) == temp(2));if temp_index1 &lt; temp_inedx2 %保證 temp_index1代表低頻,temp_index2代表高頻index_low = temp_index1;index_high = temp_inedx2;elseindex_low = temp_inedx2;index_high = temp_index1;end%判斷按鍵if index_low == 1 &amp;&amp; index_high == 5keynum = '1';elseif index_low == 1 &amp;&amp; index_high == 6keynum = '2';elseif index_low == 1 &amp;&amp; index_high == 7keynum = '3';elseif index_low == 2 &amp;&amp; index_high == 5keynum = '4';elseif index_low == 2 &amp;&amp; index_high == 6keynum = '5';elseif index_low == 2 &amp;&amp; index_high == 7keynum = '6';elseif index_low == 3 &amp;&amp; index_high == 5keynum = '7';elseif index_low == 3 &amp;&amp; index_high == 6keynum = '8';elseif index_low == 3 &amp;&amp; index_high == 7keynum = '9';elseif index_low == 4 &amp;&amp; index_high == 6keynum = '0';elseif index_low == 4 &amp;&amp; index_high == 5keynum = '*';elseif index_low == 4 &amp;&amp; index_high == 7keynum = '#';endPhoneCell_Indent(round(i/2)) = keynum;enddisplay(PhoneCell_Indent) %顯示識別結果特殊音訊.歐式音調：