MATLAB® Coder™ generates highly optimized ANSI C and C++ code from functions and System objects in DSP System Toolbox™ . You can deploy this code in a wide variety of applications.
This example generates C code from the Construct a Sinusoidal Signal Using High Energy FFT Coefficients example and builds an executable from the generated code.
Here is the MATLAB code for this example:
L = 1020; Sineobject = dsp.SineWave('SamplesPerFrame',L,... 'PhaseOffset',10,'SampleRate',44100,'Frequency',1000); ft = dsp.FFT('FFTImplementation','FFTW'); ift = dsp.IFFT('FFTImplementation','FFTW','ConjugateSymmetricInput',true); rng(1); numIter = 1000; for Iter = 1:numIter Sinewave1 = Sineobject(); Input = Sinewave1 + 0.01*randn(size(Sinewave1)); FFTCoeff = ft(Input); FFTCoeffMagSq = abs(FFTCoeff).^2; EnergyFreqDomain = (1/L)*sum(FFTCoeffMagSq); [FFTCoeffSorted, ind] = sort(((1/L)*FFTCoeffMagSq),1,'descend'); CumFFTCoeffs = cumsum(FFTCoeffSorted); EnergyPercent = (CumFFTCoeffs/EnergyFreqDomain)*100; Vec = find(EnergyPercent > 99.99); FFTCoeffsModified = zeros(L,1); FFTCoeffsModified(ind(1:Vec(1))) = FFTCoeff(ind(1:Vec(1))); ReconstrSignal = ift(FFTCoeffsModified); end max(abs(Input-ReconstrSignal)) plot(Input,'*'); hold on; plot(ReconstrSignal,'o'); hold off;
You can run the generated executable inside the MATLAB environment. In addition, you can package and relocate the code to another
development environment that does not have MATLAB installed. You can generate code using the MATLAB
Coder app or the codegen
function. This example shows you
the workflow using the codegen
function. For more information on
the app workflow, see Generate C Code by Using the MATLAB Coder App (MATLAB Coder).
The first step is to set up a supported C compiler. MATLAB
Coder automatically locates and uses a supported installed compiler. You can
change the default compiler using mex -setup
. For more details,
see Change Default Compiler (MATLAB). For a
current list of supported compilers, see Supported and
Compatible Compilers.
To generate C code, the entry point must be a function. You do not have to generate code for the entire MATLAB application. If you have specific portions that are computationally intensive, generate code from these portions in order to speed up your algorithm. The harness or the driver that calls this MATLAB function does not need to generate code. The harness runs in MATLAB and can contain visualization and other verification tools that are not actually part of the system under test. For example, in the Construct a Sinusoidal Signal Using High Energy FFT Coefficients example, the plot
functions plot the input signal and the reconstructed signal. plot
is not supported for code generation and must stay in the harness. To generate code from the harness that contains the visualization tools, rewrite the harness as a function and declare the visualization functions as extrinsic functions using coder.extrinsic
. To run the generated code that contains the extrinsic functions, you must have MATLAB installed on your machine.
The MATLAB code in the for
loop that reconstructs the original signal using high-energy FFT coefficients is the computationally intensive portion of this algorithm. Speed up the for
loop by moving this computational part into a function of its own, GenerateSignalWithHighEnergyFFTCoeffs.m
.
L = 1020; Sineobject = dsp.SineWave('SamplesPerFrame',L,... 'SampleRate',44100,'Frequency',1000); rng(1); numIter = 1000; for Iter = 1:numIter Sinewave1 = Sineobject(); Input = Sinewave1 + 0.01*randn(size(Sinewave1)); [ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input); end max(abs(Input-ReconstrSignal)) figure(1); plot(Input) hold on; plot(ReconstrSignal,'*') hold off
function [ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input) ft = dsp.FFT('FFTImplementation','FFTW'); ift = dsp.IFFT('FFTImplementation','FFTW','ConjugateSymmetricInput',true); FFTCoeff = ft(Input); FFTCoeffMagSq = abs(FFTCoeff).^2; L = size(Input,1); EnergyF = (1/L)*sum(FFTCoeffMagSq); [FFTCoeffSorted, ind] = sort(((1/L)*FFTCoeffMagSq),1,'descend'); CumFFTCoeffs = cumsum(FFTCoeffSorted); EnergyPercent = (CumFFTCoeffs/EnergyF)*100; Vec = find(EnergyPercent > 99.99); FFTCoeffsModified = zeros(L,1); FFTCoeffsModified(ind(1:Vec(1))) = FFTCoeff(ind(1:Vec(1))); numCoeff = Vec(1); ReconstrSignal = ift(FFTCoeffsModified); end
Before you generate code, you must prepare your MATLAB code for code generation.
The first step is to eliminate unsupported constructs and check for any code generation issues. For a list of DSP System Toolbox features supported by MATLAB Coder, see Functions and System Objects Supported for C Code Generation. For a list of supported language constructs, see MATLAB Language Features Supported for C/C++ Code Generation (MATLAB Coder).
The code analyzer detects coding issues at design time as you enter the code. To enable the code analyzer, you must add the %#codegen
pragma to your MATLAB file.
The code generation readiness tool screens MATLAB code for features that are not supported for code generation. One of the ways to access this tool is by right-clicking on the MATLAB file in its current folder. Running the code generation tool on GenerateSignalWithHighEnergyFFTCoeffs.m
finds no issues.
Before you generate C code, ensure that the MATLAB code successfully generates a MEX function. The codegen
command used to generate the MEX function detects any errors that prevent the code for being suitable for code generation.
Run codegen
on GenerateSignalWithHighEnergyFFTCoeffs.m
function.
codegen -args {Input} GenerateSignalWithHighEnergyFFTCoeffs
The following message appears in the MATLAB command prompt:
??? The left-hand side has been constrained to be non-complex, but the right-hand side is complex. To correct this problem, make the right-hand side real using the function REAL, or change the initial assignment to the left-hand side variable to be a complex value using the COMPLEX function. Error in ==> GenerateSignalWithHighEnergy Line: 24 Column: 1 Code generation failed: View Error Report Error using codegen
This message is referring to the variable FFTCoeffsModified
. The coder is expecting this variable to be initialized as a complex variable. To resolve this issue, initialize the FFTCoeffsModified
variable as complex.
FFTCoeffsModified = zeros(L,1)+0i;
Rerun the codegen
function and you can see that a MEX file is generated successfully in the current folder with a .mex
extension.
codegen -args {Input} GenerateSignalWithHighEnergyFFTCoeffs
Run the generated MEX function to see if there are any run-time issues reported. To do so, replace
[ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input);
[ReconstrSignalMex,numCoeffMex] = GenerateSignalWithHighEnergyFFTCoeffs_mex(Input);
The harness now looks like:
L = 1020; Sineobject = dsp.SineWave('SamplesPerFrame',L,... 'SampleRate',44100,'Frequency',1000); rng(1); numIter = 1000; for Iter = 1:numIter Sinewave1 = Sineobject(); Input = Sinewave1 + 0.01*randn(size(Sinewave1)); [ReconstrSignalMex,numCoeffMex] = GenerateSignalWithHighEnergyFFTCoeffs_mex(Input,L); end max(abs(Input-ReconstrSignalMex)) figure(1); plot(Input) hold on; plot(ReconstrSignalMex,'*') hold off
The code runs successfully, indicating that there are no run-time errors.
Notice that the harness runs much faster with the MEX function compared to the regular function. The reason for generating the MEX function is not only to detect code generation and run-time issues, but also to speed up specific parts of your algorithm. For an example, see Signal Processing Algorithm Acceleration in MATLAB.
You must also check that the numeric output results from the MEX and the regular function match. Compare the reconstructed signal generated by the GenerateSignalWithHighEnergyFFTCoeffs.m
function and its MEX counterpart GenerateSignalWithHighEnergyFFTCoeffs_mex
.
max(abs(ReconstrSignal-ReconstrSignalMex)) ans = 2.2204e-16
The results match very closely, confirming that the code generation is successful.
If your goal is to run the generated code inside the MATLAB environment, your build target can just be a MEX function. If deployment of code to another application is the goal, then generate a standalone executable from the entire application. To do so, the harness must be a function that calls the subfunction GenerateSignalWithHighEnergyFFTCoeffs
. Rewrite the harness as a function.
function reconstructSignalTestbench() L = 1020; Sineobject = dsp.SineWave('SamplesPerFrame',L,... 'SampleRate',44100,'Frequency',1000); rng(1); numIter = 1000; for Iter = 1:numIter Sinewave1 = Sineobject(); Input = Sinewave1 + 0.01*randn(size(Sinewave1)); [ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input,L); end
Log all 1000 frames of the input and reconstructed signal and the number of FFT coefficients used to reconstruct each frame of the signal. Write all this data to a binary file named data.bin
using the dsp.BinaryFileWriter
System
object™. This example logs the number of coefficients, which are scalar values, as the first element of each frame of the input signal and the reconstructed signal. The data to be written has a frame size of M = L + 1 and has a format that looks like this figure.
N is the number of FFT coefficients that represent 99.99% of the signal energy of the current input frame. The meta data of the binary file specifies this information. Release the binary file writer and close the binary file at the end.
The updated harness function, reconstructSignalTestbench
, is shown here:
function reconstructSignalTestbench() L = 1020; Sineobject = dsp.SineWave('SamplesPerFrame',L,... 'SampleRate',44100,'Frequency',1000); header = struct('FirstElemInBothCols','Number of Coefficients',... 'FirstColumn','Input','SecondColumn','ReconstructedSignal'); bfw = dsp.BinaryFileWriter('data.bin','HeaderStructure',header); numIter = 1000; M = L+1; ReSignalAll = zeros(M*numIter,1); InputAll = zeros(M*numIter,1); rng(1); for Iter = 1 : numIter Sinewave1 = Sineobject(); Input = Sinewave1 + 0.01*randn(size(Sinewave1)); [ReconstrSignal,numCoeffs] = GenerateSignalWithHighEnergyFFTCoeffs(Input); InputAll(((Iter-1)*M)+1:Iter*M) = [numCoeffs;Input]; ReSignalAll(((Iter-1)*M)+1:Iter*M) = [numCoeffs;ReconstrSignal]; end bfw([InputAll ReSignalAll]); release(bfw);
The next step in generating a C executable is to create a coder.config
object for an executable and provide a main.c
function to this object.
cfg = coder.config('exe'); cfg.CustomSource = 'reconstructSignalTestbench_Main.c';
Here is how the reconstructSignalTestbench_Main.c
function looks for this example.
/* ** reconstructSignalTestbench_main.c * * Copyright 2017 The MathWorks, Inc. */ #include <stdio.h> #include <stdlib.h> #include "reconstructSignalTestbench_initialize.h" #include "reconstructSignalTestbench.h" #include "reconstructSignalTestbench_terminate.h" int main() { reconstructSignalTestbench_initialize(); reconstructSignalTestbench(); reconstructSignalTestbench_terminate(); return 0; }
For additional details on creating the main function, see Generating Standalone C/C++ Executables from MATLAB Code (MATLAB Coder).
Set the CustomInclude
property of the configuration object to specify the location of the main file. In this example, the location is the current folder.
cfg.CustomInclude = ['"',pwd,'"'];
Generate the C executable by running the following command in the MATLAB command prompt:
codegen -config cfg -report reconstructSignalTestbench
MATLAB
Coder compiles and links the main function with the C code that it generates
from the reconstructSignalTestbench.m
.
If you are using Windows, you can see that reconstructSignalTestbench.exe
is generated in the current folder. If you are using Linux, the generated executable does not have the .exe
extension.
Running the executable creates a binary file, data.bin
, in the current directory and writes the input, reconstructed signal, and the number of FFT coefficients used to reconstruct the signal.
!reconstructSignalTestbench
You can read this data from the binary file using the dsp.BinaryFileReader
object. To verify that the data is written correctly, read data from the binary file in MATLAB and compare the output with variables InputAll
and ReSignalAll
.
The header prototype must have a structure similar to the header structure written to the file. Read the data as two channels.
M = 1021; numIter = 1000; headerPro = struct('FirstElemInBothCols','Number of Coefficients',... 'FirstColumn','Input','SecondColumn','ReconstructedSignal'); bfr = dsp.BinaryFileReader('data.bin','HeaderStructure',... headerPro,'SamplesPerFrame',M*numIter,'NumChannels',2); Data = bfr();
Compare the first channel with InputAll
and the second channel with ReSignalAll
.
isequal(InputAll,Data(:,1))
ans = logical 1
isequal(ReSignalAll,Data(:,2))
ans = logical 1
The results match exactly, indicating a successful write operation.
Once you generate code from your MATLAB algorithm, you can relocate the code to another development
environment, such as a system or an integrated development environment (IDE) that
does not include MATLAB. You can package the files into a compressed file using the
packNGo
function at the command line or the
Package option in the MATLAB
Coder app. For an example that illustrates both the workflows, see Package Code for Other Development Environments (MATLAB Coder). For more information
on the packNGo
option, see packNGo
in
Build Information Methods (MATLAB Coder). You can
relocate and unpack the compressed zip file using a standard zip utility. For an
example on how to package the executable generated in this example, see Relocate Code Generated from MATLAB Code to Another Development Environment.