Skip to main content

parseMuscleTendonPersonalizationSettingsTree.m


% This function is part of the NMSM Pipeline, see file for full license.
%
% This function parses the settings tree resulting from xml2struct of the
% Joint Model Personalization Settings XML file.
%
% (struct) -> (struct, struct, string)
% returns the input values for Muscle Tendon Personalization


function [inputs, params, resultsDirectory] = ...
parseMuscleTendonPersonalizationSettingsTree(settingsTree)
inputs = getInputs(settingsTree);
params = getParams(settingsTree);
inputs = getMtpModelInputs(inputs);
inputs = mergeStructs(getSynergyExtrapolationInputs(...
inputs.model, ...
inputs.synergyExtrapolation, ...
inputs.emgDataColumnNames, ...
inputs.emgData, ...
inputs.muscleNames), ...
inputs);
resultsDirectory = getFieldByName(settingsTree, 'results_directory').Text;
if(isempty(resultsDirectory))
resultsDirectory = pwd;
end
end

function inputs = getInputs(tree)
inputs = parseMtpNcpSharedInputs(tree);
dataDirectory = getFieldByNameOrError(tree, 'data_directory').Text;
inputs = parseEmgData(tree, inputs, dataDirectory);
inputs.tasks = getTasks(tree);
inputs.synergyExtrapolation = getSynergyExtrapolationParameters(tree, ...
inputs.model);
inputs.synergyExtrapolation = getTrialIndexes( ...
inputs.synergyExtrapolation, size(inputs.emgData, 1), inputs.prefixes);
inputs = reorderPreprocessedDataByMuscleNames(inputs, inputs.muscleNames);
if ~isfield(inputs, "emgSplines")
inputs.emgSplines = makeEmgSplines(inputs.emgTime, ...
inputs.emgDataExpanded);
end
end

function inputs = parseEmgData(tree, inputs, dataDirectory)
emgDataFileNames = findFileListFromPrefixList( ...
fullfile(dataDirectory, "EMGData"), inputs.prefixes);
collectedEmgGroupNames = parseSpaceSeparatedList(tree, "collected_emg_channel_muscle_groups");
[inputs.fullEmgData, inputs.emgDataColumnNames] = parseMtpStandard(emgDataFileNames);
collectedEmgGroupNamesMembers = ismember(inputs.emgDataColumnNames, collectedEmgGroupNames);
inputs.emgData = inputs.fullEmgData(:, collectedEmgGroupNamesMembers, :);
inputs.emgDataColumnNames = inputs.emgDataColumnNames(collectedEmgGroupNamesMembers);
firstEmgDataExpanded = expandEmgDatas(inputs.model, squeeze(inputs.emgData(1, :, :)), collectedEmgGroupNames, inputs.muscleNames);
inputs.emgDataExpanded = zeros(size(inputs.emgData, 1), size(firstEmgDataExpanded, 1), size(firstEmgDataExpanded, 2));
inputs.emgDataExpanded(1, :, :) = firstEmgDataExpanded;
for i = 2 : size(inputs.emgData, 1)
inputs.emgDataExpanded(i, :, :) = ...
expandEmgDatas(inputs.model, squeeze(inputs.emgData(i, :, :)), collectedEmgGroupNames, inputs.muscleNames);
end
inputs.emgTime = parseTimeColumn(findFileListFromPrefixList(...
fullfile(dataDirectory, "EMGData"), inputs.prefixes));
inputs.numPaddingFrames = (size(inputs.emgData, 3) - 101) / 2;
end

% (struct, string, struct) -> (struct)
function output = getTasks(tree)
tasks = getFieldByNameOrError(tree, 'MTPTaskList');
counter = 1;
mtpTasks = orderByIndex(tasks.MTPTask);
for i=1:length(mtpTasks)
if(length(mtpTasks) == 1)
task = mtpTasks;
else
task = mtpTasks{i};
end
if(strcmp(task.is_enabled.Text, "true"))
output{counter} = getTask(task);
counter = counter + 1;
end
end
end

% (integer, struct, string, struct) -> (struct)
function output = getTask(tree)
output.muscleSpecificElectromechanicalDelays = ...
parseElementBoolean(tree, "muscle_specific_electromechanical_delays");
output.isIncluded = ones(1, 7);
output.costTerms = parseRcnlCostTermSet(tree.RCNLCostTermSet.RCNLCostTerm);
end

% (struct) -> (struct)
function params = getParams(tree)
params = struct();
maxIterations = getFieldByName(tree, 'max_iterations');
if(isstruct(maxIterations))
params.maxIterations = str2double(maxIterations.Text);
end
maxFunctionEvaluations = getFieldByName(tree, 'max_function_evaluations');
if(isstruct(maxFunctionEvaluations))
params.maxFunctionEvaluations = str2double( ...
maxFunctionEvaluations.Text);
end
stepTolerance = getFieldByName(tree, 'step_tolerance');
if(isstruct(stepTolerance))
params.stepTolerance = str2double( ...
stepTolerance.Text);
end
performMuscleTendonLengthInitialization = getFieldByNameOrError(tree, ...
'MuscleTendonLengthInitialization').is_enabled;
if(performMuscleTendonLengthInitialization.Text == "true")
params.performMuscleTendonLengthInitialization = 1;
else
params.performMuscleTendonLengthInitialization = 0;
end
end

function synergyExtrapolation = ...
getSynergyExtrapolationParameters(tree, model)
groupNames = parseSpaceSeparatedList(tree, ...
"missing_emg_channel_muscle_groups");
synergyExtrapolation.missingEmgChannelGroups = groupNamesToGroups( ...
groupNames, model);
synergyExtrapolationTree = getFieldByNameOrError(tree, "MTPSynergyExtrapolation");
synergyExtrapolation.matrixFactorizationMethod = ...
getFieldByName(synergyExtrapolationTree, 'matrix_factorization_method').Text;
synergyExtrapolation.numberOfSynergies = ...
str2double(getFieldByName(synergyExtrapolationTree, 'number_of_synergies').Text);
synergyExtrapolation.synergyExtrapolationCategorization = ...
getFieldByName(synergyExtrapolationTree, 'synergy_extrapolation_categorization').Text;
synergyExtrapolation.residualCategorization = ...
getFieldByName(synergyExtrapolationTree, 'residual_categorization').Text;
synergyExtrapolation.taskNames = ...
parseSpaceSeparatedList(synergyExtrapolationTree, 'task_prefixes');
synergyExtrapolation.costTerms = parseRcnlCostTermSet(...
synergyExtrapolationTree.RCNLCostTermSet.RCNLCostTerm);
end

function inputs = getSynergyExtrapolationInputs(model, ...
synergyExtrapolation, emgDataColumnNames, emgData, muscleNames)
model = Model(model);
groupToName = getMuscleNameByGroupStruct(model, ...
emgDataColumnNames);
for i = 1 : length(fieldnames(groupToName))
musclesInGroup = groupToName.(emgDataColumnNames(i));
for j = 1 : length(musclesInGroup)
synergyExtrapolation.currentEmgChannelGroups{i}(1, j) = ...
find(strcmp(muscleNames, musclesInGroup(j)));
end
end
[inputs.synergyWeights, inputs.numberOfExtrapolationWeights, ...
inputs.numberOfResidualWeights] = ...
getSynergyWeights(synergyExtrapolation, ...
size(emgData, 1), ...
size(synergyExtrapolation.currentEmgChannelGroups, 2), ...
size(synergyExtrapolation.missingEmgChannelGroups, 2));
[inputs.extrapolationCommands, inputs.residualCommands] = ...
getSynergyCommands(emgData, ...
synergyExtrapolation.numberOfSynergies, ...
synergyExtrapolation.matrixFactorizationMethod, ...
synergyExtrapolation.synergyCategorizationOfTrials, ...
synergyExtrapolation.residualCategorizationOfTrials);
inputs.synergyExtrapolation = synergyExtrapolation;
end