Skip to content

Commit

Permalink
fix formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanharvey1 committed Nov 3, 2024
1 parent 3d2eb14 commit 421feb1
Showing 1 changed file with 110 additions and 111 deletions.
221 changes: 110 additions & 111 deletions brainStates/SleepScoreMaster/SleepScoreMaster.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
function SleepState = SleepScoreMaster(basepath,varargin)
function SleepState = SleepScoreMaster(basepath, varargin)
%SleepScoreMaster(basePath,<options>)
%This is the master function for sleep state scoring.
%
%It's strongly recommended that you
%It's strongly recommended that you
% 1) indicate bad (noisy) channels using bz_getSessionInfo(basePath,'editGUI',true)
% before running SleepScoreMaster.
% 2) use the 'ignoretime' input to exclude time windows with opto
Expand All @@ -12,50 +12,50 @@
% needed, and implement sticky thresholds.
%
%
%INPUT
%INPUT
% basePath folder containing .xml and .lfp files.
% basePath and files should be of the form:
% 'whateverfolder/recordingName/recordingName'
% (optional) If no inputs included, select folder(s) containing .lfp
% and .xml file in prompt.
% (optional) if no .lfp in basePath, option to select multiple
% (optional) if no .lfp in basePath, option to select multiple
% lfp-containing subfolders
%
%
% OPTIONS
% 'savedir' Default: basePath
% 'overwrite' Overwrite all processing steps (Default: false)
% 'ignoreManual' Default: false. Overwrite manual scoring from TheStateEditor
% 'savebool' Default: true. Save anything.
% 'scoretime' Window of time to score. Default: [0 Inf]
% 'scoretime' Window of time to score. Default: [0 Inf]
% NOTE: must be continous interval
% 'ignoretime' Time intervals winthin scoretime to ignore
% (for example, opto stimulation or behavior with artifacts)
% 'ignoretime' Time intervals winthin scoretime to ignore
% (for example, opto stimulation or behavior with artifacts)
% 'winparms' [FFT window , smooth window] (Default: [2 15])
% (Note: updated from [10 10] based on bimodaility optimization, 6/17/19)
% 'SWWeightsName' Name of file in path (in Dependencies folder)
% 'SWWeightsName' Name of file in path (in Dependencies folder)
% containing the weights for the various frequencies to
% be used for SWS detection.
% be used for SWS detection.
% Default is to use Power Spectrum Slope ('PSS'),
% If this doesn't work, try 'SWweights.mat'
% If this doesn't work, try 'SWweights.mat'
% or 'SWweightsHPC.mat' for HPC recording.
% 'Notch60Hz' Boolean 0 or 1. Value of 1 will notch out the 57.5-62.5 Hz
% band, default is 0, no notch. This can be necessary if
% electrical noise.
% 'NotchUnder3Hz' Boolean 0 or 1. Value of 1 will notch out the 0-3 Hz
% band, default is 0, no notch. This can be necessary
% due to poor grounding and low freq movement transients
% 'NotchHVS' Boolean 0 or 1. Value of 1 will notch the 4-10 and
% 12-18 Hz bands for SW detection, default is 0, no
% 'NotchHVS' Boolean 0 or 1. Value of 1 will notch the 4-10 and
% 12-18 Hz bands for SW detection, default is 0, no
% notch. This can be useful in
% recordings with prominent high voltage spindles which
% have prominent ~16hz harmonics
% 'NotchTheta' Boolean 0 or 1. Value of 1 will notch the 4-10 Hz
% band for SW detection, default is 0, no notch. This
% band for SW detection, default is 0, no notch. This
% can be useful to
% transform the cortical spectrum to approximately
% hippocampal, may also be necessary with High Voltage
% Spindles
% 'stickytrigger' Implements a "sticky" trigger for SW/EMG threshold
% 'stickytrigger' Implements a "sticky" trigger for SW/EMG threshold
% crossings: metrics must reach halfway between threshold
% and opposite peak to count as crossing (reduces
% flickering) (default:true)
Expand All @@ -67,19 +67,20 @@
% 'saveLFP' (default:true) to save SleepScoreLFP.lfp.mat file
% 'noPrompts' (default:false) an option to not prompt user of things
%
%OUTPUT
%OUTPUT
% !THIS IS OUT OF DATE - UPDATE!
% StateIntervals structure containing start/end times (seconds) of
% NREM, REM, WAKE states and episodes. states is the
% "raw" state scoring. episodes are joined episodes of
% extended (40s) time in a given states, allowing for
% brief interruptions. also contains NREM packets,
% NREM, REM, WAKE states and episodes. states is the
% "raw" state scoring. episodes are joined episodes of
% extended (40s) time in a given states, allowing for
% brief interruptions. also contains NREM packets,
% unitary epochs of NREM as described in Watson et al 2016.
% saved in a .mat file:
% recordingname_SleepScore.mat
%
% recordingname_SleepScore.mat
%
%
% DLevenstein and BWatson 2015/16

%% Recording Selection
%if recname is 'select' or something
%use uigetfile to pick and get list of filenames
Expand All @@ -88,22 +89,23 @@
%if no input arguements... select uigetfile

%Select from no input
if ~exist('basepath','var')
basepath = uigetdir(cd,...
if ~exist('basepath', 'var')
basepath = uigetdir(cd, ...
'Which recording(s) would you like to state score?');
if isequal(basepath,0);return;end
if isequal(basepath, 0)
return;
end
end

%Separate datasetfolder and recordingname
[datasetfolder,~,~] = fileparts(basepath);
[datasetfolder, ~, ~] = fileparts(basepath);
recordingname = basenameFromBasepath(basepath); % fileparts parses '.' into extension


%% If there is no .lfp in basePath, choose (multiple?) folders within basePath.
%Select from dataset folder - need to check if .xml/lfp exist
if ~exist(fullfile(basepath,[recordingname,'.lfp']),'file') && ...
~exist(fullfile(basepath,[recordingname,'.eeg']),'file')
error(['no ' fullfile(basepath,[recordingname,'.lfp']) 'file found.']);
if ~exist(fullfile(basepath, [recordingname, '.lfp']), 'file') && ...
~exist(fullfile(basepath, [recordingname, '.eeg']), 'file')
error(['no ', fullfile(basepath, [recordingname, '.lfp']), 'file found.']);
end

%If multiple recordings, loop calling SleepScoreMaster with each
Expand All @@ -118,36 +120,36 @@
% end
% return
elseif numrecs == 1 && iscell(recordingname)
recordingname = recordingname{1};
recordingname = recordingname{1};
end

display(['Scoring Recording: ',recordingname]);
display(['Scoring Recording: ', recordingname]);

%% inputParse for Optional Inputs and Defaults
p = inputParser;

addParameter(p,'overwrite',false)
addParameter(p,'savebool',true,@islogical)
addParameter(p,'savedir',datasetfolder)
addParameter(p,'scoretime',[0 Inf])
addParameter(p,'ignoretime',[])
addParameter(p,'SWWeightsName','PSS')
addParameter(p,'Notch60Hz',0)
addParameter(p,'NotchUnder3Hz',0)
addParameter(p,'NotchHVS',0)
addParameter(p,'NotchTheta',0)
addParameter(p,'SWChannels',0)
addParameter(p,'ThetaChannels',0)
addParameter(p,'rejectChannels',[]);
addParameter(p,'noPrompts',true);
addParameter(p,'stickytrigger',false);
addParameter(p,'saveLFP',true);
addParameter(p,'winparms',[2 15]);
addParameter(p,'ignoreManual',false)

parse(p,varargin{:})
addParameter(p, 'overwrite', false)
addParameter(p, 'savebool', true, @islogical)
addParameter(p, 'savedir', datasetfolder)
addParameter(p, 'scoretime', [0, Inf])
addParameter(p, 'ignoretime', [])
addParameter(p, 'SWWeightsName', 'PSS')
addParameter(p, 'Notch60Hz', 0)
addParameter(p, 'NotchUnder3Hz', 0)
addParameter(p, 'NotchHVS', 0)
addParameter(p, 'NotchTheta', 0)
addParameter(p, 'SWChannels', 0)
addParameter(p, 'ThetaChannels', 0)
addParameter(p, 'rejectChannels', []);
addParameter(p, 'noPrompts', true);
addParameter(p, 'stickytrigger', false);
addParameter(p, 'saveLFP', true);
addParameter(p, 'winparms', [2, 15]);
addParameter(p, 'ignoreManual', false)

parse(p, varargin{:})
%Clean up this junk...
overwrite = p.Results.overwrite;
overwrite = p.Results.overwrite;
savedir = p.Results.savedir;
savebool = p.Results.savebool;
scoretime = p.Results.scoretime;
Expand All @@ -164,46 +166,46 @@
stickytrigger = p.Results.stickytrigger;
saveLFP = p.Results.saveLFP;
winparms = p.Results.winparms;
ignoreManual = p.Results.ignoreManual;
ignoreManual = p.Results.ignoreManual;

%% Database File Management
savefolder = fullfile(savedir,recordingname);
if ~exist(savefolder,'dir')
%% Database File Management
savefolder = fullfile(savedir, recordingname);
if ~exist(savefolder, 'dir')
mkdir(savefolder)
end

%Buzcode outputs
bz_sleepstatepath = fullfile(savefolder,[recordingname,'.SleepState.states.mat']);
bz_sleepstatepath = fullfile(savefolder, [recordingname, '.SleepState.states.mat']);

%% Check for existing Manual Scoring
if exist(bz_sleepstatepath,'file') && ~overwrite && ~ignoreManual
SleepState_old = load(bz_sleepstatepath);
if isfield(SleepState_old.SleepState.detectorinfo,'LastManualUpdate')
disp(['Manual scoring detected... will update the SleepScoreMetrics and AutoScoreInts, ',...
'but keep previous (manual) scoring.'])
disp(['To overwrite manual scoring use ''ignoreManual'',true ',...
'or ''overwrite'',true to overwrite all metrics.'])
ManScore.ints = SleepState_old.SleepState.ints;
ManScore.idx = SleepState_old.SleepState.idx;
ManScore.LastManualUpdate = SleepState_old.SleepState.detectorinfo.LastManualUpdate;
end
if exist(bz_sleepstatepath, 'file') && ~overwrite && ~ignoreManual
SleepState_old = load(bz_sleepstatepath);
if isfield(SleepState_old.SleepState.detectorinfo, 'LastManualUpdate')
disp(['Manual scoring detected... will update the SleepScoreMetrics and AutoScoreInts, ', ...
'but keep previous (manual) scoring.'])
disp(['To overwrite manual scoring use ''ignoreManual'',true ', ...
'or ''overwrite'',true to overwrite all metrics.'])
ManScore.ints = SleepState_old.SleepState.ints;
ManScore.idx = SleepState_old.SleepState.idx;
ManScore.LastManualUpdate = SleepState_old.SleepState.detectorinfo.LastManualUpdate;
end
end

%% Get channels not to use
chInfo = hackInfo('basepath',basepath);
channels = setdiff(chInfo.one.channels,chInfo.one.badChannels);
chInfo = hackInfo('basepath', basepath);
channels = setdiff(chInfo.one.channels, chInfo.one.badChannels);
badChannels = chInfo.one.badChannels;

% check that SW/Theta channels exist in rec..
if length(SWChannels) > 1
if sum(ismember(SWChannels,channels)) ~= length(SWChannels)
if length(SWChannels) > 1
if sum(ismember(SWChannels, channels)) ~= length(SWChannels)
error('some of the SW input channels dont exist in this recording...?')
end
end
end
if length(ThetaChannels) > 1
if sum(ismember(ThetaChannels,channels)) ~= length(ThetaChannels)
if length(ThetaChannels) > 1
if sum(ismember(ThetaChannels, channels)) ~= length(ThetaChannels)
error('some of the theta input channels dont exist in this recording...?')
end
end
end

%Is this still needed?/Depreciated?
Expand All @@ -212,47 +214,45 @@
% rejectChannels = [rejectChannels SessionMetadata.ExtracellEphys.BadChannels];
% elseif isfield(sessionInfo,'badchannels')

rejectChannels = [rejectChannels badChannels]; %get badchannels from the .xml
rejectChannels = [rejectChannels, badChannels]; %get badchannels from the .xml

if isempty(rejectChannels)
disp('No rejected channels - it''s recommended you identify noisy channels to ignore')
end

%% CALCULATE EMG FROM HIGH-FREQUENCY COHERENCE
% Load/Calculate EMG based on cross-shank correlations
% (high frequency correlation signal = high EMG).
% Load/Calculate EMG based on cross-shank correlations
% (high frequency correlation signal = high EMG).
% Schomburg et al., Neuron, 2014)
EMGFromLFP = getEMGFromLFP(basepath,'overwrite',overwrite,...
'rejectChannels',rejectChannels,'noPrompts',noPrompts,...
'saveMat',savebool,'chInfo',chInfo);
EMGFromLFP = getEMGFromLFP(basepath, 'overwrite', overwrite, ...
'rejectChannels', rejectChannels, 'noPrompts', noPrompts, ...
'saveMat', savebool, 'chInfo', chInfo);

%% DETERMINE BEST SLOW WAVE AND THETA CHANNELS
%Determine the best channels for Slow Wave and Theta separation.
%Described in Watson et al 2016, with modifications
SleepScoreLFP = PickSWTHChannel_km(basepath,...
scoretime,SWWeightsName,...
Notch60Hz,NotchUnder3Hz,NotchHVS,NotchTheta,...
channels,SWChannels,ThetaChannels,rejectChannels,...
overwrite,'ignoretime',ignoretime,...
'noPrompts',noPrompts,'saveFiles',saveLFP&savebool,...
'window',winparms(1),'smoothfact',winparms(2),'IRASA',true);
SleepScoreLFP = PickSWTHChannel_km(basepath, ...
scoretime, SWWeightsName, ...
Notch60Hz, NotchUnder3Hz, NotchHVS, NotchTheta, ...
channels, SWChannels, ThetaChannels, rejectChannels, ...
overwrite, 'ignoretime', ignoretime, ...
'noPrompts', noPrompts, 'saveFiles', saveLFP & savebool, ...
'window', winparms(1), 'smoothfact', winparms(2), 'IRASA', true);

%% CLUSTER STATES BASED ON SLOW WAVE, THETA, EMG

%Calculate the scoring metrics: broadbandLFP, theta, EMG
disp('Quantifying metrics for state scoring')
[SleepScoreMetrics,StatePlotMaterials] = ClusterStates_GetMetrics(...
basepath,SleepScoreLFP,EMGFromLFP,overwrite,...
'onSticky',stickytrigger,'ignoretime',ignoretime,...
'window',winparms(1),'smoothfact',winparms(2),...
'IRASA',true,'ThIRASA',true);
[SleepScoreMetrics, StatePlotMaterials] = ClusterStates_GetMetrics( ...
basepath, SleepScoreLFP, EMGFromLFP, overwrite, ...
'onSticky', stickytrigger, 'ignoretime', ignoretime, ...
'window', winparms(1), 'smoothfact', winparms(2), ...
'IRASA', true, 'ThIRASA', true);

%Use the calculated scoring metrics to divide time into states
disp('Clustering States Based on EMG, SW, and TH LFP channels')
[ints,idx,MinTimeWindowParms] = ClusterStates_DetermineStates(SleepScoreMetrics);

[ints, idx, MinTimeWindowParms] = ClusterStates_DetermineStates(SleepScoreMetrics);


%% RECORD PARAMETERS from scoring
detectionparms.userinputs = p.Results;
detectionparms.MinTimeWindowParms = MinTimeWindowParms;
Expand All @@ -265,24 +265,24 @@
SleepState.detectorinfo.detectorname = 'SleepScoreMaster';
SleepState.detectorinfo.detectionparms = detectionparms;
SleepState.detectorinfo.detectionparms.histsandthreshs_orig = detectionparms.SleepScoreMetrics.histsandthreshs;
SleepState.detectorinfo.detectiondate = datetime("now",'Format','yyyy-MM-dd');
SleepState.detectorinfo.detectiondate = datetime("now", 'Format', 'yyyy-MM-dd');
SleepState.detectorinfo.StatePlotMaterials = StatePlotMaterials;

%Put old manual scoring back in
if exist('ManScore','var')
if exist('ManScore', 'var')
SleepState.AutoScoreInts = SleepState.ints;
SleepState.ints = ManScore.ints;
SleepState.idx = ManScore.idx;
SleepState.detectorinfo.LastManualUpdate = ManScore.LastManualUpdate;
end

%Saving SleepStates
save(bz_sleepstatepath,'SleepState');
save(bz_sleepstatepath, 'SleepState');

%% MAKE THE STATE SCORE OUTPUT FIGURE
%ClusterStates_MakeFigure(stateintervals,stateIDX,figloc,SleepScoreMetrics,StatePlotMaterials);
try
ClusterStates_MakeFigure(SleepState,basepath,noPrompts);
ClusterStates_MakeFigure(SleepState, basepath, noPrompts);
disp('Figures Saved to StateScoreFigures')
catch
disp('Figure making error')
Expand All @@ -292,17 +292,17 @@

% Extract states, Episodes, properly organize params etc, prep for final saving
disp('Calculating/Saving Episodes')
StatesToEpisodes(SleepState,basepath);
StatesToEpisodes(SleepState, basepath);

disp(['Sleep Score ',recordingname,': Complete!']);
disp(['Sleep Score ', recordingname, ': Complete!']);

%% PROMPT USER TO MANUALLY CHECK DETECTION WITH THESTATEEDITOR
if ~noPrompts
str = input('Would you like to check detection with TheStateEditor? [Y/N] ','s');
str = input('Would you like to check detection with TheStateEditor? [Y/N] ', 's');
switch str
case {'Y','y',''}
TheStateEditor([basepath,filesep,recordingname])
case {'N','n'}
case {'Y', 'y', ''}
TheStateEditor([basepath, filesep, recordingname])
case {'N', 'n'}
otherwise
disp('Unknown input..... you''ll have to load TheStateEditor on your own')
end
Expand All @@ -311,4 +311,3 @@
%% DETECT THETA AND NONTHETA EPOCHS
[SleepState] = thetaEpochs(basepath);
end

0 comments on commit 421feb1

Please sign in to comment.