我正在尝试将最初用 numpy 编写的算法转换为 JavaScript,但我无法从反向 FFT 重现结果。
原始算法使用numpy.fft.rfft
and numpy.fft.irfft
:
# Get the amplitude
amplitudes = abs(np.fft.rfft(buf, axis=0))
# Randomize phases
ph = np.random.uniform(0, 2*np.pi, (amplitudes.shape[0], 1)) * 1j
amplitudes = amplitudes * np.exp(ph)
# do the inverse FFT
buf = np.fft.irfft(amplitudes, axis=0)
我找到了一个似乎可以为 FFT 工作的 JavaScript 库,并且我正在使用mathjs进行矩阵/向量工作。
我做了很多尝试,问题是我不知道我应该做什么来模仿numpy.fft.irfft
。
2 个 FFT 之间的差异:
JavaScript FFT 函数返回具有负频率的复数输出,因此它包含的点数是使用
numpy.fft.rfft
. 尽管正频率的幅度[0, WIN/2]
似乎匹配。JavaScript iFFT 返回一个复数输出,同时
numpy.fft.rfft
返回一个实数输出。
回答
感谢@hotpaw2,我设法解决了我的问题。
真实信号的频谱是对称的,并且numpy.fft.rfft
只返回该频谱的唯一分量。因此,对于 128 个样本的块,numpy.fft.rfft
返回包含128/2 + 1
值的频谱,即65
值。
因此,如果我想做同样的事情,我需要从我的幅度中丢弃所有对称值,然后应用相位变化。
对于反向 FFT:“要从全长 IFFT 获得仅实数输出,输入必须是复共轭对称的”。所以我需要通过使实部对称和虚部镜像对称来重建光谱。
这是算法:
fft(1, re, im)
amplitudes = math.select(re)
.subset([math.range(0, frameCount / 2)]) // get only the unique part
.abs().done() // input signal is real, so abs value of `re` is the amplitude
// Apply the new phases
re = math.emultiply(math.cos(phases), amplitudes)
im = math.emultiply(math.sin(phases), amplitudes)
// Rebuild `re` and `im` by adding the symetric part
re = math.concat(re, math.subset(re, [symRange]).reverse())
im = math.concat(im, math.select(im).subset([symRange]).emultiply(-1).done().reverse())
// do the inverse FFT
fft(-1, re, im)