Contenido principal

Generate FMU from C/C++ Code Using S-Function Builder

R2026b

This example shows how to generate a Functional Mock-up Unit (FMU) that is compatible with FMI 3.0 standards from your C/C++ code using the S-Function Builder. Use S-Function Builder to integrate your existing C/C++ code into Simulink®. You can then generate an FMU from the S-Function Builder block.

Generate FMU for First-Order Low Pass Filter

Open Model That Integrates C/C++ Code into Simulink

In this model, we integrate a C++ class that defines a first-order low pass filter into Simulink using an S-Function Builder.

The filter is defined by the following equation,

H(s)=(ωcutoffs+ωcutoff),

where:

ωcutoff is the cutoff frequency

The filter can be represented in time domain using Tustin Bilinear transform as,

yk=(1-α1+α)yk-1+(α1+α)[uk+uk-1],

α=(ωcutoffTs2),

where:

yk and yk-1 are the filter outputs at the current and previous time steps

uk and uk-1 are the filter inputs at the current and previous time steps

Ts is the sample time

OutputBusDefinition; 
open_system("FirstOrderLowPassFilterSFunctionBuilder.slx");

Integrate C/C++ Code and Build S-Function

Define the first-order low pass filter using C++ code.

class FirstOrderLowPassFilter
{
   private:
      double cutoffFrequency;
      double sampleTime; 
      double previousOutput;
      double previousInput; 

   public: 
      FirstOrderLowPassFilter(double cutoffFrequency, double sampleTime);
      double computeFilterOutput(double input); 
};

Define the methods.

#include "FirstOrderLowPassFilter.hpp"

FirstOrderLowPassFilter::FirstOrderLowPassFilter(double cutoffFreq, double SampleTime)
{
    cutoffFrequency = cutoffFreq;
    sampleTime = SampleTime;
    previousInput = 0.0;
    previousOutput = 0.0; 
}


double FirstOrderLowPassFilter::computeFilterOutput(double input)
{
    double alpha = (3.14*cutoffFrequency*sampleTime);

    double output = ((1-alpha)/(1+alpha))*previousOutput + ((alpha)/(1+alpha))*(input + previousInput); 

    previousOutput = output;
    previousInput = input; 

    return output; 
}  

The model uses the S-Function Builder block to integrate this code into Simulink and implement the low pass filter. Specify the source and header files names and paths in the Libraries table of the S-Function Builder editor.

handle = getSimulinkBlockHandle("FirstOrderLowPassFilterSFunctionBuilder/S-Function Builder");
Simulink.SFunctionBuilder.add(handle,"LibraryItem","LibraryItemTag","INC_PATH",'LibraryItemValue',fullfile(pwd));
Simulink.SFunctionBuilder.add(handle,"LibraryItem","LibraryItemTag","SRC_PATH",'LibraryItemValue',fullfile(pwd));
Simulink.SFunctionBuilder.add(handle,"LibraryItem","LibraryItemTag","ENTRY","LibraryItemValue",'FirstOrderLowPassFilter.cpp');
Simulink.SFunctionBuilder.add(handle,"LibraryItem","LibraryItemTag","ENTRY","LibraryItemValue",'FirstOrderLowPassFilter.hpp');

The S-Function Builder code uses one PWork to store a pointer to the instantiated filter object. The S-Function Builder is configured to have one input and one output port. The input port receives the noisy signal. The output port is configured with a bus data type. The bus output signal gives the filtered and unfiltered signal. The sample time and cutoff frequency are set as parameters with units for the S-Function Builder.

SFB_editor_lowPassFilter.png

Build the S-Function by clicking Build on the S-Function Builder editor toolstrip or run this code.

Simulink.SFunctionBuilder.build(handle);
Generating files: ###  'LowPassFilterSFcn.cpp' created successfully
Generating files: ###  'LowPassFilterSFcn_wrapper.cpp' created successfully
Generating files: ### Output folder is '/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792'
Generating files: ###  'LowPassFilterSFcn.tlc' created successfully
Generating files: ###  'LowPassFilterSFcn_bus.h' created successfully
Compiling: Compiling 'LowPassFilterSFcn.cpp' ....Please wait
### Using toolchain: GNU gcc/g++ | gmake (64-bit Linux)
### Creating '/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/SFBTemp/mex/LowPassFilterSFcn.mk' ...
### Building 'LowPassFilterSFcn': "/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/bin/glnxa64/gmake"  -j 4 -l 4 -f LowPassFilterSFcn.mk all
g++ -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -pthread -DMATLAB_MEX_FILE -DUSE_PUBLISHED_ONLY -DMATLAB_MEXCMD_RELEASE=R2018a -DMATLAB_DEFAULT_RELEASE=R2018a  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/simulink/include -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "LowPassFilterSFcn.o" "/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/LowPassFilterSFcn.cpp"
g++ -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -pthread -DMATLAB_MEX_FILE -DUSE_PUBLISHED_ONLY -DMATLAB_MEXCMD_RELEASE=R2018a -DMATLAB_DEFAULT_RELEASE=R2018a  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/simulink/include -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "LowPassFilterSFcn_wrapper.o" "/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/LowPassFilterSFcn_wrapper.cpp"
g++ -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -pthread -DMATLAB_MEX_FILE -DUSE_PUBLISHED_ONLY -DMATLAB_MEXCMD_RELEASE=R2018a -DMATLAB_DEFAULT_RELEASE=R2018a  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/simulink/include -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "FirstOrderLowPassFilter.o" "/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/FirstOrderLowPassFilter.cpp"
gcc -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -pthread -DMATLAB_MEX_FILE -DUSE_PUBLISHED_ONLY -DMATLAB_MEXCMD_RELEASE=R2018a -DMATLAB_DEFAULT_RELEASE=R2018a  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/simulink/include -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "c_mexapi_version.o" "/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/version/c_mexapi_version.c"
### Creating shared library "../../LowPassFilterSFcn.mexa64" ...
g++ -shared -Wl,--no-undefined -pthread -Wl,-rpath-link,/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/matlab/extern/bin/glnxa64 -Wl,-rpath-link,/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/matlab/extern/bin/glnxa64 -Wl,--version-script,LowPassFilterSFcn.def -o ../../LowPassFilterSFcn.mexa64 LowPassFilterSFcn.o LowPassFilterSFcn_wrapper.o FirstOrderLowPassFilter.o c_mexapi_version.o -Wl,--start-group -L/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/bin/glnxa64 -lmx -lmex -lMatlabDataArray  -Wl,--end-group
### Created: "../../LowPassFilterSFcn.mexa64"
### Successfully generated all binary outputs.
gmake: Nothing to be done for `all'.

Generate FMU from S-Function Builder

Use the Simulink.SFunctionBuilder.generateFMU function to generate FMU for the S-Function Builder block. For example, the following command generates a co-simulation FMU compatible with FMI 3.0 standards and adds a description for it.

Simulink.SFunctionBuilder.generateFMU(handle,'FMUType','CS','Description','FirstOrderLowPassFilter');
### Using toolchain: GNU gcc/g++ | gmake (64-bit Linux)
### Creating '/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/slprj/fmuWorkingDir/LowPassFilterSFcn.mk' ...
### Building 'LowPassFilterSFcn': "/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/bin/glnxa64/gmake"  -j 4 -l 4 -f LowPassFilterSFcn.mk all
gcc -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -DFMI_VERSION=3  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/rtw/c/src/rapid/fmu/fmi3 -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "fmi3Functions.o" "/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/slprj/fmuWorkingDir/fmi3Functions.c"
g++ -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -DFMI_VERSION=3  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/rtw/c/src/rapid/fmu/fmi3 -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "LowPassFilterSFcn.o" "/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/slprj/fmuWorkingDir/LowPassFilterSFcn.cpp"
g++ -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -DFMI_VERSION=3  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/rtw/c/src/rapid/fmu/fmi3 -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "LowPassFilterSFcn_wrapper.o" "/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/slprj/fmuWorkingDir/LowPassFilterSFcn_wrapper.cpp"
g++ -c -fwrapv -fPIC -Wno-error=incompatible-pointer-types -Wno-error=stringop-overflow -O0 -DFMI_VERSION=3  -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/rtw/c/src/rapid/fmu/fmi3 -I/mathworks/devel/bat/filer/batfs2566-0/Bdoc26b.3351752/build/runnable/matlab/extern/include -I/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792 -o "FirstOrderLowPassFilter.o" "/tmp/Bdoc26b_3351752_1255651/tpe24aae35/fmubuilder-ex40088792/FirstOrderLowPassFilter.cpp"
### Creating shared library "./LowPassFilterSFcn.so" ...
g++ -shared -Wl,--no-undefined -o ./LowPassFilterSFcn.so fmi3Functions.o LowPassFilterSFcn.o LowPassFilterSFcn_wrapper.o FirstOrderLowPassFilter.o -Wl,--start-group   -Wl,--end-group
### Created: "./LowPassFilterSFcn.so"
### Successfully generated all binary outputs.
gmake: Nothing to be done for `all'.
Packaging FMU: FMU 'LowPassFilterSFcn.fmu' created successfully.

Import and Simulate FMU in Simulink

Use the FMU Import block to import the generated FMU into a Simulink Model.

open_system("FirstOrderLowPassFilterFMU.slx");

The generated FMU has the same port, parameter, and unit configuration as specified in the S-Function Builder editor.

SFB_FMU_Import_Output.png

View and specify the parameters SampleTime and CutoffFrequency in the Parameters tab of the FMU Import block dialog box.

SFB_FMU_Import_Parameters.png

Simulate the model and observe the FMU output.

output = sim("FirstOrderLowPassFilterFMU.slx");
plotData = figure(1);
plot(output.yout{1}.Values,'-r','LineWidth',1.5);
hold on;
plot(output.yout{2}.Values,'--g','LineWidth',1.5);
legend('Filtered Signal','Noisy Signal');
xlabel('Time (s)');
ylabel('Signal Value');
grid minor; 

Figure contains an axes object. The axes object with title Time Series Plot:<FilteredSignal>, xlabel Time (s), ylabel Signal Value contains 2 objects of type stair. These objects represent Filtered Signal, Noisy Signal.

See Also

|