简体   繁体   English

使用 python 计算频谱的 FFT

[英]Computing FFT of a spectrum using python

The spectrum shows ripples that we can visually quantify as ~50 MHz ripples.频谱显示波纹,我们可以直观地量化为 ~50 MHz 波纹。 I am looking for a method to calculate the frequency of these ripples other than by visual inspection of thousands of spectra.我正在寻找一种方法来计算这些波纹的频率,而不是通过目视检查数千个光谱。 Since the function is in frequency domain, taking FFT would get it back into time domain (with time reversal if I am correct).由于 function 处于频域中,因此采用 FFT 会将其返回到时域(如果我是正确的,可以使用时间反转)。 How can we get frequency of these ripples?我们怎样才能得到这些涟漪的频率?

The problem arises from the fact that you are making a confusion between the term 'frequency' which you are measuring and the frequency of your data.问题源于您混淆了您正在测量的术语“频率”和数据的频率。

What you want is the ripple frequency, which actually is the period of your data.你想要的是纹波频率,它实际上是你的数据的周期。

With that out of the way, let's have a look at how to fix your fft.有了这个,让我们看看如何修复你的 fft。

As pointed out by Dmitrii's answer , you must determine the sampling frequency of your data and also get rid of the low frequency components in your FFT result.正如Dmitrii 的回答所指出的,您必须确定数据的采样频率,并消除 FFT 结果中的低频分量。

To determine the sampling frequency, you can determine the sampling period by subtracting each sample by its predecessor and computing the average.要确定采样频率,您可以通过将每个样本减去其前一个样本并计算平均值来确定采样周期。 The average sampling frequency will just be the inverse of that.平均采样频率正好是它的倒数。

fs = 1 / np.mean(freq[1:] - freq[:-1])

For the high pass filter, you may use a butterworth filter, this is a good implementation.对于高通滤波器,您可以使用巴特沃斯滤波器,是一个很好的实现。

# Defining a high pass filter
def butter_highpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = signal.butter(order, normal_cutoff, btype='high', analog=False)
    return b, a

def butter_highpass_filter(data, cutoff, fs, order=5):
    b, a = butter_highpass(cutoff, fs, order=order)
    y = signal.filtfilt(b, a, data)
    return y

Next, when plotting the fft, you need to take the absolute value of it, that is what you are after.接下来,在绘制 fft 时,您需要取它的绝对值,这就是您所追求的。 Also, since it gives you both the positive and negative parts, you can just use the positive one.此外,因为它给了你积极和消极的部分,你可以只使用积极的部分。 As far as the x-axis is concerned, it will be from 0 to half of your sampling frequency.就 x 轴而言,它将是采样频率的 0 到一半。 This is further explored on this answer对此答案进行了进一步探讨

fft_amp = np.abs(np.fft.fft(amp, amp.size))
fft_amp = fft_amp[0:fft_amp.size // 2]
fft_freq = np.linspace(0, fs / 2, fft_amp.size)

Now, to determine the ripple frequency, simply obtain the peak of the FFT.现在,要确定纹波频率,只需获取 FFT 的峰值即可。 The value you are looking for (around 50MHz) will be the period of the ripple peak (in GHz), since your original data was in GHz.您正在寻找的值(大约 50MHz)将是纹波峰值的周期(以 GHz 为单位),因为您的原始数据以 GHz 为单位。 For this example, it is actually around 57MHz.对于这个例子,它实际上是 57MHz 左右。

peak = fft_freq[np.argmax(fft_amp)]

ripple_period = 1 / peak * 1000

print(f'The ripple period is {ripple_period} MHz')

And here is the complete code, which also plots the data.这是完整的代码,它还绘制了数据。

import numpy as np
import pylab as plt
from scipy import signal as signal


# Defining a high pass filter
def butter_highpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = signal.butter(order, normal_cutoff, btype='high', analog=False)
    return b, a

def butter_highpass_filter(data, cutoff, fs, order=5):
    b, a = butter_highpass(cutoff, fs, order=order)
    y = signal.filtfilt(b, a, data)
    return y


with open('ripple.csv', 'r') as fil:
    data = np.genfromtxt(fil, delimiter=',', skip_header=True)

amp = data[:, 0]
freq = data[:, 1]


# Determine the sampling frequency of the data (it is around 500 Hz)
fs = 1 / np.mean(freq[1:] - freq[:-1])

# Apply a median filter to remove the noise
amp = signal.medfilt(amp)

# Apply a highpass filter to remove the low frequency components 5 Hz was chosen
# as the cutoff fequency by visual inspection. Depending on the problem, you
# might want to choose a different value

cutoff_freq = 5
amp = butter_highpass_filter(amp, cutoff_freq, fs)

_, ax = plt.subplots(ncols=2, nrows=1)
ax[0].plot(freq, amp)
ax[0].set_xlabel('Frequency GHz')
ax[0].set_ylabel('Intensity dB')
ax[0].set_title('Filtered signal')

# The FFT part is as follows

fft_amp = np.abs(np.fft.fft(amp, amp.size))
fft_amp = fft_amp[0:fft_amp.size // 2]
fft_freq = np.linspace(0, fs / 2, fft_amp.size)

ax[1].plot(fft_freq, 2 / fft_amp.size * fft_amp, 'r-')  # the red plot
ax[1].set_xlabel('FFT frequency')
ax[1].set_ylabel('Intensity dB')

plt.show()

peak = fft_freq[np.argmax(fft_amp)]

ripple_period = 1 / peak * 1000

print(f'The ripple period is {ripple_period} MHz')

And here is the plot:这是 plot:

在此处输入图像描述

To get a proper spectrum for the blue plot you need to do two things:要获得蓝色 plot 的正确光谱,您需要做两件事:

  1. Properly calculate frequencies for the spectrum plot (the red one)正确计算频谱 plot(红色)的频率
  2. Remove bias in the data so the spectrum is less contaminated with low frequencies.消除数据中的偏差,使频谱较少受到低频的污染。 That's because you're interested in the ripple, not in the slow fluctuations.那是因为你对波纹感兴趣,而不是对缓慢的波动。

Note, that when you compute fft, you get complex values that contain information about both amplitude and phase of oscillations for each frequency.请注意,当您计算 fft 时,您会得到复数值,其中包含有关每个频率的振荡幅度和相位的信息。 In your case, the red plot should be an amplitude spectrum (compared to the phase spectrum).在您的情况下,红色 plot 应该是幅度谱(与相位谱相比)。 To get that, we take absolute values of fft coefficients.为此,我们采用 fft 系数的绝对值。

Also, the spectrum you get with fft is two-sided and symmetric (since the signal is real).此外,您使用 fft 获得的频谱是两侧对称的(因为信号是真实的)。 You really need only one side to get the idea where your ripple peak frequency is.你真的只需要一面就可以知道你的纹波峰值频率在哪里。 I've implemented this in code.我已经在代码中实现了这一点。

After playing with your data, here's what I've got:处理完您的数据后,我得到了以下信息:

import pandas as pd
import numpy as np
import pylab as plt
import plotly.graph_objects as go
from scipy import signal as sig

df = pd.read_csv("ripple.csv")
f = df.Frequency.to_numpy()
data = df.Data
data = sig.medfilt(data)  # median filter to remove the spikes

fig = go.Figure()
fig.add_trace(go.Scatter(x=f, y=(data - data.mean())))
fig.update_layout(
    xaxis_title="Frequency in GHz", yaxis_title="dB"
)  # the blue plot with ripples
fig.show()

# Remove bias to get rid of low frequency peak
data_fft = np.fft.fft(data - data.mean())

L = len(data)  # number of samples

# Compute two-sided spectrum
tssp = abs(data_fft / L)

# Compute one-sided spectrum
ossp = tssp[0 : int(L / 2)]
ossp[1:-1] = 2 * ossp[1:-1]

delta_freq = f[1] - f[0]  # without this freqs computation is incorrect
freqs = np.fft.fftfreq(f.shape[-1], delta_freq)

# Use first half of freqs since spectrum is one-sided
plt.plot(freqs[: int(L / 2)], ossp, "r-")  # the red plot
plt.xlim([0, 50])
plt.xticks(np.arange(0, 50, 1))
plt.grid()
plt.xlabel("Oscillations per frequency")
plt.show()

光谱

So you can see there are two peaks: low-freq.所以你可以看到有两个峰值:低频。 oscillations between 1 and 2 Hz and your ripple at around 17 oscillations per GHz. 1 到 2 Hz 之间的振荡和每 GHz 大约 17 次振荡的纹波。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM