Skip to content

Commit 3dd2405

Browse files
authoredApr 4, 2024
Add files via upload
1 parent 793efe8 commit 3dd2405

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
%Code for converting MUEdit exports to OTB style exports for use in
2+
%openHDEMG and analyze HDEMG decompositions.
3+
%
4+
%In openHDEMG open the new file as an OTB format and ensure
5+
%you select the same extension factor that was chosen in MUEdit as this
6+
%will not be hardcoded anywhere.
7+
8+
%Note that this is designed for a very specific export. you need to change
9+
%the descriptions dection to match your export or the torque data to pull
10+
%your correct aux channel info. Further, this only imports the first
11+
%channel of aux data as 'aquired force' if you wish to use some other
12+
%channel as the reference signal then that must be changed to select the
13+
%proper column of data from signal.auxillary on line 83
14+
15+
%Last edit April 4 2024
16+
17+
%Copyright Ryan C. A. Foley 2024
18+
%Github: rcafoley
19+
%Twitter: foleyneuromech
20+
%LinkedIn: ryancafoley
21+
22+
%% Clear all variables from the workspace, clear the Command Window, and print the current directory
23+
clear all; % Clears all variables from the workspace
24+
clc; % Clears the Command Window
25+
disp(['Current working directory: ', pwd]); % Prints the current directory
26+
27+
%Variables to change to accomodate different experimental setups
28+
%TypeofArray(s)
29+
emgArray = 'GR04MM1305'
30+
%muscle
31+
muscle = 'Tibialis Anterior'
32+
%MVC value & offset
33+
mvc = 0.466 %in mV
34+
offset = 0.13 %inmV
35+
36+
% List all .mat files in the current directory
37+
files = dir('*.mat');
38+
39+
for i = 1:length(files)
40+
originalFileName = files(i).name;
41+
fprintf('Processing %s\n', originalFileName);
42+
43+
% Load the .mat file
44+
loadedData = load(originalFileName);
45+
% Assume 'edition' and 'signal' are variables within 'loadedData'
46+
% Adjust the following lines if they're nested within another structure
47+
edition = loadedData.edition; % Or the appropriate field if nested differently
48+
signal = loadedData.signal; % Adjust based on your data structure
49+
50+
51+
%% Data Conversion here
52+
53+
%define the number of units from the decomp - this may need to the "clean"
54+
%versions if a unit is deleted
55+
56+
numRawChannels = size(signal.data, 1);
57+
numMUs = size(edition.Dischargetimes, 2);
58+
59+
%define the total channels to be created Array size (monopolar + 1
60+
%force + number of MUs x2 decomp and source)
61+
62+
totalChannels = numRawChannels + 1 + numMUs + numMUs;
63+
64+
%FileType
65+
OTBFile = 'unknown';
66+
%SamplingRate
67+
SamplingFrequency = signal.fsamp;
68+
69+
%Time Vector
70+
% Initialize the cell array
71+
Time = cell(1,1); % Creates a 1x1 cell array
72+
% Insert the array 'edition.time' into the {1,1} location of the cell array 'Time'
73+
Time{1,1} = edition.time';
74+
75+
%Convert the HDEMG Array Data Arrays
76+
%first 64 are the raw data from the grid
77+
Data = cell(1,1); % Creates a 1x1 cell array
78+
Data{1,1} = signal.data';
79+
80+
% Extract the specific torque data from 'signal.auxillary' - ours is in
81+
% row 1 but you may have more or want a different row for other aux or
82+
% the target path
83+
torqueColumnData = signal.auxiliary(10, :).'; % Transpose it to match the orientation (rows, not columns)
84+
85+
% Check to ensure dimensions match for concatenation
86+
if size(Data{1,1}, 1) == length(torqueColumnData)
87+
% Add the new column to the array in 'Data{1,1}'
88+
Data{1,1} = [Data{1,1}, torqueColumnData];
89+
else
90+
disp('Error: Dimension mismatch. Cannot concatenate torque to the new column.');
91+
end
92+
93+
%Extract the decomposition aka discharge times
94+
maxSamples = length(edition.time);
95+
96+
% Initialize the array to hold all converted data. Rows represent samples, columns represent scenarios.
97+
allDecompositionConvertedData = zeros(maxSamples, numMUs);
98+
99+
% Loop through each scenario and fill in the allConvertedData array
100+
for i = 1:numMUs
101+
% Extract the event data for the current scenario
102+
spikeData = edition.Dischargetimes{1, i};
103+
104+
% Create a temporary array of zeros for the current scenario
105+
otbSpikeData = zeros(maxSamples, 1);
106+
107+
% Mark the events in the tempData
108+
otbSpikeData(spikeData) = 1;
109+
110+
% Assign the converted data to the corresponding column in allConvertedData
111+
allDecompositionConvertedData(:, i) = otbSpikeData;
112+
end
113+
114+
% Check to ensure dimensions match for concatenation
115+
if size(Data{1,1}, 1) == length(allDecompositionConvertedData)
116+
% Add the new column to the array in 'Data{1,1}'
117+
Data{1,1} = [Data{1,1}, allDecompositionConvertedData];
118+
else
119+
disp('Error: Dimension mismatch. Cannot concatenate spike decomposition to the new column.');
120+
end
121+
122+
%Extract the source data for the decomposition aka individual optimised
123+
%EMG channels for that MU **** NOTE: this may have to be changed to
124+
%PulseTrainCLEAN as that may be the edited data - same for decomp
125+
sourceEMGColumnData = edition.Pulsetrain{1,1}.'; % Transpose it to match the orientation (rows, not columns)
126+
127+
% Check to ensure dimensions match for concatenation
128+
if size(Data{1,1}, 1) == length(sourceEMGColumnData)
129+
% Add the new column to the array in 'Data{1,1}'
130+
Data{1,1} = [Data{1,1}, sourceEMGColumnData];
131+
else
132+
disp('Error: Dimension mismatch. Cannot concatenate sourceEMG to the new column.');
133+
end
134+
135+
%Reconstruct the needed Descriptions for each channel
136+
%these cannot be extracted from the MUedit export easily so we will
137+
%reconstruct them. If your setup is different you will need to adjust
138+
%these to have the coorect info. this can be found
139+
140+
% Initialize the Descriptions cell array
141+
Description = cell(totalChannels, 1); % Adjust the size based on the total number of descriptions
142+
143+
% Fill the array with descriptions using dynamic components
144+
for i = 1:numRawChannels % For the repetitive part with numbers 1 to 64
145+
Description{i} = sprintf('%s - MULTIPLE IN 1 (Channel 1->64) - %s (%d)[mV]', muscle, emgArray, i);
146+
end
147+
148+
% Add unique descriptions after the loop with dynamic MVC and Offset values
149+
Description{numRawChannels+1} = sprintf('acquired data MVC=%.3f Offset=%.2f[mV]', mvc, offset);
150+
151+
% For Decomposition and Source for decomposition descriptions using dynamic components
152+
for i = 1:numMUs
153+
Description{(numRawChannels+1) + i} = sprintf('Decomposition of %s - MULTIPLE IN 1 (Channel 1->64) - %s (%d)[a.u]', muscle, emgArray, i);
154+
Description{(numRawChannels+1+numMUs) + i} = sprintf('Source for decomposition of %s - MULTIPLE IN 1 (Channel 1->64) - %s (%d)[a.u]', muscle, emgArray, i);
155+
end
156+
157+
% Display the Descriptions cell array to verify
158+
%disp(Description);
159+
160+
%% Output here
161+
%% Output here
162+
% Save the original data with a new name (_MUEditExport appended)
163+
originalFileNewName = [originalFileName(1:end-4), '_MUEditExport.mat'];
164+
save(originalFileNewName, '-struct', 'loadedData');
165+
166+
% Save the variables into the new MAT file
167+
newFileName = [originalFileName(1:end-4), '_ConvertedToOTBExportFormat.mat'];
168+
save(newFileName, 'Time', 'Data', 'Description', 'SamplingFrequency', 'OTBFile');
169+
end

0 commit comments

Comments
 (0)
Please sign in to comment.