Programowanie obiektowe w języku C++
Dokumentacja projektu
Opracował:
Władysław Partyka
Opis programu.
Fourier jest programem do analizy sygnałów zarówno w dziedzinie czasu jak i częstotliwości. W zależności od trybu pracy, na ekranie obserwujemy dwie pary wykresów.
W trybie „przebieg czasowy” możemy niejako „projektować” swój własny sygnał okresowy poprzez podanie częstotliwości podstawowej w szeregu Fouriera oraz amplitud i faz poszczególnych harmonicznych. Kolejna n-ta składowa sinusoidalna ma częstotliwość odpowiednio n razy większą od częstotliwości podstawowej. Po lewej stronie ekranu widzimy odpowiadający sygnałowi szereg Fouriera w postaci zespolonej - każdej harmonicznej odpowiada para wektorów na płaszczyźnie zespolonej, których suma geometryczna w każdej chwili czasu odpowiada wartości pojedynczego sygnału sinusoidalnego. Wynika stąd, że moduły pary wektorów odpowiadających za n-tą harmoniczna są dwukrotnie mniejsze od amplitudy tej harmonicznej, a kąty tworzone przed te wektory z osią Re układu współrzędnych wynoszą odpowiednio phi - 90° oraz
-(phi - 90°), ponieważ dla sygnału sinusoidalnego o fazie początkowej phi = 0°, obydwa wektory w chwili początkowej muszą znajdować się w położeniu prostopadłym do osi Re, tworząc z nią kąty odpowiednio 90° oraz -90°. Aby uprościć obsługę programu, zrezygnowałem z możliwości wprowadzenia składowej stałej, która w tym ujęciu decyduje jedynie o przesunięciu w poziomie względem początku układu współrzędnych na płaszczyźnie zespolonej punktu zaczepienia pierwszego wektora.
W trybie „analiza harmoniczna” widoczne są na ekranie dwa wykresy - po lewej stronie znajduje się charakterystyka amplitudowa badanego sygnału, po prawej zaś stronie charakterystyka fazowa. Obydwa wykresy przedstawiają pewne dyskretne wartości, prążki odpowiadające poszczególnym harmonicznym.
Instrukcja obsługi.
Przebieg czasowy.
Dodawanie kolejnej harmonicznej.
Aby dodaj kolejną składową sinusoidalną, należy wprowadzić wartości do pól edycyjnych oznaczonych „Amplituda” i „Faza”. W przypadku wartości rzeczywistych należy pamiętać o rozdzieleniu części ułamkowej i całkowitej kropką, a nie przecinkiem. W przypadku amplitudy wartość ta musi być nieujemna (ponieważ inna nie ma sensu). Standardowo kolejne wektory dodawane są na koniec listy (czyli na koniec tworzonego szeregu Fouriera). Aby móc wstawić harmoniczną w inne miejsce, należy podświetlić na liście „Składowe harmoniczne” pozycję, przed która chcemy wstawić nową harmoniczną
i odfajkować pole „wstawiaj na koniec”. Dodanie następuje w momencie kliknięcia przycisku „Dodaj”.
Usuwanie wybranej harmonicznej.
Aby usunąć wybraną harmoniczną, należy podświetlić ją na liście „Składowe harmoniczne” i kliknąć przycisk „Usuń”. Jeżeli żadna pozycja nie jest zaznaczona, kliknięcie przycisku nie spowoduje wykonania żadnej akcji.
Czyszczenie listy harmonicznych.
Aby usunąć wszystkie składowe sinusoidalne aktualnie znajdujące się na liście odpowiadającej szeregowi Fouriera, należy kliknąć przycisk „Wyczyść”. Wykonanie tej akcji w przypadku listy pustej nie da żadnego efektu.
Zmiana częstotliwości podstawowej.
Standardowo wynosi ona 1 Hz. W celu zmiany tej wartości, należy wpisać nową liczbę w pole edycyjne „Częstotliwość podstawowa”. Zatwierdzenie nowej wartości odbywa się poprzez kliknięcie przycisku „OK” znajdującego się obok. Wartość ta musi być liczbą rzeczywistą dodatnią (inna nie ma sensu). Pamiętać należy także o kropce zamiast przecinka dziesiętnego.
Start / Pauza.
W każdym momencie działania programu można zatrzymać animację ilustrującą przebieg sygnału w dziedzinie czasu i na płaszczyźnie zespolonej. W tym celu należy kliknąć przycisk „Pauza”. Po wykonaniu tej czynności, napis na przycisku automatycznie zmieni się na „Start”. Ponowne kliknięcie przycisku spowoduje wznowienie animacji.
Analiza harmoniczna.
Sygnał poddawany analizie harmonicznej podawany jest przez użytkownika
w postaci funkcji opisanej w dziedzinie czasu i określonej na pewnym przedziale czasu bądź też przedziałach - stwarza to możliwość analizowania funkcji nierożniczkowalnych, jak wszelkiego rodzaju sygnału prostokątne, trójkątne, trapezoidalne.
Dodawanie funkcji określonej na przedziale czasu.
Aby dodać funkcję, należy wpisać jej równanie w polu edycyjnym „Sygnał opisany w dziedzinie czasu - f(t)”. Funkcja może być dowolnie zagnieżdżona i złożona, trzeba jednak pamiętać o właściwym umiejscowieniu nawiasów. Operator potęgowania „^” używa się w następujący sposób: t^2.5, co oznacza podniesienie do potęgi niecałkowitej równej 2,5. Zarówno podstawa potęgi jak i wykładnik mogą być funkcjami złożonymi, muszą być wtedy objęte nawiasami. W zapisie funkcji dopuszcza się stałe, w których część całkowita oddzielona jest od części ułamkowej kropką dziesiętną. Uwzględniona została tutaj stała π, która zapisuje się słownie - pi. Dopuszczalne funkcje specjalne to: sin, cos, tan, abs (moduł), ln, lg (logarytm o podstawie 2), log (logarytm o podstawie 10), atan, asin, acos, exp, sqrt (pierwiastek kwadratowy), sinh, cosh, tanh. Argumenty powyższych funkcji należy podawać zawsze w nawiasach. W przypadku stwierdzenia błędu w zapisie funkcji program poinformuje o tym użytkownika. Umieszczanie spacji nie ma wpływu na poprawność zapisu funkcji - są one usuwane przed sprawdzeniem poprawności. Domyślnie początek przedziału czasu jest końcem poprzedniego przedziału, należy zatem jedynie określić koniec przedziału. Koniec przedziału czasu powinien być zawsze liczbą większą niż początek, w przeciwnym wypadku wyświetlony zostanie komunikat o nieprawidłowym przedziale czasowym. Każdy przedział jest domknięty z prawej strony i otwarty z lewej, z wyjątkiem przedziału, którego początek stanowi 0 - ten przedział domknięty jest obustronnie. Jeżeli funkcja i przedział zostały określone prawidłowo, po kliknięciu przycisku „Dodaj” informacje te zostaną umieszczone w liście opisującej sygnał. Każdy wiersz tej listy składa się z przedziału czasu oraz równania funkcji, oddzielonych od siebie spacją.
Aktualizacja charakterystyk częstotliwościowych.
Następuje ona automatycznie po dodaniu lub usunięciu wiersza z listy opisującej sygnał albo po zmianie ilości próbek sygnału przypadających na jeden okres. Jeśli lista opisująca sygnał jest pusta, wykresy są puste.
Zmiana ilości próbek sygnału przypadających na okres.
Standardowo ilość próbek sygnału ustawiona jest na 256, co oznacza, że użyteczne widmo sygnału ograniczone jest do pierwszych 128 harmonicznych. Zmiana ilości próbek odbywa się poprzez wybór odpowiedniej wartości w liście kombinowanej. Maksymalna ilość próbek wynosi 1024, minimalna zaś - 8. Za okres sygnały przyjmowany jest koniec ostatniego przedziału, na którym określony jest sygnał. Okres próbkowania dobierany jest automatycznie w ten sposób, aby otrzymać określoną liczbę próbek przypadającą na okres sygnału.
Eksport widma.
Dzięki ten opcji można obejrzeć analizowany sygnał przybliżony określoną liczbą harmonicznych w trybie „Przebieg czasowy”. Jeśli lista opisująca sygnał nie jest pusta, należy wpisać odpowiednia wartość całkowitą dodatnią w pole edycyjne „Ogranicz sygnał do ... harmonicznych”. Maksymalna wartość, jaką można wpisać w to pole równa jest połowie ilości próbek przypadających na okres sygnału, minimalna zaś - 1. Pamiętać należy przy tym, że pierwsza harmoniczna to składowa stała, która nie jest uwzględniana na wykresie czasowym (program poinformuje użytkownika o wielkości składowej stałej), zatem w rzeczywistości do przebiegu czasowego program weźmie o jedną mniej harmoniczną niż to wynika z podanej przez użytkownika liczby. Po kliknięciu przycisku „Eksportuj” obliczone przez FFT harmoniczne zostaną dodane do szeregu Fouriera, którego w tej chwili nie widać. Jeśli wcześniej szereg ten nie był pusty, zostanie on najpierw wyczyszczony. W celu obejrzenia przebiegu czasowego wystarczy teraz zmienić tryb w menu programu na „Przebieg czasowy”.
Zmiana trybu pracy programu.
W celu zmiany tryby pracy programu należy wybrać odpowiedni tryb („Przebieg czasowy” lub „Analiza harmoniczna”) z menu „Tryb” widocznego w lewym górnym rogu okna programu.
Opis zastosowanego w programie algorytmu do interpretacji zapisu symbolicznego funkcji i obliczania wartości funkcji na podstawie tego zapisu.
Ograniczę się tutaj jedynie do słownego opisu działania tych algorytmów, bez odniesienia do konkretnego kodu. Zarówno algorytm interpretera jak również algorytm obliczania wartości funkcji są algorytmami rekurencyjnymi. Obydwa działają na podobnej zasadzie. Poniżej przedstawiona jest kolejność wykonywanych czynności w przypadku algorytmu do interpretacji symbolicznego zapisu.
Podział łańcucha opisującego funkcję na podłańcuchy oddzielone od siebie znakami operacji arytmetycznych takich jak: `+', `-` , `*', `/', `^' (czyli podniesienie do potęgi). W przypadku napotkania znaku `(`, pozostała część łańcucha, do napotkania odpowiadającego mu znaku `)', jest traktowana jako pewna całość i nie jest rozdzielana. W przypadku braku nawiasu zamykającego, generowany jest błąd. Podłańcuchy umieszczane są w liście.
Dla każdego elementu otrzymanej w poprzednim punkcie listy wykonujemy operację z punktu 1., tworząc listę podłańcuchów związaną z każdym elementem listy.
Jeśli doszliśmy do elementarnego łańcucha niepodzielnego na mniejsze, sprawdzamy jego poprawność - dopuszczalne wyrażenia to stała liczbowa, `t' oraz funkcje złożone, umieszczone w tablicy łańcuchów funkcyjnych. Jeśli na początku sprawdzanego łańcucha występuje operator funkcji złożonej, który zawiera powyższa tablica, to po tym łańcuchu musi wystąpić `(` oraz na końcu `)', jeśli po tym znaku występuje coś jeszcze, to generowany jest błąd. Następnie dla ciągu znaków zawartego pomiędzy `(` i `)' wykonywane są kolejno punkty od 1 do 3. Podobnie wygląda sytuacja, jeśli na początku sprawdzanego łańcucha znajduje się `(`. Nic poza zdefiniowanymi - stałą liczbową, `t' oraz funkcjami złożonymi z argumentem ujętym w nawiasy `()' nie jest poprawną funkcją.
Kolejność wykonywanych operacji przy obliczaniu wartości funkcji (przy założeniu, że jest ona poprawnie zapisana).
Podział łańcucha zawierającego symboliczną postać funkcji na podłańcuchy oddzielone od siebie znakami operacji arytmetycznych takich jak: `+', `-` , `*', `/', `^' (czyli podniesienie do potęgi). W przypadku napotkania znaku `(`, pozostała część łańcucha, do napotkania odpowiadającego mu znaku `)', jest traktowana jako pewna całość i nie jest rozdzielana. Tworzona jeśli lista podłańcuchów oraz odpowiadająca tej liście lista operacji arytmetycznych.
Wartość wyrażenia jest obliczana na podstawie listy podłańcuchów i listy operacji z zachowaniem priorytetów operacji - zatem najpierw wykonywane jest potęgowanie, potem mnożenie i dzielenie, a na końcu dodawania i odejmowania.
Jeśli łańcuch, którego wartość należy obliczyć jest elementarną stałą liczbową lub `t', to wartość ta jest dana natychmiast, w przeciwnym wypadku na danym łańcuchu wykonywane są operacje od 1 do 3. W przypadku napotkania funkcji złożonej, najpierw wykonuje się operacje od 1 do 3 na argumencie a dopiero później funkcję specjalną na obliczonej wartości argumentu. Jeśli łańcuch, którego wartość należy obliczyć, zaczyna się od `(`, to operacje od 1 do 3 wykonywane są na ciągu znaków znajdujących się pomiędzy nawiasami.
W zapisie funkcji, oprócz stałych liczbowych (część ułamkowa po kropce a nie po przecinku) dopuszczalna jest również stała `pi'. Program uwzględnia takie funkcje złożone jak: abs (moduł), sin (sinus), cos (cosinus), tan (tangens), ln (logarytm naturalny), lg (logarytm o podstawie 2), log (logarytm od postawie 10), asin (arcus sinus), acos (arcus cosinus), atan (arcus tangens), sinh (sinus hiperboliczny), cosh (cosinus hiperboliczny), tanh (tangens hiperboliczny), sqrt (pierwiastek kwadratowy). Program nie sprawdza, czy dziedziną funkcji jest cały podany przedział, dlatego w przypadku niespełnienia tego warunku, program może się zawiesić. Należy więc tak dobierać funkcję, aby była ona określona na całym przedziale.
Przykład
W każdym wywołaniu funkcji __Value tworzone są dwie listy: lista podłańcuchów FunList, która będzie w skrócie nazywana FL oraz lista operatorów działań matematycznych OperatorList, która będzie w skrócie nazywana OL. Zapis ilość(FO) będzie oznaczał ilość elementów w liście FL. Zapis FL: t, t^2+1, 4 będzie oznaczał, że elementami listy FL są kolejno „t”, „t^2+1” oraz „4” i elementy te mają indeksy odpowiednio 0, 1, 2. Analogiczny zapis będzie stosowany dla listy OL. Zapis OL: EMPTY będzie oznaczał, że lista OL nie posiada żadnych elementów. Wykonanie operacji na tymczasowej tablicy wartości będzie równoznacznie z zastąpieniem argumentów tej operacji jej wynikiem, co wiąże się ze zmniejszeniem rozmiaru tablicy. Zapis „?” będzie oznaczał, że w danym momencie komórka tablicy posiada wartość nieokreśloną. Rozpatrywana w przykładzie będzie funkcja zapisana w postaci: 25*(t/3+9)-cos(2*pi)/(t^2-8)+1 dla argumentu t = 3.
1)Wywołanie funkcji __Value dla łańcucha „25*(t/3+9)-cos(2*pi)/(t^2-8)+1”
FL: 25, (t/3+9), cos(2*pi), (t^2-8), 1
OL: *, -, /, +
ilość(FO) > 1, zatem tworzona jest tablica wartości tab1: ?, ?, ?, ?, ?. Wypełniana jest ona kolejno poprzez wywołanie funkcji __Value dla wszystkich elementów listy FO.
2)Wywołanie funkcji __Value dla łańcucha „25” z poziomu wywołania 1
FL: 25
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje stałą, zatem funkcja zwraca natychmiast wartość 25, koniec wywołania 2, powrót do wywołania 1.
tab1: 25, ?, ?, ?, ?
3)Wywołanie funkcji __Value dla łańcucha „(t/3+9)” z poziomu wywołania 1
FL: (t/3+9)
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 rozpoczyna się od „('', funkcja powoduje kolejne wywołanie.
4) Wywołanie funkcji __Value dla łańcucha „t/3+9” z poziomu wywołania 3
FL: t, 3, 9
OL: /, +
ilość(FL) > 1 zatem tworzona jest tablica wartości tab2: ?, ?, ?. Wypełniana jest ona kolejno poprzez wywołanie funkcji __Value dla wszystkich elementów listy FO.
5)Wywołanie funkcji __Value dla łańcucha „t” z poziomu wywołania 4
FL: t
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 jest równy „t”, zatem funkcja zwraca natychmiast wartość 3, koniec wywołania 5, powrót do wywołania 4.
tab2: 3, ?, ?
6)Wywołanie funkcji __Value dla łańcucha „3” z poziomu wywołania 4
FL: 3
FO: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje stałą, zatem funkcja zwraca natychmiast wartość 3, koniec wywołania 6, powrót do wywołania 4.
tab2: 3, 3, ?
7)Wywołanie funkcji __Value dla łańcucha „9” z poziomu wywołania 4
FL: 9
FO: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje stałą, zatem funkcja zwraca natychmiast wartość 9, koniec wywołania 7, powrót do wywołania 4.
tab2: 3, 3, 9
Następuje obliczenie wartości tablicy tab2 na poziomie wywołania 4.
a)wykonanie potęgowań - nie ma
b)wykonania mnożeń i dzieleń
- wykonanie dzielenia o indeksie 0 z listy OL na elementach 0, 1 z tab2
tab2: 1, 9
OL: +
c)wykonanie dodawań i odejmowań
- wykonanie dodawania o indeksie 0 z listy OL na elementach 0, 1 z tab2
tab2: 10
OL: EMPTY
Funkcja zwraca wartość 10, koniec wywołania 4, powrót do wywołania 3.
Funkcja zwraca wartość 10, koniec wywołania 3, powrót do wywołania 1.
tab1: 25, 10, ?, ?, ?
8)Wywołanie funkcji __Value dla łańcucha „cos(2*pi)” z poziomu wywołania 1
FL: cos(2*pi)
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 rozpoczyna się od „cos”, funkcja powoduje kolejne wywołanie.
9)Wywołanie funkcji __Value dla łańcucha „2*pi” z poziomu wywołania 8
FL: 2, pi
OL: EMPTY
Ilość(FL) > 1 zatem tworzona jest tablica wartości tab3: ?, ?. Wypełniana jest ona kolejno poprzez wywołanie funkcji __Value dla wszystkich elementów listy FO.
10)Wywołanie funkcji __Value dla łańcucha „2” z poziomu wywołania 9
FL: 2
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje stałą, zatem funkcja zwraca natychmiast wartość 2, koniec wywołania 10, powrót do wywołania 9.
tab3: 2, ?
11)Wywołanie funkcji __Value dla łańcucha „pi” z poziomu wywołania 9
FL: pi
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje predefiniowaną stałą, zatem funkcja zwraca natychmiast wartość 3.14, koniec wywołania 11, powrót do wywołania 9.
tab3: 2, 3.14
Następuje obliczenie wartości tablicy tab3 na poziomie wywołania 9.
a)wykonanie potęgowań - nie ma
b)wykonanie mnożeń i dzieleń
- wykonanie mnożenia o indeksie 0 z listy OL na elementach 0, 1 z tab3
tab3: 6.28
OL: EMPTY
c)wykonanie dodawań i odejmowań - nie ma
Funkcja zwraca wartość 6.28, koniec wywołania 9, powrót do wywołania 8.
Funkcja zwraca wartość cos(6.28) czyli 1, koniec wywołania 8, powrót do wywołania 1.
tab1: 25, 10, 1, ?, ?
12)Wywołanie funkcji __Value dla łańcucha „(t^2-8)” z poziomu wywołania 1
FL: (t^2-8)
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 rozpoczyna się od „('', funkcja powoduje kolejne wywołanie.
13)Wywołanie funkcji __Value dla łańcucha „t^2-8” z poziomu wywołania 12
FL: t, 2, 8
OL: ^, -
ilość(FL) > 1 zatem tworzona jest tablica wartości tab4: ?, ?, ?. Wypełniana jest ona kolejno poprzez wywołanie funkcji __Value dla wszystkich elementów listy FO.
14)Wywołanie funkcji __Value dla łańcucha „t” z poziomu wywołania 13
FL: t
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 jest równy „t”, zatem funkcja zwraca natychmiast wartość 3, koniec wywołania 14, powrót do wywołania 13.
tab4: 3, ?, ?
15)Wywołanie funkcji __Value dla łańcucha „2” z poziomu wywołania 13
FL: 2
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje stałą, zatem funkcja zwraca natychmiast wartość 2, koniec wywołania 15, powrót do wywołania 13.
tab4: 3, 2, ?
16)Wywołanie funkcji __Value dla łańcucha „8” z poziomu wywołania 13
FL: 8
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje stałą, zatem funkcja zwraca natychmiast wartość 8, koniec wywołania 16, powrót do wywołania 13.
tab4: 3, 2, 8
Następuje obliczenie wartości tablicy tab4 na poziomie wywołania 13.
a)wykonanie potęgowań
- wykonanie potęgowania o indeksie 0 z listy OL na elementach 0, 1 z tab4
tab4: 9, 8
OL: -
b)wykonanie mnożeń i dzieleń - nie ma
c)wykonanie dodawań i odejmowań
- wykonanie odejmowania o indeksie 0 z listy OL na elementach 0, 1 z tab4
tab4: 1
OL: EMPTY
Funkcja zwraca wartość 1, koniec wywołania 13, powrót do wywołania 12.
Funkcja zwraca wartość 1, koniec wywołania 12, powrót do wywołania 1.
tab1: 25, 10, 1, 1, ?
17)Wywołanie funkcji __Value dla łańcucha „1” z poziomu wywołania 1
FL: 1
OL: EMPTY
ilość(FL) = 1 i element o indeksie 0 reprezentuje stałą, zatem funkcja zwraca natychmiast wartość 1, koniec wywołania 17, powrót do wywołania 1.
tab1: 25, 10, 1, 1, 1
Następuje obliczenie wartości tablicy tab1 na poziomie wywołania 1.
a)wykonanie potęgowań - nie ma
b)wykonanie mnożeń i dzieleń
- wykonanie mnożenia o indeksie 0 z listy OL na elementach 0, 1 z tab1
tab1: 250, 1, 1, 1
OL: -, /, +
- wykonanie dzielenia o indeksie 1 z listy OL na elementach 1, 2 z tab1
tab1: 250, 1, 1
OL: -, +
c)wykonanie dodawań i odejmowań
- wykonanie odejmowania o indeksie 0 z listy OL na elementach 0, 1 z tab1
tab1: 249, 1
OL: +
- wykonanie dodawania o indeksie 0 z listy OL na elementach 0, 1 z tab1
tab1: 250
OL: EMPTY
Funkcja zwraca wartość 250, koniec wywołania 1, koniec algorytmu.
4. Opis poszczególnych klas.
Klasa: CFFT
Zastosowanie: Klasa ta udostępnia mechanizm szybkiej transformaty Fouriera (ang. Fast Fourier Transform). Wszystkie metody tej klasy są statyczne, nie trzeba więc tworzyć obiektu tej klasy, aby z nich skorzystać.
Atrybuty: nie posiada
Metody:
static void DrawAxies(CDC* pCDC,BOOL leftWnd);
Funkcja rysuje osie układu współrzędnych w dziedzinie zmiennej zespolonej i w dziedzinie czasu. Parametry:
pCDC - wskaźnik do kontekstu rysowania
leftWnd - zmienna typu boolean, jeśli true, to funkcja rysuje osie płaszczyzny zespolonej (lewy wykres w trybie „Przebieg czasowy”), w przeciwnym wypadku rysowane są osie dla dziedziny czasu.
static void DrawPhSpec(CDC* pCDC,complex<double> *Y,double f0,int N);
Funkcja rysuje dyskretną charakterystykę fazową. Parametry:
pCDC - wskaźnik do kontekstu rysowania
Y - tablica wartości zespolonych, na podstawie których rysowana jest dyskretna charakterystyka fazowa (tablica ta powinna być utworzona za pomocą metody fft)
f0 - częstotliwość podstawowa w widmie (rozdzielczość częstotliwościowa)
N - liczba próbek, które brane były do FFT
static void DrawAmpSpec(CDC *pCDC, complex<double> *Y, int N);
Funkcja rysuje dyskretną charakterystykę amplitudową. Parametry:
pCDC - wskaźnik do kontekstu rysowania
Y - tablica wartości zespolonych, na podstawie których rysowana jest dyskretna charakterystyka amplitudowa (tablica ta powinna być utworzona za pomocą metody fft)
f0 - częstotliwość podstawowa w widmie (rozdzielczość częstotliwościowa)
N - liczba próbek, które brane były do FFT
static complex<double>* fft(double *sampleTab,int N);
Funkcja przeprowadza szybką transformatę Fouriera na zadanej tablicy próbek. Zwracaną wartością jest tablica liczb zespolonych otrzymanych w wyniku FFT. Parametry:
sampleTab - tablica próbek sygnału
N - ilość próbek w tablicy, które zostaną wzięte do FFT
static double FindNearestTen(double value);
Funkcja zwraca najbliższą większą liczbę od zadanej, podzielną przez 10. Parametry:
value - liczba, dla której szukamy najbliższej większej liczby podzielnej przez 10
static void Point1(CDC* pCDC,int x,int y);
Funkcja rysuje pionową działkę w punkcie o zadanych współrzędnych. Parametry:
pCDC - wskaźnik do kontekstu rysowania
x - współrzędna pozioma punktu
y - współrzędna pionowa punktu
static void Point2(CDC* pCDC,int x,int y);
Funkcja rysuje poziomą działkę w punkcie o zadanych współrzędnych. Parametry:
pCDC - wskaźnik do kontekstu rysowania
x - współrzędna pozioma punktu
y - współrzędna pionowa punktu
CFFT();
Konstruktor. Nie wykonuje żadnych operacji.
virtual ~CFFT();
Destruktor. Nie wykonuje żadnych operacji.
Klasa: CVector
Zastosowanie: Klasa reprezentuje wirujący wektor na płaszczyźnie zespolonej.
Atrybuty:
double f - częstotliwość odpowiadającej wektorowi harmonicznej
double Phi - faza początkowa wektora
double X - moduł wektora
Metody:
void Draw(CDC* pMyCDC,BOOL clkcounter, double Re0, double Im0, double t);
Funkcja rysuje obraz wektora na płaszczyźnie zespolonej w określonej chwili czasu. Parametry:
pMyCDC - wskaźnik do kontekstu rysowania
clkcounter - parametr ten określa, w którym kierunku wiruje wektor, jeśli true, to zgodnie z kierunkiem ruchu wskazówek zegara, w przeciwnym wypadku odwrotnie
Re0 - współrzędna pozioma punktu zaczepienia wektora
Im0 - współrzędna pionowa punktu zaczepienia wektora
t - chwila czasowa, w której jest rysowany wektor
double Im(double t);
Funkcja zwraca długość rzutu wektora na oś Imaginaris w danym momencie czasu. Parametry:
t - chwila czasowa, w której rzutujemy wektor
double Re(double t);
Funkcja zwraca długość rzutu wektora na oś Realis w danym momencie czasu. Parametry:
t - chwila czasowa, w której rzutujemy wektor
CVector(double _X = 0., double _Phi = 0., double _f = 0.);
Konstruktor. Parametry są opcjonalne. Inicjuje atrybuty obiektu.
virtual ~CVector();
Destruktor. Nie wykonuje żadnych operacji.
Klasa: CPeriod
Zastosowanie: Klasa reprezentuje przedział, w którym opisana jest funkcja. Ze względu na sposób działania algorytmu obliczającego wartość funkcji opisanej przedziałami, niezbędne jest jedynie podanie końca przedziału, w którym funkcja jest opisana danym wzorem.
Atrybuty:
double Tstop - koniec przedziału
CString Fun - łańcuch zawierający symboliczną postać funkcji
Metody:
CPeriod(CString fun, double tstop);
Konstruktor. Parametry są wymagane. Inicjuje atrybuty na zadane wartości.
virtual ~CPeriod();
Destruktor. Nie wykonuje żadnych operacji.
Klasa: COperator
Zastosowanie: Klasa reprezentuje symboliczny operator działania matematycznego. Jest wykorzystywana przez algorytm sprawdzający poprawność symbolicznego zapisu funkcji oraz przez algorytm wyznaczający wartość funkcji na postawie jej symbolicznego zapisu.
Atrybuty:
CString Operator - łańcuch zawierający symboliczny zapis działania matematycznego
Metody:
COperator(CString __operator = "");
Konstruktor. Parametr jest opcjonalny. Inicjuje jedyny atrybut klasy na zadaną wartość.
virtual ~COperator();
Destruktor. Nie wykonuje żadnych operacji.
Klasa: CMyPoint
Zastosowanie: Klasa reprezentuje punkt na płaszczyźnie zespolonej. Jest wykorzystywana przy rysowaniu toru, po którym porusza się koniec ostatniego wektora zespolonego szeregu Fouriera.
Atrybuty:
int Im - współrzędna pionowa punktu na płaszczyźnie zespolonej
int Re - współrzędna pozioma punktu na płaszczyźnie zespolonej
Metody:
int GetIm();
Funkcja zwraca współrzędna pionową punktu.
int GetRe();
Funkcja zwraca współrzędna poziomą punktu.
CMyPoint(double _Re, double _Im);
Konstruktor. Parametry są wymagane. Inicjuje atrybuty na zadane wartości.
virtual ~CMyPoint();
Destruktor. Nie wykonuje żadnych operacji.
Klasa: CMyList
Zastosowanie: Wzorzec klasy. Reprezentuje listę jednokierunkową wskaźników do obiektów dowolnego typu. Typ wskazywanych obiektów jest parametrem wzorca klasy. Ta dynamiczna struktura danych jest wielokrotnie wykorzystywana w programie.
Atrybuty:
class Item
{
public:
t* ptr;
Item(t* _ptr,Item *_next): ptr(_ptr), next(_next) {}
~Item() { delete ptr; }
Item *next;
} *Items - wskaźnik do pierwszego elementu listy; klasa Item jest widoczna również poza klasą CMyList, reprezentuje ona element listy i zawiera wskaźnik do kolejnego elementu (atrybut next) oraz wskaźnik do właściwego obiektu (atrybut ptr), który jest przechowywany w liście, konstruktor klasy ma jako parametry wskaźnik do obiektu przechowywanego w liście oraz wskaźnik do następnego elementu listy, atrybuty te są wymagane; destruktor klasy usuwa z pamięci obiekt związany z danym element listy
int Count - ilość elementów listy w danym momencie
Metody:
t* operator[] (int nIndex);
Operator indeksowania, zwraca wskaźnik do obiektu przechowywany w zadanym elemencie listy. Parametry:
nIndex - indeks elementu w liście, liczony od 0
void Delete(int nIndex);
Funkcja usuwa z listy zadany element. Jeśli element o danym indeksie nie istnieje, funkcja nie wykona żadnej operacji na elementach listy. Parametry:
nIndex - indeks elementu w liście do usunięcia, liczony od 0
void Add(t *_new);
Funkcja wstawia element na koniec listy. Parametry:
_new - wskaźnik do obiektu, który ma być przechowywany w liście
void Insert(int nIndex,t *_new);
Funkcja wstawia do listy nowy element przed elementem o zadanym indeksie. Jeżeli indeks ten jest większy lub równy ilości elementów listy w danym momencie, to funkcja po prostu doda element na koniec listy. Parametry:
nIndeks - indeks elementu w liście, przed który zostanie wstawiony nowy element
_new - wskaźnik do obiektu, który ma być przechowywany w liście
void Clear();
Funkcja usuwa wszystkie elementy listy. Parametry: brak.
CMyList();
Konstruktor. Inicjuje pierwszy element listy na NULL i ilość elementów w liście na 0.
~CMyList();
Destruktor. Usuwa z pamięci wszystkie elementy listy.
Klasa: CFunc
Zastosowanie: Klasa ta udostępnia mechanizmy interpretacji i obliczania wartości funkcji na podstawie jej symbolicznego zapisu. Dziedziną funkcji jest czas - t.
Atrybuty:
CString Content - łańcuch zawierający symboliczną postać funkcji
Metody:
void Separate(CMyList<CFunc> *FunList,CMyList<COperator> *OperatorList = NULL);
Funkcja tworzy listę podłańcuchów funkcji oraz listę operatorów działań matematycznych, którymi te podłańcuchy są oddzielone. W przypadku operacji sprawdzenia poprawności zapisu funkcji lista operatorów jest niepotrzebna, dlatego jest ona parametrem opcjonalnym (domyślnie NULL). Parametry:
FunList - wskaźnik do listy obiektów klasy CFunc, w której zostaną zapisane podłańcuchy
OperatorList - wskaźnik do listy obiektów klasy COperator, w której zostaną zapisane operatory oddzielające poszczególne podłańcuchy
void DeleteSpaces();
Funkcja usuwa spacje w łańcuchu zawierającym symboliczną postać funkcji. Operacja ta jest przeprowadzana przed przystąpieniem do przetwarzania tego łańcucha. Parametry: brak.
int GetEndBracePos(int startbrace, CString content);
Funkcja zwraca odległość (liczoną w ilości znaków) nawiasu zamykającego od nawiasu otwierającego o zadanym indeksie w określonym łańcuchu. W Przypadku nie znalezienia nawiasu zamykającego, funkcja zwraca wartość 0. Parametry:
startbrace - indeks (rozumiany jako numer porządkowy znaku w łańcuchu, liczony od 0) nawiasu otwierającego w przeszukiwanym łańcuchu
content - przeszukiwany łańcuch
int IsFun(CString fun);
Funkcja zwraca indeks zadanego łańcucha w tablicy łańcuchów FunTable zadeklarowanej w programie. W przypadku, gdy dany łańcuch nie został odnaleziony w tej tablicy, funkcja zwraca -1. Parametry:
fun - łańcuch, którego indeksu szukamy
CString GetContent();
Funkcja zwraca łańcuch zawierający postać symboliczną funkcji. Parametry: brak.
double __Value(double t);
Rekurencyjna funkcja obliczająca wartość funkcji dla danego argumentu na podstawie jej zapisu symbolicznego. Parametry:
t - argument, dla którego ma być wyznaczona wartość funkcji
BOOL __IsValid();
Rekurencyjna funkcja sprawdzająca poprawność zapisu symbolicznego funkcji. Zwracaną wartością jest true w przypadku stwierdzenia poprawności lub false w przeciwnym przypadku. Parametry: brak.
BOOL IsValid();
Funkcja widoczna na zewnątrz klasy, sprawdza poprawność zapisu symbolicznego funkcji. Zwracaną wartością jest wartość zwrócona przez funkcję __IsValid(). Parametry: brak.
double Value(double t);
Funkcja widoczna na zewnątrz klasy, oblicza wartość funkcji dla podanego argumentu. Zwracana wartością jest wynik obliczeń funkcji __Value(t). Parametry:
t - argument, dla którego ma być wyznaczona wartość funkcji
CFunc(CString content = "");
Konstruktor. Parametr jest opcjonalny. Inicjuje jedyny atrybut klasy na zadaną wartość.
virtual ~CFunc();
Destruktor. Nie wykonuje żadnych operacji.
Klasa: CFourierDlg
Zastosowanie: Klasa okna programu. Część metod i atrybutów tej klasy została utworzona automatycznie przez środowisko Visual C++. Przy opisie tej klasy ograniczę się więc tylko do omówienia atrybutów i metod dodanych lub zmodyfikowanych przeze mnie.
Atrybuty:
int m_nStepIndex - numer chwili czasowej, w której znajduje się aktualnie badany przebieg okresowy; numer ten jest liczony modulo m_nStepCount, tzn. po osiągnięciu wartości m_nStepCount - 1 numer chwili czasowej jest zerowany; zmienna ta jest potrzebna do obliczenia wartości sygnału w dyskretnych momentach czasowych; jest to zegar sygnału, jego wartość zwiększana jest w każdym wywołaniu zdarzenia OnTimer, jeśli program nie jest zapauzowany
int m_nStepCount - liczba dyskretnych momentów czasowych przypadających na okres, w których obliczana jest wartość sygnału
int *m_pTrace - wskaźnik do tablicy liczb całkowitych o rozmiarze m_nStepCount, w której przechowywane są wartości sygnału odpowiadające kolejnym chwilom czasowym, tablica ta jest wykorzystywana przy rysowaniu przebiegu czasowego
int m_nVectorCount - liczba harmonicznych w szeregu Fouriera, każdej harmonicznej odpowiada para dwóch wektorów sprzężonych, jednakże w liście przechowywane są informacje tylko o jednym wektorze
int m_nN - liczba próbek sygnału przypadających na okres, zmienna ta wykorzystywana jest w trybie „Analiza harmoniczna”
int m_nOldCx - poprzednia szerokość okna, zmienna wykorzystywana przy zmianie rozmiaru okna programu
int m_nOldCy - poprzednia wysokość okna, zmienna wykorzystywana przy zmianie rozmiaru okna programu
CMyList<CPeriod> m_FList - lista opisująca funkcję w postaci symbolicznej, każdy element listy opisuje jeden przedział określenia funkcji (zobacz: klasa CPeriod)
enum {pc, ah} m_nTryb - zmienna zawierająca informację o aktualnym trybie pracy programu („Przebieg czasowy” albo „Analiza harmoniczna”)
double* m_pSampleTab - wskaźnik do tablicy liczb rzeczywistych rozmiaru m_nN, zawierającej próbki sygnału w dziedzinie czasu; tablica ta wykorzystywana jest przy wyznaczaniu FFT sygnału
double m_nDeltaf - krok w dziedzinie częstotliwości, częstotliwość podstawowa w szeregu Fouriera
complex<double>* m_pFFTSampleTab - wskaźnik do tablicy liczb zespolonych otrzymanej w wyniku FFT
BOOL m_bPause - zmienna zawierająca informację, czy program jest zapauzowany
BOOL m_bFirstSize - zmienna zawierająca informację, czy zdarzenie OnSize zostało wywołane po raz pierwszy
int m_nTrcPtsCount - indeks w tablicy m_pTrace, pod którym nie została jeszcze zapisana żadna wartość
CMyList<CMyPoint> m_Plist - lista przechowująca punkty płaszczyzny zespolonej, tworzą one tor, po którym porusza się koniec ostatniego wektora w zespolonym szeregu Fouriera
CMyList<CVector> m_VList - lista przechowująca informacje o harmonicznych szeregu Fouriera, każdej harmonicznej odpowiada para wektorów sprzężonych
CClientDC *m_pClientDC - wskaźnik do kontekstu rysowania związanego z oknem programu
CDC *m_pCDC - wskaźnik do kontekstu rysowania, powiązany z bitmapą
CBitmap *m_pBitmap1 - wskaźnik do bitmapy przechowywanej w pamięci, wykorzystana jest ona do efektu animacji, obraz najpierw tworzony jest w pamięci a dopiero później pokazywany w całości; bitmapa odpowiada lewemu wykresowi widocznemu w oknie programu
CBitmap *m_pBitmap2 - analogicznie jak wyżej, bitmapa ta odpowiada prawemu wykresowi widocznemu na ekranie
CComboBox m_cbN - lista rozwijalna związana z kontrolką w oknie programu, służy do wybierania ilość próbek sygnału na okres
CListBox m_cbList - lista zwykła związana z kontrolką na ekranie, w zależności od trybu pracy programu pokazuje skład harmoniczny sygnału lub opisaną przedziałami funkcję
double m_nAmp - zmienna powiązana z polem typu Edit, w zależności od trybu pracy programu wpisywana jest do niej amplituda nowej harmonicznej lub dolna granica przedziału czasowego
double m_nFaza - zmienna powiązana z polem typu Edit, w zależności od trybu pracy programu wpisywana jest do niej faza nowej harmonicznej lub górna granica przedziału czasowego
double m_nFP - zmienna powiązana z polem typu Edit, wpisywana jest do niej częstotliwość podstawowa szeregu Fouriera
BOOL m_bDodaj - zmienna powiązana z kontrolką typu CheckBox, determinuje, czy wstawiamy element do środka czy też na koniec listy m_cbList
CString m_sFun - zmienna powiązana z polem typu Edit, wpisywana jest do niej funkcja w postaci symbolicznej
int m_nLimit - ilość harmonicznych, do których zostanie obcięte widmo sygnału; zmienna wykorzystywana w trybie „Analiza harmoniczna”
BOOL m_bScaled - zmienna powiązana z kontrolką typu CheckBox, determinuje, czy rysowane wykresy w trybie „Przebieg czasowy” są przeskalowane (przydatna możliwość, jeśli wykres nie mieści się na ekranie)
Metody:
void AddTracePoint(double Re);
Funkcja dodaje punkt do tablicy m_pTrace pod indeks wskazywany przez m_nTrcPtsCount, w przypadku gdy indeks jest równy rozmiarowi tablicy, wszystkie elementy przesuwane są o jeden w lewo i punkt dodawany jest na koniec tablicy. Parametry:
Re - wartość sygnału w dziedzinie czasu
void ArrangeItems(UINT nType);
Funkcja rozmieszcza odpowiednio kontrolki w oknie w zależności od jego rozmiaru. Parametry:
nType - zmienna informująca, w jakim stanie jest okno programu (np. zmaksymalizowane, zminimalizowane itp.)
void CalcSampleValues();
Funkcja oblicza wartości sygnału opisanego przez listę m_Flist i umieszcza je w tablicy m_pSampleTab. Parametry: brak.
void ChangeView(int mode);
Funkcja modyfikuje odpowiednio podpisy kontrolek oraz ich widoczność na ekranie w zależności od aktualne trybu pracy programu. Parametry:
mode - nowy tryb pracy programu
void Draw();
Funkcja rysuje na ekranie wszystkie wykresy w zależności od aktualnego trybu pracy i chwili czasowej. Parametry: brak.
void DrawAxies(BOOL leftWnd);
Funkcja rysuje osie układu współrzędnych dla lewego lub prawego wykresu. Używana jedynie w trybie pracy „Przebieg czasowy”. Parametry:
leftWnd - zmienna zawierająca informację, dla którego wykresu należy narysować osie współrzędnych, jeśli true to dla lewego (czyli dla płaszczyzny zespolonej), w przeciwnym wypadku dla prawego (dziedzina czasu)
void DrawPrint();
Funkcja rysuje dwa symetryczne tory zakreślane przez końce ostatnich w szeregu Fouriera wektorów sprzężonych. Wykorzystywana jest tutaj lista m_Plist. Parametry: brak.
void DrawResultant(double length);
Funkcja rysuje wypadkową wszystkich wektorów na płaszczyźnie zespolonej. Wypadkowa ta zawsze jest prosta pokrywającą się z osią Realis płaszczyzny zespolonej. Parametry:
length - długość wypadkowej, jeśli ujemna, oznacza to że zwrot wypadkowego wektora będzie w lewo
void DrawTrace();
Funkcja rysuje przebieg czasowy sygnału. Każdemu punktowi na osi czasu przyporządkowuje jeden punkt z tablicy m_pTrace. Parametry: brak.
void DrawVectors(BOOL clkcounter,double t);
Funkcja rysuje wektory odpowiadające harmonicznym sygnału. Pierwszy wektor jest zaczepiony w środku układu współrzędnych na płaszczyźnie zespolonej a każdy następny w końcu poprzedniego. Parametry:
clkcounter - zmienna zawierająca informację o kierunku wirowania wektorów, jeśli true, to zgodnie z kierunkiem ruchu wskazówek zegara
t - moment czasu, w którym należy narysować obraz wektorów na płaszczyźnie zespolonej
void ExportVList();
Funkcja buduje listę wektorów m_VList na podstawie tablicy m_pFFTSampleTab otrzymanej w wyniku FFT. Parametry: brak.
int GetPeriodIndex(double t);
Funkcja zwraca indeks elementu w liście m_FList, który opisuje sygnał w danym momencie czasu. Parametry:
t - moment czasu, dla którego jest szukana postać funkcji
double GetTstop(int nIndex);
Funkcja zwraca koniec przedziału określoności dla elementu u zadanym indeksie w liście m_Flist. Parametry:
nIndex - indeks elementu w liście m_Flist, dla którego szukany jest koniec przedziału określoności
void ImportList(int mode);
W zależności od trybu pracy funkcja wypełnia listę m_cbList na podstawie informacji o harmonicznych z listy m_VList albo informacji o opisie funkcji z listy m_FList. Parametry:
mode - tryb pracy programu
void Refresh();
Funkcja „odświeża” zawartość tablicy m_pFFTSampleTab. Parametry: brak.
void ResizeWindows(int oldw,int neww);
Funkcja zmienia rozmiar bitmap m_pBitmap1 i m_pBitmap2 po zmianie wielkości okna programu. Parametry:
oldw - poprzednia szerokość okna wykresu
neww - nowa szerokość okna wykresu
void UpdateScale();
Funkcja uaktualnia współczynnik skalowania, który jest zmienną globalną. Parametry: brak.
void UpdateVectors(int nIndex);
Funkcja powoduje uaktualnienie częstotliwości wektorów w liście m_VList począwszy od wektora o zadanym indeksie. Funkcja ta jest wywoływana w przypadku usunięcia jakiegoś wektora ze środka listy, dodania wektora do środka listy lub też po zmianie częstotliwości podstawowej szeregu Fouriera. Parametry:
nIndex - indeks wektora w liście m_VList, od którego wszystkie wektory mają być uaktualnione
double VectorSum();
Funkcja zwraca łączną długość wszystkich wektorów w liście m_VList (sumuje ich moduły). Wykorzystywana jest przy skalowaniu do wyznaczaniu maksymalnej wartości, która powinna być widoczna na wykresie. Parametry: brak.
afx_msg void OnCheckSkalowanie();
Funkcja obsługi meldunku od kontrolki typu CheckBox. Uaktualnia stan zmiennej m_bScaled.
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
Funkcja obsługi meldunku. Następuje w niej inicjalizacja niektórych zmiennych klasy okna.
afx_msg void OnDestroy();
Funkcja obsługi meldunku. Wykonuje operacje związane ze zwolnieniem pamięci zmiennych, które zostały zainicjowane w funkcji OnCreate.
afx_msg void OnDodaj();
Funkcja obsługuje meldunek od kontrolki typu Button. W zależności od trybu pracy programu dodaje element do listy m_VList albo do m_FList oraz do listy m_cbList.
afx_msg void OnOk();
Funkcja obslugi meldunku od kontrolki typu Button. Powoduje zmianę częstotliwości podstawowej w szeregu Fouriera i uaktualnia listę m_nVList po zmianie tej częstotliwości (zobacz: funkcja UpdateVectors).
afx_msg void OnSelchangeComboN();
Funkcja obsługi meldunku od kontrolki typu ComboBox. Wywoływana po wyborze elementu listy rozwijalnej. Powoduje zmianę rozmiaru tablicy m_pSampleTab.
afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
Funkcja obsługi meldunku. Inicjuje niektóre zmienne klasy okna, których nie można było zainicjować w funkcji OnCreate.
afx_msg void OnSize(UINT nType, int cx, int cy);
Funkcja obsługi meldunku o zmianie rozmiaru okna programu. Rozmieszcza odpowiednio elementy w oknie oraz zeruje niektóre zmienne.
afx_msg void OnStart();
Funkcja obsługi meldunku od kontrolki typu Button. W zależności od trybu pracy programu powoduje wznowienie animacji (nadając zmiennej m_bPaused wartości false) i zmianę tekstu na przycisku albo wyeksportowanie danych z tablicy m_pFFTSampleTab do listy m_VList.
afx_msg void OnTimer(UINT nIDEvent);
Funkcja obsługi meldunku od timera. Wywoływana jest co 17ms. Powoduje odświeżenie wszystkich wykresów w oknie programu, wywołując funkcję Draw.
afx_msg void OnTrybPrzebiegczasowy();
Funkcja obsługi meldunku od elementu menu. Powoduje zmianę wartości zmiennej m_nTryb na odpowiednią wartość.
afx_msg void OnTrybAnalizaharmoniczna();
Funkcja obsługi meldunku od elementu menu. Powoduje zmianę wartości zmiennej m_nTryb na odpowiednią wartość.
afx_msg void OnUsun();
Funkcja obsługi meldunku od kontrolki typu Button. W zależności od trybu pracy programu usuwa element z listy m_VList albo z m_FList oraz z listy m_cbList.
afx_msg void OnWyczysc();
Funkcja obsługi meldunku od kontrolki typu Button. W zależności od trybu pracy programu powoduje wyczyszczenie listy m_VList albo m_FList oraz listy m_cbList.