S-функции моделируют пересечение нулей с использованием рабочего вектора режима (или вектора DWork, сконфигурированного как вектор режима) и вектора непрерывного пересечения нулей. Независимо от того, использует ли S-функция векторы mode или DWork, концепция и реализация одинаковы. Пример использования векторов DWork для моделирования пересечений нуля см. в разделе Вектор режима DWork в разделе «Использование рабочих векторов». В остальной части этого раздела для моделирования пересечений нулей используются векторы режимов.
Элементы вектора режима являются целочисленными значениями. Количество векторных элементов режима задается в mdlInitializeSizes, использование ssSetNumModes(S,num). Затем можно получить доступ к вектору режима с помощью ssGetModeVector. Значения вектора режима определяют, как mdlOutputs процедура работает, когда решатели осуществляют самонаведение на пересечениях нулей. Решатели Simulink ® отслеживают переходы нуля или события состояния (т.е. разрывы в первых производных) некоторого сигнала, обычно функции входа в S-функцию, путем просмотра непрерывных переходов нуля. Регистрация количества непрерывных нулевых пересечений вmdlInitializeSizes, использование ssSetNumNonsampledZCs(S, num), затем включить mdlZeroCrossings подпрограмма для расчета непрерывных пересечений нуля. S-функция sfun_zc_sat.c содержит пример пересечения нуля. В оставшейся части этого раздела описаны части этой S-функции, которые относятся к обнаружению пересечения нуля. Полное описание этого примера см. в разделе Обнаружение пересечения нулей.
Во-первых, mdlInitializeSizes задает размеры векторов режима и непрерывного пересечения нулей с использованием следующих строк кода.
ssSetNumModes(S, DYNAMICALLY_SIZED); ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED);
Поскольку количество режимов и непрерывных переходов через ноль динамически изменяется, mdlSetWorkWidths необходимо инициализировать фактический размер этих векторов. В этом примере, показанном ниже, имеется один вектор режима для каждого выходного элемента и два непрерывных пересечения нуля для каждого режима. В общем, количество непрерывных переходов через ноль, необходимых для каждого режима, зависит от количества событий, которые необходимо обнаружить. В этом случае каждый выход (режим) должен обнаруживать, когда он достигает верхней или нижней границы, следовательно, два непрерывных пересечения нуля на режим.
static void mdlSetWorkWidths(SimStruct *S)
{
int nModes;
int nNonsampledZCs;
nModes = numOutput;
nNonsampledZCs = 2 * numOutput;
ssSetNumModes(S,nModes);
ssSetNumNonsampledZCs(S,nNonsampledZCs);
}Далее, mdlOutputs определяет, в каком режиме выполняется моделирование в начале каждого основного временного шага. Метод сохраняет эту информацию в векторе режима так, чтобы она была доступна при вычислении выходных сигналов на основном и вспомогательном временных этапах.
/* Get the mode vector */
int_T *mode = ssGetModeVector(S);
/* Specify three possible mode values.*/
enum { UpperLimitEquation, NonLimitEquation, LowerLimitEquation };
/* Update the mode vector at the beginning of a major time step */
if ( ssIsMajorTimeStep(S) ) {
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( *uPtrs[uIdx] > *upperLimit ) {
/* Upper limit is reached. */
mode[iOutput] = UpperLimitEquation;
} else if ( *uPtrs[uIdx] < *lowerLimit ) {
/* Lower limit is reached. */
mode[iOutput] = LowerLimitEquation;
} else {
/* Output is not limited. */
mode[iOutput] = NonLimitEquation;
}
/* Adjust indices to give scalar expansion. */
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 */Выходные расчеты в mdlOutputs выполняются на основе значений, хранящихся в векторе режима.
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( mode[iOutput] == UpperLimitEquation ) {
/* Output upper limit. */
*y++ = *upperLimit;
} else if ( mode[iOutput] == LowerLimitEquation ) {
/* Output lower limit. */
*y++ = *lowerLimit;
} else {
/* Output is equal to input */
*y++ = *uPtrs[uIdx];
}
После расчета выходных данных модуль Simulink вызывает mdlZeroCrossings чтобы определить, произошло ли пересечение нуля. Пересечение нуля обнаруживается, если любой элемент вектора непрерывного пересечения нуля переключается с отрицательного на положительный или с положительного на отрицательный. Если это происходит, моделирование изменяет размер шага и пересчитывает выходные данные, чтобы найти точное пересечение нуля. Для этого примера значения для векторов непрерывного пересечения нуля вычисляются, как показано ниже.
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 );
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 );
/*Check if the input has crossed an upper or lower limit */
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
zcSignals[2*iOutput] = *uPtrs[uIdx] - *upperLimit;
zcSignals[2*iOutput+1] = *uPtrs[uIdx] - *lowerLimit;
/* Adjust indices to give scalar expansion if needed */
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}