分段DFT(如何将长DFT拆成多个短DFT?)

  离散傅里叶变换是将时域信号转化为频域信号的算法,在信号处理领域中应用广泛。当要对一段很长的时域数据进行傅里叶变换时,可能会面临以下问题:
  (1) 数据过长,存储深度无法满足需求
  (2) 串行计算无法满足效率需求
  为应对上述问题,可考虑将长FFT拆分成多个短FFT来实现。本文将详细告诉你怎么做!

   1. 原理推导
  DFT变换的表达式为
X [ k ] = ∑ n = 0 N − 1 x [ n ] e x p ( − j 2 π N k n ) , 0 ≤ k < N a , 0 ≤ n < N a (1) X[k]=\sum_{n=0}^{N-1}x[n]exp(-j\frac{2\pi}{N}kn),0 \leq k < N_a, 0\leq n <N_a \tag{1} X[k]=n=0N1x[n]exp(jN2πkn),0k<Na,0n<Na(1)
  其中, x [ n ] x[n] x[n]是长度为 N N N的时域序列, X [ k ] X[k] X[k]是频域序列。现在要将原始时域序列切成 M M M段(每段长度为 L = N / M L=N/M L=N/M)分别进行DFT变换,期望通过这 M M M段短的FFT变换结果重构出完整的频域序列 X [ k ] X[k] X[k]。下面一步一步对(1)进行改造。首先将原始长时域序列切成 M M M段变为 x [ m , l ] , 0 ≤ m < M , 0 ≤ l < L x[m,l],0\leq m<M,0\leq l<L x[m,l],0m<M,0l<L,此时(1)中的时域序列索引 n n n变为 n = m L + l n=mL+l n=mL+l,将其带入(1)有:
X [ k ] = ∑ m = 0 M − 1 ∑ l = 0 L − 1 x [ m , l ] e x p ( − j 2 π M L k ( m L + l ) ) (2) X[k]=\sum_{m=0}^{M-1}\sum_{l=0}^{L-1}x[m,l]exp(-j\frac{2\pi}{ML}k(mL+l))\tag{2} X[k]=m=0M1l=0L1x[m,l]exp(jML2πk(mL+l))(2)
  下面需要对(2)式中的 k k k进行分段, k k k表示频率索引,每一个频点上的值都应该由所有分段时域时域数据共同作用得到,所以可将 k k k替换为 k = q M + p , 0 ≤ q < L , 0 ≤ p < M k=qM+p, 0\leq q <L, 0\leq p<M k=qM+p,0q<L,0p<M,由此有:
X [ k ] = X [ q , p ] = ∑ m = 0 M − 1 ∑ l = 0 L − 1 x [ m , l ] e x p ( − j 2 π M L ( q M + p ) ( m L + l ) ) = ∑ m = 0 M − 1 ∑ l = 0 L − 1 x [ m , l ] e x p ( − j 2 π q m ) e x p ( − j 2 π L q l ) e x p ( − j 2 π M p m ) e x p ( − j 2 π M L p l ) = ∑ l = 0 L − 1 [ [ ∑ m = 0 M − 1 x [ m , l ] e x p ( − j 2 π M p m ) ] e x p ( − j 2 π M L p l ) ] e x p ( − j 2 π L q l ) X[k]=X[q,p]=\sum_{m=0}^{M-1}\sum_{l=0}^{L-1}x[m,l]exp(-j\frac{2\pi}{ML}(qM+p)(mL+l))\\ =\sum_{m=0}^{M-1}\sum_{l=0}^{L-1}x[m,l]exp(-j2\pi qm)exp(-j\frac{2\pi}{L}ql)exp(-j\frac{2\pi}{M}pm)exp(-j\frac{2\pi}{ML}pl)\\=\sum_{l=0}^{L-1}[[\sum_{m=0}^{M-1}x[m,l]exp(-j\frac{2\pi}{M}pm)]exp(-j\frac{2\pi}{ML}pl)]exp(-j\frac{2\pi}{L}ql) X[k]=X[q,p]=m=0M1l=0L1x[m,l]exp(jML2π(qM+p)(mL+l))=m=0M1l=0L1x[m,l]exp(j2πqm)exp(jL2πql)exp(jM2πpm)exp(jML2πpl)=l=0L1[[m=0M1x[m,l]exp(jM2πpm)]exp(jML2πpl)]exp(jL2πql)
  根据上面的推导过程,可将分段DFT算法分成如下步骤:
  (1)将原始时域序列 x [ n ] x[n] x[n]分成 M × L M\times L M×L的矩阵 x [ m , l ] , 0 ≤ m < M , 0 ≤ l < L x[m,l],0\leq m<M,0\leq l<L x[m,l],0m<M,0l<L,示意图如下:
在这里插入图片描述

图1. 原始时域序列格式变换示意图

  (2)按列计算DFT,得到 X 1 [ p , l ] = ∑ m = 0 M − 1 x [ m , l ] e x p ( − j 2 π M p m ) X_1[p,l]=\sum_{m=0}^{M-1}x[m,l]exp(-j\frac{2\pi}{M}pm) X1[p,l]=m=0M1x[m,l]exp(jM2πpm),该步骤总共需要进行 L L L次的 M M M点DFT,示意图如下:
在这里插入图片描述

图2. 按列进行DFT变换示意图

  (3)对上述 X 1 [ p , l ] X_1[p,l] X1[p,l]逐点乘以调制因子得到 X 2 [ p , l ] = X 1 [ p , l ] e x p ( − j 2 π M L p l ) ] X_2[p,l]=X_1[p,l]exp(-j\frac{2\pi}{ML}pl)] X2[p,l]=X1[p,l]exp(jML2πpl)]
  (4)对上述 X 2 [ p , l ] X_2[p,l] X2[p,l]按逐行进行DFT变换得到 X [ p , q ] = ∑ l = 0 L − 1 X 2 [ p , l ] e x p ( − j 2 π L q l ) X[p,q]=\sum_{l=0}^{L-1}X_2[p,l]exp(-j\frac{2\pi}{L}ql) X[p,q]=l=0L1X2[p,l]exp(jL2πql),该步骤总共需要进行 M M M L L L点DFT,操作示意图如下图所示
在这里插入图片描述

图3. 按行进行DFT变换示意图

  (5)将上述 X [ p , q ] X[p,q] X[p,q]进行重新整理,首先需对其进行转置得到 X [ q , p ] X[q,p] X[q,p],然后按照得到 X [ k ] X[k] X[k],示意图如下:
在这里插入图片描述

图4. 数据重排示意图

【备注】: (1)上面的分析过程同样适用于傅里叶逆变换过程;(2)可分段的前提是原始数据长度不是质数。

   2. 计算量分析
   一般情况下,离散傅里叶变换采用DFT算法实现, N N N点DFT需要 N 2 N^2 N2次复数乘法和 N ( N − 1 ) N(N-1) N(N1)次复数加法。当点数 N N N为2的整数次幂时,可利用蝶形算法进行加速,此时DFT将转化为FFT进行计算, N N N点FFT需要 N 2 l o g 2 N \frac{N}{2}log_2N 2Nlog2N次复数乘法和 N l o g 2 N Nlog_2N Nlog2N次复数加法。基于此本小节对原始长FFT和分段FFT的计算量进行比较。

实现方法(DFT或FFT)长FFT分段FFT
DFT N 2 N^2 N2次复乘+ N ( N − 1 ) N(N-1) N(N1)次复加 M ( L 2 ) M(L^2) M(L2)次复乘+ M [ L ( L − 1 ) ] M[L(L-1)] M[L(L1)]次复加
+
N N N次复乘+ N N N次复加
+
L ( M 2 ) L(M^2) L(M2)次复乘+ L [ M ( M − 1 ) ] L[M(M-1)] L[M(M1)]次复加
=
N ( L + M + 1 ) N(L+M+1) N(L+M+1)次复乘+ N ( L + M − 1 ) N(L+M-1) N(L+M1)次复加
FFT N 2 l o g 2 N \frac{N}{2}log_2N 2Nlog2N次复乘和 N l o g 2 N Nlog_2N Nlog2N次复加 M ( L 2 l o g 2 L ) M(\frac{L}{2}log_2L) M(2Llog2L)次复乘+ M [ L l o g 2 L ] M[Llog_2L] M[Llog2L]次复加
+
N N N次复乘+ N N N次复加
+
L ( M 2 l o g 2 M ) L(\frac{M}{2}log_2M) L(2Mlog2M)次复乘+ L [ M l o g 2 M ] L[Mlog_2M] L[Mlog2M]次复加
=
N 2 l o g 2 N + N \frac{N}{2}log_2N+N 2Nlog2N+N次复乘+ N l o g 2 N + N Nlog_2N+N Nlog2N+N次复乘

   显然,若分段前后都采用DFT进行计算,则一般情况下分段长度相比原始长度具有明显的计算量优势。若分段前后都采用FFT进行计算,则分段长度的计算量大于原始长度。即分段的做法并不能保证计算量上的优势,它的好处在于各个分段可以并行处理,在某些场景下可降低对计算量或存储深度的要求。

   3. 仿真验证
  仿真生成一个单频信号,比较用原始长度进行一次DFT变换的结果和分段做DFT的结果,结果如下,两者一致性较高,证明了上述分段算法的有效性。
在这里插入图片描述

图5. 仿真结果

【附录1】:仿真代码

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # 使用微软雅黑字体
plt.rcParams['axes.unicode_minus'] = False  # 处理负号显示异常


if __name__ == "__main__":
    # 设置参数
    N = int(1e6)
    M = int(1e3)
    L = int(N / M)
    # 仿真生成信号
    fs = 1000
    f0 = 101
    t = np.arange(0, N, 1) / fs
    sig = np.sin(2 * np.pi * f0 * t)

    # 步骤1:转换信号格式
    batch_sig = np.reshape(sig, (M, L))

    # 步骤2:按列计算FFT
    X_1 = np.fft.fft(batch_sig, axis=1)

    # 步骤3:乘以调制因子
    coef = np.zeros((M, L)).astype(complex)
    for m in range(M):
        coef[m, :] = np.exp(-1j * 2 * np.pi * np.arange(0, L, 1) / N)
    X_2 = X_1 * coef

    # 步骤4:逐列进行FFT
    X = np.fft.fft(X_2, axis=0)

    # 步骤5:转换信号格式
    X = X.T.ravel()

    # 比较分段前后的结果
    plt.figure()
    plt.plot(np.linspace(-fs / 2, fs / 2, N), 20 * np.log10(np.abs(np.fft.fftshift(np.fft.fft(sig)))), 'ro-')
    plt.plot(np.linspace(-fs / 2, fs / 2, N), 20 * np.log10(np.abs(np.fft.fftshift(X))), 'b.-')
    plt.xlabel('Frequency(Hz)')
    plt.ylabel('Amplitude(dB)')
    plt.legend(['original', 'batch'])
    plt.show()
    pass
  • 43
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DFT(离散傅里叶变换)和 FFT(快速傅里叶变换)都是用来计算信号频谱的算法,但是 FFT 是一种比 DFT 更快的算法。如果您已经实现了基于时窗的 DFT 频谱分析算法,并且想将其更新为 FFT 算法,可以按照以下步骤进行: 1. 选择合适的 FFT 库 与 DFT 不同,FFT 可以高效地计算大型信号的频谱,因此有很多 FFT 库可供选择。一些常见的 FFT 库包括:Intel MKL、FFTW、cuFFT 等。您可以根据自己的需求和计算平台选择合适的 FFT 库。 2. 将 DFT 转换为 FFT DFTFFT 的核心思想是相同的,只是 FFT 是通过重复使用一些中间结果来减少计算量的。因此,将 DFT 转换为 FFT 的过程相对简单,只需要将 DFT 的计算过程改为 FFT 的计算过程即可。例如,使用 FFTW 库来计算 DFT 的代码如下: ```matlab % 定义信号和窗函数 x = ...; win = ...; % 定义 FFT 相关参数 n_fft = length(win); n_overlap = n_fft / 2; n_hop = n_fft - n_overlap; % 初始化频谱矩阵 spec = zeros(n_fft/2+1, length(x)/n_hop); % 计算每个时刻的频谱 for i = 1:n_hop:length(x)-n_fft+1 % 从信号中截取一个窗口 x_win = x(i:i+n_fft-1) .* win; % 计算 DFT X = fft(x_win); % 将 DFT 转换为幅度谱 spec(:, (i-1)/n_hop+1) = abs(X(1:n_fft/2+1)); end ``` 将上面的代码中的 `fft` 函数替换为 FFTW 库中的 `fftw` 函数即可将 DFT 转换为 FFT。 3. 时窗不变 由于 FFTDFT 的计算过程是相同的,只是 FFT 能够更快地计算出信号频谱,因此在将 DFT 更新为 FFT 的过程中,时窗不需要进行任何修改。只需要将 DFT 的计算过程改为 FFT 的计算过程即可。 希望这些信息能对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值