S-funkcja w języku C

Transkrypt

S-funkcja w języku C
Programowanie Systemów
Sterowania
Dr inż. Dariusz Bismor
Gliwice, 2007
S-funkcje
aSystem-functions – reprezentacja bloku
Simulinka w języku programowania
aS-funkcje stanowią wygodny mechanizm
poszerzający możliwości Simulinka
aZastosowanie s-funkcji obejmuje:
`tworzenie nowych bloków funkcyjnych
`tworzenie bloków sterowników urządzeń
`wykorzystywanie istniejącego kodu w C, C++,
Fortranie, Adzie
`opis systemu jako układu równań
`przyspieszanie czasu symulacji
`dodawanie animacji do symulacji
Jak działają S-funkcje?
Blok w Simulinku:
wejście u
wyjście y
stany x
y = f o (t , x, u )
x&c = f d (t , x, u )
xd ,k +1 = f u (t , x, u )
x = xd + xc
równanie wyjścia
równanie różniczkowe
równanie różnicowe
Jak działają S-funkcje?
Fazy symulacji:
Inicjalizacja
Obliczenie wyjść
Aktualizacja stanów dyskretnych
Obliczenie pochodnych
Obliczenie wyjść
Obliczenie pochodnych
Lokalizacja przejść przez zero
Całkowanie
Pętla symulacji
Obliczenie czasu następnej próbki
Jak działają S-funkcje?
Metody wywołania S-funkcji:
aInicjalizacja:
`utworzenie struktury SimStruct
`ustawienie ilości i wymiarów wejść i wyjść
`ustawienie okresu próbkowania dla bloku
`przydzielenie pamięci dla stanów i tablicy
„sizes”
Jak działają S-funkcje?
Metody wywołania S-funkcji (c.d):
aObliczenie następnego kroku próbkowania
(dla bloków ze zmiennym próbkowaniem)
aObliczenie wyjść
aAktualizacja stanów dyskretnych
aCałkowanie równań ciągłych (z małym
krokiem całkowania)
aZakończenie symulacji
S-funkcja jako „M-file”
Składnia i parametry funkcji:
[sys, x0, str, ts] = funkcja(t, x, u, flaga, p1, p2, ...)
Wektor
Wektor
stanu
wejść
Bieżący
funkcji
czas
Warunki
Nieużywane,
Wektor
początkowe
czasów
[]Nazwa
próbkowania
Metoda
Parametry
wywołaniawywołania
Zwracana
wartość
Metody wywołania:
• flaga = 0 - inicjalizacja
• flaga = 1 - całkowanie równań ciągłych
• flaga = 2 - obliczanie równań dyskretnych
• flaga = 3 - obliczenie wyjść s-funkcji
• flaga = 4 - obliczenie czasu następnej próbki
• flaga = 9 - zakończenie symulacji
S-funkcja jako „M-file”
Przykładowa s-funkcja – wzmacniacz:
function [sys, x0, str, ts] = pomnoz(t, x, u, flaga, p1)
switch flaga,
case 0
[sys, x0, str, ts] = inicjalizacja;
case 3
sys = oblicz_wyjscie(t, x, u, p1 );
case {1, 2, 4, 9}
sys = [];
otherwise
error([‘Nieobsługiwana flaga ’, num2str(flaga)]);
end
S-funkcja jako „M-file”
Przykładowa s-funkcja – wzmacniacz:
function [sys, x0, str, ts] = inicjalizacja
mysizes = simsizes;
mysizes.NumContStates = 0;
mysizes.NumDiscStates = 0;
mysizes.NumInputs = 1;
mysizes.NumOutputs = 1;
mysizes.DirFeedthrough = 1;
mysizes.NumSampleTimes = 1;
sys = simsizes(mysizes)
x0 = []; str = [];
ts = [-1, 0]
%Dziedziczony czas próbkowania
S-funkcja jako „M-file”
Przykładowa s-funkcja – wzmacniacz:
function sys = oblicz_wyjscie(t, x, u, mnoznik)
sys = mnoznik.*u;
S-funkcja dla obiektów ciągłych
function [sys,x0,str,ts] = csfunc(t,x,u,flag)
% Macierze modelu układu ciągłego w postaci równań stanu:
A=[-0.09 -0.01; 1 0]; B=[ 1 -7; 0 -2]; C=[ 0 2; 1 -5]; D=[-3 0; 1 0];
% Obsługa flagi.
switch flag,
case 0
% Initializacja
[sys,x0,str,ts]=mdlInitializeSizes(A,B,C,D);
case 1
% Obliczenie pochodnych
sys = mdlDerivatives(t,x,u,A,B,C,D);
case 3
% Obliczenie wyjść
sys = mdlOutputs(t,x,u,A,B,C,D);
case { 2, 4, 9 }
% Nieużywane flagi
sys = [];
otherwise
% Obsługa błędów
error(['Unhandled flag = ',num2str(flag)]);
end
S-funkcja dla obiektów ciągłych
function [sys,x0,str,ts] = mdlInitializeSizes(A,B,C,D)
sizes = simsizes;
sizes.NumContStates = 2;
sizes.NumDiscStates = 0;
sizes.NumOutputs = 2;
sizes.NumInputs = 2;
sizes.DirFeedthrough = 1;
% Ponieważ macierz D niezerowa
sizes.NumSampleTimes = 1;
sys = simsizes(sizes);
% Zerowe warunki początkowe.
x0 = zeros(2,1);
str = [];
% Inicjalizacja macierzy czasów próbkowania bloku [tp, off]
% Dla układu ciągłego należy użyć wartości tp=0 i off=0
ts = [0 0];
S-funkcja dla obiektów ciągłych
function sys = mdlDerivatives(t,x,u,A,B,C,D)
%
sys = A*x + B*u;
function sys = mdlOutputs(t,x,u,A,B,C,D)
%
sys = C*x + D*u;
S-funkcja dla obiektów dyskretnych
function [sys,x0,str,ts] = dsfunc(t,x,u,flag)
% Macierze modelu układu dyskretnego w postaci równań stanu
A=[–1.3839 –0.5097; 1.0000 0]; B=[–2.5559 0; 0 4.2382];
C=[ 0 2.0761; 0 7.7891]; D=[ –0.8141 –2.9334; 1.2426 0];
switch flag,
case 0
% Initializacja
sys = mdlInitializeSizes(A,B,C,D);
case 2
% Aktualizacja stanów dyskretnych
sys = mdlUpdate(t,x,u,A,B,C,D);
case 3
% Obliczanie wyjść
sys = mdlOutputs(t,x,u,A,B,C,D);
case {1, 4, 9}
% Nieużywane flagi
sys = [];
otherwise
% Obsługa błędów
error(['unhandled flag = ',num2str(flag)]);
end
S-funkcja dla obiektów dyskretnych
function [sys,x0,str,ts] = mdlInitializeSizes(A,B,C,D)
sizes = simsizes;
sizes.NumContStates = 0;
sizes.NumDiscStates = 2;
sizes.NumOutputs = 2;
sizes.NumInputs = 2;
sizes.DirFeedthrough = 1;
sizes.NumSampleTimes = 1;
sys = simsizes(sizes);
x0 = ones(2,1);
str = [];
ts = [1 0];
% Ponieważ macierz D niezerowa
% Zerowe warunki macierzowe.
% Okres próbkowania: [tp, off]
S-funkcja dla obiektów dyskretnych
function sys = mdlUpdates(t,x,u,A,B,C,D)
%
sys = A*x + B*u;
function sys = mdlOutputs(t,x,u,A,B,C,D)
%
sys = C*x + D*u;
S-funkcja dla obiektów mieszanych
function [sys,x0,str,ts] = mixedm(t,x,u,flag)
% Element całkujący (1/s) połączony szeregowo z opóźnieniem jednostkowym (1/z).
% Okres próbkowania i offset dla części dyskretnej:
dperiod = 1; doffset = 0;
switch flag,
case 0
% Initializacja
[sys,x0,str,ts] = mdlInitializeSizes(dperiod,doffset);
case 1
% Obliczanie pochodnych
sys = mdlDerivatives(t,x,u);
case 2
% Aktualizacja stanów dyskretnych
sys = mdlUpdate(t,x,u,dperiod,doffset);
case 3
% Obliczanie wyjść
sys = mdlOutputs(t,x,u,doffset,dperiod);
case {4, 9}
% Nieużywane flagi
sys = [];
otherwise
% Obsługa błędów
error(['unhandled flag = ',num2str(flag)]);
end
S-funkcja dla obiektów mieszanych
function [sys,x0,str,ts] = mdlInitializeSizes(dperiod,doffset)
sizes = simsizes;
sizes.NumContStates = 1;
sizes.NumDiscStates = 1;
sizes.NumOutputs = 1;
sizes.NumInputs = 1;
sizes.DirFeedthrough = 0;
sizes.NumSampleTimes = 2;
sys = simsizes(sizes);
x0 = ones(2,1);
str = [];
ts = [0, 0; dperiod, doffset];
% DWA czasy próbkowania
S-funkcja dla obiektów mieszanych
function sys = mdlDerivatives(t,x,u)
sys = u;
function sys = mdlUpdate(t,x,u,dperiod,doffset)
% Funkcja aktualizująca stany dyskretne. Poniższy kod sprawdza, czy czas
% "t" jest wielokrotnością dyskretnego okresu próbkowania, z dokładnością 1e-8.
% Jeżeli tak jest, bieżąca wartość stanu ciągłego (x(1)) jest zwracana jako nowa
% wartość stanu dyskretnego
if abs(round((t-doffset)/dperiod)-(t-doffset)/dperiod) < 1e-8
sys = x(1);
% Wielokrotność dyskr. okresu próbkowania
% Przepisanie stanu ciągłego x(1) do stanu dyskretnego
else
sys = [];
% Czas "t" nie jest wielokrotnością okresu próbkowania
% Zwracana pusta macierz symbolizuje brak zmian
% stanu dyskretnego
end
S-funkcja dla obiektów mieszanych
function sys = mdlOutputs(t,x,u,doffset,dperiod)
% Funkcja aktualizująca wyjście. Poniższy kod sprawdza, czy czas
% "t" jest wielokrotnością dyskretnego okresu próbkowania, z dokładnością 1e-8
% (mdlOutputs jest także wywoływana w małym kroku symulacji)
% Jeżeli tak jest, bieżąca wartość stanu dyskretnego (x(2)) jest przepisywana na
% wyjście. W przeciwnym przypadku zwraca się pustą macierz symbolizującą
% brak zmian na wyjściu.
if abs(round((t-doffset)/dperiod)-(t-doffset)/dperiod) < 1e-8
sys = x(2);
else
sys = [];
end
S-funkcja ze zmiennym okresem
próbkowania
function [sys,x0,str,ts] = vsfunc(t,x,u,flag)
% Przykład s-funkcji ze zmiennym czasem próbkowania. Realizowana funkcja to zmienne
% opóźnienie - opóźnienie sygnału na pierwszym wejściu zależy od wartości sygnału na
% drugim wejściu: dt = u(2); y(t+dt) = u(t)
%
switch flag,
case 0
[sys,x0,str,ts] = mdlInitializeSizes;
% Inicjalizacja
case 2
sys = mdlUpdate(t,x,u);
% Aktualiz. stanów dyskretnych
case 3
sys = mdlOutputs(t,x,u);
% Obliczanie wyjść
case 4
sys = mdlGetTimeOfNextVarHit(t,x,u); % Obliczanie czasu nast. próbki
case { 1, 9 }
sys = [];
% Pozostałe flagi
otherwise
error(['Unhandled flag = ',num2str(flag)]); % Ewentualny błąd
end
S-funkcja ze zmiennym okresem
próbkowania
function [sys,x0,str,ts] = mdlInitializeSizes
%
sizes = simsizes;
sizes.NumContStates = 0;
sizes.NumDiscStates = 1;
sizes.NumOutputs = 1;
sizes.NumInputs = 2;
sizes.DirFeedthrough = 1;
% flag=4 - wymaganie bezpośredniego sprzężenia
sizes.NumSampleTimes = 1; %
wejścia z wyjściem
sys = simsizes(sizes);
x0 = [0];
str = [];
ts = [–2 0];
% Zerowe warunki początkowe
% Zwracana macierz pusta (niewykorzystywany parametr)
% Kod dla zmiennego czasu próbkowania
S-funkcja ze zmiennym okresem
próbkowania
function sys = mdlUpdate(t,x,u)
% Funkcja do aktualizacji stanów dyskretnych
sys = u(1);
function sys = mdlOutputs(t,x,u)
% Funkcja do aktualizacji wyjść
sys = x(1);
function sys = mdlGetTimeOfNextVarHit(t,x,u)
% Funkcja obliczająca czas następnego próbkowania bloku
% Uwaga! Jest to czas bezwzględny, a nie przyrost czasu
sys = t + u(2);
Wersja 2 interfejsu s-funkcji
aObecnie istnieje druga wersja interfejsu dla
s-funkcji pisanych w języku Matlaba
aWersja ta nie posiada ograniczeń wersji
pierwszej, jak jedno wejście i jedno wyjście
bloku
aZapis kodu s-funkcji w wersji 2 interfejsu jest
bardzo podobny do kodu w języku C
aKod s-funkcji w wersji 2 interfejsu traci
podstawową zaletę s-funkcji w wersji pierwszej prostotę
S-funkcje w języku C
aNajprostszym sposobem zapisania kodu
s-funkcji w języku C jest wykorzystanie
dostarczanego z Simulinkiem "kreatora" sfunkcji
aS-funkcję można także zapisać samodzielnie i
skompilować przy użyciu dostarczonego skryptu
"mex"
aTrzecim sposobem jest wykorzystanie narzędzi
do dziedziczenia kodu
aKażdy ze sposobów wymaga posiadania
zewnętrznych kompilatorów
S-funkcje w języku C
a Interakcja "silnika" Simulinka z s-funkcją napisaną w
języku C polega na wywoływaniu odpowiednich funkcji
na każdym z etapów symulacji
a Funkcje te nazywa się metodami – odpowiadają one
funkcjom pomocniczym dla poszczególnych faz symulacji
a Liczba metod, za pomocą których można sterować
przebiegiem wykonania bloku, jest znacznie większa od
faz symulacji s-funkcji w języku Matlaba
a Każda s-funkcja musi co najmniej implementować
metody dla fazy inicjalizacji rozmiarów, inicjalizacji
czasów próbkowania, obliczania wyjść i zakończenia
symulacji
Wywołania metod dla języka C
„Silnik” Simulinka
mdlInitializeSizes
mdlCheckParameters
mdlSetInputPortFrameData
mdlSetInputPortWidth/mdlSetOutputPortWidth
mdlSetInputPortDimensionInfo/mdlSetOutputPortDimensionInfo
mdlSetInputPortSampleTime/mdlSetOutputPortSampleTime
mdlInitializeSampleTime
Wywołania metod dla języka C
„Silnik” Simulinka
mdlSetInputPortDataType/mdlSetOutputPortDataType
mdlSetDefaultPortDataTypes
mdlSetInputPortComplexSignal/mdlSetOutputPortComplexSignal
mdlSetDefaultPortComplexSignal
mdlSetWorkWidths
mdlStart
mdlCheckParameters
mdlProcessParameters
mdlInitializeConditions
mdlOutputs
Wywołania metod dla języka C
Duży krok symulacji
mdlProcessParameters
mdlGetTimeOfNextVarHit
mdlInitializeConditions
mdlOutputs
mdlUpdate
Wywołania metod dla języka C
Całkowanie
Mały krok symulacji
mdlCheckParameters
mdlDerivatives
mdlOutputs
mdlDerivatives
mdlOutputs
Detekcja
przejść przez 0
mdlZeroCrossings
mdlTerminate
Struktura SimStruct
a Wszystkie metody przyjmują jako parametr wskaźnik do
struktury typu SimStruct
a Struktura ta przechowuje informacje konfiguracyjne bloku
a Struktura SimStruct jest zdefiniowana w pliku
nagłówkowym "simstruct.h" znajdującym się w
podkatalogu simulink/include głównego katalogu Matlaba
a Plik "simstruct.h" zawiera także szereg makr do
manipulacji strukturą SimStruct – makra te należy
traktować jako interfejs struktury
a Obecnie struktura zawiera 17 składowych takich, jak
nazwa bloku, ścieżka do pliku bloku, stan błędu bloku czy
wskaźniki do podsystemów bloku
S-funkcje w języku C
#define S_FUNCTION_NAME nazwa_funkcji
#define S_FUNCTION_LEVEL 2
#include ”simstruc.h”
static void mdlInitializeSizes(SimStruct *S){}
static void mdlInitializeSampleTime(SimStruct *S){}
static void mdlOutputs(SimStruct *S, int_T tid){}
static void mdlTerminate(SimStruct *S){}
/* Kod dodatkowych funkcji */
#ifdef MATLAB_MEX_FILE
#include "simulink.c" /* interfejs dla plików MEX */
#else
#include "cg_sfun.h" /* Funkcje dla generacji kodu */
#endif
S-funkcja w języku C - przykład
#define S_FUNCTION_NAME cpomnoz
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
/* Definicja dla łatwiejszego dostępu do parametru s-funkcji */
#define PARAM1(S) ssGetSFcnParam(S,0)
/* Poniższe umożliwia wyłączenie kontroli poprawności zakresu parametru
- w tym celu należy zmienić na #undef */
#define MDL_CHECK_PARAMETERS
S-funkcja w języku C - przykład
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
static void mdlCheckParameters( SimStruct *S ){
/* Sprawdzenie, czy parametr jest skalarny (wzmocnienie) */
if (mxGetNumberOfElements(PARAM1(S)) != 1) {
ssSetErrorStatus(S,"Parametr tej S-funkcji ma być skalarem!");
return;
}
/* Wymuszenie dodatniego wzmocnienia */
if (mxGetPr(PARAM1(S))[0] < 0) {
ssSetErrorStatus(S, "Parametr tej S-funkcji ma być nieujemny!");
return;
}
}
#endif /* MDL_CHECK_PARAMETERS */
S-funkcja w języku C - przykład
static void mdlInitializeSizes( SimStruct *S ){
ssSetNumSFcnParams(S, 1);
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if( ssGetErrorStatus(S) != NULL ){
return; /* Simulink ostrzeże o problemie z parametrami */
}
} else {
return;
}
if ( !ssSetNumInputPorts(S, 1) ) return;
ssSetInputPortWidth( S, 0, DYNAMICALLY_SIZED );
ssSetInputPortDirectFeedThrough( S, 0, 1 );
S-funkcja w języku C - przykład
if (!ssSetNumOutputPorts(S,1)) return;
/*
/*
}
ssSetOutputPortWidth( S, 0, DYNAMICALLY_SIZED );
ssSetNumSampleTimes( S, 1);
Poniższą opcję włącza się po upewnieniu się, że kod nie generuje
sytuacji wyjątkowych w rozumieniu Matlaba */
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); */
static void mdlInitializeSampleTimes( SimStruct *S ){
ssSetSampleTime( S, 0, INHERITED_SAMPLE_TIME );
ssSetOffsetTime( S, 0, 0.0 );
}
S-funkcja w języku C - przykład
static void mdlOutputs(SimStruct *S, int_T tid){
int_T i;
real_T *mnoznik = mxGetPr( PARAM1(S) );
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T width = ssGetOutputPortWidth(S,0);
for (i=0; i<width; i++) {
*y++ = (*mnoznik) *(*uPtrs[i]);
}
}
static void mdlTerminate(SimStruct *S){}
S-funkcja w języku C - przykład
#ifdef MATLAB_MEX_FILE
#include “simulink.c”
#else
#include “cg_sfun.h”
#endif
Zmienne bloku Simulinka
Wejścia
zewnętrzne
(porty)
Blok
wejściowowyjściowy
Wyjścia
zewnętrzne
(porty)
Jednym z zadań metody mdlInitializeSizes jest ustawienie
Blokzmiennych
funkcyjny wykorzystywanych
rozmiarów
wszystkich
Masa
w Simulinku
przed dany
blok Simulinka
Stany
Parametry
Dodatkowe
zmienne
wektorowe
Tworzenie wejść bloku
Składnia:
boolean_T ssSetNumInputPorts( SimStruct *S, int_T ile);
gdzie:
S
ile
- wskaźnik do struktury SimSizes,
- żądana liczba wejść bloku.
Zazwyczaj:
if( !ssSetNumInputPorts(S, ile) ) return;
Wejścia bloku
a Po zdefiniowaniu liczby wejść dla każdego wejścia
należy określić:
`wymiar wejścia lub dynamiczny dobór wymiaru,
`właściwość „rozciągania” wielkości skalarnych (ssSetOption),
`czy port uczestniczy bezpośrednio w obliczaniu wyjścia lub
następnego okresu próbkowania za pomocą makra
ssSetInputPortDirectFeedThrough
`typ danych portu, jeśli jest to inny typ niż domyślny (double)
`typ numeryki portu, jeśli port ma przyjmować dane zespolone
Wejścia bloku - rozmiar
a Dla portu jednowymiarowego o długości Dlugosc:
void ssSetInputPortVectorDimension( SimStruct *S,
int_T NumPortu, int_T Dlugosc);
(lub ssSetInputPortWidth())
a Dla portu, którego sygnałem jest macierz o rozm. M x N:
void ssSetInputPortMatrixDimensions( SimStruct *S,
int_T NumPortu, int_T M, int_T N );
a W innym przypadku:
void ssSetInputPortDimensionInfo( SimStruct *S,
int_T NumPortu, DimsInfo_T *dimsI );
Wejścia bloku - rozmiar
dynamiczny
a Używany w przypadku, gdy rozmiar portu ma zależeć od
rozmiaru sygnału podłączonego do portu
a Można wyróżnić trzy przypadki:
`jeśli port ma akceptować sygnały dowolnych rozmiarów, należy użyć
funkcji: ssSetInputPortDimensionInfo( S, NumPortu,
DYNAMIC_DIMENSION);
`jeśli port ma akceptować jedynie wektory, lecz o dowolnym
rozmiarze, należy użyć funkcji:
ssSetInputPortWidth( S, NumPortu, DYNAMICALLY_SIZED);
`jeśli port ma akceptować macierz, lecz ilość jej wierszy lub kolumn
ma być dowolna, należy użyć funkcji
ssSetInputPortMatrixDimensions( S, NumPortu, M, N ); gdzie
M i/lub N może być zapisane jako DYNAMICALLY_SIZED
Wejścia bloku - rozmiar
dynamiczny
aNależy zapewnić obsługę metody
mdlSetInputPortDimensionInfo(), która
dostosowuje rozmiar portu do sygnału do niego
dołączonego
aMożna także zapewnić obsługę metody
mdlSetDefaultPortDimensionInfo(), która
dobiera rozmiar portu, gdy nie można określić
rozmiaru dołączonego do niego sygnału (np.
niepodłączony port)
Wejścia bloku – właściwość
"rozszerzania"
aPrzykład: blok sumatora z jednym sygnałem
wektorowym a drugim skalarnym
aUłatwienie: ustawienie, w mdlInitializeSizes(), opcji
SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION
aS-funkcja musi definiować wejścia jako
DYNAMICALLY_SIZED
aS-funkcja nie może implementować żadnej z metod
mdlSetInputPortWidth(), mdlSetOutputPortWidth(),
mdlSetInputPortDimesionInfo(),
mdlSetOutputPortDimensionInfo(),
mdlSetDefaultPortDimensionInfo()
Wejścia bloku - bezpośredni
wpływ na wyjście
aFunkcja:
ssSetInputPortDirectFeedThrough(
SimStruct *S, int_T NumPortu, int_T Flaga);
umożliwia powiadomienie Simulinka, że sygnał z
danego portu jest wykorzystywany w metodach
mdlOutput lub mdlGetTimeOfNextVarHit (wartość
Flaga = 1)
Wejścia bloku - typ danych
aFunkcja:
ssSetInputPortDataType( SimStruct S,
int_T NumPortu, DTypeID Id );
umożliwia określenie innego niż domyślny typu
danych akceptowanych przez port
aOkreślenie typu jako DYNAMICALLY_TYPED
umożliwia dobór typu na podstawie typu sygnału
podłączonego do portu
aWówczas powinno się zdefiniować metody
mdlSetInputPortDataType() i mdlSetDefaultPortDataTypes()
Wejścia bloku - dane zespolone
aFunkcja:
ssSetInputPortComplexSignal( SimStruct S,
int_T NumPortu, CSignal_T sig );
umożliwia dobór numeryki portu (rzeczywista lub
zespolona)
aZmienna sig może przyjmować wartości:
COMPLEX_NO dla danych rzeczywistych,
COMPLEX_YES dla danych zespolonych i
COMPLEX_INHERITED dla typu danych zależnego
od typu sygnału podłączonego do portu
Wejścia bloku - dane zespolone
aW przypadku użycia typu COMPLEX_INHERITED
należy zdefiniować metody
mdlSetInputPortComplexSignal() i ewentualnie
mdlSetDefaultPortComplexSignal()
Dostęp do wejść przez wskaźniki
W ogólnym przypadku przestrzeń wejściowo-wyjściowa
bloku nie jest ciągła:
Dostęp do wejść przez wskaźniki
Składnia:
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs( S, portIndex);
gdzie:
portIndex
S
uPtrs
- numer wejścia układu, począwszy od 0,
- wskaźnik do struktury SimSizes,
- tablica wskaźników do sygnałów wejściowych.
Odwołanie do konkretnego elementu:
u1 = *uPtrs[ NrElementu ];
uPtrs to wskaźnik do tablicy!
Dostęp do wejść przez wskaźniki przykład
Wejście1
Wejście2
Blok
funkcyjny
Dostęp do elementów Wejścia1:
InputRealPtrsType wskWe1 = ssGetInputPortRealSignalPtrs( S, 0 );
Dostęp do elementów Wejścia2:
InputRealPtrsType wskWe2 = ssGetInputPortRealSignalPtrs( S, 1 );
Rozmiar tablicy Wejścia1:
int_T rozmWe1 = ssGetInputPortWidth( S, 0 );
Testowanie poprawności obsługi
wejść
aWiele błędów programowych dotyczących
obsługi wejść nie ujawnia się, gdy do wejścia
podłączony jest sygnał skalarny
aDlatego zaleca się testowanie poprawności
obsługi wejść według następującego schematu:
Dostęp do wejść przestrzeni ciągłej
Wymuszenie ciągłości przestrzeni wejściowej – w metodzie
mdlInitializeSizes():
void ssSetInputPortRequiredContiguous( S, portIndex, flaga )
gdzie:
portIndex
S
flaga
- numer wejścia układu, począwszy od 0,
- wskaźnik do struktury SimSizes,
- równa TRUE gdy wymagamy ciągłości.
Odwołanie do wejścia:
const real_T *u = ssGetInputPortRealSignal( S, portIndex);
Odwołanie do konkretnego elementu:
u1 = u[0];
Wymuszenie ciągłości wpływa na efektywność!
Tworzenie wyjść bloku
Składnia:
boolean_T ssSetNumOutputPorts( SimStruct *S, int_T ile);
gdzie:
S
ile
- wskaźnik do struktury SimSizes,
- żądana liczba wyjść bloku.
Zazwyczaj:
if( !ssSetNumOutputPorts(S, ile) ) return;
Wyjścia bloku
aPo określeniu liczby wyjść dla każdego portu
wyjściowego należy określić:
`wymiar wyjścia lub dynamiczny dobór wymiaru za
pomocą funkcji
ssSetOutputPortDimensionInfo(),
ssSetOutputPortMatrixDimensions(),
ssSetOutputPortVectorDimensions(),
ssSetOutputPortWidth();
`w przypadku wyjść z dynamicznym rozmiarem należy
zdefiniować metody
mdlSetOutputPortDimensionInfo() i ewentualnie
mdlSetDefaultPortDimensionInfo()
Wyjścia bloku
`typ danych portu za pomocą funkcji
ssSetOutputPortDataType();
`jeśli typ jest zależny od połączeń bloku
(DYNAMICALLY_TYPED), powinno się zdefiniować
metodę mdlSetOutputPortDataType() i ewentualnie
mdlSetDefaultPortDataTypes()
`typ numeryki portu za pomocą funkcji
ssSetOutputPortComplexSignal();
`jeśli typ jest zależny od połączeń bloku
(COMPLEX_INHERITED), to należy zdefiniować
metodę mdlSetOutputPortComplexSignal() i
ewentualnie mdlSetDefaultPortComplexSignal()
Dostęp do wyjść przez wskaźniki
Składnia:
real_T *y = ssGetOutputPortRealSignal( S, portIndex);
gdzie:
portIndex
S
y
- numer wyjścia układu, począwszy od 0,
- wskaźnik do struktury SimSizes,
- wskaźnik do sygnałów wyjściowych.
Odwołanie do konkretnego elementu:
y[0] = 0;
Zazwyczaj rozmiar portu wyjściowego jest powiązany z rozmiarem
portu wejściowego
Istnieją także makra ssGetOutputPortNumDimensions() i
ssGetOutputPortDimensions() umożliwiające odczyt rozmiaru wyjścia
Przykład kodu
Poniższy kod przepisuje dane z portu wejściowego o numerze NrPortuWe
do portu wyjściowego o numerze NrPortuWy
int_T PozWektora;
int_T RozmiarWe = ssGetInputPortWidth( S, NrPortuWe );
InputRealPtrsType WskWe = ssGetInputPortRealSignalPtrs( S, NrPortuWe );
real_T *y = ssGetOutputPortSignal( S, NrPortuWy );
for ( PozWektora=0; PozWektora < RozmiarWe; PozWektora++ ) {
y[ PozWektora ] = *WskWe[ PozWektora ];
}
Tworzenie stanów bloku
Składnia:
ssSetNumContStates( SimStruct *S, int_T ile);
ssSetNumDiscStates( SimStruct *S, int_T ile);
gdzie:
S
ile
- wskaźnik do struktury SimSizes,
- żądana liczba stanów bloku
(lub DYNAMICALLY_SIZED)
Jeżeli w metodzie mdlInitializeSizes() użyto
DYNAMICALLY_SIZED, makra należy jeszcze raz wywołać w
metodzie mdlSetWorkWidths()!
Inicjalizacja stanów bloku
W metodzie mdlInitializeConditions() lub mdlStart():
real_T *x0 = ssGetContStates(S);
int_T lx = ssGetNumContStates(S);
for(; lx > 0; lx--){
*x0++ = 0.0;
}
Jest to jedyne miejsce, gdzie stany zmienia się "ręcznie"!
Uaktualnianie stanów bloku
a Stany ciągłe uaktualnia się w metodzie mdlDerivatives()
a Programista powinien obliczyć wektor pochodnych (na
podstawie prawej strony równania stanu) i zapisać w
miejscu wskazywanym przez wynik wywołania:
real_T *dx = ssGetdX(S);
a Stany dyskretne uaktualnia się w metodzie mdlUpdate()
a Dostęp do bieżącego wektora stanów dyskretnym można
uzyskać dzięki wywołaniu:
real_T *x = ssGetDiscStates(S);
a Zazwyczaj aktualizacja stanów wymaga alokacji
dodatkowego wektora buforowego
Parametry okna dialogowego
a Należy zdecydować, w jakim porządku parametry będą
wpisywane
a W metodzie mdlInitializeSizes() należy wywołać makro
ssSetNumSFcnParams() w celu ustawienia ilości
obsługiwanych parametrów
a Ustawioną liczbę parametrów zwraca makro:
int_T ssGetNumSFcnParams(SimStruct* S);
a Aktualną liczbę parametrów zwraca makro:
int_T ssGetNumSFcnParamsCount(SimStruct *S);
a Jeżeli programuje się metodę mdlCheckParams(), należy
ją wywołać z metody mdlInitializeSizes()
a Dostęp do parametrów następuje przez makro const
mxArray *ssGetSFcnParams( S, index )
Parametry okna dialogowego
a Tablica Matlaba mxArray może przechowywać dana
różnych typów
a W celu sprawdzenia typu danych w tablicy należy
wywołać:
DTypeID ssGetDTypeIdFromMxArray(const mxArray *m);
a Inne funkcje przydatne do sprawdzania typu to
mxIsComplex(), mxIsChar(), mxIsStruct()
Parametry okna dialogowego
Wartość
Typ danych Matlaba
Typ danych C
SS_DOUBLE
mxDOUBLE_CLASS
real_T
SS_SINGLE
mxSINGLE_CLASS
real32_T
SS_INT8
mxINT8_CLASS
int8_T
SS_UINT8
mxUINT8_CLASS
uint8_T
SS_INT16
mxINT16_CLASS
int16_T
SS_UINT16
mxUINT16_CLASS
uint16_T
SS_INT32
mxINT32_CLASS
int32_T
SS_UINT32
mxUINT32_CLASS
uint32_T
SS_BOOLEAN
mxUINT8_CLASS
boolean_T
INVALID_DTYPE_ID
Parametry dostrajalne...
...to parametry, które można zmieniać podczas symulacji
Dla każdego z parametrów dostrajalnych należy wywołać makro
void ssSetFcnParamTunable( SimStruct *S, int_T Param, int_T Mode )
gdzie:
S
Param
Mode
- wskaźnik do struktury SimSizes,
- numer parametru,
- SS_PRM_TUNABLE (dostrajalny),
SS_PRM_NOT_TUNABLE (nie dostrajalny),
SS_PRM_SIM_ONLY_TUNABLE (dostrajalny w symul.)
Uwaga! Po wykryciu zmiany parametru Simulink wywoła
metodę mdlCheckParameters()
Metoda mdlProcessParameters() pozwoli przeprowadzić
konieczne po dostrojeniu parametru zmiany
Parametry wykonania (run-time)
a Stanowią wewnętrzną reprezentację parametrów
dialogowych
a Każdy parametr wykonania może odpowiadać jednemu
lub większej liczbie parametrów dialogowych
a Parametry wykonania mogą się różnić od parametrów
dialogowych nie tylko wartością, ale i typem
a Typowe zastosowania:
`przeliczania, potencjalnie wielu, parametrów dialogowych na
jeden parametr konfiguracyjny
`konwersji skali bądź typu
`w połączeniu z RTW
Parametry wykonania (run-time)
Aby zarejestrować parametr wykonania dla każdego parametru
dialogowego, należy w metodzie mdlSetWorkWidths wykonać:
void ssRegAllTunableParamsAsRunTimeParams( S, names)
gdzie:
S
names
- wskaźnik do struktury SimSizes,
- wskaźnik do tablicy nazw parametrów dla
RTW (const char_T *names[])
Simulink rozróżnia jedynie pierwsze cztery znaki
każdej nazwy (muszą być niepowtarzalne)!
Parametry wykonania (run-time)
Aby zarejestrować pojedynczy parametr wykonania lub kilka takich
parametrów należy, w metodzie mdlSetWorkWidths() wykonać:
ssSetNumRunTimeParams( SimStruct *S, int_T num)
gdzie:
S
num
- wskaźnik do struktury SimSizes,
- liczba parametrów wykonania
Dalsze czynności zależą od typu relacji pomiędzy parametrem
wykonania a parametrem dialogowym
Parametry wykonania (run-time)
Aby powiązać parametr wykonania z jednym i tylko jednym
parametrem dialogowym, należy wykonać:
void ssRegDlgParamAsRunTimeParam(SimStruct *S, int_T nrD,
int_T nrW, const char_T *nazwa, DTypeId nazwaT);
gdzie:
S
– wskaźnik do struktury SimStruct bloku,
nrD
– numer parametru dialogowego
nrW
– numer parametru wykonania
nazwa
– symboliczna nazwa parametru wykonania
nazwaT – nazwa typu parametru wykonania
Parametry mogą różnić się typem!
Parametry wykonania (run-time)
Aby powiązać parametr wykonania z kilkoma parametrami
dialogowymi, należy wykonać:
void ssSetRunTimeParamInfo(SimStruct *S, int_T nrW,
ssParamRec *info);
gdzie:
S
nrW
info
– wskaźnik do struktury SimStruct bloku,
– numer parametru wykonania
– struktura określająca powiązania pomiędzy parametrami
Parametry mogą różnić się typem!
Parametry wykonania (run-time)
Aby uaktualnić parametry wykonania należy, w metodzie
mdlProcessParametres(), wywołać jedną z funkcji:
ssUpdateAllTunableParamsAsRunTimeParams(SimStruct *S)
dla uaktualnienia wszystkich parametrów,
ssUpdateRunTimeParamData( SimStruct *S, int_T nrW, void *dana);
dla uaktualnienia pojedynczego parametru, utworzonego przez
wywołanie ssRegDlgParamAsRunTimeParam() ("dana" to
wskaźnik do nowej wartości parametru),
ssUpdateDlgParamAsRunTimeParam(SimStruct *S, int_T nrW)
dla uaktualnienia parametru utworzonego przez wywołanie
ssSetRunTimeParamInfo().
Parametry wykonania (run-time)
Aby posłużyć się wartością parametru wykonania należy wywołać
makro:
ssParamRec * ssGetRunTimeParamInfo(SimStruct *S, int_T nrW);
którego drugim argumentem jest numer żądanego
parametru wykonania.
Zwrócona przez makro struktura posiada pole "data", którego jest
wskaźnikiem do żądanej wartości parametru wykonania. Wskaźnik
ten należy wcześniej skonwertować do żądanego typu, np.:
real_T par1 = *( (real_T *)((ssGetRunTimeParamInfo(S,0)->data));
Czasy próbkowania
a Czas próbkowania ustalany jest zazwyczaj w metodzie
mdlInitializeSizes, najczęściej jako jeden dla całego bloku
funkcyjnego (tzw. block-based sample times)
a W takim rozwiązaniu należy wywołać funkcję
void ssSetNumSampleTimes( SimStruct *S,
int_T Num);
gdzie:
S
- wskaźnik do struktury SimStruct bloku
Num - ilość czasów próbkowania, > 0
a Simulink wywoła wtedy metodę mdlInitializeSampleTimes,
w której czasy próbkowania powinny zostać ustawione
Czasy próbkowania
a Czasy próbkowania wpisuje się jako pary
[czas_próbkowania, offset] za pomocą funkcji:
ssSetSampleTime( SimStruct *S, st_Index, time_T Czas );
ssSetOffsetTime( SimStruct *S, st_Index, time_T Offset );
gdzie:
S
- wskaźnik do struktury SimStruct bloku,
st_Index
- indeks pary, dla której dokonuje się
zmian, począwszy od 0,
Czas, Offset - czas i offset dla danej pary
Czasy próbkowania
a Możliwe są następujące kombinacje:
`[CONTINUOUS_SAMPLE_TIME, 0.0] - funkcja ciągła, która
dokonuje zmian w małych krokach symulacji,
`[CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
- funkcja ciągła, która nie dokonuje zmian wyjść w małym kroku
symulacji,
`[Tp, To ] - funkcja dyskretna, z określonym okresem próbkowania,
Tp > 0 oraz 0 ≤ To ≤ Tp,
`[VARIABLE_SAMPLE_TIME, 0.0] - funkcja, której czas próbkowania
jest zmienny; funkcja taka powinna zdefiniować metodę
mdlGetTimeOfNexVarHit(),
Czasy próbkowania
`[INHERITED_SAMPLE_TIME, 0.0] - funkcja, której czas
próbkowania zależy od sygnału podłączonego do bloku
`[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] funkcja, której czas próbkowania zależy od sygnału podłączonego
do bloku, lecz funkcja nie zmienia swoich wartości wyjściowych
podczas małego kroku symulacji
a Dla sprawdzenia, czy bieżąca chwila jest chwilą
dyskretnego próbkowania, bądź chwilą pracy małej pętli
symulacji, należy wykorzystać makra:
ssIsSampleHit( SimStructure *S, st_Index, tid );
ssIsContinuousTask( SimStructure *S, st_Index, tid );
Czasy próbkowania
a Można także używać innych czasów próbkowania dla
różnych portów - wówczas należy w metodzie
mdlInitializeSizes() wykonać funkcję
ssSetNumSampleTimes(S, PORT_BASED_SAMPLE_TIMES),
a następnie:
ssSetInputPortSampleTime( S, NumPortu, Czas1 );
ssSetInputPortOffsetTime( S, NumPortu, Offset1 );
ssSetOutputPortSampleTime( S, NumPortu, Czas2 );
ssSetOutputPortOffsetTime( S, NumPortu, Offset2 );
a S-funkcja wykorzystująca indywidualne czasy próbkowania
nie powinna wywoływać makr ssSetSampleTime() i
ssSetOffsetTime()
a Możliwe jest także dziedziczenie czasu próbkowania, bądź
użycie "stałego" czasu próbkowania dla portu
Czasy próbkowania
a S-funkcja, która posiada choć jeden port, który dziedziczy
czas próbkowania, powinna zdefiniować metody
mdlSetInputPortSampleTime() – jeżeli jest to port
wejściowy, bądź mdlSetOutputPortSampleTime() – jeżeli
jest to port wyjściowy, bądź obydwie te metody
a Metody te mogą posłużyć się makrami:
real_T ssGetInputPortSampleTime(SimStruct *S, int_T nr)
real_T ssGetInputPortOffsetTime (SimStruct *S, int_T nr)
real_T ssGetOutputPortSampleTime(SimStruct *S, int_T nr)
real_T ssGetOutputPortOffsetTime(SimStruct *S, int_T nr)
w celu odczytania aktualnego czasu próbkowania
Czasy próbkowania
a Przez "stały" czas próbkowania (ang. constant sample
time) dokumentacja Simulinka rozumie sytuację, gdy
wartość wejścia lub wyjścia s-funkcji nie ulega zmianie w
trakcie symulacji
a Aby zezwolić na wykorzystanie "stałego" czasu
próbkowania, należy w metodzie mdlInitializeSizes()
ustawić opcję
SS_OPTION_ALLOW_CONSTANT_PORT_SAMPLE_TIME
a "Stały" czas próbkowania uzyskuje się przez ustawienie
czasu próbkowania portu równego nieskończoności, i
offsetu równego zero, np.:
ssSetInputPortSampleTime(S, 0, mxGetInf() );
ssSetInputPortOffsetTime(S, 0, 0);
Czasy próbkowania
a Jeżeli portem, dla którego ustala się "stały" czas
próbkowania, jest port wyjściowy, należy w metodzie
mdlOutput() sprawdzać, czy drugi parametr jej wywołania
jest równy symbolicznej wartości CONSTANT_TID
a Jeżeli tak jest, należy ustawić wartość portu o "stałym"
czasie próbkowania
Czasy próbkowania
a Funkcja o zmiennym czasie próbkowania powinna
implementować metodę mdlGetTimeOfNextVarHit()
a W metodzie tej należy wywołać makro:
ssSetTNext(SimStruct *S, time_T tnext);
a Drugim parametrem powyższego makra jest bezwzględny
czas symulacji, w którym powinno nastąpić następne
przetwarzanie metod bloku
a Bieżącą chwilę symulacji można określić za pomocą makra:
time_T ssGetT(SimStruct *S)
a Jeżeli metoda mdlGetTimeOfNextVarHit() korzysta z
wartości któregoś z wejść, należy dla tego wejścia ustawić
cechę bezpośredniego przejścia sygnału przez blok
Wektory robocze
aZmienne, których wartości mają być
przechowywane pomiędzy kolejnymi
wywołaniami, można zadeklarować jako statyczne
zmienne języka C
aWadą takiego rozwiązania jest nadpisywanie tych
zmiennych przez kolejne instancje danego bloku
aWektory robocze Simulinka są pozbawione tej
wady - mogą one przechowywać zmienne typu
integer, real, wskaźnik i typu ogólnego
Wektory robocze
a Do ustawiania rozmiarów wektorów roboczych służą
następujące makra, wywoływane w
mdlInitializeSizes():
`ssSetNumContStates - rozmiar wektorów stanów ciągłych
`ssSetNumDiscStates - rozmiar wektorów stanów dyskretnych
`ssSetNumDWork - rozmiar wektorów roboczych typu ogólnego
`ssSetNumRWork - rozmiar wektorów roboczych typu real
`ssSetNumIWork - rozmiar wektorów roboczych typu integer
`ssSetNumPWork - rozmiar wektorów roboczych typu
wskaźnikowego
`ssSetNumModes - rozmiar wektora trybów
`ssSetNumNonsampledZCs - ilość stanów, dla których
wyznaczane są chwile przejść przez zero
Wektory robocze
a Makra te są dwuparametrowe – drugim parametrem jest
żądana liczba wektorów roboczych danego typu
a Drugi parametr może przyjmować następujące wielkości:
`zero (domyślna wielkość),
`wartość całkowita dodatnia,
`DYNAMICALLY_SIZED dla wielkości zależnej od wielkości wyjścia
bloku (dobór automatyczny).
a Można zdefiniować metodę mdlSetWorkWidths, w
której dobiera się rozmiary wektorów roboczych, a w
której znane są już rozmiary wejść i wyjść
Wektory robocze
a W przypadku wektorów typu ogólnego należy dookreślić typ
i rodzaj każdego pola wektora przez wywołania:
void ssSetDWorkDataType(SimStruct *S, int_T pos, DTypeID typ);
void ssSetDWorkWidth(SimStruct *S, int_T pos, int_T rozm)
void ssSetDWorkComplexSignal(SimStruct *S, int_T pos,
Csignal_T flaga);
gdzie flata = COMPLEX_YES lub COMPLEX_NO
Wektory robocze
a W dowolnej z metod możliwe jest odczytanie rozmiaru
każdego z wektorów roboczych przez wywołania makr:
int_T
int_T
int_T
int_T
ssGetNumDWork(SimStruct *S);
ssGetNumIWork(SimStruct *S);
ssGetNumPWork(SimStruct *S);
ssGetNumRWork(SimStruct *S);
a Dostęp do wektorów zmiennych roboczych uzyskuje się za pomocą
wywołań:
void* ssGetDWork(SimStruct *S, int_T pozycja);
int_T* ssGetIWork(SimStruct *S);
void** ssGetPWork(SimStruct *S);
real_T* ssGetRWork(SimStruct *S);
Raportowanie błędów
a Do zgłaszania Simulinkowi błędów, np. błędnie wpisanych
parametrów, używa się funkcji:
void ssSetErrorStatus( SimStruct *S, const char_T *msg );
gdzie:
S
- wskaźnik do struktury SimStruct,
msg - wskaźnik do łańcucha z opisem błędu.
a Łańcuch z opisem błędu powinien być na stałe w pamięci
(static), nie może to być zmienna lokalna!
a Do wyświetlenia okna dialogowego z ostrzeżeniem służy
makro:
void ssWarning( SimStruct *S, const char_T *msg );
Raportowanie błędów
a Do wyświetlenia dowolnego komunikatu w głównym oknie
Matlaba służy makro:
int_T ssPrintf(const char_T *msg, ... );
a Wartością zwracaną jest liczba poprawnie wyświetlonych
bajtów, lub zero w przypadku błędu
Raportowanie błędów
a Wyjątki w dokumentacji Simulinka są rozumiane jako
"długie skoki" (ang. long jumps)
a Wystąpienie tak rozumianych wyjątków jest możliwe przy
wywołaniu wszystkich funkcji, których nazwy rozpoczynają
się od "mex"
a Również funkcje o nazwach rozpoczynających się od "mx"
nie dają gwarancji braku wyjątków
a Nigdy nie powodują wystąpienia wyjątku te z funkcji o
nazwach rozpoczynających się od "mx", które zwracają
wskaźnik lub rozmiar, np. mxGetPr(), mxGetData(),
msGetM(), mxGetNumberOfElements()
a Obsługa wyjątków pociąga za sobą niebagatelny koszt
czasowy
Raportowanie błędów
a W celu wyłączenia obsługi błędów można wykorzystać
makro:
ssSetOptions( S, SS_OPTION_EXCEPTION_FREE_CODE )
a W przypadku błędów funkcja z włączoną powyższą opcją
może spowodować niezdefiniowane zachowanie Simulinka
(zwykle zawieszenie)
a Opcja SS_OPTION_RUNTIME_EXCEPTION_FREE_CODE
sygnalizuje, że wyjątków nie powoduje kod najczęściej
wykonywanych metod mdlGetTimeOfNextVarHit(),
mdlUpdate(), mdlDerivatives() i mdlOutputs()
S-funkcje w C++
a Mechanizm Simulinka zakłada sposób wywołań s-funkcji
właściwy dla języka C
a W celu wykorzystania w Simulinku kodu z języka C++
należy wszystkie wywołania metod Simulinka objąć
dyrektywą <extern ”C”>:
#ifdef __cplusplus
extern ”C”{
#endif
...
#ifdef __cplusplus
}
#endif
S-funkcje w C++
Przechowywanie obiektów języka C++ pomiędzy
wywołaniami metod Simulinka wymaga wykorzystania
wskaźnikowych wektorów roboczych. Sposób postępowania
jest następujący:
1. static void mdlInitializeSizes( SimStruct *S ){
...
ssSetNumPWork( S, 1 ); //Utworzenie wektora wsk.
}
2. static void mdlStart( SimStruct *S, int_T tid ){
ssGetPWork(S)[ 0 ] = (void *) new Obiekt;
}
S-funkcje w C++
3.
4.
Aby odwołać się do obiektu, należy odczytać wskaźnik:
void mdlOutputs( SimStruc *S, int_T tid){
Obiekt *Ob1 = (Obiekt *) ssGetPWork(S)[ 0 ];
real_T *y = ssGetOutputPortRealSignal( S, 0 );
y[ 0 ] = Obiekt->wyjscie();
}
Na koniec symulacji należy obiekt skasować:
void mdlTerminate( SimStruct *S ){
Obiekt *Ob1 = (Obiekt *) ssGetPWork(S)[ 0 ];
delete Ob1;
}
S-funkcje w C++
Przykład: wzmacniacz w języku C++