% example batch script to use the mobilab code by Marius Klug (2018-09-11). You can run everything at once, but then the
% plots of the mobilab mocap data streams won't contain event markers

% change your matlab folder to the folder where the script and data is stored!

% cd 'P:\Marius\mobilab-testing'


%% 1) importing and exemplary processing of head yaw movements

if ~exist('mobilab','var'); eeglab; runmobilab; end

filename='example_EEG_eyeTracking_rigidBody';


disp(['Importing file: ' filename '...']);

input_filepath = [pwd '\' filename '.xdf'];
output_filepath = [pwd '\' filename '_MoBI'];

mobilab.allStreams = dataSourceXDF(input_filepath,output_filepath);

mobilab.refresh

disp('...done.');

% processing HMD (5th stream in mobilab)

% quaternion values sometimes flip their sign for mathematical reasons
% which is bad for filtering. but the values stay the same if they are
% flipped back, so this is done to allow filtering.
unflippedHead = mobilab.allStreams().item{5}.unflipSigns();

% lowpass filtering with the specified cutoff frequency
lowPassFilteredHead = unflippedHead.lowpass(6);

% intesting new way of filtering that has pros and cons, you might want to check it out:
% oneEuroFilteredHead = unflippedHead.oneEuroFilter(1,10);

% quaternion orientation values are transformed to euler angle to be
% interpretable for humans
eulerHeadLowpass = lowPassFilteredHead.quaternionsToEuler();

% 3 time derivatives are calculated (velocity, acceleration, and jerk)
eulerHeadLowpass.timeDerivative(3);

% HERE COMES THE MOCAP MARKER CREATION:

% markers are created based on the velocity data stream (1st child of euler
% angle values). entered values are:
% - channel that should be used for marker generation (4 are yaw values in our
% case)
% - algorithm that should be used for marker creation
% - one string with onset and offset marker names separated by a single
% whitespace
% - window length for determining the fine movement onset/offset
% threshold (in s after coarse onset detection). set this to a value that you expect the average
% movement to be long
% - boolean only used in algorithms that are NOT 'movement' (i.e. you
% can probably forget it)
% - VERY IMPORTANT! threshold for generally detecting a movement (values that are above are
% classified as movement). the entered value is the percentage of datapoints
% that are lower than the threshold (example: the threshold is set such that 65% of the
% data are lower, meaning in practice that we assume the participant is
% moving 35% of the time.)
% - IMPORTANT! threshold for the fine tuned onsets and offsets in
% percentage of the maximum value present in the window after onset detected
% - minimum movement duration (if 'movement' is chosen as algorithm)
% necessary for a movement to also be marked as such (in ms). the first
% time we do this with 0, as written at the top of this loop, so all movements are detected
% and then the appropriate minDuration is chosen.
eulerHeadLowpass.children{1}.createEvents(4,'movements','yawHeadMovement:start yawHeadMovement:end',1,0,65,5,0);

% add the created events to inspect them in the plot - IMPORTANT! This means when you export them, you have to delete
% them again, because otherwise the events will be present twice, as long as you export this data stream and the time
% derivatives as well
eulerHeadLowpass.addEventsFromDerivatives();


mobilab.refresh

unflippedHand = mobilab.allStreams().item{4}.unflipSigns();

% lowpass filtering with the specified cutoff frequency
lowPassFilteredHand = unflippedHand.lowpass(6);

% quaternion orientation values are transformed to euler angle to be
% interpretable for humans
eulerHandLowpass = lowPassFilteredHand.quaternionsToEuler();

% 3 time derivatives are calculated (velocity, acceleration, and jerk)
eulerHandLowpass.timeDerivative(3);

% HERE COMES THE MOCAP MARKER CREATION:

% markers are created based on the velocity data stream (1st child of euler
% angle values). entered values are:
% - channel that should be used for marker generation (4 are yaw values in our
% case)
% - algorithm that should be used for marker creation
% - one string with onset and offset marker names separated by a single
% whitespace
% - window length for determining the fine movement onset/offset
% threshold (in s after coarse onset detection). set this to a value that you expect the average
% movement to be long
% - boolean only used in algorithms that are NOT 'movement' (i.e. you
% can probably forget it)
% - VERY IMPORTANT! threshold for generally detecting a movement (values that are above are
% classified as movement). the entered value is the percentage of datapoints
% that are lower than the threshold (example: the threshold is set such that 65% of the
% data are lower, meaning in practice that we assume the participant is
% moving 35% of the time.)
% - IMPORTANT! threshold for the fine tuned onsets and offsets in
% percentage of the maximum value present in the window after onset detected
% - minimum movement duration (if 'movement' is chosen as algorithm)
% necessary for a movement to also be marked as such (in ms). the first
% time we do this with 0, as written at the top of this loop, so all movements are detected
% and then the appropriate minDuration is chosen.
eulerHandLowpass.children{1}.createEvents(1,'movements','XHandMovement:start XHandMovement:end',1,0,65,5,0);

% add the created events to inspect them in the plot - IMPORTANT! This means when you export them, you have to delete
% them again, because otherwise the events will be present twice, as long as you export this data stream and the time
% derivatives as well
eulerHandLowpass.addEventsFromDerivatives();

% just for a better plot
eulerHandLowpass.throwOutChannels(4:6);

% now plot the head quat2eul and the controller throwOut_quat2eul streams and you can see movement on- and offset
% markers for head yaw movement (channel 4) and controller X movements (channel 1).
mobilab.refresh
%% 3) exporting to EEGLAB

% delete the events to not have them twice
eulerHeadLowpass.deleteMarkers({''});
eulerHandLowpass.deleteMarkers({''});

% export to EEGLAB -> the EEG.chanloc.type values correspond to the classes of the streams
% second vector is an additional marker stream. All marker events of the data streams are taken by default! -> Maybe
% change that to give more flexibility?
MoBI_EEG = mobilab.allStreams.export2eeglab( [1 3 14 15 16 17 8 9 10 11], [2]);

% NOTE: Mobilab changes the EEGLAB options to use memora mapped file. In case this doesn't work for you, you have to
% change it back here. 

% new data set in EEGLAB
[ALLEEG, EEG, CURRENTSET] = pop_newset(ALLEEG, MoBI_EEG, CURRENTSET, 'gui', 'off');

% split into different data types
[ALLEEG, EEG, CURRENTSET, EEG_split_sets] = bemobil_split_MoBI_set(ALLEEG, EEG, CURRENTSET);
        
eeglab redraw

