%% Before starting this routine % You must use matlab version 2016b or later or the file won't run. You % will also need to have access to the statistics package in matlab % This file of code must be placed in a folder along with two folders % called "Data" and "Graphs". All of the raw data files (.csv is % recommedned) to be analyzed should be placed in the "Data" Directory. % The following list shows where users are expected to input paths % or unique values according to user needs. %Line 53 - Modify C2000 to a larger value if you have more than %2000 measuments in your raw data files %Lines 91-99 - Threshold values to be set by user %Line 201 - Parent folder path % This code will run its analysis of all the raw data files in "Data" % simultaneously. The graphical output will be sent to the "Graphs" % folder, while the matlab vectors named "diameter" and "rnd_thickness" % will contain the the values of diameter and rind thickness for all % the raw data files. %% OPEN RAW DATA FILES AND COMPUTE KEY MEASURMENTS FROM EACH GRAPH %Clear workspace clear all close all %Save all names of raw data files global starting_folder; starting_folder = cd(); %save the file path to the current folder as "starting_folder" dinfo = dir('Data'); %look in the 'Data' folder to get all of the file names cd('Data') %browse to 'Data' folder %analyze each test file for i = 3:length(dinfo(:,1)) % Start at three because first two entries are an artifact of using the dir command %convert file name to set number stalk number and internode number fileName{i} = dinfo(i).name; name1=split(fileName(i),"_"); stalk(i) = str2num(name1{1}); name2=split(name1(2),"."); internode(i) = str2num(name2{1}); %Run the fucntion 'process data' to analyze each raw data file and return key %measurments from each graph to the Table 'T'. The function %definition is provided at the end of this file. Note if using a %matlabl version prior to 2016b the funciton definition must be in %a seperate file. if exist(fileName{i}, 'file')==2 %only open the file if it exists forceDispData = xlsread(fileName{i},'A3:C2000'); %read data from cell A8 to C2000 and store in 'forceDispData' %call function to analyse test_data [diameter(i), rnd_thickness(i), peaks_magnitude2(i),... peaks_prom2(i), peaks_magnitude1(i)]... =process_data(forceDispData,i,fileName(i)); end cd(starting_folder); cd('Data'); clear('forceDispData') end cd(starting_folder); clear('num', 'num2', 'num3', 'num4','i','j','dinfo','folder_names','starting_folder','test_data','test_names') T=table(stalk(:),internode(:),diameter(:),rnd_thickness(:)); T([1,2],:)=[]; T.Properties.VariableNames = {'stalk','internode','puncture_Diameter','puncture_Rind_Thickness'}; T.Properties.VariableUnits = {'NA','NA','mm','mm'}; %============================================================================================================================================ % Fuction Definition %============================================================================================================================================ %Pass a three column matrix into the function. first column = time, second %column = extension(mm), third column = load (N). The function calls this %matrix the variable 'num'. The word "index" used below referes to the %index location within this matrix of load and extension data. function [diameter, rnd_thickness,... peaks_magnitude2, prom2, peaks_magnitude1] = ... process_data(dataArray,ii,name) global starting_folder; %initialize function outputs to NaN in case function terminates before %values are assigned [diameter, rnd_thickness,... peaks_magnitude2, prom2, peaks_magnitude1... ] = deal(NaN); %set variables used to determine key points in force-disp graphs maxExtension = 4; %maximum extension before probe probe returns to starting point contactDerivative = 0.5; %derivative of load data used to find first contact with stalk contactForce = 1; %force threshold in Newtons used to find first contact with stalk exitDerivative = - 0.1; %derivative of load data used to find probe exit (should be negative) midDerivative = 0.075; %derivative used to find exit of first rind and entry of second rind midOffset = 65; %used to tell the algorithim to start certain counting procedures at a distance from the center of the stalk minPeakProminence = 5; %minimum peak prominence used to find rind tissue minPeakHeight = 10; %minimum force (N) to be considered rind tissue rind_threshold=35; %change this variable to alter the point selected for calculating rind thickness ticker=0; %Used to find rind thickness line 174, do not change check=0; %Used to find rind thickness line 173, do not change %Trim data (eliminate data collected while probe returning to starting position) %find where data is greater than an extension of 'maxExtension' and delete the rest maxExtensionIndex = find(dataArray(:,2) > maxExtension, 1); %index location max_extension dataArray(maxExtensionIndex:length(dataArray),:)=[]; %eliminate data from the probe returning %find index of zero extension (this is the location of the backside of the %stalk, i.e., the side of stalk resting against the flat horizontal plate during puncture tests) zero_extension_index = find(dataArray(:,2) > 0, 1); %Find stalk Diameter %find first contact using derivatives diameterIndex_1 = find(diff(dataArray(:,3))>contactDerivative,1,'first')-3; %find first data point with derivative greater than 'contactDerivative' diameter_1 = -(dataArray(diameterIndex_1,2)); %'diameter' of stalk = distance from first contanct to zero extension %find first contact using force threshold diameterIndex_2 = find(dataArray(:,3)>contactForce,1,'first')-3; %find first data point with force greater than 'contactForce' diameter_2 = -(dataArray(diameterIndex_2,2)); %'diameter' of stalk = distance from first contanct to zero extension %Compare diameter measurments diameterIndex = min([diameterIndex_1,diameterIndex_2]); diameter = max([diameter_1,diameter_2]); %error Checking if diameter < 5 || diameterIndex < 5 diameter = NaN; return end %Find the two peaks with the highest prominence [peaks_magnitude,peaks_index,~,peaks_prom] = findpeaks(dataArray(:,3),'MinPeakHeight', minPeakHeight,'MinPeakProminence',minPeakProminence); A=[peaks_magnitude,peaks_index,peaks_prom]; %error Checking size(A); if ans(1)<2 [peaks_magnitude, peaks_index,peaks_prom]=deal(NaN); return end [~,I] = sort(A(:,3),'descend'); % sort by prominence and return indices B = A(I,:); % use column indicies to sort all columns of A B = B(1:2,:); % Keep two most prominent peaks [~,I] = sort(B(:,2)); % Sort B by 'peaks_index' first peak to appear in data appears first in B C = B(I,:); % use column indices to sort other data in B %save index location, magnitude and prominence of peaks peaks_magnitude1 = C(1,1); peaks_magnitude2 = C(2,1); peaks_index = C(:,2); prom2 = C(2,3); %Find point where probe exits stalk using derivatives marching forwards from second peak exit_index = peaks_index(2) + midOffset + find( diff( dataArray(peaks_index(2)+midOffset:end,3) ) > exitDerivative, 1, 'first'); %error checking if isempty(exit_index) return end %Find midpoint of stalk midPointIndex = round(mean([diameterIndex,zero_extension_index])); %Find trough of first peak marching forwards from first peak %error checking if isempty(find( diff( dataArray( peaks_index(1)+midOffset:peaks_index(2),3) ) > (-midDerivative), 1, 'first')) return end trough_index(1) = peaks_index(1) + midOffset + find( diff( dataArray( peaks_index(1)+midOffset:peaks_index(2),3) ) > (-midDerivative), 1, 'first'); %Find trough of second peak marching backwards from second peak %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Force2ndDifferential=diff(transpose(polyder(dataArray(:,3)))); %Second derivitive of force to increase sensitivity to slope varaiations ticker=round(length(Force2ndDifferential)/2); check=Force2ndDifferential(ticker+1); while check < rind_threshold %Compares diffences in sequential data points in the second differential ticker=ticker+1 check=Force2ndDifferential(ticker); end trough_index(2)=ticker; %when consequtive data points show a change greater than the defined threshhold, the loop returns that location in the data set %The code below controls the graphical output of this alogrithm for each raw data file. split(name,'.'); name=name(1); plot(dataArray(:,2),dataArray(:,3), 'Linewidth', 3) title(name) hold on set(gcf, 'Position', [500, 500, 600, 300]) plot(dataArray(diameterIndex,2),dataArray(diameterIndex,3),'b*') plot(dataArray(peaks_index(1),2),dataArray(peaks_index(1),3),'r*') plot(dataArray(peaks_index(2),2),dataArray(peaks_index(2),3),'r*') plot(dataArray(exit_index,2),dataArray(exit_index,3),'b*') plot(dataArray(midPointIndex,2),dataArray(midPointIndex,3),'k*') plot(dataArray(zero_extension_index,2),dataArray(zero_extension_index,3),'k*') plot(dataArray(trough_index(1),2),dataArray(trough_index(1),3),'b+') plot(dataArray(trough_index(2),2),dataArray(trough_index(2),3),'b+') hold off cd(starting_folder) cd('Graphs') %The path to the "Graphs" folder goes on the line below cd('C:\###########Path Here##########\Graphs'); print(strcat(name{1},'.png'),'-dpng') end