Main Content

Generate C++ Classes for MATLAB Classes That Model Simple and Damped Oscillators

MATLAB® classes provide a natural framework for modeling physical systems:

  • You can model a simple system as a MATLAB class. The private class properties are the system parameters. The class constructor creates an instance of the system with given parameters. A public method captures the dynamics of the system by returning the final state for a given initial state and a time interval. The class can also contain other helper methods that modularize the mathematical analysis.

  • You often start your analysis with a simple system and then introduce additional effects (such as mechanical damping) to increase the accuracy of your analysis. In MATLAB, you can model the enhanced system as a subclass that inherits from the original class. The subclass might contain additional private properties for additional system parameters (such as damping constant). Depending on the specifics of the system, the subclass might inherit certain methods from the base class and might overload the other methods.

This example shows how to generate C++ code for a MATLAB function that compares the time evolution of a simple oscillator and a damped oscillator with identical parameters and initial conditions. The two oscillator systems are modeled by using the MATLAB classes simpleOscillator and dampedOscillator that are defined inside a MATLAB namespace mySystem. The generated code contains C++ classes for the source MATLAB classes. The example also shows how the MATLAB classes map to the generated C++ classes and how to use the generated code in a custom C++ main function.

Simple and Damped Oscillators as MATLAB Classes

Governing Equations

A simple harmonic oscillator has two parameters, the mass m and the spring constant k. The angular frequency of the oscillator is ω=km. The position of the oscillator x as a function of time t is given by:

x(t)=Asin(ωt+ϕ).

The amplitude A and the phase constant ϕ are determined by the initial position x0 and the initial velocity v0 of the simple oscillator. In this example, the MATLAB class simpleOscillator models this system.

A damped harmonic oscillator has one additional parameter, the damping constant b. This example considers the case where the normalized damping parameter γ=b2m is small compared to the angular frequency ω such that only first-order damping effects are significant. The position of the damped oscillator xd as a function of time t is:

xd(t)=Ae-γtsin(ωt+ϕd)

Like before, the amplitude A and the phase constant ϕd are determined by the initial position x0 and the initial velocity v0 of the damped oscillator. The main effect of damping is to cause the amplitude to decay exponentially. In this example, the MATLAB class dampedOscillator which is a subclass of simpleOscillator models the damped system.

MATLAB and C++ Files

This example uses these supporting files that are present in the current working directory:

  • The namespace folder +mySystem contains the two class files simpleOscillator.m and dampedOscillator.m.

  • The function effectOfDamping calculates and returns the trajectories of a simple oscillator and a damped oscillator with given parameters and initial conditions.

  • The C++ header and source files main_damped_oscillator.h and main_damped_oscillator.cpp implement the custom C++ main function and are used to generate an executable in the last part of the example.

Run MATLAB Code

Define a structure params that has fields for the three oscillator parameters. Make sure the dampingConstant parameter is small compared to springConstant and mass (in normalized units).

params.springConstant = 1;
params.dampingConstant = 0.1;
params.mass = 1;

Call the effectOfDamping function to calculate the position vs. time trajectories of the simple and damped oscillators from t=0 to t=100. Specify initial position x0=1and initial velocity v0=0.

[time1,position1,time2,position2] = effectOfDamping(params,1,0,100,0.01);

Plot position vs. time graphs of the simple and damped oscillators. Observe how the amplitude of the damped oscillator decays exponentially with time.

plot(time1,position1)
hold on
plot(time2,position2)

Display the final position of the simple oscillator.

disp(position1(end))
    0.8623

Display the final position of the damped oscillator. Observe that damping causes this final position to be close to the mean position xmean=0.

disp(position2(end))
    0.0056

Generate and Run C++ MEX

To check for run-time issues, generate a C++ MEX function for the effectOfDamping function. Specify the first argument to have the same type and size as params. Specify the other arguments to be scalar doubles.

codegen -lang:c++ effectOfDamping -args {params,0,0,0,0} -report
Code generation successful: To view the report, open('codegen/mex/effectOfDamping/html/report.mldatx')

Call the generated MEX function effectOfDamping_mex to calculate the position vs. time trajectories of the simple and damped oscillators from t=0 to t=100. Specify initial position x0=1and initial velocity v0=0.

[time1,position1,time2,position2] = effectOfDamping_mex(params,1,0,100,0.01);

Plot position vs. time graphs of the simple and damped oscillators. Observe that the plot is identical to the one produced by the original MATLAB function.

plot(time1,position1)
hold on
plot(time2,position2)

Display the final positions of the two oscillators. These values are also identical to those produced by the original MATLAB code.

disp(position1(end))
    0.8623
disp(position2(end))
    0.0056

Clear the MEX file from memory.

clear effectOfDamping_mex

Generate and Inspect Static C++ Library

Create a code configuration object for generating a static C++ library with class interface. Specify the name of the interface class to be 'myOscillators'. For these settings, the code generator produces the entry-point function as a methods of the C++ class 'myOscillators'. The constructor and the destructor of this interface class implement the initialize and terminate functions, respectively.

cfg = coder.config('lib');
cfg.TargetLang = 'C++';
cfg.CppInterfaceStyle = 'Methods';
cfg.CppInterfaceClassName = 'myOscillators';

Adjust the global settings for function inlining to:

  • Preserve the modularity in the code that you wrote for better readability. Set InlineBetweenUserFunctions to 'Readability'.

  • Generate highly optimized code for MathWorks® functions, even if that results in less readable code because you are less likely to inspect this part of your code base. Set InlineBetweenMathWorksFunctions to 'Speed'.

  • In the generated code, separate functions that you write and MathWorks functions so that the generated code does not look very different from your MATLAB code. Set InlineBetweenUserAndMathWorksFunctions to 'Readability'.

cfg.InlineBetweenUserFunctions = 'Readability'; 
cfg.InlineBetweenUserAndMathWorksFunctions = 'Readability';
cfg.InlineBetweenMathWorksFunctions = 'Speed';

For more information about controlling function inlining behavior of the code generator, see Control Inlining to Fine-Tune Performance and Readability of Generated Code.

Generate a static C++ library by using the codegen command.

codegen -config cfg effectOfDamping -args {params,0,0,0,0} -report
Code generation successful: To view the report, open('codegen/lib/effectOfDamping/html/report.mldatx')

Open the code generation report and inspect the generated C++ source code:

  • The files simpleOscillator.h and simpleOscillator.cpp contain the implementation of the C++ class for the simple oscillator. The files dampedOscillator.h and dampedOscillator.cpp contain the implementation of the C++ class for the damped oscillator. The inheritance structure of the MATLAB classes is flattened in the generated code. So, dampedOscillator is not a subclass of simpleOscillator and reimplements all the methods that the corresponding MATLAB class inherits. For more information on the mapping between the MATLAB classes and the C++ classes, see Generate C++ Classes for MATLAB Classes.

  • The MATLAB namespace is mapped to a C++ namespace. In the generated code, the simpleOscillator and dampedOscillator classes are defined in the mySystem namespace. For more information, see Organize Generated C++ Code into Namespaces.

  • The files myOscillators.h and myOscillators.cpp contain the implementation of the interface class myOscillators. The entry-point function is implemented in the method myOscillators::effectOfDamping. The initialize and terminate functions are implemented in the class constructor and the class destructor, respectively. The next part of this example shows how to use this class interface in your custom C++ main function. For more information, see Generate C++ Code with Class Interface.

  • The size of output arguments of the effectOfDamping function are determined by the run-time inputs timeInterval and timeStep. So, the generated code represents these arguments as dynamic arrays C++ that are implemented by using the coder::array class template. The next part of this example shows how to use the coder::array class template in your custom C++ main function. For more information, see Use Dynamically Allocated C++ Arrays in Generated Function Interfaces.

For example, here is the declaration of the generated mySystem::simpleOscillator class contained in the header file simpleOscillator.h.

type codegen/lib/effectOfDamping/simpleOscillator.h
//
// File: simpleOscillator.h
//
// MATLAB Coder version            : 24.1
// C/C++ source code generated on  : 12-Feb-2024 21:02:52
//

#ifndef SIMPLEOSCILLATOR_H
#define SIMPLEOSCILLATOR_H

// Include Files
#include "rtwtypes.h"
#include "coder_array.h"
#include <cstddef>
#include <cstdlib>

// Type Definitions
namespace mySystem {
class simpleOscillator {
public:
  void init(double m, double k);
  void evolution(double initialPosition, double initialVelocity,
                 double timeInterval, double timeStep,
                 coder::array<double, 1U> &b_time,
                 coder::array<double, 1U> &position) const;
  double dynamics(double initialPosition, double initialVelocity,
                  double timeInterval) const;
  double amplitude(double initialPosition, double initialVelocity) const;
  double angularFrequency() const;
  double phase(double initialPosition, double initialVelocity) const;

protected:
  double mass;
  double springConstant;
};

} // namespace mySystem

#endif
//
// File trailer for simpleOscillator.h
//
// [EOF]
//

If you have Embedded Coder®, you can set the VerificationMode property of the configuration object to 'SIL' and generate a SIL MEX function effectOfDamping_sil. This SIL interface allows you to verify the production ready source code inside the MATLAB environment. See Software-in-the-Loop Execution from Command Line (Embedded Coder).

Generate and Run Executable

In the previous part of this example, when you generate library code, the code generator also produces example main files main.h and main.cpp in the examples subfolder of the build folder. The supporting C++ files main_damped_oscillator.h and main_damped_oscillator.cpp are modified versions of these example files.

  • In main_damped_oscillator.cpp, the main function uses the interface class myOscillators to interact with the generated code. This function uses the C++ new operator to allocate memory for an instance of myOscillators, invokes the main_effectOfDamping function, and finally frees the memory by using the C++ delete operator.

  • The main_effectOfDamping function performs the same computation that the MATLAB script in the first part of this example does. It uses the coder::array API to interact with the dynamic arrays that the generated effectOfDamping function return. At the end of its execution, the main_effectOfDamping function prints the final positions of the two oscillators.

Create a code configuration object for generating a C++ executable. Use the same settings as in the previous part of this example.

cfg = coder.config('exe');
cfg.TargetLang = 'C++';
cfg.CppInterfaceStyle = 'Methods';
cfg.CppInterfaceClassName = 'myOscillators';

cfg.InlineBetweenUserFunctions = 'Readability'; 
cfg.InlineBetweenUserAndMathWorksFunctions = 'Readability';
cfg.InlineBetweenMathWorksFunctions = 'Speed';

Specify the custom C++ source file and the custom include folder.

cfg.CustomSource = 'main_damped_oscillator.cpp';
cfg.CustomInclude = {pwd};

Generate an executable by using the codegen command.

codegen -config cfg effectOfDamping -args {params,0,0,0,0} -report
Code generation successful: To view the report, open('codegen/exe/effectOfDamping/html/report.mldatx')

Run the generated executable. Observe that the final positions of the two oscillators that this execution returns match the outputs of the original MATLAB code.

if isunix
    system('./effectOfDamping')
elseif ispc
    system('effectOfDamping.exe')
else
    disp('Platform is not supported')
end
0.862319
0.00563263
ans = 0

See Also

|

Related Topics