Нулевые пересечения модели S-функций с помощью режима работают вектор (или вектор DWork, сконфигурированный как вектор режима) и непрерывный вектор пересечения нулем. Использует ли S-функция режим или векторы DWork, концепцией и реализацией является то же самое. Для примера с помощью векторов DWork для образцовых нулевых пересечений см. Вектор Режима DWork в разделе “Using Work Vectors”. Остаток от этого раздела использует векторы режима для образцовых нулевых пересечений.
Элементы вектора режима являются целочисленными значениями. Вы задаете количество элементов вектора режима в mdlInitializeSizes
, с помощью ssSetNumModes(S,num)
. Можно затем получить доступ к вектору режима использование ssGetModeVector
. Значения вектора режима определяют, как стандартная программа mdlOutputs
действует, когда решатели концентрируются на нулевых пересечениях. Решатели Simulink® отслеживают нулевые пересечения или утверждают события (i. e., разрывы в первых производных) некоторого сигнала, обычно функция входного параметра к вашей 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; } }