leggi il paragrafo relativo al Teorema di Fourier

Sviluppo in serie di Fourier

Avviare wxDevC++. Creare un nuovo progetto basato su wxWidgets Frame, denominarlo Fourier e salvarlo nella cartella Fourier.

Dichiarazione della classe Funzione.

Nel menu Progetto scegliere la voce Nuova Unità e completare, la pagina che si apre, con la dichiarazione della classe Funzione:

  class Funzione {
    private:
      double Periodo, Frequenza, Omega;
    public:
      Funzione(double T); // Costruttore di oggetti di classe Funzione
      double f(double t);
      double w();
   }; // fine classe Funzione
  

I campi membro della classe, di tipo double, sono:

I valori di Frequenza e di Omega sono ottenuti dal periodo.

Salvare questo file con il nome Funzione.h

Implementazione della classe Funzione.

Nel menu Progetto scegliere la voce Nuova Unità e completare la pagina con la seguente definizione della classe Funzione:

  #include "Funzione.h"
  

Il costruttore:

  Funzione::Funzione(double T) {
    Periodo = T;
    Frequenza = 1/T;
    Omega = 6.28/T;
  }
  

Il costruttore della classe Funzione riceve il parametro T con il quale, dopo aver inizializzato il campo membro Periodo, calcola la Frequenza e la pulsanzione Omega.

La funzione Onda Quadra:

espressione della funzione:
* parametro ricevuto: t (tempo) di tipo double;
* parametro restituito: valore della funzione al tempo t.

Calcola il valore della funzione in un istante t contenuto all'interno di un periodo dell'onda quadra

  double Funzione::f(double t) {
    int NrPer;
    if (t>Periodo) {
      NrPer = (int) (t/Periodo);
      t = t - (double) (NrPer*Periodo);
    } 
    if (t<=Periodo/2) return -10.0;
    else return 10.0;
  } // fine metodo f. 
  

Funzioni di accesso ai campi membro della classe:

Restituisce il valore del campo membro privato Omega.

  double Funzione::w(){
    return Omega;
  }
  

Salvare questo file con il nome Funzione.cpp.

Il metodo f(t) riceve un valore t come parametro e calcola il valore della funzione al tempo t, supponendo che questa rappresenti un'onda quadra.

Il metodo w() restituisce il valore della pulsazione Omega.

Dichiarazione della classe Armoniche.

Nel menu Progetto scegliere la voce Nuova Unità e completare, la pagina che si apre, con la seguente dichiarazione della classe Armoniche:

  #include "Funzione.h"
  class Armoniche { 
    private:
      double A0, *A, *B; // i coefficienti delle armoniche
      double *M, *fi, T, dt, dwt;
      Funzione F;
    public:
      Armoniche(double Periodo);
    private:
      double Media();
      double Integrale(int k, bool AoB);
    public:
      double Coefficienti(int k);
      double a0();
  }; // fine classe Armoniche
  

I campi membro della classe sono: il termine costante A0, due array A e B (puntatori a double) che conterranno i coefficienti dello sviluppo in serie di Fourier, un array (puntatore a double) che contiene i moduli M e un array che contiene la fase fi di ciascuna componente dello spettro del segnale.

Questa classe incorpora un oggetto F di classe Funzione.

Il costruttore riceve il parametro con cui inizializzerà il campo membro Periodo della Funzione.

I metodi della classe comrpendono: Media() per il calcolo del termine costante, Integrale() per calcolare le ampiezze dei coefficienti A e B.

Due metodi per leggere i coefficienti Ak e Bk e il termine costante A0.

Implementazione della classe Armoniche.

Aggiungere una nuova unità (menu progetto) e completarla con il seguente codice:

  #include "Armoniche.h"
  #include <math.h>
  

il Costruttore.

Il costruttore richiama il costruttore della classe Funzione. Notare la sintassi: dopo la dichiarazione del costruttre della classe Armoniche segue il carattere ":" e il richiamo del costruttore al quale viene passato il parametro ricevuto dal costruttore della classe Armoniche.

  Armoniche::Armoniche(double Periodo) : F(Periodo) {
  

Il costruttore ha il compito di assegnare i valori iniziali ai campi membro dell'oggetto. In questo caso viene assegnato il valore iniziale al Periodo, e da esso si calcola un intervallo di ampiezza dt pari a 0.01·T. in questo intervallo il vettore descive un angolo di ampiezza ω·dt.

    T = Periodo;
    dt = T/100;
    dwt=F.w()*dt;
  

I quattro vettori A, B, M, fi vengono inizializzati, ognuno, con il puntatore ad un vettore di 3 double.

    A = new double[3];
    B = new double[3];
    M = new double[3];
    fi= new double[3]; 
  

I coefficienti Ak e Bk sono ottenuti integrando, su un periodo, il prodotto della funzione per il sen o per il coseno di k·ω·t

    for (int i=0; i<3; i++) {
      A[i] = Integrale(i+1, true);
      B[i] = Integrale(i+1, false);
    }
  

Dopo aver calcolato i coefficienti Ak e Bk si calcola il modulo Mk e la fase fik di ciascuna componente armonica.

    A0=Media();
    M[0]  = sqrt(A[0]*A[0]+B[0]*B[0]);
    fi[0] = atan(A[0]/B[0]);
    M[1]  = sqrt(A[1]*A[1]+B[1]*B[1]);
    fi[1] = atan(A[1]/B[1]);
    M[2]  = sqrt(A[2]*A[2]+B[2]*B[2]);
    fi[2] = atan(A[2]/B[2]);
  }
  

I metodi della classe

Calcolo della media: (il termine costante): si calcola l'area compresa tra la curva e l'asse delle ascisse, il risultato viene diviso per il periodo.

  double Armoniche::Media() { // il termine costante
    double t, somma;
    somma = 0.0;
    for (double alfa=0.0; alfa < F.w()*T; alfa += dwt) {
      t=alfa/F.w();
      somma += F.f(t)*dwt;
    }
    somma/=(2*3.14);
    return somma;
  }
  

l'integrale esteso ad un periodo viene calcolato con il metodo dei rettangoli: si fa variare l'angolo da 0 a 2·π, con incrementi ω·dt e si calcolano le aree f(t)·d(ωt. La somma delle areee viene, alla fine, divisa per π.

  double Armoniche::Integrale(int k, bool AoB) {
    double t, somma;
    somma = 0.0;
    for (double alfa=0.0; alfa < F.w()*T; alfa += dwt) {
      t=alfa/F.w();
      if (AoB) somma += F.f(t)*cos(k*F.w()*t)*dwt;
      else somma += F.f(t)*sin(k*F.w()*t)*dwt;
    }
    somma /= 3.14;
    return somma;
  }
  

Funzioni di accesso ai campi membro della classe.

  double Armoniche::Coefficienti(int k) {
    return M[k];
  }
  double Armoniche::a0() {
    return A0;
  }
  

Disegnare.

Nel file FourierFrm.cpp aggiungere il gestore di evento (richiamabile da una voce di menu):

  #include "FourierFrm.h"
  #include "Armoniche.h"
  void FourierFrm:: Mnuondaquadra1003Click (wxCommandEvent& event) {
    Funzione Quadra(20.0);
    Armoniche C(20.0);
    wxClientDC dc(this);
    wxPen pen(*wxRED, 1); // penna rossa di spessore 1
    dc.SetPen(pen);
    int AsseY, xAsseY, AsseX, X, Y, p, YPrec, gradi, somma;
    double alfa, scalaX, scalaY, t; 
    scalaX=300.0/(6.28);
    scalaY=200.0/25.0; 
    AsseY=150;
    AsseX=150;
    xAsseY=200;
    dc.DrawLine(0,AsseY,400,AsseY);
    dc.DrawLine(AsseX, 240,AsseX,0);
    for (alfa=0.0; alfa <= 6.28; alfa+=6.28/250.0){ 
      X = (int) (scalaX*alfa);
      t=alfa/Quadra.w();
      Y = AsseY - (int) (Quadra.f(t)*scalaY);
      dc.DrawPoint(X,Y); // Punto in posizione alfa, f(wt)
      Y = (int) (scalaY*C.a0()); // il valore costante
      dc.DrawPoint(X,Y);
      somma = Y; 
      Y = (int) (scalaY*C.Coefficienti(0)*sin(Quadra.w()*t));
      somma += Y;
      Y += AsseY;
      dc.DrawPoint(X,Y); 
      Y = (int) (scalaY*C.Coefficienti(1)*sin(2.0*Quadra.w()*t));
      somma += Y;
      Y += AsseY;
      dc.DrawPoint(X,Y); 
      Y= (int) (scalaY*C.Coefficienti(2)*sin(3.0*Quadra.w()*t));
      somma += Y;
      Y += AsseY;
      dc.DrawPoint(X,Y); 
      somma += AsseY;
      dc.SetPen(*wxBLACK);
      dc.DrawPoint(X,somma);
      dc.SetPen(*wxRED);
    } // fine ciclo for
  }
  

Per disegnare su un frame si crea un device context (dc) di classe wxClientDC, si crea una penna e si usano i metodi dell'oggetto dc per disegnare linee e punti sul frame.

Prima di disegnare le armoniche si calcolano i fattori di scala per l'asse X e per l'asse Y. Si tracciano i due assi, si disegnano le prime tre armoniche e la loro somma.

Esercizi: