Simulink
C-Programm Simulink Interface
Da sich komplexere Berechnungen nur mühselig in Simulink darstellen lassen, kann man so genannte Mex-Funktionen einbauen. Diese können in Form eines m-Scriptes geschrieben werden - deutlich schneller rechnen jedoch C-Funktionen.
Einfache C-Funktionen können wiederum mit dem Mex-Function-Builder gebastelt werden, wenn man jedoch etwas weniger triviale Funktionalitäten benötigt, kommt man wohl nicht umhin, sich das Interface zwischen Simulink und der C-Funktion selbst zu schreiben. Wie das funktioniert ist natürlich in der Matlab-Hilfe erklärt *sigh* Vielleicht hilft zur Veranschaulichung aber dieses komplette Beispiel.
- Mexfile als .dll kompilieren:
mex C_Programm_Simulink_Interface.c C_Programm.c - Simulink starten
- Aus User-Defined Functions → S-Function ins Arbeitsfenster ziehen.
- Doppelklick auf das Symbol liefert die Function Block-Parameters. Hier den Namen C_Programm_Simulink_Interface in S-function name eintragen, in S-function parameters den Wert 10 für each und unter S-function modules alle Sourcecode-Datein, hier also "C_Programm". Anschließend sollte der Block 2 Ein- und 2 Ausgänge haben.
- Als Bezeichnung (unterhalb des Blockes) kann C_Programm eingegeben werden.
- Durch Klicken mit der rechten Maustaste auf den Block, kann man unter Mask S-Function die Ein- und Ausgänge benennen:
port_label("input",1,"Vin") port_label("input",2,"dt") port_label("output",1,"Vout") port_label("output",2,"error")
- In Simulation → Configuration Parameters Type auf Fixed-step setzen und in Fixed-step size dt eintragen.
- In Matlab dt=0.1 eingeben und Simulation starten.
�
C_Programm_Simulink_Interface.c
/* Mit dem Befehl mex C_Programm_Simulink_Interface.c C_Programm.c eingegeben in Matlab, lässt sich eine .dll erzeugen, die sich als Mex-Funktion in Matlab einbinden lässt. */ /* Hier MUSS unnuetzerweise der (Datei-) Name wiederholt werden. */ #define S_FUNCTION_NAME C_Programm_Simulink_Interface #define S_FUNCTION_LEVEL 2 #include "simstruc.h" #include "C_Programm_globals.h" FILE *out1; /* Zunaechst muessen alle Ein- und Ausgaenge initialisiert werden. Die S-Function kann diskrete oder kontinuierliche Zustaende haben, die hier nicht genutzt werden. Dieses Beispiel hat 2 Eingaenge, 1 Parameter und 2 Ausgaenge (siehe auch den Simulinkteil weiter unten). Die PortWidth gibt die Zahl der uebertragenen Werte an - statt einem Skalar koennte hier auch ein Vektor uebergeben werden. Fuer die Eingaenge MUSS jeweils noch "DirectFeedThrough" auf 1 gesetzt werden, damit die Werte an das C_Programm uebergeben werden. */ static void mdlInitializeSizes(SimStruct *S){ int i; const int Anz_Param = 1; const int Anz_Input = 2; const int Anz_Output = 2; /* Parameter Ports */ ssSetNumSFcnParams(S, Anz_Param); if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; } /* Input Ports */ if (!ssSetNumInputPorts(S, Anz_Input)) return; for (i=0;i/* Output Ports */ if (!ssSetNumOutputPorts(S, Anz_Output)) return; for (i=0;i /* weitere Default Einstellungen */ ssSetOptions(S, 0); } /* Die S-Function kann intern mit einem feineren Zeitraster als Simulink berechnet werden. Hier wird die Zeitschritt- weite des aufrufenden Modells verwendent (vererbt). */ static void mdlInitializeSampleTimes(SimStruct *S){ ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); } /* Dieses Beispiel hat einen inneren Zustand, der zu Beginn einmalig als Vektor eingelesen wird. (Da der Zustandsvektor global bekannt ist, eruebrigt sich eine explizite Uebergabe.) */ #define MDL_INITIALIZE_CONDITIONS static void mdlInitializeConditions(SimStruct *S){ ReadState(); out1=fopen("Zeitreihen.plot","w"); } /* Hier erfolgt der eigentliche Funktionsaufruf und die Rueckgabewerte werden den Ausgaengen zugewiesen. */ static void mdlOutputs(SimStruct *S, int_T tid){ /* Eingaenge */ InputRealPtrsType u1_in = ssGetInputPortRealSignalPtrs(S,0); InputRealPtrsType u2_in = ssGetInputPortRealSignalPtrs(S,1); /* Parameter */ double *p1_in = mxGetPr(ssGetSFcnParam(S,0)); /* Ausgaenge */ double *w_out = ssGetOutputPortRealSignal(S, 0); double *error = ssGetOutputPortRealSignal(S, 1); /* weitere Deklarationen */ int FehlerFlag; // Fehler-Rueckgabewert int each; // Intervall fuer Ausgabe double Vin; // Eingangswert double Vout; // Ausgangswert /* Handhabung der Pointer */ each = *p1_in; Vin = *u1_in[0]; dt = *u2_in[0]; /* Auch mit Simulink kann man Schleifenzaehler verwenden! */ Counter++; /* Der Programmaufruf - deswegen treiben wir den Umstand */ FehlerFlag = C_Programm( Vin, dt, &Vout ); if (FehlerFlag!=0){ *error = (double)FehlerFlag; if (FehlerFlag==1){ mexWarnMsgTxt("Eine Berechnung ist fehlgeschlagen!"); } } /* Ausgabe von Zeitreihen. Die Ausgaenge sind natuerlich in Simulink sichtbar, aber hier koennen z.B. bestimmte Werte des Zustandsvektors ausgegeben werden. */ if (each!=0){ if ((double)Counter/(double)each==(double)(Counter/each)){ fprintf(out1,"%6.3f | %6.3f %E n", (double)Counter*dt, v_old[1], v_old[N-1] ); } } /* Ausgangswerte als Pointer */ *w_out = v_old[N-1]; return; } /* Beim letzten Aufruf wird die Ausgabedatei geschlossen und der letzte Zustand auf Platte geschrieben. */ static void mdlTerminate(SimStruct *S){ fclose(out1); WriteState(); } #ifdef MATLAB_MEX_FILE #include "simulink.c" #else #include "cg_sfun.h" #endif
C_Programm.c
#include#include #include "C_Programm_globals.h" /* einmaliges Ansprechen der globalen Variablen */ volatile const int N=10; volatile int Counter, each; volatile double dt; volatile double v_old[10]; /* =================================== */ int C_Programm( double Vin, double dt, double *Vout ){ int i; int FehlerFlag; double v_new[10]; double kappa; kappa=0.01; FehlerFlag=0; if (dt==0.0){ FehlerFlag=1; }else{ v_new[0] = v_old[0]+kappa*(Vin-v_old[0])/dt; for (i=1; i Simulations are like miniskirts, they show a lot and hide the essentials. n Hubert Kirrman n"); #endif *Vout = v_old[N-1]; return FehlerFlag; } /* =================================== */ int ReadState(void){ int i; FILE *iop; char line[200], dummy1[20], dummy2[20]; iop = fopen("Zustand.ini","r"); for (i=0; i /* =================================== */ int WriteState(){ int i; FILE *iop; iop = fopen("Zustand.out","w"); for (i=0; i C_Programm_globals.h
/* Wird das folgende einkommentiert, erzeugt die C-Routine Testausgaben zum Debuggen. */ //#define debug /* Durch die Deklaration als "volatile extern" stehen diese Variablen allen C-Programmen (auch im Interface) zur Verfuegung. */ volatile extern const int N; volatile extern int Counter, each; volatile extern double dt; volatile extern double v_old[10]; /* Maske fuer die C_Funktion */ extern int C_Programm( double Vin, double dt, double *Vout );Zustand.ini
v00= 1.000000E+001 v01= 2.000000E+001 v02= 3.000000E+001 v03= 4.000000E+001 v04= 5.000000E+001 v05= 6.000000E+001 v06= 7.000000E+001 v07= 8.000000E+001 v08= 9.000000E+001 v09= 1.000000E+002plot_v.sh
Mit gnuplot lassen sich per Skript binnen kurzer Zeit eine Vielzahl an Grafiken erzeugen in diversen Grafikformaten - unter anderem Vektorgrafiken in eps oder pdf.
#!/bin/sh gnuplot << EOF set size 0.9,0.9 set xlabel "Time [s]" set ylabel "v [AU]" set key left top set logscale x 10 set xrange [1:2000] set ytics (0,500,1000) set mxtics 5 set grid xtics ytics mytics set style line 1 lt 2 pt 7 set term pdf set output "plot_v.pdf" plot "Zeitreihen.plot" u 1:3 ti "v" w points linestyle 1 EOF /usr/bin/xpdf "plot_v.pdf"