Python 获取fft振幅以匹配输入时域信号振幅标度(numpy.fft)

Python 获取fft振幅以匹配输入时域信号振幅标度(numpy.fft),python,numpy,signal-processing,fft,Python,Numpy,Signal Processing,Fft,我试图得到fft振幅,以匹配输入时域信号的振幅标度 对于这个问题,我已经尝试了太多的“答案”——似乎没有一个对我有效。所以我写了一个实验程序 这需要一段音频(来自文件或-您可以使用加法合成来创建波形) 然后我给出了用零填充信号的选项(选择要分析的步数和步数) 我没有一个网站来发布我创建的绘图的图像——但是如果你在底部(Python3.x)剪切并发布程序,你会很快看到它们 下面是该计划的一小部分 代码的相关部分(下面的plot_pad_stats())是: 我怀疑与相位有关,但不知道如何获得fft

我试图得到fft振幅,以匹配输入时域信号的振幅标度

对于这个问题,我已经尝试了太多的“答案”——似乎没有一个对我有效。所以我写了一个实验程序

这需要一段音频(来自文件或-您可以使用加法合成来创建波形)

然后我给出了用零填充信号的选项(选择要分析的步数和步数)

我没有一个网站来发布我创建的绘图的图像——但是如果你在底部(Python3.x)剪切并发布程序,你会很快看到它们

下面是该计划的一小部分

代码的相关部分(下面的plot_pad_stats())是:

我怀疑与相位有关,但不知道如何获得fft振幅标度以匹配所分析的时域信号

添加填充时的变化不仅有趣,而且信号从单一正弦波变为更复杂的波时的变化也有趣

非常感谢您的帮助:-托尼。fdlr@gmail.com

arrays=plot\u pad\u stats:只需在没有参数的情况下调用,即可绘制440hz信号的全幅

使用

然后

使用

注意=相位值以采样为单位,而不是以度或弧度为单位

或者为了更有趣:

锯齿波:

        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)
尽情地玩其他ARG吧

请注意,这可能需要一点时间来策划

下面是供您使用的代码(Python3.x)

将numpy导入为np
将matplotlib.pyplot作为plt导入
def垫(s、n):
'''
pad信号使长度为n
'''
ls=len(s)
#打印('Pad请求:sig len:{},new len:{}'。格式(ls,n))

如果n如果我理解正确,您希望在频谱域中看到时间信号的适当振幅。在下面的例子中,我添加了两个振幅为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)');

看看。。。。给出了解析fft调用输出的公式。。。在这个答案的底部是一个伪代码链接,以获取更多详细信息。。。为了给自己信心,您的过程正在工作,将时域信号转换为频域(fft调用),然后执行反向fft调用以返回时域中的相同数据。。。如果过程准确,则此输出时域将与输入时域大致匹配
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)
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()
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)');