Skip to content

Instantly share code, notes, and snippets.

@ClaudiuPapasteri
Created May 13, 2019 15:24
Show Gist options
  • Save ClaudiuPapasteri/338703a73b0916dd125594d0d048e974 to your computer and use it in GitHub Desktop.
Save ClaudiuPapasteri/338703a73b0916dd125594d0d048e974 to your computer and use it in GitHub Desktop.
Sync Shimmer through key and time logger
function SyncKeyTimeLog(logFileName)
% SyncKeyTimeLog(logFileName)
%
% Logs each key press using a figure call-back. The key name and press time
% are both recorded, with the most recent data being displayed on the
% figure.
%
% INPUTS:
% logFileName = string = save file name (optional)
%
% OUTPUTS:
% A .mat file with a data structure: KEY_PRESS_DATA
%
% KEY_PRESS_DATA.time = vector of the times when keys were pressed, in
% units of seconds, with 0.0 corresponding to the time when this function
% was called.
%
% KEY_PRESS_DATA.clock = vector of the times when keys were pressed, in
% machine time
%
% KEY_PRESS_DATA.keyName = cell array of the names of the keys that were
% pressed, matching one to one with the time data.
%
% USAGE:
% Calling this function will start a timer (with tic) and then write the
% name of each key that is pressed, along with the time to a data
% structure.
%
% The most recent data is copied to the figure, for user feedback
%
% Data logging is stopped by pressing the excape key or by
% closing the figure window. Once this occurs, a dialogue box will pop up
% to confirm the file save operation (to prevent accidentally overwriting
% old data), and change the save name if desired.
%
% NOTES:
% The figure is set to be in "modal" style which forces it to the top of
% all other windows. This is done to prevent missing keystrokes. This
% behavior can be changed by simply finding and removing the line:
% >> KEY_LOG_FIG.WindowStyle = 'modal';
%
% Shimmer NOTES:
% Convert timetamp unix -> object datetime ... time zone Bucharest = UTC+3 hours
% numtime derived for Bucharest (+1080000 means 3 hours) and transform in numTime
% Time1 = ( TimeStamp + 10800000 ) / 86400000 + 719529;
% % # == datenum(1970,1,1) ... +10800 is Bucharest time offset for dif time zone
% Time2 = datestr(Time1, 'dd/mm/YYYY HH:MM:SS.FFF');
% % transform in human readable Bucharest time
global KEY_PRESS_DATA DATA_LOG_INDEX DATA_LOG_LENGTH
global SAVE_FILE_NAME KEY_LOG_FIG TEXT_HANDLE
if nargin == 0
logFileName = 'M2_ID.mat';
end
% Initialize data logging:
DATA_LOG_LENGTH = 100;
KEY_PRESS_DATA.keyName = cell(DATA_LOG_LENGTH, 1);
KEY_PRESS_DATA.time = zeros(DATA_LOG_LENGTH, 1);
KEY_PRESS_DATA.clock = zeros(DATA_LOG_LENGTH, 1);
DATA_LOG_INDEX = 1;
SAVE_FILE_NAME = logFileName;
% Create a new figure to use for call-backs
KEY_LOG_FIG = figure();
% Register callbacks with the figure
KEY_LOG_FIG.KeyPressFcn = @keyCallback; % Assign the callback for the arrow keys
KEY_LOG_FIG.DeleteFcn = @stopLoggingData;
% Force the window to be on top (avoid missing key presses);
KEY_LOG_FIG.WindowStyle = 'modal';
% Set parameters for annotations on the figure
dim1 = [.1 .1 .3 .3];
str1 = 'Log: Any Key';
dim2 = [.8 .1 .3 .3];
str2 = 'Exit: Esc';
% Put text on the figure for feed-back:
TEXT_HANDLE = text(0.3,0.8,{'',''}); axis off;
annotation('textbox', dim1, 'String', str1, 'FitBoxToText', 'on');
annotation('textbox', dim2, 'String', str2, 'FitBoxToText', 'on');
TEXT_HANDLE.FontSize = 16;
% Start a timer
tic;
end
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
function keyCallback(~,event)
global KEY_PRESS_DATA DATA_LOG_INDEX DATA_LOG_LENGTH
global KEY_LOG_FIG TEXT_HANDLE
if strcmp('escape',event.Key) % Escape key = stop logging data
close(KEY_LOG_FIG);
else
% Allocate more memory if necessary
if DATA_LOG_INDEX > DATA_LOG_LENGTH
KEY_PRESS_DATA.time = ...
[KEY_PRESS_DATA.time; zeros(size(KEY_PRESS_DATA.time))];
KEY_PRESS_DATA.clock = ...
[KEY_PRESS_DATA.clock; zeros(size(KEY_PRESS_DATA.clock))];
KEY_PRESS_DATA.keyName = ...
[KEY_PRESS_DATA.keyName; cell(size(KEY_PRESS_DATA.keyName))];
DATA_LOG_LENGTH = length(KEY_PRESS_DATA.time);
end
% Log the key press
timeNow = toc;
clockNow = now;
keyName = event.Key;
KEY_PRESS_DATA.time(DATA_LOG_INDEX) = timeNow;
KEY_PRESS_DATA.clock(DATA_LOG_INDEX) = clockNow;
KEY_PRESS_DATA.keyName{DATA_LOG_INDEX} = keyName;
DATA_LOG_INDEX = DATA_LOG_INDEX + 1;
% Put most recent data on the figure for the user:
TEXT_HANDLE.String{1} = sprintf('Time: %4.4f, ',timeNow);
TEXT_HANDLE.String{2} = ['Clock: ' datestr(clockNow,'HH:MM:SS.FFF')];
TEXT_HANDLE.String{3} = ['Key Name: ' keyName];
end
end
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
function stopLoggingData(~,~)
global KEY_PRESS_DATA DATA_LOG_INDEX DATA_LOG_LENGTH
global SAVE_FILE_NAME KEY_LOG_FIG TEXT_HANDLE
% Remove extra memory:
KEY_PRESS_DATA.time(DATA_LOG_INDEX:end) = [];
KEY_PRESS_DATA.clock(DATA_LOG_INDEX:end) = [];
KEY_PRESS_DATA.keyName(DATA_LOG_INDEX:end) = [];
% Ask user to confirm file save/overwrite (file name can be optionally passed to function):
answer = inputdlg({'Save file:'},'KeyLogger',1,{SAVE_FILE_NAME});
if ~isempty(answer) % Set name
SAVE_FILE_NAME = answer{1};
end
if exist(SAVE_FILE_NAME) == 2 % Check to avoid overiding an existing file
warning = questdlg('That file already exists! Append a .x (1) or Overwrite (2)?',...
'Warning',...
'Append x','Overwrite', 'Append x');
switch warning
case 'Append x'
fileproblem = 1;
case 'Overwrite'
fileproblem = 2;
end
if fileproblem==2
save(SAVE_FILE_NAME,'KEY_PRESS_DATA')
elseif fileproblem==1
SAVE_FILE_NAME = ['X_' SAVE_FILE_NAME];
save(SAVE_FILE_NAME,'KEY_PRESS_DATA');
end
else
save(SAVE_FILE_NAME,'KEY_PRESS_DATA');
end
% Release memory:
KEY_PRESS_DATA = [];
DATA_LOG_INDEX = [];
DATA_LOG_LENGTH = [];
SAVE_FILE_NAME = [];
KEY_LOG_FIG = [];
TEXT_HANDLE = [];
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment