-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathbids_writechanfile.m
More file actions
157 lines (138 loc) · 5.08 KB
/
Copy pathbids_writechanfile.m
File metadata and controls
157 lines (138 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
% eeg_writechanfile - write channels.tsv from single EEG dataset. The
% function also outputs channels count
%
% Usage:
% channelsCount = eeg_writechanfile(EEG, fileOut)
%
%
%
% Inputs:
% 'EEG' - [struct] the EEG structure
%
% 'fileOut' - [string] filepath of the desired output location with file basename
% e.g. ~/BIDS_EXPORT/sub-01/ses-01/eeg/sub-01_ses-01_task-GoNogo
%
% Outputs:
%
% channelsCount - [struct] count of different types of channels
%
% Authors: Dung Truong, Arnaud Delorme, 2022
function bids_writechanfile(EEG, fileOut)
fid = fopen( [ fileOut '_channels.tsv' ], 'w');
isEMG = isfield(EEG, 'etc') && isfield(EEG.etc, 'datatype') && strcmpi(EEG.etc.datatype, 'emg');
isiEEG = isfield(EEG, 'etc') && isfield(EEG.etc, 'datatype') && strcmpi(EEG.etc.datatype, 'ieeg');
if isempty(EEG.chanlocs)
if isiEEG
fprintf(fid, 'name\ttype\tunits\tlow_cutoff\thigh_cutoff\n');
for iChan = 1:EEG.nbchan
fprintf(fid, 'E%d\tiEEG\tmicroV\tn/a\tn/a\n', iChan);
end
elseif isEMG
% EMG - only write REQUIRED columns when no chanlocs
fprintf(fid, 'name\ttype\tunits\n');
for iChan = 1:EEG.nbchan
fprintf(fid, 'E%d\tEMG\tV\n', iChan);
end
else
fprintf(fid, 'name\ttype\tunits\n');
for iChan = 1:EEG.nbchan
fprintf(fid, 'E%d\tEEG\tmicroV\n', iChan);
end
end
channelsCount = struct([]);
else
% Determine which columns to write based on available data
columnsToWrite = {'name', 'type', 'units'}; % REQUIRED
if isEMG
% Check EMG RECOMMENDED columns for actual data
recommendedFields = {'signal_electrode', 'reference', 'group', 'target_muscle', ...
'placement_scheme', 'placement_description', 'interelectrode_distance', ...
'low_cutoff', 'high_cutoff', 'sampling_frequency'};
availableFields = {};
missingFields = {};
for iField = 1:length(recommendedFields)
fieldName = recommendedFields{iField};
hasData = false;
% Check if any channel has this field with actual data
for iChan = 1:EEG.nbchan
if isfield(EEG.chanlocs(iChan), fieldName) && ...
~isempty(EEG.chanlocs(iChan).(fieldName)) && ...
~strcmpi(EEG.chanlocs(iChan).(fieldName), 'n/a')
hasData = true;
break;
end
end
if hasData
columnsToWrite{end+1} = fieldName;
availableFields{end+1} = fieldName;
else
missingFields{end+1} = fieldName;
end
end
% Display warning about missing RECOMMENDED columns
if ~isempty(missingFields)
fprintf('Note: The following RECOMMENDED EMG channel columns are not included (no data available): %s\n', ...
strjoin(missingFields, ', '));
end
elseif isiEEG
% iEEG includes low_cutoff and high_cutoff
columnsToWrite = [columnsToWrite, {'low_cutoff', 'high_cutoff'}];
end
% Write header
fprintf(fid, '%s\n', strjoin(columnsToWrite, '\t'));
% Write data
acceptedChannelTypes = { 'AUDIO' 'EEG' 'EOG' 'ECG' 'EMG' 'EYEGAZE' 'GSR' 'HEOG' 'MISC' 'PUPIL' 'REF' 'RESP' 'SYSCLOCK' 'TEMP' 'TRIG' 'VEOG' };
for iChan = 1:EEG.nbchan
values = {};
% Name (always)
values{end+1} = EEG.chanlocs(iChan).labels;
% Type
if ~isfield(EEG.chanlocs, 'type') || isempty(EEG.chanlocs(iChan).type)
type = 'n/a';
elseif ismember(upper(EEG.chanlocs(iChan).type), acceptedChannelTypes)
type = upper(EEG.chanlocs(iChan).type);
else
type = 'MISC';
end
values{end+1} = type;
% Unit
if isfield(EEG.chanlocs(iChan), 'unit')
unit = EEG.chanlocs(iChan).unit;
else
if strcmpi(type, 'eeg')
unit = 'uV';
else
unit = 'n/a';
end
end
values{end+1} = unit;
% Additional columns (only those determined to have data)
for iCol = 4:length(columnsToWrite)
fieldName = columnsToWrite{iCol};
if isfield(EEG.chanlocs(iChan), fieldName) && ~isempty(EEG.chanlocs(iChan).(fieldName))
val = EEG.chanlocs(iChan).(fieldName);
if isnumeric(val)
values{end+1} = num2str(val);
else
values{end+1} = val;
end
else
values{end+1} = 'n/a';
end
end
if isnan(values{end}), values{end} = 'n/a'; end
fprintf(fid, '%s\n', strjoin(values, '\t'));
end
end
fclose(fid);
% Helper function to get field value or 'n/a'
function value = getfield_or_na(struct, fieldname)
if isfield(struct, fieldname) && ~isempty(struct.(fieldname))
value = struct.(fieldname);
% Convert numeric to string
if isnumeric(value)
value = num2str(value);
end
else
value = 'n/a';
end