如何找到信号周期(自相关与快速傅立叶变换与功率谱密度)?
How to find period of signal (autocorrelation vs fast fourier transform vs power spectral density)?
假设要找出给定正弦波信号的周期。从我在网上阅读的内容来看,这两种主要方法似乎采用傅立叶分析或自相关。我正在尝试使用 python 使该过程自动化,我的用例是将此概念应用于来自围绕恒星运行的模拟物体的位置(或速度或加速度)时间序列的类似信号。
为了简单示例,考虑 x = sin(t)
用于 0 ≤ t ≤ 10 pi
。
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
## sample data
t = np.linspace(0, 10 * np.pi, 100)
x = np.sin(t)
fig, ax = plt.subplots()
ax.plot(t, x, color='b', marker='o')
ax.grid(color='k', alpha=0.3, linestyle=':')
plt.show()
plt.close(fig)
给定 x = a sin(b(t+c)) + d
形式的正弦波,正弦波的周期为 2 * pi / b
。由于b=1
(或通过目测),我们的正弦波的周期是2 * pi
。我可以根据此基线检查从其他方法获得的结果。
尝试 1:自相关
据我了解(如果我错了请纠正我),相关性可以用来查看一个信号是否是另一个信号的时间滞后副本(类似于余弦和正弦因相位差而不同的方式) ).因此,自相关是针对自身测试信号,以测量时滞重复所述信号的时间。使用 example posted here:
result = np.correlate(x, x, mode='full')
由于x
和t
分别由100
个元素组成,而result
由199
个元素组成,我不确定为什么要任意select最后100
个元素。
print("\n autocorrelation (shape={}):\n{}\n".format(result.shape, result))
autocorrelation (shape=(199,)):
[ 0.00000000e+00 -3.82130761e-16 -9.73648712e-02 -3.70014208e-01
-8.59889695e-01 -1.56185995e+00 -2.41986054e+00 -3.33109112e+00
-4.15799070e+00 -4.74662427e+00 -4.94918053e+00 -4.64762251e+00
-3.77524157e+00 -2.33298717e+00 -3.97976240e-01 1.87752669e+00
4.27722402e+00 6.54129270e+00 8.39434617e+00 9.57785701e+00
9.88331103e+00 9.18204933e+00 7.44791758e+00 4.76948221e+00
1.34963425e+00 -2.50822289e+00 -6.42666652e+00 -9.99116299e+00
-1.27937834e+01 -1.44791297e+01 -1.47873668e+01 -1.35893098e+01
-1.09091510e+01 -6.93157447e+00 -1.99159756e+00 3.45267493e+00
8.86228186e+00 1.36707567e+01 1.73433176e+01 1.94357232e+01
1.96463736e+01 1.78556800e+01 1.41478477e+01 8.81191526e+00
2.32100171e+00 -4.70897483e+00 -1.15775811e+01 -1.75696560e+01
-2.20296487e+01 -2.44327920e+01 -2.44454330e+01 -2.19677060e+01
-1.71533510e+01 -1.04037163e+01 -2.33560966e+00 6.27458308e+00
1.45655029e+01 2.16769872e+01 2.68391837e+01 2.94553896e+01
2.91697473e+01 2.59122266e+01 1.99154591e+01 1.17007613e+01
2.03381596e+00 -8.14633251e+00 -1.78184255e+01 -2.59814393e+01
-3.17580589e+01 -3.44884934e+01 -3.38046447e+01 -2.96763956e+01
-2.24244433e+01 -1.26974172e+01 -1.41464998e+00 1.03204331e+01
2.13281784e+01 3.04712823e+01 3.67721634e+01 3.95170295e+01
3.83356037e+01 3.32477037e+01 2.46710643e+01 1.33886439e+01
4.77778141e-01 -1.27924775e+01 -2.50860560e+01 -3.51343866e+01
-4.18671622e+01 -4.45258983e+01 -4.27482779e+01 -3.66140001e+01
-2.66465884e+01 -1.37700036e+01 7.76494745e-01 1.55574483e+01
2.90828312e+01 3.99582426e+01 4.70285203e+01 4.95000000e+01
4.70285203e+01 3.99582426e+01 2.90828312e+01 1.55574483e+01
7.76494745e-01 -1.37700036e+01 -2.66465884e+01 -3.66140001e+01
-4.27482779e+01 -4.45258983e+01 -4.18671622e+01 -3.51343866e+01
-2.50860560e+01 -1.27924775e+01 4.77778141e-01 1.33886439e+01
2.46710643e+01 3.32477037e+01 3.83356037e+01 3.95170295e+01
3.67721634e+01 3.04712823e+01 2.13281784e+01 1.03204331e+01
-1.41464998e+00 -1.26974172e+01 -2.24244433e+01 -2.96763956e+01
-3.38046447e+01 -3.44884934e+01 -3.17580589e+01 -2.59814393e+01
-1.78184255e+01 -8.14633251e+00 2.03381596e+00 1.17007613e+01
1.99154591e+01 2.59122266e+01 2.91697473e+01 2.94553896e+01
2.68391837e+01 2.16769872e+01 1.45655029e+01 6.27458308e+00
-2.33560966e+00 -1.04037163e+01 -1.71533510e+01 -2.19677060e+01
-2.44454330e+01 -2.44327920e+01 -2.20296487e+01 -1.75696560e+01
-1.15775811e+01 -4.70897483e+00 2.32100171e+00 8.81191526e+00
1.41478477e+01 1.78556800e+01 1.96463736e+01 1.94357232e+01
1.73433176e+01 1.36707567e+01 8.86228186e+00 3.45267493e+00
-1.99159756e+00 -6.93157447e+00 -1.09091510e+01 -1.35893098e+01
-1.47873668e+01 -1.44791297e+01 -1.27937834e+01 -9.99116299e+00
-6.42666652e+00 -2.50822289e+00 1.34963425e+00 4.76948221e+00
7.44791758e+00 9.18204933e+00 9.88331103e+00 9.57785701e+00
8.39434617e+00 6.54129270e+00 4.27722402e+00 1.87752669e+00
-3.97976240e-01 -2.33298717e+00 -3.77524157e+00 -4.64762251e+00
-4.94918053e+00 -4.74662427e+00 -4.15799070e+00 -3.33109112e+00
-2.41986054e+00 -1.56185995e+00 -8.59889695e-01 -3.70014208e-01
-9.73648712e-02 -3.82130761e-16 0.00000000e+00]
尝试 2:傅立叶
由于我不确定从上次尝试到哪里去,我寻求新的尝试。据我了解,傅立叶分析基本上将信号 from/to 移到时域 (x(t) vs t
) to/from 频域 (x(t) vs f=1/t
); frequency-space 中的信号应该显示为随时间衰减的正弦波。该周期是从最常观察到的频率获得的,因为这是频率分布峰值的位置。
由于我的值都是实值,应用傅里叶变换应该意味着我的输出值都是复值。我不认为这是一个问题,除了 scipy has methods for real-values. I do not fully understand the differences between all of the different scipy methods. That makes following the algorithm proposed in this posted solution 对我来说很难遵循这一事实(即 how/why 是选择的阈值吗?)。
omega = np.fft.fft(x)
freq = np.fft.fftfreq(x.size, 1)
threshold = 0
idx = np.where(abs(omega)>threshold)[0][-1]
max_f = abs(freq[idx])
print(max_f)
这输出0.01
,意思是周期是1/0.01 = 100
。这也说不通。
尝试3:功率谱密度
根据scipy docs, I should be able to estimate the power spectral density (psd) of the signal using a periodogram (which, according to wikipedia,是自相关函数的傅里叶变换)。通过selecting信号峰值的主频率fmax
,可以得到信号的周期为1 / fmax
.
freq, pdensity = signal.periodogram(x)
fig, ax = plt.subplots()
ax.plot(freq, pdensity, color='r')
ax.grid(color='k', alpha=0.3, linestyle=':')
plt.show()
plt.close(fig)
下面显示的周期图在 49.076...
处以 fmax = 0.05
的频率达到峰值。所以,period = 1/fmax = 20
。这对我来说没有意义。我感觉跟采样率有关系,但是还没有足够的知识来确认或者进一步的进展。
我意识到我在理解这些东西是如何工作时遗漏了一些基本的差距。网上的资源很多,但是大海捞针实在是太难了。有人可以帮我了解更多吗?
让我们先看看你的信号(我添加了 endpoint=False
以使除法均匀):
t = np.linspace(0, 10*np.pi, 100, endpoint=False)
x = np.sin(t)
让我们划分弧度(基本上是 t /= 2*np.pi
)并通过与频率相关来创建相同的信号:
fs = 20 # Sampling rate of 100/5 = 20 (e.g. Hz)
f = 1 # Signal frequency of 1 (e.g. Hz)
t = np.linspace(0, 5, 5*fs, endpoint=False)
x = np.sin(2*np.pi*f*t)
这使得 f/fs == 1/20 == 0.05
更加突出(即信号的周期正好是 20 个样本)。正如您已经猜到的那样,数字信号中的频率总是与其采样率相关。注意无论f
和fs
的值是多少,只要它们的比值相同,实际信号是完全一样的:
fs = 1 # Natural units
f = 0.05
t = np.linspace(0, 100, 100*fs, endpoint=False)
x = np.sin(2*np.pi*f*t)
下面我将使用这些自然单位 (fs = 1
)。唯一的区别在于 t
以及生成的频率轴。
自相关
您对自相关函数作用的理解是正确的。它检测信号与其自身的时间滞后版本的相关性。它通过将信号滑过自身来实现这一点,如右栏所示(来自 Wikipedia):
请注意,由于相关函数的两个输入相同,结果信号必然是对称的。这就是为什么 np.correlate
的输出通常从中间:
acf = np.correlate(x, x, 'full')[-len(x):]
现在索引 0 对应于信号两个副本之间的 0 延迟。
接下来,您需要找到相关性最大的索引或延迟。由于重叠缩小,默认情况下这也是索引 0,因此以下内容不起作用:
acf.argmax() # Always returns 0
相反,我建议找到最大的 peak,其中 peak 被定义为具有比其两个直接邻居都大的值的任何索引:
inflection = np.diff(np.sign(np.diff(acf))) # Find the second-order differences
peaks = (inflection < 0).nonzero()[0] + 1 # Find where they are negative
delay = peaks[acf[peaks].argmax()] # Of those, find the index with the maximum value
现在 delay == 20
,它告诉您信号的频率是其采样率的 1/20
:
signal_freq = fs/delay # Gives 0.05
傅里叶变换
您使用以下方法计算 FFT:
omega = np.fft.fft(x)
freq = np.fft.fftfreq(x.size, 1)
这些函数是为复值信号重新设计的。它们适用于实值信号,但您将获得对称输出,因为负频率分量与正频率分量相同。 NumPy 为实值信号提供单独的函数:
ft = np.fft.rfft(x)
freqs = np.fft.rfftfreq(len(x), t[1]-t[0]) # Get frequency axis from the time axis
mags = abs(ft) # We don't care about the phase information here
一起来看看:
plt.plot(freqs, mags)
plt.show()
注意两点:峰值频率为 0.05,轴上的最大频率为 0.5(Nyquist frequency,正好是采样率的一半)。如果我们选择 fs = 20
,这将是 10.
现在让我们找出最大值。您尝试过的阈值方法 可以 工作,但目标频率仓是盲目地 selected,因此这种方法会在其他信号存在的情况下受到影响。我们可以 select 最大值:
signal_freq = freqs[mags.argmax()] # Gives 0.05
但是,如果我们有一个大的 DC 偏移(因此在索引 0 中有一个大的组件),这将失败。在那种情况下,我们可以再次 select 最高峰,以使其更稳健:
inflection = np.diff(np.sign(np.diff(mags)))
peaks = (inflection < 0).nonzero()[0] + 1
peak = peaks[mags[peaks].argmax()]
signal_freq = freqs[peak] # Gives 0.05
如果我们选择了 fs = 20
,这将给出 signal_freq == 1.0
,因为生成频率轴的时间轴不同。
周期图
这里的方法本质上是一样的。 x
的自相关函数与x
具有相同的时间轴和周期,所以我们可以使用上面的FFT求信号频率:
pdg = np.fft.rfft(acf)
freqs = np.fft.rfftfreq(len(x), t[1]-t[0])
plt.plot(freqs, abs(pdg))
plt.show()
这条曲线明显与x
上的直接FFT特性略有不同,但主要要点是相同的:频率轴范围从0
到0.5*fs
,我们找到与之前相同信号频率的峰值:freqs[abs(pdg).argmax()] == 0.05
.
编辑:
要测量np.sin
的实际周期性,我们可以在生成频率轴时直接使用我们传递给np.sin
的"angle axis"代替时间轴:
freqs = np.fft.rfftfreq(len(x), 2*np.pi*f*(t[1]-t[0]))
rad_period = 1/freqs[mags.argmax()] # 6.283185307179586
虽然这似乎毫无意义,对吧?我们传入 2*np.pi
并得到 2*np.pi
。然而,我们可以对任何常规时间轴做同样的事情,而无需在任何时候预先假设 pi
:
fs = 10
t = np.arange(1000)/fs
x = np.sin(t)
rad_period = 1/np.fft.rfftfreq(len(x), 1/fs)[abs(np.fft.rfft(x)).argmax()] # 6.25
自然地,真值现在位于两个 bin 之间。这就是插值的用武之地,相关的需要选择合适的 window 函数。
假设要找出给定正弦波信号的周期。从我在网上阅读的内容来看,这两种主要方法似乎采用傅立叶分析或自相关。我正在尝试使用 python 使该过程自动化,我的用例是将此概念应用于来自围绕恒星运行的模拟物体的位置(或速度或加速度)时间序列的类似信号。
为了简单示例,考虑 x = sin(t)
用于 0 ≤ t ≤ 10 pi
。
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
## sample data
t = np.linspace(0, 10 * np.pi, 100)
x = np.sin(t)
fig, ax = plt.subplots()
ax.plot(t, x, color='b', marker='o')
ax.grid(color='k', alpha=0.3, linestyle=':')
plt.show()
plt.close(fig)
给定 x = a sin(b(t+c)) + d
形式的正弦波,正弦波的周期为 2 * pi / b
。由于b=1
(或通过目测),我们的正弦波的周期是2 * pi
。我可以根据此基线检查从其他方法获得的结果。
尝试 1:自相关
据我了解(如果我错了请纠正我),相关性可以用来查看一个信号是否是另一个信号的时间滞后副本(类似于余弦和正弦因相位差而不同的方式) ).因此,自相关是针对自身测试信号,以测量时滞重复所述信号的时间。使用 example posted here:
result = np.correlate(x, x, mode='full')
由于x
和t
分别由100
个元素组成,而result
由199
个元素组成,我不确定为什么要任意select最后100
个元素。
print("\n autocorrelation (shape={}):\n{}\n".format(result.shape, result))
autocorrelation (shape=(199,)):
[ 0.00000000e+00 -3.82130761e-16 -9.73648712e-02 -3.70014208e-01
-8.59889695e-01 -1.56185995e+00 -2.41986054e+00 -3.33109112e+00
-4.15799070e+00 -4.74662427e+00 -4.94918053e+00 -4.64762251e+00
-3.77524157e+00 -2.33298717e+00 -3.97976240e-01 1.87752669e+00
4.27722402e+00 6.54129270e+00 8.39434617e+00 9.57785701e+00
9.88331103e+00 9.18204933e+00 7.44791758e+00 4.76948221e+00
1.34963425e+00 -2.50822289e+00 -6.42666652e+00 -9.99116299e+00
-1.27937834e+01 -1.44791297e+01 -1.47873668e+01 -1.35893098e+01
-1.09091510e+01 -6.93157447e+00 -1.99159756e+00 3.45267493e+00
8.86228186e+00 1.36707567e+01 1.73433176e+01 1.94357232e+01
1.96463736e+01 1.78556800e+01 1.41478477e+01 8.81191526e+00
2.32100171e+00 -4.70897483e+00 -1.15775811e+01 -1.75696560e+01
-2.20296487e+01 -2.44327920e+01 -2.44454330e+01 -2.19677060e+01
-1.71533510e+01 -1.04037163e+01 -2.33560966e+00 6.27458308e+00
1.45655029e+01 2.16769872e+01 2.68391837e+01 2.94553896e+01
2.91697473e+01 2.59122266e+01 1.99154591e+01 1.17007613e+01
2.03381596e+00 -8.14633251e+00 -1.78184255e+01 -2.59814393e+01
-3.17580589e+01 -3.44884934e+01 -3.38046447e+01 -2.96763956e+01
-2.24244433e+01 -1.26974172e+01 -1.41464998e+00 1.03204331e+01
2.13281784e+01 3.04712823e+01 3.67721634e+01 3.95170295e+01
3.83356037e+01 3.32477037e+01 2.46710643e+01 1.33886439e+01
4.77778141e-01 -1.27924775e+01 -2.50860560e+01 -3.51343866e+01
-4.18671622e+01 -4.45258983e+01 -4.27482779e+01 -3.66140001e+01
-2.66465884e+01 -1.37700036e+01 7.76494745e-01 1.55574483e+01
2.90828312e+01 3.99582426e+01 4.70285203e+01 4.95000000e+01
4.70285203e+01 3.99582426e+01 2.90828312e+01 1.55574483e+01
7.76494745e-01 -1.37700036e+01 -2.66465884e+01 -3.66140001e+01
-4.27482779e+01 -4.45258983e+01 -4.18671622e+01 -3.51343866e+01
-2.50860560e+01 -1.27924775e+01 4.77778141e-01 1.33886439e+01
2.46710643e+01 3.32477037e+01 3.83356037e+01 3.95170295e+01
3.67721634e+01 3.04712823e+01 2.13281784e+01 1.03204331e+01
-1.41464998e+00 -1.26974172e+01 -2.24244433e+01 -2.96763956e+01
-3.38046447e+01 -3.44884934e+01 -3.17580589e+01 -2.59814393e+01
-1.78184255e+01 -8.14633251e+00 2.03381596e+00 1.17007613e+01
1.99154591e+01 2.59122266e+01 2.91697473e+01 2.94553896e+01
2.68391837e+01 2.16769872e+01 1.45655029e+01 6.27458308e+00
-2.33560966e+00 -1.04037163e+01 -1.71533510e+01 -2.19677060e+01
-2.44454330e+01 -2.44327920e+01 -2.20296487e+01 -1.75696560e+01
-1.15775811e+01 -4.70897483e+00 2.32100171e+00 8.81191526e+00
1.41478477e+01 1.78556800e+01 1.96463736e+01 1.94357232e+01
1.73433176e+01 1.36707567e+01 8.86228186e+00 3.45267493e+00
-1.99159756e+00 -6.93157447e+00 -1.09091510e+01 -1.35893098e+01
-1.47873668e+01 -1.44791297e+01 -1.27937834e+01 -9.99116299e+00
-6.42666652e+00 -2.50822289e+00 1.34963425e+00 4.76948221e+00
7.44791758e+00 9.18204933e+00 9.88331103e+00 9.57785701e+00
8.39434617e+00 6.54129270e+00 4.27722402e+00 1.87752669e+00
-3.97976240e-01 -2.33298717e+00 -3.77524157e+00 -4.64762251e+00
-4.94918053e+00 -4.74662427e+00 -4.15799070e+00 -3.33109112e+00
-2.41986054e+00 -1.56185995e+00 -8.59889695e-01 -3.70014208e-01
-9.73648712e-02 -3.82130761e-16 0.00000000e+00]
尝试 2:傅立叶
由于我不确定从上次尝试到哪里去,我寻求新的尝试。据我了解,傅立叶分析基本上将信号 from/to 移到时域 (x(t) vs t
) to/from 频域 (x(t) vs f=1/t
); frequency-space 中的信号应该显示为随时间衰减的正弦波。该周期是从最常观察到的频率获得的,因为这是频率分布峰值的位置。
由于我的值都是实值,应用傅里叶变换应该意味着我的输出值都是复值。我不认为这是一个问题,除了 scipy has methods for real-values. I do not fully understand the differences between all of the different scipy methods. That makes following the algorithm proposed in this posted solution 对我来说很难遵循这一事实(即 how/why 是选择的阈值吗?)。
omega = np.fft.fft(x)
freq = np.fft.fftfreq(x.size, 1)
threshold = 0
idx = np.where(abs(omega)>threshold)[0][-1]
max_f = abs(freq[idx])
print(max_f)
这输出0.01
,意思是周期是1/0.01 = 100
。这也说不通。
尝试3:功率谱密度
根据scipy docs, I should be able to estimate the power spectral density (psd) of the signal using a periodogram (which, according to wikipedia,是自相关函数的傅里叶变换)。通过selecting信号峰值的主频率fmax
,可以得到信号的周期为1 / fmax
.
freq, pdensity = signal.periodogram(x)
fig, ax = plt.subplots()
ax.plot(freq, pdensity, color='r')
ax.grid(color='k', alpha=0.3, linestyle=':')
plt.show()
plt.close(fig)
下面显示的周期图在 49.076...
处以 fmax = 0.05
的频率达到峰值。所以,period = 1/fmax = 20
。这对我来说没有意义。我感觉跟采样率有关系,但是还没有足够的知识来确认或者进一步的进展。
我意识到我在理解这些东西是如何工作时遗漏了一些基本的差距。网上的资源很多,但是大海捞针实在是太难了。有人可以帮我了解更多吗?
让我们先看看你的信号(我添加了 endpoint=False
以使除法均匀):
t = np.linspace(0, 10*np.pi, 100, endpoint=False)
x = np.sin(t)
让我们划分弧度(基本上是 t /= 2*np.pi
)并通过与频率相关来创建相同的信号:
fs = 20 # Sampling rate of 100/5 = 20 (e.g. Hz)
f = 1 # Signal frequency of 1 (e.g. Hz)
t = np.linspace(0, 5, 5*fs, endpoint=False)
x = np.sin(2*np.pi*f*t)
这使得 f/fs == 1/20 == 0.05
更加突出(即信号的周期正好是 20 个样本)。正如您已经猜到的那样,数字信号中的频率总是与其采样率相关。注意无论f
和fs
的值是多少,只要它们的比值相同,实际信号是完全一样的:
fs = 1 # Natural units
f = 0.05
t = np.linspace(0, 100, 100*fs, endpoint=False)
x = np.sin(2*np.pi*f*t)
下面我将使用这些自然单位 (fs = 1
)。唯一的区别在于 t
以及生成的频率轴。
自相关
您对自相关函数作用的理解是正确的。它检测信号与其自身的时间滞后版本的相关性。它通过将信号滑过自身来实现这一点,如右栏所示(来自 Wikipedia):
请注意,由于相关函数的两个输入相同,结果信号必然是对称的。这就是为什么 np.correlate
的输出通常从中间:
acf = np.correlate(x, x, 'full')[-len(x):]
现在索引 0 对应于信号两个副本之间的 0 延迟。
接下来,您需要找到相关性最大的索引或延迟。由于重叠缩小,默认情况下这也是索引 0,因此以下内容不起作用:
acf.argmax() # Always returns 0
相反,我建议找到最大的 peak,其中 peak 被定义为具有比其两个直接邻居都大的值的任何索引:
inflection = np.diff(np.sign(np.diff(acf))) # Find the second-order differences
peaks = (inflection < 0).nonzero()[0] + 1 # Find where they are negative
delay = peaks[acf[peaks].argmax()] # Of those, find the index with the maximum value
现在 delay == 20
,它告诉您信号的频率是其采样率的 1/20
:
signal_freq = fs/delay # Gives 0.05
傅里叶变换
您使用以下方法计算 FFT:
omega = np.fft.fft(x)
freq = np.fft.fftfreq(x.size, 1)
这些函数是为复值信号重新设计的。它们适用于实值信号,但您将获得对称输出,因为负频率分量与正频率分量相同。 NumPy 为实值信号提供单独的函数:
ft = np.fft.rfft(x)
freqs = np.fft.rfftfreq(len(x), t[1]-t[0]) # Get frequency axis from the time axis
mags = abs(ft) # We don't care about the phase information here
一起来看看:
plt.plot(freqs, mags)
plt.show()
注意两点:峰值频率为 0.05,轴上的最大频率为 0.5(Nyquist frequency,正好是采样率的一半)。如果我们选择 fs = 20
,这将是 10.
现在让我们找出最大值。您尝试过的阈值方法 可以 工作,但目标频率仓是盲目地 selected,因此这种方法会在其他信号存在的情况下受到影响。我们可以 select 最大值:
signal_freq = freqs[mags.argmax()] # Gives 0.05
但是,如果我们有一个大的 DC 偏移(因此在索引 0 中有一个大的组件),这将失败。在那种情况下,我们可以再次 select 最高峰,以使其更稳健:
inflection = np.diff(np.sign(np.diff(mags)))
peaks = (inflection < 0).nonzero()[0] + 1
peak = peaks[mags[peaks].argmax()]
signal_freq = freqs[peak] # Gives 0.05
如果我们选择了 fs = 20
,这将给出 signal_freq == 1.0
,因为生成频率轴的时间轴不同。
周期图
这里的方法本质上是一样的。 x
的自相关函数与x
具有相同的时间轴和周期,所以我们可以使用上面的FFT求信号频率:
pdg = np.fft.rfft(acf)
freqs = np.fft.rfftfreq(len(x), t[1]-t[0])
plt.plot(freqs, abs(pdg))
plt.show()
这条曲线明显与x
上的直接FFT特性略有不同,但主要要点是相同的:频率轴范围从0
到0.5*fs
,我们找到与之前相同信号频率的峰值:freqs[abs(pdg).argmax()] == 0.05
.
编辑:
要测量np.sin
的实际周期性,我们可以在生成频率轴时直接使用我们传递给np.sin
的"angle axis"代替时间轴:
freqs = np.fft.rfftfreq(len(x), 2*np.pi*f*(t[1]-t[0]))
rad_period = 1/freqs[mags.argmax()] # 6.283185307179586
虽然这似乎毫无意义,对吧?我们传入 2*np.pi
并得到 2*np.pi
。然而,我们可以对任何常规时间轴做同样的事情,而无需在任何时候预先假设 pi
:
fs = 10
t = np.arange(1000)/fs
x = np.sin(t)
rad_period = 1/np.fft.rfftfreq(len(x), 1/fs)[abs(np.fft.rfft(x)).argmax()] # 6.25
自然地,真值现在位于两个 bin 之间。这就是插值的用武之地,相关的需要选择合适的 window 函数。