И.В.Черных
|
S-функции, созданные с помощью S-Function Builder обладают одним существенным недостатком. В большинстве случаев при расчете переменных состояния, выходных сигналов либо производных непрерывных переменных состояния должны использоваться некоторые ранее вычисленные константы. В задаче моделирования двигателя постоянного тока - это элементы матриц A, B, С уравнений пространства-состояния, рассчитываемые через параметры двигателя (сопротивление, индуктивность и т.п.). В примере на языке MATLAB (см. п. 16.7.6) расчет матриц выполняется в методе mdlInitializeSizes, благодаря чему этот расчет производится только один раз - на этапе инициализации модели. Далее рассчитанные матрицы только используются для вычисления производных переменных состояния в методе mdlDerivatives и выходных сигналов в методе mdlOutputs. Такая компоновка S-функции обеспечивает наибольшую скорость вычислений. К сожалению, в S-Function Builder отсутствует возможность ввести фрагмент кода на вкладке Initialization, а также скрыты заголовки методов, что делает невозможным добавление новых параметров, которые могли бы передаваться в эти методы. Такая ситуация приводит к тому, что константы, которые можно было бы вычислить всего один раз и, затем лишь использовать, вычисляются вновь и вновь на каждом шаге расчета (в примере с двигателем постоянного тока п.16.6 - это A00, A01, B00 и т.д.). И, если в относительно простых моделях с эти еще можно смириться, то для сложных моделей с большим объемом предварительных вычислений такая ситуация приведет к существенному замедлению процесса расчета.
Выходом из сложившейся ситуации может явиться редактирование автоматически сгенерированного кода S-функции. Редактирование должно обеспечить вычисление констант один раз на этапе инициализации и передачу их в нужные методы. В рассматриваемом примере модели двигателя постоянного тока на этапе инициализации должны вычисляться константы A00, A01, B00 и т.д., а затем, передаваться в методы для расчета производных и выходных переменных. Для реализации поставленной задачи следует проанализировать полученные с помощью S-Function Builder файлы DPT_Sfunc_1_C.c и DPT_Sfunc_1_C_wrapper.c. Первый файл содержит исходный текст S-функции, а второй текст методов этой S-функции.
Текст файла DPT_Sfunc_1_C.c приведен ниже:
/* * File: DPT_Sfunc_1_C.c * * * * --- THIS FILE GENERATED BY S-FUNCTION BUILDER: BASIC, 1.0 --- * * This file is an S-function produced by the Basic S-Function * Builder which only recognizes certain fields. Changes made * outside these fields will be lost the next time the block is * used to load, edit, and resave this file. This file will be overwritten * by the S-function Builder block. If you want to edit this file by hand, * you must change it only in the area defined as: * * %%%-SFUNWIZ_defines_Changes_BEGIN * #define NAME 'replacement text' * %%% SFUNWIZ_defines_Changes_END * * DO NOT change NAME--Change te 'replacement text' only. * * For better compatibility with the Real-Time Workshop, the * "wrapper" S-function technique is used. This is discussed * in the Real-Time Workshop User's Manual in the Chapter titled, * "Wrapper S-functions". * * ------------------------------------------------------------------------ *| See matlabroot/simulink/src/sfuntmpl_doc.c for a more detailed template | * ------------------------------------------------------------------------ * Created: Sun Mar 30 13:07:10 2003 * * */ #define S_FUNCTION_NAME DPT_Sfunc_1_C #define S_FUNCTION_LEVEL 2 /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ /* %%%-SFUNWIZ_defines_Changes_BEGIN --- EDIT HERE TO _END */ #define NUM_INPUTS 1 #define INPUT_0_WIDTH 2 #define INPUT_0_FEEDTHROUGH 0 #define NUM_OUTPUTS 1 #define OUTPUT_0_WIDTH 2 #define NPARAMS 6 #define SAMPLE_TIME_0 INHERITED_SAMPLE_TIME #define NUM_DISC_STATES 0 #define DISC_STATES_IC [0] #define NUM_CONT_STATES 2 #define CONT_STATES_IC [0,0] #define SFUNWIZ_GENERATE_TLC 1 #define SOURCEFILES "//my_lib.lib" #define PANELINDEX 5 #define SFUNWIZ_REVISION 1.0 /* %%%-SFUNWIZ_defines_Changes_END --- EDIT HERE TO _BEGIN */ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ #include "simstruc.h" #define PARAM_DEF0(S) ssGetSFcnParam(S, 0) #define PARAM_DEF1(S) ssGetSFcnParam(S, 1) #define PARAM_DEF2(S) ssGetSFcnParam(S, 2) #define PARAM_DEF3(S) ssGetSFcnParam(S, 3) #define PARAM_DEF4(S) ssGetSFcnParam(S, 4) #define PARAM_DEF5(S) ssGetSFcnParam(S, 5) extern void DPT_Sfunc_1_C_Outputs_wrapper(const real_T *u, real_T *y, const real_T *xC, const real_T *param0, const int_T p_width0, const real_T *param1,
const int_T p_width1, const real_T *param2, const int_T p_width2, const real_T *param3, const
int_T p_width3, const real_T *param4, const int_T p_width4, const real_T *param5, const int_T
p_width5); extern void DPT_Sfunc_1_C_Update_wrapper(const real_T *u, const real_T *y, const real_T *param0, const int_T p_width0,const real_T *param1, const
int_T p_width1,const real_T *param2, const int_T p_width2,const real_T *param3, const int_T
p_width3,const real_T *param4, const int_T p_width4, const real_T *param5, const int_T p_width5); extern void DPT_Sfunc_1_C_Derivatives_wrapper(const real_T *u, const real_T *y, real_T *dx, real_T *xC, const real_T *param0, const int_T p_width0,const real_T *param1, const
int_T p_width1,const real_T *param2, const int_T p_width2,const real_T *param3, const int_T
p_width3,const real_T *param4, const int_T p_width4, const real_T *param5, const int_T p_width5); /*====================* * S-function methods * *====================*/ #define MDL_CHECK_PARAMETERS #if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE) /* Function: mdlCheckParameters========================================== * Abstract: * Validate our parameters to verify they are okay. */ static void mdlCheckParameters(SimStruct *S) { int i; bool validParam = false; /* All parameters must be scalar */ for (i = 0; i < ssGetSFcnParamsCount(S); i++) { const mxArray *pVal = ssGetSFcnParam(S,i); if ( !mxIsNumeric(pVal) || !mxIsDouble(pVal) || mxIsLogical(pVal) || mxIsComplex(pVal) || mxIsSparse(pVal) || !mxIsFinite(mxGetPr(pVal)[0])) { validParam = true; break; } } if (validParam) { ssSetErrorStatus(S,"All parameters must be a scalar or vectors"); return; } } #endif /* MDL_CHECK_PARAMETERS */ /* Function: mdlInitializeSizes =========================================== * Abstract: * Setup sizes of the various vectors. */ static void mdlInitializeSizes(SimStruct *S) { DECL_AND_INIT_DIMSINFO(inputDimsInfo); DECL_AND_INIT_DIMSINFO(outputDimsInfo); 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 will be reported by Simulink */ } #endif ssSetNumContStates(S, NUM_CONT_STATES); ssSetNumDiscStates(S, NUM_DISC_STATES); if (!ssSetNumInputPorts(S, 1)) return; inputDimsInfo.width = INPUT_0_WIDTH; ssSetInputPortDimensionInfo(S, 0, &inputDimsInfo); ssSetInputPortFrameData(S, 0, FRAME_INHERITED); ssSetInputPortDirectFeedThrough(S, 0, INPUT_0_FEEDTHROUGH); ssSetInputPortRequiredContiguous(S, 0, 1); /*direct input signal access*/ if (!ssSetNumOutputPorts(S,1)) return; outputDimsInfo.width = OUTPUT_0_WIDTH; ssSetOutputPortDimensionInfo(S, 0, &outputDimsInfo); ssSetOutputPortFrameData(S, 0, FRAME_INHERITED); 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 | SS_OPTION_USE_TLC_WITH_ACCELERATOR); } /* Function: mdlInitializeSampleTimes ====================================== * Abstract: * Specifiy the sample time. */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, SAMPLE_TIME_0); ssSetOffsetTime(S, 0, 0.0); } #define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions ====================================== * Abstract: * Initialize the states */ static void mdlInitializeConditions(SimStruct *S) { real_T *xC = ssGetContStates(S); xC[0] = 0; xC[1] = 0; } /* Function: mdlOutputs =================================================== * */ static void mdlOutputs(SimStruct *S, int_T tid) { const real_T *u = (const real_T*) ssGetInputPortSignal(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); const real_T *xC = ssGetContStates(S); const real_T *param0 = mxGetPr(PARAM_DEF0(S)); const real_T *param1 = mxGetPr(PARAM_DEF1(S)); const real_T *param2 = mxGetPr(PARAM_DEF2(S)); const real_T *param3 = mxGetPr(PARAM_DEF3(S)); const real_T *param4 = mxGetPr(PARAM_DEF4(S)); const real_T *param5 = mxGetPr(PARAM_DEF5(S)); const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); const int_T p_width1 = mxGetNumberOfElements(PARAM_DEF1(S)); const int_T p_width2 = mxGetNumberOfElements(PARAM_DEF2(S)); const int_T p_width3 = mxGetNumberOfElements(PARAM_DEF3(S)); const int_T p_width4 = mxGetNumberOfElements(PARAM_DEF4(S)); const int_T p_width5 = mxGetNumberOfElements(PARAM_DEF5(S)); DPT_Sfunc_1_C_Outputs_wrapper(u, y, xC, param0, p_width0, param1, p_width1, param2, p_width2,
param3, p_width3, param4, p_width4, param5, p_width5); } #undef MDL_UPDATE /* Change to #define to use the function */ #if defined(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) { const real_T *u = (const real_T *) ssGetInputPortSignal(S,0); real_T *xD = ssGetDiscStates(S); const real_T *y = ssGetOutputPortSignal(S,0); const real_T *param0 = mxGetPr(PARAM_DEF0(S)); const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); const real_T *param1 = mxGetPr(PARAM_DEF1(S)); const int_T p_width1 = mxGetNumberOfElements(PARAM_DEF1(S)); const real_T *param2 = mxGetPr(PARAM_DEF2(S)); const int_T p_width2 = mxGetNumberOfElements(PARAM_DEF2(S)); const real_T *param3 = mxGetPr(PARAM_DEF3(S)); const int_T p_width3 = mxGetNumberOfElements(PARAM_DEF3(S)); const real_T *param4 = mxGetPr(PARAM_DEF4(S)); const int_T p_width4 = mxGetNumberOfElements(PARAM_DEF4(S)); const real_T *param5 = mxGetPr(PARAM_DEF5(S)); const int_T p_width5 = mxGetNumberOfElements(PARAM_DEF5(S)); DPT_Sfunc_1_C_Update_wrapper(u, y, param0, p_width0, param1, p_width1, param2, p_width2, param3,
p_width3, param4, p_width4, param5, p_width5); } #endif /* MDL_UPDATE */ #define MDL_DERIVATIVES /* Change to #undef to remove function */ #if defined(MDL_DERIVATIVES) /* Function: mdlDerivatives ============================================== * Abstract: * In this function, you compute the S-function block's derivatives. * The derivatives are placed in the derivative vector, ssGetdX(S). */ static void mdlDerivatives(SimStruct *S) { const real_T *u = (const real_T *) ssGetInputPortSignal(S,0); real_T *dx = ssGetdX(S); real_T *xC = ssGetContStates(S); const real_T *y = ssGetOutputPortSignal(S,0); const real_T *param0 = mxGetPr(PARAM_DEF0(S)); const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); const real_T *param1 = mxGetPr(PARAM_DEF1(S)); const int_T p_width1 = mxGetNumberOfElements(PARAM_DEF1(S)); const real_T *param2 = mxGetPr(PARAM_DEF2(S)); const int_T p_width2 = mxGetNumberOfElements(PARAM_DEF2(S)); const real_T *param3 = mxGetPr(PARAM_DEF3(S)); const int_T p_width3 = mxGetNumberOfElements(PARAM_DEF3(S)); const real_T *param4 = mxGetPr(PARAM_DEF4(S)); const int_T p_width4 = mxGetNumberOfElements(PARAM_DEF4(S)); const real_T *param5 = mxGetPr(PARAM_DEF5(S)); const int_T p_width5 = mxGetNumberOfElements(PARAM_DEF5(S)); DPT_Sfunc_1_C_Derivatives_wrapper(u, y, dx, xC, param0, p_width0, param1, p_width1, param2, p_width2,
param3, p_width3, param4, p_width4, param5, p_width5); } #endif /* MDL_DERIVATIVES */ /* Function: mdlTerminate ================================================== * Abstract: * No termination needed, but we are required to have this routine. */ static void mdlTerminate(SimStruct *S) { } #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */ #include "simulink.c" /* MEX-file interface mechanism */ #else #include "cg_sfun.h" /* Code generation registration function */ #endif
Жирным шрифтом в тексте S-функции выделены те строки кода, которые обеспечивают считывание параметров блока S-function, заданных в окне диалога. При инициализации S-функции создается структура с именем S, которая содержит в численном виде все свойства S-функции и из которой, собственно, и происходит извлечение параметров с помощью указанных строк кода. Например, строка const real_T *param0 = mxGetPr(PARAM_DEF0(S)); обеспечивает извлечение первого (из списка в окне параметров блока S-function) параметра, а в строке const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); переменной p_width0 присваивается значение размерности этого же параметра (параметры блока S-function могут быть не только скалярами, но и векторами). Отметим также, что такие строки кода имеются в каждом из методов - mdlDerivatives, mdlOutputs и mdlUpdate, благодаря чему параметры блока S-function являются доступными в каждом из методов. Таким образом, пользователю остается добавить выделенные строки в метод mdlInitializeSizes, записать в этом же методе выражения для вычисления нужных констант и обеспечить передачу рассчитанных значений в методы mdlDerivatives, mdlOutputs и mdlUpdate. Удобнее всего это сделать с помощью специально написанной функции. Ниже приводится текст такой функции (Sfun_Get_Parameters.c) для рассматриваемого примера.
Файл Sfun_Get_Parameters.c:
double A00, A01, A10, B00, B11, C00, C11; void Sfun_Get_Parameters(SimStruct *S) { const real_T *param0 = mxGetPr(PARAM_DEF0(S)); const real_T *param1 = mxGetPr(PARAM_DEF1(S)); const real_T *param2 = mxGetPr(PARAM_DEF2(S)); const real_T *param3 = mxGetPr(PARAM_DEF3(S)); const real_T *param4 = mxGetPr(PARAM_DEF4(S)); const real_T *param5 = mxGetPr(PARAM_DEF5(S)); const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); const int_T p_width1 = mxGetNumberOfElements(PARAM_DEF1(S)); const int_T p_width2 = mxGetNumberOfElements(PARAM_DEF2(S)); const int_T p_width3 = mxGetNumberOfElements(PARAM_DEF3(S)); const int_T p_width4 = mxGetNumberOfElements(PARAM_DEF4(S)); const int_T p_width5 = mxGetNumberOfElements(PARAM_DEF5(S)); double L, R, J, Cm, Cw, Fi; L=*param0; R=*param1; J=*param2; Cm=*param3; Cw=*param4; Fi=*param5; A00 = -R/L; A01 = -Cw*Fi/L; A10 = Cm*Fi/J; B00 = 1/L; B11 = -1/J; C00 = Cm*Fi; C11 = 1; }
В первой строке файла выполнено объявление переменных A00, A01, A10, B00, B11, C00, C11. Благодаря тому, что это объявление выполнено вне тела функции указанные переменные являются глобальными. Это облегчает реализацию их передачи в нужные методы. Затем в файле функции следует объявление самой функции, и, несколько строк кода, извлекающие параметры и их размерность из структуры S. Далее идет объявление переменных, являющихся параметрами электродвигателя и расчет значений переменных A00, A01, A10, B00, B11, C00, C11.
Таким образом, исходный файл DPT_Sfunc_1_C.c подвергается модернизации в двух местах:
В результате файл DPT_Sfunc_1_C.c будет выглядеть следующим образом (жирным шрифтом выделены добавления):
/* * File: DPT_Sfunc_1_C.c * * * * --- THIS FILE GENERATED BY S-FUNCTION BUILDER: BASIC, 1.0 --- * * This file is an S-function produced by the Basic S-Function * Builder which only recognizes certain fields. Changes made * outside these fields will be lost the next time the block is * used to load, edit, and resave this file. This file will be overwritten * by the S-function Builder block. If you want to edit this file by hand, * you must change it only in the area defined as: * * %%%-SFUNWIZ_defines_Changes_BEGIN * #define NAME 'replacement text' * %%% SFUNWIZ_defines_Changes_END * * DO NOT change NAME--Change the 'replacement text' only. * * For better compatibility with the Real-Time Workshop, the * "wrapper" S-function technique is used. This is discussed * in the Real-Time Workshop User's Manual in the Chapter titled, * "Wrapper S-functions". * * ------------------------------------------------------------------------- *| See matlabroot/simulink/src/sfuntmpl_doc.c for a more detailed template | * ------------------------------------------------------------------------ * Created: Sun Mar 30 13:07:10 2003 * * */ #define S_FUNCTION_NAME DPT_Sfunc_1_C #define S_FUNCTION_LEVEL 2 /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ /* %%%-SFUNWIZ_defines_Changes_BEGIN --- EDIT HERE TO _END */ #define NUM_INPUTS 1 #define INPUT_0_WIDTH 2 #define INPUT_0_FEEDTHROUGH 0 #define NUM_OUTPUTS 1 #define OUTPUT_0_WIDTH 2 #define NPARAMS 6 #define SAMPLE_TIME_0 INHERITED_SAMPLE_TIME #define NUM_DISC_STATES 0 #define DISC_STATES_IC [0] #define NUM_CONT_STATES 2 #define CONT_STATES_IC [0,0] #define SFUNWIZ_GENERATE_TLC 1 #define SOURCEFILES "//my_lib.lib" #define PANELINDEX 5 #define SFUNWIZ_REVISION 1.0 /* %%%-SFUNWIZ_defines_Changes_END --- EDIT HERE TO _BEGIN */ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ #include "simstruc.h" #define PARAM_DEF0(S) ssGetSFcnParam(S, 0) #define PARAM_DEF1(S) ssGetSFcnParam(S, 1) #define PARAM_DEF2(S) ssGetSFcnParam(S, 2) #define PARAM_DEF3(S) ssGetSFcnParam(S, 3) #define PARAM_DEF4(S) ssGetSFcnParam(S, 4) #define PARAM_DEF5(S) ssGetSFcnParam(S, 5) //=========================NEW CODE================================== #include "Sfun_Get_Parameters.c" //=========================NEW CODE================================== extern void DPT_Sfunc_1_C_Outputs_wrapper(const real_T *u, real_T *y, const real_T *xC, const real_T *param0, const int_T p_width0, const real_T *param1,
const int_T p_width1, const real_T *param2, const int_T p_width2, const real_T *param3, const
int_T p_width3, const real_T *param4, const int_T p_width4, const real_T *param5, const int_T
p_width5); extern void DPT_Sfunc_1_C_Update_wrapper(const real_T *u, const real_T *y, const real_T *param0, const int_T p_width0,const real_T *param1, const
int_T p_width1,const real_T *param2, const int_T p_width2,const real_T *param3, const int_T
p_width3,const real_T *param4, const int_T p_width4, const real_T *param5, const int_T p_width5); extern void DPT_Sfunc_1_C_Derivatives_wrapper(const real_T *u, const real_T *y, real_T *dx, real_T *xC, const real_T *param0, const int_T p_width0,const real_T *param1, const
int_T p_width1,const real_T *param2, const int_T p_width2,const real_T *param3, const int_T
p_width3,const real_T *param4, const int_T p_width4, const real_T *param5, const int_T p_width5); /*====================* * S-function methods * *====================*/ #define MDL_CHECK_PARAMETERS #if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE) /* Function: mdlCheckParameters ========================================= * Abstract: * Validate our parameters to verify they are okay. */ static void mdlCheckParameters(SimStruct *S) { int i; bool validParam = false; /* All parameters must be scalar */ for (i = 0; i < ssGetSFcnParamsCount(S); i++) { const mxArray *pVal = ssGetSFcnParam(S,i); if ( !mxIsNumeric(pVal) || !mxIsDouble(pVal) || mxIsLogical(pVal) || mxIsComplex(pVal) || mxIsSparse(pVal) || !mxIsFinite(mxGetPr(pVal)[0])) { validParam = true; break; } } if (validParam) { ssSetErrorStatus(S,"All parameters must be a scalar or vectors"); return; } } #endif /* MDL_CHECK_PARAMETERS */ /* Function: mdlInitializeSizes ============================================ * Abstract: * Setup sizes of the various vectors. */ static void mdlInitializeSizes(SimStruct *S) { DECL_AND_INIT_DIMSINFO(inputDimsInfo); DECL_AND_INIT_DIMSINFO(outputDimsInfo); 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 will be reported by Simulink */ } #endif ssSetNumContStates(S, NUM_CONT_STATES); ssSetNumDiscStates(S, NUM_DISC_STATES); if (!ssSetNumInputPorts(S, 1)) return; inputDimsInfo.width = INPUT_0_WIDTH; ssSetInputPortDimensionInfo(S, 0, &inputDimsInfo); ssSetInputPortFrameData(S, 0, FRAME_INHERITED); ssSetInputPortDirectFeedThrough(S, 0, INPUT_0_FEEDTHROUGH); ssSetInputPortRequiredContiguous(S, 0, 1); /*direct input signal access*/ if (!ssSetNumOutputPorts(S,1)) return; outputDimsInfo.width = OUTPUT_0_WIDTH; ssSetOutputPortDimensionInfo(S, 0, &outputDimsInfo); ssSetOutputPortFrameData(S, 0, FRAME_INHERITED); 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 | SS_OPTION_USE_TLC_WITH_ACCELERATOR); //=========================NEW CODE================================== Sfun_Get_Parameters(S); //=========================NEW CODE================================== } /* Function: mdlInitializeSampleTimes =================================== * Abstract: * Specifiy the sample time. */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, SAMPLE_TIME_0); ssSetOffsetTime(S, 0, 0.0); } #define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions ==================================== * Abstract: * Initialize the states */ static void mdlInitializeConditions(SimStruct *S) { real_T *xC = ssGetContStates(S); xC[0] = 0; xC[1] = 0; } /* Function: mdlOutputs ================================================= * */ static void mdlOutputs(SimStruct *S, int_T tid) { const real_T *u = (const real_T*) ssGetInputPortSignal(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); const real_T *xC = ssGetContStates(S); const real_T *param0 = mxGetPr(PARAM_DEF0(S)); const real_T *param1 = mxGetPr(PARAM_DEF1(S)); const real_T *param2 = mxGetPr(PARAM_DEF2(S)); const real_T *param3 = mxGetPr(PARAM_DEF3(S)); const real_T *param4 = mxGetPr(PARAM_DEF4(S)); const real_T *param5 = mxGetPr(PARAM_DEF5(S)); const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); const int_T p_width1 = mxGetNumberOfElements(PARAM_DEF1(S)); const int_T p_width2 = mxGetNumberOfElements(PARAM_DEF2(S)); const int_T p_width3 = mxGetNumberOfElements(PARAM_DEF3(S)); const int_T p_width4 = mxGetNumberOfElements(PARAM_DEF4(S)); const int_T p_width5 = mxGetNumberOfElements(PARAM_DEF5(S)); DPT_Sfunc_1_C_Outputs_wrapper(u, y, xC, param0, p_width0, param1, p_width1, param2, p_width2,
param3, p_width3, param4, p_width4, param5, p_width5); } #undef MDL_UPDATE /* Change to #define to use the function */ #if defined(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) { const real_T *u = (const real_T *) ssGetInputPortSignal(S,0); real_T *xD = ssGetDiscStates(S); const real_T *y = ssGetOutputPortSignal(S,0); const real_T *param0 = mxGetPr(PARAM_DEF0(S)); const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); const real_T *param1 = mxGetPr(PARAM_DEF1(S)); const int_T p_width1 = mxGetNumberOfElements(PARAM_DEF1(S)); const real_T *param2 = mxGetPr(PARAM_DEF2(S)); const int_T p_width2 = mxGetNumberOfElements(PARAM_DEF2(S)); const real_T *param3 = mxGetPr(PARAM_DEF3(S)); const int_T p_width3 = mxGetNumberOfElements(PARAM_DEF3(S)); const real_T *param4 = mxGetPr(PARAM_DEF4(S)); const int_T p_width4 = mxGetNumberOfElements(PARAM_DEF4(S)); const real_T *param5 = mxGetPr(PARAM_DEF5(S)); const int_T p_width5 = mxGetNumberOfElements(PARAM_DEF5(S)); DPT_Sfunc_1_C_Update_wrapper(u, y, param0, p_width0, param1, p_width1, param2, p_width2, param3,
p_width3, param4, p_width4, param5, p_width5); } #endif /* MDL_UPDATE */ #define MDL_DERIVATIVES /* Change to #undef to remove function */ #if defined(MDL_DERIVATIVES) /* Function: mdlDerivatives ============================================= * Abstract: * In this function, you compute the S-function block's derivatives. * The derivatives are placed in the derivative vector, ssGetdX(S). */ static void mdlDerivatives(SimStruct *S) { const real_T *u = (const real_T *) ssGetInputPortSignal(S,0); real_T *dx = ssGetdX(S); real_T *xC = ssGetContStates(S); const real_T *y = ssGetOutputPortSignal(S,0); const real_T *param0 = mxGetPr(PARAM_DEF0(S)); const int_T p_width0 = mxGetNumberOfElements(PARAM_DEF0(S)); const real_T *param1 = mxGetPr(PARAM_DEF1(S)); const int_T p_width1 = mxGetNumberOfElements(PARAM_DEF1(S)); const real_T *param2 = mxGetPr(PARAM_DEF2(S)); const int_T p_width2 = mxGetNumberOfElements(PARAM_DEF2(S)); const real_T *param3 = mxGetPr(PARAM_DEF3(S)); const int_T p_width3 = mxGetNumberOfElements(PARAM_DEF3(S)); const real_T *param4 = mxGetPr(PARAM_DEF4(S)); const int_T p_width4 = mxGetNumberOfElements(PARAM_DEF4(S)); const real_T *param5 = mxGetPr(PARAM_DEF5(S)); const int_T p_width5 = mxGetNumberOfElements(PARAM_DEF5(S)); DPT_Sfunc_1_C_Derivatives_wrapper(u, y, dx, xC, param0, p_width0, param1, p_width1, param2,
p_width2, param3, p_width3, param4, p_width4, param5, p_width5); } #endif /* MDL_DERIVATIVES */ /* Function: mdlTerminate ================================================== * Abstract: * No termination needed, but we are required to have this routine. */ static void mdlTerminate(SimStruct *S) { } #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */ #include "simulink.c" /* MEX-file interface mechanism */ #else #include "cg_sfun.h" /* Code generation registration function */ #endif
Следующим шагом является модернизация файла DPT_Sfunc_1_C_wrapper.c, в котором содержится вычислительный код методов mdlDerivatives, mdlOutputs и mdlUpdate. Для рассматриваемого примера модернизация данного файла сводится к следующим шагам:
В результате файл DPT_Sfunc_1_C_wrapper.c будет выглядеть следующим образом (жирным шрифтом отмечены изменения в этом файле):
/* * * --- THIS FILE GENERATED BY S-FUNCTION BUILDER: BASIC, 1.0 --- * * This file is a wrapper S-function produced by the S-Function * Builder which only recognizes certain fields. Changes made * outside these fields will be lost the next time the block is * used to load, edit, and resave this file. This file will be overwritten * by the S-function Builder block. If you want to edit this file by hand, * you must change it only in the area defined as: * * %%%-SFUNWIZ_wrapper_XXXXX_Changes_BEGIN * Your Changes go here * %%%-SFUNWIZ_wrapper_XXXXXX_Changes_END * * For better compatibility with the Real-Time Workshop, the * "wrapper" S-function technique is used. This is discussed * in the Real-Time Workshop User's Manual in the Chapter titled, * "Wrapper S-functions". * * Created: Sun Mar 30 13:07:10 2003 */ /* * Include Files * */ #include "tmwtypes.h" /* %%%-SFUNWIZ_wrapper_includes_Changes_BEGIN --- EDIT HERE TO _END */ #include //=========================NEW CODE================================== double A00, A01, A10, B00, B11, C00, C11; //=========================NEW CODE================================== /* %%%-SFUNWIZ_wrapper_includes_Changes_END --- EDIT HERE TO _BEGIN */ /* * Create external references here. * */ /* %%%-SFUNWIZ_wrapper_externs_Changes_BEGIN --- EDIT HERE TO _END */ //extern double func(double a); /* %%%-SFUNWIZ_wrapper_externs_Changes_END --- EDIT HERE TO _BEGIN */ /* * Output functions * */ void DPT_Sfunc_1_C_Outputs_wrapper(const real_T *u, real_T *y, const real_T *xC, const real_T *param0, const int_T p_width0, const real_T *param1,
const int_T p_width1, const real_T *param2, const int_T p_width2, const real_T *param3, const
int_T p_width3, const real_T *param4, const int_T p_width4, const real_T *param5, const int_T
p_width5) { /* This Outputs function allows for 1 input and 1 output * signal of any width, any number of discrete states and * parameters, all of type real_T. * * If you need the FULL POWER of the S-function interface, * familiarize yourself with the full template file at: * * matlabroot/simulink/src/sfuntmpl_doc.c * * and the "Writing S-functions" manual in the documentation. */ /* %%%-SFUNWIZ_wrapper_Outputs_Changes_BEGIN --- EDIT HERE TO _END */ //=========================OLD CODE================================== /* double Cm = param3[0], Fi = param5[0]; double C00=Cm*Fi, C11= 1; */ //=========================OLD CODE================================== y[0] = C00*xC[0]; y[1] = C11*xC[1]; /* %%%-SFUNWIZ_wrapper_Outputs_Changes_END --- EDIT HERE TO _BEGIN */ } /* * Updates function * */ void DPT_Sfunc_1_C_Update_wrapper(const real_T *u, const real_T *y, const real_T *param0, const int_T p_width0,const real_T *param1, const
int_T p_width1,const real_T *param2, const int_T p_width2,const real_T *param3, const
int_T p_width3,const real_T *param4, const int_T p_width4, const real_T *param5, const int_T p_width5) { /* %%%-SFUNWIZ_wrapper_Update_Changes_BEGIN --- EDIT HERE TO _END */ /* * Code example * xD[0] = u[0]; */ /* %%%-SFUNWIZ_wrapper_Update_Changes_END --- EDIT HERE TO _BEGIN */ } /* * Derivatives function * */ void DPT_Sfunc_1_C_Derivatives_wrapper(const real_T *u, const real_T *y, real_T *dx, real_T *xC, const real_T *param0, const int_T p_width0,const real_T *param1, const
int_T p_width1,const real_T *param2, const int_T p_width2,const real_T *param3, const int_T
p_width3,const real_T *param4, const int_T p_width4, const real_T *param5, const int_T p_width5) { /* %%%-SFUNWIZ_wrapper_Derivatives_Changes_BEGIN --- EDIT HERE TO _END */ //=========================OLD CODE================================== /* double L = param0[0], R = param1[0], J =param2[0]; double Cm = param3[0], Cw = param4[0], Fi=param5[0]; double A00 = -R/L , A01 = -Cw*Fi/L , B00 = 1/L; double A10 = Cm*Fi/J , B11 = -1/J; */ //=========================OLD CODE================================== dx[0] = A00*xC[0]+A01*xC[1]+B00*u[0]; dx[1] = A10*xC[0]+B11*u[1]; /* %%%-SFUNWIZ_wrapper_Derivatives_Changes_END --- EDIT HERE TO _BEGIN */ }
Выполнить сборку S-функции на основе измененных файлов следует командой из рабочего окна MATLAB:
mex DPT_Sfunc_1_C.c DPT_Sfunc_1_C_wrapper.c или mex -g DPT_Sfunc_1_C.c DPT_Sfunc_1_C_wrapper.c .
При этом папка, где находятся исходные файлы, должна быть настроена как текущая. Команда mex с опцией -g создает отладочный вариант S-функции.
В результате использования предложенной методики будет получена S-функция, обеспечивающая наилучшие характеристики по быстродействию. Вносимые в исходные файлы изменения не очень значительны и могут быть выполнены даже начинающим пользователем.