Implementazione dell'equazione di Wikipedia per il DFT


10

Stavo scrivendo una semplice implementazione di trasformata di Fourier e ho guardato l' equazione DFT su Wikipedia per riferimento , quando ho notato che stavo facendo qualcosa di diverso, e dopo averci pensato ho sentito che la versione di Wikipedia deve essere sbagliata perché è molto semplice pensare a un segnala che quando Fourier trasformato (con quell'equazione) restituirà uno spettro errato: Poiché l'equazione avvolge il segnale attorno al piano complesso solo una volta (a causa di con ), qualsiasi segnale periodico un numero pari di volte (mentre si avvolge il piano complesso) non avrà spettro come i picchi abituali (mentre si gira intorno al cerchio unitario) che apparirebbero durante un DFT si annulleranno a vicenda (quando appare un numero pari di essi).n/N0<n<N1

Per verificare questo ho scritto un codice che ha prodotto la seguente immagine, che sembra confermare ciò che i miei pensieri. inserisci qui la descrizione dell'immagine

"Tempo che usa l'equazione" usa l'equazione con un vettore of time (quindi l'ora in cui stato campionato ad esempio). Si trova nella funzione seguente.

Xf=n=0N1xn(cos(2πftn)isin(2πftn))
ttnxnft

L'equazione di Wikipedia, collegata sopra, viene copiata qui come riferimento: Può essere trovato nella funzione .

Xf=n=0N1xn(cos(2πfnN)isin(2πfnN))
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()

Ovviamente sembra piuttosto improbabile che avrei trovato casualmente un errore su una pagina wiki di così alto profilo. Ma non riesco a vedere un errore in quello che ho fatto?


Per una comprensione più approfondita del significato di un DFT, ti consiglio di leggere i miei primi due articoli del blog: "The Exponential Nature of the Complex Unit Circle" ( dsprelated.com/showarticle/754.php ) e "DFT Graphical Interpretation: Centroids of Weighted Roots of Unity "( dsprelated.com/showarticle/768.php ).
Cedron Dawg,

Grazie darò un'occhiata. Sono sinceramente molto sorpreso dell'attenzione che ha avuto quando tutto è dovuto a un bug molto sciocco nel mio codice.
Nimitz14

Anche io sono sorpreso. La cosa continua vs discreta è un grosso problema però. Il mio blog tratta del caso discreto senza alcun riferimento al caso continuo, che è diverso dall'insegnare il caso discreto come una versione campionata del caso continuo.
Cedron Dawg,

Risposte:


16

Hai un bug dentro ft2. Stai incrementando ie freqinsieme. Non è così che vuoi che la tua somma funzioni. Ho fatto un casino con la riparazione, ma è diventato disordinato. Ho deciso di riscriverlo da una prospettiva discreta invece di provare a usare la terminologia continua. Nel DFT, la frequenza di campionamento è irrilevante. Ciò che conta è quanti campioni vengono utilizzati ( N). I numeri bin ( k) corrispondono quindi alla frequenza in unità di cicli per frame. Ho cercato di mantenere il codice il più integro possibile in modo da renderlo facilmente comprensibile per te. Ho anche spiegato i cicli di calcolo DFT per rivelare, un po 'meglio, la loro natura.

Spero che sia di aiuto.

ced

importa numpy come np
import matplotlib.pyplot come plt 

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

def ft3 (s, N): # Forma più efficiente di equazione di Wikipedia

    S = []

    fetta = 0,0
    nastro = 2 * np.pi / float (N) 

    per k nell'intervallo (N / 2):

        sum_real = 0.0    
        sum_imag = 0.0
        angolo = 0,0
        per n nell'intervallo (N):
            sum_real + = s [n] * np.cos (angolo)
            sum_imag + = -s [n] * np.sin (angolo)
            angolo + = fetta

        fetta + = nastro
        tmpsum = (sum_real ** 2 + sum_imag ** 2) ** 0,5 
        S.append (tmpsum)

    ritorna

def ft4 (s, N): # Utilizzo dell'equazione di Wikipedia

    S = []

    per k nell'intervallo (N / 2):

        sum_real = 0.0    
        sum_imag = 0.0
        per n nell'intervallo (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)

    ritorna

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

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

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

    angolo = 0,0
    per r nell'intervallo (N):
        root_real [r] = np.cos (angolo)
        root_imag [r] = -np.sin (angolo)
        angolo + = nastro

    S = []

    per k nell'intervallo (N / 2):

        sum_real = 0.0    
        sum_imag = 0.0
        r = 0

        per n nell'intervallo (N):
            sum_real + = s [n] * root_real [r]
            sum_imag + = s [n] * root_imag [r]
            r + = k
            se r> = N: r - = N

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

    ritorna

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 ('Tempo usando l'equazione')
    ax.set_xlabel ( 'frequenza')
    ax.plot (freqs, S)

    S = ft3 (y, N) 
    ax = fig.add_subplot (313)
    ax.set_title ('Uso dell'equazione di Wiki')
    ax.set_xlabel ( 'frequenza')
    ax.set_xticks (np.arange (0, freqs [-1], 2)) 
    stampa len (S), len (freqs)
    ax.plot (freqs, S)

    plt.tight_layout ()
    plt.show ()

principale()

inserisci qui la descrizione dell'immagine


tra l'altro probabilmente hai avuto problemi perché il mio codice presupponeva python3;)
Nimitz14

1
@ Nimitz14, non è un grosso problema. Ho aggiunto "float ()" e un mucchio di ".0" s sui numeri. Il tuo codice funzionava bene, l'unica cosa che dovevo rimuovere era l'istruzione "plt.style.use ('ggplot')".
Cedron Dawg,

1
@ Nimitz14, ho dimenticato di menzionare, ho aggiunto una routine ft5 al codice che pre-calcola le radici dei valori unitari e mostra davvero come il DFT viene calcolato usando le stesse radici per ogni bin.
Cedron Dawg,

4

non esaminerò il tuo codice. la pagina di Wikipedia sembra a posto, ma è un buon esempio di "guerra dei formati" o "guerra della notazione" o "guerra di stile" tra matematici e ingegneri elettrici. in parte, penso che la gente di matematica abbia ragione. Gli EE non avrebbero mai dovuto adottare " " per l'unità immaginaria. detto questo, questa è una migliore espressione del DFT e il contrario è:j

DFT:

X[k]=n=0N1x[n]ej2πnk/N

iDFT:

x[n]=1Nk=0N1X[k]ej2πnk/N

perché gli ingegneri elettrici che fanno DSP amano usare come sequenza di campioni in "tempo" e come sequenza di campioni discreti in "frequenza". ai matematici potrebbe piacere di più:x[n]X[k]

DFT:

Xk=n=0N1xnei2πnk/N

iDFT:

xn=1Nk=0N1Xkei2πnk/N

e questo è lo stesso della pagina di Wikipedia.

potresti dover prestare maggiore attenzione all'uso di o nell'esponente e al modo in cui ciò si traduce in o rispetto al termine .++sin()


3
Se avessimo usato io invece di j, non potremmo dire ELI l'uomo ICE. ELJ l'uomo JCE non ha lo stesso anello. La civiltà sarebbe minacciata

1
elijah l'uomo del succo?
robert bristow-johnson il

@ user28715 Beh, in quel caso non è attualmente la radice quadrata di meno 1 .... youtube.com/watch?v=2yqjMiFUMlA
Peter K.

0

Sono tornato a questo e ho provato a ricavare la versione discreta che ha contribuito a rendere le cose più sensate:

In qualche modofktn=f(n,k,N)

fk=fsNk etn=TNn

fs=NT

Così

fktn=fsNkTNn=NTNkTNn=knN

Fatto!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.