当我发现自己做的事情有所不同时,我正在编写一个简单的傅立叶变换实现,并查看了Wikipedia上的DFT公式以供参考,考虑了一下之后,我觉得Wikipedia版本肯定是错误的,因为考虑到信号表示当进行傅立叶变换(使用该方程式)时,它将返回错误的频谱:因为该方程式仅将信号绕复平面缠绕一次(由于$ n / N $且$ 0
要检查这一点,我编写了一些代码,产生了以下图像,这似乎证实了我的想法。


“使用等式的时间”的用法等式
$$ X_f = \ sum_ {n = 0} ^ {N-1} x_n(\ cos(2 \ pi f t_n)-i \ sin(2 \ pi f t_n))$$
,其中$ t $是时间向量(例如,采样$ x_n $的时间$ t_n $)。可以在下面的函数ft中找到。

上面链接的维基百科方程式复制到此处,以供参考:
$$ X_f = \ sum_ {n = 0} ^ {N- 1} x_n \ left(\ cos \ left(2 \ pi f \ frac {n} {N} \ right)-i \ sin \ left(2 \ pi f \ frac {n} {N} \ right)\ right )$$
可以在函数ft2中找到。

import numpy as np
import matplotlib.pyplot as plt 
plt.style.use('ggplot')

def ft(t, s, fs):
    freq_step = fs / len(s)
    freqs = np.arange(0, fs/2 + freq_step, freq_step)
    S = []
    for freq in freqs:
        real = np.sum(s * np.cos(2*np.pi*freq * t)) 
        compl = np.sum(- s * np.sin(2*np.pi*freq * t)) 
        tmpsum = (real**2 + compl**2) ** 0.5 
        S.append(tmpsum)
    return S, freqs

def ft2(s, fs):  # Using wikipedia equation
    nump=len(s)
    freq_step = fs / nump
    freqs = np.arange(0, fs/2 + freq_step, freq_step)
    S = []
    for i, freq in enumerate(freqs):
        real = np.sum(s * np.cos(2*np.pi*freq * i/nump))
        compl = np.sum(- s * np.sin(2*np.pi*freq * i/nump))
        tmpsum = (real**2 + compl**2) ** 0.5 
        S.append(tmpsum)
    return S, freqs


def main():
    f = 5 
    fs = 100 
    t = np.linspace(0, 2, 200)
    y = np.sin(2*np.pi*f*t) + np.cos(2*np.pi*f*2*t)

    fig = plt.figure()
    ax = fig.add_subplot(311)
    ax.set_title('Signal in time domain')
    ax.set_xlabel('t')
    ax.plot(t, y)

    S, freqs = ft(t, y, fs) 

    ax = fig.add_subplot(312)
    ax.set_xticks(np.arange(0, freqs[-1], 2)) 
    ax.set_title('Time using equation')
    ax.set_xlabel('frequency')
    ax.plot(freqs, S)

    S, freqs = ft2(y, fs) 
    ax = fig.add_subplot(313)
    ax.set_title('Using Wiki equation')
    ax.set_xlabel('frequency')
    ax.set_xticks(np.arange(0, freqs[-1], 2)) 
    ax.plot(freqs, S)

    plt.tight_layout()
    plt.show()

main()


显然,我不太可能在如此高的温度下随机发现错误个人资料Wiki页面。但是我看不到自己做过的错误吗?

评论

为了更深入地了解DFT的含义,我建议您阅读我的前两篇博客文章:“复杂单位圆的指数性质”(dsprelated.com/showarticle/754.php)和“ DFT图形解释:质心”加权根源”(dsprelated.com/showarticle/768.php)。

谢谢,我来看看。老实说,由于代码中的一个非常愚蠢的错误而引起的关注,我实在感到非常惊讶。

我也很惊讶连续与离散的关系虽然很大。我的博客全都涉及离散案例,而没有提及连续案例,这与将离散案例作为连续案例的样本版本进行教学有所不同。

#1 楼

您在ft2中有错误。您将同时增加ifreq。那不是您希望汇总工作的方式。我搞砸了修复它,但它变得混乱了。我决定从不连续的角度重写它,而不是尝试使用连续的术语。在DFT中,采样速率无关紧要。重要的是使用了多少样本(N)。档号(k)然后对应于频率,以每帧周期为单位。我试图使您的代码尽可能完整,以使您容易理解。我还展开了DFT计算循环,希望可以更好地揭示它们的性质。

希望对您有所帮助。

Ced

import numpy as np
import matplotlib.pyplot as plt 

def ft(t, s, fs):
    freq_step = fs / len(s)
    freqs = np.arange(0, fs/2, freq_step)
    S = []
    for freq in freqs:
        real = np.sum(s * np.cos(2*np.pi*freq * t)) 
        compl = np.sum(- s * np.sin(2*np.pi*freq * t)) 
        tmpsum = (real**2 + compl**2) ** 0.5 
        S.append(tmpsum)
    return S, freqs

def ft3(s, N):  # More efficient form of wikipedia equation

    S = []

    slice  = 0.0
    sliver = 2*np.pi/float(N) 

    for k in range(N/2):

        sum_real = 0.0    
        sum_imag = 0.0
        angle = 0.0
        for n in range(N):
            sum_real +=  s[n] * np.cos(angle)
            sum_imag += -s[n] * np.sin(angle)
            angle += slice

        slice += sliver
        tmpsum = (sum_real**2 + sum_imag**2) ** 0.5 
        S.append(tmpsum)

    return S

def ft4(s, N):  # Using wikipedia equation

    S = []

    for k in range(N/2):

        sum_real = 0.0    
        sum_imag = 0.0
        for n in range(N):
            sum_real +=  s[n] * np.cos(2*np.pi*k*n/float(N))
            sum_imag += -s[n] * np.sin(2*np.pi*k*n/float(N))

        tmpsum = (sum_real**2 + sum_imag**2) ** 0.5 
        S.append(tmpsum)

    return S

def ft5(s, N):  # Roots of Unity Weighted Sum

    sliver = 2 * np.pi / float( N ) 

    root_real = np.zeros( N )
    root_imag = np.zeros( N )

    angle = 0.0
    for r in range(N):
        root_real[r] =  np.cos( angle )
        root_imag[r] = -np.sin( angle )
        angle += sliver

    S = []

    for k in range( N/2 ):

        sum_real = 0.0    
        sum_imag = 0.0
        r = 0

        for n in range( N ):
            sum_real += s[n] * root_real[r]
            sum_imag += s[n] * root_imag[r]
            r += k
            if r >= N : r -= N

        tmpsum = np.sqrt( sum_real*sum_real + sum_imag*sum_imag )
        S.append( tmpsum )

    return S

def main():

    N  = 200
    fs = 100.0

    time_step = 1.0 / fs
    t = np.arange(0, N * time_step, time_step)

    f = 5.0
    y = np.sin(2*np.pi*f*t) + np.cos(2*np.pi*f*2*t)

    fig = plt.figure()
    ax = fig.add_subplot(311)
    ax.set_title('Signal in time domain')
    ax.set_xlabel('t')
    ax.plot(t, y)

    S, freqs = ft(t, y, fs) 

    ax = fig.add_subplot(312)
    ax.set_xticks(np.arange(0, freqs[-1], 2)) 
    ax.set_title('Time using equation')
    ax.set_xlabel('frequency')
    ax.plot(freqs, S)

    S = ft3(y, N) 
    ax = fig.add_subplot(313)
    ax.set_title('Using Wiki equation')
    ax.set_xlabel('frequency')
    ax.set_xticks(np.arange(0, freqs[-1], 2)) 
    print len(S), len(freqs)
    ax.plot(freqs, S)

    plt.tight_layout()
    plt.show()

main()




评论


$ \ begingroup $
顺便说一句,您可能遇到了问题,因为我的代码假定使用python3;)
$ \ endgroup $
– Nimitz14
18年4月27日在18:33

$ \ begingroup $
@ Nimitz14,没什么大不了的。我在数字上添加了“ float()”和一堆“ .0”。您的代码运行良好,我唯一需要删除的是“ plt.style.use('ggplot')”语句。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
18年4月27日在18:46

$ \ begingroup $
@ Nimitz14,我忘了提了,我在代码中添加了ft5例程,该例程预先计算了单位值的根,并真正显示了如何为每个bin使用相同的根来计算DFT。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
18年4月27日在21:19

#2 楼

我不会看你的代码。维基百科页面看起来还不错,但这是数学家和电气工程师之间“格式大战”或“符号大战”或“风格大战”的一个很好的例子。其中一些,我认为数学界人士是正确的。 EE永远不要为虚构单位采用“ $ j $”。也就是说,这是DFT的更好表达,反之则是:

DFT:
$$ X [k] = \ sum \ limits_ {n = 0} ^ {N-1 } x [n] \,e ^ {-j 2 \ pi nk / N} $$

iDFT:
$$ x [n] = \ frac {1} {N} \ sum \ limits_ {k = 0} ^ {N-1} X [k] \,e ^ {j 2 \ pi nk / N} $$

因为执行DSP的电气工程师喜欢使用$ x [n] $是“时间”中的样本序列,$ X [k] $是“频率”中的离散样本序列。数学家可能会更喜欢这样:

DFT:
$$ X_k = \ sum \ limits_ {n = 0} ^ {N-1} x_n \,e ^ {-i 2 \ pi nk / N} $$

iDFT:
$$ x_n = \ frac {1} {N} \ sum \ limits_ {k = 0} ^ {N-1} X_k \, e ^ {i 2 \ pi nk / N} $$

,与Wikipedia页面相同。

您可能需要更加注意使用指数中的$ + $或$-$以及如何相对于$ \ sin(\ cdot)$项将其转换为$ + $或$-$。

评论


$ \ begingroup $
如果我们使用i而不是j,就不能说ICE人是ELI。 ELJ JCE男子没有相同的戒指。文明将受到威胁
$ \ endgroup $
–user28715
18年4月26日在3:30

$ \ begingroup $
以利亚(Elijah)是汁人吗?
$ \ endgroup $
–罗伯特·布里斯托-约翰逊
18年4月26日在9:59

$ \ begingroup $
@ user28715好吧,在那种情况下,我的当前值不是负1的平方根。...youtube.com/watch?v=2yqjMiFUMlA
$ \ endgroup $
– Peter K.♦
20年1月15日在15:21

#3 楼

我回到了这一点,并尝试导出离散版本,这使事情变得更有意义:

以某种方式$ f_k t_n = f(n,k,N)$

$ f_k = \ frac {f_s} {N} k $和$ t_n = \ frac {T} {N} n $

$ f_s = \ frac {N} {T} $



$ f_k t_n = \ frac {f_s} {N} k \ frac {T} {N} n = \ frac {N} {TN} k \ frac {T} { N} n = \ frac {kn} {N} $

完成!