S-Function

Una S-Function è una descrizione, mediante linguaggio di programmazione, di un sistema dinamico, il cui codice può essere scritto utilizzando i linguaggi Matlab o C. Le S-Function scritte con quest’ultimo linguaggio sono compilate come MEX-file (Memory Executable) utilizzando il comando mex disponibile all'interno dell'ambiente Matlab.

Il codice generato è contenuto in una libreria dinamica o DLL (Dynamic Link Library), che viene invocata dal Matlab in modo dinamico, ovvero il Matlab, nel momento in cui il Simulink ne richiede l'uso, cerca la DLL nel path del Matlab e ci si collega utilizzando le funzioni che la DLL esporta. Una volta effettuato, il collegamento diventa stabile (la DLL risulta in permanente utilizzazione da parte del Matlab) per cui la DLL non è più cancellabile (da Explorer) o modificabile (da un programma esterno a Matlab) finché dal Matlab non viene eseguita l'istruzione "clear mex" o viene ricompilata di nuovo tramite l'istruzione "mex".

La forma di una S-Function è molto generale e può descrivere sistemi continui, discreti e ibridi. Quasi tutti i modelli Simulink possono essere, quindi, realizzati come S-Function.

Le S-Function vengono inglobate nei modelli Simulink utilizzando il blocco S-Function nella sottolibreria Nonlinear Block (Matlab 5.x) o User-Defined Blocks (Matlab 6.x). Accedendo alla finestra di dialogo del blocco è possibile specificare il nome della S-Function da utilizzare.

Si tenga presente che qualora esistano due S-Function, di cui una scritta come C MEX-file e l’altra come M-file (Matlab file), sarà la prima ad essere utilizzata. Si possono utilizzare le finestre di Simulink e le relative istruzioni per inserire documentazioni inerenti al blocco, inserire parametri additivi ecc.

Quando utilizzare una S-Function

L’utilizzo più comune di una S-Function è come blocco utente di Simulink.

E’ possibile utilizzarle in svariate applicazioni, tra cui:

  • Aggiungere blocchi di utilità generale a Simulink;
  • Inserire codice C già esistente in una simulazione;
  • Descrivere un sistema come una sequenza di istruzioni;
  • Utilizzare animazioni grafiche;
  • Velocizzare notevolmente la simulazione integrando codice C;

Una stessa S-function può essere utilizzata in più punti della stessa simulazione, modificando solamente i parametri con cui la S-function viene invocat

Caratteristiche generali delle S-function

Ciascun blocco interno ad un modello Simulink possiede le seguenti caratteristiche generali: un vettore degli ingressi u, un vettore delle uscite y, ed un vettore degli stati x, come nella seguente figura:

Il vettore degli stati può contenere stati continui, discreti, o una combinazione di essi. La relazione matematica tra ingressi, uscite, e stati sono espresse dalle seguenti equazioni:

Uscite: y = f0(t,x ,u)

Derivata: dx/dt = fd(t,x,u)

Aggiornamento: xd(k+1) = fu (t, x, u) d

ove x = [xc , xd].

Il vettore degli stati viene quindi suddiviso in due parti: una contenete gli stati continui e l’altra i discreti. Gli stati continui occupano la prima parte del vettore, e i discreti la seconda.

Per blocchi privi di stati x è un vettore vuoto. Nei MEX-file delle S-Function, ci sono due vettori separati per gli stati continui e discreti.

Stadi di simulazione e routine di S-function

Durante l’esecuzione di un modello Simulink effettua chiamate successive ai singoli blocchi, e quindi anche alle S-Function, onde effettuare su essi specifiche operazioni relative allo stadio attuale in cui si trova la computazione, es. inizializzazione del blocco, definizione dei parametri, calcolo delle uscite e così via. La successione di questi stadi può essere visualizzata con il presente diagramma di flusso:

Figura 2: Come Simulink esegue la simulazione

 

L’esecuzione di questi passi è realizzata mediante routine che eseguono il loro compito durante ciascun passo.Queste azioni si possono classificare come:

  • Inizializzazione – Durante questo stadio Simulink:
  • Inizializza SimStruct, una struttura che contiene informazioni sulla S-Function;
  • Definisce numero e dimensione delle porte di ingresso e di uscita;
  • Definisce gli istanti di campionamento;
  • Predispone aree di memorizzazione e l’array size.
  • Calcolo del prossimo istante di campionamento – Se si sta utilizzando un passo variabile si effettua il calcolo del successivo istante di campionamento.
  • Calcolo dell’uscita nell’istante di campionamento principale – Al completamento di questa fase le uscite del blocco saranno disponibili nell’istante di tempo attuale.
  • Aggiornamento degli stati discreti nell’intervallo principale – Ciascun blocco esegue l’aggiornamento degli stati discreti per il prossimo istante.
  • Integrazione – Questa si applica a modelli con stati continui e/o attraversamenti dello zero non campionati. Se la S-Function lavora con stati continui il calcolo delle uscite e delle derivate della S-Function avviene nell’istante di tempo secondario. Se la S-Function ha attraversamenti dello zero non campionati, Simulink calcola le uscite e gli attraversamenti dello zero nell’istante secondario.

Introduzione alle C-Mex S-function

Le C-MEX S-Function sono realizzate come funzioni in linguaggio C. Esiste un set di routine per S-Function le quali vengono chiamate direttamente da Simulink per eseguire il loro compito e possono essere utilizzate nella scrittura delle S-Function.

Stadio della simulazione Routine S-Function
Inizializzazione mdlInitializeSizes
Calcolo istante camp. succ.(facoltativo ) mdlGetTimeOfNextVarHit
Calcolo uscite mdlOutputs
Aggiornam. stati discr. mdlUpdate
Calcolo derivate mdlDerivatives
Fine simulazione mdlTerminate

Le routine C-MEX S-Function devono contenere le funzioni definite nella tabella. La presenza di maschere predefinite (vedi sfuntmpl.c e sfuntmpl.doc, situate nella directory simulink/src) forniscono un adeguato sostegno alla scrittura del codice. Se ne consiglia l’uso al fine di limitare errori di scrittura.

Concetti di base

Una piena comprensione dei seguenti concetti aiuterà ad una corretta scrittura delle S-Function:

  • Direct feedthrough
  • Ampiezza ingresso impostata dinamicamente
  • Impostazione tempo di campionamento e offset
  • S-Function - Direct feedthrough

Con questo termine si definisce la caratteristica del nostro algoritmo di presentare un’influenza diretta degli ingressi sulle uscite. Una S-Function ha questa caratteristica se:

  • La funzione di uscita (mdlOutputs) è una funzione dell’ingresso u. C’è quindi un direct feedthrough quando le istruzioni interne a mdlOutputs utilizzano la variabile d’ingresso u. Per uscite s’intendono anche quelle grafiche (Scope, XY Graph, ecc.).
  • La S-Function ha un passo di campionamento (chiamata a mdlGetTimeOfNextVarHit) variabile dipendente da u.

Un esempio di algoritmo che non richiede direct feedthrough è il seguente:

Uscite: y = x
Derivata: dx/dt = u

dove x è lo stato, dx/dt è la derivata rispetto al tempo, u è l’ingresso e y l’uscita.

Impostazione dinamica dell’ampiezza dell’ingresso

Le S-Function possono essere realizzate in modo tale da prevedere un’impostazione dinamica dell’ampiezza dell’ingresso, determinata cioè a tempo di esecuzione e stabilita dall’ampiezza delle uscite dei blocchi collegati alla S-Function, essa può essere utilizzata per definire il numero di stati continui, discreti e il numero di uscite.

Le C-MEX S-Function consentono, quindi, di definire porte di accesso e di uscita multiple di forma vettoriale. Vedremo in seguito esempi in tal senso.

Impostazione del tempo di campionamento e dell’offset

Le C-MEX S-Function consentono un alto grado di flessibilità nello stabilire quando avviene l’esecuzione fornendo il seguente insieme di scelte:

  • Campionamento continuo: Per S-Function che hanno stati continui e/o zeri non campionati. In questo caso le uscite cambiano nell’istante di tempo secondario.
  • Tempo di campionamento continuo ma fisso nell’istante di tempo secondario: Per S-Function che necessitano di esecuzione ad ogni passo di simulazione principale, ma non cambia valore durante quello secondario.
  • Tempo di campionamento discreto: Per S-Function il cui comportamento è funzione di intervalli discreti, si può quindi definire un tempo in cui Simulink chiama il blocco ed anche un ritardo (offset) nel campionamento, es.:

TimeHit = (n * period) + offset

Dove n è un intero, e il suo primo valore è sempre zero.

  • Tempo di campionamento variabile: Tempo di campionamento la cui ampiezza viene definita all’inizio di ciascun passo di campionamento e può variare da passo a passo.
  • Tempo di campionamento ereditato: Quando la nostra S-Function non ha caratteristiche interne particolari, stati continui, discreti ecc., si può fare in modo che assuma il tempo di campionamento dei blocchi che in qualche modo ne influenzano il comportamento, ad es.:
    • Il blocco connesso all’ingresso
    • Il blocco di destinazione
    • Il tempo di campionamento più veloce nel sistema

     

Si potranno definire questi parametri utilizzando le routine di S-Function appropriate che vedremo in seguito.

Esempi di C-MEX S-Function possono essere trovati nella directory simulink/src, ne citiamo alcune: timestwo.c, csfunc.c, dsfunc.c, dlimint.c, vsfunc.c, mixed.c ed altre.

Guida alla scrittura delle S-function

Introduzione

La realizzazione di una S-Function mediante linguaggio C, come abbiamo già detto, deve sottostare ad una serie di regole che hanno come scopo la realizzazione di un C MEX-file (C Matlab Executable file) che interagisca con Simulink e con l’ODE solver per l’esecuzione dei singoli passi di calcolo che portano all’esecuzione del modello (definizione delle condizioni e delle caratteristiche iniziali del blocco, calcolo delle derivate, stati discreti e uscite del blocco).

Un utile strumento per la compilazione del codice è fornito da sfuntmpl.c, un file di codice C, e dal relativo file sfuntmpl.doc  entrambi presenti nella directory simulink/src.

Un semplice esempio di C-MEX S-Function è il seguente:

#define S_FUNCTION_NAME your_sfunction_name_here
#define S_FUNCTION_LEVEL 2
#include “simstruct.h”

static void mdlInitializeSizes(SimStrusct *S)
{

}


<aggiunta routine/codice di S-Function>


static void mdlTerminate(SimStruct *S)
{

}


#ifdef MATLAB­MEX_FILE /* Il file è stato compilato come MEX-file? */
#include “simulink.c” /* Interfaccia tra Simulink e MEX-file */
#else
#include “cg_sfun.h” /* Funzione per la registrazione della generazione del codice */
#endif

La routine mdlInitializeSizes è solo una, la prima in genere ad essere chiamata quando Simulink interagisce con la S-Function, delle tante routine del tipo mdl* che devono essere utilizzate nella compilazione del codice. Il contenuto delle suddette routine è stabilito dal programmatore e varia con il tipo di S_Function che viene implementata. L’ultima routine ad essere chiamata da Simulink è mdlTerminate che termina l’esecuzione del blocco.

Le informazioni relative alla S_Function sono contenute in una struttura dati chiamata SimStruct. L’inclusione nel codice, mediante l’istruzione #include, del file simstruc.h fornisce, oltre alla dichiarazione della struttura, anche un insieme di macro mediante le quali è possibile accedere, copiare e leggere dati dalla struttura.

Scrittura delle C-MEX S-function

Lo scopo di questo paragrafo è quello di fornire dei semplici esempi di S-Function, dove semplici significa contenenti quelle routine che devono essere sempre presenti anche se vuote, cioè prive di codice. Uno di questi esempi è quello della S_Function  timestwo  che prende un ingresso e lo moltiplica per due (vedi matlabroot/simulink/src/timestwo.c).

Il modello simulink che la rappresenta è il seguente:

Il blocco S_Function è stato prelevato dalla libreria Simulink ed è stato modificato il nome del blocco in timestwo accedendo alla finestra delle proprietà, non sono stati specificati altri parametri. Il codice che è stato scritto per la S_Function rispetta il seguente schema:

  E’ necessario quindi che le suddette routine siano inserite nel codice della S_Function, in quest’esempio sono state digitate all’interno di un file di nome timestwo.c, la successiva esecuzione del comando:

    mex   timestwo.c 

dal prompt di Matlab permette a quest’ultimo di eseguire la compilazione e il link di questo programma, creando un file caricabile ed eseguibile da Simulink. 

Ciò che ne risulta è un file di tipo MEX la cui estensione, se si lavora in ambiente Windows, è .dll. Il codice di quest’esempio è dato dal seguente listato:

#define S_FUNCTION_NAME timestwo

#define S_FUNCTION_LEVEL 2

#include "simstruc.h"
 
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) 
{
return; /* Parameter mismatch will be reported by Simulink */
}
 
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetInputPortDirectFeedThrough(S, 0, 1);
 
if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
 
ssSetNumSampleTimes(S, 1);
 
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}
 
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);

}
 
static void mdlOutputs(SimStruct *S, int_T tid) {
int_T i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T width = ssGetOutputPortWidth(S,0);
 
for (i=0; i<width; i++) {
*y++ = 2.0 *(*uPtrs[i]);
}
}
 
static void mdlTerminate(SimStruct *S)
{
}
 
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif
 

E’ utile a questo punto fornire ulteriori spiegazioni relative all’esempio fornito.

  • Istruzioni define ed include – Queste due istruzioni vengono utilizzate per informare Simulink che si intende realizzare una funzione di livello due (specificare sempre quest’opzione) ed il nome di questa, oltre a fornirgli il riferimento (il file header  simstruct.h) in cui trovare la struttura dati SimStruct utilizzata dal modello.
  • mdlInitialize specifica i  seguenti parametri:
    • Zero parametri – Significa che il campo S-function parameters nella finestra di dialogo della S_Function è vuota.
    • Una porta di ingresso e una di uscita – Le ampiezze delle porte di ingresso e uscita sono stabilite dinamicamente, Simulink moltiplicherà ciascuna linea d’ingresso (nel caso in cui tale porta sia un vettore n-dimensionale) per due e renderà tali valori disponibili in uscita.
    • Singolo istante di campionamento. E’ necessario specificare il tempo di campionamento nella routine mdlInitializeSampleTimes.
    • Il codice è exception free. Qualora il codice non preveda chiamate a funzioni esterne che interrompono l’esecuzione di Simulink è opportuno specificare questa opzione che contribuisce a rendere il codice più efficiente
  • mdlInitializeSampleTimes
    •  Il tempo di campionamento è acquisito dal blocco che precede la S_Function quindi funzionerà bene con qualsiasi blocco connesso in ingresso.
  • mdlOutput: 
    • Il calcolo numerico. mdlOutput dice a Simulink di moltiplicare il segnale d’ingresso per 2.0 e mettere il risultato nel segnale d’uscita.

     

Per accedere al segnale d’ingresso si utilizza:

InputRealPtrsType uptrs = ssGetInputPortRealSignaluPtrs(S,0)

dove uPtrs è un vettore di puntatori e si può accedere ai suoi elementi nel modo seguente:

Elementoi = *uPtrs[i]

Per accedere al segnale d’uscita si utilizza:

real_T *y = ssGetOutputPortRealSignal(S,0)

che fornisce un array contenente i valori in uscita.

  • mdlTerminate:

    • In questo caso particolare non c’è bisogno di eseguire nessuna azione conclusiva.
    • Al termine del codice è necessario inserire il codice che permette di connettere il blocco realizzato a Simulink o al Real-Time Workshop.

#ifdef  MATLAB_MEX_FILE

    #include “simulink.c”

#else

    #include “cg_sfun.h”

#endif