DSP课程设计DTMF信号的产生及检测Word文件下载.docx
《DSP课程设计DTMF信号的产生及检测Word文件下载.docx》由会员分享,可在线阅读,更多相关《DSP课程设计DTMF信号的产生及检测Word文件下载.docx(33页珍藏版)》请在冰点文库上搜索。
![DSP课程设计DTMF信号的产生及检测Word文件下载.docx](https://file1.bingdoc.com/fileroot1/2023-5/9/5bd47b2f-b2e3-4c38-aa30-6d110c1f80b5/5bd47b2f-b2e3-4c38-aa30-6d110c1f80b51.gif)
为输出正弦波的幅度。
该式初值为
。
其中,上面一个数字振荡器用于产生行频,下面一个数字振荡器用于产生列频,将行频信号和列频信号通过加法器进行叠加即可产生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<
stdio.h>
//程序头文件
math.h>
type.h>
board.h>
codec.h>
mcbsp54.h>
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亮
brd_led_toggle(BRD_LED2);
//LED2亮
//打开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;
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
{//前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
}
}
voidsend(intj)//判决子程序send,输入j的值,输出num的值
{
switch(telephonenumber[j])
case'
1'
:
num=1;
break;
case'
2'
num=2;
3'
num=3;
4'
num=4;
5'
num=5;
6'
num=6;
7'
num=7;
8'
num=8;
9'
num=9;
A'
num=10;
B'
num=11;
C'
num=12;
0'
num=0;
D'
num=13;
*'
num=14;
#'
num=15;
}
voiddelay(intperiod)//延时子程序delay,运用了指令循环的原理,延时
{//时间的长短由输入period决定
inti,j;
for(i=0;
i<
period;
i++)
for(j=0;
j<
period>
>
1;
j++);
DTMF信号检测流程图如下:
DTMF信号检测程序基本部分如下:
//头文件
floatbuffer[256];
//DTMF样点缓冲区,定义其容量为256
s16test[256];
//定义数组test
s16data;
intdetect_result[256]={0};
//缓存DTMF检测结果
intl=0;
//延时子程序delay
voidDTMF_detect(void);
//检测子程序DTMF_detect
voidmain()//主函数main
//控制灯闪烁的次数为3次,如果灯循环
//亮三次,则程序运行正常
while(cnt--)
//打开codec,获取ADC的句柄
hHandset=codec_open(HANDSET_CODEC);
//设置D/A工作在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(!
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
i<
8;
i++)
{a[i][0]=0;
//vk(n-2)=0
a[i][1]=0;
//vk(n-1)=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;
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;
i=2;
y==6)
i=3;
elseif(x==1&
i=4;
i=5;
i=6;
elseif(x==2&
i=7;
i=8;
i=9;
y==7)
printf("
TheDTMFsignalisA\n"
);
TheDTMFsignalisB\n"
TheDTMFsignalisC\n"
elseif(x==3&
TheDTMFsignalisD\n"
TheDTMFsignalis*\n"
TheDTMFsignalis#\n"
if(i!
=-2)
TheDTMFsignalis%d.\r\n"
i);
voiddelay(intperiod)//延时子程序