leggi il paragrafo relativo al Teorema di Fourier
Avviare wxDevC++. Creare un nuovo progetto basato su wxWidgets Frame, denominarlo Fourier e salvarlo nella cartella Fourier.
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:
Periodo: fissa il periodo del segnale.
Frequenza: viene ottenuto come inverso del periodo.
Omega: rappresenta la velocità angolare.
I valori di Frequenza e di Omega sono ottenuti dal periodo.
Salvare questo file con il nome Funzione.h
Nel menu Progetto scegliere la voce Nuova Unità e completare la pagina con la seguente definizione della classe Funzione:
#include "Funzione.h"
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.
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.
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.
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.
Aggiungere una nuova unità (menu progetto) e completarla con il seguente codice:
#include "Armoniche.h" #include <math.h>
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]); }
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; }
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.
Modificare la funzione onda quadra data come prova, con la funzione periodica dente di sega, con il segnale triangolare, o altra funzione periodica. Per calcolare l'equazione di una funzione a dente di sega si devono determinare le equazioni di due rette. A tale scopo si fornisce il promemoria:
L' equazione di una retta passante per 2 punti (x0, y0) e (x1, y1) si ottiene applicando la similitudine tra triangoli rettangoli:
(y - y0)/(y1 - y0) = (x - x0)/(x1 - x0)
Si scelgano i seguenti punti attraverso cui passa la retta crescente del dente di sega:
x0 = 0
y0 = 0
e
x1 = 16,
y1 = 10,
Si scelgano i seguenti punti attraverso cui passa la retta decrescente del dente di sega:
x1 = 16,
y1 = 10,
e
x2 = 20,
y2 = 0,
Si dovrebbe ottenere il seguente segnale a dente di sega:
y = | 0.625*t | (0 < t ≤ 16) |
-2.5*t + 50 | (16 ≤ t < 20) |
Creare un menu di scelta del segnale periodico