Analisi di Fourier di una funzione periodica

la classe funzione

In un file di nome Funzione.java creato con il blocco note, inserire il seguente commento (facoltativo):

/**
   * questa classe descrive la funzione periodica
   di cui si vuole calcolare lo spettro.
*/

Questo commento serve al programmatore per rammentarsi il ruolo svolto da questa classe, qualora avesse bisogno di apportare modifiche.

L'intestazione della classe è preceduta da public se la classe viene salvata in un file di nome Funzione.java e non ci sono altre dichiarazioni di classi all'interno dello stesso file.


public class Funzione {

La classe contiene le proprietà Periodo, Frequenza e velocità angolare della funzione periodica.
Questi campi sono accessibili solo tramite i metodi della classe, quindi la loro regione di visibilità viene resa private:


    private double Periodo, Frequenza, Omega;

La classe deve possedere un costruttore che inizializza i campi membro (o proprietà) della classe, nel momento in cui si crea un'istanza della classe.

Il costruttore riceve come parametro il valore del periodo nella variabile T di tipo double e calcola la frequenza e la velocità angolare.


    /** Costruttore di oggetti di classe Funzione */
     public Funzione(double T) {
       Periodo = T;
       Frequenza = 1/T;
       Omega = 2*Math.PI/T;
     }


metodi della classe

La funzione seguente contiene l'espressione matematica che restituisce un valore di tipo double in corrispondenza ad un determinato istante di tempo ricevuto come parametro:


    /**
       * espressione della funzione:
       * parametro ricevuto: tempo;
       * parametro restituito: valore della funzione al tempo t.
    */
     public double f(double t) {
       /**
       * calcola il valore della funzione in un istante t
       * contenuto all'interno di un periodo dell'onda quadra
       */

Anche in questo caso è opportuno inserire dei commenti.

Se il tempo t è maggiore del periodo, si calcolano quante onde sono già passate
dividendo il tempo t per il periodo, e poi sottraendo t dalla durata di queste onde.


     int NrPer;
     if (t>Periodo) {
       NrPer = (int) (t/Periodo);
       t = t - (double) (NrPer*Periodo);
     }


In questa funzione viene rappresentata un'onda quadra usando il seguente metodo:
se il tempo t cade nel primo semiperiodo si restituisce il valore -10, altrimenti si restituisce il valore +10.


     if (t<=Periodo/2) return -10.0;
     else return 10.0;
     } // fine metodo f.


    /**
      accesso ai campi membro della classe
     */
       public double w(){
       return Omega;
       } // fine metodo w
    } // fine classe Funzione


la classe Armoniche
Il commento che precede la dichiarazione di classe riepiloga lo scopo della classe.


    /**
       * Applicazione del teorema di Fourier ad una funzione
       * periodica definita nella classe Funzione.
       * Calcola i coefficienti e poi somma le prime
       * tre armoniche in un periodo.
     */

La dichiarazione della classe è preceduta dalla regione di visibilità, public, solo se la classe viene salvata in un file separato.


    public class Armoniche {

le proprietà della classe sono
  • i riferimenti agli array dei coefficienti Ak e Bk,
  • agli array degli spettri di Ampiezza M e di fase fi,
  • Il periodo T
  • un incremento infinitesimo del tempo dt e il corrispondente angolo coperto dwt.
  • un oggetto F di classe Funzione


     // i coefficienti delle armoniche
     private double A0, A[], B[];
     private double M[], fi[], T, dt, dwt;
     private Funzione F;

Tutte le proprietà della classe hanno il modificatore private per obbligare il programmatore a far riferimento ad esse solo tramite i metodi della classe stessa.

Il Costruttore:


     /**
     * Costruttore di oggetti di classe Fourier
     * crea un oggetto Funzione e calcola il passo di
     * integrazione dt.
     */
     public Armoniche(double Periodo) {

Il costruttore riceve un parametro che serve per passare il periodo T alla funzione periodica da sviluppare in serie.


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

Poi, il costruttore riserva lo spazio per gli elementi degli array: richiama l'operatore new per assegnare lo spazio sufficiente a contenere tre valori di tipo double per ognuno degli array A, B, M e fi, ed assegna il riferimento all'array, così creato, alla proprietà della classe.


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

Il costruttore provvede anche al calcolo delle ampiezze Ak e Bk richiamando il metodo integrale(int, boolean)della classe:


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

Viene poi richiamato il metodo Media(), della classe, per calcolare il termine A0.

Infine, il costruttore calcola lo spettro M delle ampiezze e lo spettro fi delle fasi.


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

Il costruttore, a questo punto, è terminato.

Altri metodi della classe sono:
la funzione per il calcolo della Media in un periodo:


     private double Media() {
       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*Math.PI);
       return somma;
     }

la funzione per il calcolo dell'integrale con il metodo dei rettangoli.


     private double Integrale(int k, boolean 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)*Math.cos(k*F.w()*t)*dwt;
         else somma += F.f(t)*Math.sin(k*F.w()*t)*dwt;
       }
       somma /= Math.PI;
       return somma;
     }

La classe contiene anche le funzioni per accedere in lettura ai campi privati.


     public double Coefficienti(int k) {
       return M[k];
       }
     public double a0() {
       return A0;
     }
    } // fine classe Armoniche


La classe Gestore della Finestra


    import java.awt.*;
    import java.awt.event.*;
    public class GestoreFinestra implements WindowListener {
      public void windowIconified(WindowEvent e){};
      public void windowDeiconified(WindowEvent e){};
      public void windowDeactivated(WindowEvent e){};
      public void windowActivated(WindowEvent e){};
      public void windowOpened(WindowEvent e){};
      public void windowClosed(WindowEvent e){};
      public void windowClosing(WindowEvent e) {
         System.exit(0);
      } // fine window closing
    } // fine classe Gestore Finestra


La classe che produce il grafico
La classe che produce il grafico eredita proprietà e metodi dalla classe Canvas. Deve ridefinire il metodo paint ereditato. Questa classe non definisce proprietà.


    public class Diagramma extends Canvas {
    public void paint(Graphics g) {

Variabili locali al metodo paint:


      int QuotaYAscisse,
          DistanzaOrdinateOr,
          DistanzaOrdinateTr,
          X, Y, p, YPrec, gradi, somma;
      double alfa, scalaX, scalaY, t;

Vengono poi creati due oggetti, Uno per calcolare le armoniche di un segnale periodico, di periodo 20 unità, e un oggetto di classe Funzione, anch'esso di periodo 20 unità il cui scopo è solo quello di tracciare il grafico della funzione.


      Armoniche C = new Armoniche(20.0);
      Funzione G = new Funzione(20.0);

Viene calcolato il fattore di scala orizzontale rapportando 300 pixel a 6.28 radianti, e il fattore di scala verticale, rapportando 200 pixel a 25 unità di ampiezza.


      scalaX=300.0/(Math.PI*2);
      scalaY=200.0/25.0;

Poi vengono disegnati gli assi del diagramma su cui si rappresenta il grafico


     QuotaYAscisse=150;
     DistanzaOrdinateTr=150;
     DistanzaOrdinateOr=200;
     g.setColor(Color.red);
     g.drawLine(0,QuotaYAscisse,400,QuotaYAscisse);
     g.drawLine(DistanzaOrdinateTr, 240,DistanzaOrdinateTr,0);

Si entra in un ciclo in cui la variabile alfa, assume i valori da 0 a 6.28 radianti, con incremento dwt radianti,


    for (alfa=0.0; alfa <= 2*Math.PI; alfa+=Math.PI/100.0){

in corrispondenza dell'angolo alfa si calcola la coordinata X sullo schermo, e il tempo t impiegato a descrivere l'angolo alfa:


      X = (int) (scalaX*alfa);
      t=alfa/G.w();

Quindi si calcola la posizione Y sullo schermo del valore assunto dalla funzione al tempo t e si traccia un punto di colore verde.


      Y = QuotaYAscisse - (int) (G.f(t)*scalaY);
      g.setColor(Color.green);
      g.drawLine(X,Y,X,Y); // Punto in posizione alfa, f(wt)

Di colore arancione si disegna, invece, il valore della componente continua A0


      g.setColor(Color.orange);
      Y = (int) (scalaY*C.a0()); // il valore costante
      g.drawLine(X,Y,X,Y);

La componente continua è il primo termine della serie. Si inizializza la somma della serie con il valore di A0


      somma = Y;

L'armonica fondamentale:


      Y = (int) (scalaY*C.Coefficienti(0)*Math.sin(G.w()*t));
      somma += Y;
      Y += QuotaYAscisse;
      g.drawLine(X,Y,X,Y);

La seconda armonica


      Y = (int) (scalaY*C.Coefficienti(1)*Math.sin(2.0*G.w()*t));
      somma += Y;
      Y += QuotaYAscisse;
      g.drawLine(X,Y,X,Y);

La terza armonica


      Y= (int) (scalaY*C.Coefficienti(2)*Math.sin(3.0*G.w()*t));
      somma += Y;
      Y += QuotaYAscisse;
      g.drawLine(X,Y,X,Y);
      

La somma delle prime tre armoniche e del termine costante.


      somma += QuotaYAscisse;
      g.setColor(Color.magenta);
      g.drawLine(X,somma,X,somma);
    } // fine ciclo for
  } // fine metodo paint
} // fine classe


la Classe Principale


    public class Fourier {
     public static void main(String argv[]) {
       Frame Finestra = new Frame("Analisi di Fourier");
       Diagramma Grafico = new Diagramma();
       Finestra.addWindowListener(new GestoreFinestra());
       Finestra.setSize(500,400);
       Finestra.setLocation(100,100);
       Finestra.setResizable(false);
       Finestra.add(Grafico); // si aggiunge l’oggetto grafico
       Finestra.setVisible(true);
  }
}