DSP课程设计DTMF信号的产生及检测.docx
《DSP课程设计DTMF信号的产生及检测.docx》由会员分享,可在线阅读,更多相关《DSP课程设计DTMF信号的产生及检测.docx(33页珍藏版)》请在冰豆网上搜索。
DSP课程设计DTMF信号的产生及检测
DSP课程设计
实验报告
DTMF信号的产生及检测
院(系):
电子信息工程学院通信工程专业
设计人员:
宋佳阳学号:
08211042
一、设计任务书
设计要求及目标
基本部分:
(1)使用C语言编写DTMF信号的发生程序,要求循环产生0~9、*、#、A、B、C、D对应的DTMF信号,并且符合CCITT对DTMF信号规定的指标。
(2)使用C语言编写DTMF信号的检测程序,检测到的DTMF编码在屏幕上显示。
发挥部分:
利用DTMF信号完成数据通讯的功能,并试改进DTMF信号的规定指标,使每秒内传送的DTMF编码越多越好。
二、设计内容
双音多频DTMF(DualToneMultiFrequency)是在按键式电话机上得到广泛应用的音频拨号信令,一个DTMF信号由两个频率的音频信号叠加构成。
这两个音频信号的频率分别来自两组预定义的频率组:
行频组和列频组。
每组分别包括4个频率,分别抽出一个频率进行组合就可以组成16种DTMF编码,分别记作0~9、*、#、A、B、C、D。
如图2-1所示。
图2-1DTMF信令的编码
要用DSP产生DTMF信号,只要产生两个正弦波叠加在一起即可;DTMF检测时采用改进的Goertzel算法,从频域搜索两个正弦波的存在。
三、设计方案、算法原理说明
1.DTMF信号的产生
DTMF发生器基于两个二阶数字正弦振荡器,一个用于产生行频,一个用于产生列频。
DSP只要装载相应的系数和初始条件,就可以只用两个振荡器产生所需的八种音频信号。
典型的DTMF信号频率范围是700~1700Hz,选取8000Hz作为采样频率,即可满足奈奎斯特定理。
正弦波是任何波形构成的基本元素,产生正弦波的方法一般有:
查表法和计算法(泰勒级数展开法或数字正弦振荡器法)。
这里我们使用计算法产生正弦波,有以下两种方案:
方案一:
使用数字正弦振荡器计算法产生正弦波
图3-1DTMF数字振荡器对
由图3-1所示数字振荡器对的框图,可以得到DTMF数字振荡器对的二阶系统函数的差分方程为:
其中
,
,
,
为采样频率,
为输出正弦波的频率,
为输出正弦波的幅度。
该式初值为
,
。
其中,上面一个数字振荡器用于产生行频,下面一个数字振荡器用于产生列频,将行频信号和列频信号通过加法器进行叠加即可产生DTMF信号。
方案二:
使用sin函数产生正弦波
直接利用sin函数生成离散的正弦值,其生成DTMF信号的方程为:
y[t]=sin(t*2*pi*f1/fs)+sin(t*2*pi*f2/fs)
其中t为采样序数,由0开始递增;f1,f2为生成DTMF信号的两个正弦波的频率;fs为采样频率,由前面的分析可知,采样频率应该设定为8000Hz。
将行频信号的采样值与列频信号的采样值进行叠加,即可得到序数为t时的采样值,即为y[t]。
将两种方案进行比较后,我们认为,使用正弦振荡器计算法这种方法计算时所需的计算量小,但是由于使用了迭代的方法产生样点值,所以当前时刻的输出序列需要反馈到输入端。
在程序中实现,就需要不断对y(n-1)和y(n-2)的值进行更新。
同时,当前时刻的输出序列也会影响下一时刻和下两个时刻的输出。
因此,如果用这种方法来产生长时间连续的正弦信号和余弦信号,则累积误差较大。
直接使用sin函数产生正弦波的方法,其计算时所需的计算量与方案一相当,并且也能达到误差要求。
同时,由于使用方案二的方法产生正弦波,其当前时刻的输出序列只与当前时刻行频和列频的输入有关,所以不会产生累积误差,适合用来产生长时间连续的DTMF信号。
综上所述,我们使用方案二来产生DTMF信号。
根据CCITT的规定,数字之间必须有适当长度的静音,因此编码器有两个任务,一是产生双音频信号的任务,二是静音任务。
由于采样频率为8000Hz,所以DSP有足够的计算时间,可以使用查询模式通过D/A转换器输出DTMF信号。
CCITT规定每秒传送/接收10个数字,即每个数字持续100ms。
由于1秒采样8000个点,则每个数字采样800个点。
由于代表数字的音频信号必须持续至少45ms,但不超过55ms。
100ms内其他时间为静音,以便区别连续的两个按键信号。
所以,需要设置800个点的缓存,其中400个点用于产生DTMF信号中的音频信号,另外400个点用于产生DTMF信号中的静音信号。
根据这样的设计,音频信号的持续时间为50ms,在45ms和55ms之间,满足CCITT的规定。
静音信号的持续时间为50ms。
2.DTMF信号的检测
DTMF检测是对进入解码端的信号进行检测,并把双音频信号转换成对应的数字信息。
由于数据流是连续的,为了保证DTMF检测的实时性,因此要求检测过程必须是实时连续的。
在输入信号中检测DTMF信号,需要在输入的数据信号流中连续地搜索DTMF信号频谱的存在。
检测过程有两部分的任务,一是在输入信号中提取频谱信息;二是检查检测结果的有效性。
任务一:
在输入信号中提取频谱信息
DTMF解码时在输入信号中搜索出有效的行频和列频。
计算数字信号的频谱可以采用DFT及其快速算法FFT,而在实现DTMF解码时,采用Goertzel算法要比FFT更快。
通过FFT可以计算得到信号所有谱线,了解信号整个频域信息,而对于DTMF信号只需关心其8个行频/列频及其二次谐波信息即可,二次谐波的信息用于将DTMF信号与声音信号区别开。
此时Goertzel算法能更加快速的在输入信号中提取频谱信息。
Goertzel算法实质是一个两极点的IIR滤波器,其算法原理框图如图3-2所示。
图3-2Goertzel算法原理框图
其传递函数为:
DTMF检测器的核心是Goertzel算法。
该算法利用二极点的IIR滤波器计算离散傅立叶变换值,能够快速高效地提取输入信号的频谱信息。
由于IIR滤波器是一个递归结构,它利用只有一个实系数的差分方程进行操作,并不像DFT或FFT算法那样需要计算数据块,而是每输入一个样值就执行一次算法。
DFT计算可以等价为:
在实际的DTMF检测中,只需DFT的幅度(本算法为平方幅度)信息就足够了,因此在Goertzel滤波器中,当N点(相当于DFT数据块的长度)样值输入滤波器后,滤波器输出伪DFT值vk(n),由vk(n)即可确定频谱的幅度平方。
其中k=f*N/fs,f为输入信号的频率,N为样值的个数,fs为抽样频率。
任务二:
检查检测结果的有效性
严格来讲,DTMF信号的有效性检验应该包括几项内容,在此不一一赘述。
由于严格意义上DTMF信号有效性的检查实现起来比较困难,所以在这里我们只是进行了简单的有效性检测。
当得到频谱的幅度平方
之后,将幅度平方与门限作比较。
门限的设定,应该保证能够检测到DTMF发送信号,同时应该保证不产生误判漏判的情况。
所以,门限的设定至关重要。
在我们看来,门限的取值应该满足下面两点要求:
一是门限的大小应该小于DTMF发送信号行频分量和列频分量的幅度平方,这样才能够有效地检测到信号;二是门限的取值也不能太小,否则噪声会对判决产生很大的影响。
同时,为了防止重复检测,下一个判决必须在检测到静音信号后才能有效。
四、程序设计、调试与结果分析
1.程序设计部分:
DTMF信号产生流程图如图4-1所示。
图4-1信号产生流程图
DTMF信号产生程序如下:
#include//程序头文件
#include
#include
#include
#include
#include
voiddelay(intperiod);//延时子程序delay
voidsend(intj);//判决子程序send
HANDLEhHandset;//codec句柄变量
s16out_buffer[800];//输出缓冲区,数据类型为S16
floatbuffer[800];//缓冲区,数据类型为float
s16num=0;//定义num,用于查询频率表
intcount=0;//定义count,用于控制发送的次数
intk=0;//定义k,用于控制采样点数
inti;
intj;
f32x,y;//定义x和y,用于存放发送的行频和列频
floatfs=8000;//定义fs为抽样频率8000Hz
floatpi=3.1415926;//定义PI的值
chartelephonenumber[18];//定义字符型数组telephonenumber
//用于存放键入的字符
floatfreq[16][2]={941,1336,//定义16行2列的二维数组,第一列用于
697,1209,//存放行频,第二列用于存放列频
697,1336,
697,1477,
770,1209,
770,1336,
770,1477,
852,1209,
852,1336,
852,1477,
697,1633,
770,1633,
852,1633,
941,1633,
941,1209,
941,1477
};
voidmain()//主程序main
{
intcnt=3;//cnt=3控制亮灯的次数为3次
if(brd_init(100))//初始化DSK板
{
return;
}
while(cnt--)
{
brd_led_toggle(BRD_LED0);//LED0亮
delay(1000);//延时1000个时间单位
brd_led_toggle(BRD_LED1);//LED1亮
delay(1000);//延时1000个时间单位
brd_led_toggle(BRD_LED2);//LED2亮
delay(1000);//延时1000个时间单位
}
//打开codec,获取DAC的句柄
hHandset=codec_open(HANDSET_CODEC);
//设置DAC的工作参数
codec_dac_mode(hHandset,CODEC_DAC_15BIT);//D/A工作在15bit模式
codec_adc_mode(hHandset,CODEC_ADC_15BIT);//A/D工作在15bit模式
codec_ain_gain(hHandset,CODEC_AIN_6dB);//模拟输入增益为6dB
codec_aout_gain(hHandset,CODEC_AOUT_MINUS_12dB);//模拟输出增益为
//-12dB
codec_sample_rate(hHandset,SR_8000);//D/A转换频率为8kHz
gets(telephonenumber);//gets函数,用于将键入的字符存入数组
j=0;
send(j);//调用send函数对发送的第一个字符进行判定
x=freq[num][0]/fs;//查表得行频,并赋给x
y=freq[num][1]/fs;//查表得列频,并赋给y
for(k=0;k<400;k++)
{//前400个点为音频信号,存入buffer
buffer[k]=(0.65*sin(2*pi*y*k)+0.8*sin(2*pi*x*k))*16384;
out_buffer[k]=buffer[k];//将float型强行转化为s16型
//后400个点为静音信号,存入buffer
buffer[k+400]=0;
out_buffer[k+400]=buffer[k];//将float型强行转化为s16型
}
i=0;
j=0;
while
(1)
{
while(!
MCBSP_XRDY(HANDSET_CODEC)){};//等待D/A转换器准备好
//发送
*(volatileu16*)DXR1_ADDR(HANDSET_CODEC)=buffer[i];
i++;
if(i==400)//采足400个样值点,完成第一次发送
{
i=0;
count++;
if(count==20)//控制每一个数反复发送20次
{
count=0;
j++;
if(j==16)//如果发送完16个字符,则返回
return;
send(j);//调用send函数,对发送的字符进行判定,返回num
x=freq[num][0]/fs;//查表得行频,并赋给x
y=freq[num][1]/fs;//查表得列频,并赋给y
for(k=0;k<400;k++)
{//前400个点为音频信号,存入buffer
buffer[k]=(0.65*sin(2*pi*y*k)+0.8*sin(2*pi*x*k))*16384;
out_buffer[k]=buffer[k];
//后400个点为静音信号,存入buffer
buffer[k+400]=0;
out_buffer[k+400]=buffer[k];
}
}
}
}
}
voidsend(intj)//判决子程序send,输入j的值,输出num的值
{
switch(telephonenumber[j])
{
case'1':
num=1;break;
case'2':
num=2;break;
case'3':
num=3;break;
case'4':
num=4;break;
case'5':
num=5;break;
case'6':
num=6;break;
case'7':
num=7;break;
case'8':
num=8;break;
case'9':
num=9;break;
case'A':
num=10;break;
case'B':
num=11;break;
case'C':
num=12;break;
case'0':
num=0;break;
case'D':
num=13;break;
case'*':
num=14;break;
case'#':
num=15;break;
}
}
voiddelay(intperiod)//延时子程序delay,运用了指令循环的原理,延时
{//时间的长短由输入period决定
inti,j;
for(i=0;i{
for(j=0;j>1;j++);
}
}
DTMF信号检测流程图如下:
DTMF信号检测程序基本部分如下:
#include//头文件
#include
#include
#include
#include
#include
HANDLEhHandset;//codec句柄变量
floatbuffer[256];//DTMF样点缓冲区,定义其容量为256
floatpi=3.1415926;
s16test[256];//定义数组test
s16data;
intk=0;
intdetect_result[256]={0};//缓存DTMF检测结果
intl=0;
voiddelay(intperiod);//延时子程序delay
voidDTMF_detect(void);//检测子程序DTMF_detect
voidmain()//主函数main
{
intcnt=3;//控制灯闪烁的次数为3次,如果灯循环
//亮三次,则程序运行正常
if(brd_init(100))//初始化DSK板
return;
while(cnt--)
{
brd_led_toggle(BRD_LED0);
delay(1000);
brd_led_toggle(BRD_LED1);
delay(1000);
brd_led_toggle(BRD_LED2);
delay(1000);
}
//打开codec,获取ADC的句柄
hHandset=codec_open(HANDSET_CODEC);
//设置D/A工作在15bit模式
codec_dac_mode(hHandset,CODEC_DAC_15BIT);
//设置A/D工作在15bit模式
codec_adc_mode(hHandset,CODEC_ADC_15BIT);
//设置输入增益为6dB
codec_ain_gain(hHandset,CODEC_AIN_6dB);
//设置输出增益为-6dB
codec_aout_gain(hHandset,CODEC_AOUT_MINUS_6dB);
//设置取样频率为8000Hz
codec_sample_rate(hHandset,SR_8000);
while
(1)
{
while(!
MCBSP_RRDY(HANDSET_CODEC)){};//等待A/D转换器输出数据
data=*(volatileu16*)DRR1_ADDR(HANDSET_CODEC);
test[k]=data;将A/D的输出存入数组test
buffer[k++]=data/16384.0;将16进制整数转化为浮点数存入数组buffer
if(k==256)
{k=0;//当采集满256个样点值后,调用DTMF_detect对采集
DTMF_detect();}//到的信号进行判决
}
}
voidDTMF_detect(void)
{
floatw[8],a[8][3];//数组w[8]用于存放
的系数
floatresult[8];//数组result[8]用于存放判决后的输出结果
inti,j,x,y;
//k=f*N/fs,N为DFT数据块的长度,这里取N=205,k的计算结果取整数
w[0]=2*cos(2*pi*18/205);//f=697Hz,k=18
w[1]=2*cos(2*pi*20/205);//f=770Hz,k=20
w[2]=2*cos(2*pi*22/205);//f=852Hz,k=22
w[3]=2*cos(2*pi*24/205);//f=941Hz,k=24
w[4]=2*cos(2*pi*31/205);//f=1209Hz,k=31
w[5]=2*cos(2*pi*34/205);//f=1336Hz,k=34
w[6]=2*cos(2*pi*37/205);//f=1477Hz,k=37
w[7]=2*cos(2*pi*42/205);//f=1633Hz,k=42
for(i=0;i<8;i++)
{a[i][0]=0;//vk(n-2)=0
a[i][1]=0;//vk(n-1)=0
for(j=0;j<205;j++)
{
a[i][2]=w[i]*a[i][1]-a[i][0]+buffer[j];//对vk(n)的值进行计算
a[i][0]=a[i][1];//对vk(n-2)的值进行更新
a[i][1]=a[i][2];//对vk(n-1)的值进行更新
}
result[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(result[i]>1500)//判决门限设置为1500
{
j++;
if(j==1)//第一个大于门限的是行频信号
x=i;//将行频信号的编号赋给x
elseif(j==2)//第二个大于门限的是列频信号
y=i;//将列频信号的编号赋给x
}
}
i=-2;
if(j==2)//利用行频信号的编号x和列频信号的y确定接收到的字
//符,并将其输出。
{
if(x==3&&y==5)
i=0;
elseif(x==0&&y==4)
i=1;
elseif(x==0&&y==5)
i=2;
elseif(x==0&&y==6)
i=3;
elseif(x==1&&y==4)
i=4;
elseif(x==1&&y==5)
i=5;
elseif(x==1&&y==6)
i=6;
elseif(x==2&&y==4)
i=7;
elseif(x==2&&y==5)
i=8;
elseif(x==2&&y==6)
i=9;
elseif(x==0&&y==7)
printf("TheDTMFsignalisA\n");
elseif(x==1&&y==7)
printf("TheDTMFsignalisB\n");
elseif(x==2&&y==7)
printf("TheDTMFsignalisC\n");
elseif(x==3&&y==7)
printf("TheDTMFsignalisD\n");
elseif(x==3&&y==4)
printf("TheDTMFsignalis*\n");
elseif(x==3&&y==6)
printf("TheDTMFsignalis#\n");
}
if(i!
=-2)
{
printf("TheDTMFsignalis%d.\r\n",i);
}
}
voiddelay(intperiod)//延时子程序
{
inti,j;
for(i=0;i