Convert LabVIEW MATLAB Functions to Shared Objects on Linux Real Time Targets With Intel x64 Architecture

Updated Sep 9, 2024

Environment

Software

  • LabVIEW
  • LabVIEW Real-Time Module
  • MATLAB

Operating System

  • Linux
  • Windows

Other

This article explains how to:
  1. Use the MATLAB Coder to convert the MATLAB function(s) to a C/C++ source files 
  2. Build the source into a shared object (.so file). 
  3. Deploy the shared objects in the RT machine and creating a VI that can be altered to use a call library node instead of the MathScript node with similar functionality as before.
Notes: Please consider the points below before going through the steps described for this task.
  • In LabVIEW 2023 Q3 and later, the MathScript Node will not be available in new applications. To ensure already built applications still work, this article converts the MATLAB functions to shared objects on Linux Real-Time (RT) Targets.
  • Ensure you have followed the steps in Migrating to LabVIEW MATLAB Functions from MathScript Nodes. This allows VIs using the MathScript node (which in turn is using a MATLAB function) to be runnable in Linux RT after removing the MathScript node.
  • Ensure your RT device has Intel (x64) architecture, RT devices with ARM architecture are not supported for this solution.
    • Use this link to verify the architecture of your RT target.

Convert MATLAB functions to C/C++ Sources using MATLAB Coder

  1. Prepare the code for use with MATLAB Coder by
    1. Modify the code so that the MATLAB coder is aware of all the data types used in the function.
    2. Add the directive: %#codgen to tell MATLAB Code Analyzer that the code in the function is intended for code generation. This will help us diagnose any errors that might occur in the code generation process.
  2. Under the MATLAB Apps tab ribbon, click on the MATLAB Coder app.  
    1. This opens a dialog that asks you to select the entry point functions for conversion.  
    2. Choose the path to the prepared MATLAB code and click Next.
    3. If the MATLAB function has any input arguments, choose the appropriate input types and click Next.
    4. Check for run-time errors or other issues if you have written a test script that invokes the function which you are trying to convert. Click Next.
    5. In the Build Type drop-down select Source code (C/C++) and click on Generate button.
  3. See the build log output result and check that there are no errors.
    1. If there is an error, follow the guidance from the build log output to troubleshoot the issue. For further problems, consult MATLAB Support
 

Building the Shared Object from the Source Files 

  1. We need to build a shared object in a Linux environment. If you do not have a Linux OS installed, then install a Windows subsystem for Linux (WSL). This will allow us to make use of Linux command line utilities and compilers directly on Windows.
    1. Install Windows Subsystem for Linux by running the command wsl --install in PowerShell.
    By default, the Ubuntu distribution will be installed.
  2. Copy over the header files present under C:\Program Files\MATLAB\<MATLAB Version>\extern\include to the Linux workspace.
    1. Install gcc and g++ if not already available in the Linux environment
    2. Run command gcc -print-search-dirs to get the list of all search directories. Copy all the header files into one of the search directories for gcc.
  3. Copy the output folder (generated from MATLAB Coder) to a location that is available to the Linux system. 
  4. Navigate to the folder from the Linux system and run the commands as shown in the example below to build the shared object.
    1. Generate the .o output file (repeat as necessary) : gcc -c -Wall -fpic <file name>.cpp -o <output file name>.o -lm  
    2. Generate the .so shared object using object files generated in the previous step : gcc <output file name 1>.o <output file name x>.o -shared -o <shared object file name>.so 
 

Deploying the created shared object to Linux RT

  1. Use file transfer tools to connect to the RT machine. The IP for the machine can be obtained from NI Measurement and Automation Explorer (MAX) if it is connected. 
    1. Note down the location where the file is deployed. In the example case, it is copied over to /usr/local/bin/ folder.
    2. Determine the name of the exported function to be invoked. As there is no external "C" qualifier in the exported functions, the actual function names will be mangled.
      1. In the Linux environment run the command nm -D <Generated .so file>
      2. Identify the mangled name from the output
  2. Set up the call library node on the VI which will be deployed to the Linux RT target. 
    1. Create a LabVIEW Project and add the RT target to the project.
    2. Add a new VI under the RT target
    3. Create a call library node in the new VI 
      1. Configure the Library name to load the .so file from the deployed location that we noted down in the previous step
      2. Select the Function name from the drop-down that matches the name determined in the previous step
      3. Look for the header file from the generated C/C++ source code to determine the inputs and outputs and configure the function prototype.
    4. Connect the input and output terminals of the call library node and run the VI and check that there are no errors
Note: MATLAB Coder output can have some differences compared to the original MATLAB function. Read about the potential differences here.
 

This example uses:
  • MATLAB node - Lorenz Diff Eq.vi example available from the LabVIEW examples. 
  • Windows Subsystem for Linux operating system.
 

LorenzDiffEq.m File:

function[t,Y] = LorenzDiffEq()
    y= [1, 1, 1];
    n = 30 / 0.05;
    % Generate a vector of values which are 
    % linearly spaced between 0 and 30
    T = linspace(0, 30, n);
    % Solve nonstiff differential equations — low order method
    [t, Y] = ode23('LorenzEqs',T, y );
end

 

LorenzEqs.m File:

%Lorenz attractor equations
function dxdt = LorenzEqs(~, x)
   dxdt(1) = 10*(x(2)-x(1));
   dxdt(2) = x(1)*(28-x(3))-x(2);
   dxdt(3) = x(1)*x(2)-(8/3)*x(3);
   dxdt = dxdt';
end

 

Step 1: Generating the C/C++ source code

  1. Prepare the code for use with MATLAB Coder
    1. Modify the above code so that the coder is aware of the datatype of dxdt in the function LorenzEqs.
      1. Specify that dxdt is a vector with 3 elements
      2. Add directive %#codgen which tells MATLAB Code Analyzer that the code in the function is intended for code generation
    2. The modified code will look as follows:
      • %Lorenz attractor equations
        function dxdt = LorenzEqs(~, x)
           %#codegen
           dxdt = zeros(1, 3);
           dxdt(1) = 10*(x(2)-x(1));
           dxdt(2) = x(1)*(28-x(3))-x(2);
           dxdt(3) = x(1)*x(2)-(8/3)*x(3);
           dxdt = dxdt';
        end
  2. Generating C/C++ source using MATLAB Coder
    1. Launch MATLAB coder from MATLAB Apps tab ribbon. This opens up the dialog box to select the entry point function. Once the entry point function is selected the screen will be as shown below.   
Picture1.png
 
In this example, both functions don’t have any inputs, so there is no need to choose any input types. For other cases when the function has input arguments, it is necessary to properly define the inputs.
Picture2.png
  1. (Optional) Check for run-time errors or other issues if you have written a test script that invokes the function. The test script can also be used to auto-generate input types in the Define Input stage.
Picture3.png
  1. Navigate next to the generate dialog, select the Build type as Source Code from the drop-down menu. The screen will be as shown below:Picture4.png
  2. Click the Generate button to generate the source code. Check the build log and verify that there are no errors
 

Step 2: Building the shared object

  1. If using Windows Subsystem for Linux as the environment for building, access the file system in Windows Explorer by entering \\wsl$ in the address bar
​​​​​​Picture5.png
  1. Copy the source code (generated using MATLAB Coder) to a temporary directory available to the Linux system.
Picture6.png
  1. Copy the header files from C:\Program Files\MATLAB\R2021a\extern\include (this is for R2021a) to one of the search directories for gcc. Run the command gcc -print-search-dirs in bash to get the list of directories. For this example, the header files are copied to <...>/include directory as shown below
Picture7.png
  1. In the source code (generated using MATLAB coder) you will notice that there are many files that are generated. For this example use case, only the following files are relevant:
    • LorenzDiffEq.cpp (will contain the exported function which we will invoke)
    • rt_nonfinite.cpp (contains constants needed by LorenzDiffEq.cpp)
    • Note: If you can’t figure out which files are necessary (in the final shared object file) then build everything into the shared object.
  2. In the Linux system, navigate to the copied folder and run the following commands to build the shared object:
    • gcc -c -Wall -fpic LorenzDiffEq.cpp -o lor.o -lm  (generates lor.o output file)
    • gcc -c -Wall -fpic rt_nonfinite.cpp -o rt.o -lm  (generates rt.o output file)
    • gcc lor.o rt.o -shared -o lor.so (generates the shared object lor.so from the above two object files)
 

Step 3: Deploying the shared object to Linux RT target

  1. Obtain the IP address of the RT target from NI MAX if the target is connected to the network
  2. Use the file transfer tool to connect to the RT Target and copy the shared object to the directory in the RT Target. For this example, we are copying the shared object to the /usr/local/bin folder 
  3. Determine the name of the exported function to be invoked by running the command nm -D lor.so in the Linux environment. The name LorenzDiffEq is mingled to _Z12LorenzDiffEqPdPiS_S0_ as shown below  
 Picture8.png
  1. To set up the call library node in the next step, it is necessary to know the data types of the parameters. From the header file (LorenzDiffEq.h) we know the function prototype to be: 
    • extern void LorenzDiffEq(double t_data[], int t_size[1], double Y_data[], int Y_size[2]); 
  2. In this example, we are passing in two arrays. Look at the main.cpp file under the example folder in the code generated using MATLAB Coder. As shown in the below code the Y array size is 1800 and the t array size is 600. Use this data when passing the array into the call library node in the next step
Picture9.png  
 

Step 4: Setup call library node in RT VI

  1. Create a LabVIEW Project and add the RT target to the project
  2. Add a new VI under the RT target
  3. Create a Call Library node in the new VI and configure the node in the following way:
Picture10.png
  1. In the VI, when connecting the array controls, initialize the array sizes to 600 and 1800, as determined in Step 3.