DTMF信号的产生及检测解读
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
DSP课程设计
实验报告
DTMF信号的产生及检测
院(系):电子信息工程学院-通信工程
设计人员:周钰哲学号:08211052
苗祚雨08212075
目录
一、设计任务书 (2)
二、设计内容 (2)
三、设计方案、算法原理说明 (3)
四、程序设计、调试与结果分析 (6)
五、设计(安装)与调试的体会 (16)
六、参考文献 (16)
一设计任务要求
双音多频DTMF(Dual Tone Multi Frequency)是在按键式电话机上得到广泛应用的音频拨号信令,一个DTMF信号由两个频率的音频信号叠加构成。
这两个音频信号的频率分别来自两组预定义的频率组:行频组和列频组。
每组分别包括4个频率,分别抽出一个频率进行组合就可以组成16种DTMF编码,分别记作0~9、*、#、A、B、C、D。
如下图1所示。
图1 DTMF信令的编码
要用DSP产生DTMF信号,只要产生两个正弦波叠加在一起即可;DTMF检测时采用改进的Goertzel算法,从频域搜索两个正弦波的存在。
1、基本部分:
(1)使用C语言编写DTMF信号的发生程序,要求循环产生0~9、*、#、A、B、C、D对应的DTMF信号,并且符合CCITT对DTMF信号规定的指标。
(2)使用C语言编写DTMF信号的检测程序,检测到的DTMF编码在屏幕上显示。
2、发挥部分:
利用DTMF信号完成数据通讯的功能,并试改进DTMF信号的规定指标,使每秒内传送的DTMF编码越多越好。
3、要求完成的任务
(1)编写C语言程序,并在CCS集成开发环境下调试通过。
(2)实现设计所要求的各项功能。
(3)按要求撰写设计报告。
二、设计内容
DTMF发生器基于两个二阶数字正弦振荡器,一个用于产生行频,一个用于产生列频。
在输入信号中检测DTMF信号,需要在输入的数据信号流中连续地搜索DTMF信号频谱的存在。
整个检测过程分两步:首先采用Goertzel算法在输入信号中提取频
谱信息;接着作检测结果的有效性检查。
Goertzel算法实质是一个两极点的IIR滤
波器。
三、设计方案、算法原理说明
要用DSP 产生DTMF 信号,只要通过两个正弦波叠加在一起即可;DTMF 检测时
采用改进的Goertzel 算法,从频域搜索两个正弦波的存在。
(1)DTMF信号的产生
DTMF编码器基于两个二阶数字正弦波振荡器,一个用于产生行频,一个用于产
生列频。
向DSP装入相应的系数和初始条件,就可以只用两个振荡器产生所需的八
个音频信号。
典型的DTMF信号频率范围是700~1700Hz,选取8000Hz作为采样频
率,即可满足Nyquist条件。
DTMF数字振荡器对的二阶系统函数的差分方程为:
其中,,,为采样频率,为输出
正弦波的频率,为输出正弦波的幅度。
该式初值为,。
用sin函数产生离散的正弦值,生成DTMF的公式为:
buffer[t]=sin(t*2*pi*f1/fs)+sin(t*2*pi*f2/fs) 其中t为采样序数,由0开始递增;f1,f2为生成DTMF信号的两个正弦波的频
率;fs为采样频率;buffer[t]为序数t时的得出的采样值。
将这些数据转换为Q15
格式然后通过codec发送出去。
CCITT对DTMF信号规定的指标是,传送/接收率为每秒10个数字,即每个数字
100ms。
代表数字的音频信号必须持续至少45ms,但不超过55ms。
100ms内其他时
间为静音,以便区别连续的两个按键信号。
我们使用8000Hz的采样频率(电话信号
f=8kHZ),即1秒采样8000个点,则100ms采样800个点,的典型抽样频率为sam
我们设置800个点的缓存,其中用400个存产生的DTMF信号值,即音频信号必须持
续50ms,另外400个存0值,即静音信号。
(2)DTMF信号的检测
DTMF检测是对进入解码端的信号进行检测,并把双音频信号转换成对应的数字
信息。
它是一个比DTMF产生更加复杂过程。
由于数据流是连续的,为了保证DTMF
检测的实时性,因此要求检测过程必须是实时连续的。
在输入信号中检测DTMF 信号,需要在输入的数据信号流中连续地搜索DTMF 信号频谱的存在。
整个检测过程分两步:首先采用Goertzel 算法在输入信号中提取频谱信息;接着作检测结果的有效性检查。
DTMF 解码时在输入信号中搜索出有效的行频和列频。
计算数字信号的频谱可以采用DFT 及其快速算法FFT ,而在实现DTMF 解码时,采用Goertzel 算法要比FFT 更快。
通过FFT 可以计算得到信号所有谱线,了解信号整个频域信息,而对于DTMF 信号只需关心其8 个行频/列频及其二次谐波信息即可,二次谐波的信息用于将DTMF 信号与声音信号区别开。
此时Goertzel 算法能更加快速的在输入信号中提取频谱信息。
Goertzel 算法实质是一个两极点的IIR 滤波器。
Goertzel 算法原理:
DTMF 检测器的核心是Goertzel 算法。
该算法利用二极点的IIR 滤波器计算离散傅立叶变换值,能够快速高效地提取输入信号的频谱信息。
由于IIR 滤波器是一个递归结构,它利用只有一个实系数的差分方程进行操作,并不像DFT 或FFT 算法那样需要计算数据块,而是每输入一个样值就执行一次算法。
完成时域到频域的变换可以用离散傅立叶变换(DFT)或快速傅立叶变(FFT).FFT 计算出所有点频率,而DFT 可以只计算感兴趣的频率点.如果要计算的频率点数少于log 2N(N 为输入信号点数),采用DFT 的计算速度比FFT 更快。
直接计算DFT ,需要很多复系数,即使只计算一点的DFT 也需要N 个复系数.采用数字信号处理中的Goertzel 算法,如图2,则可明显地提高速度。
图2 Goertzel 算法原理框图
input n x v v n x n v n v k n v k k k k N k ==-=-+---⋅=)( 0)2( 0)1( )()2()1()cos(2)(2π
在实际的DTMF 检测中,只需DFT 的幅度(本算法为平方幅度)信息就足够了,
因此在Goertzel 滤波器中,当N 点(相当于DFT 数据块的长度)样值输入滤波器后,滤波器输出伪DFT 值vk (n ),由vk (n )即可确定频谱的平方幅度。
)
1()()cos(2)1()( )
()()(22
2
2
---+==*
N v N v k N v N v N y N y k X k k N
k k k k π
其中k=f*N/fs ,当N 取值为125时, k 的取值经计算如表1所示:
信号频率(Hz)
计算值k 取整值k 绝对误差 相对误差 697 10.890625 11 0.109375 1.00430% 770 12.03125 12 0.03125 0.25974% 852 13.3125 13 0.3125 2.34741% 941 14.703125 15 0.296875 2.01912% 1209 18.890625 19 0.109375 0.57899% 1336 20.875 21 0.125 0.59880% 1477 23.078125 23 0.078125 0.33852% 1633
25.515625
25
0.515625
2.02082%
表1
一旦得到行/列频率的频谱平方幅度信息,就可以通过一系列的判决来确定音频及数字结果的有效性。
首先,检测可能DTMF 信号的强度是否足够大。
行频率分量和列频率分量的平方幅度和应高于某一确定门限。
注意与DTMF 频率相符的正弦波的能量集中在频域内一段很窄的范围当中,所以门限取值应占动态范围的大部分。
第二,如果DTMF 信号存在,由于构成DTMF 信号的行频都低于列频,因此从小到大依次判断各个频率点的频谱幅度,得到的第一个达到门限要求的的频率点即为行频,第二个即为列频,综合行频和列频即可得出检测到的按键信息。
检测流程图:
图3 检测流程图
四、程序设计、调试与结果分析
(1)发送源程序代码如下:
/*****************************************************************/
/* DTMFsignal 发送程序 send.c */ /*****************************************************************/
#include <stdio.h>
#include <math.h>
#include <type.h>
#include <board.h>
#include <codec.h>
#include <mcbsp54.h>
void delay(int period);
void generate(int num);
HANDLE hHandset;
float buffer[800];
s16 num=0;
int count=0;
float freq[16][2]={ 941,1336, //键值0对应的行频列频
697,1209, //1
697,1336, //2
697,1477, //3
770,1209, //4
770,1336, //5
770,1477, //6
852,1209, //7
852,1336, //8
852,1477, //9
697,1633, //A
770,1633, //B
852,1633, //C
941,1633, //D
941,1209, //*
941,1477 //#
};
float pi=3.1415926;
void main()
{
int cnt=2;
if(brd_init(100))
{
return;
}
/* blink the leds a couple times */
while ( cnt-- ) /* 二极管闪两次 */
{
brd_led_toggle(BRD_LED0);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED1);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED2);
//brd_delay_msec(1000);
delay(1000);
}
/* Open Handset Codec */
hHandset = codec_open(HANDSET_CODEC); /* Acquire handle to codec */
/* Set codec parameters */
codec_dac_mode(hHandset, CODEC_DAC_15BIT); /* DAC in 15-bit mode */
codec_adc_mode(hHandset, CODEC_ADC_15BIT); /* ADC in 15-bit mode */
codec_ain_gain(hHandset, CODEC_AIN_6dB);/* 6dB gain on analog input to ADC */
codec_aout_gain(hHandset, CODEC_AOUT_MINUS_12dB); /* -12dB gain on analog */ /* output from DAC */
codec_sample_rate(hHandset,SR_8000); /* 8KHz sampling rate */
generate(num);
}
void generate(int num)
{
f32 x,y;
int k=0;
int i;
i=0;
while(1)
{
// Wait for sample from handset
while (!MCBSP_XRDY(HANDSET_CODEC)) {};
*(volatile u16*)DXR1_ADDR(HANDSET_CODEC)=buffer[i];
i++;
if(i==800)
{
i=0;
num++;
if(num==16)
num=0;
x=freq[num][0]/8000;
y=freq[num][1]/8000;
for(k=0;k<400;k++)
{ buffer[k]=(0.65*sin(2*pi*y*k)+0.8*sin(2*pi*x*k))*16384;
buffer[k+400]=0;
}
}
}
}
void delay(int period) /*延时子程序 */
{
int i, j;
for(i=0; i<period; i++)
{
for(j=0; j<period>>1; j++);
}
}
(2)检测源程序代码如下:
/*****************************************************************/
/* DTMFsignal 检测程序 receive.c */ /*****************************************************************/
#include <stdio.h>
#include <math.h>
#include <type.h>
#include <board.h>
#include <codec.h>
#include <mcbsp54.h>
HANDLE hHandset;
float buffer[125];
float pi=3.1415926;
s16 receive[125];
s16 dacdata;
int k=0;
int detect_result[100]={0};
int l=0;
int flag=0;
void delay(int period);
void detect();
void main()
{
int cnt=2;
/********************************************
Description:
* vk(n) = 2*coef*vk(n-1) - vk(n-2) + x(n)
*
* Coefficients are in w[8]
* x(n) is in buffer[256]
* vk(n-2) is a[i][0]
* vk(n-1) is a[i][1] ....
* vk(n) is a[i][2]
*********************************************/
if(brd_init(100))
{return;}
// blink the leds a couple times
while ( cnt-- )
{
brd_led_toggle(BRD_LED0);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED1);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED2);
//brd_delay_msec(1000);
delay(1000);
}
/* Open Handset Codec */
hHandset = codec_open(HANDSET_CODEC); /* Acquire handle to codec */ /* Set codec parameters */
codec_dac_mode(hHandset, CODEC_DAC_15BIT); /* DAC in 15-bit mode */
codec_adc_mode(hHandset, CODEC_ADC_15BIT); /* ADC in 15-bit mode */
codec_ain_gain(hHandset, CODEC_AIN_6dB); /* 6dB gain on analog input to ADC */
codec_aout_gain(hHandset, CODEC_AOUT_MINUS_6dB);
/* -6dB gain on analog output from DAC */
codec_sample_rate(hHandset,SR_8000); /* 8KHz sampling rate */
while(1)
{
while (!MCBSP_RRDY(HANDSET_CODEC)) {};
dacdata = *(volatile u16*)DRR1_ADDR(HANDSET_CODEC);
receive[k]=dacdata;
buffer[k]=dacdata/16384.0;
k++;
if(k==125)
{
k=0;
detect();
}
}
}
void detect()
{ int i,j,x,y;
float w[8],a[8][3],amp[8];
w[0]=2*cos(2*pi*11/125);
w[1]=2*cos(2*pi*12/125);
w[2]=2*cos(2*pi*13/125);
w[3]=2*cos(2*pi*15/125);
w[4]=2*cos(2*pi*19/125);
w[5]=2*cos(2*pi*21/125);
w[6]=2*cos(2*pi*23/125);
w[7]=2*cos(2*pi*26/125);
for(i=0;i<8;i++)
{
a[i][0]=0;
a[i][1]=0;
for(j=1;j<=125;j++)
{
a[i][2]=w[i]*a[i][1]-a[i][0]+buffer[j-1];
a[i][0]=a[i][1];
a[i][1]=a[i][2];
}
//计算频谱的幅度平方值
amp[i]=a[i][1]*a[i][1]+a[i][0]*a[i][0]-w[i]*a[i][1]*a[i][0];}
j=0;
for(i=0;i<8;i++) {
if(amp[i]>500) //门限设为500
{ //printf("The amplitude %d is %f.\r\n",i,amp[i]);
j++;
if(j==1)
{
x=i;
}
else if(j==2)
{
y=i;
}
}
}
i=-1;
if(flag==0)
{
if(j==2)
{
if(x==0 && y==4)
{ i='1'; }
else if(x==0 && y==5)
{ i='2'; }
else if(x==0 && y==6)
{ i='3'; }
else if(x==1 && y==4)
{ i='4'; }
else if(x==1 && y==5)
{ i='5'; }
else if(x==1 && y==6)
{ i='6'; }
else if(x==2 && y==4)
{ i='7'; }
else if(x==2 && y==5)
{ i='8'; }
else if(x==2 && y==6)
{ i='9'; }
else if(x==3 && y==5)
{ i='0'; }
else if(x==0 && y==7)
{ i='A'; }
else if(x==1 && y==7)
{ i='B'; }
else if(x==2 && y==7)
{ i='C'; }
else if(x==3 && y==7)
{ i='D'; }
else if(x==3 && y==4)
{ i='*'; }
else if(x==3 && y==6)
{ i='#'; }
}
if(i!=-1)
{
detect_result[l]=i;
l++;
if(l==100)
{
for(l=0;l<100;l++)
printf("The DTMF signal is %c.\r\n ",detect_result[l]);
}
flag++;
}
}
else if(j==0)
flag=0;
}
void delay(int period)
{
int i, j;
for(i=0; i<period; i++)
{
for(j=0; j<period>>1; j++);
}
}
(3)cmd源代码如下:
/*############################################################################# ## $Id: $
##
############################################################################### ## Copyright (c) 1999 DNA Enterprises, Inc.
############################################################################### ## Revision History
## ----------------
## $Log: $
##
###############################################################################
###############################################################################
## XFERMAKE.CMD
## ------------
## C54x Linker Command File for 5402 DSK Memory Transfer Module
##
#############################################################################*/
/*****************************************************************************/
/* Object Files */
/*****************************************************************************/
/*****************************************************************************/
/* Linker Options */
/*****************************************************************************/
/*****************************************************************************/
/* C5402 DSK DSP Memory Map */
/* */
/*****************************************************************************/
MEMORY
{
PAGE 0: VECS: origin = 0080h, length = 0080h /* Internal Program RAM */
PRAM: origin = 0100h, length = 8000h /* Internal Program RAM */
PAGE 1: SCRATCH: origin = 0060h, length = 0020h /* Scratch Pad Data RAM */
DMARAM: origin = 0C00h, length = 0300h /* DMA buffer */
DATA: origin = 1100h, length = 0080h /* Internal Data RAM */
STACK: origin = 1180h, length = 0560h /* Stack Memory Space */
INRAM: origin = 1900h, length = 0100h /* Internal Data RAM */
HPRAM0: origin = 1A00h, length = 0002h /* HPI memory accessible by Host and DSP */
HPRAM1: origin = 1A02h, length = 0280h /* HPI memory accessible by Host and DSP */
HPRAM2: origin = 1C82h, length = 0280h /* HPI memory accessible by Host and DSP */
EXRAM: origin = 1F10h, length = 4000h /* External Data RAM */
}
/*****************************************************************************/
/* DSP Memory Allocation */
/*****************************************************************************/
SECTIONS
{
.cinit > PRAM PAGE 0
.text > PRAM PAGE 0
.vectors > VECS PAGE 0
init_var > PRAM PAGE 0
detect > PRAM PAGE 0
vrcprg > PRAM PAGE 0
matprg > PRAM PAGE 0
.stack > STACK PAGE 1
.trap > SCRATCH PAGE 1
.const > EXRAM PAGE 1
.data > EXRAM PAGE 1
.bss > EXRAM PAGE 1
.cio > EXRAM PAGE 1
.switch > EXRAM PAGE 1
tables > EXRAM PAGE 1
var > EXRAM PAGE 1
svctab > EXRAM PAGE 1 /* SS_V LSP table */
vctab > EXRAM PAGE 1 /* V LSP table */
uvctab > EXRAM PAGE 1 /* UV LSP table */
cuvtab > EXRAM PAGE 1 /* Stochastic codebook */
cdbktab > EXRAM PAGE 1 /* various codebook tables*/
logtab > EXRAM PAGE 1 /* table for log2 */
powtab > EXRAM PAGE 1 /* table for pow2 */
hamtab > EXRAM PAGE 1 /* table for hamming */
lgwtab > EXRAM PAGE 1 /* table for lag window */
acostab > EXRAM PAGE 1 /* table for arccos */
sqrtab > EXRAM PAGE 1 /* table for square root */
acbtab > EXRAM PAGE 1 /* table for thresholds in acb */
pm03tab > EXRAM PAGE 1 /* table for x^(-0.3) computation */ costab > EXRAM PAGE 1 /* table for cosine */
V23 > INRAM PAGE 1
FSK > INRAM PAGE 1
hpibuff0 > HPRAM0 PAGE 1
hpibuff1 > HPRAM1 PAGE 1
hpibuff2 > HPRAM2 PAGE 1
dma_buff > DMARAM PAGE 1
}
(4)调试与结果分析
发送端的频域图形如图4所示:
接收端的频域图形接受判决结果如图5所示:
图5
(5)调试过程中产生的问题及解决方案
1、起初采用每检测到一次字符便用printf()函数输出,而printf()函数占用
DSP的处理时间过多,因此丢失了很多的DTMF样点,致使很多数字按键信息无法被检测出来,因而不仅产生字符丢失的情况,也达不到每秒10个字符的传送
标准。
后来为了改善信息丢失的现象,我们采取了每个字符多次发送的措施,
但虽然可以达到字符不漏的目的,但显示速度却更慢了,根本达不到CCITT标准。
后来我们建立了缓存区用于存储检测结果,当缓存区存满检测结果后再用printf()函数将数据输出,输出速度明显提高,可以达到每秒10个字符的传送标准。
2、为了改善偶尔仍会发生字符丢失的现象,我们改变了门限值。
3、将cmd文件改为片内寻址后,数据传送速度会加快。
修改分配命令如下:
PRAM:origin=0100h,length=3700h
EXRAM:origin=3200h,length=4000h
五、设计(安装)与调试的体会
1、周钰哲:由之前的单片机和数字信号处理做基础再加上此次DSP课程设计,使我们对DSP及其相关的知识有了更深入的理解,进一步扩宽了我的视野。
例如我们这次实验做的双音多频信号的产生和接收其实是我们生活中经常用到的技术,经过此次试验,我们对双音多频有了深入的了解,并较完美的实现了预期的结果。
同时也掌握了DSK开发环境的基本使用方法及过程。
例如新的工程的建立、文件的加载、头文件库文件的加载及其使用这些文件的意义,它们对编程起着十分关键的作用,同时也简化了用户的编程,使我们能够在掌握基本C语言的编程的前提下,加以实际题目的研究就可以实现其功能。
在实验中我们也遇到了很多问题,例如DSP板的配置、Goertzel算法的推导、断点的设置、判决门限的选取、最终显示图形的参数设置等。
相信我们在实验中已经掌握了最基本的思路,为我们后续实验打下了很好的基础。
同时,从中也提高了自己的自学能力,收益很大。
2、苗祚雨:这个小学期初次接触到了DSP信号处理的硬件部分,以前学过的数字信号处理虽然老师有让用matlab做仿真实验,但是这次这种真实的接触DSP给我留下了很深的印象。
由于实验室的能用的板子有限,所以我根据老师的建议,从2号开始就每天到实验室熟悉硬件环境。
这期间,我和周钰哲两个人一点点学会了如何使用和调试。
这次课设中遇到的最大的困难可能就是这个DTMF接受端的检测算法了。
戈泽尔算法的运用使得DSP处理的速度有了一个比较大的进步,而戈泽尔算法的改进型算法能够进一步解放芯片有限的存储资源,高效充分利用了DSP芯片的优势。
对课本知识的学习还是需要实际的操作才能达到融会贯通的效果,从开始选题,到对所做课题的初步了解和熟悉,到自己编写程序产生DTMF信号,到对多种接收方式的探索,以及对速度和重复、混叠的解决方案,每一次的小进步都使我们欣喜不已,也让我们感觉到有了理论基础是远不够的,只有全方位的考虑到实际中所遇到的问题,才能真正做出理想的效果。
六参考文献
(1)高海林、钱满义.DSP技术及其应用讲义.2005年10月
(2) 苑毅.双音多频DTMF信号的产生与检测.北京:大众科技.2008
(3) 薛曼芳.基于改进的Goertzel算法的双音多频检测器的设计.甘肃:兰州工业高等专科学校.2008
课程设计成绩评定姓名电话
学号专业、班级
课程设计题目
软件设计与调试
30%
实现的技术指标分数
答辩30% 回答问题的正确性、逻辑思维清
楚、语言表达的准确性
分数
课程设计报告
20%
书写规范、表达清楚、分数
发挥
20%
有创造性分数
成绩
(优、良、中、及格、
不及格)
指导教师签字:。