DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

这篇具有很好参考价值的文章主要介绍了DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1.引言

2.细节介绍

3.DTMF双音频信号的产生

4.DTMF双音频信号的检测

4.1(Goertzel)戈泽尔算法的使用

4.2戈泽尔算法的详细计算过程

 4.3计算的优化

4.3.1时间复杂度方面

4.3.2空间复杂度方面

4.4算法的具体实现

4.4.1从串行思维入手

4.4.2如何减少资源的使用

4.5仿真

5.源码分享

6.后续展望


1.引言

双音频信令国际上采用697HZ,770HZ,852HZ,941HZ,1209HZ,1336HZ,1477HZ,1633HZ共8个频率,仔细观察不难发现,这8个频率中,有4个低于1000HZ的频率有4个高于1000HZ的频率,我们称低于1000HZ的4个频率为低频群,高于1000HZ的4个频率为高频群。

4个低频群和4个高频群交叉摆放,他们将会有16个交叉点,这16个交叉点也正是16个按键,每个按键对应一个信令。如下图所示:

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

这个图很清晰得表现出了按键和音频频率的关系。虽然这个图长得非常像矩阵键盘,但是不要打同时按下两个按键的想法,毕竟双音频一次最多两个频率,按下两个按键至少有三个频率了,这样做是不讲武德的(手动狗头)。

2.细节介绍

第1章简要介绍了什么是DTMF,DTMF是用来做什么的。接下来详细介绍DTMF的具体的详细的规程。

DTMF信号的采样率为8KHZ。DTMF每秒可以发送10个信令,即每个信令的传输时间是100ms,数字音连续时间为45ms~55ms,其余时间均为静音。

3.DTMF双音频信号的产生

DTMF双音频信号虽然有4个低频,4个高频,但是在实际使用中同一时刻仅有2个频率,于是实际上仅需要两个DDS分别产生此时需要的一个高频和一个低频,然后叠加即可。

不过此处要注意一点,DDS产生的波形实质是以不同的速率来刷正弦表,起输出数据的更新频率是不同的,当然这个频率也显然和8KHZ 的采样时钟频率是不同的,所以使用两个DDS产生对应频率的波形后,一定要使用异步采样的方式,即使用8KHZ的时钟进行采样并相加。

DDS的实现本文不做介绍,DDS的代码资料如下(资源免费,如果需要收费请在评论区通知我):

DDS基于Verilog语言实现-嵌入式文档类资源-CSDN下载

4.DTMF双音频信号的检测

DTMF双音频信号是一高一低两种频率的正弦波叠加,那么最简单的思路当然是FFT变换,然后看不同频率的幅值进而判断传了什么信令按了什么键。

使用FFT的思路本是没有错的,但是略有杀鸡焉用宰牛刀。DTMF的检测中无需得知相位信息也无需计算所有的频点。因为DTMF的频率是已知的,直接针对已知的8种频率的频点进行计算即可。这就是戈泽尔算法(Goertzel)

4.1(Goertzel)戈泽尔算法的使用

首先,从感性角度和使用角度来看看戈泽尔算法。戈泽尔算法是仅对已知的个别频点进行幅值的计算。其“频点”的概念实质上和傅里叶变换中的频点意义是相同的,频率识别的分辨率及对应关系也是相同的。例如以1KHZ采样频率,采集持续1S的时间,即采集到1K个点,此时的频率分辨率为1HZ,此时若想要查看50HZ的幅值,只需要查看第51个频点的幅值即可(第1个频点是直流分量)。

综上内容稍加思索便可得知一个关键问题:采样点数和频点的确定。

既然我们已知4个低频和4个高频的频率,那么就确定采样点数,得到频率分辨率,并进而确定频点吧。

采样点越多,频率分辨率越高,但是也意味着更大量的计算和更高的延迟(采集数据的时间变长)因此,戈泽尔算法中常用的采样点数为205,其4个低频和4个高频对应的频点如下图所示。

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

 “频点”是整数,所以在取频点时会有一些误差,上图中频点是k,而误差正是最后一列所示。

4.2戈泽尔算法的详细计算过程

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

 这是计算的第一步,式中N是采样点数即205,k是频点,x(n)是n时刻接收到的信号的量化值。

从式子中可以发现,此时计算的是Q在n时刻的值,但是需要用到Q在n-1时刻的值和n-2时刻的值,由此可见此算法是不停迭代计算的。

至此,眼尖的小伙伴一定发现了两个问题:1. n取值到多少时结束;2. 初始状态下即n=0时,Qk(n-1)和Qk(n-2)咋办。

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

这两个问题接着用第二个公式解答。第二个公式中,首先阐明了n=0时刻Qk(n-1)即Qk(-1)和Qk(n-2)即Qk(-2)的取值,两者初始值均为0。然后解释了n的取值范围 即0到N(采样点,205)。

顺延着计算,当得到Qk(N)的值后,就可以计算幅值了。幅值的计算公式如下:

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

即Qk(N)的平方 + Qk(N-1)的平方 - 2cos(2*Π*频点/采样点数)*Qk(N)*Qk(N-1)

 4.3计算的优化

4.3.1时间复杂度方面

仔细观察计算过程不难发现,2cos(2*Π*频点/采样点数)在每次的计算中都会用到,由于频点和采样点数固定了,所以此值是固定的,在实际的计算中,可以提前计算出来,并以常数的姿态参与计算。

4.3.2空间复杂度方面

其次,Q这个序列在结果的求解中实际只使用了Qk(N),Qk(N-1),Qk(N-2)三个值,所以大可不必在计算时使用与采样点数等长的序列。仅需要长度为3的序列即可。

在实际计算中,得到Qk(n)后,增加一步序列位置交换即可。

具体如下:

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

在此步骤的计算中,下一步的计算即n+1时刻的计算,将要使用Qk(n)和Qk(n-1)计算得到Qk(n+1)。而Qk(n-2)已经用不到了。所以增加一步,Qk(n-2) = Qk(n-1),Qk(n-1) = Qk(n),Qk(n)的内存空间即可留出Qk(n+1)。

4.4算法的具体实现

4.4.1从串行思维入手

前节提到的公式所示的计算过程均为从上到下依次计算的思路,进行实现。其思路类似于C语言,但是细节上与C语言是不同的。首先比较一下这两种思路,首先看看C语言实现戈泽尔算法并在单片机上运行的代码:

	for(i=0;i<8;i++)
	{
		b[i][0]=0;
		b[i][1]=0;
		for(j=1;j<=205;j++)
		{
			b[i][2]=w[i]*b[i][1]-b[i][0]+dtmf[start+j-1];      
			b[i][0]=b[i][1];
			b[i][1]=b[i][2];
		}
		result[i]=b[i][1]*b[i][1]+b[i][0]*b[i][0]-w[i]*b[i][1]*b[i][0];   //信号幅值的平方
	}

两层循环,其中内层循环计算的正式如下公式所示内容:

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

外层公式计算最终结果,如公式:

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

可见,从理解上而言,C语言实现与论文中所描述的公式计算流程一模一样,非常易懂。但是,使用这种实现方式有个前提,即计算使用的数据在计算开始时就已经全部采集完毕,例如此例子中205点数据全部采集完毕后才可以开始计算,这将耗费大量的存储空间,FPGA中如果使用寄存器存放所有等采样点将是不现实的做法。

FPGA中需要每采样一个点,就开始做对应的计算,按照这个要求,我们修改一下上面的C语言代码:

    for(i=1; i<8; i++)
    {
        b[i][0] = 0;
        b[i][1] = 0;
    }

    for(j=1; j<=205; j++)
    {
        for(i=0;i<8;i++)
        {
			b[i][2]=w[i]*b[i][1]-b[i][0]+dtmf[start+j-1];      
			b[i][0]=b[i][1];
			b[i][1]=b[i][2];
        }
    }
    result[i]=b[i][1]*b[i][1]+b[i][0]*b[i][0]-w[i]*b[i][1]*b[i][0];   //信号幅值的平方

修改的精髓就在于,将两个for循环的位置互换,将205点的循环放在外面,这样就可以每来一个采样点单独对此采样点做计算工作了。

将此部分用Verilog语言重新编写后,核心代码为:

//放大7位
parameter coeff1 = 218;
parameter coeff2 = 209;
parameter coeff3 = 200;
parameter coeff4 = 190;
parameter coeff5 = 149;
parameter coeff6 = 129;
parameter coeff7 = 108;
parameter coeff8 = 72;

always @(posedge clk or negedge  rst_n) begin
    if(!rst_n)  begin
        status <= 0;
    end
    else    begin
        case(status)    
        0:  begin
            status <= status + 1;
            for(i=0; i<8; i=i+1)  begin
                b[i][0] <= 0;
                b[i][1] <= 0;
                b[i][2] <= 0;
            end
            result_697 <= result[0];
            result_770 <= result[1];
            result_852 <= result[2];
            result_941 <= result[3];
            result_1209 <= result[4];
            result_1336 <= result[5];
            result_1477 <= result[6];
            result_1633 <= result[7];
            // data_in <= $signed({{14{sample_data[11]}}, sample_data[10:0], 6'h00});
            data_in <= $signed({{24{sample_data[11]}}, sample_data[10:4]});
        end
        206:    begin
            status <= 0;
            result_697 <= (b[0][1]*b[0][1]+b[0][0]*b[0][0] - ((coeff1*b[0][0]*b[0][1])>>>7));//实际发现第三项还需要乘2,与除128对冲后,除64
            result_770 <= (b[1][1]*b[1][1]+b[1][0]*b[1][0] - ((coeff2*b[1][0]*b[1][1])>>>7));
            result_852 <= (b[2][1]*b[2][1]+b[2][0]*b[2][0] - ((coeff3*b[2][0]*b[2][1])>>>7));
            result_941 <= (b[3][1]*b[3][1]+b[3][0]*b[3][0] - ((coeff4*b[3][0]*b[3][1])>>>7));
            result_1209 <= (b[4][1]*b[4][1]+b[4][0]*b[4][0] - ((coeff5*b[4][0]*b[4][1])>>>7));
            result_1336 <= (b[5][1]*b[5][1]+b[5][0]*b[5][0] - ((coeff6*b[5][0]*b[5][1])>>>7));
            result_1477 <= (b[6][1]*b[6][1]+b[6][0]*b[6][0] - ((coeff7*b[6][0]*b[6][1])>>>7));
            result_1633 <= (b[7][1]*b[7][1]+b[7][0]*b[7][0] - ((coeff8*b[7][0]*b[7][1])>>>7));
        end
        default:    begin
            status <= status + 1;
            grz_calculate(data_in);
            // data_in <= $signed({{14{sample_data[11]}}, sample_data[10:0], 6'h00});
            data_in <= $signed({{24{sample_data[11]}}, sample_data[10:4]});
        end
        endcase
    end
end

task  grz_calculate(input integer grz_data_in);
    b[0][2]=((coeff1*b[0][1])>>>7) - b[0][0] + grz_data_in;
    b[0][0] = b[0][1];
    b[0][1] = b[0][2];
    b[1][2]=((coeff2*b[1][1])>>>7) - b[1][0] + grz_data_in;
    b[1][0] = b[1][1];
    b[1][1] = b[1][2];
    b[2][2]=((coeff3*b[2][1])>>>7) - b[2][0] + grz_data_in;
    b[2][0] = b[2][1];
    b[2][1] = b[2][2];   
    b[3][2]=((coeff4*b[3][1])>>>7) - b[3][0] + grz_data_in;
    b[3][0] = b[3][1];
    b[3][1] = b[3][2];        
    b[4][2]=((coeff5*b[4][1])>>>7) - b[4][0] + grz_data_in;
    b[4][0] = b[4][1];
    b[4][1] = b[4][2];     
    b[5][2]=((coeff6*b[5][1])>>>7) - b[5][0] + grz_data_in;
    b[5][0] = b[5][1];
    b[5][1] = b[5][2];     
    b[6][2]=((coeff7*b[6][1])>>>7) - b[6][0] + grz_data_in;
    b[6][0] = b[6][1];
    b[6][1] = b[6][2];     
    b[7][2]=((coeff8*b[7][1])>>>7) - b[7][0] + grz_data_in;
    b[7][0] = b[7][1];
    b[7][1] = b[7][2];         
endtask 

其中,always正是前文所述的C语言代码中的两个for循环和结果计算。首先,第0个状态,对应C语言代码的对b数组清零的for循环;其次,第1个到第205个状态,对应C语言的循环205次的for循环,最后第206个状态对应C语言的求解result的代码。

这种实现方式仿真后可以得到正确答案,且可以综合,但是由于其中用到了太多的并行乘法运算,手头现有的FPGA没有足够的资源,其次,DTMF原本也不是什么要求很高的功能,使用太多的资源在实际项目中也是不切合实际的。

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

 这是在EP4CE6E22C8N上综合的结果,需要这样两片半FPGA才能装下这个戈泽尔算法。然而此时的戈泽尔算法还没有加入2次谐波部分的计算,和多通道配合计算,因此,这种实现方式虽然很好理解,但是不切合实际。

4.4.2如何减少资源的使用

从上述代码中不难发现,每个乘法都是和固定数据绑定的,即无法复用乘法器,导致占用资源过多。因此,减少资源使用等方法很简单,即将需要做乘法的地方通过三个寄存器统一访问乘法器这样在综合时,就不会出现综合出来很多乘法器的情况了。其次,将算式拆分,没次只计算整个算式的一部分,多次计算最终得到结果。

例如计算:result[i]=b[i][1]*b[i][1]+b[i][0]*b[i][0]-w[i]*b[i][1]*b[i][0];

可以分为3步:

第一步:计算b[i][1]*b[i][1]和b[i][0]*b[i][0],共使用2个乘法器

第二步:计算第一步得到两个乘积的和,并计算w[i]*b[i][1]*b[i][0],共使用2个乘法器,复用第一步是使用的两个乘法器

第三步:将第二步求得的和与第二步求得的乘积相减,得到最终答案。

实际上,由于寄存器送数据会延迟一个时钟,此思路在FPGA上具体实现时需要4个时钟,代码如下:

1:  begin res_mul_dataa <= b[0][0]; res_mul_datab <= b[0][0]; res_mul_datac <= b[0][1]; res_mul_datad <= b[0][1]; result_counter <= result_counter + 1; end
2:  begin result_tempb <= mul_result + mul_resultb; res_mul_dataa <= coeff1; res_mul_datab <= (b[0][0]>>>3); res_mul_datad <= (b[0][1]>>>4); result_step <= 1; result_counter <= result_counter + 1;end
3:  begin result_temp <= mul_resultb; result_step <= 0; result_counter <= result_counter + 1;  end
4:  begin result_697 <= result_tempb - result_temp; result_counter <= result_counter + 1;  end

经过修改后,占用资源显著减少,可以在资源非常稀缺的FPGA,如EP4CE6E22C8N上配置。

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

这是增加2次谐波部分计算后外加PCM接收接口,G711编码解压扩等一系列操作后的资源占用情况。

4.5仿真

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

 通过仿真图可以清晰明了得看到,在每个8KHZ的时钟内,进行中间变量的计算。依次计算基频部分和2次谐波部分。

一直到205点全部计算完毕后,即第206个状态时,开始计算结果。

DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现

 从结果以倾斜分布就可明了得看出,其是串行一个个计算出来的。

至此在不改变任何外界信号的条件下,仅使用两个乘法器完成了同样的功能。

5.源码分享

Goertzel算法,基于Verilog实现,可仿真,可综合-嵌入式文档类资源-CSDN下载

完整的分享,里面tb开头等文件是testbeench,可以直接在仿真软件上运行。其余所有.v和.sv文件均可综合。现在已经在EP4CE6E22C8N和EP4CE10F17C8N上成功综合并运行。

6.后续展望

戈泽尔算法在DTMF双音频检测中占有举足轻重的地位,功能稳定且资源占用少的戈泽尔算法实现为整个DTMF双音频检测奠定了坚实的基础。后续需要结合PCM的32个时隙以及协调A律13折线编解码功能实现对多个通道的音频进行检测。文章来源地址https://www.toymoban.com/news/detail-425478.html

到了这里,关于DTMF双音频信令检测,基于Goertzel(戈泽尔算法),FPGA实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 音视频--DTMF信号发送及检测

    https://zh.wikipedia.org/wiki/%E5%8F%8C%E9%9F%B3%E5%A4%9A%E9%A2%91 https://www.cnblogs.com/lijingcheng/p/4454932.html 双音多频信号(英语:Dual-Tone Multi-Frequency,简称:DTMF),电话系统中电话机与交换机之间的一种信令,最常用于拨号时发送被叫号码。不过双音多频的发明,除了缩短拨号时间,也扩展

    2024年02月12日
    浏览(29)
  • 【毕业设计】基于程序化生成和音频检测的生态仿真与3D内容生成系统----程序化生成地形算法设计

    Input: Output: 程序化生成地形算法是一种在计算机中生成地形的方法,通常用于游戏开发和虚拟现实应用。下面是几种常见的程序化生成地形算法: Diamond-Square Algorithm(钻石-正方形算法) 该算法通过随机值填充网格的四个角落,然后计算中间点的高度值,不断重复直到整个网

    2024年02月01日
    浏览(33)
  • 基于插值算法和Gardner定时误差检测的OOK信号定时同步的FPGA实现

      本文介绍如何用FPGA实现基于插值算法的OOK信号定时同步,Verilog代码参考杜勇《数字调制解调技术的MATLAB与FPGA实现》。我们的目标是用外部提供50MHz时钟的zynq7100芯片实现400MHz采样频率和100Mbps的OOK数字基带信号的定时同步。   采用传统的锁相环技术实现定时同步时,本

    2024年02月16日
    浏览(31)
  • 基于FPGA的运动目标检测跟踪系统项目:从顶层架构设计到RTL编写与仿真,完整实现FPGA图像处理算法实现的项目流程。

    基于FPGA的运动目标检测跟踪系统项目 ,FPGA项目,FPGA图像处理 FPGA项目 采用帧间差分法作为核心算法,该项目涉及图像采集,颜色空间转换,帧间差分核心算法,腐蚀等形态学处理,目标定位,目标标识,图像显示等模块。 通过该项目可以学习到以下两方面内容 1.FPGA顶层架

    2024年04月16日
    浏览(50)
  • 三、音频隐写[Audacity、deepsound、dtmf2num、MMSSTV、虚拟声卡、MP3Stego]

    下载:https://www.audacityteam.org/download/windows/ 使用: 删除:先用左键长按拖着选中内容,然后选择软件最上方菜单栏的编辑,然后选择“删除”,最后点击文件的导出音频就能成功导出删减后的内容 增大音量 下载:https://github.com/Jpinsoft/DeepSound/releases 下载:http://aluigi.altervist

    2024年04月26日
    浏览(20)
  • 音频啸叫检测算法

            扩声系统用于放大声音,其典型应用包括:多媒体电教室、本地会议系统、助听器和人工耳蜗等,该电声系统至少包括一个麦克风、一个放大器以及一个扬声器。当麦克风与扬声器处于同一个声学环境时,扬声器信号经过反馈路径之后被 麦克风 采集,被放大器放大

    2024年04月15日
    浏览(17)
  • 基于手机信令数据的区域出行特征分析

            居民的日常出行活动是城市交通需求的直接反映。随着通信技术在交通出行调查中的应用和推广,交通数据获取方法发展很快。居民手机拥有量日益增长,手机已经可以作为居民随身的出行探测器,移动通信数据中会留下用户的电子脚印,实时反映居民的出行位置等

    2024年02月07日
    浏览(33)
  • 基于fpga的sobel边缘检测

    基于fpga的sobel边缘检测,部分的代码参考的是野火正点原子的代码和视频。通过matlab将图片转成txt文件,并编写verilog代码将处理好的数据再转成txt文件,同时通过matlab将txt文件再读取成图片。 文章目录 前言 一、verilog代码 1.sobel_edge代码 2.tb文件(读写txt文件) 注意 Sobel算法

    2024年02月08日
    浏览(51)
  • 基于手机信令数据研究交通出行特征具体实现

            这学期期末智慧交通大作业,我选择使用手机信令数据研究人类交通出行特征,期间我也查阅了许多资料,但绝大多数帖子只有手机信令数据的应用方向,没有具体实现做法。经过一两周的探索,我大概根据数据和应用方向实现了具体的方法。但本人才学疏浅,也

    2024年02月16日
    浏览(31)
  • 基于手机信令数据的数据清洗和出行方式识别

    数据来自2020年第十一届服务外包创新大赛A06赛题。 与定位误差不超过10米、时间采样均匀的GPS数据相比,手机信令数据的精确度较低且时间间隔不均匀,因此更需要对其进行数据清洗,下面对手机信令数据的特征 做具体分析:
 (1) 存在大量脏数据 在数据清洗领域中,脏数据

    2024年02月09日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包