Pliki dołączone do zasobu:

cvi_przyklad.zip


Opis zasobu:

Zadanie jest postawione w następujący sposób. Należy wygenerować sygnał sinusoidalny o częstotliwości 10 Hz. Następnie generujemy szum biały o częstotliwości większej niż 150 Hz. Dodajemy do siebie te sygnały. W ten sposób otrzymujemy sygnał zaszumiony. Następnie należy zaprojektować filtr cyfrowy, który odfiltruje szum i pozostawi czysty sygnał sinusoidalny.

Zaczynamy od stworzenia nowego projektu. Możemy go od razu nazwać, np. Sip_przykład1. Na razie projekt jest pusty, nie ma dołączonych żadnych plików. Stwórzmy nowy plik z definicją interfejsu użytkownika (File -> New -> UserInterface). Powstanie plik o rozszerzeniu .uir. Umożliwia on stworzenie interfejsu użytkownika. Robi się to w bardzo prosty sposób wybierając i odpowiednio układając potrzebne elementy graficzne. Na tym etapie należy się dobrze zastanowić co będzie potrzebne i jak rozmieścić elementy interfejsu graficznego.  W naszym przypadku musimy utworzyć dwa wykresy. Jeden z nich będzie przedstawiał zaszumiony, drugi przefiltrowany sygnał. Potrzebne będą też trzy przyciski do sterowania programem. Naciśnięcie pierwszego spowoduje generację i wyświetlenie sygnału zaszumionego, naciśnięcie drugiego zainicjuje filtrację sygnału i jednocześnie sygnał po filtracji zostanie wyświetlany na drugim wykresie. Trzeci przycisk będzie służył do zakończenia działanie programu. Najpierw stworzymy pierwszy wykres. Klikamy lewym przyciskiem myszy na szarym polu panelu interfejsu graficznego i wybieramy element Graph (rys. 6.1). Zmieniamy rozmiar wykresu do postaci, która nas zadowala. Podobnie umieszczamy drugi wykres. Następnie tworzymy trzy przyciski sterujące. W tym celu wybieramy, w taki sam jak wyżej sposób, element Command Button -> OK. Tworzymy trzy przyciski. Można je również dowolnie rozmieścić na panelu, a także zmienić ich rozmiar. Mamy już wszystkie potrzebne elementy. Teraz należy im przypisać odpowiednie atrybuty. Z każdym elementem związane jest wiele różnych atrybutów. Niektóre są bardzo istotne, inne mniej ważne. Okienko atrybutów jest dostępne po podwójnym kliknięciu danego elementu lewym przyciskiem myszki, albo z prawej strony okna środowiska LabWindows CVI (rys. 6.1). W przypadku panelu zmieniamy jego nazwę. Jeśli chcemy, żeby program się zamykał po kliknięciu krzyżyka, należy również przypisać do panelu odpowiednią funkcję zamykającą. Dla elementów typu Graph zmieniamy ich nazwy i ukrywamy legendę (opcja Legend Visible). Możemy też odpowiednio ustawić skalowanie osi na wykresie i sposób wyświetlania siatki z podziałką. W każdym przypadku ważne są nazwy stałych identyfikujących poszczególne elementy w programie (opcja Constant Name). Muszą one być unikalne. Nazwy tych stałych można zmienić, albo pozostawić przypisane automatycznie. Na końcu opisujemy przyciski. Tu musimy wykonać trochę więcej rzeczy, bo są to elementy sterujące programem. Dla pierwszego przycisku ustalamy nazwą (Properties -> Lebel) na _Wyświetl zaszumiony sygnał, drugi na _Filtruj sygnał i trzeci na _Wyjdź. Efekt końcowy jest pokazany na rys. 6.2.

Rys.6.1  Tworzenie interfejsu graficznego

 

Rys.6.2  Gotowy interfejs GUI programu

 

Teraz dołączymy plik interfejsu graficznego do projektu. Najpierw zapisujemy plik pod dowolną nazwą, np. sip_przyklad1.uir. Następnie dołączamy go do projektu (klikamy lewym przyciskiem myszy na nazwie projektu i wybieramy Add Existing file i nasz plik. Inną opcją dodawania plików do projektu jest polecenie Edit -> Add Files to project.

W projekcie musimy też oczywiście mieć plik źródłowy. Tworzymy go i dodajemy do projektu. Klikamy prawym przyciskiem myszy na nazwie projektu i wybieramy opcję Create New File i następnie Source File. Nazwa pliku źródłowego może być taka sama jak nazwa projektu (ale nie musi). Po kliknięciu OK zostanie utworzony nowy plik źródłowy. Jednocześnie, automatycznie zostanie dodany plik nagłówkowy. Mamy już wszystkie potrzebne pliki. W LabWindows CVI programujemy w języku C. Środowisko dostarcza jednak wielu narzędzi pomocnych w programowaniu. Część kodu można też wygenerować automatycznie.

W celu wygenerowania funkcji main() wracamy do pliku .uir. Klikamy w panel. Z menu wybieramy polecenie Code -> Generate_Main Function. W pliku źródłowym (z rozszerzeniem .c) powinny się pojawić definicje potrzebnych zmiennych oraz definicja funkcji main () (Listing 1).

 

#include <cvirte.h>                       

#include <userint.h>

#include "prz1.h"

static int panelHandle;

 

int main (int argc, char *argv[])

{

                if (InitCVIRTE (0, argv, 0) == 0)

                               return -1;             /* out of memory */

                if ((panelHandle = LoadPanel (0, "prz1.uir", PANEL)) < 0)

                               return -1;

                DisplayPanel (panelHandle);

                RunUserInterface ();

                DiscardPanel (panelHandle);

                return 0;

}

Listing 1. Wygenerowana funkcja main()

 

Usuwamy niepotrzebne linie kodu:

 

/// HIFN  What does your function do?

/// HIPAR x/What inputs does your function expect?

/// HIRET What does your function return?

int Define_Your_Functions_Here (int x)

{

                return x;

}

 

Tak utworzony program można już skompilować. Po kompilacji do pliku nagłówkowego sip_przyklad1.h zostaną automatycznie dołączone definicje odpowiednich stałych związanych z utworzonym wcześniej interfejsem użytkownika (panelem). Program można też uruchomić. Jednak po jego uruchomieniu pokazuje się panel główny i … nie ma jak z niego wyjść. Jest to związane z działaniem wygenerowanego kodu (funkcja main() ). Funkcja ta ładuje panel do pamięci ( LoadPanel() ) i wyświetla ten panel na ekranie monitora ( DisplayPanel() ). Następnie wykonywana jest funkcja RunUserInterface(). W dużym skrócie funkcja ta sprawdza w nieskończonej pętli, czy nie nastąpiły jakieś zdarzenia ( callback()). My nie mamy zdefiniowanych żadnych zdarzeń więc w programie nie można nic zrobić. Program możemy zatrzymać naciskając ikonkę STOP ze środowiska LabWindows CVI.

W następnym kroku należy dodać zdarzenia powiązane z naciśnięciami zdefiniowanych wcześniej przycisków.  Na początek zaprogramujmy możliwość wyjścia z programu. W tym celu najpierw musimy zdefiniować funkcję callback() (czyli zdarzenie) związane z przyciskiem Wyjdź. Klikamy plik .uir. Na panelu interfejsu użytkownika klikamy dwa razy przycisk Wyjdź i otwieramy okienko właściwości. Wpisujemy nazwę funkcji callback – quit. Należy też ustawić Control mode na Hot.  Zamykamy okienko właściwości. Następnie prawym przyciskiem myszy klikamy przycisk Wyjdź i wybieramy opcję Generate Control Calback. W pliku .c pojawi się kod odpowiedniej funkcji callback() jak na Listingu 2.

 

 

int CVICALLBACK quit (int panel, int control, int event,

  void *callbackData, int eventData1, int eventData2)

{

                switch (event)

                {

                               case EVENT_COMMIT:

 

                                               break;

                }

                return 0;

}

 

Listing 2. Wygenerowana funkcja callback()

 

Jednak funkcja ta nadal nic nie robi. Trzeba jeszcze dopisać funkcję kończącą program (Listing 3).

 

int CVICALLBACK quit (int panel, int control, int event,

                                                                                  void *callbackData, int eventData1, int eventData2)

{

                switch (event)

                {

                               case EVENT_COMMIT:

                                              

                                               QuitUserInterface(0);

 

                                               break;

                }

                return 0;

}

 

Listing 3. Kompletna funkcja callback()

W tym momencie można już zakończyć program przyciskiem Wyjdź. Aby można było zakończyć program kliknięciem krzyżyka trzeba jeszcze powiązać z panelem interfejsu użytkownika stworzoną funkcję kończenia programu. Robi się to we właściwościach panelu (Panel Settings -> CloseControl). Ustawiamy funkcję _Wyjdz(QUIT). Przy okazji możemy też zablokować maksymalizację panelu i zmianę jego wielkości (opcje Can Maximize i Sizable).

Musimy jeszcze oprogramować pozostałe przyciski. LabWindows CVI oferuje wiele różnych funkcji. Warto się z nimi zapoznać. Wszystkie mają tzw. panel funkcji,  poprzez który można wprowadzać zmienne i uzyskać pomoc odnośnie działania funkcji. Funkcje są zgromadzone w zakładce Library i pogrupowane w kategorie. Jest ich dość dużo. Oczywiście można też używać standardowych funkcji języka C. Ponieważ celem ćwiczenia nie jest nauka programowania, tu zostaną tylko opisane konkretne fragmenty kodu związane z przyciskami Wyświetl zaszumiony sygnał i Filtruj sygnał.

Najpierw tworzymy zdarzenia (funkcje callback() ) dla przycisków Wyświetl zaszumiony sygnał i Filtruj sygnał w sposób opisany wcześniej. Fragmenty kodu definiujące działanie przycisków należy wstawić ręcznie (Listing 4).

 

 

#include <analysis.h> 

#define NUMELEM 1024

#define ORDER 5

int i=0;                                                                    

const int order=ORDER;                //rząd filtru

double phase=0.0;                           //faza sygnału sinusoidalnego

double noise[NUMELEM];              //szum biały

double clipnoise[NUMELEM];       //szum biały obcięty do <150Hz

double sine[NUMELEM];                //sygnał sinusoidalny

double dirty[NUMELEM];               //zaszumiony sygnał sinusoidalny

double clean[NUMELEM];              //przefiltrowany sygnał sinusoidalny

double x1[ORDER+1];                       //warunki graniczne dla filtru IIR

double y1[ORDER+1];                       //"

double b[ORDER+1];                         //współczynniki filtru IIR

double a[ORDER+1];                         //"

 

int CVICALLBACK Start (int panel, int control, int event,

                                                                                  void *callbackData, int eventData1, int eventData2)

{

                switch (event)

                {

                               case EVENT_COMMIT:

                                               {

                                              

                                               WhiteNoise (NUMELEM, 1.0, 100, noise);  //Generacja szumu białego 

                                                                                             

                                               //Obliczenie współczynników BW (tablice a i b) dla filtru IIR górnoprzepustowego 150 Hz

                                               Bw_Coef (HIGHPASS, order, 1024, 150, 0, a, order+1, b, order+1);

               

                                               //Warunki graniczne dla filtru IIR, zakładamy, że nie znamy systemu, ustawiamy na 0.0

                                               for (i=0;i<(ORDER+1);i++) {

                                                               y1[i]=0.0;

                                                               x1[i]=0.0;

                                               }

 

                                               //Zastosowanie filtru górnoprzepustowego BW (150 Hz) do szumu

                                          IIRFiltering (noise, NUMELEM, a, y1, order+1, b, x1, order+1, clipnoise);

               

                                               SineWave (NUMELEM, 1.0, 7.8125e-3, &phase, sine);  //Generate a Sine wave

               

                                               //Dodanie przefiltrowannego szumu do sinusoidy

                                               Add1D (sine, clipnoise, NUMELEM, dirty);

               

                                               //Narysowanie zaszumionej sinusoidy

                                               PlotWaveform (panelHandle, PANEL_GRAPH, dirty,

                                                                                                                NUMELEM, VAL_DOUBLE, 1.0, 0.0, 0.0,

                                                                                                                1, VAL_THIN_LINE, VAL_EMPTY_SQUARE,

                                                                                                                VAL_SOLID, 1, VAL_YELLOW);

                                              

                                               }

                                               break;

                }

                return 0;

}

 

int CVICALLBACK Filtruj (int panel, int control, int event,

                                                                                               void *callbackData, int eventData1, int eventData2)

{

                switch (event)

                {

                               case EVENT_COMMIT:

                                              

                                               {

                                               //Obliczenie współczynników filtru dolnoprzepustowego (25Hz) 

                                               Bw_Coef (LOWPASS, order, 1024, 25, 0, a, order+1, b, order+1);

               

                                               //x1 i y1 zawierają warunki graniczne dla zastosowanego powyżej filtru IIR                                                                                     for (i=0;i<(ORDER+1);i++) {

                                                               y1[i]=0.0;

                                                               x1[i]=0.0;

                                               }

               

                                               //Zastosowanie filtru

                                               IIRFiltering (dirty, NUMELEM, a, y1, order+1, b, x1, order+1, clean);

 

                                              //Wykreślenie przefiltrowanej sinusoidy

                                               PlotWaveform (panelHandle, PANEL_GRAPH_2, clean,

                                                                                                                NUMELEM, VAL_DOUBLE, 1.0, 0.0, 0.0,

                                                                                                                1, VAL_THIN_LINE, VAL_EMPTY_SQUARE,

                                                                                                                VAL_SOLID, 1, VAL_YELLOW);

                                               }

 

                                               break;

                }

                return 0;

}

Listing 4. Funkcje callback() odpowiadające przyciskom Wyświetl zaszumiony sygnał i Filtruj sygnał

 

Funkcje przetwarzania danych pochodzą z biblioteki Advanced Analysis, stąd trzeba dodać nagłówek #include <analysis.h> . Funkcje do wyświetlania danych pochodzą z biblioteki User Interface (wcześniej dodany nagłówek #include <userint.h>). Oczywiście wszystkie potrzebne zmienne należy wcześniej zadeklarować.

Poniżej zamieszczono opis kodu związanego z przyciskiem Wyświetl zaszumiony sygnał. Na początku generowany jest szum biały (funkcja WhiteNoise() ). Opis parametrów funkcji można uzyskać przez panel funkcji - Recall Function Panel (rys. 6.3). Aby go wywołać klikamy na nazwie funkcji lewym przyciskiem myszy i wybieramy opcję Recall Function Panel. Dodatkową pomoc uzyskujemy przez kliknięcie prawym przyciskiem muszy gdziekolwiek na panelu.

Rys. 6.3 Panel funkcji WhiteNoise()

Klikając prawym przyciskiem myszy ma nazwie zmiennej otrzymujemy jej opis. Funkcja WhiteNoise() generuje 1024 próbki szumu i zapisuje je w zmiennej noise.  Następnie szum jest przepuszczany przez filtr NOI, tak by pozostawić tylko częstotliwości wyższe od 150 Hz. Filtr cyfrowy Buterwortha jest zaprojektowany z użyciem funkcji Bw_Coef(). Filtracja jest przeprowadzona z użyciem funkcji IIRFiltering(). Następnie generowany jest sygnał sinusoidalny (funkcja SineWave() ). Szum jest dodawany do sygnału sinusoidalnego (funkcja Add1D() ). Wynikowy sygnał jest wykreślany na wykresie na panelu (funkcja PlotWaveform() ).

Kod związany z przyciskiem Filtruj sygnał jest następujący. Funkcja Bw_Coef() jest użyta do zaprojektowania filtru Butterwortha dolnoprzepustowego o częstotliwości odcięcia 25 Hz. Następnie zaszumiony sygnał jest przepuszczany przez ten filtr (funkcja IIRFiltering() ). Przefiltrowany sygnał jest wyświetlany na wykresie na panelu (funkcja PlotWaveform() ).

W ten sposób zakończyliśmy projekt. Można go oczywiście modyfikować na różne sposoby.

Ostatnia modyfikacja: środa, 17 listopada 2021, 12:03