DFTのためのWikipedia方程式の実装


10

私は簡単なフーリエ変換の実装を書いていて、参考のためにウィキペディアDFT方程式を見て、私が何か違うことをしていることに気づき、それについて考えた後、ウィキペディアのバージョンは間違っているに違いないと感じました。 (その方程式で)フーリエ変換すると誤ったスペクトルが返されることを通知します。/N0<<N1)、(複素平面を包み込みながら)偶数回周期的な信号は、DFT中に表示される通常のピーク(単位円を回っている間)が互いに打ち消し合うため、スペクトルがありません(偶数の場合)それらの数が表示されます)。

これを確認するために、次の画像を生成するコードをいくつか作成しました。 ここに画像の説明を入力してください

「方程式を使用する時間」は方程式を使用します

バツf=Σ=0N1バツcos2πft2πft
t 時間のベクトル(つまり、時間 t これで バツたとえば、サンプリングされました)。ft以下の関数で見つけることができます。

上でリンクされたウィキペディアの方程式は、参照用にここにコピーされます。

バツf=Σ=0N1バツcos2πfN2πfN
関数にあります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の意味をより深く理解するには、最初の2つのブログ記事「複雑な単位円の指数的性質」(dsprelated.com/showarticle/754.php)と「DFTのグラフィカルな解釈:重心」を読むことをお勧めします。 Unityの重み付けされたルーツ」(dsprelated.com/showarticle/768.php)。
Cedron Dawg

ありがとうございます。これが私のコードの非常にばかげたバグが原因であるときに、この注意が得られたことに正直に非常に驚いています。
Nimitz14、2018

私もびっくりです。しかし、継続的なものと離散的なものは大したことです。私のブログは、連続したケースのサンプルバージョンとして離散したケースを教えることとは異なる、連続したケースへの言及のない離散的なケースについてのすべてです。
Cedron Dawg

回答:


16

にバグがありft2ます。あなたは増加していますi、そしてfreq一緒です。それはあなたがあなたの総計を機能させたい方法ではありません。私はそれを修正するためにいじりましたが、それは散らかりました。継続的な用語を使うのではなく、個別の観点から書き直すことにしました。DFTでは、サンプリングレートは無関係です。重要なのは、使用されるサンプルの数です(N)。ビン番号(k)は、フレームあたりのサイクル単位の周波数に対応します。私は、コードをできるだけそのままにして、簡単に理解できるようにしようとしました。また、DFT計算ループを展開して、その性質を少しだけ明らかにしたいと思っています。

お役に立てれば。

セド

numpyをnpとしてインポートする
matplotlib.pyplotをpltとしてインポート 

def ft(t、s、fs):
    freq_step = fs / len(s)
    freqs = np.arange(0、fs / 2、freq_step)
    S = []
    freqsのfreqの場合:
        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)
    S、周波数を返す

def ft3(s、N):#ウィキペディア方程式のより効率的な形式

    S = []

    スライス= 0.0
    sliver = 2 * np.pi / float(N) 

    範囲(N / 2)のkの場合:

        sum_real = 0.0    
        sum_imag = 0.0
        角度= 0.0
        範囲(N)のnの場合:
            sum_real + = s [n] * np.cos(angle)
            sum_imag + = -s [n] * np.sin(angle)
            角度+ =スライス

        スライス+ =スライバー
        tmpsum =(sum_real ** 2 + sum_imag ** 2)** 0.5 
        S.append(tmpsum)

    戻り値

def ft4(s、N):#ウィキペディアの方程式を使用

    S = []

    範囲(N / 2)のkの場合:

        sum_real = 0.0    
        sum_imag = 0.0
        範囲(N)の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)

    戻り値

def ft5(s、N):#Unityの加重和の根

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

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

    角度= 0.0
    範囲(N)のrの場合:
        root_real [r] = np.cos(angle)
        root_imag [r] = -np.sin(角度)
        角度+ =スライバー

    S = []

    範囲内のkの場合(N / 2):

        sum_real = 0.0    
        sum_imag = 0.0
        r = 0

        範囲内のnの場合(N):
            sum_real + = s [n] * root_real [r]
            sum_imag + = s [n] * root_imag [r]
            r + = k
            r> = Nの場合:r-= N

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

    戻り値

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( '時間ドメインの信号')
    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( '方程式を使用した時間')
    ax.set_xlabel( '頻度')
    ax.plot(周波数、S)

    S = ft3(y、N) 
    ax = fig.add_subplot(313)
    ax.set_title( 'Wiki方程式の使用')
    ax.set_xlabel( '頻度')
    ax.set_xticks(np.arange(0、freqs [-1]、2)) 
    len(S)、len(freqs)を出力します
    ax.plot(周波数、S)

    plt.tight_layout()
    plt.show()

メイン()

ここに画像の説明を入力してください


ところで、私のコードはpython3を想定しているため、おそらく問題が発生していました;)
Nimitz14 '27

1
@ Nimitz14、大したことではない。「float()」と数値に「.0」の束を追加しました。あなたのコードは問題なく実行されました、私が削除しなければならなかった唯一のものは「plt.style.use( 'ggplot')」ステートメントでした。
Cedron Dawg

1
@ Nimitz14、私は言及するのを忘れていました、コードにft5ルーチンを追加しました。これは、単位値の根を事前計算し、各ビンに同じ根を使用してDFTがどのように計算されるかを実際に示します。
Cedron Dawg

4

コードを確認するつもりはありません。ウィキペディアのページは問題ないように見えますが、これ数学者と電気技術者の間の「フォーマット戦争」「表記戦争」、または「スタイル戦争」の良い例です。その一部は、数学の人々が正しいと思います。EEは採用すべきではなかった」j"虚数単位の場合。これは、DFTのより良い表現であり、逆は:

DFT:

バツ[k]=Σ=0N1バツ[]ej2πk/N

iDFT:

バツ[]=1NΣk=0N1バツ[k]ej2πk/N

DSPを行う電気技術者は、 バツ[] 「時間」における一連のサンプルとして バツ[k]「周波数」の離散サンプルのシーケンスとして。数学者はこれを好むかもしれません:

DFT:

バツk=Σ=0N1バツe2πk/N

iDFT:

バツ=1NΣk=0N1バツke2πk/N

ウィキペディアのページと同じです。

あなたはの使用にもっと注意を払う必要があるかもしれません + または 指数とそれがどのように変換されるか + または に対して 期間。


3
jの代わりにiを使用した場合、ICEの人であるELIとは言えません。JCEの男ELJは同じ指輪を持っていません。文明は脅かされる

1
エリヤジュースマン?
robert bristow-johnson

@ user28715そうですね、その場合、私は現在、マイナス1の平方根ではありません。... youtube.com/watch?v=2yqjMiFUMlA
Peter K.

0

私はこれに戻って、物事をより意味のあるものにするのに役立つ離散バージョンを導出しようとしました:

何とかして fkt=fkN

fk=fsNk そして t=TN

fs=NT

そう

fkt=fsNkTN=NTNkTN=kN

できた!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.