我拥有的数据存储在2D列表中,其中一列表示频率,另一列是其对应的dB。我想以编程方式识别通带两端的3db点的频率。我有两个关于如何做到这一点的想法,但它们都有缺点。
缺点
你能想到更好的想法和/或实现我所描述的方法吗?
答案 0 :(得分:0)
假设您已经从信号分析仪加载了多个PSD读数,请在尝试找到边缘之前尝试对它们求平均值。如果信号变化不太大,平均过程可能会消除通带内的任何峰值和谷值以及噪声,从而更容易找到边缘。这就是许多频谱分析仪可以做的更平滑的PSD。
如果不清楚,假设每次读数为您提供128个频率和功率元组,并且您捕获了100个这些数据缓冲区。现在平均来自bin 0的100个样本,然后是来自1,2,...,128的样本。现在尝试在该数据上找到带通。它应该比任何单个缓冲区更容易。注意我用100作为例子。如果您的数据非常嘈杂,可能需要更多。如果没有太多噪音,那就更少了。
进行平均时要小心。您的数据以dB为单位。要将样本添加到一起以找到平均值,必须首先将dB数据转换回十进制,执行加法,除数以找到平均值,然后将平均功率转换回dB。
答案 1 :(得分:0)
好吧,这似乎必须通过数据分析来解决。我会建议这些步骤:
如果您怀疑它太嘈杂,请预处理您的数据。我建议使用移动平均滤波器(sp.convolve(data, sp.ones(n)/n, "same")
)或更好的savitzky-golay滤波器(sp.signal.savgol_filter(data, n, polyorder=3)
),因为您会对数据的极值感兴趣,这将被不必要地扭曲ma过滤器。您可能还想在此阶段摆脱60Hz噪声等伪像。
如果您感兴趣的信号生活在一个窄带中,则频谱将是一个明显的峰值。在这种情况下,您可以在数据中拟合曲线,在这种情况下,高斯是合适的。
import scipy as sp
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
freq, pow = read_in_your_data_here()
freq, pow = sp.asarray(freq), sp.asarray(pow)
def gauss(x, a, mu, sig):
return a**sp.exp(-(x-mu)**2/(2.*sig**2))
(a, mu, sig), _ = curve_fit(gauss, freq, pow)
fitted_curve = gauss(freq, a, mu, sig)
plt.plot(freq, pow)
plt.plot(freq, fitted_curve)
plt.vlines(mu, min(pow)-2, max(pow)+2)
plt.show()
center_idx = sp.absolute(freq-mu).argmin()
pow_center = pow[center_idx]
pow_3db = pow_center - 3.
def interv_from_binvec(data):
indicator = sp.convolve(data, [-1,1], "same")
return indicator.argmin(), indicator.argmax()
passband_idx = interv_from_binvec(pow > pow_3db)
passband = freq[passband_idx[0]], freq[passband_idx[1]]
这是一个比解决方案更好的例子,并且在很大程度上依赖于您正在搜索的假设并找到具有窄带的高SNR信号。可以通过使用混合模型将其扩展为处理多个信号。
答案 2 :(得分:0)
您可以使用scipy的UnivariateSpline和minimumsq方法:
y-(np.max(y)-3)
的样条线from scipy.interpolate import UnivariateSpline
from scipy.optimize import leastsq
x = df["Wavelength / nm"]
y = df["Power / dBm"]
#create spline
spline = UnivariateSpline(x, y-(np.max(y)-3), s=0)
# find the roots
r1, r2 = spline.roots()
# calculate the difference
threedB_bandwidth = abs(r2-r1)