Interfejs języka C++ ze środowiskiem obliczeniowym Matlab.

Transkrypt

Interfejs języka C++ ze środowiskiem obliczeniowym Matlab.
Katedra Elektrotechniki Teoretycznej i Informatyki
Przedmiot: Zintegrowane Pakiety Obliczeniowe W
Zastosowaniach InŜynierskich
Numer ćwiczenia:
8
Temat: Interfejs języka C++ ze środowiskiem
obliczeniowym Matlab.
1. Wprowadzenie
MATLAB, pomimo, iŜ jest kompletnym, samodzielnym środowiskiem do programowania i
manipulacji danych, często jego działanie jest wspomagane przez zewnętrzne programy spoza
środowiska MATLAB. MATLAB umoŜliwia zastosowanie, jako podprocedur, programów
napisanych w C lub Fortranie.
Podprocedury napisane w języku C lub Fortran moŜna wywoływać w MATLABie tak jak by
to były jego wbudowanymi funkcjami. Funkcje napisane w C lub Fortranie wywoływane w
środowisku MATLAB nazywane są MEX-plikami. MEX-plik (skrót od Matlab EXecutable) to
dynamicznie ładowana biblioteka (w Microsoft Windows są to pliki *.dll) którą moŜna
uruchomić z wiersza poleceń w oknie MATLABa tak jak zwykły m-plik.
MoŜliwość skorzystania z napisanych w C lub Fortranie podprocedur to niewątpliwie zaleta,
dzięki której moŜna nie tylko w prostszy sposób obsłuŜyć wiele urządzeń (np. generatory,
przetworniki A/C i C/A, programowalne filtry, mierniki), ale równieŜ zwiększyć szybkość
wykonywania wielu obliczeń (szczególnie jeśli oparte są one na instrukcjach sterujących takich
jak for czy while). Trzeba jednak pamiętać, iŜ MATLAB jest efektywnym narzędziem,
umoŜliwiającym oszczędzenie cennego czasu na pisanie programów w języku niŜszego-rzędu
takich jak C i Fortran i z tego względu naleŜy unikać pisania zbyt wielu podprocedur w pastaci
MEX-plików.
MATLAB rozpoznaje MEX-pliki przez odpowiadające danej platformie rozszerzenie. W
tabeli 1 podano moŜliwe rozszerzenia MEX-plików.
Tabela 1: MEX-pliki
Platforma
HP-UX
Linux
Macintosh
Solaris
Windows
Rozszerzenie MEX-pliku
mexhpux
mexglx
mexmac
mexsol
dll
2. Struktura MEX-pliku
KaŜdy MEX-plik składa się z:
a) #include ”mex.h”
b) Procedury mexFuction
c) Macierz mxArray
d) Wywołań funkcji API (interfejs programowania aplikacji, ang. Application Programming
Interface)
1
Grzegorz Psuj, Katedra Elektrotechniki Teoretycznej i Informatyki
Kontakt: e-mail: [email protected], tel: 91 449 47 27
#include ”mex.h”
KaŜdy MEX-plik C/C++ powinien być opatrzony nagłówkiem ”mex.h”. W tym wypadku
konieczne jest stosowanie w takim pliku procedur mx* oraz mex*.
Procedura mexFunction
KaŜdy MEX-plik musi zawierać zawierać procedure mxFuction (zwanna równieŜ procedura
Gateway). W ten sposób MATLAB otrzymuje dostęp do pliku DLL.
Składnia tej procedury jest następująca:
mexFunction(int nlhs, mxArray*plhs[], int nrhs, const mxArray *prhs[])
{
...
}
gdzie:
nlhs: liczba parametrów wyjściowych
phls: macierz wskaźników do parametrów wyjściowych
nrhs: liczba parametrów wejściowych
prhs: macierz wskaźników do parametrów wejściowych (parametry wejściowe mągą być
wyłącznie czytane przez mexFunction; nie powinny być modyfikowane wewnątrz procedury)
Macierz mxArray
mxArray jest specjalną strukturą zawierająca dane ze środowiska MATLAB. MATLAB
rozpoznaję tylko pojedyncze obiekty, np. typu mxArray. Wszystkie zmienne (skalary, wektory,
macierze, łańcuch, macierze komórkowe, struktury) są przechowywane jako mxArrary. W
mxArray moŜna wyróŜnić:
nazwę zmiennej w środowisku MATLAB
jej rozmiar
jej typ
czy jest typu rzeczywistego czy zespolonego
Procedura postaci:
mxArray *array;
umoŜliwia deklaracje zmiennej mxArray o nazwie array. Zmienna ta nie zawiera jeszcze Ŝadnych
wartości. Musza one dopiero zostać zdefiniowane/zainicjowane przy wykorzystaniu procedur
typu mx*
Wywołania funkcji API
Procedury typu mx* są wykorzystywane wewnątrz macierzy mxArray. SłuŜą do zarządzania
pamięcią oraz do tworzenia i niszczenia macierzy mxArray. Do bardziej przydatnych procedur
typu mx* zaliczyć moŜna:
tworzenie macierzy: mxCreateNumericArray, mxCreateCellArray,
mxCreateCharArray
dostęp do macierzy: mxGetPr, mxGetPi, mxGetM, mxGetData, mxGetCell
modyfikacja macierzy: mxSetPr, mxSetPi, mxSetData, mxSetField
zarządzanie pamięcią: xMalloc, mxCalloc, mxFree, mexMakeMemoryPersistent,
mexAtExit, mxDestroyArray, memcpy
Inne przydatne procedury:
mexFunction: przekazuje wskaźnik MEX-pliku C
mexErrMsgTxt: Wyświetla komunikat błędu i zwraca kontrolę do MATALBa
mexCallMATLAB: wzywa funkcję MATLABa lub teŜ zdefiniowany przez uŜytkownika
dowolny M-plik lub MEX-plik
mexGetArray: kopuje zmienne z innej przestrzeni wykonawczej
2
Grzegorz Psuj, Katedra Elektrotechniki Teoretycznej i Informatyki
Kontakt: e-mail: [email protected], tel: 91 449 47 27
mexPrintf: procedura druku ANSI C
mexWarnMsgTxt: wyświetla komunikat ostrzeŜenia
Kompletną lista procedur mx* oraz mex* moŜna znaleźć na stronach MATLABa
(http://www.mathworks.com) lub w plikach pomocy pod hasłem External/API Refrence.
3. Tworzenie MEX-pliku
Tworzenia MEX-pliku zostanie omówione na podstawie przykładu dostępnego równieŜ w
plikach pomocy MATLABa.
#include "mex.h"
void timestwo(double y[], double x[])
{
y[0] = 2.0*x[0];
}
Na początku naleŜy dodać nagłówek
”mex.h”
Następnie pojawia się procedura
obliczeniowa; w tym wypadku
dotyczy ona mnoŜenia jednej liczby
przez 2 i wyrzucania policzonej
wartości na zewnątrz jej
void mexFunction(int nlhs, mxArray *plhs[], int nrhs,
const mxArray *prhs[])
{
double *x, *y;
int mrows, ncols;
Następnie
pojawia
się
procedura mexFunction wraz
z procedurami wywołującymi
funkcje API
/* SPrawdzenie poprawnej ilości parametrow. */
if (nrhs != 1) {
mexErrMsgTxt("Wymagany jest jeden parametr wejsciowy.");
} else if (nlhs > 1) {
mexErrMsgTxt("Podano za duzo parametrow wyjsciowych");
}
/* The input must be a noncomplex scalar double. */
mrows = mxGetM(prhs[0]);
ncols = mxGetN(prhs[0]);
if (!mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]) ||
!(mrows == 1 && ncols == 1)) {
mexErrMsgTxt("Input must be a noncomplex scalar double.");
}
/* Create matrix for the return argument. */
plhs[0] = mxCreateDoubleMatrix(mrows,ncols, mxREAL);
/* Assign pointers to each input and output. */
x = mxGetPr(prhs[0]);
y = mxGetPr(plhs[0]);
/* Call the timestwo subroutine. */
timestwo(y,x);
}
Napisany MEX-plik nazwijmy timestwo.c
4. Kompilacja MEX-pliku
Przed wykonaniem kompilacji MEX-pliku naleŜy określić rodzaj kompilatora. MATLAB
posiada swój wewnętrzny interpreter języka C++, ale moŜna wykorzystać równieŜ kompilator
3
Grzegorz Psuj, Katedra Elektrotechniki Teoretycznej i Informatyki
Kontakt: e-mail: [email protected], tel: 91 449 47 27
Microsoft Visual C/C++. Definicja aktywnego kompilatora odbywa się z wykorzystaniem
składni mex –setup. Wybór kompilatora z pozycji MATLABa:
>> mex -setup
Please choose your compiler for building external interface (MEX) files:
Would you like mex to locate installed compilers [y]/n? y
Select a compiler:
[1] Digital Visual Fortran version 6.0 in C:\Program Files\Microsoft Visual Studio
[2] Lcc C version 2.4 in D:\MATLAB701\sys\lcc
[3] Microsoft Visual C/C++ version 6.0 in C:\Program Files\Microsoft Visual Studio
[0] None
Compiler: 3
Please verify your choices:
Compiler: Microsoft Visual C/C++ 6.0
Location: C:\Program Files\Microsoft Visual Studio
Are these correct?([y]/n): y
Po dokonaniu rodzaju kompilatora wykonuje się kompilację napisanego MEX-pliku poleceniem:
>> mex timestwo.c
Po udanej kompilacji moŜna wywołać utworzony MEX-plik poleceniem:
>> timestwo(5,2)
ans = 10
5. Ćwiczenie
Zdalne sterowanie generatorem funkcyjnym wymaga podania do generatora łańcucha
składającego się z liter i wartości numerycznych. Ciąg wartości moŜe np. odpowiadać jednemu
okresowi sygnału wyjściowego na generatorze. Niniejsze ćwiczenie będzie polegało na
stworzeniu takiego łańcucha w środowisku obliczeniowym MATLAB z wykorzystaniem
wbudowanych funkcji oraz z wykorzystaniem MEX-pliku C oraz porównaniu czasu trwania obu
operacji.
Przykładowy łańcuch moŜe mieć poniŜszą postać:
‘GENVAL,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30’
W podanym łańcuchu GENVAL dotycz określenia rodzaju danych wysyłanych do generatora. W
tym przypadku następujące po GENVAL wartości numeryczne to kolejne wartości sygnału
generowanego. Na wyjściu generatora dostaniemy sygnał liniowo rosnący.
Listing MEX-pliku słuŜący do utworzenia takiego łańcucha ma następująca postać:
#include "mex.h"
#include "stdio.h"
#include "stdlib.h"
4
Grzegorz Psuj, Katedra Elektrotechniki Teoretycznej i Informatyki
Kontakt: e-mail: [email protected], tel: 91 449 47 27
void mexFunction(
int nlhs,
//
mxArray *plhs[],
//
int nrhs,
//
const mxArray *prhs[] //
)
{
int a, mrows, ncols;
long j,n;
double *tab;
char *input_buf;
int buflen;
double ttmp;
char *ptr;
char tmp[5];
j=0;
n = 0;
tab = mxGetPr(prhs[1]);
Number of left hand side (output) arguments
Array of left hand side arguments
Number of right hand side (input) arguments
Array of right hand side arguments
/* Check for proper number of arguments. */
if (nrhs != 2) {
mexErrMsgTxt("Two inputs required");
} else if (nlhs > 2) {
mexErrMsgTxt("Too many output arguments");
}
if (mxIsChar(prhs[0]) != 1)
mexErrMsgTxt("Input must be a string.");
if (mxGetM(prhs[0]) != 1)
mexErrMsgTxt("Input must be a row vector.");
mrows = mxGetM(prhs[1]);
ncols = mxGetN(prhs[1]);
if (!mxIsDouble(prhs[1]) || mxIsComplex(prhs[1]) || (mrows != 1))
{
mexErrMsgTxt("Input must be a noncomplex vector double.");
}
buflen = (mxGetM(prhs[0]) * mxGetN(prhs[0])) + 1;
//
for (a=0;a<ncols;a++)
{
ttmp = abs((int)tab[a]);
if(ttmp<10)
n++;
else
if(ttmp<100)
n+=2;
else
if(ttmp<1000)
n+=3;
else
if(ttmp<10000)
n+=4;
else
n+=5;
if(tab[a]<0)
n++;
}
mexPrintf("%d",n);
input_buf = mxCalloc(buflen, sizeof(char));
mxGetString(prhs[0], input_buf, buflen);
5
Grzegorz Psuj, Katedra Elektrotechniki Teoretycznej i Informatyki
Kontakt: e-mail: [email protected], tel: 91 449 47 27
ptr = mxCalloc(n+ncols+buflen-1, sizeof(char));
j+=sprintf(ptr+j,"%s",input_buf);
for (a=0;a<ncols;a++)
{
j+=sprintf(ptr+j,"%s",_itoa((int)tab[a],tmp,10));
if (a!=ncols-1)
j+=sprintf(ptr+j,",");
}
plhs[0] = mxCreateString(ptr);
}
Proszę wpisać powyŜszą funkcję do edytora a następnie zapisać jako faststring.c. Kolejnym
krokiem będzie określenie kompilatora. W tym celu proszę uŜyć polecenia:
>> mex –setup
i wybrać kompilator Microsoft Visual C/C++ [3].
Po wybraniu kompilatora naleŜy przeprowadzić kompilację stworzonego MEX-pliku. W tym
celu naleŜy uŜyć polecenia:
>> mex faststring.c
Po skompilowaniu MEX-pliku naleŜy napisać skrypt pozwalający porównać czasy generacji
łańcuch przy uŜyciu wbudowanych w MATLABa funkcji i stworzonego MEX-pliku. Zakładamy
Ŝe poŜądany ciąg ma zawierać 4096 wartości rosnących od 1 do 4096.
Przydatne funkcje:
− tic, toc: podaje czas pomiędzy komendą tic a toc
tic
operacje
time=toc
− s = sprintf(format, A, ...): zapisuje sformatowane dane do łańcucha.
format
%c – pojedynczy znak
%d notyfikacja dziesiętna
%s ciąg znaków
Przykłady skryptu:
1)
% ilosc probek sygnalu podawanego na wyjscie generatora
N=4096;
%generacja lancuch przy uzyciu wbudowanych w srodowisko MATLAB funkcji
tic
str = sprintf('GENVAL')
for a = 1:N
str = sprintf('%s,%d',str,a);
% wywołanie rekurencyjne
end
m_time=toc
%generacja lancuch przy uzyciu stowrzonego MEX-pliku
tic
faststring('GENVAL',[1:N]);
c_time=toc
coef=m_time/c_time
6
Grzegorz Psuj, Katedra Elektrotechniki Teoretycznej i Informatyki
Kontakt: e-mail: [email protected], tel: 91 449 47 27
2)
% ilosc probek sygnalu podawanego na wyjscie generatora
N=65536;
%generacja lancuch przy uzyciu wbudowanych w srodowisko MATLAB funkcji
tic
s=sprintf('%d,',[1:N]);
str=['GENVAL,',s] ;
m_time=toc
%generacja lancuch przy uzyciu stowrzonego MEX-pliku
tic
faststring('GENVAL',[1:N]);
c_time=toc
coef=m_time/c_time
7
Grzegorz Psuj, Katedra Elektrotechniki Teoretycznej i Informatyki
Kontakt: e-mail: [email protected], tel: 91 449 47 27

Podobne dokumenty