Why does the mdlOutputs block in S-functions only execute when there is a change in the input values?
11 views (last 30 days)
Show older comments
I am working on creating a frequency sweep function in Simulink, where all operations need to be defined within the S-Function. Since one of the dependencies of the function is the time variable, I initially set up my implementation to receive time as an external input, and the S-Function worked correctly. This is because the time data increases in the same manner as the discrete simulation time, so a value that changes at each cycle tick is passed into the S-Function.
In my subsequent work, I modified the setup to receive only a rising edge trigger as the function input and count the time internally within the S-Function. However, in this case, the mdlOutputs block only updates once when the trigger input changes from 0 to 1, and it doesn't execute again unless there is another change in the input. I explored the ssSetOptions function block but couldn't achieve a positive result.
From what I understand in the above image, mdlCheckParameters calls the mdlOutputs function when there is a change, but I haven't fully grasped how it works. Could you assist me with this? Thank you in advance.
Here is my code for initialization and for output block.
static void mdlInitializeSizes(SimStruct* S)
{
static char_T msg[256];
/* Set Function Arguments */
ssSetNumSFcnParams(S, N_ARGS);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
sprintf(msg, "Wrong number of input arguments passed.\n%d arguments are expected\n", N_ARGS);
ssSetErrorStatus(S, msg);
return;
}
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);
/* Set Input Ports */
if (!ssSetNumInputPorts(S, N_PORT_IN)) return;
ssSetInputPortWidth(S, PORT_IN_ARRAY, WIDTH_OF_INPORT);
ssSetInputPortDataType(S, PORT_IN_ARRAY, SS_DOUBLE);
ssSetInputPortRequiredContiguous(S, PORT_IN_ARRAY, 1);
ssSetInputPortDirectFeedThrough(S, PORT_IN_ARRAY, 1);
ssSetInputPortWidth(S, PORT_IN_TIME, WIDTH_OF_INPORT_2);
ssSetInputPortDataType(S, PORT_IN_TIME, SS_DOUBLE);
ssSetInputPortRequiredContiguous(S, PORT_IN_TIME, 1);
ssSetInputPortDirectFeedThrough(S, PORT_IN_TIME, 1);
/* Set Output Ports */
if (!ssSetNumOutputPorts(S, N_PORT_OUT)) return;
ssSetOutputPortWidth(S, PORT_OUT_ELEM_1, WIDTH_OF_OUTPORT);
ssSetOutputPortDataType(S, PORT_OUT_ELEM_1, SS_DOUBLE);
ssSetOutputPortWidth(S, PORT_OUT_ELEM_2, WIDTH_OF_OUTPORT);
ssSetOutputPortDataType(S, PORT_OUT_ELEM_2, SS_UINT8);
/* Set Number of Sample Times, this should be >1 when there are different rate inputs */
ssSetNumSampleTimes(S, 1);
/* Set Number Work Vectors */
ssSetNumDWork(S, N_DWORK);
ssSetNumRWork(S, N_IWORK);
ssSetNumIWork(S, N_RWORK);
ssSetNumPWork(S, N_PWORK);
/* specify the sim state compliance to be same as a built-in block */
ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT);
/* Set this S-function as runtime thread-safe for multicore execution */
ssSetRuntimeThreadSafetyCompliance(S, RUNTIME_THREAD_SAFETY_COMPLIANCE_TRUE);
/* None of the parameters are tunable */
ssSetSFcnParamTunable(S, 0, 0);
/* Set S-function Options */
ssSetOptions(S, SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_ASYNC_RATE_TRANSITION |
SS_OPTION_EXCEPTION_FREE_CODE);
}
static void mdlOutputs(SimStruct* S, int_T tid)
{
/* Retrieve C++ object object */
LinearSweep* sweepGenerator = (LinearSweep*)ssGetPWork(S)[P_WORK_SWEEP];
real_T* params = (real_T*)ssGetInputPortSignal(S, PORT_IN_ARRAY);
real_T* t = (real_T*)ssGetInputPortSignal(S, PORT_IN_TIME);
//double t = ssGetT(S); //this function can be used if the functionwill be triggered by a flag
real_T* wave_output = (real_T*)ssGetOutputPortSignal(S, 0); // Output signal
uint8_T* bdone = (uint8_T*)ssGetOutputPortSignal(S, 1); // Done flag
// Call the freqSweep function on the instance
sweepGenerator->freqSweep(params, *t, wave_output, bdone);
/* Unused Argument */
UNUSED_ARG(tid);
}
Normally time output is used from "Time trigger source" to s-function. Thus, inputs are changing at each time step, but I changed the code as trigger will be input. But at this time s-func doesn't work properly.
0 Comments
Accepted Answer
Deep
on 21 Oct 2024
To clarify, “mdlCheckParameters” does not check for changes in inputs; it verifies the validity of the S-Function parameters. Parameters in Simulink are constants or tunable values that define the behavior of the block, whereas inputs are dynamic signals fed into the block during simulation. You can find more details on “mdlCheckParameters” in the documentation (https://www.mathworks.com/help/simulink/sfg/mdlcheckparameters.html).
From the attached image, it appears that the "Trigger" is a constant block, likely modified manually during simulation to simulate a rising edge. This makes me suspect that the sample time of the constant block is the issue, as it defaults to infinity. This infinite sample time means the S-Function will execute only when a constant changes, as it inherits this sample time from its input blocks, all of which seem to originate from constant blocks. A practical troubleshooting step would be to set the sample time of the Trigger block to 0, ensuring the signal propagates through the S-Function at every simulation step rather than just once. For more information on sample times, refer to the sample time documentation (https://www.mathworks.com/help/simulink/ug/what-is-sample-time.html).
To resolve the issue of the S-Function only running on manual trigger, I suggest keeping the sample time of all constant blocks as infinite but modifying the S-Function to include the following snippet in its initialization code.
ssSetOptions(S, SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME);
This option prevents the S-Function block from inheriting an infinite sample time, because of which the block should run at every major time step. You can learn more about this option in the documentation (https://www.mathworks.com/help/simulink/sfg/ss_option_disallow_constant_sample_time.html).
More Answers (0)
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!