如何进行 FFT 分数时间延迟(已解决)

信息处理 fft 过滤器 Python
2021-12-26 10:42:46

我正在尝试使用 FFT 对信号进行时移,但是我遇到了一些奇怪的效果,这些效果取决于时移的大小。我需要能够将时间移动任意量 - 即浮点时间。我正在使用该线程中的方法:

这是我的代码:

import numpy as np
import matplotlib.pyplot as plt

f1 = 1.8
f2 = 2.6
#try tDelay = .02002 and tDelay = .0205
tDelay = .0205 #seconds
samples = 1024 #number of samples in the time interval
tstart = 0.0
tend = 1.0

# create a waveform to use for the time shifting
samplePeriod = (tend - tstart) / (samples)
print("\nThe sampling period is %f seconds" % samplePeriod)
print("The time delay is %f seconds" % tDelay)
tDelayInSamples = tDelay / samplePeriod
print("The time delay in samples is %f samples" % tDelayInSamples)
timeList = np.linspace(tstart, tend, samples)
waveform = np.sin(2 * np.pi * f1 * timeList) + np.sin(2 * np.pi * f2 * timeList)

# do the time shifting
fftOut = np.fft.fft(waveform)
N = fftOut.shape[0]
k = np.linspace(0, N-1, N)
phaseShiftFunction = np.exp((-2*np.pi*1j*k*tDelayInSamples)/(N))
fftWithDelay = np.multiply(fftOut, phaseShiftFunction)
waveform2 = np.fft.ifft(fftWithDelay)

plots = 1
plt.subplot(plots, 1, 1)
plt.plot(waveform)
plt.plot(waveform2)
plt.show()

如果您使用 .02002 和 .0205 之类的 tDelay 运行上述代码,您将看到恢复的信号完全不同。我不明白为什么会这样,或者该怎么做。

良好的时移示例(tDelay = .0205):

不良时移示例(tDelay = .02007):

编辑

经过相当大的努力,我相信我已经解决了这个问题。这里有两个关键见解:

  1. 当您进行分数时移时,您必须对相移值数组进行 fftshift 以使其对称。(整数样本时移不需要这样做)。看到这个线程。

  2. 将 FFT 后的数据与相移值相乘后,如果立即进行 IFFT,您会发现数据正确时移,但波形在复平面中旋转了某个角度。该角度取决于与时间偏移相对应的样本分数。例如,对应于 N+.5 个样本的时间偏移将是完全虚构的(即旋转)。要旋转回实轴,请添加额外的相移(在进行 IFFT 之前): (其中 D 是样本中的时移)。π/2eπiD

所以为了超级清楚,这个过程是:

  1. 采取 FFT
  2. 构造相移 (其中 k=样本数,D=样本时间偏移,N=FFT 的样本长度)e2πikDN+πiD
  3. 对 #2 中计算的相移系数进行 FFTshift
  4. 现在将 FFT 数据与相移系数相乘
  5. 做IFFT

示例代码:

import numpy as np
import matplotlib.pyplot as plt

f1 = 12.8
f2 = 22.6
samples = 1024
tDelay = .00938
tstart = 0.0
tend = 1.0

# 0. Example waveform to demonstrate the time shift
timeList = np.linspace(tstart, tend, samples)
waveform = np.sin(2 * np.pi * f1 * timeList) + 1*np.sin(2 * np.pi * f2 * timeList)

# 1. Take the FFT
fftData = np.fft.fft(waveform)

# 2. Construct the phase shift
samplePeriod = (tend - tstart) / (samples)
tDelayInSamples = tDelay / samplePeriod
N = fftData.shape[0]
k = np.linspace(0, N-1, N)
timeDelayPhaseShift = np.exp(((-2*np.pi*1j*k*tDelayInSamples)/(N)) + (tDelayInSamples*np.pi*1j))

# 3. Do the fftshift on the phase shift coefficients
timeDelayPhaseShift = np.fft.fftshift(timeDelayPhaseShift)

# 4. Multiply the fft data with the coefficients to apply the time shift
fftWithDelay = np.multiply(fftData, timeDelayPhaseShift)

# 5. Do the IFFT
shiftedWaveform = np.fft.ifft(fftWithDelay)

print("\nThe sampling period is %f seconds" % samplePeriod)
print("The time delay is %f seconds" % tDelay)
print("The time delay in samples is %f samples" % tDelayInSamples)
print("The correction phase shift is %f pi" % (tDelayInSamples))

plots = 1
plt.subplot(plots, 1, 1)
plt.plot(waveform)
plt.plot(shiftedWaveform)
plt.show()

我欢迎任何其他人可能有更好的方法来做到这一点,或者如果我忽略了任何事情!

3个回答

Overlap-addOverlap-save中,FFT 正在执行离散傅里叶变换,它会定期扩展您的输入数据。DFT只做循环卷积,所以需要把这个做循环卷积的工具变成做线性卷积的工具。Overlap-add 和 Overlap-save 是对循环卷积器的改编,用于完成线性卷积的任务。而且由于 FFT 节省了计算量,结果证明对于非常长的 FIR 滤波器是值得的。

所以我想说的是;如果您的目的是将信号延迟一个以样本分数精度表示的延迟量,那么您需要与表示延迟的脉冲响应进行卷积并使用 Overlap-add 或 Overlap-save 来执行此操作。这是在流或非常长的块输入上实现分数延迟的唯一无故障方法。

这是因为 .0205 非常接近 30 个样本,但 .02002 非常接近 20.5 个样本。该方案仅适用于整数样本延迟,不太适合任意延迟。不幸的是,我从来没有尝试过进行部分延迟,所以我不知道我可以给你任何关于如何做得更好的建议。然而,这就是为什么你看到你所看到的。

编辑我正在为后代保存我以前的帖子,但我现在看到你在做什么。您正在执行循环移位,因为波形的末端环绕到开头。如果您要使用实时/因果过滤器尝试此操作,您可能会得到一些令人讨厌的结果。我使用您的代码生成了相应的脉冲响应。如您所见,尾部不会“褪色”为零,这表明您会因此得到伪影。 带有尾部伪影的分数延迟脉冲响应

正如您可能已经注意到的,您最初使用的相移公式不能保证分数延迟信号的共轭对称性(仅适用于整数延迟)。这里描述了一个很好的问题解决方案