Generate Generic C Code for a Human Health Monitoring Network
This example shows how to generate a generic C MEX application for a deep learning network that reconstructs electrocardiogram (ECG) signals from a continuous wave (CW) radar. CW radars are used for daily long-term monitoring and are an alternative to wearable devices, which can monitor ECG signals directly.
To learn about the network and the network training process, see Human Health Monitoring Using Continuous Wave Radar and Deep Learning. This example uses the network that contains a maximal overlap discrete wavelet transform (MODWT) layer.
Download and Prepare Data
The data set [1] in this example contains synchronized data from a CW radar and ECG signals measured simultaneously by a reference device on 30 healthy subjects. The CW radar system uses the six-port technique and operates at 24 GHz in the Industrial Scientific and Medical (ISM) band.
Because the original data set contains a large volume of data, this example uses the data from resting, apnea, and Valsalva maneuver scenarios. Additionally, it uses only the data from subjects 1 through 5 to train and validate the model, and data from subject 6 to test the trained model.
Because the main information in the ECG signal is usually in a frequency band less than 100 Hz, all signals are downsampled to 200 Hz and divided into segments of 1024 points to create signals of approximately 5s.
Download the data set by using the downloadSupportFile function. The whole data set is approximately 16 MB in size and contains two folders. The trainVal folder contains the training and validation data and the test folder contains the test data. Each folder contains ecg and radar folders for ECG and radar signals, respectively.
datasetZipFile = matlab.internal.examples.downloadSupportFile('SPT','data/SynchronizedRadarECGData.zip'); datasetFolder = fullfile(fileparts(datasetZipFile),'SynchronizedRadarECGData'); if ~exist(datasetFolder,'dir') unzip(datasetZipFile,datasetFolder); end
Create signalDatastore objects to access the data in the files. Create datastores radarTrainValDs and radarTestDs to store the CW radar data used for training/validation and testing, respectively. Then create ecgTrainValDs and ecgTestDs datastore objects to store the ECG signals for training/validation and testing, respectively.
radarTrainValDs = signalDatastore(fullfile(datasetFolder,"trainVal","radar")); radarTestDs = signalDatastore(fullfile(datasetFolder,"test","radar")); ecgTrainValDs = signalDatastore(fullfile(datasetFolder,"trainVal","ecg")); ecgTestDs = signalDatastore(fullfile(datasetFolder,"test","ecg"));
View the categories and distribution of the training data.
trainCats = filenames2labels(radarTrainValDs,'ExtractBefore','_radar'); summary(trainCats)
trainCats: 830×1 categorical
GDN0001_Resting 59
GDN0001_Valsalva 97
GDN0002_Resting 60
GDN0002_Valsalva 97
GDN0003_Resting 58
GDN0003_Valsalva 103
GDN0004_Apnea 14
GDN0004_Resting 58
GDN0004_Valsalva 106
GDN0005_Apnea 14
GDN0005_Resting 59
GDN0005_Valsalva 105
<undefined> 0
View the categories and distribution of the test data.
testCats = filenames2labels(radarTestDs,'ExtractBefore','_radar'); summary(testCats)
testCats: 200×1 categorical
GDN0006_Apnea 14
GDN0006_Resting 59
GDN0006_Valsalva 127
<undefined> 0
Apply normalization to the ECG signals. Use the helperNormalize helper function to center each signal by subtracting its median and rescale it so that its maximum peak is 1.
ecgTrainValDs = transform(ecgTrainValDs,@helperNormalize); ecgTestDs = transform(ecgTestDs,@helperNormalize);
Combine the data sets into a training and a test data set.
trainValDs = combine(radarTrainValDs,ecgTrainValDs); testDs = combine(radarTestDs,ecgTestDs);
Visualize the CW radar and measured ECG signals for one of each of the scenarios. The deep learning network takes the radar signal and reconstructs it to the corresponding ECG signal.
numCats = cumsum(countcats(testCats)); previewindices = [randi([1,numCats(1)]),randi([numCats(1)+1,numCats(2)]),randi([numCats(2)+1,numCats(3)])]; testindices = [randi([1,numCats(1)]),randi([numCats(1)+1,numCats(2)]),randi([numCats(2)+1,numCats(3)])]; helperPlotData(testDs,previewindices);

Inspect the Entry-Point Function
The radarToEcgNet_predict entry-point function loads the dlnetwork object from the radarToEcgNet MAT file into a persistent variable and reuses the variable for subsequent prediction calls. The entry-point function converts the input CW radar data into a dlarray format and calls the predict method on the network by using the dlarray input data. Finally, it reshapes the output into a two-dimensional array.
type('radarToEcgNet_predict.m')function out = radarToEcgNet_predict(in) %#codegen
% Copyright 2024 The MathWorks, Inc.
persistent dlnet
if isempty(dlnet)
dlnet = coder.loadDeepLearningNetwork('radarToEcgNet.mat');
end
% convert input to a dlarray
dlIn = dlarray(single(in),'CTB');
% pass in input
dlOut = predict(dlnet,dlIn);
% extract data from dlarray and reshape it to two dimensions
out = extractdata(dlOut);
out = reshape(out, [1,numel(dlOut)]);
end
Configure Code Generation Options and Generate Code
Create a code generation configuration object by using the coder.config (MATLAB Coder) function. For the cfg object, the default value of DeepLearningConfig is a coder.DeepLearningCodeConfig (MATLAB Coder) object. By default, the generated code does not depend on any third-party deep learning libraries.
cfg = coder.config('mex');
cfg.DeepLearningConfigans =
coder.DeepLearningCodeConfig with properties:
TargetLibrary: 'none'
CPU configuration parameters
LearnablesCompression: 'None'
Because the network requires the CW radar data as an input argument, prepare a sample CW radar data for code generation.
exampleRadarData = read(radarTrainValDs);
Run the codegen command. Specify the sample data as the input type.
codegen radarToEcgNet_predict -config cfg -args {exampleRadarData}
Code generation successful.
Run the Generated MEX Function
Run the generated MEX function by passing a CW radar signal.
idx = testindices(1); ds = subset(radarTestDs,idx); testRadarData = read(ds); predictedEcg_mex = radarToEcgNet_predict_mex(testRadarData);
Run the entry-point function in MATLAB to compare the results.
predictedEcg = radarToEcgNet_predict(testRadarData);
Plot the data.
tiledlayout(1, 2, 'Padding', 'none', 'TileSpacing', 'compact'); fs = 200; t = linspace(0,length(predictedEcg)/fs,length(predictedEcg)); plot(t,predictedEcg) title("Reconstructed ECG Signal from MATLAB") xlabel("Time (s)") grid on nexttile(2)

plot(t,predictedEcg_mex) title("Reconstructed ECG Signal from Generated MEX") xlabel("Time (s)") grid on

The reconstructed ECG signals from MATLAB and the generated MEX function match.
Compare Predicted Signals to the Measured ECG Signals
Visualize the output of the deep learning network for all three input signal scenarios and compare it to the measured ECG signal data. To do this, call the helper function helperPlotData by passing the generated MEX function, radarToEcgNet_predict_mex, as a function handle. The helper function picks a representative input for each scenario, runs the generated MEX with that data, and visualizes the result alongside the corresponding measured ECG signal.
helperPlotData(testDs,testindices,@radarToEcgNet_predict_mex);

Reference
[1] Schellenberger, S., Shi, K., Steigleder, T. et al. A data set of clinically recorded radar vital signs with synchronised reference sensor signals. Sci Data 7, 291 (2020). https://doi.org/10.1038/s41597-020-00629-5
Helper Functions
The helperNormalize function normalizes input signals by subtracting the median and dividing the signals by the maximum value.
function x = helperNormalize(x) % This function is only intended to support this example. It may be changed % or removed in a future release. x = x-median(x); x = {x/max(x)}; end
The helperPlotData function plots the radar and ECG signals.
function helperPlotData(DS,Indices,mexFunction) % This function is only intended to support this example. It may be changed % or removed in a future release. arguments DS Indices mexFunction = [] end fs = 200; N = numel(Indices); M = 2; if ~isempty(mexFunction) M = M + 1; end tiledlayout(M, N, 'Padding', 'none', 'TileSpacing', 'compact'); for i = 1:N idx = Indices(i); ds = subset(DS,idx); [~,name,~] = fileparts(ds.UnderlyingDatastores{1}.Files{1}); data = read(ds); radar = data{1}; ecg = data{2}; t = linspace(0,length(radar)/fs,length(radar)); nexttile(i) plot(t,radar) title(["Sample",regexprep(name, {'_','radar'}, '')]) xlabel(["Radar Signal","Time (s)"]) grid on nexttile(N+i) plot(t,ecg) xlabel(["Measured ECG Signal","Time (s)"]) ylim([-0.3,1]) grid on if ~isempty(mexFunction) nexttile(2*N+i) predictedEcg = mexFunction(radar); plot(t,predictedEcg) grid on ylim([-0.3,1]) xlabel(["Reconstructed ECG Signal from Generated MEX","Time (s)"]) end end set(gcf,'Position',[0 0 300*N,150*M]) end