├── .gitignore
├── README.rst
├── dependencies
├── csc_Topoplot.m
├── csc_add2dlocations.m
├── csc_events_to_hypnogram.m
├── csc_export_for_scoring.m
├── csc_hypnogram_to_events.m
├── csc_import_events_from_EEG.m
├── csc_labeled_locations.m
├── csc_progress_indicator.m
└── csc_subplot.m
├── io
├── csc_convert_from_raw.m
├── csc_load_eeglab.m
└── mff_scripts_internal
│ ├── convert_mffevents_to_eeglab_eventsNS5.m
│ ├── mff_convert_to_EEGLAB.m
│ ├── mff_convert_to_chanlocs.m
│ ├── mff_determine_calibrations.m
│ ├── mff_determine_epoch_breaks.m
│ ├── mff_export_event_track.m
│ ├── mff_export_event_track_from_meta_data.m
│ ├── mff_find_absolute_timestamp.m
│ ├── mff_import_coordinates.m
│ ├── mff_import_epochs.m
│ ├── mff_import_events.m
│ ├── mff_import_eventsNS5.m
│ ├── mff_import_info.m
│ ├── mff_import_info_NS5.m
│ ├── mff_import_meta_data.m
│ ├── mff_import_sensorLayout.m
│ ├── mff_import_sessions.m
│ ├── mff_import_signal_binary.m
│ ├── mff_import_signal_binary_meta_data.m
│ ├── mff_import_signal_info.m
│ ├── mff_map_session_breaks.m
│ ├── mff_mark_bad_channels.m
│ ├── mff_read_block.m
│ ├── mff_read_channels.m
│ ├── mff_read_samples.m
│ ├── mff_read_to_mat_file.m
│ ├── mff_transmogrify_to_mat_file.asv
│ ├── mff_transmogrify_to_mat_file.m
│ └── write_mff_events_from_secs_vector.m
├── preprocessing
├── artifact_rejection
│ ├── csc_artefacts_from_events.m
│ ├── csc_artifact_detection_fft.m
│ ├── csc_artifact_detection_semiauto.m
│ ├── csc_artifact_rejection.m
│ ├── csc_artifact_rejection_fft_gui.m
│ ├── csc_average_reference_and_FFT.m
│ ├── csc_calculate_freq_bands.m
│ ├── csc_ecg_removal.m
│ └── csc_heart_beat_detection.m
├── csc_erp_simulation.m
└── csc_quick_spectral_analysis.m
├── template
└── csc_preprocessing_template.m
├── tools
├── csc_compare_scoring.m
├── csc_compare_sleep_scoring.m
├── csc_convert_to_classic.m
├── csc_eg_rms_v_mean.m
├── csc_epoched_fft.m
├── csc_ersp_analysis.m
├── csc_filter.m
├── csc_first_order_highpass.m
├── csc_phase_locking_factor.m
├── csc_sleep_statistics.m
├── csc_trial_differentiation_analysis.m
├── csc_unique_count.m
└── external
│ └── eBridge.m
├── visualisation
├── Montages
│ └── test.emo
├── UndocumentedMatlab
│ ├── findjobj.m
│ └── license.txt
├── csc_component_plot.m
├── csc_eeg_plotter.m
├── csc_get_plotter_defaults.m
├── csc_group_time_series.m
├── csc_spectrogram.m
└── csc_topography_analysis.m
└── wiki
└── figures
├── 0.1-50Hz filter binica 1000Hz sampling rate with 1Hz Filter.jpg
└── 0.1-50Hz filter binica 1000Hz sampling rate.jpg
/.gitignore:
--------------------------------------------------------------------------------
1 | *.m~
2 | visualisation/Montages/*
3 | !visualisation/Montages/test.emo
4 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | csc-eeg-tools
2 | =============
3 |
4 | This set of functions is designed to be used as quick tools in matlab for eeg processing
5 | The tools can be used on their own or by using one of the template files provided
6 |
7 | Get the code
8 | ^^^^^^^^^^^^
9 |
10 | To get the latest code using git, simply type:
11 | git clone https://github.com/CSC-UW/csc-eeg-tools.git
12 |
13 | Installation
14 | ^^^^^^^^^^^^
15 | 1. download zip file
16 | 2. extract and save under your prefered path
17 | 3. add the path to matlab and you're ready to go
18 |
19 | Key-Shortcuts for Plotter
20 | ^^^^^^^^^^^^^^^^^^^^^^^^^
21 | Navigation
22 | ''''''''''
23 | +-------------------------------------+----------------------------------------------------------------+
24 | | Usage | Shortcut |
25 | +=====================================+================================================================+
26 | | number of displayed channels | ctrl + d |
27 | +-------------------------------------+----------------------------------------------------------------+
28 | | change epoch length | ctrl + e |
29 | +-------------------------------------+----------------------------------------------------------------+
30 | | toggle components/channels | ctrl + t |
31 | +-------------------------------------+----------------------------------------------------------------+
32 | | set filter parameters | ctrl + f |
33 | +-------------------------------------+----------------------------------------------------------------+
34 | | move to next epoch | arrow right |
35 | +-------------------------------------+----------------------------------------------------------------+
36 | | move to previous epoch | arrow left |
37 | +-------------------------------------+----------------------------------------------------------------+
38 | | move right (not a full epoch) | ctrl + arrow right |
39 | +-------------------------------------+----------------------------------------------------------------+
40 | | move left (not a full epoch) | ctrl + arrow left |
41 | +-------------------------------------+----------------------------------------------------------------+
42 | | move down to next channel section | page up |
43 | +-------------------------------------+----------------------------------------------------------------+
44 | | move up to previous channel section | page down |
45 | +-------------------------------------+----------------------------------------------------------------+
46 | | set event marker | use keyboard numbers to place event at mouse position |
47 | +-------------------------------------+----------------------------------------------------------------+
48 | | set event marker alternative | right mouse click and use context menu |
49 | +-------------------------------------+----------------------------------------------------------------+
50 | | delete event marker | left mouse click on the event marker |
51 | +-------------------------------------+----------------------------------------------------------------+
52 |
53 |
54 | View
55 | ''''
56 | +-------------------------------------+----------------------------------------------------------------+
57 | | Usage | Shortcut |
58 | +=====================================+================================================================+
59 | | increase channel scale | arrow up |
60 | +-------------------------------------+----------------------------------------------------------------+
61 | | decrease channel scale | arrow down |
62 | +-------------------------------------+----------------------------------------------------------------+
63 | | hide/show dotted vertical grid | g |
64 | +-------------------------------------+----------------------------------------------------------------+
65 | | hide/show dotted horizontal grid | h |
66 | +-------------------------------------+----------------------------------------------------------------+
67 | | set vertical scale spacing | ctrl + g |
68 | +-------------------------------------+----------------------------------------------------------------+
69 | | set horizontal grid spacing | ctrl + h |
70 | +-------------------------------------+----------------------------------------------------------------+
71 | | hide / mark channels | click on channel label |
72 | +-------------------------------------+----------------------------------------------------------------+
73 | | toggle negative up/down | ctrl + n |
74 | +-------------------------------------+----------------------------------------------------------------+
75 |
76 |
77 |
78 | Tools
79 | '''''
80 | +-------------------------------------+----------------------------------------------------------------+
81 | | Usage | Shortcut |
82 | +=====================================+================================================================+
83 | | load eeg | ctrl + l |
84 | +-------------------------------------+----------------------------------------------------------------+
85 | | save eeg | ctrl + s |
86 | +-------------------------------------+----------------------------------------------------------------+
87 | | export hidden channels | ctrl + x |
88 | +-------------------------------------+----------------------------------------------------------------+
89 | | export marked trials | ctrl + t |
90 | +-------------------------------------+----------------------------------------------------------------+
91 |
92 | How to sleep score using the plotter
93 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
94 | Open the *csc_eeg_plotter(EEG)*, you will likely want to adjust the montage to match a more typical sleep scoring montage (e.g. F3-A2).
95 |
96 | For classic sleep scoring, change the mode in the tools menu and use the numbers on the keyboard to mark the stages (wake should be number 6, not 0).
97 | You can mark arousal beginnings and end using the number 4 (since there is no NREM4 anymore).
98 |
99 | For continuous sleep scoring simple place a marker at the beginning of the stage transition (keyboard number at mouse position).
100 |
101 | If there is a long period prior to sleep that should not be considered for scoring (e.g. lights on), then simply begin scoring at a later epoch.
102 |
103 | Once your scoring is complete, you can use the function *csc_events_to_hypnogram* to produce a table and convert into stages stored in *EEG.swa_scoring.stages*.
104 | You should probably also save the events to file just in case (using the event menu in the plotter).
105 |
106 | How to use the functions
107 | ^^^^^^^^^^^^^^^^^^^^^^^^
108 | in this example we assume `EEG` is you loaded dataset:
109 |
110 | to open the plotter and check e.g. for artifacts and hide bad channels use the code
111 | ```
112 | EEG = csc_eeg_plotter(EEG);
113 | ```
114 | to change the hidden channels to bad channels use
115 | ```
116 | EEG.bad_channels{1} = EEG.hidden_channels;
117 | ```
118 | to reject/delete channels with artifacts
119 | ```
120 | EEG = pop_select(EEG, 'nochannel', EEG.bad_channels{1});
121 | ```
122 |
123 | if you have run e.g. ICA on your dataset and want to look/remove components
124 |
125 | open the plotter and also the component plot for each channel
126 | ```
127 | csc_eeg_plotter(EEG);
128 | EEG.good_components = csc_component_plot(EEG);
129 | ```
130 | now to remove the marked bad components and automaticaly change the local eeg variable use
131 | ```
132 | EEG = pop_subcomp( EEG , find(~EEG.good_components));
133 | EEG = eeg_checkset(EEG);
134 | ```
135 | you can also open the *csc_eeg_plotter* to visualise the components and also plot the channel activity as if the selected components had already been removed
136 |
137 | Troubleshooting
138 | ^^^^^^^^^^^^^^^
139 | Comments and suggestions are more than welcome, just create an issue in github with the specific question/suggestion
140 |
141 |
142 |
--------------------------------------------------------------------------------
/dependencies/csc_Topoplot.m:
--------------------------------------------------------------------------------
1 | function H = csc_Topoplot(data_to_plot, e_loc, varargin)
2 | %% New Topoplot Function
3 | %
4 | % Usage H = csc_Topoplot(data_to_plot, e_loc, varargin)
5 | %
6 | % V is a single column vector of the data to be plotted (must have the length of the number of channels)
7 | % e_loc is the EEGLAB generated structure containing all information about electrode locations
8 | %
9 | %
10 | % Optional Arguments:
11 | % See default settings below (can all be changed as optional arguments
12 |
13 | % you can redistribute it and/or modify
14 | % it under the terms of the GNU General Public License as published by
15 | % the Free Software Foundation, either version 3 of the License, or
16 | % (at your option) any later version.
17 | % ept_ResultViewer is distributed in the hope that it will be useful,
18 | % but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | % GNU General Public License for more details.
21 | % You should have received a copy of the GNU General Public License
22 | % along with ept_ResultViewer. If not, see .
23 |
24 | %% Set defaults
25 | GridScale = 100; % Determines the quality of the image
26 | HeadWidth = 2.5;
27 | HeadColor = [0,0,0];
28 | ContourWidth = 0.5;
29 | NumContours = 12;
30 |
31 | PlotContour = 1; % Determines whether the contour lines are drawn
32 | PlotSurface = 0; % Determines whether the surface is drawn
33 | PlotHead = 1; % Determines whether the head is drawn
34 | PlotChannels = 1; % Determines whether the channels are drawn
35 | MarkedChannels = false(length(e_loc), 1); % Determines whether significant channels are plotted separately
36 | MarkedString = '!';
37 | MarkedColor = [0, 0, 0];
38 |
39 | ExcludeChannels = [];
40 |
41 | NewFigure = 1; % 0/1 - Whether to explicitly draw a new figure for the topoplot
42 | Axes = 0;
43 |
44 | if isempty(data_to_plot)
45 | fprintf(1, 'Warning: Input data was found to be empty \n');
46 | return;
47 | end
48 |
49 | %% Process Secondary Arguments
50 | if nargin > 2
51 | if ~(round(nargin/2) == nargin/2)
52 | error('Odd number of input arguments??')
53 | end
54 | for n = 1:2:length(varargin)
55 | Param = varargin{n};
56 | Value = varargin{n+1};
57 | if ~ischar(Param)
58 | error('Flag arguments must be strings')
59 | end
60 | Param = lower(Param);
61 |
62 | switch Param
63 | case 'headwidth'
64 | HeadWidth = Value;
65 | case 'numcontours'
66 | NumContours = Value;
67 | case 'newfigure'
68 | NewFigure = Value;
69 | case 'axes'
70 | Axes = Value;
71 | case 'plotcontour'
72 | PlotContour = Value;
73 | case 'plotsurface'
74 | PlotSurface = Value;
75 | case 'plotchannels'
76 | PlotChannels = Value;
77 | case 'markedchannels'
78 | MarkedChannels = Value;
79 | case 'markedcolor'
80 | MarkedColor = Value;
81 | case 'markedstring'
82 | MarkedString = Value;
83 | case 'plothead'
84 | PlotHead = Value;
85 | case 'excludechannels'
86 | ExcludeChannels = Value;
87 | otherwise
88 | display (['Unknown parameter setting: ' Param])
89 | end
90 | end
91 | end
92 |
93 | %% Adjust Settings based on Arguments
94 |
95 | if nansum(data_to_plot(:)) == 0
96 | PlotContour = 0;
97 | end
98 |
99 | % if axes is specified no new figure
100 | if Axes ~= 0
101 | NewFigure = 0;
102 | end
103 |
104 | % Overwrite number of contours if the interpolated surface is drawn
105 | if PlotSurface == 1
106 | PlotContour = 0; % Overwrite contours for surface
107 | NumContours = 5;
108 | end
109 |
110 | % check that marked channels size is same as electrodes
111 | if ~isempty(MarkedChannels)
112 | if length(MarkedChannels) ~= length(e_loc)
113 | fprintf(1, 'Warning: Marked Channels different size than number of electrodes.\n');
114 | MarkedChannels = false(length(e_loc), 1);
115 | end
116 | end
117 |
118 | % Adjust the contour lines to account for the minimum and maximum difference in values
119 | LevelList = linspace(min(data_to_plot(:)), max(data_to_plot(:)), NumContours);
120 |
121 | % adjust to exclude channels
122 | if ~isempty(ExcludeChannels)
123 | e_loc(ExcludeChannels) = [];
124 | data_to_plot(ExcludeChannels) = [];
125 | MarkedChannels(ExcludeChannels) = [];
126 | end
127 |
128 | %% Use the e_loc to project points to a 2D surface
129 |
130 | Th = pi/180*[e_loc.theta]; % Calculate theta values from x,y,z e_loc
131 | Rd = [e_loc.radius]; % Calculate radian values from x,y,z e_loc
132 |
133 | x_coordinates = Rd.*cos(Th); % Calculate 2D projected X
134 | y_coordinates = Rd.*sin(Th); % Calculate 2D projected Y
135 |
136 | % Squeeze the coordinates into a -0.5 to 0.5 box
137 | intrad = min(1.0,max(abs(Rd))); intrad = max(intrad,0.5); squeezefac = 0.5/intrad;
138 |
139 | x_coordinates = x_coordinates * squeezefac;
140 | y_coordinates = y_coordinates * squeezefac;
141 |
142 | %% Create the plotting mesh
143 | XYrange = linspace(-0.5, 0.5, GridScale);
144 | XYmesh = XYrange(ones(GridScale,1),:);
145 |
146 | %% Create the interpolation function
147 | x_coordinates=x_coordinates(:); y_coordinates=y_coordinates(:); data_to_plot = data_to_plot(:); % Ensure data is in column format
148 |
149 | % Check Matlab version for interpolant...
150 | if exist('scatteredInterpolant', 'file')
151 | % If its available use the newest function
152 | F = scatteredInterpolant(x_coordinates, y_coordinates, data_to_plot, 'natural', 'none');
153 | else
154 | % Use the old function
155 | F = TriScatteredInterp(x_coordinates, y_coordinates, data_to_plot, 'natural');
156 | end
157 |
158 | % apply function
159 | interpolated_map = F(XYmesh', XYmesh);
160 |
161 | %% Actual Plot
162 |
163 | % Prepare the figure
164 | % Check if there is a figure currently opened; otherwise open a new figure
165 | if isempty(get(0,'children')) || NewFigure == 1
166 | H.Figure = figure;
167 | set(H.Figure,...
168 | 'Color', 'w' ,...
169 | 'Renderer', 'painters' );
170 |
171 | H.CurrentAxes = axes('Position',[0 0 1 1]);
172 | elseif Axes ~= 0
173 | H.CurrentAxes = Axes;
174 | else
175 | H.CurrentAxes = gca;
176 | end
177 | %
178 | % Prepare the axes
179 | set(H.CurrentAxes,...
180 | 'XLim', [-0.5, 0.5] ,...
181 | 'YLim', [-0.5, 0.5] ,...
182 | 'NextPlot', 'add' );
183 |
184 | %% Plot the contour map
185 | if PlotContour == 1
186 | [~,H.Contour] = contourf(H.CurrentAxes, XYmesh,XYmesh',interpolated_map);
187 | set(H.Contour,...
188 | 'EdgeColor', 'none' ,...
189 | 'LineWidth', ContourWidth ,...
190 | 'LineStyle', 'none' ,...
191 | 'LevelList', LevelList ,...
192 | 'HitTest', 'off' );
193 | end
194 |
195 | %% Plot the surface interpolation
196 | if PlotSurface == 1
197 | unsh = (GridScale+1)/GridScale; % un-shrink the effects of 'interp' SHADING
198 | H.Surface = surface(XYmesh*unsh ,XYmesh'*unsh, zeros(size(interpolated_map)), interpolated_map);
199 | set(H.Surface,...
200 | 'EdgeColor', 'none' ,...
201 | 'FaceColor', 'interp' ,...
202 | 'HitTest', 'off' );
203 | end
204 |
205 | %% Prepare the Head, Ears, and Nose (thanks EEGLAB!)
206 | if PlotHead == 1;
207 | sf = 0.333/0.5; %Scaling factor for the headsize
208 |
209 | % Head
210 | angle = 0:1:360;
211 | datax = (cos(angle*pi/180))/3;
212 | datay = (sin(angle*pi/180))/3;
213 |
214 | % Nose...
215 | base = 0.4954;
216 | basex = 0.0900; % nose width
217 | tip = 0.5750;
218 | tiphw = 0.02; % nose tip half width
219 | tipr = 0.005; % nose tip rounding
220 |
221 | % Ears...
222 | q = .04; % ear lengthening
223 | EarX = [.497-.005 .510 .518 .5299 .5419 .54 .547 .532 .510 .489-.005]; % rmax = 0.5
224 | EarY = [q+.0555 q+.0775 q+.0783 q+.0746 q+.0555 -.0055 -.0932 -.1313 -.1384 -.1199];
225 |
226 | % Plot the head
227 | H.Head(1) = plot(H.CurrentAxes, datax, datay);
228 | H.Head(2) = plot(H.CurrentAxes,...
229 | [basex;tiphw;0;-tiphw;-basex]*sf,[base;tip-tipr;tip;tip-tipr;base]*sf);
230 | H.Head(3) = plot(H.CurrentAxes,...
231 | EarX*sf,EarY*sf);% plot left ear
232 | H.Head(4) = plot(H.CurrentAxes,...
233 | -EarX*sf,EarY*sf); % plot right ear
234 |
235 | % Set the head properties
236 | set(H.Head,...
237 | 'Color', HeadColor ,...
238 | 'LineWidth', HeadWidth ,...
239 | 'HitTest', 'off' );
240 | end
241 |
242 |
243 | % Plot Channels
244 | % ^^^^^^^^^^^^^
245 | labels = {e_loc.labels};
246 | % set the label for each individually
247 | for n = 1:size(labels,2)
248 | H.Channels(n) = text(y_coordinates(n),x_coordinates(n), '.', ...
249 | 'userdata', char(labels(n)), ...
250 | 'visible', 'off', ...
251 | 'parent', H.CurrentAxes);
252 | end
253 |
254 | % set channel parameters
255 | set(H.Channels, ...
256 | 'HorizontalAlignment', 'center' ,...
257 | 'VerticalAlignment', 'middle' ,...
258 | 'Color', 'k' ,...
259 | 'FontSize', 10 ,...
260 | 'FontWeight', 'bold' );
261 |
262 | % if plot channels is enabled, make them visible
263 | % and configure the button down function
264 | if PlotChannels
265 | set(H.Channels, ...
266 | 'visible', 'on', ...
267 | 'buttondownfcn', ...
268 | ['tmpstr = get(gco, ''userdata'');' ...
269 | 'set(gco, ''userdata'', get(gco, ''string''));' ...
270 | 'set(gco, ''string'', tmpstr); clear tmpstr;'] );
271 | end
272 |
273 | % plot the marked channels
274 | % ^^^^^^^^^^^^^^^^^^^^^^^^
275 | if ~isempty(MarkedChannels)
276 | set(H.Channels(MarkedChannels), ...
277 | 'visible', 'on', ...
278 | 'fontSize', 15, ...
279 | 'color', MarkedColor, ...
280 | 'string', MarkedString);
281 | end
282 |
283 |
284 | % Adjustments
285 | % ^^^^^^^^^^^
286 | % square axes
287 | set(H.CurrentAxes, 'PlotBoxAspectRatio', [1, 1, 1]);
288 | % hide the axes
289 | set(H.CurrentAxes, 'visible', 'off');
290 |
291 |
--------------------------------------------------------------------------------
/dependencies/csc_add2dlocations.m:
--------------------------------------------------------------------------------
1 | %% Add 2D coordinates to electrode file...
2 |
3 | function eloc = swa_add2dlocations(eloc, GS)
4 |
5 | if nargin < 2
6 | GS = 40;
7 | end
8 |
9 | Th = pi/180*[eloc.theta]; % Calculate theta values from x,y,z e_loc
10 | Rd = [eloc.radius]; % Calculate radian values from x,y,z e_loc
11 |
12 | x = Rd.*cos(Th); % Calculate 2D projected X
13 | y = Rd.*sin(Th); % Calculate 2D projected Y
14 |
15 | x = x(:);
16 | x = x-min(x);
17 | x = num2cell(((x/max(x))*(GS-1))+1);
18 |
19 | y = y(:);
20 | y = y-min(y);
21 | y = num2cell(((y/max(y))*(GS-1))+1);
22 |
23 | [eloc.x] = deal(x{:});
24 | [eloc.y] = deal(y{:});
--------------------------------------------------------------------------------
/dependencies/csc_events_to_hypnogram.m:
--------------------------------------------------------------------------------
1 | function [EEG, table_data, handles] = csc_events_to_hypnogram(EEG, flag_plot, flag_mode, flag_type)
2 | % turns event data from the csc_eeg_plotter into sleep stages and plots data
3 |
4 | % [EEG, table_data, handles] = csc_events_to_hypnogram(EEG, ...
5 | % flag_plot, ... % [0/1] whether to plot the hypnogram or not
6 | % flag_mode, ... % [0/1] whether classic [0] or continuous [1] scoring was used... or basically whether arousals have two 4 events [0] or artefacts have a single event 4 at the start [1]
7 | % flag_type) % [0/1] whether to output the long table [0] with lots of accurate parameters (for research purposes), or the nice summary table for patient reports [1]
8 |
9 | handles = [];
10 |
11 | % Notes
12 | % ^^^^^
13 | % flag_mode denotes whether its classical (0) or transitional scoring (1)
14 | % the essential distinction is how to treat "event 4": in classical scoring this
15 | % is used to mark arousals (one at the start and end of the arousal), in
16 | % transitional scoring arousals are marked simply as brief wake periods and
17 | % "event 4" is used to denote signal artefacts (and ignored for scoring)
18 |
19 | % custom options
20 | if nargin < 2
21 | flag_plot = false;
22 | end
23 | if nargin < 3
24 | flag_mode = 0; % default to classical sleep scoring
25 | end
26 | if nargin < 4
27 | flag_type = 0; % default to csc table
28 | end
29 |
30 | %% make sure events are sorted by time
31 | [~, sort_ind] = sort([EEG.csc_event_data{:, 2}]);
32 | EEG.csc_event_data = EEG.csc_event_data(sort_ind, :);
33 |
34 | %% remove any events > 6
35 | non_sleep_events = [EEG.csc_event_data{:, 3}] > 6;
36 | EEG.csc_event_data(non_sleep_events, :) = [];
37 |
38 | %% remove all event 4 and save in separate arousal/artefact table
39 | % ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
40 | % find event 4s
41 | event4_logical = [EEG.csc_event_data{:, 3}] == 4;
42 | event4_ind = find(event4_logical);
43 |
44 | % create new table of only event 4s
45 | event4_table = [EEG.csc_event_data(event4_logical, :)];
46 |
47 | % pre-allocate arousal logical
48 | EEG.swa_scoring.arousals = false(EEG.pnts, 1);
49 |
50 | % loop over each event 4
51 | if flag_mode == 0 && sum(event4_logical) > 0
52 | % there should be an even number of event 4s since each marks start and end
53 | % of an arousal
54 | if mod(sum(event4_logical), 2)
55 | error('There is an odd number of event 4s, check your events')
56 | end
57 |
58 | for n = 1 : 2 : sum(event4_logical)
59 | % find samples
60 | start_sample = floor(event4_table{n, 2} + 0.000001 * EEG.srate) + 1;
61 | end_sample = floor(event4_table{n + 1, 2} * EEG.srate);
62 | % mark the interval samples as true
63 | EEG.swa_scoring.arousals(start_sample : end_sample) = true;
64 | end
65 |
66 | elseif flag_mode == 1
67 | % each artefact's end is simply marked by the next stage
68 | for n = 1 : sum(event4_logical)
69 | % find samples
70 | start_sample = floor(event4_table{n, 2} * EEG.srate) + 1;
71 |
72 | % check that last event is a 4
73 | if EEG.csc_event_data{end, 3} == 4
74 | end_sample = EEG.pnts;
75 | else
76 | end_sample = floor(EEG.csc_event_data{event4_ind(n) + 1, 2} * EEG.srate);
77 | end
78 | % mark the interval samples as true
79 | EEG.swa_scoring.arousals(start_sample : end_sample) = true;
80 | end
81 | end
82 |
83 |
84 | %% produce the sleep scoring stages %
85 | % '''''''''''''''''''''''''''''''' %
86 | % create a temporary table without event 4
87 | tmp_events = EEG.csc_event_data(~event4_logical, :);
88 |
89 | % convert event timing from seconds to samples
90 | event_timing = ceil([tmp_events{:, 2}] * EEG.srate);
91 |
92 | % check that first event sample is not 0
93 | if event_timing(1) == 0
94 | event_timing(1) = 1;
95 | end
96 |
97 | % pre-allocate to wake
98 | stages = int8(ones(1, EEG.pnts) * -1);
99 |
100 | % loop over each scoring event
101 | for n = 1 : length(tmp_events)
102 |
103 | % check for last event
104 | if n ~= length(tmp_events)
105 | stages(event_timing(n) : event_timing(n + 1)) = ...
106 | tmp_events{n, 3};
107 | else
108 | stages(event_timing(n) : end) = ...
109 | tmp_events{n, 3};
110 | end
111 |
112 | end
113 |
114 | % convert 6 to wake
115 | stages(stages == 6) = 0;
116 |
117 | % put back in EEG
118 | EEG.swa_scoring.stages = stages;
119 |
120 | % get the sleep stats
121 | if flag_type == 0
122 | table_data = csc_sleep_statistics(EEG, flag_mode);
123 | else
124 | table_data = swa_sleep_statistics(EEG, 0, 'english', flag_mode);
125 | end
126 |
127 | %% plot classic hypnogram and stage pie chart
128 | if flag_plot
129 |
130 | % add 1 to all stages except wake
131 | stages(stages > 0) = stages(stages > 0) + 1;
132 |
133 | % convert REM (now 6 after previous line) to 1
134 | stages(stages == 6) = 1;
135 |
136 | % convert time to hours for plot
137 | time_range = EEG.times / 1000 / 60 / 60;
138 |
139 | % create a new range to colour REM in red
140 | REM_range = double(stages); REM_range(REM_range ~= 1) = NaN;
141 |
142 | % plot hypnogram
143 | handles.fig = figure('color', 'w');
144 | handles.ax = axes('nextplot', 'add', ...
145 | 'xlim', [0, time_range(end)], ...
146 | 'ytick', 0:4, ...
147 | 'yticklabel', {'WAKE', 'REM', 'N1', 'N2', 'N3'}, ...
148 | 'yDir', 'reverse', ...
149 | 'ylim', [-0.5, 4.5]);
150 | handles.hypno = plot(time_range, stages, ...
151 | 'lineWidth', 1);
152 | handles.REM_plot = plot(time_range, REM_range, ...
153 | 'lineWidth', 4, ...
154 | 'color', 'r');
155 |
156 | % plot arousals/artefacts
157 | arousal_range = double(EEG.swa_scoring.arousals);
158 | arousal_range(arousal_range == 0) = NaN;
159 | handles.arousals = plot(time_range, arousal_range - 1.2, ...
160 | 'lineWidth', 3, ...
161 | 'color', 'k');
162 |
163 | % label axes
164 | xlabel('time (hours)')
165 | ylabel('sleep stage')
166 |
167 | % make a pie chart
168 | % TODO: make pie chart with csc table output
169 | if flag_type
170 | pie_data = cell2mat(table_data(3:7, 4));
171 | figure('color', 'w');
172 | handles.pie = pie(pie_data, {'', '', '', '', ''});
173 | end
174 | end
--------------------------------------------------------------------------------
/dependencies/csc_export_for_scoring.m:
--------------------------------------------------------------------------------
1 | function EEG = csc_export_for_scoring(EEG, desired_channels)
2 | % function to downsample and export only relevant channels to new smaller file
3 |
4 | if nargin < 2
5 | % extended psg channels for EGI-256 with backup channels around mastoid electrodes
6 | desired_channels = ...
7 | [241, 10, 41, 15, 214, 59, 183, 86, 101, 162, 124, 149, 111, 190, 94, 102, 201, 178, 93, 95];
8 | end
9 |
10 | % select only relevant channels
11 | % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 | % get channels from data
13 | EEG.data = EEG.data(desired_channels, :);
14 | % from channel locations
15 | EEG.chanlocs = EEG.chanlocs(desired_channels);
16 | % from channel info
17 | EEG.nbchan = length(desired_channels);
18 | EEG = eeg_checkset(EEG);
19 |
20 | % filter the data before downsampling
21 | % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22 | % first-order highpass (mimics analog filter)
23 | fprintf(1, 'applying high pass filter...\n');
24 | EEG = csc_first_order_highpass(EEG, 0.1);
25 |
26 | % use EEGLAB's resample which uses Nyquist low-pass filter
27 | % N: perhaps not the best filter generally but sufficient for viewing and scoring
28 | EEG = pop_resample(EEG, 100);
29 |
30 |
--------------------------------------------------------------------------------
/dependencies/csc_hypnogram_to_events.m:
--------------------------------------------------------------------------------
1 | % hypnogram to events
2 |
3 | % find all the transitions
4 | transitions = [0, find(diff(int8(EEG.swa_scoring.stages)) ~= 0)] + 1;
5 |
6 | % what stage did it transition to?
7 | events = EEG.swa_scoring.stages(transitions);
8 |
9 | % change stage 0 to event 6
10 | events(events==0) = 6;
11 | % change stage 255 to 4
12 | events(events==255) = 4;
13 |
14 | % plot found transitions
15 | figure('color', 'w');
16 | axes('nextplot', 'add', ...
17 | 'ylim', [-0.5, 5.5]);
18 | plot(EEG.swa_scoring.stages);
19 | plot(transitions, events, 'linestyle', 'none', 'marker', 'v', ...
20 | 'markerFaceColor', 'k');
21 |
22 |
23 | % create event_data for csc_eeg_plotter
24 | % pre-allocate the event data
25 | event_data = cell(length(events), 3);
26 |
27 | % insert latencies
28 | event_data(:, 2) = num2cell(transitions / EEG.srate);
29 |
30 | % insert type
31 | event_data(:, 3) = num2cell(events);
32 |
33 | % insert event labels
34 | % NOTE: no one ever changes the event labels from default so this might be deletable
35 | all_labels = cellfun(@(x) ['event ', num2str(x)], event_data(:, 3), 'uni', 0);
36 | event_data(:, 1) = all_labels;
37 |
38 | % sort the events by latency
39 | [~, sort_ind] = sort([event_data{:, 2}]);
40 | EEG.csc_event_data = event_data(sort_ind, :);
--------------------------------------------------------------------------------
/dependencies/csc_import_events_from_EEG.m:
--------------------------------------------------------------------------------
1 | function EEG = csc_import_events_from_EEG(EEG, event_names)
2 | % function used to create the event_table from the handle structure
3 |
4 | % pull out the events from the handles structure
5 | relevant_events = find(cellfun(@(x) any(strcmp(x, event_names)), {EEG.event.type}));
6 |
7 | % calculate the number of unique events
8 | no_events = length(relevant_events);
9 |
10 | % pre-allocate the event data
11 | event_data = cell(no_events, 3);
12 |
13 | % loop for each event type
14 | for n = 1 : no_events
15 |
16 | % event type into the event_data
17 | event_data(n, 1) = {EEG.event(relevant_events(n)).type};
18 |
19 | % event latency (converto seconds)
20 | event_data(n, 2) = {EEG.event(relevant_events(n)).latency / EEG.srate};
21 |
22 | % add the event type number in case labels are changed
23 | event_data(n, 3) = {find(strcmp(event_data(n, 1), event_names))};
24 |
25 | end
26 |
27 | % sort the events by latency
28 | [~, sort_ind] = sort([event_data{:, 2}]);
29 | EEG.csc_event_data = event_data(sort_ind, :);
--------------------------------------------------------------------------------
/dependencies/csc_labeled_locations.m:
--------------------------------------------------------------------------------
1 | % plot electrode locations with labels
2 |
3 | e_loc = csc_add2dlocations(EEG.chanlocs);
4 |
5 | hdl.fig = figure('color', 'w');
6 | hdl.ax = axes('nextplot', 'add');
7 |
8 | hdl.markers = plot(...
9 | [e_loc.y], [e_loc.x], ...
10 | 'lineStyle', 'none',...
11 | 'marker', 'o',...
12 | 'markerSize', 25,...
13 | 'markerEdgeColor', [0.2, 0.2, 0.2],...
14 | 'markerFaceColor', [0.9, 0.9, 0.9]);
15 |
16 | % add labels
17 | for n = 1 : length(e_loc)
18 |
19 | hdl.text(n) = text(e_loc(n).y, e_loc(n).x, ...
20 | char(e_loc(n).labels), ...
21 | 'visible', 'on', ...
22 | 'horizontalAlignment', 'center' ,...
23 | 'verticalAlignment', 'middle' ,...
24 | 'color', 'k' ,...
25 | 'fontSize', 8 );
26 |
27 | end
--------------------------------------------------------------------------------
/dependencies/csc_progress_indicator.m:
--------------------------------------------------------------------------------
1 | function csc_progress_indicator(type, current, maximum)
2 | % command line progress bar that erases past progress
3 |
4 | % usage:
5 | % swa_progress_indicator('initialise', 'percentage complete')
6 | %
7 | % for n = 1:30
8 | %
9 | % WaitSecs(0.1);
10 | %
11 | % swa_progress_indicator('update', n, 30);
12 | %
13 | % end
14 |
15 |
16 | switch type
17 |
18 | case {'initialise', 'initialize'}
19 |
20 | fprintf(1, ['\n', current, ': 00.00 %%']);
21 |
22 | case 'update'
23 |
24 | percentage = current / maximum * 100;
25 |
26 | if ~(percentage > 10)
27 |
28 | fprintf(1, '\b\b\b\b\b\b\b\b 0%.2f %%', percentage);
29 |
30 | else
31 |
32 | fprintf(1, '\b\b\b\b\b\b\b\b %.2f %%', percentage);
33 |
34 | end
35 |
36 | if current == maximum
37 |
38 | fprintf(1, '\n');
39 |
40 | end
41 |
42 | end
--------------------------------------------------------------------------------
/dependencies/csc_subplot.m:
--------------------------------------------------------------------------------
1 | function handles = csc_subplot(n_rows, n_columns, axes_space)
2 |
3 | if nargin < 3
4 | axes_space = 0.05;
5 | end
6 |
7 | % open a figure
8 | handles.fig = figure('color', 'w', 'units', 'norm');
9 |
10 | % determine axes spacing (normalised units)
11 | axes_space = 0.05;
12 | axes_width = 1 / n_columns - axes_space * 1.5;
13 | axes_height = 1 / n_rows - axes_space * 1.5;
14 |
15 | % determine axes positions
16 | axes_x_s = linspace(axes_width + axes_space, 1 - axes_space, n_columns) - axes_width;
17 | axes_y_s = linspace(axes_height + axes_space, 1 - axes_space, n_rows) - axes_height;
18 |
19 | % create all axes
20 | for h = 1 : n_columns
21 | for v = 1 : n_rows
22 |
23 | handles.axes(v, h) = axes(...
24 | 'position', [...
25 | axes_x_s(h), axes_y_s(n_rows - v + 1), ...
26 | axes_width, axes_height], ...
27 | 'nextplot', 'add');
28 | end
29 | end
--------------------------------------------------------------------------------
/io/csc_convert_from_raw.m:
--------------------------------------------------------------------------------
1 | function EEG = csc_convert_from_raw(files)
2 | % function to import and concatenate raw files into a single eeglab file
3 | % input should be a list of file names in a cell array or left empty
4 |
5 | % check number of input arguments and have user select files if empty
6 | if nargin < 1
7 | [files, file_path] = uigetfile('*.raw', 'multiselect', 'on');
8 | % change the current directory to the files
9 | cd(file_path);
10 | end
11 |
12 | % initialise the EEG structure
13 | EEG = [];
14 | Eventdata = [];
15 | EEG = eeg_emptyset;
16 |
17 | % loop through each file and downsample
18 | for n = 1:length(files)
19 | % check for existence
20 | if exist(files{n}, 'file')
21 | disp(['Importing ' files{n}]);
22 | [Head, tmpdata, tmpevent] = readegi( files{n} );
23 |
24 | % concatenate the events
25 | Eventdata = [ Eventdata tmpevent ];
26 |
27 | % concatenate the data
28 | EEG.data = [ EEG.data tmpdata ];
29 |
30 | else
31 | % if the file isn't found break the loop
32 | fprintf(1, '%s not found, continuing \n', files{n} );
33 | break;
34 | end
35 | end;
36 |
37 | if ~exist('Head', 'var')
38 | fprintf(1, 'Warning: No files found, returning empty EEG \n');
39 | return;
40 | end
41 |
42 | % add EEG information
43 | EEG.comments = [ 'Original files: ' files{1} ' to ' files{n}];
44 | EEG.filepath = '';
45 | EEG.setname = 'EGI file';
46 | EEG.nbchan = size(EEG.data,1);
47 | EEG.srate = Head.samp_rate;
48 | EEG.trials = Head.segments;
49 | EEG.pnts = Head.segsamps;
50 | EEG.xmin = 0;
51 | EEG.times = [1:EEG.pnts]/EEG.srate;
52 |
53 | % add one channel with the event data
54 | % -----------------------------------
55 | if ~isempty(Eventdata) && size(Eventdata,2) == size(EEG.data,2)
56 | EEG.data(end+1:end+size(Eventdata,1),:) = Eventdata;
57 | end;
58 |
59 | % importing the events
60 | % --------------------
61 | if ~isempty(Eventdata)
62 | orinbchans = EEG.nbchan;
63 | for index = size(Eventdata,1):-1:1
64 | EEG = pop_chanevent( EEG, orinbchans-size(Eventdata,1)+index, 'edge', 'leading', ...
65 | 'delevent', 'off', 'typename', Head.eventcode(index,:), ...
66 | 'nbtype', 1, 'delchan', 'on');
67 | end;
68 | end;
69 |
70 | EEG = eeg_checkset(EEG);
71 |
72 |
--------------------------------------------------------------------------------
/io/csc_load_eeglab.m:
--------------------------------------------------------------------------------
1 | function EEG = csc_load_eeglab(filename)
2 | % easier loading of .set files without all the checking and bloat of EEGLAB
3 |
4 | % load the set file
5 | load('-mat', filename);
6 |
7 | if flag_load
8 | % load the data
9 | % ^^^^^^^^^^^^^
10 | % open the file
11 | fid = fopen( EEG.datfile , 'r', 'ieee-le'); % little endian (see also pop_saveset)
12 |
13 | % read from the file (attempt to read as "single" directly)
14 | EEG.data = fread(fid, [EEG.nbchan EEG.pnts], '*single');
15 |
16 | % close the file
17 | fclose(fid);
18 |
19 | else
20 | % memory map the data
21 | tmp = memmapfile(EEG.data,...
22 | 'Format', {'single', [EEG.nbchan EEG.pnts * EEG.trials], 'eegData'});
23 | EEG.data = tmp.Data.eegData;
24 |
25 | end
26 |
27 |
28 | % eeglab saves the data using:
29 | % fid = fopen(fname,'wb',fform);
30 | % fwrite(fid,A,'float');
--------------------------------------------------------------------------------
/io/mff_scripts_internal/convert_mffevents_to_eeglab_eventsNS5.m:
--------------------------------------------------------------------------------
1 | function eventstruct = convert_mffevents_to_eeglab_eventsNS5(mffevents,properties,sampling_rate)
2 | eventstruct = struct('type',{},'latency',{},'duration',{},'code',{});
3 |
4 | mffevent_fields = fieldnames(mffevents);
5 | for mefi = 1:length(mffevent_fields)
6 | % latency in samples, duration in seconds
7 | fieldstruct = struct('type',mffevents.(mffevent_fields{mefi}).label,...
8 | 'latency',num2cell(round(seconds(datetime(mffevents.(mffevent_fields{mefi}).recording_datenum,'ConvertFrom','datenum')' - ...
9 | properties.recording_start_datetime(1))).*sampling_rate),...
10 | 'duration',num2cell(round(mffevents.(mffevent_fields{mefi}).duration./1e3,3)),...
11 | 'code',mffevents.(mffevent_fields{mefi}).code);
12 |
13 | eventstruct = [eventstruct fieldstruct]; %#ok
14 | end
15 | % replace empty labels with their code (just in case)
16 | emptytype_index = find(cellfun(@isempty,{eventstruct.type}));
17 | % must be a better way
18 | for ei = emptytype_index
19 | eventstruct(ei).type = eventstruct(ei).code;
20 | end
21 | eventstruct = rmfield(eventstruct,'code');
22 | end
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_convert_to_EEGLAB.m:
--------------------------------------------------------------------------------
1 | % function to get EGI mff data and export to EEGLAB .set % .fdt
2 |
3 | function EEG = mff_convert_to_EEGLAB(fileName, save_name, recording_system)
4 |
5 | FLAG_TO_STRUCT = true;
6 | FLAG_DOWNSAMPLE = false;
7 |
8 | % if the filename is not specified open a user dialog
9 | if nargin < 1
10 | fileName = uigetdir('*.mff');
11 | end
12 | if isempty(fileName)
13 | fileName = uigetdir('*.mff');
14 | end
15 |
16 | if nargin < 2
17 | save_name = fileName(1:end-4);
18 | end
19 | if isempty(save_name)
20 | save_name = fileName(1:end-4);
21 | end
22 |
23 | if nargin < 3
24 | recording_system = 1;
25 | end
26 |
27 | % check if file name already exists
28 | while exist([save_name ,'.set'], 'file')
29 | save_name = [save_name, '_1'];
30 | end
31 |
32 | % get the mff meta data for all the data information
33 | mffData = mff_import_meta_data(fileName);
34 |
35 | % EEG structure
36 | % `````````````
37 | % initialise EEG structure
38 | EEG = eeg_emptyset;
39 |
40 | % get meta info
41 | EEG.comments = [ 'Original file: ' fileName ];
42 | EEG.setname = 'mff file';
43 |
44 | EEG.nbchan = mffData.signal_binaries(recording_system).num_channels - 1;
45 | EEG.srate = mffData.signal_binaries(recording_system).channels.sampling_rate(1);
46 | EEG.trials = length(mffData.epochs);
47 | EEG.pnts = mffData.signal_binaries(recording_system).channels.num_samples(1);
48 | EEG.xmin = 0;
49 |
50 | tmp = mff_convert_to_chanlocs(fileName);
51 | EEG.chanlocs = tmp.chanlocs;
52 | EEG.chanlocs(257:end)=[];
53 |
54 | % either write directly to file or to struct
55 | if FLAG_TO_STRUCT
56 |
57 | % open a progress bar
58 | waitHandle = waitbar(0,'Please wait...', 'Name', 'Importing Channels');
59 |
60 | if ~FLAG_DOWNSAMPLE
61 | % calculate total size and pre-allocate
62 | EEG.data = zeros(EEG.nbchan, mffData.signal_binaries(recording_system).channels.num_samples(1), 'single');
63 |
64 | % fill the EEG.data
65 | for current_channel = 1 : EEG.nbchan
66 |
67 | % update the waitbar
68 | waitbar(current_channel/EEG.nbchan, waitHandle, sprintf('Channel %d of %d', current_channel, EEG.nbchan))
69 |
70 | % get all the data
71 | temp_data = mff_import_signal_binary(mffData.signal_binaries(recording_system), current_channel, 'all');
72 | EEG.data(current_channel, :) = temp_data.samples;
73 |
74 | end
75 |
76 | else
77 |
78 | % get the first channel
79 | temp_data = mff_import_signal_binary(mffData.signal_binaries(recording_system), 1, 'all');
80 |
81 | % NOTE: decimate function takes care of filtering
82 | % % design filter (cheby 1 _ 10th order _ nyquist)
83 | % filter_design = designfilt('lowpassiir', 'FilterOrder', 10, ...
84 | % 'PassbandFrequency', temp_data.sampling_rate / 4, 'PassbandRipple', 1, 'SampleRate', temp_data.sampling_rate);
85 | %
86 | % % filter the channel
87 | % temp_data.samples = filtfilt(filter_design, double(temp_data.samples));
88 |
89 | % downsample the channel
90 | temp_data.samples = single(decimate(double(temp_data.samples), 2));
91 |
92 | % pre-allocate EEG to remaining size
93 | EEG.data = zeros(EEG.nbchan, length(temp_data.samples), 'single');
94 | EEG.data(1, :) = temp_data.samples;
95 |
96 | % loop for rest of channels
97 | for current_channel = 2 : EEG.nbchan
98 |
99 | % update the waitbar
100 | waitbar(current_channel/EEG.nbchan, waitHandle, sprintf('Channel %d of %d', current_channel, EEG.nbchan))
101 |
102 | % get all the data
103 | temp_data = mff_import_signal_binary(mffData.signal_binaries(recording_system), current_channel, 'all');
104 | temp_data.samples = single(decimate(double(temp_data.samples), 2));
105 | EEG.data(current_channel, :) = temp_data.samples;
106 |
107 | end
108 | end
109 |
110 | % delete the progress bar
111 | delete(waitHandle);
112 |
113 | % check the eeg for consistency
114 | EEG = eeg_checkset(EEG);
115 |
116 | % save the dataset
117 | EEG = pop_saveset(EEG, [save_name ,'.set']);
118 |
119 | else
120 |
121 | % get data from bin to fdt
122 | % ````````````````````````
123 | dataName = [save_name, '.fdt'];
124 | % link the struct to the data file
125 | EEG.data = dataName;
126 |
127 | % open a file in append mode
128 | fid = fopen(dataName, 'a+');
129 |
130 | % open a progress bar
131 | waitHandle = waitbar(0,'Please wait...', 'Name', 'Importing Blocks');
132 |
133 | % loop for each block individually and append to binary file
134 | nBlocks = mffData.signal_binaries(recording_system).num_blocks;
135 | for current_channel = 1 : nBlocks
136 |
137 | % update the waitbar
138 | waitbar(current_channel/nBlocks, waitHandle,sprintf('Block %d of %d', current_channel, nBlocks))
139 |
140 | % loop each channel to avoid memory problems (ie. double tmpData)
141 | tmpData = zeros(EEG.nbchan, mffData.signal_binaries(recording_system).blocks.num_samples(current_channel));
142 | for nCh = 1:EEG.nbchan
143 | chData = mff_import_signal_binary(mffData.signal_binaries(recording_system), nCh, current_channel);
144 | tmpData(nCh,:) = chData.samples;
145 | end
146 |
147 | % write the block of data to the fdt file
148 | fwrite(fid, tmpData, 'single', 'l');
149 |
150 | end
151 | % delete the progress bar
152 | delete(waitHandle);
153 |
154 | % close the file
155 | fclose(fid);
156 |
157 | save([save_name, '.set'], 'EEG', '-mat');
158 | end
159 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_convert_to_chanlocs.m:
--------------------------------------------------------------------------------
1 | function [EEG] = mff_convert_to_chanlocs(meta_file)
2 |
3 | coordinates = mff_import_coordinates(meta_file);
4 | sfp=transpose([coordinates.number;coordinates.x;coordinates.y;coordinates.z]);
5 |
6 | save('temp.sfp','sfp','-ascii','-tabs')
7 | EEG.chanlocs = readlocs('temp.sfp');
8 | delete('temp.sfp')
9 | end
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_determine_calibrations.m:
--------------------------------------------------------------------------------
1 | function [calibrated_gains, calibrated_zeros] = mff_determine_calibrations(default_gains, default_zeros, num_epochs, epochs, num_calibrations, calibrations)
2 | calibrated_gains = default_gains;
3 | calibrated_zeros = default_zeros;
4 |
5 | if num_calibrations > 0
6 | for calibration_num = 1:num_calibrations
7 | for epoch_num = 1:num_epochs
8 | if (calibrations.time_from(calibration_num) == epochs.time_from(epoch_num)) && ((calibrations.time_to(calibration_num) >= epochs.time_to(epoch_num)) || isnan(calibrations.time_to(calibration_num)))
9 | switch calibrations.type(calibration_num, :)
10 | case 'GCAL'
11 | for block_num = epochs.block_from(epoch_num):epochs.block_to(epoch_num)
12 | calibrated_gains(block_num, :) = single(calibrations.values(calibration_num, :));
13 | end
14 |
15 | case 'ZCAL'
16 | for block_num = epochs.block_from(epoch_num):epochs.block_to(epoch_num)
17 | calibrated_zeros(block_num, :) = single(calibrations.values(calibration_num, :));
18 | end
19 | end
20 |
21 | break;
22 | end
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_determine_epoch_breaks.m:
--------------------------------------------------------------------------------
1 | function [num_epoch_breaks, epoch_breaks] = mff_determine_epoch_breaks(num_epochs, epochs)
2 | num_epoch_breaks = 0;
3 | epoch_breaks = struct([]);
4 |
5 | if num_epochs > 1
6 | num_epoch_breaks = num_epochs - 1;
7 | epoch_breaks(1).onset(1) = epochs.time_to(1);
8 | epoch_breaks(1).duration(1) = epochs.time_from(2) - epochs.time_to(1);
9 |
10 | for epoch_num = 2:num_epochs - 1
11 | epoch_breaks.onset(epoch_num) = epochs.time_to(epoch_num) - epoch_breaks.duration(epoch_num - 1);
12 | epoch_breaks.duration(epoch_num) = epoch_breaks.duration(epoch_num - 1) + epochs.time_from(epoch_num + 1) - epochs.time_to(epoch_num);
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_export_event_track.m:
--------------------------------------------------------------------------------
1 | function mff_export_event_track(meta_file, event_track_name, events)
2 | absolute_timestamp = mff_find_absolute_timestamp(meta_file);
3 |
4 | event_track_file = ['Events_' strrep(event_track_name, ' ', '_') '.xml'];
5 | event_track_file_id = fopen([meta_file filesep event_track_file], 'w');
6 |
7 | fprintf(event_track_file_id, '\n');
8 | fprintf(event_track_file_id, '\n');
9 | fprintf(event_track_file_id, '\t%s\n', event_track_name);
10 | fprintf(event_track_file_id, '\tEVNT\n');
11 |
12 | num_events = length(events.code);
13 |
14 | for event_num = 1:num_events
15 | onset = round(1000 * events.onset(event_num));
16 | duration = round(1000000 * events.duration(event_num));
17 |
18 | fprintf(event_track_file_id, '\t\n');
19 | fprintf(event_track_file_id, '\t\t%s%s%s\n', datestr(addtodate(absolute_timestamp.datenum, onset, 'millisecond'), 'yyyy-mm-ddTHH:MM:SS.FFF'), absolute_timestamp.offset, absolute_timestamp.timezone);
20 | fprintf(event_track_file_id, '\t\t%lu000\n', duration);
21 | fprintf(event_track_file_id, '\t\t%s
\n', events.code{event_num});
22 | fprintf(event_track_file_id, '\t\t\n', events.label{event_num});
23 | fprintf(event_track_file_id, '\t\t%s\n', events.description{event_num});
24 | fprintf(event_track_file_id, '\t\t%s\n', events.source{event_num});
25 |
26 | num_keys = length(events.keys(event_num).code);
27 |
28 | if num_keys > 0
29 | fprintf(event_track_file_id, '\t\t\n');
30 |
31 | for key_num = 1:num_keys
32 | fprintf(event_track_file_id, '\t\t\t\n');
33 | fprintf(event_track_file_id, '\t\t\t\t%s\n', events.keys(event_num).code{key_num});
34 | fprintf(event_track_file_id, '\t\t\t\t%s\n', events.keys(event_num).data_type{key_num}, events.keys(event_num).data{key_num});
35 | fprintf(event_track_file_id, '\t\t\t\n');
36 | end
37 |
38 | fprintf(event_track_file_id, '\t\t\n');
39 | end
40 |
41 | fprintf(event_track_file_id, '\t\n');
42 | end
43 |
44 | fprintf(event_track_file_id, '\n');
45 |
46 | fclose(event_track_file_id);
47 | end
48 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_export_event_track_from_meta_data.m:
--------------------------------------------------------------------------------
1 | function mff_export_event_track_from_meta_data(meta_data, event_track_name, events)
2 | % events is a structure that has the event info
3 | event_track_file = [meta_data.meta_file filesep sprintf('Events_%s.xml', strrep(event_track_name, ' ', '_'))];
4 |
5 | id = fopen(event_track_file, 'w');
6 |
7 | fprintf(id, '\n');
8 | fprintf(id, '\n');
9 | fprintf(id, '\t%s\n', event_track_name);
10 | fprintf(id, '\tEVNT\n');
11 |
12 | num_events = length(events.code);
13 |
14 | for event_num = 1:num_events
15 | onset = round(1000 * events.onset(event_num));
16 | duration = round(1000000000 * events.duration(event_num));
17 |
18 | fprintf(id, '\t\n');
19 | fprintf(id, '\t\t%s%s%s\n', datestr(addtodate(meta_data.info.recording_datenum, onset, 'millisecond'), 'yyyy-mm-ddTHH:MM:SS.FFF'), meta_data.info.recording_timestamp_submilliseconds, meta_data.info.recording_timestamp_timezone);
20 | fprintf(id, '\t\t%lu\n', duration);
21 | fprintf(id, '\t\t%s
\n', events.code{event_num});
22 | fprintf(id, '\t\t\n', events.label{event_num});
23 | fprintf(id, '\t\t%s\n', events.description{event_num});
24 | fprintf(id, '\t\t%s\n', events.source{event_num});
25 |
26 | num_keys = length(events.keys(event_num).code);
27 |
28 | if num_keys > 0
29 | fprintf(id, '\t\t\n');
30 |
31 | for key_num = 1:num_keys
32 | fprintf(id, '\t\t\t\n');
33 | fprintf(id, '\t\t\t\t%s\n', events.keys(event_num).code{key_num});
34 | fprintf(id, '\t\t\t\t%s\n', events.keys(event_num).data_type{key_num}, events.keys(event_num).data{key_num});
35 | fprintf(id, '\t\t\t\n');
36 | end
37 |
38 | fprintf(id, '\t\t\n');
39 | end
40 |
41 | fprintf(id, '\t\n');
42 | end
43 |
44 | fprintf(id, '\n');
45 |
46 | fclose(id);
47 | end
48 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_find_absolute_timestamp.m:
--------------------------------------------------------------------------------
1 | function absolute_timestamp = mff_find_absolute_timestamp(meta_file)
2 | info_file = [meta_file filesep 'info.xml'];
3 | info_file_id = fopen(info_file, 'r');
4 |
5 | absolute_timestamp = '';
6 |
7 | while ~feof(info_file_id)
8 | line = fgetl(info_file_id);
9 |
10 | if ischar(line)
11 | [absolute_timestamp] = regexp(line, '(?[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3})(?[0-9]{6})(?[-+][0-9]{2}:[0-9]{2})', 'names');
12 |
13 | if ~isempty(absolute_timestamp)
14 | absolute_timestamp.datevec = datevec(absolute_timestamp.timestamp, 'yyyy-mm-ddTHH:MM:SS.FFF');
15 | absolute_timestamp.datenum = datenum(absolute_timestamp.datevec);
16 |
17 | break
18 | end
19 | end
20 | end
21 |
22 | fclose(info_file_id);
23 | end
24 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_import_coordinates.m:
--------------------------------------------------------------------------------
1 | %%written by Brady Riedner 07/23/2012 adapted from scripts by unnamed
2 | %%source
3 | %%% University of wisconsin
4 | function coordinates = mff_import_coordinates(meta_file)
5 | num_sensors = 0;
6 | coordinates = struct([]);
7 | coordinates_file = [meta_file filesep 'coordinates.xml'];
8 |
9 | id = fopen(coordinates_file, 'r');
10 | section_name = {20}; %20 is abritrary
11 | section_index = 0;
12 |
13 | while ~feof(id)
14 | line = fgetl(id);
15 |
16 | [variables] = regexp(line, '<(?coordinates|sensorLayout|sensors)( .+)?>', 'names');
17 |
18 | if ~isempty(variables)
19 | section_index = section_index + 1;
20 | section_name{section_index} = variables.section;
21 |
22 | continue;
23 | end
24 |
25 | [variables] = regexp(line, '(?coordinates|sensorLayout|sensors)>', 'names');
26 |
27 | if ~isempty(variables)
28 | section_name{section_index} = '';
29 | section_index = section_index - 1;
30 |
31 | continue;
32 | end
33 |
34 | if section_index > 0
35 | switch section_name{section_index}
36 | case 'coordinates'
37 | [variables] = regexp(line, '(?.*)', 'names');
38 |
39 | if ~isempty(variables)
40 | coordinates(1).acqTime = variables.acqTime;
41 |
42 | continue;
43 | end
44 |
45 | [variables] = regexp(line, '(?.*)', 'names');
46 |
47 | if ~isempty(variables)
48 | coordinates(1).acqMethod = variables.acqMethod;
49 |
50 | continue;
51 | end
52 |
53 | [variables] = regexp(line, '(?.*)', 'names');
54 |
55 | if ~isempty(variables)
56 | coordinates(1).defaultSubject = variables.defaultSubject;
57 |
58 | continue;
59 | end
60 |
61 | case 'sensorLayout'
62 | [variables] = regexp(line, '(?.*)', 'names');
63 |
64 | if ~isempty(variables)
65 | coordinates(1).sensorLayout = variables.name;
66 |
67 | continue;
68 | end
69 |
70 |
71 | case 'sensors'
72 | [variables] = regexp(line, '', 'names');
73 |
74 | if ~isempty(variables)
75 | num_sensors = num_sensors + 1;
76 |
77 | coordinates(1).name{num_sensors} = [];
78 | coordinates(1).number(num_sensors) = NaN;
79 | coordinates(1).type(num_sensors) = NaN;
80 | coordinates(1).x(num_sensors) = NaN;
81 | coordinates(1).y(num_sensors) = NaN;
82 | coordinates(1).z(num_sensors) = NaN;
83 | continue;
84 | end
85 |
86 | [variables] = regexp(line, '(?.*)', 'names');
87 |
88 | if ~isempty(variables)
89 | coordinates(1).name{num_sensors} = variables.name;
90 |
91 | continue;
92 | end
93 |
94 | [variables] = regexp(line, '(?[0-9]+)', 'names');
95 |
96 | if ~isempty(variables)
97 | coordinates(1).number(num_sensors) = str2double(variables.number);
98 |
99 | continue;
100 | end
101 |
102 | [variables] = regexp(line, '(?[0-9]+)', 'names');
103 |
104 | if ~isempty(variables)
105 | coordinates(1).type(num_sensors) = str2double(variables.type);
106 |
107 | continue;
108 | end
109 |
110 | [variables] = regexp(line, '(?.+)', 'names');
111 |
112 | if ~isempty(variables)
113 | coordinates(1).x(num_sensors) = str2double(variables.x);
114 |
115 | continue;
116 | end
117 |
118 | [variables] = regexp(line, '(?.+)', 'names');
119 |
120 | if ~isempty(variables)
121 | coordinates(1).y(num_sensors) = str2double(variables.y);
122 |
123 | continue;
124 | end
125 |
126 |
127 | [variables] = regexp(line, '(?.+)', 'names');
128 |
129 | if ~isempty(variables)
130 | coordinates(1).z(num_sensors) = str2double(variables.z);
131 |
132 | continue;
133 | end
134 |
135 | end
136 | end
137 | end
138 | fclose(id);
139 | end
140 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_import_epochs.m:
--------------------------------------------------------------------------------
1 | function [num_epochs, epochs] = mff_import_epochs(file)
2 | num_epochs = 0;
3 | epochs = struct([]);
4 |
5 | id = fopen(file, 'r');
6 | section = '';
7 |
8 | while ~feof(id)
9 | line = fgetl(id);
10 |
11 | [variables] = regexp(line, '<(?epochs)( .+)?>', 'names');
12 |
13 | if ~isempty(variables)
14 | section = variables.section;
15 |
16 | continue;
17 | end
18 |
19 | [variables] = regexp(line, '(?epochs)>', 'names');
20 |
21 | if ~isempty(variables)
22 | section = '';
23 |
24 | continue;
25 | end
26 |
27 | switch section
28 | case 'epochs'
29 | [variables] = regexp(line, '', 'names');
30 |
31 | if ~isempty(variables)
32 | num_epochs = num_epochs + 1;
33 |
34 | epochs(1).time_from(num_epochs) = NaN;
35 | epochs(1).time_to(num_epochs) = NaN;
36 | epochs(1).block_from(num_epochs) = NaN;
37 | epochs(1).block_to(num_epochs) = NaN;
38 |
39 | continue;
40 | end
41 |
42 | [variables] = regexp(line, '(?[0-9]+)', 'names');
43 |
44 | if ~isempty(variables)
45 | epochs(1).time_from(num_epochs) = str2double(variables.time_from) / 1000000000;
46 |
47 | continue;
48 | end
49 |
50 | [variables] = regexp(line, '(?[0-9]+)', 'names');
51 |
52 | if ~isempty(variables)
53 | epochs(1).time_to(num_epochs) = str2double(variables.time_to) / 1000000000;
54 |
55 | continue;
56 | end
57 |
58 | [variables] = regexp(line, '(?[0-9]+)', 'names');
59 |
60 | if ~isempty(variables)
61 | epochs(1).block_from(num_epochs) = str2double(variables.block_from);
62 |
63 | continue;
64 | end
65 |
66 | [variables] = regexp(line, '(?[0-9]+)', 'names');
67 |
68 | if ~isempty(variables)
69 | epochs(1).block_to(num_epochs) = str2double(variables.block_to);
70 |
71 | continue;
72 | end
73 | end
74 | end
75 |
76 | fclose(id);
77 | end
78 |
--------------------------------------------------------------------------------
/io/mff_scripts_internal/mff_import_events.m:
--------------------------------------------------------------------------------
1 | %%written by Brady Riedner 07/23/2012 adapted from scripts by some one
2 | %%better at code writing
3 | %%% University of wisconsin
4 | function events = mff_import_events(meta_file)
5 | eventtracks = dir(fullfile(meta_file, 'Events_*'));
6 | events = struct([]);
7 |
8 | for t = 1:length(eventtracks)
9 | num_events = 0;
10 | events(1).(genvarname(strtok(eventtracks(t).name,'.'))) = [];
11 | event_file = [meta_file filesep eventtracks(t).name];
12 |
13 | id = fopen(event_file, 'r');
14 | section_name = {20}; %20 is abritrary
15 | section_index = 0;
16 |
17 | while ~feof(id)
18 | line = fgetl(id);
19 | % checks if it is a section start
20 | [variables] = regexp(line, '<(?eventTrack|event|keys)( .+)?>', 'names');
21 |
22 | if ~isempty(variables)
23 | section_index = section_index + 1;
24 | section_name{section_index} = variables.section;
25 |
26 | continue;
27 | end
28 | % checks if it is a section end
29 | [variables] = regexp(line, '(?eventTrack|event|keys)>', 'names');
30 |
31 | if ~isempty(variables)
32 | section_name{section_index} = '';
33 | section_index = section_index - 1;
34 |
35 | continue;
36 | end
37 |
38 | if section_index > 0
39 | switch section_name{section_index}
40 | case 'eventTrack'
41 | [variables] = regexp(line, '(?.*)', 'names');
42 |
43 | if ~isempty(variables)
44 | events(1).(genvarname(strtok(eventtracks(t).name,'.'))).TrackName = variables.name;
45 |
46 | continue;
47 | end
48 |
49 | [variables] = regexp(line, '(?.*)', 'names');
50 |
51 | if ~isempty(variables)
52 | events(1).(genvarname(strtok(eventtracks(t).name,'.'))).trackType = variables.trackType;
53 |
54 | continue;
55 | end
56 |
57 |
58 |
59 |
60 | case 'event'
61 | num_keys = 0;
62 |
63 | [variables] = regexp(line, '(?[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3})(?[0-9]{6})(?[-+][0-9]{2}:[0-9]{2})', 'names');
64 |
65 | if ~isempty(variables)
66 | num_events = num_events + 1;
67 |
68 | events(1).recording_timestamp = variables.timestamp;
69 | events(1).recording_timestamp_submilliseconds = variables.timestamp_submilliseconds;
70 | events(1).recording_timestamp_timezone = variables.timestamp_timezone;
71 | try
72 | events(num_events).recording_datevec = datevec(variables.timestamp, 'yyyy-mm-ddTHH:MM:SS.FFF');
73 | catch
74 | events(num_events).recording_datevec = datevec(variables.timestamp, 'yyyy-mm-ddTHH:MM:SS.FFF');
75 | end
76 | try
77 | events(num_events).recording_datenum = datenum(variables.timestamp, 'yyyy-mm-ddTHH:MM:SS.FFF');
78 | catch
79 | events(num_events).recording_datenum = datenum(variables.timestamp, 'yyyy-mm-ddTHH:MM:SS.FFF');
80 | end
81 | continue;
82 | end
83 |
84 |
85 | [variables] = regexp(line, '(?[0-9]+)', 'names');
86 |
87 | if ~isempty(variables)
88 | events(1).(genvarname(strtok(eventtracks(t).name,'.')))(t).duration(num_events) = str2double(variables.duration);
89 |
90 | continue;
91 | end
92 |
93 | [variables] = regexp(line, '(?.*)
', 'names');
94 |
95 | if ~isempty(variables)
96 | events(1).(genvarname(strtok(eventtracks(t).name,'.')))(t).code{num_events} = variables.code;
97 |
98 | continue;
99 | end
100 |
101 | [variables] = regexp(line, '