这一篇在干嘛?

从 DFT 到 FFT:Cooley-Tukey、Good-Thomas、Winograd 三大算法与 Rader/Chirp-z 变换,频域处理的完整实现路径。 原书代码为 VHDL,本篇所有代码已改写为 Verilog。

  • 第6章 傅立叶变换
  • 6.1 傅立叶变换概述
  • 6.2 离散傅立叶变换算法
  • 例6.1 开窗操作
    1. 实序列的 DFT
  • 算法6.2 用一个 点DFT计算长度为 的DFT变换
    1. 利用 DFT 计算快速卷积
  • 算法 6.4 Bluestein Chirp-z 算法
  • 例6.5 的Rader算法
  • 例6.6 Rader算法的FPGA实现
  • 例6.7 的WinogradDFT算法
  • 6.3 快速傅立叶变换算法
  • 算法 6.8 Cooley-Tukey 算法
    1. 基 Cooley-Tukey算法
    1. 基 2 Cooley-Tukey 算法的实现
  • 算法6.10 高效的复数乘法器
    1. Cooley-Tukey 长度 256 FFT 算法的实现
  • 例6.11 HDL中的256点FFT
  • 定理 6.12 Good-Thomas 索引映射
  • 算法 6.13 Good-Thomas FFT 算法
  • 例 6.15 使用 Kronecker 乘积且 N=12 的 IDFT
  • 例 6.20 生成长度为 256 的 FFT IP
  • 6.4 与傅立叶相关的变换
  • 6.5 练习

自测一下

6.1 傅立叶变换概述

离散傅立叶变换(Discrete Fourier Transform, DFT)以及它的快速实现——快速傅立叶变换(Fast Fourier Transform, FFT),是数字信号处理的核心工具之一。简单说:DFT 把一段有限长的采样信号,从”时间的样子”翻译成”频率的样子”,告诉我们在哪些频率上有多少能量。没有它,频谱分析、滤波、图像压缩等任务在计算机和 FPGA 上都无从下手。

DFT 和 FFT 算法已经被”发明(和再发明)“了很多次。Heideman 等人指出,高斯早在计算机出现之前就使用过一种与今天的 Cooley-Tukey FFT 同类的算法。本章讨论图 6-1 中总结的最重要的一些算法。


图 6-1 DFT 和 FFT 算法的分类

分类术语沿用 Burrus的做法:按照输入序列与输出序列之间的(多维)索引映射关系来划分。凡是没有使用多维索引映射的算法统称为 DFT 算法(其中有些算法确实包含必不可少的少量计算,如 Winograd DFT 算法)。需要注意的是,DFT 算法和 FFT 算法并非彼此孤立:实际中的高效实现往往是二者组合的结果。例如 Rader 质数算法与 Good-Thomas FFT 的组合就产生了著名的 VLSI 实现。文献给出了用 PDSP 和 ASIC 实现 FFT 的许多设计示例,而 FPGA 已经可以实现一维和二维的 FFT 变换。

本章将讨论 4 种最重要的 DFT 算法和 3 种最常用的 FFT 算法,并按计算量比较它们的实现问题;最后还会介绍与傅立叶相关的变换(如 DCT),它是 JPEG、MPEG 等图像压缩标准的重要工具。若想深入钻研,可参阅基础 DSP 书籍和专门的 FFT 书籍。

6.2 离散傅立叶变换算法

这一节先复习 DFT 的关键性质,再介绍 Bluestein、Goertzel、Rader 和 Winograd 提出的几种基本 DFT 算法。

6.2.1 用 DFT 近似傅立叶变换

连续时间的傅立叶变换对定义为:

这个公式假设信号在时间和频率上都是连续、无限持续的。但真实系统只能处理有限个、量化过的采样点,于是就有了 DFT:在时域和频域各取 次采样:

逆变换 IDFT 为:

写成向量/矩阵形式就是:

用 DFT 去近似傅立叶频谱时,必须记住”采样”这个动作本身带来的两个影响——否则结果会悄悄失真:

  • 时域采样使频谱变成周期性的(周期为采样频率 )。按”香农采样定理”,只有当 的频率分量全部集中在低于奈奎斯特频率 的范围内时,DFT 才能合理地近似傅立叶变换;否则会出现混叠。
  • 频域采样使时间函数变成周期性的,也就是说 DFT 默认时间序列是周期性的。如果信号在 点窗口内没有完成整数个周期,就会产生”泄漏”(leakage)现象。因此如果 是周期信号,应尽量选择覆盖整数个周期的采样频率和分析窗口。

更实用的抗泄漏手段是使用窗函数:给数据乘上一个两边逐渐衰减为 0 的权函数。这类窗函数在第 3 章 FIR 滤波器设计上下文中已经讨论过(表 3-2)。图 6-2 给出了几种典型窗函数的时间和频率特性。


典型窗函数的时域形状


图 6-2 时域和频域内的窗函数

例 6.1 开窗操作

图 6-3(a) 给出了一个在采样窗口内不能完成整数个周期的正弦信号。它的理想傅立叶变换应该只在 处有两个单位脉冲函数,如图 6-3(b) 所示。图 6-3(c) 和 (d) 分别是采用框函数(即不加窗)和汉宁窗做 DFT 分析的结果。可以直观看到:框函数的旁瓣纹波明显更多;而汉宁窗虽然把泄漏压下去了,但主瓣宽度变宽了——频率分辨率略有牺牲。这就是加窗的基本折衷:用主瓣变宽换旁瓣降低。


图 6-3(a) DFT 窗口


图 6-3(b) 理想傅立叶变换


图 6-3(c) 采用框函数的 DFT


图 6-3(d) 采用汉宁窗的 DFT

6.2.2 DFT 的性质

表 6-1 总结了 DFT 最重要的性质。许多性质与连续傅立叶变换一致:变换是唯一(双射)的、满足叠加原理、实部与虚部通过希尔伯特变换相联系。

表 6-1 DFT 定理

定理
变换
逆变换
叠加
时间反向
共轭复数
实部
虚部
实偶数部分
实奇数部分
对称性
循环卷积
乘法
周期平移
帕斯瓦尔定理

正、逆变换结构的相似性还提供了一个巧妙的”反演技巧”。由向量/矩阵表示(式 6-5):

两边取共轭可得:

也就是说:只要对 做一次正向 DFT 再除以 ,就得到了逆 DFT。这样硬件上只需实现一个方向的变换核。

1. 实序列的 DFT

工程中输入往往是实数序列,而 DFT 本身按复数设计,这就有可省的计算。利用”实序列的频谱具有偶对称的实部和奇对称的虚部”这一性质(表 6-1),可以合成两个经典技巧:要么用一个 点 DFT 同时算两个 点实序列,要么用一个 点 DFT 算一个长度 的实序列。

算法 6.2 用一个 点 DFT 计算长度为 的 DFT

由时间序列 计算 点 DFT :

  1. 构造 点复序列 ,(偶数位放实部、奇数位放虚部)。
  2. 计算 。
  3. 最后组合:

其中 。除了一次 点 DFT(或 FFT)之外,额外开销只有来自旋转因子 的 次实数加法和乘法——用一半规模的变换算出了两倍长度的谱。

算法 6.3 用一个 点 DFT 计算两个长度为 的 DFT

要同时计算 和 :

  1. 构造 点复序列 ,。
  2. 计算 。
  3. 最后分离:

其中 。原理是:实序列的频谱共轭对称,纯虚序列的频谱反对称,两者叠加后仍可无损拆开。除一次 点 DFT 外,额外开销只有 次实数加法。

2. 利用 DFT 计算快速卷积

DFT(或 FFT)最常见的用途之一是计算卷积:时域卷积对应频域相乘。先把两个序列变换到频域,做标量点积,再变换回时域。但要注意关键区别:DFT 算的是循环卷积,而不是线性卷积。用 FFT 做快速卷积时必须处理这一点,于是产生了两种经典方法:

  • 重叠保留(overlap-save):直接做循环卷积,然后简单放弃边界处被”绕回”污染的采样点;
  • 重叠相加(overlap-add):对信号和滤波器补 0,再把各段部分结果直接相加拼接成完整输出。

快速卷积的输入通常是实序列,因此可以用实变换(如练习 6.15 讨论的 Hartley 变换)高效完成。Hartley 变换也能构造类 FFT 算法,与复变换相比性能可提高两倍。如果手头只有复数 FFT 程序,可以套用算法 6.2 或 6.3。图 6-4 给出一种与算法 6.3 类似的替代方案:用一个 点 DFT 实现两个 点变换——实部用作 DFT,虚部用作 IDFT,根据卷积理论,逆变换时需要用到虚部。


图 6-4 采用复数 FFT 的实数卷积

此外,若实值滤波器的 DFT(满足 )已经离线算好,频域内只需要 次乘法即可完成 。

6.2.3 Goertzel 算法

如果只需要频谱中的某一个分量 ,用整套 DFT 就太浪费了。把 DFT 的单项展开:

将所有 用公共因子 逐步括起来,就得到 Horner 嵌套形式:

这是一个可行的递归计算——这就是 Goertzel 算法,图 6-5 给出了图形解释。递归从输入序列的最后一个值 开始计算,经过 步递推后,输出端就得到 这一个频谱值。


图 6-5 长度为 4 的 Goertzel 算法

如果需要计算好几个相邻的频谱分量,还可以把 类型的因子两两组合,得到一个带分母的二阶系统:

这样一来,所有的复数乘法都简化成了实数乘法,硬件代价大幅下降。

什么时候用 Goertzel?判断标准很简单:若只需要少量频谱分量,Goertzel 非常划算;若要算整个 DFT,计算量仍是 数量级,与直接计算相比就没有任何优势了——此时该换 FFT。

6.2.4 Bluestein Chirp-z 变换

Bluestein Chirp-z 变换(CZT)的核心技巧是把 DFT 指数 二次展开:

代入 DFT 定义后,原本”每个输出都要乘遍所有输入”的 交叉项消失了,DFT 变成:

这个求和正是一个线性卷积。图 6-6 给出了算法的图形解释,于是得到:


图 6-6 Bluestein Chirp-z 算法

算法 6.4 Bluestein Chirp-z 算法,DFT 的计算分为 3 步:

  1. 与 的 次乘法(预调制);
  2. 与 的线性卷积(核心步骤,可用 FIR 滤波器实现);
  3. 结果再与 的 次乘法(后调制)。

一次完整变换需要一个长度为 的卷积加 次复数乘法。与后面要讲的 Rader 算法相比,CZT 最大的优点是:变换长度 不必是质数,可以定义成任意长度。

Narasimha等人进一步注意到:CZT 中 FIR 滤波器部分的许多系数其实是无关紧要或彼此相同的。例如长度为 8 的 CZT,其 FIR 滤波器长度为 14,但图 6-7 中只有 4 个不同的复系数——分别是 、 和 ,也就是说真正需要实现的不可或缺的实系数只有 2 个。