Все примеры основаны на шаблонах S-функций C MEX sfuntmpl_basic.c и sfuntmpl_doc.c. Открытый sfuntmpl_doc.c. для подробного обсуждения шаблона S-функции.
csfunc.c В примере показано, как моделировать непрерывную систему с состояниями, используя S-функцию C MEX. Следующая модель Simulink ® использует эту S-функцию.
При непрерывной интеграции состояний решатели Simulink интегрируют набор непрерывных состояний, используя следующие уравнения.

S-функции, содержащие непрерывные состояния, реализуют уравнение состояние-пространство. mdlOutputs способ содержит выходную часть и mdlDerivatives способ содержит производную от уравнения state-space. Чтобы визуализировать работу интеграции, см. блок-схему взаимодействия Simulink Engine с C S-функциями. Выходное уравнение соответствует mdlOutputs на основном временном шаге. Далее пример входит в раздел интеграции блок-схемы. Здесь механизм Simulink выполняет ряд второстепенных временных шагов, в течение которых он вызывает mdlOutputs и mdlDerivatives. Каждая из этих пар вызовов называется этапом интеграции. Интеграция возвращается с непрерывным обновлением состояний, и время моделирования продвигается вперед. Время перемещается вперед, насколько это возможно, при условии соблюдения допусков ошибок в состоянии. Максимальный шаг времени зависит от ограничений дискретных событий, таких как фактическое время остановки моделирования и ограничение, заданное пользователем.
csfunc.c В примере показано, что входной порт имеет прямой канал. Это потому, что матрица D инициализируется в ненулевую матрицу. Если D устанавливается равным нулевой матрице в представлении state-space, входной сигнал не используется в mdlOutputs. В этом случае прямой проход может быть установлен в 0, что указывает на то, что csfunc.c не требует входного сигнала при выполнении mdlOutputs.
S-функция csfunc.c начинается с #define операторы для имени и уровня S-функции и #include заявление для simstruc.h заголовок. После этих операторов S-функция может включать в себя или определять любые другие необходимые заголовки, данные и т.д. csfunc.c Пример определяет переменную U как указатель на сигнал первого входного порта и инициализирует статические переменные для матриц state-space.
/* File : csfunc.c
* Abstract:
*
* Example C S-function for defining a continuous system.
*
* x' = Ax + Bu
* y = Cx + Du
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-2013 The MathWorks, Inc.
*/
#define S_FUNCTION_NAME csfunc
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */
static real_T A[2][2]={ { -0.09, -0.01 } ,
{ 1 , 0 }
};
static real_T B[2][2]={ { 1 , -7 } ,
{ 0 , -2 }
};
static real_T C[2][2]={ { 0 , 2 } ,
{ 1 , -5 }
};
static real_T D[2][2]={ { -3 , 0 } ,
{ 1 , 0 }
};
Требуемый метод S-функции mdlInitializeSizes затем устанавливает следующие характеристики S-функции.
устанавливает нулевое число ожидаемых параметров диалогового окна S-функции.ssSetNumSFcnParams
определяет, сколько параметров пользователь фактически ввел в диалоговое окно S-функции. Если число пользовательских параметров не соответствует числу, возвращенному ssGetSFcnParamsCount, ошибки S-функции.ssGetNumSFcnParams
Если счетчик параметров S-функции проходит, mdlInitializeSizes устанавливает количество непрерывных и дискретных состояний с помощью и ssSetNumContStatesсоответственно. Этот пример имеет два непрерывных состояния и нулевые дискретные состояния.ssSetNumDiscStates
Затем метод настраивает S-функцию так, чтобы она имела один входной и выходной порт, каждый с шириной два, чтобы соответствовать размерам матриц состояния-пространства. Метод передает значение 1 кому для указания на то, что входной порт имеет прямой канал.ssSetInputPortDirectFeedThrough
инициализирует один момент времени выборки, который ssSetNumSampleTimesmdlInitializeSampleTimes функция настраивается позже.
S-функция указывает, что никакие рабочие векторы не используются при передаче значения 0 кому , ssSetNumRWorkи т.д. Эти строки можно опустить, так как нулевое значение является значением по умолчанию для всех этих макросов. Однако для ясности S-функция явно задает количество рабочих векторов.ssSetNumIWork
Наконец, задает любые применимые опции. В этом случае единственным вариантом является ssSetOptionsSS_OPTION_EXCEPTION_FREE_CODE, который предусматривает, что код является свободным от исключений.
mdlInitializeSizes функция для этого примера показана ниже.
/*====================*
* S-function methods *
*====================*/
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}
ssSetNumContStates(S, 2);
ssSetNumDiscStates(S, 0);
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, 2);
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
/* Take care when specifying exception free code - see sfuntmpl_doc.c */
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}
Требуемый метод S-функции mdlInitializeSampleTimes определяет частоты дискретизации S-функции. Стоимость CONTINUOUS_SAMPLE_TIME передано в макрос указывает, что частота выборки первой S-функции должна быть непрерывной. ssSetSampleTime затем задает нулевое время смещения для этой частоты дискретизации. Вызов ssSetOffsetTimessSetModelReferenceSampleTimeDefaultInheritance сообщает решателю использовать правило по умолчанию, чтобы определить, могут ли ссылочные модели, содержащие эту S-функцию, наследовать свое время выборки от родительской модели.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Specify that we have a continuous sample time.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
Необязательный метод S-функции mdlInitializeConditions инициализирует вектор непрерывного состояния. #define прежде чем этот метод потребуется для вызова этой функции механизмом Simulink. В приведенном ниже примере: получает указатель на вектор непрерывного состояния. ssGetContStatesfor затем цикл инициализирует каждое состояние до нуля.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize both continuous states to zero.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetContStates(S);
int_T lp;
for (lp=0;lp<2;lp++) {
*x0++=0.0;
}
}
Необходимое mdlOutputs функция вычисляет выходной сигнал этой S-функции. Начало функции получает указатели на первый выходной порт, непрерывные состояния и первый входной порт. S-функция использует данные в этих массивах для решения выходного уравнения y=Cx+Du.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = Cx + Du
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
UNUSED_ARG(tid); /* not used in single tasking mode */
/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}
mdlDerivatives функция вычисляет производные непрерывного состояния. Поскольку эта функция является необязательным методом, #define оператор должен предшествовать функции. Начало функции получает указатели на непрерывные состояния S-функции, производные состояния и первый входной порт. S-функция использует эти данные для решения уравнения dx=Ax+Bu.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives =================================================
* Abstract:
* xdot = Ax + Bu
*/
static void mdlDerivatives(SimStruct *S)
{
real_T *dx = ssGetdX(S);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/* xdot=Ax+Bu */
dx[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
dx[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);
}
Необходимое mdlTerminate выполняет любые действия, такие как освобождение памяти, необходимые в конце моделирования. В этом примере функция пуста.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}Требуемый трейлер 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
Примечание
mdlOutputs и mdlTerminate функции используют UNUSED_ARG чтобы указать, что входной аргумент, требуемый для обратного вызова, не используется. Этот дополнительный макрос определен в simstruc_types.h. Если используется, необходимо вызвать этот макрос один раз для каждого входного аргумента, который не используется при обратном вызове.
dsfunc.c Пример показывает, как моделировать дискретную систему в C MEX S-функции. Следующая модель Simulink использует эту S-функцию.
Дискретные системы могут быть смоделированы следующим набором уравнений.

dsfunc.c пример реализует дискретное уравнение состояния-пространства. mdlOutputs содержит выходную часть и mdlUpdate способ содержит часть обновления дискретного уравнения состояния-пространства. Чтобы визуализировать работу моделирования, см. блок-схему взаимодействия Simulink Engine с C S-функциями. Приведенное выше выходное уравнение соответствует mdlOutputs на основном временном шаге. Предыдущее уравнение обновления соответствует mdlUpdate на основном временном шаге. Если модель не содержит непрерывных элементов, модуль Simulink пропускает фазу интеграции и время перемещается вперед к следующему попаданию дискретной выборки.
S-функция dsfunc.c начинается с #define операторы для имени и уровня S-функции вместе с #include заявление для simstruc.h заголовок. После этих операторов S-функция может включать в себя или определять любые другие необходимые заголовки, данные и т.д. dsfunc.c пример определяет U как указатель на сигнал первого входного порта и инициализирует статические переменные для матриц state-space.
/* File : dsfunc.c
* Abstract:
*
* Example C S-function for defining a discrete system.
*
* x(n+1) = Ax(n) + Bu(n)
* y(n) = Cx(n) + Du(n)
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-2013 The MathWorks, Inc.
*/
#define S_FUNCTION_NAME dsfunc
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */
static real_T A[2][2]={ { -1.3839, -0.5097 } ,
{ 1 , 0 }
};
static real_T B[2][2]={ { -2.5559, 0 } ,
{ 0 , 4.2382 }
};
static real_T C[2][2]={ { 0 , 2.0761 } ,
{ 0 , 7.7891 }
};
static real_T D[2][2]={ { -0.8141, -2.9334 } ,
{ 1.2426, 0 }
};
Требуемый метод S-функции mdlInitializeSizes затем устанавливает следующие характеристики S-функции.
устанавливает нулевое число ожидаемых параметров диалогового окна S-функции.ssSetNumSFcnParams
определяет, сколько параметров пользователь фактически ввел в диалоговое окно S-функции. Если число пользовательских параметров не соответствует числу, возвращенному ssGetSFcnParamsCount, ошибки S-функции.ssGetNumSFcnParams
Если счетчик параметров S-функции проходит, mdlInitializeSizes затем устанавливает количество непрерывных и дискретных состояний с помощью и ssSetNumContStatesсоответственно. Этот пример имеет нулевые непрерывные состояния и два дискретных состояния.ssSetNumDiscStates
Затем метод настраивает S-функцию так, чтобы она имела один входной и выходной порт, каждый с шириной два, чтобы соответствовать размерам матриц состояния-пространства. Метод передает значение 1 кому для указания на то, что входной порт имеет прямой канал.ssSetInputPortDirectFeedThrough
инициализирует один момент времени выборки, который ssSetNumSampleTimesmdlInitializeSampleTimes функция настраивается позже.
S-функция указывает, что никакие рабочие векторы не используются при передаче значения 0 кому , ssSetNumRWorkи т.д. Эти строки можно опустить, так как нулевое значение является значением по умолчанию для всех этих макросов. Однако для ясности S-функция явно задает количество рабочих векторов.ssSetNumIWork
Наконец, задает любые применимые опции. В этом случае единственным вариантом является ssSetOptionsSS_OPTION_EXCEPTION_FREE_CODE, который предусматривает, что код является свободным от исключений.
mdlInitializeSizes функция для этого примера показана ниже.
/*====================*
* S-function methods *
*====================*/
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 2);
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, 2);
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
/* Take care when specifying exception free code - see sfuntmpl_doc.c */
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}Требуемый метод S-функции mdlInitializeSampleTimes определяет частоты дискретизации S-функции. Вызов устанавливает этот первый период выборки S-функции равным 1,0. ssSetSampleTime затем задает нулевое время смещения для первой частоты дискретизации. Вызов ssSetOffsetTimessSetModelReferenceSampleTimeDefaultInheritance сообщает решателю использовать правило по умолчанию, чтобы определить, могут ли ссылочные модели, содержащие эту S-функцию, наследовать свое время выборки от родительской модели.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Specify a sample time 0f 1.0.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, 1.0);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
Необязательный метод S-функции mdlInitializeConditions инициализирует вектор дискретного состояния. #define прежде чем этот метод потребуется для вызова этой функции механизмом Simulink. В приведенном ниже примере: получает указатель на вектор дискретного состояния. ssGetRealDiscStatesfor затем цикл инициализирует каждое дискретное состояние в одно.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize both discrete states to one.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetRealDiscStates(S);
int_T lp;
for (lp=0;lp<2;lp++) {
*x0++=1.0;
}
}
Необходимое mdlOutputs функция вычисляет выходной сигнал этой S-функции. Начало функции получает указатели на первый выходной порт, дискретные состояния и первый входной порт. S-функция использует данные в этих массивах для решения выходного уравнения y=Cx+Du.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = Cx + Du
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
UNUSED_ARG(tid); /* not used in single tasking mode */
/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}
Движок Simulink вызывает mdlUpdate один раз в каждый основной шаг времени интегрирования для обновления значений дискретных состояний. Поскольку эта функция является необязательным методом, #define оператор должен предшествовать функции. Начало функции получает указатели на дискретные состояния S-функции и первый входной порт. S-функция использует данные в этих массивах для решения уравнения dx=Ax+Bu, которая хранится во временной переменной tempX перед назначением в вектор дискретного состояния x.
#define MDL_UPDATE
/* Function: mdlUpdate ======================================================
* Abstract:
* xdot = Ax + Bu
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T tempX[2] = {0.0, 0.0};
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
UNUSED_ARG(tid); /* not used in single tasking mode */
/* xdot=Ax+Bu */
tempX[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
tempX[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);
x[0]=tempX[0];
x[1]=tempX[1];
}
Необходимое mdlTerminate выполняет любые действия, такие как освобождение памяти, необходимые в конце моделирования. В этом примере функция пуста.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}
Требуемый трейлер 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
Примечание
mdlOutputs и mdlTerminate функции используют UNUSED_ARG чтобы указать, что входной аргумент, требуемый для обратного вызова, не используется. Этот дополнительный макрос определен в simstruc_types.h. Если используется, необходимо вызвать этот макрос один раз для каждого входного аргумента, который не используется при обратном вызове.
mixedm.c В примере показана гибридная (комбинация непрерывного и дискретного состояний) система. mixedm.c пример комбинирует элементы csfunc.c и dsfunc.c. Следующая модель Simulink использует эту S-функцию.
При наличии гибридной системы mdlDerivatives способ вычисляет производные непрерывных состояний вектора состояния, x, и mdlUpdate способ содержит уравнения, используемые для обновления вектора дискретного состояния, xD. mdlOutputs способ вычисляет выходные сигналы S-функции после проверки совпадений выборок, чтобы определить, в какой момент вызывается S-функция.
В форме блок-схемы Simulink S-функция mixedm.c похоже на
![]()
который реализует непрерывный интегратор с последующей дискретной единичной задержкой.
S-функция mixedm.c начинается с #define операторы для имени и уровня S-функции вместе с #include заявление для simstruc.h заголовок. После этих операторов S-функция может включать в себя или определять любые другие необходимые заголовки, данные и т.д. mixedm.c пример определяет U в качестве указателя на сигнал первого входного порта.
/* File : mixedm.c * Abstract: * * An example S-function illustrating multiple sample times by implementing * integrator -> ZOH(Ts=1second) -> UnitDelay(Ts=1second) * with an initial condition of 1. * (e.g. an integrator followed by unit delay operation). * * For more details about S-functions, see simulink/src/sfuntmpl_doc.c * * Copyright 1990-2007 The MathWorks, Inc. */ #define S_FUNCTION_NAME mixedm #define S_FUNCTION_LEVEL 2 #include "simstruc.h" #define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */
Требуемый метод S-функции mdlInitializeSizes затем устанавливает следующие характеристики S-функции.
устанавливает нулевое число ожидаемых параметров диалогового окна S-функции.ssSetNumSFcnParams
определяет, сколько параметров пользователь фактически ввел в диалоговое окно S-функции. Если число пользовательских параметров не соответствует числу, возвращенному ssGetSFcnParamsCount, ошибки S-функции.ssGetNumSFcnParams
Если счетчик параметров S-функции проходит, mdlInitializeSizes затем устанавливает количество непрерывных и дискретных состояний с помощью и ssSetNumContStatesсоответственно. Этот пример имеет одно непрерывное состояние и одно дискретное состояние.ssSetNumDiscStates
S-функция инициализирует один рабочий вектор с плавающей запятой, передавая значение 1 кому . Другие рабочие векторы не инициализируются.ssSetNumRWork
Далее метод использует и ssSetNumInputPorts для конфигурирования S-функции с одним входным и выходным портом, ширина каждого из которых равна единице. Метод передает значение ssSetNumOutputPorts1 кому для указания на то, что входной порт имеет прямой канал.ssSetInputPortDirectFeedThrough
Эта S-функция назначает время выборки с использованием гибридного блочного и портового метода. Макрос
инициализирует два времени выборки на основе блоков, которые ssSetNumSampleTimesmdlInitializeSampleTimes функция настраивается позже. Макрос и ssSetInputPortSampleTime инициализируйте входной порт, чтобы иметь непрерывное время выборки со смещением, равным нулю. Аналогично, ssSetInputPortOffsetTime и ssSetOutputPortSampleTime инициализировать время выборки выходного порта для ssSetOutputPortOffsetTime1 со смещением, равным нулю.
Наконец, устанавливает две опции S-функции.ssSetOptions SS_OPTION_EXCEPTION_FREE_CODE предусматривает, что код не содержит исключений и SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED указывает комбинацию времени выборки на основе блоков и портов.
mdlInitializeSizes функция для этого примера показана ниже.
*====================*
* S-function methods *
*====================*/
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}
ssSetNumContStates(S, 1);
ssSetNumDiscStates(S, 1);
ssSetNumRWork(S, 1); /* for zoh output feeding the delay operator */
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, 1);
ssSetInputPortDirectFeedThrough(S, 0, 1);
ssSetInputPortSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetInputPortOffsetTime(S, 0, 0.0);
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, 1);
ssSetOutputPortSampleTime(S, 0, 1.0);
ssSetOutputPortOffsetTime(S, 0, 0.0);
ssSetNumSampleTimes(S, 2);
/* Take care when specifying exception free code - see sfuntmpl_doc.c. */
ssSetOptions(S, (SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED));
} /* end mdlInitializeSizes */
Требуемый метод S-функции mdlInitializeSampleTimes определяет частоты дискретизации на основе блоков S-функции. Первый вызов указывает, что первая частота дискретизации непрерывна, с последующим вызовом ssSetSampleTime установка смещения на ноль. Второй вызов этой пары макросов устанавливает время второй выборки равным ssSetOffsetTime1 со смещением, равным нулю. Время выборки на основе порта S-функции, установленное в mdlInitializeSizes все они должны быть зарегистрированы как время выборки на основе блоков. Вызов ssSetModelReferenceSampleTimeDefaultInheritance сообщает решателю использовать правило по умолчанию, чтобы определить, могут ли ссылочные модели, содержащие эту S-функцию, наследовать свое время выборки от родительской модели.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Two tasks: One continuous, one with discrete sample time of 1.0.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetSampleTime(S, 1, 1.0);
ssSetOffsetTime(S, 1, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */
Необязательный метод S-функции mdlInitializeConditions инициализирует векторы непрерывного и дискретного состояний. #define прежде чем этот метод потребуется для вызова этой функции механизмом Simulink. В этом примере: получает указатель на вектор непрерывного состояния и ssGetContStates получает указатель на вектор дискретного состояния. Затем метод устанавливает начальные условия всех состояний единицами.ssGetRealDiscStates
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ==========================================
* Abstract:
* Initialize both continuous states to one.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *xC0 = ssGetContStates(S);
real_T *xD0 = ssGetRealDiscStates(S);
xC0[0] = 1.0;
xD0[0] = 1.0;
} /* end mdlInitializeConditions */
Необходимое mdlOutputs функция выполняет вычисления на основе текущей задачи. Макрос проверяет, выполняется ли непрерывная задача. Если этот макрос возвращается ssIsContinuousTasktrue, затем проверяет, выполняется ли также дискретная частота дискретизации. Если этот макрос также возвращается ssIsSpecialSampleHittrue, способ устанавливает значение рабочего вектора с плавающей запятой в текущее значение непрерывного состояния с помощью указателей, полученных с помощью и ssGetRWorkсоответственно. ssGetContStatesmdlUpdate позже метод использует рабочий вектор с плавающей запятой в качестве входных данных для удержания нулевого порядка. Обновление рабочего вектора в mdlOutputs обеспечивает доступность правильных значений во время последующих вызовов mdlUpdate. Наконец, если S-функция работает с дискретной скоростью, т.е. вызов прибыль ssIsSampleHittrueспособ устанавливает выходной сигнал в значение дискретного состояния.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = xD, and update the zoh internal output.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
/* update the internal "zoh" output */
if (ssIsContinuousTask(S, tid)) {
if (ssIsSpecialSampleHit(S, 1, 0, tid)) {
real_T *zoh = ssGetRWork(S);
real_T *xC = ssGetContStates(S);
*zoh = *xC;
}
}
/* y=xD */
if (ssIsSampleHit(S, 1, tid)) {
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *xD = ssGetRealDiscStates(S);
y[0]=xD[0];
}
} /* end mdlOutputs */
Движок Simulink вызывает mdlUpdate один раз в каждый основной шаг времени интегрирования для обновления значений дискретных состояний. Поскольку эта функция является необязательным методом, #define оператор должен предшествовать функции. Вызов ssIsSampleHit обеспечивает выполнение тела метода только тогда, когда S-функция работает с дискретной скоростью. Если ssIsSampleHit прибыль trueспособ получает указатели на дискретное состояние S-функции и рабочий вектор с плавающей запятой и обновляет значение дискретного состояния, используя значение, сохраненное в рабочем векторе.
#define MDL_UPDATE
/* Function: mdlUpdate ======================================================
* Abstract:
* xD = xC
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
UNUSED_ARG(tid); /* not used in single tasking mode */
/* xD=xC */
if (ssIsSampleHit(S, 1, tid)) {
real_T *xD = ssGetRealDiscStates(S);
real_T *zoh = ssGetRWork(S);
xD[0]=*zoh;
}
} /* end mdlUpdate */
mdlDerivatives функция вычисляет производные непрерывного состояния. Поскольку эта функция является необязательным методом, #define оператор должен предшествовать функции. Функция получает указатели на производную непрерывного состояния S-функции и первый входной порт затем устанавливает производную непрерывного состояния равной значению первого входа.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives =================================================
* Abstract:
* xdot = U
*/
static void mdlDerivatives(SimStruct *S)
{
real_T *dx = ssGetdX(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/* xdot=U */
dx[0]=U(0);
} /* end mdlDerivatives */
Необходимое mdlTerminate выполняет любые действия, такие как освобождение памяти, необходимые в конце моделирования. В этом примере функция пуста.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}
Трейлер 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
Примечание
mdlUpdate и mdlTerminate функции используют UNUSED_ARG чтобы указать, что входной аргумент, требуемый для обратного вызова, не используется. Этот дополнительный макрос определен в simstruc_types.h. Если используется, необходимо вызвать этот макрос один раз для каждого входного аргумента, который не используется при обратном вызове.
Пример S-функции vsfunc.c использует время выборки с переменным шагом. Следующая модель Simulink использует эту S-функцию.
Функции переменного размера шага требуют вызова mdlGetTimeOfNextVarHit, которая является подпрограммой S-функции, которая вычисляет время следующего попадания выборки. S-функции, использующие время выборки с переменным шагом, могут использоваться только с решателями с переменным шагом. vsfunc.c пример - дискретная S-функция, которая задерживает свой первый вход на величину времени, определяемую вторым входом.
vsfunc.c пример выводит входные данные u задерживается на переменный промежуток времени. mdlOutputs устанавливает выходные данные y равно состоянию x. mdlUpdate задает вектор состояния x равно u, входной вектор. Этот пример вызывает mdlGetTimeOfNextVarHit для расчета и установки времени следующего попадания пробы, то есть времени, когда vsfunc.c Далее вызывается. В mdlGetTimeOfNextVarHit, макрос получает указатель на вход ssGetInputPortRealSignalPtrsu. Затем выполняется следующий вызов:
ssSetTNext(S, ssGetT(S) + U(1));
Макрос получает время моделирования ssGetTt. Второй вход в блок, U(1), добавляется в t, и макрос устанавливает время следующего попадания равным ssSetTNextt+U(1), задерживая выход на время, установленное в (U(1)).
S-функция vsfunc.c начинается с #define операторы для имени и уровня S-функции вместе с #include заявление для simstruc.h заголовок. После этих операторов S-функция может включать в себя или определять любые другие необходимые заголовки, данные и т.д. vsfunc.c пример определяет U в качестве указателя на сигнал первого входного порта.
/* File : vsfunc.c * Abstract: * * Variable step S-function example. * This example S-function illustrates how to create a variable step * block. This block implements a variable step delay * in which the first input is delayed by an amount of time determined * by the second input: * * dt = u(2) * y(t+dt) = u(t) * * For more details about S-functions, see simulink/src/sfuntmpl_doc.c. * * Copyright 1990-2007 The MathWorks, Inc. */ #define S_FUNCTION_NAME vsfunc #define S_FUNCTION_LEVEL 2 #include "simstruc.h" #define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */
Требуемый метод S-функции mdlInitializeSizes затем устанавливает следующие характеристики S-функции.
устанавливает нулевое число ожидаемых параметров диалогового окна S-функции.ssSetNumSFcnParams
определяет, сколько параметров пользователь фактически ввел в диалоговое окно S-функции. Если число пользовательских параметров не соответствует числу, возвращенному ssGetSFcnParamsCount, ошибки S-функции.ssGetNumSFcnParams
Если счетчик параметров S-функции проходит, mdlInitializeSizes затем устанавливает количество непрерывных и дискретных состояний с помощью и ssSetNumContStatesсоответственно. Этот пример не имеет непрерывных состояний и одного дискретного состояния.ssSetNumDiscStates
Далее метод использует и ssSetNumInputPorts для конфигурирования S-функции с одним портом ввода и вывода. Звонки в ssSetNumOutputPortsssSetInputPortWidth и ssSetOutputPortWidth назначьте этим портам ввода и вывода ширину. Метод передает значение 1 кому для указания на то, что входной порт имеет прямой канал.ssSetInputPortDirectFeedThrough
затем инициализирует один момент времени выборки, который ssSetNumSampleTimesmdlInitializeSampleTimes функция настраивается позже.
S-функция указывает, что никакие рабочие векторы не используются при передаче значения 0 кому , ssSetNumRWorkи т.д. Эти строки можно опустить, так как нулевое значение является значением по умолчанию для всех этих макросов. Однако для ясности S-функция явно задает количество рабочих векторов.ssSetNumIWork
Далее, проверяет, выполняется ли S-функция при моделировании или продуктом Simulink Coder™. Если ssGetSimModessGetSimMode прибыль SS_SIMMODE_RTWGEN и прибыль ssIsVariableStepSolverfalse, указывая использование с продуктом Simulink Coder и решателем с фиксированным шагом, затем ошибки S-функции.
Наконец, задает любые применимые опции. В этом случае единственным вариантом является ssSetOptionsSS_OPTION_EXCEPTION_FREE_CODE, который предусматривает, что код является свободным от исключений.
mdlInitializeSizes функция для этого примера показана ниже.
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 1);
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, 1);
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
if (ssGetSimMode(S) == SS_SIMMODE_RTWGEN && !ssIsVariableStepSolver(S)) {
ssSetErrorStatus(S, "S-function vsfunc.c cannot be used with RTW "
"and Fixed-Step Solvers because it contains variable"
" sample time");
}
/* Take care when specifying exception free code - see sfuntmpl_doc.c */
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}
Требуемый метод S-функции mdlInitializeSampleTimes определяет частоты дискретизации S-функции. Входной аргумент VARIABLE_SAMPLE_TIME передано в указывает, что эта S-функция имеет время выборки с переменным шагом и ssSetSampleTime задает нулевое время смещения. Вызов ssSetOffsetTimessSetModelReferenceSampleTimeDefaultInheritance сообщает решателю использовать правило по умолчанию, чтобы определить, могут ли ссылочные модели, содержащие эту S-функцию, наследовать свое время выборки от родительской модели. Поскольку S-функция имеет время выборки с переменным шагом, vsfunc.c должен рассчитать время следующего попадания образца в mdlGetTimeOfNextVarHit способ, показанный ниже.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Variable-Step S-function
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, VARIABLE_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
Необязательный метод S-функции mdlInitializeConditions инициализирует вектор дискретного состояния. #define прежде чем этот метод потребуется для вызова этой функции механизмом Simulink. В примере метод использует получить указатель на вектор дискретного состояния и установить начальное значение состояния равным нулю.ssGetRealDiscStates
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize discrete state to zero.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetRealDiscStates(S);
x0[0] = 0.0;
}
Дополнительное mdlGetTimeOfNextVarHit вычисляет время следующего попадания образца. Поскольку этот метод необязателен, #define оператор предшествует ему. Сначала этот способ получает указатель на сигнал первого входного порта, используя . Если второй элемент входного сигнала положительный, то макрос ssGetInputPortRealSignalPtrs получает время моделирования ssGetTt. Макрос устанавливает время следующего попадания равным ssSetTNextt+(*U[1]), задерживая выход на время, заданное вторым элементом входа (*U[1]).
#define MDL_GET_TIME_OF_NEXT_VAR_HIT
static void mdlGetTimeOfNextVarHit(SimStruct *S)
{
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/* Make sure input will increase time */
if (U(1) <= 0.0) {
/* If not, abort simulation */
ssSetErrorStatus(S,"Variable step control input must be "
"greater than zero");
return;
}
ssSetTNext(S, ssGetT(S)+U(1));
}
Необходимое mdlOutputs функция вычисляет выходной сигнал S-функции. Функция получает указатели на первый выходной порт и дискретное состояние, а затем присваивает выходному порту текущее значение состояния.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = x
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = ssGetRealDiscStates(S);
/* Return the current state as the output */
y[0] = x[0];
}
mdlUpdate функция обновляет значение дискретного состояния. Поскольку этот метод необязателен, #define оператор предшествует ему. Функция сначала получает указатели на дискретное состояние S-функции, и первый входной порт затем присваивает состоянию значение первого элемента сигнала первого входного порта.
#define MDL_UPDATE
/* Function: mdlUpdate ========================================================
* Abstract:
* This function is called once for every major integration time step.
* Discrete states are typically updated here, but this function is useful
* for performing any tasks that should only take place once per integration
* step.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
x[0]=U(0);
}
Необходимое mdlTerminate выполняет любые действия, такие как освобождение памяти, необходимые в конце моделирования. В этом примере функция пуста.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
}
Требуемый трейлер 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
Пример S-функции sfun_matadd.c демонстрирует, как реализовать блок сложения матрицы. Следующая модель Simulink использует эту S-функцию.
S-функция добавляет сигналы различных размеров к значению параметра, введенному в S-функцию. S-функция принимает и выводит 2-D или n-D сигналы.
S-функция sfun_matadd.c начинается с #define операторы для имени и уровня S-функции вместе с #include заявление для simstruc.h заголовок. После этих операторов S-функция включает или определяет любые другие необходимые заголовки, данные и т.д. В этом примере определяются дополнительные переменные для количества параметров S-функции, значения параметра S-функции и флага EDIT_OK указывает, можно ли редактировать значение параметра во время моделирования.
/* SFUN_MATADD matrix support example.
* C MEX S-function for matrix addition with one input port,
* one output port, and one parameter.
*
* Input Signal: 2-D or n-D array
* Parameter: 2-D or n-D array
* Output Signal: 2-D or n-D array
*
* Input parameter output
* --------------------------------
* scalar scalar scalar
* scalar matrix matrix (input scalar expansion)
* matrix scalar matrix (parameter scalar expansion)
* matrix matrix matrix
*
* Copyright 1990-2007 The MathWorks, Inc.
*/
#define S_FUNCTION_NAME sfun_matadd
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
enum {PARAM = 0, NUM_PARAMS};
#define PARAM_ARG ssGetSFcnParam(S, PARAM)
#define EDIT_OK(S, ARG) \
(!((ssGetSimMode(S) == SS_SIMMODE_SIZES_CALL_ONLY) \
&& mxIsEmpty(ARG)))Затем S-функция реализует mdlCheckParameters метод проверки параметров диалогового окна S-функции. #ifdef оператор проверяет, что S-функция скомпилирована как MEX-файл, а не для использования с продуктом Simulink Coder. Поскольку mdlCheckParameters необязателен, код S-функции содержит #define оператор для регистрации метода. Тело функции проверяет, что значение параметра S-функции не является пустым. Если проверка параметров завершается неуспешно, S-функция выдает ошибку при вызове ssSetErrorStatus.
#ifdef MATLAB_MEX_FILE
#define MDL_CHECK_PARAMETERS
/* Function: mdlCheckParameters ================================
* Abstract:
* Verify parameter settings.
*/
static void mdlCheckParameters(SimStruct *S)
{
if(EDIT_OK(S, PARAM_ARG)){
/* Check that parameter value is not empty*/
if( mxIsEmpty(PARAM_ARG) ) {
ssSetErrorStatus(S, "Invalid parameter specified. The"
"parameter must be non-empty");
return;
}
}
} /* end mdlCheckParameters */
#endif Требуемый метод S-функции mdlInitializeSizes затем устанавливает следующие характеристики S-функции.
устанавливает количество ожидаемых диалоговых параметров S-функции равным единице, как определено переменной ssSetNumSFcnParamsNUM_PARAMS.
Если эта S-функция скомпилирована как MEX-файл, определяет, сколько параметров пользователь фактически ввел в диалоговое окно S-функции. Если количество пользовательских параметров соответствует числу, возвращенному ssGetSFcnParamsCount, метод вызывает ssGetNumSFcnParamsmdlCheckParameters для проверки введенных пользователем данных. В противном случае S-функция ошибается.
Если проверка параметров проходит успешно, S-функция указывает, что все параметры S-функции настраиваются с помощью .ssSetSFcnParamTunable
Затем S-функция вызывает позволяет S-функции принимать n-D сигналы.ssAllowSignalsWithMoreThan2D
Далее, и ssSetNumOutputPortsssSetNumInputPorts укажите, что S-функция имеет один выходной порт и один входной порт.
S-функция использует ssSetInputPortDimensionInfo для указания динамического размера входного порта. В этом случае S-функция должна реализовать mdlSetInputPortDimensionInfo для установки фактического входного размера.
Выходные размеры зависят от размеров параметра S-функции. Если параметр является скаляром, вызов ssSetOutputPortDimensionInfo указывает на динамический размер размеров выходного порта. Если параметр является матрицей, то размеры выходного порта инициализируются измерениями параметра S-функции. В этом случае макрос DECL_AND_INIT_DIMSINFO инициализирует dimsInfo структура. S-функция назначает ширину, размер и размеры параметра S-функции в dimsInfo и затем передает эту структуру в ssSetOutputPortDimensionInfo для установки соответствующих размеров выходного порта.
S-функция указывает, что входной порт имеет прямой канал, передавая значение 1 кому .ssSetInputPortDirectFeedThrough
инициализирует один образец времени, который будет сконфигурирован позже в ssSetNumSampleTimesmdlInitializeSampleTimes способ.
Наконец, задает любые применимые опции. В этом случае ssSetOptionsSS_OPTION_EXCEPTION_FREE_CODE предусматривает, что код не содержит исключений и SS_OPTION_WORKS_WITH_CODE_REUSE означает, что эта S-функция совместима с функцией повторного использования кода подсистемы продукта Simulink Coder.
/* Function: mdlInitializeSizes ================================
* Abstract:
* Initialize the sizes array
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, NUM_PARAMS);
#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; }
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) return;
#endif
{
int iParam = 0;
int nParam = ssGetNumSFcnParams(S);
for ( iParam = 0; iParam < nParam; iParam++ )
{
ssSetSFcnParamTunable( S, iParam, SS_PRM_TUNABLE );
}
}
/* Allow signal dimensions greater than 2 */
ssAllowSignalsWithMoreThan2D(S);
/* Set number of input and output ports */
if (!ssSetNumInputPorts( S,1)) return;
if (!ssSetNumOutputPorts(S,1)) return;
/* Set dimensions of input and output ports */
{
int_T pWidth = mxGetNumberOfElements(PARAM_ARG);
/* Input can be a scalar or a matrix signal. */
if(!ssSetInputPortDimensionInfo(S,0,DYNAMIC_DIMENSION)) {
return; }
if( pWidth == 1) {
/* Scalar parameter: output dimensions are unknown. */
if(!ssSetOutputPortDimensionInfo(S,0,DYNAMIC_DIMENSION)){
return; }
}
else{
/*
* Non-scalar parameter: output dimensions are the same
* as the parameter dimensions. To support n-D signals,
* must use a dimsInfo structure to specify dimensions.
*/
DECL_AND_INIT_DIMSINFO(di); /*Initializes structure*/
int_T pSize = mxGetNumberOfDimensions(PARAM_ARG);
const int_T *pDims = mxGetDimensions(PARAM_ARG);
di.width = pWidth;
di.numDims = pSize;
di.dims = pDims;
if(!ssSetOutputPortDimensionInfo(S, 0, &di)) return;
}
}
ssSetInputPortDirectFeedThrough(S, 0, 1);
ssSetNumSampleTimes(S, 1);
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE);
} /* end mdlInitializeSizes */Требуемый метод S-функции mdlInitializeSampleTimes определяет частоты дискретизации S-функции. Чтобы указать, что эта S-функция наследует время выборки из своего управляющего блока, S-функция вызывает с входным аргументом ssSetSampleTimeINHERITED_SAMPLE_TIME. Вызов сообщает решателю использовать правило по умолчанию, чтобы определить, могут ли ссылочные модели, содержащие эту S-функцию, наследовать свое время выборки от родительской модели.ssSetModelReferenceSampleTimeDefaultInheritance
/* Function: mdlInitializeSampleTimes ==========================
* Abstract:
* Initialize the sample times array.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */S-функция вызывает mdlSetWorkWidths способ регистрации его параметров времени выполнения. Поскольку mdlSetWorkWidths является необязательным методом, а #define оператор предшествует ему. Метод сначала инициализирует имя параметра времени выполнения, а затем использует ssRegAllTunableParamsAsRunTimeParams для регистрации параметра времени выполнения.
/* Function: mdlSetWorkWidths ==================================
* Abstract:
* Set up run-time parameter.
*/
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S)
{
const char_T *rtParamNames[] = {"Operand"};
ssRegAllTunableParamsAsRunTimeParams(S, rtParamNames);
} /* end mdlSetWorkWidths */S-функция mdlOutputs метод использует for цикл для вычисления выходного сигнала как суммы входного параметра и параметра S-функции. S-функция обрабатывает n-D массивов данных, используя один индекс в массиве.
/* Function: mdlOutputs ========================================
* Abstract:
* Compute the outputs of the S-function.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
InputRealPtrsType uPtr = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
const real_T *p = mxGetPr(PARAM_ARG);
int_T uWidth = ssGetInputPortWidth(S,0);
int_T pWidth = mxGetNumberOfElements(PARAM_ARG);
int_T yWidth = ssGetOutputPortWidth(S,0);
int i;
UNUSED_ARG(tid); /* not used in single tasking mode */
/*
* Note1: Matrix signals are stored in column major order.
* Note2: Access each matrix element by one index not two
* indices. For example, if the output signal is a
* [2x2] matrix signal,
* - -
* | y[0] y[2] |
* | y[1] y[3] |
* - -
* Output elements are stored as follows:
* y[0] --> row = 0, col = 0
* y[1] --> row = 1, col = 0
* y[2] --> row = 0, col = 1
* y[3] --> row = 1, col = 1
*/
for (i = 0; i < yWidth; i++) {
int_T uIdx = (uWidth == 1) ? 0 : i;
int_T pIdx = (pWidth == 1) ? 0 : i;
y[i] = *uPtr[uIdx] + p[pIdx];
}
} /* end mdlOutputs */Во время распространения сигнала S-функция вызывает дополнительный mdlSetInputPortDimensionInfo метод с возможными размерами входного порта, сохраненными в dimsInfo. #if defined проверяет, что S-функция скомпилирована как MEX-файл. Поскольку mdlSetInputPortDimensionInfo является необязательным методом, а #define оператор предшествует ему. В mdlSetInputPortDimensionInfo, S-функция использует для установки размеров входного порта в соответствии с возможными размерами. Если вызов этого макроса завершается успешно, S-функция дополнительно проверяет возможные размеры, чтобы убедиться, что входной сигнал является либо 2-D скаляром, либо матрицей. Если это условие выполняется, а размеры выходного порта по-прежнему динамически увеличиваются, функция S вызывает ssSetInputPortDimensionInfo задание размера выходного порта в соответствии с теми же возможными размерами. ssSetOutputPortDimensionInfossSetOutputPortDimensionInfo макрос не может изменять размеры выходного порта, если они уже указаны.
#if defined(MATLAB_MEX_FILE)
#define MDL_SET_INPUT_PORT_DIMENSION_INFO
/* Function: mdlSetInputPortDimensionInfo ======================
* Abstract:
* This routine is called with the candidate dimensions for
* an input port with unknown dimensions. If the proposed
* dimensions are acceptable, the routine should go ahead and
* set the actual port dimensions. If they are unacceptable
* an error should be generated via ssSetErrorStatus.
* Note that any other input or output ports whose dimensions
* are implicitly defined by virtue of knowing the dimensions
* of the given port can also have their dimensions set.
*/
static void mdlSetInputPortDimensionInfo(SimStruct *S,
int_T port,
const DimsInfo_T *dimsInfo)
{
int_T pWidth = mxGetNumberOfElements(PARAM_ARG);
int_T pSize = mxGetNumberOfDimensions(PARAM_ARG);
const int_T *pDims = mxGetDimensions(PARAM_ARG);
int_T uNumDims = dimsInfo->numDims;
int_T uWidth = dimsInfo->width;
int_T *uDims = dimsInfo->dims;
int_T numDims;
boolean_T isOk = true;
int iParam = 0;
int_T outWidth = ssGetOutputPortWidth(S, 0);
/* Set input port dimension */
if(!ssSetInputPortDimensionInfo(S, port, dimsInfo)) return;
/*
* The block only accepts 2-D or higher signals. Check
* number of dimensions. If the parameter and the input
* signal are non-scalar, their dimensions must be the same.
*/
isOk = (uNumDims >= 2) && (pWidth == 1 || uWidth == 1 ||
pWidth == uWidth);
numDims = (pSize != uNumDims) ? numDims : uNumDims;
if(isOk && pWidth > 1 && uWidth > 1){
for ( iParam = 0; iParam < numDims; iParam++ ) {
isOk = (pDims[iParam] == uDims[iParam]);
if(!isOk) break;
}
}
if(!isOk){
ssSetErrorStatus(S,"Invalid input port dimensions. The "
"input signal must be a 2-D scalar signal, or it must "
"be a matrix with the same dimensions as the parameter "
"dimensions.");
return;
}
/* Set the output port dimensions */
if (outWidth == DYNAMICALLY_SIZED){
if(!ssSetOutputPortDimensionInfo(S,port,dimsInfo)) return;
}
} /* end mdlSetInputPortDimensionInfo */Во время распространения сигнала, если какие-либо выходные порты имеют неизвестные размеры, S-функция вызывает дополнительный mdlSetOutputPortDimensionInfo способ. Поскольку этот метод необязателен, #define оператор предшествует ему. В mdlSetOutputPortDimensionInfo, S-функция использует задание размеров выходного порта в соответствии с возможными размерами ssSetOutputPortDimensionInfodimsInfo. Если вызов этого макроса завершается успешно, S-функция дополнительно проверяет возможные размеры, чтобы убедиться, что входной сигнал является либо 2-D, либо n-D матрицей. Если это условие не выполняется, S-функция выдает ошибку при вызове ssSetErrorStatus. В противном случае вызов S-функции задание размера входного порта в соответствии с теми же возможными размерами.ssSetInputPortDimensionInfo
# define MDL_SET_OUTPUT_PORT_DIMENSION_INFO
/* Function: mdlSetOutputPortDimensionInfo =====================
* Abstract:
* This routine is called with the candidate dimensions for
* an output port with unknown dimensions. If the proposed
* dimensions are acceptable, the routine should go ahead and
* set the actual port dimensions. If they are unacceptable
* an error should be generated via ssSetErrorStatus.
* Note that any other input or output ports whose dimensions
* are implicitly defined by virtue of knowing the dimensions
* of the given port can also have their dimensions set.
*/
static void mdlSetOutputPortDimensionInfo(SimStruct *S,
int_T port,
const DimsInfo_T *dimsInfo)
{
/*
* If the block has scalar parameter, the output dimensions
* are unknown. Set the input and output port to have the
* same dimensions.
*/
if(!ssSetOutputPortDimensionInfo(S, port, dimsInfo)) return;
/* The block only accepts 2-D or n-D signals.
* Check number of dimensions.
*/
if (!(dimsInfo->numDims >= 2)){
ssSetErrorStatus(S, "Invalid output port dimensions. "
"The output signal must be a 2-D or n-D array (matrix) "
"signal.");
return;
}else{
/* Set the input port dimensions */
if(!ssSetInputPortDimensionInfo(S,port,dimsInfo)) return;
}
} /* end mdlSetOutputPortDimensionInfo */Поскольку S-функция имеет порты динамического размера, она должна обеспечивать mdlSetDefaultPortDimensionInfo способ. Механизм Simulink вызывает этот метод во время распространения сигнала, когда он не может определить размерность сигнала, подключенного к входному порту блока. Такая ситуация может возникнуть, например, если входной порт не подключен. В этом примере mdlSetDefaultPortDimensionInfo устанавливает размеры входного и выходного портов на скаляр.
# define MDL_SET_DEFAULT_PORT_DIMENSION_INFO
/* Function: mdlSetDefaultPortDimensionInfo ====================
* This routine is called when the Simulink engine is not able
* to find dimension candidates for ports with unknown dimensions.
* This function must set the dimensions of all ports with
* unknown dimensions.
*/
static void mdlSetDefaultPortDimensionInfo(SimStruct *S)
{
int_T outWidth = ssGetOutputPortWidth(S, 0);
/* Input port dimension must be unknown. Set it to scalar.*/
if(!ssSetInputPortMatrixDimensions(S, 0, 1, 1)) return;
if(outWidth == DYNAMICALLY_SIZED){
/* Output dimensions are unknown. Set it to scalar. */
if(!ssSetOutputPortMatrixDimensions(S, 0, 1, 1)) return;
}
} /* end mdlSetDefaultPortDimensionInfo */
#endifНеобходимое mdlTerminate выполняет любые действия, такие как освобождение памяти, необходимые в конце моделирования. В этом примере функция пуста.
/* Function: mdlTerminate ======================================
* Abstract:
* Called when the simulation is terminated.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
} /* end mdlTerminate */Требуемый трейлер S-функции включает файлы, необходимые для моделирования или генерации кода.
#ifdef MATLAB_MEX_FILE #include "simulink.c" #else #include "cg_sfun.h" #endif /* [EOF] sfun_matadd.c */
Примечание
mdlOutputs и mdlTerminate функции используют UNUSED_ARG чтобы указать, что входной аргумент, требуемый для обратного вызова, не используется. Этот дополнительный макрос определен в simstruc_types.h. Этот макрос необходимо вызвать один раз для каждого входного аргумента, который не используется при обратном вызове.
Пример S-функции sfun_zc_sat.c демонстрирует, как реализовать блок насыщения. Следующая модель Simulink использует эту S-функцию.
S-функция работает либо с решателями с фиксированным шагом, либо с решателями с переменным шагом. Когда эта S-функция наследует непрерывное время выборки и использует решатель с переменным шагом, она использует алгоритм пересечения нулей для определения точных точек, в которых происходит насыщение.
S-функция sfun_zc_sat.c начинается с #define операторы для имени и уровня S-функции вместе с #include заявление для simstruc.h заголовок. После этих операторов S-функция включает или определяет любые другие необходимые заголовки, данные и т.д. В этом примере определяются различные параметры, связанные с верхней и нижней границами насыщения.
/* File : sfun_zc_sat.c * Abstract: * * Example of an S-function which has nonsampled zero crossings to * implement a saturation function. This S-function is designed to be * used with a variable or fixed step solver. * * A saturation is described by three equations * * (1) y = UpperLimit * (2) y = u * (3) y = LowerLimit * * and a set of inequalities that specify which equation to use * * if UpperLimit < u then use (1) * if LowerLimit <= u <= UpperLimit then use (2) * if u < LowerLimit then use (3) * * A key fact is that the valid equation 1, 2, or 3, can change at * any instant. Nonsampled zero crossing support helps the variable step * solvers locate the exact instants when behavior switches from one equation * to another. * * Copyright 1990-2007 The MathWorks, Inc. */ #define S_FUNCTION_NAME sfun_zc_sat #define S_FUNCTION_LEVEL 2 #include "simstruc.h" /*========================* * General Defines/macros * *========================*/ /* index to Upper Limit */ #define I_PAR_UPPER_LIMIT 0 /* index to Lower Limit */ #define I_PAR_LOWER_LIMIT 1 /* total number of block parameters */ #define N_PAR 2 /* * Make access to mxArray pointers for parameters more readable. */ #define P_PAR_UPPER_LIMIT ( ssGetSFcnParam(S,I_PAR_UPPER_LIMIT) ) #define P_PAR_LOWER_LIMIT ( ssGetSFcnParam(S,I_PAR_LOWER_LIMIT) )
Далее эта S-функция реализует mdlCheckParameters способ проверки достоверности параметров диалогового окна S-функции. Поскольку этот метод необязателен, #define оператор предшествует ему. #if defined проверяет, что эта функция скомпилирована как файл MEX, а не для использования с продуктом Simulink Coder. Тело функции выполняет основные проверки, чтобы убедиться, что пользователь ввел действительные векторы равной длины для верхнего и нижнего пределов насыщения. Если проверка параметра завершается неуспешно, S-функция выдает ошибку.
#define MDL_CHECK_PARAMETERS
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/* Function: mdlCheckParameters =============================================
* Abstract:
* Check that parameter choices are allowable.
*/
static void mdlCheckParameters(SimStruct *S)
{
int_T i;
int_T numUpperLimit;
int_T numLowerLimit;
const char *msg = NULL;
/*
* check parameter basics
*/
for ( i = 0; i < N_PAR; i++ ) {
if ( mxIsEmpty( ssGetSFcnParam(S,i) ) ||
mxIsSparse( ssGetSFcnParam(S,i) ) ||
mxIsComplex( ssGetSFcnParam(S,i) ) ||
!mxIsNumeric( ssGetSFcnParam(S,i) ) ) {
msg = "Parameters must be real vectors.";
goto EXIT_POINT;
}
}
/*
* Check sizes of parameters.
*/
numUpperLimit = mxGetNumberOfElements( P_PAR_UPPER_LIMIT );
numLowerLimit = mxGetNumberOfElements( P_PAR_LOWER_LIMIT );
if ( ( numUpperLimit != 1 ) &&
( numLowerLimit != 1 ) &&
( numUpperLimit != numLowerLimit ) ) {
msg = "Number of input and output values must be equal.";
goto EXIT_POINT;
}
/*
* Error exit point
*/
EXIT_POINT:
if (msg != NULL) {
ssSetErrorStatus(S, msg);
}
}
#endif /* MDL_CHECK_PARAMETERS */
Требуемый метод S-функции mdlInitializeSizes устанавливает следующие характеристики S-функции.
устанавливает число ожидаемых диалоговых параметров S-функции равным двум, как определено ранее в переменной ssSetNumSFcnParamsN_PAR.
Если этот метод компилируется как файл MEX, определяет, сколько параметров пользователь фактически ввел в диалоговое окно S-функции. Если количество пользовательских параметров соответствует числу, возвращенному ssGetSFcnParamsCount, метод вызывает ssGetNumSFcnParamsmdlCheckParameters для проверки достоверности введенных пользователем данных. В противном случае S-функция ошибается.
Если проверка параметров проходит успешно, S-функция определяет максимальное количество элементов, введенных в верхний или нижний параметр предела насыщения. Это число необходимо позже для определения соответствующей ширины выходного сигнала.
Далее устанавливается количество непрерывных и дискретных состояний с помощью и ssSetNumContStatesсоответственно. Этот пример не имеет непрерывных или дискретных состояний.ssSetNumDiscStates
Метод указывает, что S-функция имеет один выходной порт с использованием и задает ширину этого выходного порта с помощью ssSetNumOutputPorts. Ширина выходного порта - это либо максимальное количество элементов в верхнем или нижнем пределе насыщения, либо динамический размер. Аналогичный код определяет один входной порт и указывает, что входной порт имеет прямой канал, передавая значение ssSetOutputPortWidth1 кому .ssSetInputPortDirectFeedThrough
инициализирует один момент времени выборки, который ssSetNumSampleTimesmdlInitializeSampleTimes функция настраивается позже.
S-функция указывает, что никакие рабочие векторы не используются при передаче значения 0 кому , ssSetNumRWorkи т.д. Эти строки можно опустить, так как нулевое значение является значением по умолчанию для всех этих макросов. Однако для ясности S-функция явно задает количество рабочих векторов.ssSetNumIWork
Метод инициализирует рабочие векторы обнаружения пересечения нуля с помощью и ssSetNumModes. ssSetNumNonsampledZCsmdlSetWorkWidths задает длину этих векторов динамического размера позже.
Наконец, задает любые применимые опции. В этом случае ssSetOptionsSS_OPTION_EXCEPTION_FREE_CODE предусматривает, что код не содержит исключений и SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION позволяет скалярное расширение входа без необходимости обеспечения mdlSetInputPortWidth функция.
mdlInitializeSizes функция для этого примера показана ниже.
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Initialize the sizes array.
*/
static void mdlInitializeSizes(SimStruct *S)
{
int_T numUpperLimit, numLowerLimit, maxNumLimit;
/*
* Set and Check parameter count
*/
ssSetNumSFcnParams(S, N_PAR);
#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch reported by the Simulink engine*/
}
#endif
/*
* Get parameter size info.
*/
numUpperLimit = mxGetNumberOfElements( P_PAR_UPPER_LIMIT );
numLowerLimit = mxGetNumberOfElements( P_PAR_LOWER_LIMIT );
if (numUpperLimit > numLowerLimit) {
maxNumLimit = numUpperLimit;
} else {
maxNumLimit = numLowerLimit;
}
/*
* states
*/
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);
/*
* outputs
* The upper and lower limits are scalar expanded
* so their size determines the size of the output
* only if at least one of them is not scalar.
*/
if (!ssSetNumOutputPorts(S, 1)) return;
if ( maxNumLimit > 1 ) {
ssSetOutputPortWidth(S, 0, maxNumLimit);
} else {
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
}
/*
* inputs
* If the upper or lower limits are not scalar then
* the input is set to the same size. However, the
* ssSetOptions below allows the actual width to
* be reduced to 1 if needed for scalar expansion.
*/
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortDirectFeedThrough(S, 0, 1 );
if ( maxNumLimit > 1 ) {
ssSetInputPortWidth(S, 0, maxNumLimit);
} else {
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
}
/*
* sample times
*/
ssSetNumSampleTimes(S, 1);
/*
* work
*/
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
/*
* Modes and zero crossings:
* If we have a variable-step solver and this block has a continuous
* sample time, then
* o One mode element will be needed for each scalar output
* in order to specify which equation is valid (1), (2), or (3).
* o Two ZC elements will be needed for each scalar output
* in order to help the solver find the exact instants
* at which either of the two possible "equation switches"
* One will be for the switch from eq. (1) to (2);
* the other will be for eq. (2) to (3) and vice versa.
* otherwise
* o No modes and nonsampled zero crossings will be used.
*
*/
ssSetNumModes(S, DYNAMICALLY_SIZED);
ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED);
/*
* options
* o No mexFunctions and no problematic mxFunctions are called
* so the exception free code option safely gives faster simulations.
* o Scalar expansion of the inputs is desired. The option provides
* this without the need to write mdlSetOutputPortWidth and
* mdlSetInputPortWidth functions.
*/
ssSetOptions(S, ( SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION));
} /* end mdlInitializeSizes */
Требуемый метод S-функции mdlInitializeSampleTimes определяет частоты дискретизации S-функции. Входной аргумент INHERITED_SAMPLE_TIME передано в указывает, что эта S-функция наследует время выборки из своего управляющего блока. Вызов ssSetSampleTimessSetModelReferenceSampleTimeDefaultInheritance сообщает решателю использовать правило по умолчанию, чтобы определить, могут ли ссылочные модели, содержащие эту S-функцию, наследовать свое время выборки от родительской модели.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Specify that the block is continuous.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
Необязательный метод mdlSetWorkWidths инициализирует размер рабочих векторов обнаружения пересечения нуля. Поскольку этот метод необязателен, #define оператор предшествует ему. #if defined проверяет, что S-функция компилируется как MEX-файл. Обнаружение пересечения нуля может быть выполнено только тогда, когда S-функция работает с непрерывной частотой дискретизации с использованием решателя с переменным шагом. if оператор использует , ssIsVariableStepSolver, и ssGetSampleTime чтобы определить, выполняется ли это условие. Если это так, то метод устанавливает количество режимов, равное ширине первого выходного порта, и количество незабранных нулевых пересечений в два раза больше этой величины. В противном случае метод устанавливает оба значения в нуль.ssGetOffsetTime
#define MDL_SET_WORK_WIDTHS
#if defined(MDL_SET_WORK_WIDTHS) && defined(MATLAB_MEX_FILE)
/* Function: mdlSetWorkWidths ===============================================
* The width of the Modes and the ZCs depends on the width of the output.
* This width is not always known in mdlInitializeSizes so it is handled
* here.
*/
static void mdlSetWorkWidths(SimStruct *S)
{
int nModes;
int nNonsampledZCs;
if (ssIsVariableStepSolver(S) &&
ssGetSampleTime(S,0) == CONTINUOUS_SAMPLE_TIME &&
ssGetOffsetTime(S,0) == 0.0) {
int numOutput = ssGetOutputPortWidth(S, 0);
/*
* modes and zero crossings
* o One mode element will be needed for each scalar output
* in order to specify which equation is valid (1), (2), or (3).
* o Two ZC elements will be needed for each scalar output
* in order to help the solver find the exact instants
* at which either of the two possible "equation switches"
* One will be for the switch from eq. (1) to (2);
* the other will be for eq. (2) to (3) and vice versa.
*/
nModes = numOutput;
nNonsampledZCs = 2 * numOutput;
} else {
nModes = 0;
nNonsampledZCs = 0;
}
ssSetNumModes(S,nModes);
ssSetNumNonsampledZCs(S,nNonsampledZCs);
}
#endif /* MDL_SET_WORK_WIDTHS */
После объявления переменных для входного и выходного сигналов, mdlOutputs функции используют if-else оператор для создания блоков кода, используемых для вычисления выходного сигнала на основе того, использует ли S-функция решатель с фиксированным или переменным шагом. if оператор запрашивает длину невыбранного вектора пересечения нуля. Если длина, установите в mdlWorkWidths, равно нулю, тогда обнаружение пересечения нуля не выполняется и выходные сигналы вычисляются непосредственно из входных сигналов. В противном случае функция использует рабочий вектор режима для определения способа вычисления выходного сигнала. Если моделирование находится на основном шаге времени, т.е. прибыль ssIsMajorTimeSteptrue, mdlOutputs определяет, в каком режиме выполняется моделирование: насыщенном на верхнем пределе, насыщенном на нижнем пределе или не насыщенном. Затем, как для основных, так и для второстепенных временных шагов, функция вычисляет выходной сигнал на основе этого режима. Если режим изменился между предыдущим и текущим шагом времени, то произошло пересечение нуля. mdlZeroCrossings функция, не mdlOutputs, указывает на переход к решателю.
/* Function: mdlOutputs =======================================================
* Abstract:
*
* A saturation is described by three equations
*
* (1) y = UpperLimit
* (2) y = u
* (3) y = LowerLimit
*
* When this block is used with a fixed-step solver or it has a noncontinuous
* sample time, the equations are used as it
*
* Now consider the case of this block being used with a variable-step solver
* and it has a continuous sample time. Solvers work best on smooth problems.
* In order for the solver to work without chattering, limit cycles, or
* similar problems, it is absolutely crucial that the same equation be used
* throughout the duration of a MajorTimeStep. To visualize this, consider
* the case of the Saturation block feeding an Integrator block.
*
* To implement this rule, the mode vector is used to specify the
* valid equation based on the following:
*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* The mode vector is changed only at the beginning of a MajorTimeStep.
*
* During a minor time step, the equation specified by the mode vector
* is used without question. Most of the time, the value of u will agree
* with the equation specified by the mode vector. However, sometimes u's
* value will indicate a different equation. Nonetheless, the equation
* specified by the mode vector must be used.
*
* When the mode and u indicate different equations, the corresponding
* calculations are not correct. However, this is not a problem. From
* the ZC function, the solver will know that an equation switch occurred
* in the middle of the last MajorTimeStep. The calculations for that
* time step will be discarded. The ZC function will help the solver
* find the exact instant at which the switch occurred. Using this knowledge,
* the length of the MajorTimeStep will be reduced so that only one equation
* is valid throughout the entire time step.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T numOutput = ssGetOutputPortWidth(S,0);
int_T iOutput;
/*
* Set index and increment for input signal, upper limit, and lower limit
* parameters so that each gives scalar expansion if needed.
*/
int_T uIdx = 0;
int_T uInc = ( ssGetInputPortWidth(S,0) > 1 );
const real_T *upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
int_T upperLimitInc = ( mxGetNumberOfElements( P_PAR_UPPER_LIMIT ) > 1 );
const real_T *lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
int_T lowerLimitInc = ( mxGetNumberOfElements( P_PAR_LOWER_LIMIT ) > 1 );
UNUSED_ARG(tid); /* not used in single tasking mode */
if (ssGetNumNonsampledZCs(S) == 0) {
/*
* This block is being used with a fixed-step solver or it has
* a noncontinuous sample time, so we always saturate.
*/
for (iOutput = 0; iOutput < numOutput; iOutput++) {
if (*uPtrs[uIdx] >= *upperLimit) {
*y++ = *upperLimit;
} else if (*uPtrs[uIdx] > *lowerLimit) {
*y++ = *uPtrs[uIdx];
} else {
*y++ = *lowerLimit;
}
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
uIdx += uInc;
}
} else {
/*
* This block is being used with a variable-step solver.
*/
int_T *mode = ssGetModeVector(S);
/*
* Specify indices for each equation.
*/
enum { UpperLimitEquation, NonLimitEquation, LowerLimitEquation };
/*
* Update the Mode Vector ONLY at the beginning of a MajorTimeStep
*/
if ( ssIsMajorTimeStep(S) ) {
/*
* Specify the mode, ie the valid equation for each output scalar.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( *uPtrs[uIdx] > *upperLimit ) {
/*
* Upper limit eq is valid.
*/
mode[iOutput] = UpperLimitEquation;
} else if ( *uPtrs[uIdx] < *lowerLimit ) {
/*
* Lower limit eq is valid.
*/
mode[iOutput] = LowerLimitEquation;
} else {
/*
* Nonlimit eq is valid.
*/
mode[iOutput] = NonLimitEquation;
}
/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
/*
* Reset index to input and limits.
*/
uIdx = 0;
upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
} /* end IsMajorTimeStep */
/*
* For both MinorTimeSteps and MajorTimeSteps calculate each scalar
* output using the equation specified by the mode vector.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( mode[iOutput] == UpperLimitEquation ) {
/*
* Upper limit eq.
*/
*y++ = *upperLimit;
} else if ( mode[iOutput] == LowerLimitEquation ) {
/*
* Lower limit eq.
*/
*y++ = *lowerLimit;
} else {
/*
* Nonlimit eq.
*/
*y++ = *uPtrs[uIdx];
}
/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}
} /* end mdlOutputs */
mdlZeroCrossings определяет, произошло ли пересечение нуля между предыдущим и текущим шагом времени. Способ получает указатель на входной сигнал, используя . Сравнение значения этого сигнала со значением верхнего и нижнего пределов насыщения определяет значения для элементов невыбранного вектора пересечения нуля. Если какой-либо элемент невыборного вектора пересечения нуля переключается с отрицательного на положительный или с положительного на отрицательный, происходит пересечение нуля. В случае пересечения нуля механизм Simulink изменяет размер шага и пересчитывает выходные данные, чтобы попытаться найти точное пересечение нуля.ssGetInputPortRealSignalPtrs
#define MDL_ZERO_CROSSINGS
#if defined(MDL_ZERO_CROSSINGS) && (defined(MATLAB_MEX_FILE) || defined(NRT))
/* Function: mdlZeroCrossings =================================================
* Abstract:
* This will only be called if the number of nonsampled zero crossings is
* greater than 0 which means this block has a continuous sample time and the
* model is using a variable-step solver.
*
* Calculate zero crossing (ZC) signals that help the solver find the
* exact instants at which equation switches occur:
*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* The key words are help find. There is no choice of a function that will
* direct the solver to the exact instant of the change. The solver will
* track the zero crossing signal and do a bisection style search for the
* exact instant of equation switch.
*
* There is generally one ZC signal for each pair of signals that can
* switch. The three equations above would break into two pairs (1)&(2)
* and (2)&(3). The possibility of a "long jump" from (1) to (3) does
* not need to be handled as a separate case. It is implicitly handled.
*
* When ZCs are calculated, the value is normally used twice. When it is
* first calculated, it is used as the end of the current time step. Later,
* it will be used as the beginning of the following step.
*
* The sign of the ZC signal always indicates an equation from the pair. For
* S-functions, which equation is associated with a positive ZC and which is
* associated with a negative ZC doesn't really matter. If the ZC is positive
* at the beginning and at the end of the time step, this implies that the
* "positive" equation was valid throughout the time step. Likewise, if the
* ZC is negative at the beginning and at the end of the time step, this
* implies that the "negative" equation was valid throughout the time step.
* Like any other nonlinear solver, this is not foolproof, but it is an
* excellent indicator. If the ZC has a different sign at the beginning and
* at the end of the time step, then a equation switch definitely occurred
* during the time step.
*
* Ideally, the ZC signal gives an estimate of when an equation switch
* occurred. For example, if the ZC signal is -2 at the beginning and +6 at
* the end, then this suggests that the switch occurred
* 25% = 100%*(-2)/(-2-(+6)) of the way into the time step. It will almost
* never be true that 25% is perfectly correct. There is no perfect choice
* for a ZC signal, but there are some good rules. First, choose the ZC
* signal to be continuous. Second, choose the ZC signal to give a monotonic
* measure of the "distance" to a signal switch; strictly monotonic is ideal.
*/
static void mdlZeroCrossings(SimStruct *S)
{
int_T iOutput;
int_T numOutput = ssGetOutputPortWidth(S,0);
real_T *zcSignals = ssGetNonsampledZCs(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/*
* Set index and increment for the input signal, upper limit, and lower
* limit parameters so that each gives scalar expansion if needed.
*/
int_T uIdx = 0;
int_T uInc = ( ssGetInputPortWidth(S,0) > 1 );
real_T *upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
int_T upperLimitInc = ( mxGetNumberOfElements( P_PAR_UPPER_LIMIT ) > 1 );
real_T *lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
int_T lowerLimitInc = ( mxGetNumberOfElements( P_PAR_LOWER_LIMIT ) > 1 );
/*
* For each output scalar, give the solver a measure of "how close things
* are" to an equation switch.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
/* The switch from eq (1) to eq (2)
*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
*
* is related to how close u is to UpperLimit. A ZC choice
* that is continuous, strictly monotonic, and is
* u - UpperLimit
* or it is negative.
*/
zcSignals[2*iOutput] = *uPtrs[uIdx] - *upperLimit;
/* The switch from eq (2) to eq (3)
*
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* is related to how close u is to LowerLimit. A ZC choice
* that is continuous, strictly monotonic, and is
* u - LowerLimit.
*/
zcSignals[2*iOutput+1] = *uPtrs[uIdx] - *lowerLimit;
/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}
#endif /* end mdlZeroCrossings */
S-функция завершается требуемым mdlTerminate функция. В этом примере функция пуста.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}
Требуемый трейлер 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
Примечание
mdlOutputs и mdlTerminate функции используют UNUSED_ARG чтобы указать, что входной аргумент, требуемый для обратного вызова, не используется. Этот дополнительный макрос определен в simstruc_types.h. Если используется, необходимо вызвать этот макрос один раз для каждого входного аргумента, который не используется при обратном вызове.
Пример S-функции stvctf.c демонстрирует изменяющуюся во времени функцию непрерывного переноса. Следующая модель Simulink использует эту S-функцию.
S-функция демонстрирует, как работать с решателями так, чтобы моделирование поддерживало согласованность, что означает, что блок поддерживает плавные и согласованные сигналы для интеграторов, хотя уравнения, которые интегрируются, изменяются.
S-функция stvctf.c начинается с #define операторы для имени и уровня S-функции вместе с #include заявление для simstruc.h заголовок. После этих операторов S-функция включает или определяет любые другие необходимые заголовки, данные и т.д. В этом примере определяются параметры числителя и знаменателя передаточной функции, которые вводятся в диалоговое окно S-функции. Комментарии в начале этой S-функции предоставляют дополнительную информацию о назначении векторов работы в этом примере.
/* * File : stvctf.c * Abstract: * Time Varying Continuous Transfer Function block * * This S-function implements a continuous time transfer function * whose transfer function polynomials are passed in via the input * vector. This is useful for continuous time adaptive control * applications. * * This S-function is also an example of how to use banks to avoid * problems with computing derivatives when a continuous output has * discontinuities. The consistency checker can be used to verify that * your S-function is correct with respect to always maintaining smooth * and consistent signals for the integrators. By consistent we mean that * two mdlOutputs calls at major time t and minor time t are always the * same. The consistency checker is enabled on the diagnostics page of the * Configuraion parameters dialog box. The update method of this S-function * modifies the coefficients of the transfer function, which cause the * output to "jump." To have the simulation work properly, we need to let * the solver know of these discontinuities by setting * ssSetSolverNeedsReset and then we need to use multiple banks of * coefficients so the coefficients used in the major time step output * and the minor time step outputs are the same. In the simulation loop * we have: * Loop: * o Output in major time step at time t * o Update in major time step at time t * o Integrate (minor time step): * o Consistency check: recompute outputs at time t and compare * with current outputs. * o Derivatives at time t * o One or more Output,Derivative evaluations at time t+k * where k <= step_size to be taken. * o Compute state, x * o t = t + step_size * End_Integrate * End_Loop * Another purpose of the consistency checker is to verify that when * the solver needs to try a smaller step_size, the recomputing of * the output and derivatives at time t doesn't change. Step size * reduction occurs when tolerances aren't met for the current step size. * The ideal ordering would be to update after integrate. To achieve * this we have two banks of coefficients. And the use of the new * coefficients, which were computed in update, is delayed until after * the integrate phase is complete. * * This block has multiple sample times and will not work correctly * in a multitasking environment. It is designed to be used in * a single tasking (or variable step) simulation environment. * Because this block accesses the input signal in both tasks, * it cannot specify the sample times of the input and output ports * (SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED). * * See simulink/src/sfuntmpl_doc.c. * * Copyright 1990-7 The MathWorks, Inc. */ #define S_FUNCTION_NAME stvctf #define S_FUNCTION_LEVEL 2 #include "simstruc.h" /* * Defines for easy access to the numerator and denominator polynomials * parameters */ #define NUM(S) ssGetSFcnParam(S, 0) #define DEN(S) ssGetSFcnParam(S, 1) #define TS(S) ssGetSFcnParam(S, 2) #define NPARAMS 3
Эта S-функция реализует mdlCheckParameters способ проверки достоверности параметров диалогового окна S-функции. Поскольку этот метод необязателен, #define оператор предшествует ему. #if defined проверяет, что эта функция скомпилирована как файл MEX, а не для использования с продуктом Simulink Coder. Тело функции выполняет основные проверки, чтобы убедиться, что пользователь ввел действительные векторы для числителя и знаменателя, и что знаменатель имеет более высокий порядок, чем числитель. Если проверка параметров завершается неуспешно, S-функция выдает ошибку.
#define MDL_CHECK_PARAMETERS
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/* Function: mdlCheckParameters =============================================
* Abstract:
* Validate our parameters to verify:
* o The numerator must be of a lower order than the denominator.
* o The sample time must be a real positive nonzero value.
*/
static void mdlCheckParameters(SimStruct *S)
{
int_T i;
for (i = 0; i < NPARAMS; i++) {
real_T *pr;
int_T el;
int_T nEls;
if (mxIsEmpty( ssGetSFcnParam(S,i)) ||
mxIsSparse( ssGetSFcnParam(S,i)) ||
mxIsComplex( ssGetSFcnParam(S,i)) ||
!mxIsNumeric( ssGetSFcnParam(S,i)) ) {
ssSetErrorStatus(S,"Parameters must be real finite vectors");
return;
}
pr = mxGetPr(ssGetSFcnParam(S,i));
nEls = mxGetNumberOfElements(ssGetSFcnParam(S,i));
for (el = 0; el < nEls; el++) {
if (!mxIsFinite(pr[el])) {
ssSetErrorStatus(S,"Parameters must be real finite vectors");
return;
}
}
}
if (mxGetNumberOfElements(NUM(S)) > mxGetNumberOfElements(DEN(S)) &&
mxGetNumberOfElements(DEN(S)) > 0 && *mxGetPr(DEN(S)) != 0.0) {
ssSetErrorStatus(S,"The denominator must be of higher order than "
"the numerator, nonempty and with first "
"element nonzero");
return;
}
/* xxx verify finite */
if (mxGetNumberOfElements(TS(S)) != 1 || mxGetPr(TS(S))[0] <= 0.0) {
ssSetErrorStatus(S,"Invalid sample time specified");
return;
}
}
#endif /* MDL_CHECK_PARAMETERS */
Требуемый метод S-функции mdlInitializeSizes затем устанавливает следующие характеристики S-функции.
устанавливает количество ожидаемых параметров диалогового окна S-функции равным трем, как определено ранее в переменной ssSetNumSFcnParamsNPARAMS.
Если этот метод компилируется как файл MEX, определяет, сколько параметров пользователь ввел в диалоговое окно S-функции. Если количество пользовательских параметров соответствует числу, возвращенному ssGetSFcnParamsCount, метод вызывает ssGetNumSFcnParamsmdlCheckParameters для проверки достоверности введенных пользователем данных. В противном случае S-функция ошибается.
Если проверка параметров проходит успешно, S-функция определяет количество непрерывных и дискретных состояний с помощью и ssSetNumContStatesсоответственно. Этот пример не имеет дискретных состояний и устанавливает число непрерывных состояний на основе числа коэффициентов в знаменателе передаточной функции.ssSetNumDiscStates
Далее, указывает, что S-функция имеет один входной порт и устанавливает его ширину в один плюс удвоенную длину знаменателя с помощью ssSetNumInputPorts. Метод использует значение, предоставленное третьим диалоговым параметром S-функции, в качестве времени выборки входного порта. Этот параметр указывает скорость изменения передаточной функции во время моделирования. S-функция указывает, что входной порт имеет прямой канал, передавая значение ssSetInputPortWidth1 кому .ssSetInputPortDirectFeedThrough
указывает, что S-функция имеет один выходной порт. Метод использует ssSetNumOutputPorts для установки ширины этого выходного порта, ssSetOutputPortWidth чтобы указать, что выходной порт имеет непрерывное время выборки, и ssSetOutputPortSampleTime для установки нулевого значения времени смещения.ssSetOutputPortOffsetTime
затем инициализирует два раза выборки, которые ssSetNumSampleTimesmdlInitializeSampleTimes функция настраивается позже.
Метод передает значение, в четыре раза превышающее число коэффициентов знаменателя для задания длины рабочего вектора с плавающей запятой. ssSetNumRWork затем устанавливает длину целого рабочего вектора равной двум. Векторы RWork хранят два банка коэффициентов передаточной функции, в то время как вектор IWork указывает, какой банк в векторе RWork используется в настоящее время. S-функция устанавливает нулевую длину всех остальных рабочих векторов. Эти строки можно опустить, поскольку значение по умолчанию для этих макросов равно нулю. Однако для ясности S-функция явно задает количество рабочих векторов.ssSetNumIWork
Наконец, задает любые применимые опции. В этом случае ssSetOptionsSS_OPTION_EXCEPTION_FREE_CODE предусматривает, что код не содержит исключений.
mdlInitializeSizes функция для этого примера показана ниже.
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
int_T nContStates;
int_T nCoeffs;
/* See sfuntmpl_doc.c for more details on the macros below. */
ssSetNumSFcnParams(S, NPARAMS); /* Number of expected parameters. */
#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch reported by the Simulink engine*/
}
#endif
/*
* Define the characteristics of the block:
*
* Number of continuous states: length of denominator - 1
* Inputs port width 2 * (NumContStates+1) + 1
* Output port width 1
* DirectFeedThrough: 0 (Although this should be computed.
* We'll assume coefficients entered
* are strictly proper).
* Number of sample times: 2 (continuous and discrete)
* Number of Real work elements: 4*NumCoeffs
* (Two banks for num and den coeff's:
* NumBank0Coeffs
* DenBank0Coeffs
* NumBank1Coeffs
* DenBank1Coeffs)
* Number of Integer work elements: 2 (indicator of active bank 0 or 1
* and flag to indicate when banks
* have been updated).
*
* The number of inputs arises from the following:
* o 1 input (u)
* o the numerator and denominator polynomials each have NumContStates+1
* coefficients
*/
nCoeffs = mxGetNumberOfElements(DEN(S));
nContStates = nCoeffs - 1;
ssSetNumContStates(S, nContStates);
ssSetNumDiscStates(S, 0);
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, 1 + (2*nCoeffs));
ssSetInputPortDirectFeedThrough(S, 0, 0);
ssSetInputPortSampleTime(S, 0, mxGetPr(TS(S))[0]);
ssSetInputPortOffsetTime(S, 0, 0);
if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, 1);
ssSetOutputPortSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOutputPortOffsetTime(S, 0, 0);
ssSetNumSampleTimes(S, 2);
ssSetNumRWork(S, 4 * nCoeffs);
ssSetNumIWork(S, 2);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
/* Take care when specifying exception free code - see sfuntmpl_doc.c */
ssSetOptions(S, (SS_OPTION_EXCEPTION_FREE_CODE));
} /* end mdlInitializeSizes */
Требуемый метод S-функции mdlInitializeSampleTimes определяет частоты дискретизации S-функции. Первый вызов указывает, что первая частота дискретизации непрерывна, и последующий вызов ssSetSampleTime устанавливает нулевое смещение. Второй вызов этой пары макросов устанавливает второе время выборки на значение третьего параметра S-функции со смещением, равным нулю. Вызов ssSetOffsetTimessSetModelReferenceSampleTimeDefaultInheritance сообщает решателю использовать правило по умолчанию, чтобы определить, могут ли ссылочные модели, содержащие эту S-функцию, наследовать свое время выборки от родительской модели.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* This function is used to specify the sample time(s) for the
* S-function. This S-function has two sample times. The
* first, a continuous sample time, is used for the input to the
* transfer function, u. The second, a discrete sample time
* provided by the user, defines the rate at which the transfer
* function coefficients are updated.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
/*
* the first sample time, continuous
*/
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
/*
* the second, discrete sample time, is user provided
*/
ssSetSampleTime(S, 1, mxGetPr(TS(S))[0]);
ssSetOffsetTime(S, 1, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */
Необязательный метод S-функции mdlInitializeConditions инициализирует вектор непрерывного состояния и векторы начального числителя и знаменателя. #define прежде чем этот метод потребуется для вызова этой функции механизмом Simulink. Функция инициализирует непрерывные состояния до нуля. Коэффициенты числителя и знаменателя инициализируются из первых двух параметров S-функции, нормированных первым коэффициентом знаменателя. Функция устанавливает значение, сохраненное в векторе IWork, равным нулю, чтобы указать, что первый банк коэффициентов числителя и знаменателя, сохраненный в векторе RWork, в настоящее время используется.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ==========================================
* Abstract:
* Initialize the states, numerator and denominator coefficients.
*/
static void mdlInitializeConditions(SimStruct *S)
{
int_T i;
int_T nContStates = ssGetNumContStates(S);
real_T *x0 = ssGetContStates(S);
int_T nCoeffs = nContStates + 1;
real_T *numBank0 = ssGetRWork(S);
real_T *denBank0 = numBank0 + nCoeffs;
int_T *activeBank = ssGetIWork(S);
/*
* The continuous states are all initialized to zero.
*/
for (i = 0; i < nContStates; i++) {
x0[i] = 0.0;
numBank0[i] = 0.0;
denBank0[i] = 0.0;
}
numBank0[nContStates] = 0.0;
denBank0[nContStates] = 0.0;
/*
* Set up the initial numerator and denominator.
*/
{
const real_T *numParam = mxGetPr(NUM(S));
int numParamLen = mxGetNumberOfElements(NUM(S));
const real_T *denParam = mxGetPr(DEN(S));
int denParamLen = mxGetNumberOfElements(DEN(S));
real_T den0 = denParam[0];
for (i = 0; i < denParamLen; i++) {
denBank0[i] = denParam[i] / den0;
}
for (i = 0; i < numParamLen; i++) {
numBank0[i] = numParam[i] / den0;
}
}
/*
* Normalize if this transfer function has direct feedthrough.
*/
for (i = 1; i < nCoeffs; i++) {
numBank0[i] -= denBank0[i]*numBank0[0];
}
/*
* Indicate bank0 is active (i.e. bank1 is oldest).
*/
*activeBank = 0;
} /* end mdlInitializeConditions */
mdlOutputs функция вычисляет выходные сигналы S-функции, когда S-функция моделируется в непрерывной задаче, т.е. является ssIsContinuousTasktrue. Если моделирование также находится на основном шаге времени, mdlOutputs проверяет необходимость обновления числителя и коэффициентов знаменателя, как указано переключателем в активном банке, сохраненном в векторе IWork. Как на больших, так и на второстепенных временных этапах S-функция вычисляет выходной сигнал с использованием числительных коэффициентов, хранящихся в активном блоке.
/* Function: mdlOutputs =======================================================
* Abstract:
* The outputs for this block are computed by using a controllable state-
* space representation of the transfer function.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
if (ssIsContinuousTask(S,tid)) {
int i;
real_T *num;
int nContStates = ssGetNumContStates(S);
real_T *x = ssGetContStates(S);
int_T nCoeffs = nContStates + 1;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T *activeBank = ssGetIWork(S);
/*
* Switch banks because we've updated them in mdlUpdate and we're no
* longer in a minor time step.
*/
if (ssIsMajorTimeStep(S)) {
int_T *banksUpdated = ssGetIWork(S) + 1;
if (*banksUpdated) {
*activeBank = !(*activeBank);
*banksUpdated = 0;
/*
* Need to tell the solvers that the derivatives are no
* longer valid.
*/
ssSetSolverNeedsReset(S);
}
}
num = ssGetRWork(S) + (*activeBank) * (2*nCoeffs);
/*
* The continuous system is evaluated using a controllable state space
* representation of the transfer function. This implies that the
* output of the system is equal to:
*
* y(t) = Cx(t) + Du(t)
* = [ b1 b2 ... bn]x(t) + b0u(t)
*
* where b0, b1, b2, ... are the coefficients of the numerator
* polynomial:
*
* B(s) = b0 s^n + b1 s^n-1 + b2 s^n-2 + ... + bn-1 s + bn
*/
*y = *num++ * (*uPtrs[0]);
for (i = 0; i < nContStates; i++) {
*y += *num++ * *x++;
}
}
} /* end mdlOutputs */
Хотя этот пример не имеет дискретных состояний, метод по-прежнему реализует mdlUpdate для обновления коэффициентов передаточной функции на каждом основном временном шаге. Поскольку этот метод необязателен, #define оператор предшествует ему. Метод использует для получения указателя на входной сигнал. Значения входного сигнала становятся новыми коэффициентами передаточной функции, которые S-функция сохраняет в банке неактивного вектора RWork. Когда ssGetInputPortRealSignalPtrsmdlOutputs функция позже вызывается на этом основном временном шаге, она обновляет активный банк, чтобы быть этим обновленным банком коэффициентов.
#define MDL_UPDATE
/* Function: mdlUpdate ========================================================
* Abstract:
* Every time through the simulation loop, update the
* transfer function coefficients. Here we update the oldest bank.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
if (ssIsSampleHit(S, 1, tid)) {
int_T i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
int_T uIdx = 1;/*1st coeff is after signal input*/
int_T nContStates = ssGetNumContStates(S);
int_T nCoeffs = nContStates + 1;
int_T bankToUpdate = !ssGetIWork(S)[0];
real_T *num = ssGetRWork(S)+bankToUpdate*2*nCoeffs;
real_T *den = num + nCoeffs;
real_T den0;
int_T allZero;
/*
* Get the first denominator coefficient. It will be used
* for normalizing the numerator and denominator coefficients.
*
* If all inputs are zero, we probably could have unconnected
* inputs, so use the parameter as the first denominator coefficient.
*/
den0 = *uPtrs[uIdx+nCoeffs];
if (den0 == 0.0) {
den0 = mxGetPr(DEN(S))[0];
}
/*
* Grab the numerator.
*/
allZero = 1;
for (i = 0; (i < nCoeffs) && allZero; i++) {
allZero &= *uPtrs[uIdx+i] == 0.0;
}
if (allZero) { /* if numerator is all zero */
const real_T *numParam = mxGetPr(NUM(S));
int_T numParamLen = mxGetNumberOfElements(NUM(S));
/*
* Move the input to the denominator input and
* get the denominator from the input parameter.
*/
uIdx += nCoeffs;
num += nCoeffs - numParamLen;
for (i = 0; i < numParamLen; i++) {
*num++ = *numParam++ / den0;
}
} else {
for (i = 0; i < nCoeffs; i++) {
*num++ = *uPtrs[uIdx++] / den0;
}
}
/*
* Grab the denominator.
*/
allZero = 1;
for (i = 0; (i < nCoeffs) && allZero; i++) {
allZero &= *uPtrs[uIdx+i] == 0.0;
}
if (allZero) { /* If denominator is all zero. */
const real_T *denParam = mxGetPr(DEN(S));
int_T denParamLen = mxGetNumberOfElements(DEN(S));
den0 = denParam[0];
for (i = 0; i < denParamLen; i++) {
*den++ = *denParam++ / den0;
}
} else {
for (i = 0; i < nCoeffs; i++) {
*den++ = *uPtrs[uIdx++] / den0;
}
}
/*
* Normalize if this transfer function has direct feedthrough.
*/
num = ssGetRWork(S) + bankToUpdate*2*nCoeffs;
den = num + nCoeffs;
for (i = 1; i < nCoeffs; i++) {
num[i] -= den[i]*num[0];
}
/*
* Indicate oldest bank has been updated.
*/
ssGetIWork(S)[1] = 1;
}
} /* end mdlUpdate */
mdlDerivatives функция вычисляет производные непрерывного состояния. Функция использует коэффициенты из активного банка для решения управляемого представления состояния-пространства передаточной функции.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives ===================================================
* Abstract:
* The derivatives for this block are computed by using a controllable
* state-space representation of the transfer function.
*/
static void mdlDerivatives(SimStruct *S)
{
int_T i;
int_T nContStates = ssGetNumContStates(S);
real_T *x = ssGetContStates(S);
real_T *dx = ssGetdX(S);
int_T nCoeffs = nContStates + 1;
int_T activeBank = ssGetIWork(S)[0];
const real_T *num = ssGetRWork(S) + activeBank*(2*nCoeffs);
const real_T *den = num + nCoeffs;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/*
* The continuous system is evaluated using a controllable state-space
* representation of the transfer function. This implies that the
* next continuous states are computed using:
*
* dx = Ax(t) + Bu(t)
* = [-a1 -a2 ... -an] [x1(t)] + [u(t)]
* [ 1 0 ... 0] [x2(t)] + [0]
* [ 0 1 ... 0] [x3(t)] + [0]
* [ . . ... .] . + .
* [ . . ... .] . + .
* [ . . ... .] . + .
* [ 0 0 ... 1 0] [xn(t)] + [0]
*
* where a1, a2, ... are the coefficients of the numerator polynomial:
*
* A(s) = s^n + a1 s^n-1 + a2 s^n-2 + ... + an-1 s + an
*/
dx[0] = -den[1] * x[0] + *uPtrs[0];
for (i = 1; i < nContStates; i++) {
dx[i] = x[i-1];
dx[0] -= den[i+1] * x[i];
}
} /* end mdlDerivatives */
Необходимое mdlTerminate выполняет любые действия, такие как освобождение памяти, необходимые в конце моделирования. В этом примере функция пуста.
/* Function: mdlTerminate =====================================================
* Abstract:
* Called when the simulation is terminated.
* For this block, there are no end of simulation tasks.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
} /* end mdlTerminate */
Требуемый трейлер 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
Примечание
mdlTerminate функция использует UNUSED_ARG чтобы указать, что входной аргумент, требуемый для обратного вызова, не используется. Этот дополнительный макрос определен в simstruc_types.h. Если используется, необходимо вызвать этот макрос один раз для каждого входного аргумента, который не используется при обратном вызове.
mdlInitializeSizes | mdlTerminate | mdlUpdate