Why does Simulink's Weighted Sample Time block sometimes use fixed-point data types for its output?
6 views (last 30 days)
MathWorks Fixed Point Team on 23 Dec 2021
In Simulink, I want to get the sample time of a signal and use that value in other calculations. The Weighted Sample Time block does this by taking one signal as input and then outputing the value of the sample time as another signal. But sometimes I'm surprised by the data type used for the output signal. In particular, why does the output signal sometimes use a fixed-point data type?
Andy Bartlett on 23 Dec 2021
Edited: Andy Bartlett on 23 Dec 2021
Weighted Sample Time block is Polymorphic
The Weighted Sample Time Math block is numeric type polymorphic. For inputs, it supports floating-point types like doubles and singles, integers types like uint8 and int16, and fixed-point types like sfix16_En7 and ufix32_En33.
Inherit via internal rule
When the blocks Output data type is set to "Inherit: Inherit via internal rule", the output type is automatically determined based on the input type, the sample time, the weight parameter, and other factors. The "internal rule" seeks to pick a type that will give good accuracy and good efficiency of the code generated by a MathWorks Coder for the block.
When the input is double or single floating-point, the output selected by the internal rule will also be double or single. But when the input is integer or fixed-point, the "internal rule" will seek for the output a fixed-point data type that gives good accuracy. In some cases, the selected fixed-point type is equivalent to an built-in integer type like int32 in which case the output will be that built-in integer type.
Example Sample Time 1/3
Let's consider the case when the signal input to the Weighted Sample Time block has sample time 1/3. And let's consider the case when the Weighted Sample Time block is configured for the following.
Operation: Ts Only
Weight value: 1
Output data type: Inherit: Inherit via internal rule
The following image shows the output of the Weighted Sample Time for four different input data types.
Look at the display blocks and notice the sample time 1/3 has been captured to 5 or more decimal digits of accuracy in all four cases. Also notice the input and output data types of each of the four Weighted Sample Time blocks.
Notice that the inputs of types uint8 and uint32 got fixed-point data types for their outputs. Why weren't the output's integers like the inputs? Simple, if the output was an integer the sample time would have been quantized from 1/3 seconds to 0 seconds. So in integer, the sample time would be represented with zero digits of accuracy. Representing the sample time as 0 would be useless. Using fixed-point gives good accuracy.
For the integer inputs, why didn't the output type use floating-point? The reason is to better support efficiency of the generated code, especially when targeting an embedded processor with no floating-point hardware support. If the input doesn't use floating-point, then the output will stay away from floating-point too. As the image shows by exploiting fixed-point scaling, a few integer bits can approximate 1/3 pretty accurately.
Controlling the Output Type
If the type selected by "Inherit: Inherit via internal rule" doesn't meet your requirements, then you can use a different setting for the "Output data type" parameter and control the type. The most powerful way to do this is to use "Inherit via back propagation"
Output data type: Inherit: Inherit: Inherit via back propagation
then use another block to set the data type of the output signal.
One way to control the signal data type from another block is to drop in a Signal Specification block and explicitly set the desired data type via that Signal Specification.
Another way is to dynamically set the data type of the output signal using the Data Type Duplication block or the Data Type Propagation block. We'll see an example of the former in the next section.
Using Weight to Change Units so Integers are Useful
The previous section described how to control the output data type of the Sample Time Math block. But if we just did that then we'd have problems like a sample time of 1/3 seconds uselessly quantized to integer value 0.
To avoid catastrophic quantization and use an integer for the output, you'd need to change the units. If you change to milliseconds, you need to quantize 1000/3 to integer. If you change to microseconds, then you need to quantize 100000/3 to integer.
The Weighted Sample Time blocks "Weight value" parameter can be used to manually make the conversion to milliseconds, microseconds, or nanoseconds, but dropping in a weight of 1e3, 1e6, or 1e9 respectively.
The image below shows this approach in action. The output is set to match the input data type using "Inherit via back propagation" and the Data Type Duplication block. The four Weighted Sample Time blocks are set to have weight values of 1, 1e3, 1e6, and 1e9. So the outputs are in the units second, milliseconds, microseconds, and nanoseconds, respectively. I've added Signal Specification blocks and set the units via them, but this is NOT necessary. I added units and displayed them just to reinforce the key technique used to make plain integers workable.
As the displays show, seconds leads to an utterly useless accuracy. As the units used gets finer, the accuracy gets better. But do keep in mind that the integer gets bigger too. If you make the units too fine, the needed integer would be inefficiently big or overflow the integer type used. For example, using picoseconds would overflow an unsigned 32 bit integer type.
Fixed-Point vs Integer
This example showed how built-in integer types for the output could be made to work pretty well. But let's contrast this with the default case that used fixed-point types.
Automatic selection of fixed-point types achieved 5 digits of accuracy for a 16 bit output and 10 digits for the 32 bit case. Manual selection of weights and a unit change achieved 3, 6, and 9 digits of accuracy with integers. The fixed-point default achieved the highest accuracy automatically.
Automatic fixed-point type selection will make sure overflows don't happen. For the manual case, the user needs to take care to avoid overflows.
The fixed-point case allowed the user to remain in the original units and still get high efficiency and accuracy. The manual approach required changing the units on a signal. The ability to stay in the units prefered by the engineers using the model is a major advantage of fixed-point.
Manual change of units of a signal will often necessitate further manual changes in downstream blocks. With fixed-point, downstream blocks may also need to react to changes, but those changes are handled automatically by Simulink and Coders. For example, if a signal is changed to have 7 fractional bits instead of 8 fractional bits, then a downstream addition block will automatically handle that change. In addition, Simulink and Coders are also designed to automatically handle things like alignment of binary points with the smallest and fastest code possible.
When you look at the generated C or HDL code for a Simulink model using fixed-point types, you'll see that the low level code is just integer operations. So in concept, you could model everything in Simulink with plain integers, but there would be a lot of burden on you to correctly manage all the details and re-manage them as model changes are made. Fixed-Point Designer's type system automatically handles the original details and changes.
If Floating-Point is Fine, Just Pre-Cast
In some cases, like when the embedded microprocess you are targeting has no floating-point hardware unit, use of floating-point can be a big negative for efficiency. But in other cases, use of floating-point is fine. If you are happy to represent the sample time using floating-point, then another approach is possible. Simply drop in a data type conversion block on the signal wire leading into the Weighted Sample Time block. Then set the output data type of the conversion block to double or single. The output of the Weighted Sample Time block with "inherit via internal rule" will be the same type as its floating-point input.
Experimenting on Your Own with Attached
The screenshots in this answer were created with the attached model using R2019b. Please play around with them to see try out the concepts discussed.