Goertzel算法在音调检测中的应用

来源:百度文库 编辑:神马文学网 时间:2024/05/01 12:17:19
上网时间: 2003年07月13日
采用快速傅立叶变换(FFT)算法程序可以方便地检测音频信号中的单个或多个音调,当只需检测少数几个频率时,本文介绍的Goertzel算法将更有效,该算法在进行音调检测时比快速傅立叶变换(FFT)所需的CPU资源少得多,运算速度更快。
在很多应用中都要求进行音调检测,例如:双音多频信号(DTMF)解码,呼叫过程(拨号音、忙音等)解码,频率响应测试(发送一个音调,同时将结果读回)。在频率响应测试中,如果在一定频率范围内进行测量,那么得到的频响曲线中可能会包含丰富的信息,例如从电话线的频响曲线可以知道线上是否有负载线圈(电感)。
尽管针对以上应用均有专用IC,但采用软件来实现这些芯片的功能时所需成本比采用专用芯片低很多。然而,很多嵌入式系统都不具备进行连续实时FFT处理的能力,这时就适合采用Goertzel算法。本文将对Goertzel基本算法和Goertzel优化算法进行讨论。
采用Goertzel基本算法能得出与常规离散傅立叶变换(DFT)或FFT相同的频率实部和虚部。如果需要的话,还可以从该频率实部和虚部中算出幅度和相位信息。Goertzel优化算法则比Goertzel基本算法更快也更简单,但Goertzel优化算法并不给出频率实部和虚部分量,它只能给出相关的幅度平方。如果需要幅度信息,可通过对该结果开方得到,但该方法无法得到相位信息。
Goertzel基本算法
Goertzel基本算法在每次采样后立即进行处理,在每个第N次采样进行一次音调检测。在采用FFT算法时,我们要对成块的采样进行处理,但这并不意味着必须按块来处理数据。数字处理的时间很短,因此如果每次采样都存在一次中断,那么这些数字处理完全可以在中断服务程序(ISR)内完成。或者,如果系统中存在采样缓存,那么可以持续采样,然后进行批处理。
在真正运行Goertzel算法之前,必须进行下面的初步计算:
1. 决定采样率;
2. 选择块大小,即N;
3. 预先进行一次余弦和正弦计算;
4. 预先计算一个系数。
这些计算均可以预先完成,然后硬编码到程序中,从而节省RAM和ROM空间,也可以动态方式计算。
选择合适的采样率
实际上,采样率可能已经由应用本身决定了。例如,在电信应用中普遍采用8kHz的采样率,即每秒8,000个采样。又如,模数转换器(或编解码器)的工作频率可能是由一个我们无法控制的外部时钟或外部晶振决定。
但如果我们可以选择采样率,那么就必须遵循奈奎斯特采样定理:采样率至少不低于最高信号频率的两倍。这是是因为如果我们要检测多个频率,那么采用更高的采样率可能会得到更好的结果。而且我们都希望采样率与每一个感兴趣的频率之间均呈整数倍关系。
块大小的设置
Goertzel算法中的块大小N与相应的FFT中的点数类似,它控制了频率分辨率的大小。例如,若采样率为8kHz,而N为100个采样,那么频率分辨率就是80Hz。
这就可能使我们为了获取最大的频率分辨率而尽量将N取高。然而N越大,检测到每个音调所需的时间就越多,因为我们必须等所有这N个采样都完成后才能开始处理。例如,采样率为8kHz时,累积800个采样需要100ms。若想缩短检测音调的时间,就必须适当调整N的值。
影响N的选择的另一个因素是采样率和目标频率之间的关系。比较理想情况是目标频率在相应的频率分辨率的中点范围内,也就是说,我们希望目标频率是sample_rate/N比值的整数倍。值得庆幸的是,Goertzel算法中的N与FFT中不同,不必是2的整数次幂。
预计算常数
在采样率和块大小确定之后,只须通过下面5个简单的计算来得出处理时所需要的常数:


每一次采样处理中都需要3个变量,我们称其为Q0‘、Q1‘和Q2。Q1是前一次采样处理的Q0值,Q2是在两次采样前的Q0值(或Q1在本次采样前的值)。
在每个采样块的开始时,都必须将Q1和Q2初始化为0。每个采样都需要按照下面三个等式进行计算:

在进行N次预采样计算之后,可以检测到音调是否存在。

这时只需进行一次简单的幅度门限测试就可以判断出是否有音调存在。之后,将Q2和Q1复位到0,开始下一个块的处理。
Goertzel优化算法
Goertzel优化算法比Goertzel基本算法所需的计算量小,但这是以损失相位信息为代价。
在Goertzel优化算法中每个采样处理完全一样,但处理的结果与Goertzel基本算法不同。在Goertzel基本算法中,通常需要计算信号的实部和虚部,然后将实部和虚部的计算结果转换为相应的幅度平方。而在优化Goertzel算法中则不需计算实部和虚部,直接计算下式:

Goertzel算法演示程序
列表1是我们编写的一个演示程序以便读者测试Goertzel算法。其中的代码均采用免费的DJGPPC/C++编译器编写和测试。读者可通过修改文件顶部的#defines语句来改变N值、采样频率(sampling_rate)和目标频率(target_frequency)。
该演示程序可进行两种演示。演示一,采用Goertzel优化算法和Goertzel基本算法计算三个不同的合成信号的相关幅度平方值和幅度值。这三个信号中,一个频率低于目标频率,一个与目标频率相等,一个高于目标频率。通过演示程序可以很容易发现,不论采用哪种算法所得到的结果几乎相同。同时,检测结果在目标频率附近出现峰值。
在第二个演示中,我们进行了频率扫描仿真,并只给出了Goertzel基本算法的处理结果。可以看到,检测输出结果在目标频率附近出现非常明显的峰值。列表2给出了列表1中代码的输出。
为了避免程序代码出现误检,我们可能还需要通过一种去抖动技术来限制原始检测器的读出结果,例如,要求必须在一系列处理中多次检测到某音调之后,才能向用户报告检测到音调的存在。
欲了解更多信息,请查阅:
www.analogdevices.com/library/dspManuals/Using_ADSP-2100_Vol1_books.html
www.analogdevices.com/library/dspManuals/pdf/Volume1/Chapter_14.pdf
www.analogdevices.com/library/dspManuals/Using_ADSP-2100_Vol2_books.html
www.analogdevices.com/library/dspManuals/pdf/2100Chapter_8.pdf
www.delorie.com/djgpp/
作者:Kevin Banks
嵌入式系统技术顾问
Email: kbanks@hiwaay.net