本帖最后由 jlass 于 2013-7-19 14:46 编辑
PS:我觉得标题应该改为痛苦分享才对
03年大学毕业之后,我接触到的第一个重要项目就是编写DTMF算法。当时公司使用的是一块外购的DTMF模块,由于其性能太烂加之为了降低成本,我就成为了公司的DSP算法工程师(这个职位还蛮唬人的,当客户听说我们公司有自己的DSP算法工程师的时候都觉得很牛)。
好了,背景介绍完了,现在开始正文。先简单介绍一下DTMF的原理。DTMF俗称双音多频,就是使用8个不同的频率相互交叉产生16个交叉点,每个交叉点对应一个按键。
编码的过程就是一个频域到时域的傅里叶变换,而解码就是对8个频点进行时域到频域的傅里叶逆变换。(不知道有没有记错:P)
因为当时我才刚毕业(大家都知道刚毕业的学生有多水,当然我也不例外),这种项目着实无从下手,不过这个时候来了一线曙光,我从网上下到了一份别人共享的DTMF源码,我就直接拿来试了一下,结果还真能用。之后的半年里我就在学习5416的汇编代码和理解那份源码的时间里度过。随着时间的流逝,我也慢慢看懂了那份代码,我对它的评价只有四个字“奇烂无比”(由于时间久远,我连源码都找不到了,不过留在我脑海里的只有这四个字,当然它对于我的入门确实也影响深远)。撇开其代码的bug不谈,代码本身应该是一个标准的DFT算法,没有经过任何的优化,但也写的中规中矩,当然问题也很明显,代码的运算量巨大,在当时这种DSP只有几十MIPS的时代是无法接受的。
这个时候来了第二份曙光,我们买到了一本DSP的教科书,里面有一个例子恰好是讲解DTMF的(其内容和附件里的ARDTMF完全相同,是谁抄谁的我想大家都明白),其采用了一个叫Goertzel的算法对DTMF的检测进行了优化,还详细讲解了DTMF检测的4个判断条件。
1、对应的两个频点的幅度足够大
2、对应的两个频点相互之间要差不多大(由于多普勒效应,频率越高的频偏越大,所以较高的频点幅度会相对小一点)
3、对应的两个频点相对于其他频点的幅度要大很多
4、对应的两个频点的二次协波要足够小(这一条我要重点说一下,因为这一直是我的一块心病。理论上这是为了区别DTMF与语音的,因为DTMF是单音,其二次协波应该是没有的,而语音一般都是有较高的二次协波分量的。可是我实际测试却发现,语音根本也没有二次协波分量,我为此验证过很多次,最后不得不面对这个事实。)
发送部分我后来改成查表法处理,及其简单,就不多说了。
到这里为止,DTMF的收发已经搞定了,然后就是路数的问题。一开始我是收到一个数据处理一个,结果最多只能处理4路。后来使用了DMA一次传输一帧数据,做到了16路,最后用DMA自动加载一次传输一帧乘以一个运算块的数据,做到了64路。后来公司又要求做128路,因为5416最多为160MIPS,而单路DTMF运算量为3MIPS不到,所以64路应该已经是极限了,于是我不得已想了个办法,接收两份数据但只处理一份,另一份直接扔掉,只要DTMF够长,倒也可以做到128路。当然,这只是一个特殊版,普通版还是64路的。
在DTMF正式使用之后,真正的问题终于出现了——误判。这是DTMF检测最难处理的问题(至少我认为是)。在话路安静的情况下,DTMF检测很完美,但是当需要二次拨号的时候,由于有欢迎词等内容的存在,DTMF的误判开始产生。而这还只是一个开始,电话会议的出现(会场有放音乐),让这个问题彻底激化。因为你既需要保证语音(包括音乐)不会被误判成DTMF,又要保证在讲话的同时按下的DTMF按键必须被收到。对此,我们想了很多的办法。最简单的莫过于组合按键,可是客户一般不会接受,还得从源头去解决。因为从检测的角度去处理是无解的(这就是为什么我对前面的第4个判断条件及其郁闷的原因了,这个最理想的判断方法,实测竟然是无效的),于是我们想到了对静音进行检测,这个方法对某些话机非常的有效,标准的DTMF应该是要包括DTMF音和静音的,那些话机就是这么处理的(业界良心啊),但更多的话机仅仅包括了DTMF音,于是皮球又回到了我们的身上(因为加入静音检测而产生了漏判)。虽然不能检测静音了,但是语音毕竟比DTMF的能量值要弱很多,所以退而求其次,我在算法里加入了DTMF音之后要跟一段能量值减弱一半以上的语音的判定。这样漏判是解决了,但是误判也增加了一些,算是勉强能用吧。
到此,我的第一份工作勉强算是结束了,剩下的就是无穷无尽的维护啊。
@zhangmangui @小跑堂
|
谢谢分享呀!