Я пишу этот дополнительный ответ, чтобы объяснить происхождение распространения шипов при использовании fft, и особенно обсуждаю руководство scipy.fftpack, с которым я в какой-то момент не согласен.
В этом примере время записи tmax=N*T=0.75
. Сигнал есть sin(50*2*pi*x)+0.5*sin(80*2*pi*x)
. Частотный сигнал должен содержать 2 пика на частотах 50
и 80
с амплитудами 1
и 0.5
. Однако, если анализируемый сигнал не имеет целого числа периодов, может появиться диффузия из-за усечения сигнала:
- Пайк 1:
50*tmax=37.5
=> частота 50
не кратна 1/tmax
=> Наличие диффузии из-за усечения сигнала на этой частоте.
- Пайк 2:
80*tmax=60
=> частота 80
кратна 1/tmax
=> Нет диффузии из-за усечения сигнала на этой частоте.
Вот код, который анализирует тот же сигнал, что и в учебнике ( sin(50*2*pi*x)+0.5*sin(80*2*pi*x)
), но с небольшими отличиями:
- Исходный пример scipy.fftpack.
- Исходный пример scipy.fftpack с целым числом периодов сигнала (
tmax=1.0
вместо того, 0.75
чтобы избежать распространения усечения).
- Исходный пример scipy.fftpack с целым числом периодов сигнала, где даты и частоты взяты из теории БПФ.
Код:
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
N = 600
tmax = 3/4
T = tmax / N
x1 = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x1) + 0.5*np.sin(80.0 * 2.0*np.pi*x1)
yf1 = scipy.fftpack.fft(y1)
xf1 = np.linspace(0.0, 1.0/(2.0*T), N//2)
tmax = 1
T = tmax / N
x2 = np.linspace(0.0, N*T, N)
y2 = np.sin(50.0 * 2.0*np.pi*x2) + 0.5*np.sin(80.0 * 2.0*np.pi*x2)
yf2 = scipy.fftpack.fft(y2)
xf2 = np.linspace(0.0, 1.0/(2.0*T), N//2)
tmax = 1
T = tmax / N
x3 = T * np.arange(N)
y3 = np.sin(50.0 * 2.0*np.pi*x3) + 0.5*np.sin(80.0 * 2.0*np.pi*x3)
yf3 = scipy.fftpack.fft(y3)
xf3 = 1/(N*T) * np.arange(N)[:N//2]
fig, ax = plt.subplots()
ax.plot(xf1, 2.0/N * np.abs(yf1[:N//2]), label='fftpack tutorial')
ax.plot(xf2, 2.0/N * np.abs(yf2[:N//2]), label='Integer number of periods')
ax.plot(xf3, 2.0/N * np.abs(yf3[:N//2]), label='Correct positionning of dates')
plt.legend()
plt.grid()
plt.show()
Выход:
Как это может быть здесь, даже при использовании целого числа периодов некоторая диффузия все же остается. Такое поведение связано с неправильным позиционированием дат и частот в учебнике scipy.fftpack. Следовательно, в теории дискретных преобразований Фурье:
- сигнал должен оцениваться в те даты,
t=0,T,...,(N-1)*T
где T - период выборки, а общая длительность сигнала равна tmax=N*T
. Обратите внимание, что мы останавливаемся на tmax-T
.
- соответствующие частоты ,
f=0,df,...,(N-1)*df
где df=1/tmax=1/(N*T)
частота дискретизации. Все гармоники сигнала должны быть кратны частоте дискретизации, чтобы избежать диффузии.
В приведенном выше примере вы можете видеть, что использование arange
вместо linspace
позволяет избежать дополнительной диффузии в частотном спектре. Более того, использование linspace
версии также приводит к смещению выбросов, которые расположены на несколько более высоких частотах, чем они должны быть, как это видно на первом рисунке, где выбросы находятся немного справа от частот 50
и 80
.
Я просто заключу, что пример использования следует заменить следующим кодом (который, на мой взгляд, менее вводит в заблуждение):
import numpy as np
from scipy.fftpack import fft
N = 600
T = 1.0 / 800.0
x = T*np.arange(N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = 1/(N*T)*np.arange(N//2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N//2]))
plt.grid()
plt.show()
Вывод (второй шип уже не разлетается):
Я думаю, что этот ответ по-прежнему дает некоторые дополнительные объяснения о том, как правильно применять дискретное преобразование Фурье. Очевидно, мой ответ слишком длинный, и всегда есть что сказать (@ewerlopes, например, кратко рассказал о псевдониме, и многое можно сказать об оконном режиме ), поэтому я остановлюсь. Я думаю, что очень важно глубоко понимать принципы дискретного преобразования Фурье при его применении, потому что мы все знаем, как много людей добавляют факторы здесь и там, применяя его, чтобы получить то, что они хотят.