我正在尝试获取fft幅度以匹配输入时域信号的幅度比例。
对于这个问题,我已经尝试了很多“答案”,但似乎没有一个对我有用。所以我写了一个实验程序。
这需要一段音频(来自文件或-您可以使用加性合成来创建波形)。
然后我可以选择将信号填充为零(选择数量和要分析的步数)。
我没有网站发布我创建的绘图的图像-但是,如果您在底部(Python 3.x)剪切并发布程序,您将很快看到它们!
该程序的一小部分在下面。
代码的相关部分(下面的plot_pad_stats())是:
for p in np.arange(start,end,step):
s1 = pad(s,int(p))
ftp = np.fft.fft(s1,norm=norma)
ft = norm_fft(s1,norm)
if real:
ftp = ftp.real
ft = ft.real
else:
ftp = np.sqrt(ftp.real**2 + ftp.imag**2)
ft = np.sqrt(ft.real**2 + ft.imag**2)
if absolute:
ftp = abs(ftp)
ft = abs(ft)
if active:
ftp = [(f+abs(f))/2 for f in ftp]
ft = [(f+abs(f))/2 for f in ft]
minft[p1] = min(ftp)
maxft[p1] = max(ftp)
meanft[p1] = np.mean(ftp)
lenft[p1] = int(p)
minnft[p1] = min(ft)
maxnft[p1] = max(ft)
meannft[p1] = np.mean(ft)
lennft[p1] = int(p)
我怀疑与相位有关-但不知道如何获得fft幅度标度以匹配正在分析的时域信号...
不仅增加了填充的变化很有趣,而且随着信号从单个正弦波变为更复杂的波而变化!
任何帮助表示感谢:-tony.fdlr@gmail.com
arrays = plot_pad_stats:只调用无参数即可绘制440hz信号全幅值
使用
additativeSignal(hza=[1],aa=None,ap=[0],n=88200,sr=44100,verbose=True):
'''
hza = herz array
aa = amplitude array
ap = phase array
n = number samples to return
sr = sampling frequency
verbose = sanity check
'''
to create additative synthesised signals eg
signal = additativeSignal([440,880,1320],[0.6,0.3,0.2],[0,0,0])
signal = additativeSignal([440,550,600],[0.6,0.5,0.3],[0,50,100])
然后
plot_pad_stats(signal)
使用
plot_signal(s,start,end) to plot the signal (or a section thereof)
注意=相位值是在样本中而不是度数或弧度!
或获得更多乐趣:
锯齿波:
f = 100
a = 0.8
ph = 0
noFqs = 20
signal = additativeSignal(
[f * i for i in range(1,noFqs+1)],
[am/i for i in range(1,noFqs+1)],
[0 for i in range(1,noFqs+1)])
plot_signal(signal[0:1000])
arrs = plot_pad_stats(signal,maxpad=1000,steps=1000,absolute=False)
与其他参数一起玩,尽您所能!
请注意,可能需要一些时间来绘制!
以下是供您使用的代码(python 3.x)
import numpy as np
import matplotlib.pyplot as plt
def pad(s,n):
'''
pad signal to make length n
'''
ls = len(s)
#print('Pad request: sig len: {}, new len: {}'.format(ls,n))
if n <= ls:
return s
s2 = np.zeros(n)
try:
s2[:ls] = s
except:
#print('Pad exception: sig len: {}, new len: {}'.format(ls,n))
return s
return s2
def norm_fft(s,norm=True):
'''
See plot_pad_stats below for explanation
'''
if norm:
norma = 'ortho'
else:
norma = None
n = len(s)
ft = np.fft.fft(s,norm=norma) / n
ft = ft[range(int(n / 2))]
return ft
def additativeSignal(hza=[1],aa=None,ap=[0],n=88200,sr=44100,verbose=True):
'''
hza = herz array
aa = amplitude array
ap = phase array
n = number samples to return
sr = sampling frequency
verbose = sanity check
'''
xa = np.arange(0,n)
s = np.zeros(n)
a = 0
apl = len(ap)
if apl < len(hza):
apl = np.zeros(len(hza))
if not (isinstance(aa,(np.ndarray,list))):
aa = np.ones(len(hza))
if not (isinstance(ap,(np.ndarray,list))):
aa = np.ones(len(hza))
for hz in hza:
amp=aa[a]
phase = ap[a]
if verbose:
print("freq %d, amp = %f, phase: %d" % (hz,amp,phase))
if hz != 0:
s += (np.sin(2*np.pi*(xa+phase)/(sr/hz))*amp)
a += 1
return s
def plot_pad_stats(s=None,
sr=44100,
minpad = 0,
maxpad = 300,
steps = 300,
real = True,
norm = None,
absolute = True,
active = False
):
'''
s = signal (audio etc)
sr = sample rate
minpad - minimum number of zeroed samples to append
maxpad - maximum number of zeroed samples to append
steps - number of ffts to calculate
real - use only real values otherwise take sqrt(real**2 + imag**2)
norm - np.fft.fft norm arg
absolute - make fft results absolute (ie >0)
active - standard activation function - any negative values changed to zero
'''
if norm:
norma = 'ortho'
else:
norma = None
minpad = int(minpad)
# Create 1 sec 4450 herz full volume audio signal at 44100 hz sampling frequency
if type(s) == type(None):
s = additativeSignal([440],[1],[0],44100,44100,True)
start = len(s) + minpad
end = start + maxpad
prange = maxpad
#Arrays of fft results:
# length fft
lenft = [0 for p in range(steps)]
# max fft val
maxft = [0 for p in range(steps)]
# min fft val
minft = [0 for p in range(steps)]
# mean fft val
meanft = [0 for p in range(steps)]
# same arrays using the above normalised function
lennft = [0 for p in range(steps)]
maxnft = [0 for p in range(steps)]
minnft = [0 for p in range(steps)]
meannft = [0 for p in range(steps)]
p1 = 0
step = prange/steps
print('fft len sig {} step {}'.format(len(s),step))
for p in np.arange(start,end,step):
#print('fft {} len sig {} padded len {}'.format(p1,len(s),int(p)))
s1 = pad(s,int(p))
ftp = np.fft.fft(s1,norm=norma)
ft = norm_fft(s1,norm)
if real:
ftp = ftp.real
ft = ft.real
else:
ftp = np.sqrt(ftp.real**2 + ftp.imag**2)
ft = np.sqrt(ft.real**2 + ft.imag**2)
if absolute:
ftp = abs(ftp)
ft = abs(ft)
if active:
ftp = [(f+abs(f))/2 for f in ftp]
ft = [(f+abs(f))/2 for f in ft]
minft[p1] = min(ftp)
maxft[p1] = max(ftp)
meanft[p1] = np.mean(ftp)
lenft[p1] = int(p)
minnft[p1] = min(ft)
maxnft[p1] = max(ft)
meannft[p1] = np.mean(ft)
lennft[p1] = int(p)
p1 += 1
#:22.8f
plt.subplot(211)
plt.title("FFT stats. Sig Len {} with {} - {} appended zeros".format(len(s),minpad,maxpad+minpad))
plt.xlabel("signal + pad length {} - {}".format(start,end))
plt.ylabel("max {:f} - {:f} and\nmin {:f} - {:f}\nand mean {:f} - {:f}\nffta values (abs/real)".format(min(maxft),max(maxft),min(minft),max(minft),min(meanft),max(meanft)))
plt.plot(lenft,maxft,'bo')
plt.plot(lenft,minft,'r+')
plt.plot(lenft,meanft,'g*')
plt.subplot(212)
plt.title("\nNormalised FFT stats. Sig Len {} with {} - {} appended zeros".format(len(s),minpad,maxpad+minpad))
plt.xlabel("signal + pad length\n{} - {}".format(start,end))
plt.ylabel("max {:22.8f} - {:22.8f} and\nmin {:22.8f} - {:22.8f}\nand mean {:22.8f} - {:22.8f}\nffta values (abs/real)".format(min(maxnft),max(maxnft),min(minnft),max(minnft),min(meannft),max(meannft)))
plt.plot(lennft,maxnft,'bo')
plt.plot(lennft,minnft,'r+')
plt.plot(lennft,meannft,'g*')
plt.show()
return maxft,minft,lenft
def plot_signal(s,start=0,end=None):
plt.title("Signal")
plt.xlabel("samples")
plt.ylabel("amplitude")
if end == None:
plt.plot(s)
else:
plt.plot(s[start:end])
plt.show()
致谢
托尼
答案 0 :(得分:0)
如果我理解的正确,您希望在频谱域中看到时间信号的正确幅度。在下面的示例中,我添加了两个振幅分别为1和0.5的正弦波。 在右边的频谱中,您可以看到振幅似乎减半了,这是由于复杂的FFT在负和正频率之间分配了功率。
import numpy as np
import matplotlib.pyplot as p
%matplotlib inline
T=3 # secs
d=0.04 # secs
n=int(T/d)
print(n)
t=np.arange(0,T,d)
fr=1 # Hz
y1= np.sin(2*np.pi*fr*t)
y2= 1/2*np.sin(2*np.pi*3*fr*t+0.5)
y=y1+y2
f=np.fft.fftshift(np.fft.fft(y))
freq=np.fft.fftshift(np.fft.fftfreq(n,d))
p.figure(figsize=(12,5))
p.subplot(121)
p.plot(t,y1,'.-',color='red', lw=0.5, ms=1)
p.plot(t,y2,'.-',color='blue', lw=0.5,ms=1)
p.plot(t,y,'.-',color='green', lw=4, ms=4, alpha=0.3)
p.xlabel('time (sec)')
p.ylabel('amplitude (Volt)')
p.subplot(122)
p.plot(freq,np.abs(f)/n)
p.xlabel(' freq (Hz)')
p.ylabel('amplitude (Volt)');