Engee documentation
Notebook

Integration of C code in Engee model using C Function block

This example shows different ways to integrate C code into the Engee model using the C Function block.

Before we start, let's connect the necessary libraries:

In [ ]:
using Plots
using MATLAB
plotlyjs();

Use the macro @__DIR__ to find out the folder where the interactive script lies:

In [ ]:
demoroot = @__DIR__
Out[0]:
"/user/start/examples/base_simulation/ccodeintegration"

Basics of Using the C Function Block

Let's look at simple examples of using the C Function block to illustrate the basics of integrating C code into the Engee model.

1. Working with multidimensional signals, passing parameters and using #define directive

In the model cCodeIntegration_basics.engee we have added the block C Function, which inputs a sinusoidal signal in:

basics_1.PNG

Suppose we want to increase the amplitude of the sinusoid in by a factor of gain and shift it by a value of bias. That is, the output signal should be determined by the formula out = gain * in + bias.

Let's go to the settings of the C Function block, where in the Ports tab we will familiarise ourselves with the definition of the block input and output. This is where the source code variables are linked to the ports of the C Function block:

  1. The scalar variable x of type double is bound to the first input port in;
  2. Vector (3 elements) variable y of type double is bound to the first output port out.

image_2.png

The gain array is also defined in the settings of the C Function block ( Parameters tab ):

image.png

It is worth noting that once a variable is passed as a parameter, it becomes:

  1. Global - gain can be accessed from any tab of the source code editor;
  2. With the qualifier const - an error will be received when trying to change the value of gain.

The offset value bias is determined by the identifier #define bias, which is set in the tab Build options:

image.png

In this example, the offset is 5.

Let's go to Main of the C Function block settings and click the "Edit Source Code" button. In the opened source code editor on the tab OutputCode the code executed at each step of the model calculation is given:

image.png

In the loop for (for each of the three output signals), the value of the variable y is determined at each step of the model calculation.

Let's simulate the system:

In [ ]:
cCodeIntegration_basics = engee.load("$demoroot/models/cCodeIntegration_basics.engee", force = true)
simulationResults_basics = engee.run(cCodeIntegration_basics)
Out[0]:
Dict{String, DataFrame} with 1 entry:
  "out" => 1001×2 DataFrame

Let's close the model:

In [ ]:
engee.close(cCodeIntegration_basics, force = true);

Import the results of the simulation:

In [ ]:
cCodeIntegration_basics_t = simulationResults_basics["out"].time;

cCodeIntegration_basics_y1 = [y[1] for y in simulationResults_basics["out"].value];
cCodeIntegration_basics_y2 = [y[2] for y in simulationResults_basics["out"].value];
cCodeIntegration_basics_y3 = [y[3] for y in simulationResults_basics["out"].value];

Plot the graph:

In [ ]:
plot(cCodeIntegration_basics_t, cCodeIntegration_basics_y1)
plot!(cCodeIntegration_basics_t, cCodeIntegration_basics_y2)
plot!(cCodeIntegration_basics_t, cCodeIntegration_basics_y3)

title!("Выходной сигнал блока C Function <br> (Многомерные сигналы, параметры и #define)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

2. Using integer data types and static variables

In the model cCodeIntegration_integers.engee we implement a simple counter using the C Function block, the output of which is a positive integer denoting the number of the current iteration:

ccodeintegration_integers_1.PNG

Let's analyse the contents of the Ports and Build Options tabs in the C Function block settings:

image.png

image.png

The names of integer data types in the C Function block are defined according to the header file <stdint.h> of the C language standard library. This file is connected in the line Headers of the tab Build options.

The tab Ports binds a 64-bit variable without the sign out (the type is set as uint64_t, not long, see the previous paragraph about <stdint.h>) of the source code to the first output port of the C Function block.

The Output code tab of the source code editor of the C Function block contains the code executed at each step of the simulation:

image.png

The static variable counter is initialised to 0 in the first step of the simulation, after which at each iteration its value is incremented by 1 and assigned to the variable out.

Let's simulate the system:

In [ ]:
cCodeIntegration_integers = engee.load("$demoroot/models/cCodeIntegration_integers.engee", force = true)
simulationResults_integers = engee.run(cCodeIntegration_integers)
Out[0]:
Dict{String, DataFrame} with 1 entry:
  "counter" => 1001×2 DataFrame

Let's close the model:

In [ ]:
engee.close(cCodeIntegration_integers, force = true);

Import the results of the simulation:

In [ ]:
cCodeIntegration_integers_t = simulationResults_integers["counter"].time;
cCodeIntegration_integers_y = simulationResults_integers["counter"].value;

Plot the graph:

In [ ]:
plot(cCodeIntegration_integers_t, cCodeIntegration_integers_y, legend = false)

title!("Выходной сигнал блока C Function <br> (Целочисленные типы и статические переменные)")
xlabel!("Время, [с]")
ylabel!("Номер итерации")
Out[0]:

For more information about the purpose of the source code editor tabs, plotting options, and available data types, see in the documentation Engee.

Integrating external source code

Task statement

Let's consider a more interesting engineering problem.

Suppose we need to control a stable second-order object that is subject to external disturbances, given the measurement noise. Consequently, we need to solve the problem of filtering the output signal of the control object, and fortunately, Engee has a tool that is perfect for this!

The files alphabetafilter.c and alphabetafilter.h offer a C implementation of the alpha-beta filter.

The contents of the file alphabetafilter.c:

#include "alphabetafilter.h"
#include <math.h>

double dt = 0.01;
static double xk_1, vk_1;

Parameters determineAlphaBeta(double processNoiseVariance, double measurementNoiseVariance)
{
    double lambda, r;
    Parameters p;

    lambda = (processNoiseVariance * pow(dt, 2)) / measurementNoiseVariance;
    r = (4 + lambda - sqrt(8 * lambda + pow(lambda, 2))) / 4;

    p.alpha = 1 - pow(r, 2);
    p.beta = 2 * (2 - p.alpha) - 4 * sqrt(1 - p.alpha);

    return p;
}

double alphaBetaFilter(double value, Parameters p)
{
    double xk, vk, rk;

    xk = xk_1 + (vk_1 * dt);
    vk = vk_1;

    rk = value - xk;

    xk += p.alpha * rk;
    vk += (p.beta * rk) / dt;

    xk_1 = xk;
    vk_1 = vk;

    return xk;
}

void initialiseStaticVariables()
{
    xk_1 = 0;
    vk_1 = 0;
}

The contents of the file alphabetafilter.h:

typedef struct {
    double alpha;
    double beta;
} Parameters;

Parameters determineAlphaBeta(double, double);
double alphaBetaFilter(double, Parameters);
void initialiseStaticVariables();

Integrate this source code into our Engee model.

Analysis of the model

Model cCodeIntegration_source.engee:

source_1.PNG

This model consists of:

  1. The control object Plant;
  2. Subsystem realising the second-order PID controller Controller;
  3. Blocks Band-Limited White Noise ProcessNoise and MeasurementNoise, modelling the external influence and measurement noise;
  4. Block C Function Filter, realising the alpha-beta filter.

The parameter Sample Time of the block Filter is equal to the step of the model solver Ts:

image.png

Let's analyse the contents of the Build options tab of the Filter block settings:

image.png

In the rows of this tab:

  • Source files - connected source code file alphabetafilter.c;
  • Iinclude directories - the path to the folder containing the header file is set;
  • Headers - connected header file alphabetafilter.h.

In the Ports tab of block settings source code variables are linked to input and output ports of the block Filter.

In the source code of the Filter block, the OutputCode tab contains cyclically executable code:

image.png

At each step of the model calculation, the functions determineAlphaBeta() and alphabetafilter() are called sequentially, after which the filtered value is stored in the variable out, corresponding to the output port x of the block Filter.

Simulation start

Let's simulate the system:

In [ ]:
cCodeIntegration_source = engee.load("$demoroot/models/cCodeIntegration_source.engee", force = true)
simulationResults_source = engee.run(cCodeIntegration_source)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

Let's close the model:

In [ ]:
engee.close(cCodeIntegration_source, force = true);

Simulation results

Import simulation results:

In [ ]:
cCodeIntegration_source_noisy_t = simulationResults_source["noisy"].time;
cCodeIntegration_source_noisy_y = simulationResults_source["noisy"].value;
In [ ]:
cCodeIntegration_source_filtered_t = simulationResults_source["filtered"].time;
cCodeIntegration_source_filtered_y = simulationResults_source["filtered"].value;

Plot the graph:

In [ ]:
plot(cCodeIntegration_source_noisy_t, cCodeIntegration_source_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_source_filtered_t, cCodeIntegration_source_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция внешнего исходного кода)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

Integration of static and dynamic libraries

Task Statement

Often source code is not supplied explicitly, but as part of static or dynamic libraries. C API libraries integrate into Engee models as easily as external source code!

Let's compile a static (alphabetafilter.a) and dynamic (alphabetafilter.so) library from the files alphabetafilter.c and alphabetafilter.h using MakeFile.

The contents of the file Makefile:

all: alphabetafilter.a alphabetafilter.so

alphabetafilter.o: alphabetafilter.c
	$(CC) -c -fPIC $^ -o $@

alphabetafilter.a: alphabetafilter.o
	ar rcs $@ $^

alphabetafilter.so: alphabetafilter.o
	$(CC) -shared $^ -o $@
    
clean:
	rm -f *.o *.a *.so

Let's start compiling the libraries:

In [ ]:
run(`make -C $demoroot/source`)
make: Entering directory '/user/start/examples/base_simulation/ccodeintegration/source'
cc -c -fPIC alphabetafilter.c -o alphabetafilter.o
ar rcs alphabetafilter.a alphabetafilter.o
cc -shared alphabetafilter.o -o alphabetafilter.so
make: Leaving directory '/user/start/examples/base_simulation/ccodeintegration/source'
Out[0]:
Process(`make -C /user/start/examples/base_simulation/ccodeintegration/source`, ProcessExited(0))

Integrate the static and dynamic library into our Engee model.

Static library: Analyse the model

By its structure, the model cCodeIntegration_library_static.engee does not differ from the previously considered cCodeIntegration_source.engee; only the directives on the Build options tab of the Filter block settings differ. Let's analyse its contents:

image.png

The following construction options are defined here:

  • Include directories - the path to the folder containing the header file is specified;
  • Library directories - the path to the folder containing static library is set;
  • Headers - connected header file alphabetafilter.h;
  • Libraries - static library alphabetafilter.a is connected.

Static library: Start simulation

Let's simulate the system:

In [ ]:
cCodeIntegration_library_static = engee.load("$demoroot/models/cCodeIntegration_library_static.engee", force = true)
simulationResults_library_static = engee.run(cCodeIntegration_library_static)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

Let's close the model:

In [ ]:
engee.close(cCodeIntegration_library_static, force = true);

Static Library: Modelling Results

Import the simulation results:

In [ ]:
cCodeIntegration_library_static_noisy_t = simulationResults_library_static["noisy"].time;
cCodeIntegration_library_static_noisy_y = simulationResults_library_static["noisy"].value;
In [ ]:
cCodeIntegration_library_static_filtered_t = simulationResults_library_static["filtered"].time;
cCodeIntegration_library_static_filtered_y = simulationResults_library_static["filtered"].value;

Plot the graph:

In [ ]:
plot(cCodeIntegration_library_static_noisy_t, cCodeIntegration_library_static_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_library_static_filtered_t, cCodeIntegration_library_static_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция статической библиотеки)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

Dynamic Library: Model Analysis

By its structure, the model cCodeIntegration_library_dynamic.engee does not differ from the previously considered cCodeIntegration_source.engee; only the directives on the Build options tab of the Filter block settings differ. Let's analyse its contents:

image.png

The following construction options are defined here:

  • Include directories - the path to the folder containing the header file is specified;
  • Library directories - the path to the folder containing the dynamic library is set;
  • Headers - the header file alphabetafilter.h is connected;
  • Libraries - connected dynamic library alphabetafilter.so.

In the Start code tab of the source code editor of the Filter block, the function initializeStaticVariables() is called:

dynamic_2.PNG

The function initializeStaticVariables() is declared in the file alphabetafilter.h and is intended to initialise the static variables xk_1 and vk_1, used in the dynamic library. This is necessary so that when the simulation is restarted, the variables do not save their last calculated value, but are reset to the initial value (in this case to 0).

Dynamic Library: Running a Simulation

Let's simulate the system:

In [ ]:
cCodeIntegration_library_dynamic = engee.load("$demoroot/models/cCodeIntegration_library_dynamic.engee", force = true)
simulationResults_library_dynamic = engee.run(cCodeIntegration_library_dynamic)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

Let's close the model:

In [ ]:
engee.close(cCodeIntegration_library_dynamic, force = true);

Dynamic Library: Modelling Results

Import the simulation results:

In [ ]:
cCodeIntegration_library_dynamic_noisy_t = simulationResults_library_dynamic["noisy"].time;
cCodeIntegration_library_dynamic_noisy_y = simulationResults_library_dynamic["noisy"].value;
In [ ]:
cCodeIntegration_library_dynamic_filtered_t = simulationResults_library_dynamic["filtered"].time;
cCodeIntegration_library_dynamic_filtered_y = simulationResults_library_dynamic["filtered"].value;

Plot the graph:

In [ ]:
plot(cCodeIntegration_library_dynamic_noisy_t, cCodeIntegration_library_dynamic_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_library_dynamic_filtered_t, cCodeIntegration_library_dynamic_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция динамической библиотеки)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

Problem Statement

Let's generate C code from the Simulink model alphabetafilter.slx and integrate it into our Engee model cCodeIntegration_cg_simulink.engee.

The model alphabetafilter.slx, which implements the alpha-beta filter, is built in Simulink from simple blocks and consists of subsystems determineAlphaBeta and filterInputSignal.

Top level of the model:

codegen_simulink_2.png

Subsystem determineAlphaBeta:

codegen_simulink_3.png

Subsystem filterInputSignal:

codegen_simulink_4.png

Let's generate the code using Simulink Embedded Coder:

In [ ]:
cgPath = joinpath(demoroot,"cg/simulink")
cgModel = joinpath(demoroot,"cg/simulink/alphabetafilter")

mat"""
model = load_system($cgModel);
path = $cgPath;

set_param(0, 'CacheFolder', path)
set_param(0, 'CodeGenFolder', path)

slbuild(model)

set_param(0, 'CacheFolder', '')
set_param(0, 'CodeGenFolder', '')
"""
>> >> >> >> >> >> >> >> >> ### Starting build procedure for: alphabetafilter
### Generating code and artifacts to 'Model specific' folder structure
### Generating code into build folder: /user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw
### Invoking Target Language Compiler on alphabetafilter.rtw
### Using System Target File: /matlab/rtw/c/ert/ert.tlc
### Loading TLC function libraries
.......
### Generating TLC interface API for custom data
.
### Initial pass through model to cache user defined code
### Caching model source code
...................................
### Writing header file alphabetafilter_types.h
.
### Writing header file alphabetafilter.h
### Writing header file rtwtypes.h
### Writing source file alphabetafilter.c
### Writing header file alphabetafilter_private.h
### Writing source file alphabetafilter_data.c
.
### Writing source file ert_main.c
### TLC code generation complete (took 18.076s).
[Warning: P-code file /matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.p
is older than source code file
/matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.m.
/matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.p may be obsolete and
may need to be regenerated.
Type "help pcode" for information about generating P-code.] 
### Saving binary information cache.
### Using toolchain: GNU gcc/g++ | gmake (64-bit Linux)
### Creating '/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/alphabetafilter.mk' ...
### Building 'alphabetafilter': "/matlab/bin/glnxa64/gmake"  -f alphabetafilter.mk all
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "alphabetafilter.o" "/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/alphabetafilter.c"
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "alphabetafilter_data.o" "/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/alphabetafilter_data.c"
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "ert_main.o" "/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/ert_main.c"
### Creating standalone executable ../alphabetafilter ...
g++  -o ../alphabetafilter alphabetafilter.o alphabetafilter_data.o ert_main.o  
### Created: ../alphabetafilter
### Successfully generated all binary outputs.
gmake: Nothing to be done for `all'.
### Successful completion of build procedure for: alphabetafilter
### Simulink cache artifacts for 'alphabetafilter' were created in '/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter.slxc'.

Build Summary

Top model targets built:

Model            Action                        Rebuild Reason                                    
=================================================================================================
alphabetafilter  Code generated and compiled.  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 46.583s

Model Analysis

By its structure, the model cCodeIntegration_cg_simulink.engee does not differ from the previously considered cCodeIntegration_source.engee; only the directives on the Build options tab of the Filter block settings differ. Let's analyse its contents:

image.png

The following construction options are defined here:

  • Source files - connected source code files alphabetafilter.c and alphabetafilter_data.c;
  • Include directories - the path to the folder containing header files is set;
  • Headers - header files alphabetafilter.h, alphabetafilter_types.h and rtwtypes.h are connected.

Cyclically executable code is given on the Output code tab of the source code editor of the Filter block:

image.png

At each step of the model calculation is performed:

  • Initialisation of the structure alphabetafilter_U with variables in, processNoiseVariance and measurementNoiseVariance (input signals);

  • Calling the function alphabetafilter_step();

  • Assignment to the variable out (output signal) of the result x, stored in the structure alphabetafilter_Y.

In the Start code tab of the source code editor of the Filter block, the function alphabetafilter_initialize() is called:

codegen_simulink_6.png

In the Terminate code tab of the source code editor of the Filter block, the function alphabetafilter_terminate() is called:

codegen_simulink_7.png

Simulation start

Let's simulate the system:

In [ ]:
cCodeIntegration_cg_simulink = engee.load("$demoroot/models/cCodeIntegration_cg_simulink.engee", force = true)
simulationResults_cg_simulink = engee.run(cCodeIntegration_cg_simulink)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

Let's close the model:

In [ ]:
engee.close(cCodeIntegration_cg_simulink, force = true);

Simulation results

Import simulation results:

In [ ]:
cCodeIntegration_cg_simulink_noisy_t = simulationResults_cg_simulink["noisy"].time;
cCodeIntegration_cg_simulink_noisy_y = simulationResults_cg_simulink["noisy"].value;
In [ ]:
cCodeIntegration_cg_simulink_filtered_t = simulationResults_cg_simulink["filtered"].time;
cCodeIntegration_cg_simulink_filtered_y = simulationResults_cg_simulink["filtered"].value;

Plot the graph:

In [ ]:
plot(cCodeIntegration_cg_simulink_noisy_t, cCodeIntegration_cg_simulink_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_cg_simulink_filtered_t, cCodeIntegration_cg_simulink_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция кода, сгенерированного из модели Simulink)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

Integration of code generated from the Engee model

Task Statement

Let's generate C code from the model Engee alphabetafilter.engee, built from simple blocks and implementing the alpha-beta filter, and integrate it into our model Engee cCodeIntegration_cg_engee.engee.

Top level of the model:

codegen_engee_2.PNG

Subsystem determineAlphaBeta:

codegen_engee_3.PNG

Subsystem filterInputSignal:

codegen_engee_4.PNG

Let's generate the code using the Engee code generator:

In [ ]:
engee.generate_code("$demoroot/cg/engee/alphabetafilter.engee", "$demoroot/cg/engee/alphabetafilter_cg")
[ Info: Generated code and artifacts: /user/start/examples/base_simulation/ccodeintegration/cg/engee/alphabetafilter_cg

Model Analysis

By its structure, the model cCodeIntegration_cg_engee.engee does not differ from the previously considered cCodeIntegration_source.engee; only the directives on the Build options tab of the Filter block settings differ. Let's analyse its contents:

image.png

The following construction options are defined here:

  • Source files - connected source code file alphabetafilter.c;
  • Include directories - the path to the folder containing header files is set;
  • Headers - connected header file alphabetafilter.h.

Cyclically executable code is given on the Output code tab of the source code editor of the Filter block:

image.png

At each step of the model calculation is performed:

  • Initialisation of the structure alphabetafilter_U with variables in, processNoiseVariance and measurementNoiseVariance (input signals);

  • Calling the function alphabetafilter_step();

  • Assignment to the variable out (output signal) of the result x, stored in the structure alphabetafilter_Y.

In the Start code tab of the source code editor of the Filter block, the function alphabetafilter_init() is called:

codegen_engee_6.PNG

In the Terminate code tab of the source code editor of the Filter block, the function alphabetafilter_term() is called:

codegen_engee_7.PNG

Simulation start

Let's simulate the system:

In [ ]:
cCodeIntegration_cg_engee = engee.load("$demoroot/models/cCodeIntegration_cg_engee.engee", force = true)
simulationResults_cg_engee = engee.run(cCodeIntegration_cg_engee)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

Let's close the model:

In [ ]:
engee.close(cCodeIntegration_cg_engee, force = true);

Simulation results

Import simulation results:

In [ ]:
cCodeIntegration_cg_engee_noisy_t = simulationResults_cg_engee["noisy"].time;
cCodeIntegration_cg_engee_noisy_y = simulationResults_cg_engee["noisy"].value;
In [ ]:
cCodeIntegration_cg_engee_filtered_t = simulationResults_cg_engee["filtered"].time;
cCodeIntegration_cg_engee_filtered_y = simulationResults_cg_engee["filtered"].value;

Plot the graph:

In [ ]:
plot(cCodeIntegration_cg_engee_noisy_t, cCodeIntegration_cg_engee_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_cg_engee_filtered_t, cCodeIntegration_cg_engee_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция кода, сгенерированного из модели Engee)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

Conclusions

This example demonstrates the features of using the C Function block and the different ways to integrate C code into the Engee model:

  1. Integration of external source code;
  2. Integration of static and dynamic libraries;
  3. Integration of code generated from the Simulink model;
  4. integration of code generated from the Engee model.

Blocks used in example