Specify S-Function Sample Times
About Sample Times
You can specify the sample-time behavior of your S-functions in mdlInitializeSampleTimes
. Your S-function can inherit its rates from the blocks that drive it or define its own rates.
You can specify your S-function rates (i.e., sample times) as
Block-based sample times
Port-based sample times
Hybrid block-based and port-based sample times
With block-based sample times, the S-function specifies a set of operating rates for the block as a whole during the initialization phase of the simulation. With port-based sample times, the S-function specifies a sample time for each input and output port individually during initialization. During the simulation phase, with block-based sample times, the S-function processes all inputs and outputs each time a sample hit occurs for the block. By contrast, with port-based sample times, the block processes a particular port only when a sample hit occurs for that port.
For example, consider two sample rates, 0.5 and 0.25 seconds, respectively:
In the block-based method, selecting 0.5 and 0.25 directs the block to execute inputs and outputs at 0.25 second increments.
In the port-based method, setting the input port to 0.5 and the output port to 0.25 causes the block to process inputs at 2 Hz and outputs at 4 Hz.
You should use port-based sample times if your application requires unequal sample rates for input and output execution or if you do not want the overhead associated with running input and output ports at the highest sample rate of your block.
In some applications, an S-Function block might need to operate internally at one or more sample rates while inputting or outputting signals at other rates. The hybrid block- and port-based method of specifying sample rates allows you to create such blocks.
In typical applications, you specify only one block-based sample time. Advanced S-functions might require the specification of port-based or multiple block sample times.
Block-Based Sample Times
C MEX S-functions specify block-based sample time information in
The next two sections discuss how to specify block-based sample times for C MEX
S-functions. A third section presents a simple example that shows how to specify
sample times in mdlInitializeSampleTimes
. For a detailed example,
see mixedm.c
.
Specifying the Number of Sample Times in mdlInitializeSizes
To configure your S-function for block-based sample times, use
ssSetNumSampleTimes
(S,numSampleTimes);
where numSampleTimes > 0
. This tells the Simulink® engine that your S-function has block-based sample times. the
engine calls mdlInitializeSampleTimes
, which in turn sets the
sample times.
Setting Sample Times and Specifying Function Calls in mdlInitializeSampleTimes
mdlInitializeSampleTimes
specifies two pieces of execution
information:
Sample and offset times — In
mdlInitializeSampleTimes
, you must specify the sampling period and offset for each sample time usingssSetSampleTime
andssSetOffsetTime
. If applicable, you can calculate the appropriate sampling period and offset prior to setting them, for example, by computing the best sample time for the block based on the S-function dialog parameters obtained usingssGetSFcnParam
.Function calls — In
mdlInitializeSampleTimes
, usessSetCallSystemOutput
to specify the output elements that are performing function calls. Seesfun_fcncall.c
for an example and Implement Function-Call Subsystems with S-Functions for an explanation of this S-function.
You specify the sample times as pairs
[
sample_time,
offset_time
]
, using these macros
ssSetSampleTime
(S, sampleTimePairIndex, sample_time)ssSetOffsetTime
(S, offsetTimePairIndex, offset_time)
where sampleTimePairIndex
and
offsetTimePairIndex
starts at 0.
The valid sample time pairs are (uppercase values are macros defined in
simstruc.h
):
[CONTINUOUS_SAMPLE_TIME, 0.0 ] [CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] [discrete_sample_period, offset ] [VARIABLE_SAMPLE_TIME , 0.0 ]
Alternatively, you can specify that the sample time is inherited from the driving block, in which case the S-function can have only one sample time pair,
[INHERITED_SAMPLE_TIME, 0.0 ]
or
[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
Note
If your S-function inherits its sample time, you should specify whether it is safe to use the S-function in a referenced model, i.e., a model referenced by another model. See Specifying Model Reference Sample Time Inheritance for more information.
The following guidelines might help in specifying sample times:
A continuous function that changes during minor integration steps should register the
[CONTINUOUS_SAMPLE_TIME, 0.0]
sample time.A continuous function that does not change during minor integration steps should register the
[CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
sample time.A discrete function that changes at a specified rate should register the discrete sample time pair
[discrete_sample_period, offset]
where
discrete_sample_period > 0.0
and
0.0 <= offset < discrete_sample_period
A discrete function that changes at a variable rate should register the variable-step discrete
[VARIABLE_SAMPLE_TIME, 0.0]
sample time. In C MEX S-functions, themdlGetTimeOfNextVarHit
function is called to get the time of the next sample hit for the variable-step discrete task. TheVARIABLE_SAMPLE_TIME
can be used with variable-step solvers only.
If your function has no intrinsic sample time, you must indicate that it is inherited according to the following guidelines:
A function that changes as its input changes, even during minor integration steps, should register the
[INHERITED_SAMPLE_TIME, 0.0]
sample time.A function that changes as its input changes, but doesn't change during minor integration steps (meaning, is held during minor steps), should register the
[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
sample time.
To check for a sample hit during execution (in mdlOutputs
or mdlUpdate
), use the ssIsSampleHit
or ssIsContinuousTask
macro. For
example, use the following code fragment to check for a continuous sample
hit:
if (ssIsContinuousTask
(S,tid)) {
}
To determine whether the third (discrete) task has a hit, use the following code fragment:
if (ssIsSampleHit
(S,2,tid) {
}
The Simulink engine always assigns an index of 0
to the
continuous sample rate, if it exists, however you get incorrect results if you
use ssIsSampleHit(S,0,tid)
.
Example: mdlInitializeSampleTimes
This example specifies that there are two discrete sample times with periods of 0.01 and 0.5 seconds.
static void mdlInitializeSampleTimes
(SimStruct *S)
{
ssSetSampleTime(S, 0, 0.01);
ssSetOffsetTime(S, 0, 0.0);
ssSetSampleTime(S, 1, 0.5);
ssSetOffsetTime(S, 1, 0.0);
} /* End of mdlInitializeSampleTimes. */
Specifying Port-Based Sample Times
You cannot use port-based sample times with S-functions that have neither input ports nor output ports. If an S-function uses port-based sample times and has no ports, the S-function produces errors when the Simulink model is updated or run. If the number of input or output ports on an S-function is variable, extra protection should be added into the S-function to ensure the total number of ports does not go to zero.
To use port-based sample times in your C MEX S-function, you must specify the
number of sample times as port-based in the S-function
mdlInitializeSizes
method:
ssSetNumSampleTimes
(S, PORT_BASED_SAMPLE_TIMES)
You must also specify the sample time of each input and output port in the
S-function mdlInitializeSizes
method, using the following
macros
ssSetInputPortSampleTime
(S, idx, period)ssSetInputPortOffsetTime
(S, idx, offset)ssSetOutputPortSampleTime
(S, idx, period)ssSetOutputPortOffsetTime
(S, idx, offset)
The call to ssSetNumSampleTimes
can be placed before or after
the port-based sample times are actually specified in
mdlInitializeSizes
. However, if
ssSetNumSampleTimes
does not configure the S-function to use
port-based sample times, any sample times set on the ports will be ignored.
For any given port, you can specify
A specific sample time and period
For example, the following code sets the sample time of the S-function first input port to
0.1
and the offset time to0
.ssSetInputPortSampleTime(S, 0, 0.1); ssSetInputPortOffsetTime(S, 0, 0);
Inherited sample time (
-1
), i.e., the port inherits its sample time from the port to which it is connected (see Specifying Inherited Sample Time for a Port)Constant sample time (
Inf
), i.e., the signal coming from the port is constant (see Specifying Constant Sample Time (Inf) for a Port)Note
To be usable in a triggered subsystem, all your S-function ports must have either inherited (
–1
) or constant sample time (Inf
). For more information, see Configuring Port-Based Sample Times for Use in Triggered Subsystems.
Specifying Inherited Sample Time for a Port
To specify that a port's sample time is inherited in a C MEX S-function, the
mdlInitializeSizes
method should set its period to
-1
and its offset to 0
. For example,
the following code specifies inherited sample time for a C MEX S-function first
input port:
ssSetInputPortSampleTime(S, 0, -1); ssSetInputPortOffsetTime(S, 0, 0);
When you specify port-based sample times, the Simulink engine calls mdlSetInputPortSampleTime
and
mdlSetOutputPortSampleTime
to determine the rates of
inherited signals.
Once all rates have been determined, the engine calls
mdlInitializeSampleTimes
. Even though there is no need to
initialize port-based sample times at this point, the engine invokes this method
to give your S-function an opportunity to configure function-call connections.
Your S-function must thus provide an implementation for this method regardless
of whether it uses port-based sample times or function-call connections.
Although you can provide an empty implementation, you might want to use it to
check the appropriateness of the sample times that the block inherited during
sample time propagation. Use
and ssGetInputPortSampleTime
in
ssGetOutputPortSampleTime
mdlInitializeSampleTimes
to obtain the values of the
inherited sample times. For example, the following code in
mdlInitializeSampleTimes
checks if the S-function first
input inherited a continuous sample time.
if (!ssGetInputPortSampleTime(S,0)) { ssSetErrorStatus(S,"Cannot inherit a continuous sample time.") };
Note
If you specify that your S-function ports inherit their sample time, you should also specify whether it is safe to use the S-function in a referenced model, i.e., a model referenced by another model. See Specifying Model Reference Sample Time Inheritance for more information.
If you write TLC code to generate inlined code from an S-function, and if the
TLC code contains an Outputs
function, you must modify the
TLC code if these conditions are true:
The output port has a constant value. It uses or inherits a sample time of
Inf
.The S-function is a multirate S-function or uses port-based sample times.
In this case, the TLC code must generate code for the constant-valued output
port by using the function OutputsForTID
instead of the
function Outputs
. For more information, see Specifying Constant Sample Time (Inf) for a Port.
To prevent ports from inheriting a sample time of Inf
, set
the option SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME
in the S-function code.
In this case, you can use the TLC function Outputs
to
generate code for constant-valued output ports.
Specifying Constant Sample Time (Inf
) for a Port
If your S-function uses port-based sample times, it can set a sample time of
Inf
on any of its ports. A port-based sample time of
Inf
means that the signal entering or leaving the port
stays constant.
To make a port output a constant value, the S-function must:
Use
ssSetOptions
in itsmdlInitializeSizes
method to add support for a sample time ofInf
:ssSetOptions(S,SS_OPTION_ALLOW_CONSTANT_PORT_SAMPLE_TIME);
Note
This option causes the S-function’s ports to support a sample time of
Inf
, including ports that inherit their sample times from other blocks. If any S-function ports that inherit sample time cannot have a sample time ofInf
, an error occurs. Set sample times for these ports using themdlSetInputPortSampleTime
andmdlSetOutputPortSampleTime
methods.Set the port's sample time to
Inf
and its offset to0
, e.g.,ssSetInputPortSampleTime(S,0,mxGetInf()); ssSetInputPortOffsetTime(S,0,0);
Check in
mdlOutputs
whether the method'stid
argument equalsCONSTANT_TID
and if so, set the value of the port's output if it is an output port.
To see an example of how to create ports which output a constant value, see
sfun_port_constant.c
, the source file for the
sfcndemo_port_constant
example.
If you write TLC code to generate inlined code from an S-function, and if the
TLC code contains an Outputs
function, modify the TLC code if
all these conditions are true:
The output port has a constant value. It uses or inherits a sample time of
Inf
.The S-function is a multirate S-function or uses port-based sample times.
In this case, the TLC code must generate code for the constant-valued output
port by using the function OutputsForTID
instead of the
function Outputs
. The function
OutputsForTID
generates output code for the
constant-valued component of the S-function. If you configure a model to
generate multitasking code, OutputsForTID
also generates
output code for the periodic components of the S-function.
For example, view the TLC file sfun_port_constant.tlc
for
the C S-function sfun_port_constant.c
in the model
sfcndemo_port_constant
. In the model, the input of the
block S-Function2 has a constant value throughout the simulation. In the
S-function code, the first output port inherits the sample time of the input
port, so the output port also has a constant value. The S-function code directly
specifies a constant value for the second output port.
In the TLC code, if the port has a constant value, the function
Outputs
does not generate code for the first output port.
The function does not generate code for the second output port under any
circumstances because the port always has a constant value.
For this S-function, OutputsForTID
generates code for
output ports that have a constant value. The code generator invokes the function
OutputsForTID
, and sets the argument
tid
to the task identifier that corresponds to constant
values. Only if the task identifier of an output port corresponds to constant
values does ,OutputsForTID
then generate code for the
port.
Configuring Port-Based Sample Times for Use in Triggered Subsystems
To use a C MEX S-function in a triggered subsystem, your port-based sample time S-function must perform the following tasks.
Use
ssSetOptions
in themdlInitializeSizes
method to indicate the S-function can run in a triggered subsystem:ssSetOptions(S, SS_OPTION_ALLOW_PORT_SAMPLE_TIME_IN_TRIGSS);
Set all of its ports to have either inherited (
-1
) or constant sample time (Inf
) in itsmdlInitializeSizes
method.Handle inheritance of a triggered sample time in
mdlSetInputPortSampleTime
andmdlSetOutputPortSampleTime
methods as follows.Since the S-function ports inherit their sample times, the Simulink engine invokes either
mdlSetInputPortSampleTime
ormdlSetOutputPortSampleTime
during sample time propagation. The macrossSampleAndOffsetAreTriggered
can be used in these methods to determine if the S-function resides in a triggered subsystem. If the S-function does reside in a triggered subsystem, whichever method is called must set the sample time and offset of the port for which it is called toINHERITED_SAMPLE_TIME
(-1
).Setting a port's sample time and offset both to
INHERITED_SAMPLE_TIME
indicates that the sample time of the port is triggered, i.e., it produces an output or accepts an input only when the subsystem in which it resides is triggered. The method must then also set the sample times and offsets of all of the other S-function input and output ports to have either triggered or constant sample time (Inf
), whichever is appropriate, e.g.,static void mdlSetInputPortSampleTime(SimStruct *S, int_T portIdx, real_T sampleTime real_T offsetTime) { /* If the S-function resides in a triggered subsystem, the sample time and offset passed to this method are both equal to INHERITED_SAMPLE_TIME. Therefore, if triggered, the following lines set the sample time and offset of the input port to INHERITED_SAMPLE_TIME.*/ ssSetInputPortSampleTime(S, portIdx, sampleTime); ssSetInputPortOffsetTime(S, portIdx, offsetTime); /* If triggered, set the output port to inherited, as well */ if (ssSampleAndOffsetAreTriggered(sampleTime,offsetTime)) { ssSetOutputPortSampleTime(S, 0, INHERITED_SAMPLE_TIME); ssSetOutputPortOffsetTime(S, 0, INHERITED_SAMPLE_TIME); /* Note, if there are additional input and output ports on this S-function, they should be set to either inherited or constant at this point, as well. */ } }
There is no way for an S-function residing in a triggered subsystem to predict whether the Simulink engine will call
mdlSetInputPortSampleTime
ormdlSetOutputPortSampleTime
to set its port sample times. For this reason, both methods must be able to set the sample times of all ports correctly so the engine has to call only one of the methods a single time.In
mdlUpdate
andmdlOutputs
, usessGetPortBasedSampleTimeBlockIsTriggered
to check whether the S-function resides in a triggered subsystem and if so, use appropriate algorithms for computing its states and outputs.
See sfun_port_triggered.c
, the source file for the
sfcndemo_port_triggered
example model, for an example
of how to create an S-function that can be used in a triggered subsystem.
Hybrid Block-Based and Port-Based Sample Times
The hybrid method of assigning sample times combines the block-based and
port-based methods. You first specify, in mdlInitializeSizes
, the
total number of rates at which your block operates, including both block and input
and output rates, using ssSetNumSampleTimes
.
You then set the SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED
option,
using ssSetOptions
, to tell the simulation engine that you are
going to use the port-based method to specify the rates of the input and output
ports individually. Next, as in the block-based method, you specify the periods and
offsets of all of the block's rates, both internal and external, using
ssSetSampleTime ssSetOffsetTime
Finally, as in the port-based method, you specify the rates for each port, using
ssSetInputPortSampleTime(S, idx, period) ssSetInputPortOffsetTime(S, idx, offset) ssSetOutputPortSampleTime(S, idx, period) ssSetOutputPortOffsetTime(S, idx, offset)
Note that each of the assigned port rates must be the same as one of the
previously declared block rates. For an example S-function, see mixedm.c
.
Note
If you use the SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED
option,
your S-function cannot inherit sample times. Instead, you must specify the rate
at which each input and output port runs.
Multirate S-Function Blocks
In a multirate S-Function block, you can encapsulate the code that
defines each behavior in the mdlOutputs
and
mdlUpdate
functions with a statement that determines whether
a sample hit has occurred. In a C MEX S-function, the
ssIsSampleHit
macro determines whether the current time is a
sample hit for a specified sample time. The macro has this syntax:
ssIsSampleHit(S, st_index, tid)
where S
is the SimStruct
,
st_index
identifies a specific sample time index, and
tid
is the task ID (tid
is an argument to
the mdlOutputs
and mdlUpdate
functions).
For example, these statements in a C MEX S-function specify three sample times: one for continuous behavior and two for discrete behavior.
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME); ssSetSampleTime(S, 1, 0.75); ssSetSampleTime(S, 2, 1.0);
In the mdlUpdate
function, the following statement encapsulates
the code that defines the behavior for the sample time of 0.75 second.
if (ssIsSampleHit(S, 1, tid)) { }
The second argument, 1
, corresponds to the second sample time,
0.75 second.
Use the following lines to encapsulate the code that defines the behavior for the continuous sample hit:
if (ssIsContinuousTask
(S,tid)) {
}
Example of Defining a Sample Time for a Continuous Block
This example defines a sample time for a block that is continuous.
/* Initialize the sample time and offset. */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); }
You must add this statement to the mdlInitializeSizes
function.
ssSetNumSampleTimes(S, 1);
Example of Defining a Sample Time for a Hybrid Block
This example defines sample times for a hybrid S-Function block.
/* Initialize the sample time and offset. */ static void mdlInitializeSampleTimes(SimStruct *S) { /* Continuous state sample time and offset. */ ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); /* Discrete state sample time and offset. */ ssSetSampleTime(S, 1, 0.1); ssSetOffsetTime(S, 1, 0.025); }
In the second sample time, the offset causes the Simulink engine to call the mdlUpdate
function at these
times: 0.025 second, 0.125 second, 0.225 second, and so on, in increments of 0.1
second.
The following statement, which indicates how many sample times are defined,
also appears in the mdlInitializeSizes
function.
ssSetNumSampleTimes(S, 2);
Multirate S-Functions and Sample Time Hit Calculations
For fixed-step solvers, Simulink uses integer arithmetic, rather than floating-point arithmetic, to calculate the sample time hits. Consequently, task times are integer multiples of their corresponding sample time periods.
This calculation method becomes important if you consider performing Boolean logic based upon task times in multirate S-functions. For example, consider an S-function that has two sample times. The fact that (ssIsSampleHit(S, idx1) == true && ssIsSampleHit(S,idx2) == true, does not guarantee that ssGetTaskTime(S, idx1) == ssGetTaskTime(S, idx2).
Synchronizing Multirate S-Function Blocks
If tasks running at different rates need to share data, you must ensure that data
generated by one task is valid when accessed by another task running at a different
rate. You can use the ssIsSpecialSampleHit
macro in the
mdlUpdate
or mdlOutputs
routine of a
multirate S-function to ensure that the shared data is valid. This macro returns
true
if a sample hit has occurred at one rate and a sample
hit has also occurred at another rate in the same time step. It thus permits a
higher rate task to provide data needed by a slower rate task at a rate the slower
task can accommodate. When using the ssIsSpecialSampleHit
macro,
the slower sample time must be an integer multiple of the faster sample time.
Suppose, for example, that your model has an input port operating at one rate (with a sample time index of 0) and an output port operating at a slower rate (with a sample time index of 1). Further, suppose that you want the output port to output the value currently on the input. The following example illustrates usage of this macro.
if (ssIsSampleHit(S, 0, tid) { if (ssIsSpecialSampleHit(S, 0, 1, tid) { /* Transfer input to output memory. */ ... } } if (ssIsSampleHit(S, 1, tid) { /* Emit output. */ ... }
In this example, the first block runs when a sample hit occurs at the input rate. If the hit also occurs at the output rate, the block transfers the input to the output memory. The second block runs when a sample hit occurs at the output rate. It transfers the output in its memory area to the block's output.
Note that higher-rate tasks always run before slower-rate tasks. Thus, the input task in the preceding example always runs before the output task, ensuring that valid data is always present at the output port.
Specifying Model Reference Sample Time Inheritance
If your C MEX S-function inherits its sample times from the blocks that drive it,
your S-function should specify whether referenced models containing your S-function
can inherit sample times from their parent model. If the S-function output does not
depend on its inherited sample time, use the ssSetModelReferenceSampleTimeInheritanceRule
macro to set the
S-function sample time inheritance rule to
USE_DEFAULT_FOR_DISCRETE_INHERITANCE
. Otherwise, set the rule
to DISALLOW_SAMPLE_TIME_INHERITANCE
to disallow sample-time
inheritance for referenced models that include S-functions whose outputs depend on
their inherited sample time and thereby avoid inadvertent simulation errors.
Note
If your S-function does not set this flag, the Simulink engine assumes that it does not preclude a referenced model containing it from inheriting a sample time. However, the engine optionally warns you that the referenced model contains S-functions that do not specify a sample-time inheritance rule (see Blocks Whose Outputs Depend on Inherited Sample Time).
If you are uncertain whether an existing S-function output depends on its inherited sample time, check whether it invokes any of the following C macros:
ssGetSampleTime
ssGetInputPortSampleTime
ssGetOutputPortSampleTime
ssGetInputPortOffsetTime
ssGetOutputPortOffsetTime
ssGetInputPortSampleTimeIndex
ssGetOutputPortSampleTimeIndex
or TLC functions:
LibBlockSampleTime
CompiledModel.SampleTime
LibBlockInputSignalSampleTime
LibBlockInputSignalOffsetTime
LibBlockOutputSignalSampleTime
LibBlockOutputSignalOffsetTime
If your S-function does not invoke any of these macros or functions, its output does not depend on its inherited sample time and hence it is safe to use in referenced models that inherit their sample time.
Sample-Time Inheritance Rule Example
As an example of an S-function that precludes a referenced model from
inheriting its sample time, consider an S-function that has the following
mdlOutputs
method:
static void mdlOutputs(SimStruct *S, int_T tid) { const real_T *u = (const real_T*) ssGetInputPortSignal(S,0); real_T *y = ssGetOutputPortSignal(S,0); y[0] = ssGetSampleTime(S,tid) * u[0]; }
The output of this S-function is its inherited sample time, hence its output depends on its inherited sample time, and hence it is unsafe to use in a referenced model. For this reason, this S-function should specify its model reference inheritance rule as follows:
ssSetModelReferenceSampleTimeInheritanceRule (S, DISALLOW_SAMPLE_TIME_INHERITANCE);
See Also
ssSetSampleTime
| ssGetSampleTime
| ssSetInputPortSampleTime