├── .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, '