Main Content

Classify Parkinson's Disease Severity from TDMS Data Using Deep Learning

R2026b
Since R2026b

This example shows how to read brain signal recordings from TDMS files, classify Parkinson's disease (PD) severity using a deep learning model, and simulate adaptive deep brain stimulation (DBS).

Adaptive DBS adjusts electrical stimulation delivered to the brain based on symptom severity. The adaptive approach reduces energy use and side effects compared to always-on stimulation. The brain signals are local field potentials (LFPs) recorded from implanted electrodes and stored in TDMS files, which efficiently store high-speed, multi-channel recordings. A 1D convolutional neural network (CNN) classifies severity into four levels from beta-band (13-30 Hz) spectral features extracted from the LFP recordings. The predicted severity then drives a closed-loop controller that sets stimulation voltage and timing.

In this example, you:

  1. Load multi-channel neural recordings from TDMS files.

  2. Preprocess LFP signals using bandpass and notch filters.

  3. Extract beta-band spectral features from the preprocessed signals.

  4. Build and train a 1D-CNN to classify PD severity from spectral features.

  5. Test the trained classifier on the test data set.

  6. Simulate adaptive DBS stimulation using the trained classifier.

  7. Visualize classification accuracy and stimulation efficiency.

  8. Explore ways to improve classification results.

Load Neural Recordings from TDMS Files

In practice, you collect LFP recordings by acquiring signals from DBS electrodes using an NI™ data acquisition system such as a cDAQ-9178 chassis with NI-9220 analog input modules. The system logs multi-channel recordings to TDMS files. To prepare the data for supervised learning, record sessions at each clinician-rated severity level and split the recordings temporally into data sets for training, validation, and testing.

For this example, load the provided TDMS files instead. The data consists of 4-channel LFP recordings sampled at 512 Hz from the subthalamic nucleus. Each of the four severity levels (Normal, Mild, Moderate, and Severe) has 60 seconds of data. The three TDMS files contain the training (60%), validation (20%), and test (20%) data sets. Each file has a channel group named STN_LFP with four bipolar contact pair channels and a severity label channel.

trainRaw = tdmsread("trainLFP.tdms");
valRaw = tdmsread("validationLFP.tdms");
testRaw = tdmsread("testLFP.tdms");

Inspect the channel groups, channel names, and data types stored in the file using tdmsinfo.

info = tdmsinfo("trainLFP.tdms");
disp(info.ChannelList(:,["ChannelGroupName","ChannelName","DataType"]))
    ChannelGroupName      ChannelName        DataType  
    ________________    ________________    ___________

       "STN_LFP"        "timeVec"           "Timestamp"
       "STN_LFP"        "Contact_0_1"       "Double"   
       "STN_LFP"        "Contact_1_2"       "Double"   
       "STN_LFP"        "Contact_2_3"       "Double"   
       "STN_LFP"        "Contact_Ref"       "Double"   
       "STN_LFP"        "Severity_Label"    "Double"   

Extract the LFP data and severity labels from each file into numeric arrays for processing. tdmsread returns a cell array with one element per channel group, so index into the first element to get the data table, then retrieve columns by name.

channels = ["Contact_0_1","Contact_1_2","Contact_2_3","Contact_Ref"];

trainLFP = trainRaw{1}{:,channels};
trainLabels = trainRaw{1}.Severity_Label;

valLFP = valRaw{1}{:,channels};
valLabels = valRaw{1}.Severity_Label;

testLFP = testRaw{1}{:,channels};
testLabels = testRaw{1}.Severity_Label;

Preprocess LFP Signals

Clean the raw LFP signals through a four-stage preprocessing pipeline using Signal Processing Toolbox™ functions.

The preprocessLfp function provided with this example removes DC offset, filters out power line noise at 50 Hz and 100 Hz, applies a 1-200 Hz bandpass filter, and rejects artifacts to remove transient movement spikes.

fs = 512; % sample time
trainClean = preprocessLfp(trainLFP,fs);
Preprocessing 73728 samples, 4 channels at 512 Hz...
  Artifacts rejected: 126 samples (0.17%)
  Bandpass: [1 - 200] Hz
  Preprocessing complete.
valClean = preprocessLfp(valLFP,fs);
Preprocessing 24576 samples, 4 channels at 512 Hz...
  Artifacts rejected: 24 samples (0.10%)
  Bandpass: [1 - 200] Hz
  Preprocessing complete.
testClean = preprocessLfp(testLFP,fs);
Preprocessing 24576 samples, 4 channels at 512 Hz...
  Artifacts rejected: 30 samples (0.12%)
  Bandpass: [1 - 200] Hz
  Preprocessing complete.

Extract Beta-Band Features

Compute windowed spectral features focused on the beta band (13-30 Hz), the primary biomarker for PD motor symptoms.

The extractBetaFeatures function provided with this example uses 1-second windows with 50% overlap to produce 8 features per channel. The extracted features are total beta-power, low and high beta-power, beta/theta ratio, peak frequency, burst index, theta power, and gamma power. extractBetaFeatures uses Signal Processing Toolbox functions for this processing.

windowLength = 1.0; % seconds
overlapFrac = 0.5;

[trainFeatures,trainWindowLabels,featureNames] = ...
    extractBetaFeatures(trainClean,trainLabels,fs,windowLength,overlapFrac);
Extracting features: 287 windows (1.0s window, 50% overlap)...
  Feature matrix: [287 windows x 32 features]
  Label distribution: Class0=72 Class1=72 Class2=72 Class3=71 
[valFeatures,valWindowLabels] = extractBetaFeatures(valClean,valLabels,fs,windowLength,overlapFrac);
Extracting features: 95 windows (1.0s window, 50% overlap)...
  Feature matrix: [95 windows x 32 features]
  Label distribution: Class0=24 Class1=24 Class2=24 Class3=23 
[testFeatures,testWindowLabels] = extractBetaFeatures(testClean,testLabels,fs,windowLength,overlapFrac);
Extracting features: 95 windows (1.0s window, 50% overlap)...
  Feature matrix: [95 windows x 32 features]
  Label distribution: Class0=24 Class1=24 Class2=24 Class3=23 

Verify the size of each data set to confirm that there is no data leakage between data sets.

fprintf("Size of data sets: Train=%d, Val=%d, Test=%d\n", ...
    size(trainFeatures,1),size(valFeatures,1),size(testFeatures,1));
Size of data sets: Train=287, Val=95, Test=95

Build and Train Severity Classifier

Build and train a 1D convolutional neural network to classify PD severity using Deep Learning Toolbox™ functions.

The buildCnnClassifier function provided with this example defines three convolutional blocks with batch normalization, followed by global average pooling and fully connected layers. The function trains the network to classify severity into four levels: Normal, Mild, Moderate, and Severe, and opens the Training Progress window. The function then reports validation accuracy to confirm learning progress.

[trainedNet,trainInfo] = buildCnnClassifier(trainFeatures,trainWindowLabels,valFeatures,valWindowLabels);
Building 1D-CNN classifier...
  Input features: 32
  Classes: 4
  Training samples: 287
  Validation samples: 95
Training 1D-CNN...

Training progress plot showing training and validation loss decreasing from 1.5 to approximately 0.3 over 800 iterations, with training parameters listed including Adam solver, learning rate 0.0005, and 100 epochs

Validation accuracy: 93.7%

Confusion Matrix (Validation):
    23     1     0     0
     1    22     1     0
     1     1    21     1
     0     0     0    23

  Normal accuracy: 95.8%
  Mild accuracy: 91.7%
  Moderate accuracy: 87.5%
  Severe accuracy: 100.0%

The trained network achieves 93.7% validation accuracy, with strongest performance on the Severe and Normal classes. Most misclassifications occur between adjacent severity levels, where clinical boundaries naturally overlap.

Test Trained Classifier

Apply the trained 1D-CNN to the test data and compute the classification accuracy. Misclassifications are typically concentrated between adjacent severity levels where clinical grades genuinely overlap.

numFeatures = size(testFeatures,2);
XTest = reshape(testFeatures',[numFeatures,1,1,size(testFeatures,1)]);

severityNames = {'Normal','Mild','Moderate','Severe'};
YTestTrue = categorical(arrayfun(@(x) severityNames{x+1}, ...
    testWindowLabels,UniformOutput=false),severityNames);
scores = minibatchpredict(trainedNet,XTest);
YTestPred = scores2label(scores,categories(YTestTrue));

testAccuracy = sum(YTestPred == YTestTrue) / numel(YTestTrue) * 100;
fprintf("Classification accuracy: %.1f%%\n",testAccuracy);
Classification accuracy: 93.7%

Display the confusion matrix to evaluate per-class performance.

confusionchart(YTestTrue,YTestPred,Title="1D-CNN Severity Classification", ...
    ColumnSummary="column-normalized",RowSummary="row-normalized");

Confusion matrix for four severity classes showing strong diagonal values. Mild achieves 100% true positive rate. Most misclassifications occur between adjacent classes Moderate and Severe.

The test accuracy matches the validation result, confirming that the model generalizes well and is suitable for driving the adaptive stimulation controller.

Simulate Adaptive DBS Controller

The ultimate goal is to implement the classification model in an adaptive DBS controller. The controller collects patient LFPs in real-time, classifies symptom severity, and applies appropriate stimulation. Specifically, the controller triggers stimulation when beta-power exceeds a threshold and scales amplitude with predicted severity. For this example, you simulate the controller's behavior on recorded data by combining the training, validation, and test features into one data set.

allFeatures = [trainFeatures;valFeatures;testFeatures];
allLabels = [trainWindowLabels;valWindowLabels;testWindowLabels];

Classify the combined features using the trained network and extract the beta-power for each window.

numAllFeatures = size(allFeatures,2);
XAll = reshape(allFeatures',[numAllFeatures,1,1,size(allFeatures,1)]);
scores = minibatchpredict(trainedNet,XAll);
allPredictions = scores2label(scores,categories(YTestTrue));
betaPower = allFeatures(:,1);

The adaptiveDbsController function provided with this example triggers stimulation when beta-power exceeds a threshold, scales the stimulation amplitude with predicted severity, and ramps gradually to prevent side effects.

controllerParams.windowSec = windowLength;
controllerParams.fs = fs;
stimResults = adaptiveDbsController(allPredictions,betaPower,controllerParams);
Running adaptive DBS controller (477 windows, 1.0s each)...

--- Adaptive DBS Results ---
  Time on stimulation: 78.0% (vs 100% conventional)
  Average amplitude when ON: 1.96 V
  Energy saving vs conventional: 73.4%
  Beta threshold: 0.0000

Visualize Stimulation Results

To evaluate stimulation efficiency, visualize the adaptive DBS controller output. The beta-power plot shows the original and suppressed signals, with a threshold line at 20% above median beta-power. The stimulation amplitude plot shows when and how strongly the controller stimulates. Amplitude scales with predicted severity. The controller is active for Mild or higher and inactive for Normal. Points where elevated beta-power coincides with a Normal prediction indicate a potential classification error. The energy savings plot compares the total energy used by adaptive DBS against conventional continuous stimulation.

numWindows = numel(stimResults.amplitude);
timeAxis = (0:numWindows-1) * stimResults.windowSec;

tiledlayout(3,1)
nexttile
plot(timeAxis,stimResults.betaPower);
hold on
plot(timeAxis,stimResults.betaSuppressed,"g");
yline(stimResults.betaThreshold,"r--");
hold off
ylabel("Beta Power");
legend("Original","After aDBS","Threshold");
title("Beta Band Power");

nexttile
area(timeAxis,stimResults.amplitude);
ylabel("Amplitude (V)");
ylim([0 4]);
title(sprintf("Stimulation Amplitude — ON %.1f%% of Time",stimResults.timeOnStim));

nexttile
bar(categorical(["Conventional DBS","Adaptive DBS"]),[100,100-stimResults.energySaving]);
ylabel("Relative Energy (%)");
title(sprintf("Energy Saving: %.1f%%",stimResults.energySaving));

Three-panel plot. Top: beta band power over time with original signal, suppressed signal after adaptive DBS, and threshold line. Middle: stimulation amplitude ranging from 0 to 3 V, active 78.0% of the time. Bottom: bar chart comparing energy usage — adaptive DBS uses approximately 27% versus 100% for conventional DBS.

Improve Results

In this example, the adaptive DBS controller reduces energy consumption compared to conventional continuous stimulation while maintaining therapeutic benefit. Improve the system by:

  • Increasing the amount of training data to improve CNN accuracy

  • Adjusting the beta-power threshold to fine-tune stimulation sensitivity

  • Using additional spectral features (for example, phase-amplitude coupling) or longer window lengths for better severity discrimination

  • Setting separate on and off thresholds for smoother stimulation transitions

Try increasing the overlap fraction from 0.5 to 0.75 in the Extract Beta-Band Features section of this example. Then run the example to observe the impact on classification accuracy and energy savings. When you increase the overlap, the number of training windows nearly doubles. This increase gives the classifier more samples to learn from and can improve classification accuracy.

See Also

Functions

Topics