Skip to content

Commit

Permalink
Merge pull request #624 from robertoostenveld/bug3385-eda
Browse files Browse the repository at this point in the history
add support for iMotions
  • Loading branch information
robertoostenveld committed Dec 29, 2017
2 parents 3583696 + e00123d commit 72b4de8
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 3 deletions.
10 changes: 10 additions & 0 deletions compat/matlablt2016b/newline.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function C = newline

% NEWLINE Create newline character
% C = newline creates the character representing a newline.
% newline is equivalent to char(10) or sprintf('\n').
%
% This is a compatibility function that should only be added to the path on
% MATLAB versions prior to 2016b.

C = char(10);
5 changes: 5 additions & 0 deletions fileio/ft_filetype.m
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,11 @@
type = 'curry_dig';
manufacturer = 'Curry';
content = 'digitizer file';

elseif filetype_check_extension(filename, '.txt') && filetype_check_header(filename, '#Study')
type = 'imotions_txt';
manufacturer = 'iMotions';
content = 'various biosignals';

elseif filetype_check_extension(filename, '.txt') && filetype_check_header(filename, '##')
type = 'smi_txt';
Expand Down
2 changes: 1 addition & 1 deletion fileio/private/read_eyelink_asc.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
fclose(fid);

aline(aline==uint8(sprintf('\r'))) = []; % remove cariage return
aline = tokenize(aline, uint8(sprintf('\n'))); % split on newline
aline = tokenize(aline, uint8(newline)); % split on newline

for i=1:numel(aline)
tline = aline{i};
Expand Down
2 changes: 1 addition & 1 deletion fileio/private/read_tobii_tsv.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
[key, val] = strtok(line, ':');
key = fixname(key);
val = val(2:end); % skip the ':'
val = deblank2(val);
val = strtrim(val);
tsv.(key) = val;
end

Expand Down
109 changes: 109 additions & 0 deletions imotions2fieldtrip.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
function [raw, event] = imotions2fieldtrip(filename, varargin)

% IMOTIONS2FIELDTRIP imports an iMotions *.txt file and represents it as a FieldTrip
% raw data structure.
%
% Use as
% data = imotions2fieldtrip(filename)
%
% See also FT_DATATYPE_RAW, FT_PREPROCESSING

% Copyright (C) 2017, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
% FieldTrip is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% FieldTrip is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% read the whole ASCII file into memory
% this will include a MATLAB table with the actual data
dat = read_imotions_txt(filename);

time = dat.TimestampInSec;
numeric = zeros(0,numel(time));
label = dat.data.Properties.VariableNames;
sellab = false(size(label));

% check for each field/column whether it is numeric
for i=1:numel(label)
% try converting the first element
str = dat.data.(label{i})(1);
val = str2double(str);
if ~isempty(str) && isnan(val)
ft_info('column %s is not numeric', label{i});
continue
end

% try converting the first 20 elements
if numel(time)>10
str = dat.data.(label{i})(1:20);
val = str2double(str);
if ~isempty(str) && any(isnan(val))
ft_info('column %s is not numeric', label{i});
continue
end
end

% try converting the whole column
str = dat.data.(label{i});
val = str2double(str);
if ~isempty(str) && any(isnan(val))
ft_info('column %s is not numeric', label{i});
continue
end

% if it gets here, it means that the whole column is numeric
sellab(i) = true;
ft_info('%s is numeric and will be represented as channel', label{i});
numeric = cat(1, numeric, val');
end

% the same timestamp can be on multiple lines in the file
dt = diff(sort(time));

if any(dt==0)
ft_notice('removing overlapping samples...\n');
t = 1;
while t<numel(time)
sel = find(time==time(t));
numeric(:,t) = nanmean(numeric(:,sel),2);
time(sel(2:end)) = nan;
t = t+numel(sel);
end

ft_notice('keeping %.0f of the original samples\n', mean(~isnan(time)));
time = time ( ~isnan(time));
numeric = numeric(:,~isnan(time));
end

% construct a raw data structure
raw.time = {time};
raw.trial = {numeric};
raw.label = label(sellab);

% interpolate the data
dt = diff(time);
dt = median(dt);
begtime = min(time);
endtime = max(time);

if any(diff(time)~=dt)
ft_notice('resampling onto regularly spaced time axis\n');
tmpcfg = [];
tmpcfg.time = {begtime:dt:endtime};
raw = ft_resampledata(tmpcfg, raw);
[~, raw] = rollback_provenance([], raw);
end
75 changes: 75 additions & 0 deletions private/read_imotions_txt.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
function dat = read_imotions_txt(filename)

% READ_IMOTIONS_TXT reads *.txt files that are exported from the iMotions software.
%
% Use as
% dat = read_imotions_txt(filename
%
% See also TEXTSCAN

% Copyright (C) 2017, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
% FieldTrip is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% FieldTrip is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

fprintf('reading header from %s\n', filename);
% read the whole file at once
fid = fopen(filename, 'rt');
aline = fread(fid, inf, 'char=>char'); % returns a single long string
fclose(fid);

aline(aline==uint8(sprintf('\r'))) = []; % remove cariage return
aline = tokenize(aline, uint8(newline)); % split on newline

dat = [];

% the first section contains multiple lines with general header information
headerline = 1;
while startsWith(aline{headerline}, '#')
% for example "#Version : 6.3.6059.1"
tok = tokenize(aline{headerline}(2:end), ':');
key = matlab.lang.makeValidName(strtrim(tok{1}));
val = strtrim(tok{2});
dat.(key) = val;
headerline = headerline + 1;
end

% there is one line with column headings
headerline = 1;
while ~startsWith(aline{headerline}, 'StudyName')
headerline = headerline+1;
end
dat.Labels = tokenize(aline{headerline});
dat.Labels = matlab.lang.makeValidName(dat.Labels);

fprintf('parsing tabular data\n');
% now that the header information is known, the actual content can be scanned
fid = fopen(filename, 'rt');
c = textscan(fid, repmat('%s', [1 numel(dat.Labels)]), 'Delimiter', '\t', 'Headerlines', headerline);
fclose(fid);

% add each column as a variable to the data structure
for i=1:numel(dat.Labels)
dat.data.(dat.Labels{i}) = c{i};
end
% convert to a table object (requires MATLAB R2013b and up)
dat.data = struct2table(dat.data);

fprintf('converting timestamps to seconds\n');
% convert timestamp to seconds
dat.TimestampInSec = datenum(dat.data.Timestamp, 'yyyymmdd_hhMMssfff');
7 changes: 6 additions & 1 deletion private/rollback_provenance.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@
continue
end

fn0 = fieldnames(cfg);
if isempty(cfg)
% allow for [] as the input cfg
fn0 = {};
else
fn0 = fieldnames(cfg);
end

if ~isfield(varargin{i}, 'cfg')
% input does not contain cfg, so no rollback to be performed
Expand Down

0 comments on commit 72b4de8

Please sign in to comment.