From 8c5c616c3ccdde7bb7a23b5ab058a96dbfa4231b Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Thu, 24 Sep 2026 11:18:52 -0500 Subject: [PATCH 01/17] Standardize repo layout: AGENTS.md, settings, LF line endings, CHANGELOG - Add AGENTS.md, CLAUDE.md, Copilot pointer, .claude and .vscode settings, and status_conduct rules - Add .gitattributes and renormalize 17 CRLF files to LF (whitespace only) - Move release history from README.md to CHANGELOG.md - Extend .gitignore with the standard local-only entries Co-Authored-By: Claude Opus 5.5 --- .claude/rules/status_conduct.md | 23 + .claude/settings.json | 47 + .gitattributes | 44 + .github/copilot-instructions.md | 18 + .../status_conduct.instructions.md | 22 + .gitignore | 17 +- .vscode/settings.json | 8 + AGENTS.md | 66 + CHANGELOG.md | 217 +++ CLAUDE.md | 7 + PrepPipeline/eegplugin_prepPipeline.m | 86 +- PrepPipeline/examples/runVEPPrepPipeline.m | 100 +- PrepPipeline/examples/runVEPPrepReport.m | 70 +- PrepPipeline/interface/MasterGUI.m | 46 +- PrepPipeline/interface/displayErrors.m | 50 +- PrepPipeline/pop_prepPipeline.m | 304 ++-- PrepPipeline/prepReport.m | 1580 ++++++++--------- PrepPipeline/preplicense.txt | 472 ++--- PrepPipeline/publishPrepReport.m | 268 +-- PrepPipeline/utilities/blasstLineNoise.m | 134 +- PrepPipeline/utilities/cleanLineNoise.m | 126 +- PrepPipeline/utilities/findNoisyChannels.m | 776 ++++---- PrepPipeline/utilities/getPrepVersion.m | 188 +- PrepPipeline/utilities/robustReference.m | 180 +- PrepPipeline/utilities/struct2str.m | 92 +- PrepPipeline/utilities/updateBadChannels.m | 58 +- README.md | 267 +-- 27 files changed, 2769 insertions(+), 2497 deletions(-) create mode 100644 .claude/rules/status_conduct.md create mode 100644 .claude/settings.json create mode 100644 .gitattributes create mode 100644 .github/copilot-instructions.md create mode 100644 .github/instructions/status_conduct.instructions.md create mode 100644 .vscode/settings.json create mode 100644 AGENTS.md create mode 100644 CHANGELOG.md create mode 100644 CLAUDE.md diff --git a/.claude/rules/status_conduct.md b/.claude/rules/status_conduct.md new file mode 100644 index 0000000..b7b4ffe --- /dev/null +++ b/.claude/rules/status_conduct.md @@ -0,0 +1,23 @@ +--- +paths: + - ".status/**" +--- + +# Working in .status/ + +- `.status/` is gitignored: the only copy is on this machine. Never delete, + move, or rewrite a file here without asking first. Appending is fine. +- Every markdown file written here opens with a `For humans:` summary - three + or four sentences at the very top: what the file is and what a person needs + to take from it. +- Nothing new is created at the `.status/` root. New material goes in + `plans/`, `prompts/`, `notes/`, or `scratch/`. +- Only `plans/` and `prompts/` are edited in place. `notes/` is write-once. + `decisions.md` is append-only - never rewrite an entry. +- Temporary scripts, experiments, and one-off test files go in + `.status/scratch/` - never the repository root. `scratch/` may hold any + file type and is deleted unread; everywhere else is markdown only. +- Do not read `archive/` unless a file is named for you. +- Filenames: lowercase ASCII, `_` as separator. Notes are + `YYYY-MM-DD_slug.md` (date first); plans and prompts are `slug.md` - no + date, no status word in the name. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..4d3406c --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": [ + "Bash(python --version)", + "Bash(unzip -l *)", + "Bash(git status)", + "Bash(git status *)", + "Bash(git log)", + "Bash(git log *)", + "Bash(git diff)", + "Bash(git diff *)", + "Bash(git show *)", + "Bash(git branch)", + "Bash(git ls-files *)", + "Bash(grep *)", + "Bash(ls)", + "Bash(ls *)", + "Bash(head *)", + "Bash(tail *)", + "Bash(cat *)", + "Bash(wc *)" + ], + "ask": [ + "Bash(matlab -batch *)", + "Bash(git add *)", + "Bash(git commit *)", + "Bash(git checkout -- *)", + "Bash(git restore *)", + "Bash(git stash *)" + ], + "deny": [ + "Read(.env)", + "Read(.envrc)", + "Read(.status/archive/**)", + "Bash(rm -rf *)", + "Bash(rm -r *)", + "Bash(git push)", + "Bash(git push *)", + "Bash(git reset --hard *)", + "Bash(pip install *)", + "Bash(python -m pip install *)", + "Bash(curl *)", + "Bash(wget *)" + ] + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a52d90c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,44 @@ +# Set default behavior to automatically normalize line endings to LF +* text=auto eol=lf + +# Explicitly declare text files you want to always be normalized and converted to LF on checkout +*.xml text eol=lf +*.mediawiki text eol=lf +*.tsv text eol=lf +*.md text eol=lf +*.rst text eol=lf +*.txt text eol=lf +*.m text eol=lf +*.tex text eol=lf +*.py text eol=lf +*.sh text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.json text eol=lf +*.toml text eol=lf +*.svg text eol=lf +*.css text eol=lf +*.js text eol=lf +*.html text eol=lf + +# Denote all files that are truly binary and should not be modified +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary +*.zip binary +*.gz binary +*.tar binary +*.mp3 binary +*.mp4 binary +*.mov binary +*.avi binary +*.exe binary +*.dll binary +*.so binary +*.dylib binary +*.class binary +*.jar binary +*.war binary diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..b8fa164 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,18 @@ +# EEG-Clean-Tools + +**`AGENTS.md` at the repository root is the instruction set for this project. +Read it before answering, and follow it.** + +This file is a pointer and duplicates nothing. One source, several pointers: a +rule stated in two files is a rule that will disagree with itself. + +Path-specific instructions live in `.github/instructions/*.instructions.md` +and load automatically when the files being worked on match their `applyTo` +glob - for example, `status_conduct.instructions.md` applies under `.status/`. +Nothing needs to reference them; this note exists so a reader knows they are +there. + +Machine-specific facts - interpreter, local paths, cache locations - are in +`.status/local-environment.md`, which is gitignored: read it when it is there +and ignore its absence when it is not. No committed file in this repository +may contain a local path or a drive letter. diff --git a/.github/instructions/status_conduct.instructions.md b/.github/instructions/status_conduct.instructions.md new file mode 100644 index 0000000..389e841 --- /dev/null +++ b/.github/instructions/status_conduct.instructions.md @@ -0,0 +1,22 @@ +--- +applyTo: ".status/**" +--- + +# Working in .status/ + +- `.status/` is gitignored: the only copy is on this machine. Never delete, + move, or rewrite a file here without asking first. Appending is fine. +- Every markdown file written here opens with a `For humans:` summary - three + or four sentences at the very top: what the file is and what a person needs + to take from it. +- Nothing new is created at the `.status/` root. New material goes in + `plans/`, `prompts/`, `notes/`, or `scratch/`. +- Only `plans/` and `prompts/` are edited in place. `notes/` is write-once. + `decisions.md` is append-only - never rewrite an entry. +- Temporary scripts, experiments, and one-off test files go in + `.status/scratch/` - never the repository root. `scratch/` may hold any + file type and is deleted unread; everywhere else is markdown only. +- Do not read `archive/` unless a file is named for you. +- Filenames: lowercase ASCII, `_` as separator. Notes are + `YYYY-MM-DD_slug.md` (date first); plans and prompts are `slug.md` - no + date, no status word in the name. diff --git a/.gitignore b/.gitignore index 21229f7..98a7093 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,16 @@ -*.asv \ No newline at end of file +*.asv + +# Personal per-repo Claude Code notes (imports .status/local-environment.md) +CLAUDE.local.md + +# Personal per-repo settings (absolute paths, claudeMdExcludes) +.claude/settings.local.json + +# Real tokens and machine values; .env.example is the committed documentation +.env + +# Working notes, plans, prompts, and decision records. Deliberately NOT +# committed: these repos are public and .status/ holds half-formed thinking. +# The consequence is that it lives on one machine only and is absent from +# fresh clones and from `claude --worktree` worktrees. +.status/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8958373 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": false +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8c08dee --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,66 @@ +# EEG-Clean-Tools + +Purpose: the PREP pipeline - standardized early-stage EEG preprocessing (boundary handling, detrending, line-noise removal, robust average referencing, bad-channel detection and interpolation, reporting) as a MATLAB toolbox and an EEGLAB plugin. + +Not in scope: EEGLAB itself, which PREP runs inside and depends on, and the downstream analysis PREP deliberately leaves open (final high-pass filtering, ICA). + +## Commands + +Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/blasst_test.m` is a vendored demo script, not a test. Do not add a suite as a side effect of other work. + +- Smoke check (no EEGLAB needed): `matlab -batch "addpath(genpath('PrepPipeline')); disp(getPrepVersion())"` - prints the version string, for example `PrepPipeline0.57.0` +- Run standalone: add `PrepPipeline` and its subfolders to the MATLAB path, then `[EEG, params, computationTimes] = prepPipeline(EEG, params)` on an EEGLAB `EEG` structure with channel locations. Needs EEGLAB and the Signal Processing Toolbox on the path. +- Run as a plugin: unzip `EEGLABPlugin/PrepPipeline.zip` into EEGLAB's `plugins/` folder; the menu entry is Tools -> Run PREP pipeline. +- Check the plugin zip: `unzip -l EEGLABPlugin/PrepPipeline.zip` + +## Layout + +- `PrepPipeline/` - entry points: `prepPipeline.m`, `pop_prepPipeline.m` (EEGLAB GUI wrapper), `prepPostProcess.m`, `prepReport.m`, `publishPrepReport.m`, `eegplugin_prepPipeline.m` (EEGLAB menu registration) +- `PrepPipeline/utilities/` - the algorithms (`removeTrend`, `cleanLineNoise`, `performReference`, `findNoisyChannels`, defaults, version); `chronux_2_modified/` and `blasst/` are vendored third-party code +- `PrepPipeline/reporting/` - report and collection-statistics functions +- `PrepPipeline/interface/` - the EEGLAB parameter GUIs +- `PrepPipeline/derived/`, `PrepPipeline/examples/`, `PrepPipeline/extracted/` - scripts built on the pipeline +- `EEGLABPlugin/` - the released plugin as a zip +- `CHANGELOG.md` - release history +- `.status/` - working notes. Gitignored; local to each machine. + +## Conventions that differ from defaults + +- **ASCII only** in prose, code, comments, and filenames: `-` not em or en dashes, `->` not arrows, `...` not an ellipsis character, straight quotes. Exception: genuine data (author names, dataset titles, recorded API responses) keeps whatever characters it actually contains. +- Markdown headers are sentence case: capitalize only the first word, proper nouns, and acronyms (PREP, EEG, EEGLAB, MATLAB). +- MATLAB: camelCase for functions. The file name must match the function name, so `.m` files keep their mixed case. +- The default branch is `master`, not `main`. + +## Rules that are easy to get wrong + +- The version exists in three places that must agree: `PrepPipeline/utilities/getPrepVersion.m` (the change log that `getPrepVersion` returns), the zip name under `EEGLABPlugin/`, and `CHANGELOG.md`. Change all three together. +- Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/` or `PrepPipeline/utilities/blasst/`. +- `PrepPipeline/reporting/showPipelineDefaults.m` declares `function showPrepDefaults`; MATLAB calls a file by its file name, so the callable name is `showPipelineDefaults`, not the `showPrepDefaults` that the `prepPipeline` help text names. `outputPrepDefaults(EEG)` prints the same defaults. +- Do not change the signature of an entry-point function without discussion; EEGLAB and user scripts call them directly, and `pop_prepPipeline` writes the call into EEGLAB history. + +## Related repositories + +Referred to by name; none is vendored here. + +- `eeglab` - the MATLAB toolbox PREP runs in, required at runtime. +- `hed-matlab` - the model for this repository's layout and documentation setup. + +## Where the thinking lives + +`.status/` is gitignored, so it exists only on the machine that wrote it and never in a fresh clone or worktree. + +- `.status/README.md` - the index. Read this first; it lists what is active. +- `.status/decisions.md` - why things are the way they are. Read before proposing structural changes. Append entries; never rewrite one. +- `.status/plans/*.md` - active plans. Check the `Status:` header and the `[ ]` / `[x]` markers before starting work. +- `.status/local-environment.md` - this machine's paths, interpreter, and quirks. Tool-agnostic. Never copy its contents into a committed file. +- IMPORTANT: do not read `.status/archive/` unless a file is named for you. Nothing new is created at the `.status/` root. + +## Working agreements + +- IMPORTANT: every file written to `.status/` opens with a `For humans:` summary - three or four sentences, at the very top: what the file is and what a person needs to take from it. The same applies to a long answer in a session: lead with the conclusion. +- IMPORTANT: temporary scripts, experiments, and one-off test files go in `.status/scratch/` - **never the repository root**. Delete them when the experiment ends; anything in `scratch/` may be deleted unread. +- IMPORTANT: never delete or rewrite a file under `.status/` without asking first. Appending is fine. +- For a change spanning more than three files, write a plan to `.status/plans/` and stop for review before editing. +- When you are guessing about an external API or data format, say so explicitly rather than assuming. +- Show evidence, not assertions: the command you ran and its actual output. +- Do not commit, push, or create branches unless asked. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1e6801f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,217 @@ +# Changelog + +Release history of the PREP pipeline, newest first. The current version is also reported by `getPrepVersion` (`PrepPipeline/utilities/getPrepVersion.m`). + +## Version 0.57.0 - Released 3/30/2025 + +* Modified to work with modified EEGLAB GUI Builder +* Modified reporting to not clutter workspace + +## Version 0.56.0 - Released 8/01/2021 + +* Corrected parfor failure when channel number not consecutive +* Fixed missing badChannelsFromDropout in updateBadChannels issue#28 + +## Version 0.55.4 - Released 7/26/2020 + +* Correctly restored EEGLAB options after execution +* Added functions to output errors from etc.noiseDetection +* Corrected findpeaks naming conflict in Chronux +* Post process does not execute if Prep had errors + +## Version 0.55.3 - Released 10/19/2017 + +* Fixed issue with interpolated channels when interpolation order is pre-process +* Fixed issue with correct removal of interpolated channels during post-processing +* Reordered preprocessing and report buttons on master GUI + +## Version 0.55.2 - Released 08/18/2017 + +* Fixed undefined reference to referenceOut in prepPipeline post process + +## Version 0.55.1 - Released 06/03/2017 + +* Wrote printListCompressed to display channels more compactly +* Put in a MATLAB version check because legend titles not supported in 2014b +* Fixed spacing on output of interpolated channel numbers + +## Version 0.55.0 - Released 05/29/2017 + +* Changed the EEG.etc.noiseDetection structure to contain removed channels and interpolated channels for easier access +* Fixed reporting to work when bad channels have been removed +* Added original channel labels to EEG.etc.noiseDetection for ease in reporting +* Added Blasst as an unsupported line noise removal option +* Moved legend of spectrum to right, put in checks for removed channels +* Corrected bug in smoothing in cleanline +* Corrected several reporting issues +* Default behavior now outputs errors to command line in addition to logging +* Renamed several functions to make naming scheme consistent +* Started supporting changelog in versions +* Fixed bug in struct2str and improved com return on pop_prepPipeline + +## Version 0.52 - Released + +* Modified code to handle EEG structures with empty EEG.error. +* Performed additional minor cleanup. + +## Version 0.51 - Not released + +* Developing bad window visualization plugin for EEGLAB + +## Version 0.50 - Released + +* Made several cleanup modifications to ready for release. + +## Version 0.48 - (Not released -- version 0.47 with EEGLAB integration) + +* Integrated EEGLAB plugin +* Changed the default structure value field name from defaults.default to + default.value and propagated the change +* Changed default names of line noise and global trend to linenoise and + globaltrend +* Modified the resampling step to allow an option low pass filter to remove + downsampling artifacts just below Nyquist frequency. + +## Version 0.47 - (Not released -- version 0.46 with additional changes) + +* Minor refactoring of performReference to avoid 1 extra filtering operation --- + should not reflect results. +* Also added average and specific referencing methods -- not tested as yet. + +## Version 0.46 - (Not released - version 0.45 with additional changes) + +* Fixed remapping of bad evaluation channels into original channel numbers + (relevant when there are none EEG channels interspersed in the channel + locations. +* Passed detrend information in reference structure to allow detrending + with other than the defaults +* Corrected several channel mapping issues in the reporting. + +## Version 0.45 - (Not released - version 0.44 with additional changes) + +* Refactored report to allow statistics to be gathered from noisy structures + +## Version 0.44 - (Not released - version 0.43 with additional changes) + +* Corrected a minor issue with reporting -- difference between robust + and ordinary reference had axes reversed. +* Updated to run with plotting compatible with MATLAB 2014b +* Added box on to cummulative plots. + +## Version 0.43 - (Not released - version 0.42 with additional changes) + +* Corrected a minor issue with reporting -- mean scalp correlation map for + beforeInterpolation was plotting the Original data rather than the + beforeInterpolation data. + +## Version 0.42 - (Not released - version 0.41 with additional changes) + +* Added default line frequencies as multiples of 60 up to half nyquist. + +## Version 0.41 - (Not released - version 0.40 with additional changes) + +* Replaced default method with channel forgetting and median initialization +* Converted EEG to double at the beginning of the pipeline +* Added a noisyStatisticsForInterpolation field to the reference reporting + structure. + +## Version 0.40 - (Not yet released - major change in strategy) + +* Changed the name from StandardLevel2 to PrepPipeline +* Implemented the HP filter-free strategy +* Added a keepFiltered version -- if false (the default) the data in the + repository is not high pass filtered +* Added an option for removing global trend +* Incorporated the different reference schemes into a single performReference + +## Version 0.28 - (Not yet released) + +* Changed the name of the noisyParameter structure in EEG.etc to + noiseDetection. This is a major change with corresponding change + in ESS. +* Added a specificReferenceChannels field to reference structure +* Changed the averageReference field name to referenceSignal in reference + structure +* Included a referenceType field in the reference structure (this + can be 'robust', 'average', or 'specific') +* Eliminated the don't interpolateHFChannels flag. +* Added routines to do specificReference (mastoid or average) +* Modified showSpectrum to return the spectra of all of the channels. +* Detrending at 0.2 Hz has replaced FIR filtering as default trend removal. + +## Version 0.27 - Released 1/7/2015 + +* Correct version of bug fix in cleanLineNoise -- watch that single + precision conversion! + +## Version 0.26 - Released 1/7/2015 + +* Release to fix bug in cleanLineNoise --- channels that are not + lineNoiseChannels were set to zero rather than being carried forward. + +## Version 0.25 - Released 1/5/2015 (major) + +* Removed saving of temporary file after line noise removal +* Fixed report of relative reference +* Modified findNoisyChannels to exclude NaN and constant channels + from noisyChannel thresholding, but to designate them as bad channels +* Moved resampling step before high pass filter +* Assigned return values in a separate step +* Put error check in ShowSpectrum when invalid data is invalid +* Correct minor issues with PlotScalpMap +* Added extractReferenceStatistics -- which extracts summary statistics + for an entire archive. +* Added iterations on the remove robust reference +* Added a summary reporting scheme for spotting problematic datasets. + +## Version 0.24 - Released 12/7/2104 (major) + +* Fixed channel selection bug in showSpectrum +* Added error handling for failures in standardLevel2Pipeline +* Added error reporting for failures +* Corrected time scale on visualization of difference between + robust and mean reference +* Added channel labels as well as numbers to spectrum visualization +* Fixed major bug in robustReference so that original signal is rereferenced +* Revised and expanded the reporting + +## Version 0.23 - Released 11/13/2014 + +* Removed the channel locations and channel information from noisyOut + because it is already in the reference structure at top level. +* Added reporting of average fraction of channels bad in windows. +* Added first version of hdf5support -- rewrites the noisyParameters + to an HDF5 file. + +## Version 0.22 - Released 11/9/2014 + +* Revised the method of computing the windowed channel deviations +* Added summary reporting functions +* Added a check to only perform ransac when sufficiently good channels + are available +* Added check to only perform ransac when channel locations are available +* Fixed the input parameter structure on findNoisyChannels +* Added the infrastructure for the summary of all datasets + +## Version 0.21 - Released 10/30/2104 + +* Removed any reference to chanlocs in highPassFilter +* Full integration with ESS Study Level 2 code +* Preliminary version of Standard Level 2 Report finalized (gives pdf) + +## Version 0.20 - Released 10/18/2014 + +* Converted standardLevel2Pipeline to a function +* Moved the computationTimes structure to standardLevel2Pipeline so that +it is returned. + +## Version 0.19 - Released 10/16/2014 + +* Refactored name is also included in the params structure. +* Renamed rereferencedChannels as channelsToBeReferenced to agree with ESS. + +## Version 0.18 - Released 10/15/2014 + +* Refactored so that all input to the pipeline is in a single params structure. +* Fixed the HF noise reporting windows and several minor bugs +* Added visualizations to show number of bad channels in each window diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..39297d0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,7 @@ +@AGENTS.md + +# Claude Code + +- Path-scoped rules live in `.claude/rules/` and load automatically when a session touches files matching their `paths:` globs - for example, `status_conduct.md` applies under `.status/`. Nothing needs to reference them; this note exists so a reader knows they are there. +- `CLAUDE.local.md` (gitignored) holds notes about this machine's Claude Code setup and imports `.status/local-environment.md`, which is where machine facts live for every tool, not just this one. +- After changing either import, run `/context` and confirm the file appears under **Memory files**. diff --git a/PrepPipeline/eegplugin_prepPipeline.m b/PrepPipeline/eegplugin_prepPipeline.m index 0bf3f1a..9919dc9 100644 --- a/PrepPipeline/eegplugin_prepPipeline.m +++ b/PrepPipeline/eegplugin_prepPipeline.m @@ -1,43 +1,43 @@ -% eegplugin_prepPipeline() - a wrapper to the prepPipeline, which does early stage -% -% Usage: -% >> eegplugin_prepPipeline(fig, try_strings, catch_strings); -% -% see also: prepPipeline - -% Author: Kay Robbins, with contributions from Nima Bigdely-Shamlo, Tim Mullen, Christian Kothe, and Cassidy Matousek. - -% This program is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with this program; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -%function eegplugin_clean_rawdata(fig,try_strings,catch_strings) - - -% eegplugin_prepPipeline() - the PREP pipeline plugin -function vers = eegplugin_prepPipeline(fig, trystrs, catchstrs) - -%% Add path to prepPipeline subdirectories if not in the list -tmp = which('getPrepDefaults'); -if isempty(tmp) - myPath = fileparts(which('prepPipeline')); - addpath(genpath(myPath)); -end -vers = getPrepVersion(); - -% create menu -comprep = [trystrs.no_check '[EEG LASTCOM] = pop_prepPipeline(EEG);' catchstrs.new_and_hist]; -menu = findobj(fig, 'tag', 'tools'); -uimenu( menu, 'label', 'Run PREP pipeline', 'callback', comprep, ... - 'separator', 'on'); - +% eegplugin_prepPipeline() - a wrapper to the prepPipeline, which does early stage +% +% Usage: +% >> eegplugin_prepPipeline(fig, try_strings, catch_strings); +% +% see also: prepPipeline + +% Author: Kay Robbins, with contributions from Nima Bigdely-Shamlo, Tim Mullen, Christian Kothe, and Cassidy Matousek. + +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +%function eegplugin_clean_rawdata(fig,try_strings,catch_strings) + + +% eegplugin_prepPipeline() - the PREP pipeline plugin +function vers = eegplugin_prepPipeline(fig, trystrs, catchstrs) + +%% Add path to prepPipeline subdirectories if not in the list +tmp = which('getPrepDefaults'); +if isempty(tmp) + myPath = fileparts(which('prepPipeline')); + addpath(genpath(myPath)); +end +vers = getPrepVersion(); + +% create menu +comprep = [trystrs.no_check '[EEG LASTCOM] = pop_prepPipeline(EEG);' catchstrs.new_and_hist]; +menu = findobj(fig, 'tag', 'tools'); +uimenu( menu, 'label', 'Run PREP pipeline', 'callback', comprep, ... + 'separator', 'on'); + diff --git a/PrepPipeline/examples/runVEPPrepPipeline.m b/PrepPipeline/examples/runVEPPrepPipeline.m index 76d3c42..dd14575 100644 --- a/PrepPipeline/examples/runVEPPrepPipeline.m +++ b/PrepPipeline/examples/runVEPPrepPipeline.m @@ -1,50 +1,50 @@ -%% Example: Running the pipeline on a directory of EEG files - -%% Set up the input and the output directories -basename = 'vep'; -indir = 'F:\DataPool\CTADATA\VEP\BiosemiOriginalSetCorrected'; -outdir = 'F:\TempData'; - -%% Make the output directory if needed -if ~exist(outdir, 'dir') - mkdir(outdir) -end - -%% Set up the params structure -params = struct(); -params.lineFrequencies = [60, 120, 180, 212, 240]; -params.referenceChannels = 1:64; -params.evaluationChannels = 1:64; -params.rereferencedChannels = 1:70; -params.detrendChannels = 1:70; -params.lineNoiseChannels = 1:70; - -params.detrendType = 'high pass'; -params.detrendCutoff = 1; -params.referenceType = 'robust'; -params.meanEstimateType = 'median'; -params.interpolationOrder = 'post-reference'; -params.removeInterpolatedChannels = true; -params.keepFiltered = false; -basenameOut = [basename 'robust_1Hz_post_median_unfiltered']; - -%% Get the filelist -fileList = getFileList('FILES', indir); -%% Run the pipeline -for k = 1:length(fileList) - [~, thisName, ~] = fileparts(fileList{k}); - EEG = pop_loadset(fileList{k}); - params.name = thisName; - [EEG, params, computationTimes] = prepPipeline(EEG, params); - fprintf('Computation times (seconds):\n %s\n', ... - getStructureString(computationTimes)); - fprintf('Post-process\n') - EEG = prepPostProcess(EEG, params); - fname = [outdir filesep thisName '.set']; - save(fname, 'EEG', '-mat', '-v7.3'); - if strcmpi(params.errorMsgs, 'verbose') - outputPrepParams(params, 'Prep parameters (non-defaults)'); - outputPrepErrors(EEG.etc.noiseDetection, 'Prep error status'); - end - -end +%% Example: Running the pipeline on a directory of EEG files + +%% Set up the input and the output directories +basename = 'vep'; +indir = 'F:\DataPool\CTADATA\VEP\BiosemiOriginalSetCorrected'; +outdir = 'F:\TempData'; + +%% Make the output directory if needed +if ~exist(outdir, 'dir') + mkdir(outdir) +end + +%% Set up the params structure +params = struct(); +params.lineFrequencies = [60, 120, 180, 212, 240]; +params.referenceChannels = 1:64; +params.evaluationChannels = 1:64; +params.rereferencedChannels = 1:70; +params.detrendChannels = 1:70; +params.lineNoiseChannels = 1:70; + +params.detrendType = 'high pass'; +params.detrendCutoff = 1; +params.referenceType = 'robust'; +params.meanEstimateType = 'median'; +params.interpolationOrder = 'post-reference'; +params.removeInterpolatedChannels = true; +params.keepFiltered = false; +basenameOut = [basename 'robust_1Hz_post_median_unfiltered']; + +%% Get the filelist +fileList = getFileList('FILES', indir); +%% Run the pipeline +for k = 1:length(fileList) + [~, thisName, ~] = fileparts(fileList{k}); + EEG = pop_loadset(fileList{k}); + params.name = thisName; + [EEG, params, computationTimes] = prepPipeline(EEG, params); + fprintf('Computation times (seconds):\n %s\n', ... + getStructureString(computationTimes)); + fprintf('Post-process\n') + EEG = prepPostProcess(EEG, params); + fname = [outdir filesep thisName '.set']; + save(fname, 'EEG', '-mat', '-v7.3'); + if strcmpi(params.errorMsgs, 'verbose') + outputPrepParams(params, 'Prep parameters (non-defaults)'); + outputPrepErrors(EEG.etc.noiseDetection, 'Prep error status'); + end + +end diff --git a/PrepPipeline/examples/runVEPPrepReport.m b/PrepPipeline/examples/runVEPPrepReport.m index 1bdf949..bcd35fd 100644 --- a/PrepPipeline/examples/runVEPPrepReport.m +++ b/PrepPipeline/examples/runVEPPrepReport.m @@ -1,36 +1,36 @@ -%% This script takes a directory of files that have been processed by PREP -% and produces reports. - -%% Read in the file and set the necessary parameters -dataDir = 'F:\TempData'; -summaryFolder = 'F:\TempDataReports'; -publishOn = true; - -%% Get the directory list -inList = dir(dataDir); -inNames = {inList(:).name}; -inTypes = [inList(:).isdir]; -inNames = inNames(~inTypes); - -%% Setup up the names -basename = 'vep'; -summaryReportName = [basename '_summary.html']; -sessionFolder = '.'; -summaryFileName = [summaryFolder filesep summaryReportName]; -if exist(summaryFileName, 'file') - delete(summaryFileName); -end - -%% Publish the reports -for k = 1:length(inNames) - [~, theName, theExt] = fileparts(inNames{k}); - if ~strcmpi(theExt, '.set') && ~strcmpi(theExt, '.mat') - continue; - end - sessionReportName = [theName '.pdf']; - fname = [dataDir filesep inNames{k}]; - load(fname, '-mat'); - sessionFileName = [summaryFolder filesep sessionReportName]; - consoleFID = 1; - publishPrepReport(EEG, summaryFileName, sessionFileName, consoleFID, publishOn); +%% This script takes a directory of files that have been processed by PREP +% and produces reports. + +%% Read in the file and set the necessary parameters +dataDir = 'F:\TempData'; +summaryFolder = 'F:\TempDataReports'; +publishOn = true; + +%% Get the directory list +inList = dir(dataDir); +inNames = {inList(:).name}; +inTypes = [inList(:).isdir]; +inNames = inNames(~inTypes); + +%% Setup up the names +basename = 'vep'; +summaryReportName = [basename '_summary.html']; +sessionFolder = '.'; +summaryFileName = [summaryFolder filesep summaryReportName]; +if exist(summaryFileName, 'file') + delete(summaryFileName); +end + +%% Publish the reports +for k = 1:length(inNames) + [~, theName, theExt] = fileparts(inNames{k}); + if ~strcmpi(theExt, '.set') && ~strcmpi(theExt, '.mat') + continue; + end + sessionReportName = [theName '.pdf']; + fname = [dataDir filesep inNames{k}]; + load(fname, '-mat'); + sessionFileName = [summaryFolder filesep sessionReportName]; + consoleFID = 1; + publishPrepReport(EEG, summaryFileName, sessionFileName, consoleFID, publishOn); end \ No newline at end of file diff --git a/PrepPipeline/interface/MasterGUI.m b/PrepPipeline/interface/MasterGUI.m index 84fffd8..da3c963 100644 --- a/PrepPipeline/interface/MasterGUI.m +++ b/PrepPipeline/interface/MasterGUI.m @@ -1,24 +1,24 @@ -function paramsOut = MasterGUI(hObject, callbackdata, userData, EEG) %#ok -geometry = {1, [1, 1], [1, 1], [1, 1]}; -geomvert = [1,1,1,1]; -title = 'PREP pipeline control panel'; -inputData = struct('signal', EEG, 'name', title, 'userData', userData); -closeOpenWindows(inputData.name); -uilist= {{'style', 'text', 'string', 'Override default parameters for processing step:'}... - {'style', 'pushbutton', 'string', 'Boundary', ... - 'Callback', {@boundaryGUI, inputData}} ... - {'style', 'pushbutton', 'string', 'Reference', ... - 'Callback', {@referenceGUI, inputData}} ... - {'style', 'pushbutton', 'string', 'Detrend', ... - 'Callback', {@detrendGUI, inputData}} ... - {'style', 'pushbutton', 'string', 'Report', ... - 'Callback', {@reportGUI, inputData}} ... - {'style', 'pushbutton', 'string', 'Line noise', ... - 'Callback', {@lineNoiseGUI, inputData}}... - {'style', 'pushbutton', 'string', 'Post process', ... - 'Callback', {@postProcessGUI, inputData}}}; -[~, paramsOut] = inputgui('geometry', geometry, 'geomvert', geomvert, ... - 'uilist', uilist, 'title', title, ... - 'helpcom', 'pophelp(''pop_prepPipeline'')'); - +function paramsOut = MasterGUI(hObject, callbackdata, userData, EEG) %#ok +geometry = {1, [1, 1], [1, 1], [1, 1]}; +geomvert = [1,1,1,1]; +title = 'PREP pipeline control panel'; +inputData = struct('signal', EEG, 'name', title, 'userData', userData); +closeOpenWindows(inputData.name); +uilist= {{'style', 'text', 'string', 'Override default parameters for processing step:'}... + {'style', 'pushbutton', 'string', 'Boundary', ... + 'Callback', {@boundaryGUI, inputData}} ... + {'style', 'pushbutton', 'string', 'Reference', ... + 'Callback', {@referenceGUI, inputData}} ... + {'style', 'pushbutton', 'string', 'Detrend', ... + 'Callback', {@detrendGUI, inputData}} ... + {'style', 'pushbutton', 'string', 'Report', ... + 'Callback', {@reportGUI, inputData}} ... + {'style', 'pushbutton', 'string', 'Line noise', ... + 'Callback', {@lineNoiseGUI, inputData}}... + {'style', 'pushbutton', 'string', 'Post process', ... + 'Callback', {@postProcessGUI, inputData}}}; +[~, paramsOut] = inputgui('geometry', geometry, 'geomvert', geomvert, ... + 'uilist', uilist, 'title', title, ... + 'helpcom', 'pophelp(''pop_prepPipeline'')'); + end % MasterGUI \ No newline at end of file diff --git a/PrepPipeline/interface/displayErrors.m b/PrepPipeline/interface/displayErrors.m index bdc1416..608f99c 100644 --- a/PrepPipeline/interface/displayErrors.m +++ b/PrepPipeline/interface/displayErrors.m @@ -1,26 +1,26 @@ -%% *****************************displayErrors****************************** -%Purpose: -% This returns the errors (if any) that are found when a user enters -% data that does not correspond with the type found by the default -% function. It displays the errors in a pop-up GUI. -%Parameters: -% I errors Cell array of strings; errors found by the -% checkPrepDefaults function -% O loop Integer that either continues the while loop found -% above or exits the loop -%Notes: -% -%Return Value: -% 0 No errors -% 1 Displays errors and continues loop -%************************************************************************** -function displayErrors(errors) - geometry={}; - geomvert=[]; - uilist={}; - for k=1:length(errors) - geometry={geometry{:},1}; - uilist={uilist{:},{'style', 'text', 'string', errors(k)}}; - end - result=inputgui('geometry', geometry, 'geomvert', geomvert, 'uilist', uilist, 'title', 'Reference Errors', 'helpcom', 'pophelp(''pop_eegfiltnew'')'); +%% *****************************displayErrors****************************** +%Purpose: +% This returns the errors (if any) that are found when a user enters +% data that does not correspond with the type found by the default +% function. It displays the errors in a pop-up GUI. +%Parameters: +% I errors Cell array of strings; errors found by the +% checkPrepDefaults function +% O loop Integer that either continues the while loop found +% above or exits the loop +%Notes: +% +%Return Value: +% 0 No errors +% 1 Displays errors and continues loop +%************************************************************************** +function displayErrors(errors) + geometry={}; + geomvert=[]; + uilist={}; + for k=1:length(errors) + geometry={geometry{:},1}; + uilist={uilist{:},{'style', 'text', 'string', errors(k)}}; + end + result=inputgui('geometry', geometry, 'geomvert', geomvert, 'uilist', uilist, 'title', 'Reference Errors', 'helpcom', 'pophelp(''pop_eegfiltnew'')'); end \ No newline at end of file diff --git a/PrepPipeline/pop_prepPipeline.m b/PrepPipeline/pop_prepPipeline.m index d04e857..a08058c 100644 --- a/PrepPipeline/pop_prepPipeline.m +++ b/PrepPipeline/pop_prepPipeline.m @@ -1,153 +1,153 @@ -% pop_prepPipeline() - runs the early stage pipeline to reference and to -% detect bad channels -% -% Usage: -% >> [OUTEEG, com] = pop_prepPipeline(INEEG, params); -% -% Inputs: -% INEEG - input EEG dataset -% params - (optional) structure with parameters to override defaults -% -% Outputs: -% OUTEEG - output dataset -% -% See also: -% prepPipeline, prepPipelineReport, EEGLAB - -% Copyright (C) 2015 Kay Robbins with contributions from Nima -% Bigdely-Shamlo, Christian Kothe, Tim Mullen, and Cassidy Matousek -% -% This program is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with this program; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -function [EEG, com] = pop_prepPipeline(EEG, params) -com = ''; % Return something if user presses the cancel button -if nargin < 1 %% display help if not enough arguments - help pop_prepPipeline; - return; -elseif nargin < 2 - params = struct(); -end - -%% Add path to prepPipeline subdirectories if not in the list -tmp = which('getPrepDefaults'); -if isempty(tmp) - myPath = fileparts(which('prepPipeline')); - addpath(genpath(myPath)); -end - -%% Pop up window -userData = getUserData(); -if nargin < 2 - params = MasterGUI([],[],userData, EEG); -end - -%% Begin the pipeline execution -paramsUpdated = updateParams(params); -com = sprintf('%s = pop_prepPipeline(%s, %s);', inputname(1), ... - struct2str(paramsUpdated)); - -options = getReportOptions(userData, paramsUpdated); - -if strcmpi(options.reportMode, 'normal') || strcmpi(options.reportMode, 'skipReport') - EEG = prepPipeline(EEG, paramsUpdated); -end - -%% Handle reporting -if (strcmpi(options.reportMode, 'normal') || strcmpi(options.reportMode, 'reportOnly')) - publishPrepReport(EEG, options.summaryFilePath, options.sessionFilePath, ... - options.consoleFID, options.publishOn); -end - -%% Perform post-processing -if strcmpi(options.reportMode, 'normal') || strcmpi(options.reportMode, 'skipReport') - EEG = prepPostProcess(EEG, paramsUpdated); -end - - function userData = getUserData() - %% Gets the userData defaults and merges it with the parameters - userData = struct('boundary', [], 'detrend', [], ... - 'lineNoise', [], 'reference', [], ... - 'report', [], 'postProcess', []); - stepNames = fieldnames(userData); - for k = 1:length(stepNames) - defaults = getPrepDefaults(EEG, stepNames{k}); - [theseValues, errors] = checkStructureDefaults(params, ... - defaults); - if ~isempty(errors) - error('pop_prepPipeline:BadParameters', ['|' ... - sprintf('%s|', errors{:})]); - end - userData.(stepNames{k}) = theseValues; - end - end % getUserData - - function paramsOut = updateParams(userDataUpdate) - paramsOut = struct(); - if ~isempty(userDataUpdate) - fNames = fieldnames(userDataUpdate); - for k = 1:length(fNames) - nextStruct = userDataUpdate.(fNames{k}); - nextNames = fieldnames(nextStruct); - for j = 1:length(nextNames) - paramsOut.(nextNames{j}) = nextStruct.(nextNames{j}); - end - end - end - end - - function options = getReportOptions(userData, paramsUpdated) - options = struct('reportMode', '', 'consoleFID', '', 'publishOn', '', ... - 'summaryFilePath', '', 'sessionFilePath', '' ); - options.reportMode = userData.report.reportMode.value; - if isfield(paramsUpdated, 'reportMode') - options.reportMode = paramsUpdated.reportMode; - end - options.consoleFID = userData.report.consoleFID.value; - if isfield(paramsUpdated, 'consoleFID') - options.consoleFID = paramsUpdated.consoleFID; - end - - options.publishOn = userData.report.publishOn.value; - if isfield(paramsUpdated, 'publishOn') - options.publishOn = paramsUpdated.publishOn; - end - - options.summaryFilePath = userData.report.summaryFilePath.value; - if isfield(paramsUpdated, 'summaryFilePath') - options.summaryFilePath = paramsUpdated.summaryFilePath; - end - options.summaryFilePath = resolvePath(options.summaryFilePath); - - options.sessionFilePath = userData.report.sessionFilePath.value; - if isfield(paramsUpdated, 'sessionFilePath') - options.sessionFilePath = paramsUpdated.sessionFilePath; - end - options.sessionFilePath = resolvePath(options.sessionFilePath); - - fprintf('summaryFilePath: %s\n', options.summaryFilePath) - fprintf('sessionFilePath: %s\n', options.sessionFilePath) - end - - function absPath = resolvePath(pathIn) - if isfolder(pathIn) || isfile(pathIn) - % Already exists, maybe absolute - absPath = fullfile(pathIn); - else - % Assume relative to current folder - absPath = fullfile(pwd, pathIn); - end -end - +% pop_prepPipeline() - runs the early stage pipeline to reference and to +% detect bad channels +% +% Usage: +% >> [OUTEEG, com] = pop_prepPipeline(INEEG, params); +% +% Inputs: +% INEEG - input EEG dataset +% params - (optional) structure with parameters to override defaults +% +% Outputs: +% OUTEEG - output dataset +% +% See also: +% prepPipeline, prepPipelineReport, EEGLAB + +% Copyright (C) 2015 Kay Robbins with contributions from Nima +% Bigdely-Shamlo, Christian Kothe, Tim Mullen, and Cassidy Matousek +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +function [EEG, com] = pop_prepPipeline(EEG, params) +com = ''; % Return something if user presses the cancel button +if nargin < 1 %% display help if not enough arguments + help pop_prepPipeline; + return; +elseif nargin < 2 + params = struct(); +end + +%% Add path to prepPipeline subdirectories if not in the list +tmp = which('getPrepDefaults'); +if isempty(tmp) + myPath = fileparts(which('prepPipeline')); + addpath(genpath(myPath)); +end + +%% Pop up window +userData = getUserData(); +if nargin < 2 + params = MasterGUI([],[],userData, EEG); +end + +%% Begin the pipeline execution +paramsUpdated = updateParams(params); +com = sprintf('%s = pop_prepPipeline(%s, %s);', inputname(1), ... + struct2str(paramsUpdated)); + +options = getReportOptions(userData, paramsUpdated); + +if strcmpi(options.reportMode, 'normal') || strcmpi(options.reportMode, 'skipReport') + EEG = prepPipeline(EEG, paramsUpdated); +end + +%% Handle reporting +if (strcmpi(options.reportMode, 'normal') || strcmpi(options.reportMode, 'reportOnly')) + publishPrepReport(EEG, options.summaryFilePath, options.sessionFilePath, ... + options.consoleFID, options.publishOn); +end + +%% Perform post-processing +if strcmpi(options.reportMode, 'normal') || strcmpi(options.reportMode, 'skipReport') + EEG = prepPostProcess(EEG, paramsUpdated); +end + + function userData = getUserData() + %% Gets the userData defaults and merges it with the parameters + userData = struct('boundary', [], 'detrend', [], ... + 'lineNoise', [], 'reference', [], ... + 'report', [], 'postProcess', []); + stepNames = fieldnames(userData); + for k = 1:length(stepNames) + defaults = getPrepDefaults(EEG, stepNames{k}); + [theseValues, errors] = checkStructureDefaults(params, ... + defaults); + if ~isempty(errors) + error('pop_prepPipeline:BadParameters', ['|' ... + sprintf('%s|', errors{:})]); + end + userData.(stepNames{k}) = theseValues; + end + end % getUserData + + function paramsOut = updateParams(userDataUpdate) + paramsOut = struct(); + if ~isempty(userDataUpdate) + fNames = fieldnames(userDataUpdate); + for k = 1:length(fNames) + nextStruct = userDataUpdate.(fNames{k}); + nextNames = fieldnames(nextStruct); + for j = 1:length(nextNames) + paramsOut.(nextNames{j}) = nextStruct.(nextNames{j}); + end + end + end + end + + function options = getReportOptions(userData, paramsUpdated) + options = struct('reportMode', '', 'consoleFID', '', 'publishOn', '', ... + 'summaryFilePath', '', 'sessionFilePath', '' ); + options.reportMode = userData.report.reportMode.value; + if isfield(paramsUpdated, 'reportMode') + options.reportMode = paramsUpdated.reportMode; + end + options.consoleFID = userData.report.consoleFID.value; + if isfield(paramsUpdated, 'consoleFID') + options.consoleFID = paramsUpdated.consoleFID; + end + + options.publishOn = userData.report.publishOn.value; + if isfield(paramsUpdated, 'publishOn') + options.publishOn = paramsUpdated.publishOn; + end + + options.summaryFilePath = userData.report.summaryFilePath.value; + if isfield(paramsUpdated, 'summaryFilePath') + options.summaryFilePath = paramsUpdated.summaryFilePath; + end + options.summaryFilePath = resolvePath(options.summaryFilePath); + + options.sessionFilePath = userData.report.sessionFilePath.value; + if isfield(paramsUpdated, 'sessionFilePath') + options.sessionFilePath = paramsUpdated.sessionFilePath; + end + options.sessionFilePath = resolvePath(options.sessionFilePath); + + fprintf('summaryFilePath: %s\n', options.summaryFilePath) + fprintf('sessionFilePath: %s\n', options.sessionFilePath) + end + + function absPath = resolvePath(pathIn) + if isfolder(pathIn) || isfile(pathIn) + % Already exists, maybe absolute + absPath = fullfile(pathIn); + else + % Assume relative to current folder + absPath = fullfile(pwd, pathIn); + end +end + end % pop_prepPipeline \ No newline at end of file diff --git a/PrepPipeline/prepReport.m b/PrepPipeline/prepReport.m index 97bdbb3..309d7ac 100644 --- a/PrepPipeline/prepReport.m +++ b/PrepPipeline/prepReport.m @@ -1,790 +1,790 @@ -function prepReport(EEG, summaryFile, consoleFID, relativeReportLocation) -%% Visualize the EEG output from the PREP processing pipeline. -% -% Calling directly: -% prepReport -% -% This helper reporting script expects that EEG will have an -% EEG.etc.noiseDetection structure containing the report. -% The reporting function appends a summary to the summary report. -% -% Usually the prepReport is called through the function: -% -% publishPrepReport -% - -%% Write data status and report header -reference = struct(); -version = ''; -fullInformation = false; -numbersPerRow = 10; -indent = ' '; - -if isfield(EEG, 'etc') && isfield(EEG.etc, 'noiseDetection') - noiseDetection = EEG.etc.noiseDetection; -else - error('prepReport:NoNoiseDetection', ... - 'PREP reporting relies on EEG.etc.noiseDetection which does not exist'); -end -if isfield(noiseDetection, 'reference') - reference = noiseDetection.reference; -end -if isfield(noiseDetection, 'version') - version = EEG.etc.noiseDetection.version; -end -if isfield(noiseDetection, 'fullReferenceInfo') - fullInformation = EEG.etc.noiseDetection.fullReferenceInfo; -end - -[channels, frames] = size(EEG.data); - -fprintf('Summary file path: %s\n', summaryFile) -fprintf('relative report location: %s\n', relativeReportLocation) -fprintf(consoleFID, '%s\nChannels: %d\nFrames: %d\n', ... - noiseDetection.name, channels, frames); -summaryHeader = [noiseDetection.name '[' ... - num2str(channels) ' channels, ' num2str(frames) ' frames]']; -summaryHeader = [summaryHeader ' Report details']; -writeSummaryHeader(summaryFile, summaryHeader); -originalChannelLabels = noiseDetection.originalChannelLabels; -currentChannelLabels = {EEG.chanlocs.labels}; -[~, iorig, ~] = ... - intersect(originalChannelLabels, currentChannelLabels); -currentChannelsInOriginal = sort(iorig); - -% Write overview status -[errorStatus, errors] = getErrors(noiseDetection); -writeSummaryHeader(summaryFile, errorStatus, 'h4'); -writeHtmlList(summaryFile, errors, 'both'); -fprintf(consoleFID, '%s\n', errorStatus); -writeTextList(consoleFID, errors); - -% Versions -writeSummaryHeader(summaryFile, ['Prep version:' version], 'h4'); -fprintf(consoleFID, 'Prep version: %s\n', version); - -% Events -summaryMsg = ['Data summary: sampling rate ' num2str(EEG.srate) 'Hz']; -writeSummaryHeader(summaryFile, summaryMsg, 'h4'); -fprintf(consoleFID, '%s\n', summaryMsg); -[summary, ~] = reportEvents(consoleFID, EEG); -writeHtmlList(summaryFile, summary, 'both'); - -% Interpolated channels for referencing -if isfield(noiseDetection, 'reference') - writeSummaryHeader(summaryFile, 'Interpolated channels', 'h4'); - removedChannels = getFieldIfExists(noiseDetection, 'removedChannelNumbers'); - interpolatedChannels = getFieldIfExists(noiseDetection, 'interpolatedChannelNumbers'); - stillNoisyChannels = getFieldIfExists(noiseDetection, 'stillNoisyChannelNumbers'); - summaryItem = {['Channels interpolated during reference: [' ... - num2str(interpolatedChannels) ']']; ... - ['Channels still noisy after reference: [' ... - num2str(stillNoisyChannels) ']']; ... - ['Channels removed during post-process: [' ... - num2str(removedChannels) ']' ]}; - writeHtmlList(summaryFile, summaryItem, 'both'); - fprintf(consoleFID, 'Channels interpolated during reference:\n'); - printList(consoleFID, interpolatedChannels, numbersPerRow, indent); - fprintf(consoleFID, 'Channels still noisy after reference:\n'); - printList(consoleFID, stillNoisyChannels, numbersPerRow, indent); - fprintf(consoleFID, 'Channels removed during post-process:\n'); - printList(consoleFID, removedChannels, numbersPerRow, indent); -end - -% Setup visualization parameters - -colors = [0, 0, 0; 0, 1, 0; 1, 0, 0]; -legendStrings = {'Original', 'Before interp' 'Final'}; -symbols = {'+', 'x', 'o'}; -scalpMapInterpolation = 'v4'; -darkElementColor = [0.5, 0.5, 0.5]; -headColor = [0.95, 0.95, 0.95]; -elementColor = [0, 0, 0]; - -%% Line noise removal step -writeSummaryHeader(summaryFile, 'Line noise removal summary', 'h4'); -summary = reportLineNoise(consoleFID, noiseDetection, numbersPerRow, indent); -writeHtmlList(summaryFile, summary, 'both'); - -%% Initial detrend for reference calculation -writeSummaryHeader(summaryFile, 'Detrend summary', 'h4'); -summary = reportDetrend(consoleFID, noiseDetection, numbersPerRow, indent); -writeHtmlList(summaryFile, summary, 'both'); - -%% Spectrum after line noise and detrend -if ~isfield(noiseDetection, 'lineNoise') - fprintf(consoleFID, 'Skipping line noise and detrend\n'); -else - lineChannels = noiseDetection.lineNoise.lineNoiseChannels; - [~, iorig, ~] = intersect(currentChannelsInOriginal, lineChannels); - actualLineChannels = sort(iorig); - channelLabels = {EEG.chanlocs(actualLineChannels).labels}; - tString = noiseDetection.name; - if isfield(noiseDetection, 'detrend') - detrend = noiseDetection.detrend; - detrendChannels = detrend.detrendChannels; - [~, iorig] = intersect(currentChannelsInOriginal, detrendChannels); - isort = sort(iorig); - detrend.detrendChannels = isort(:)'; - EEGNew = removeTrend(EEG, detrend); - else - EEGNew = EEG; - end - [~, ~, badSpectraChannels] = showSpectrum(EEGNew, channelLabels, ... - actualLineChannels, actualLineChannels, tString, 20); - clear EEGNew; - if ~isempty(badSpectraChannels) - badString = ['Channels with no spectra: ' getListString(badSpectraChannels)]; - fprintf(consoleFID, '%s\n', badString); - writeHtmlList(summaryFile, {badString}, 'both'); - end -end - -%% Referencing step -writeSummaryHeader(summaryFile, 'Reference summary', 'h4'); -summary = reportReference(consoleFID, reference, numbersPerRow, indent); -writeHtmlList(summaryFile, summary, 'both'); - -%% Robust channel deviation (referenced) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping robust channel deviation\n'); -else - noisyStatistics = reference.noisyStatistics; - showColorbar = true; - channelInformation = reference.channelInformation; - nosedir = channelInformation.nosedir; - channelLocations = reference.channelLocations; - - [referencedLocations, evaluationChannels, noiseLegendString]= ... - getReportChannelInformation(channelLocations, ... - noisyStatistics.evaluationChannels, noisyStatistics.noisyChannels); - - - interpolatedLocations = getReportChannelInformation(channelLocations, ... - noisyStatistics.evaluationChannels, reference.badChannels); - % Original locations - if ~isfield(reference, 'noisyStatisticsOriginal') || ... - isempty(reference.noisyStatisticsOriginal) - noisyStatisticsOriginal = noisyStatistics; - fprintf(consoleFID, 'No original statistics --- using final for both\n'); - else - noisyStatisticsOriginal = reference.noisyStatisticsOriginal; - end - % Original locations - if ~isfield(reference, 'noisyStatisticsBeforeInterpolation') || ... - isempty(reference.noisyStatisticsBeforeInterpolation) - noisyStatisticsBeforeInterpolation = noisyStatisticsOriginal; - fprintf(consoleFID, ... - 'No statistics before interpolation --- using original for both\n'); - else - noisyStatisticsBeforeInterpolation = ... - reference.noisyStatisticsBeforeInterpolation; - end - originalLocations = getReportChannelInformation(channelLocations, ... - noisyStatistics.evaluationChannels, noisyStatisticsOriginal.noisyChannels); - numberEvaluationChannels = length(evaluationChannels); - - tString = 'Robust channel deviation'; - dataReferenced = noisyStatistics.robustChannelDeviation; - dataReferenced = dataReferenced(evaluationChannels); - dataOriginal = noisyStatisticsOriginal.robustChannelDeviation; - dataOriginal = dataOriginal(evaluationChannels); - dataBeforeInterpolation = ... - noisyStatisticsBeforeInterpolation.robustChannelDeviation; - dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); - medRef = noisyStatistics.channelDeviationMedian; - sdnRef = noisyStatistics.channelDeviationSD; - - medOrig = noisyStatisticsOriginal.channelDeviationMedian; - sdnOrig = noisyStatisticsOriginal.channelDeviationSD; - - medInterp = noisyStatisticsBeforeInterpolation.channelDeviationMedian; - sdnInterp = noisyStatisticsBeforeInterpolation.channelDeviationSD; - - scale = max(max(abs(dataOriginal)), max(max(abs(dataBeforeInterpolation)), ... - max(abs(dataReferenced)))); - clim = [-scale, scale]; - fprintf(consoleFID, '\nNoisy channel legend: '); - for j = 1:length(noiseLegendString) - fprintf(consoleFID, '%s\n', noiseLegendString{j}); - end - fprintf(consoleFID, '\n\n'); - plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) -end - -%% Robust channel deviation (original) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping robust channel deviation (original)\n'); -else - plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) -end - -%% Robust channel deviation (interpolated) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping robust channel deviation (marking interpolated)\n'); -else - plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) -end - -%% Robust deviation window statistics -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping robust deviation window statistics\n'); -else - beforeDeviationLevels = noisyStatisticsOriginal.channelDeviations(evaluationChannels, :); - afterDeviationLevels = noisyStatistics.channelDeviations(evaluationChannels, :); - interpDeviationLevels = ... - noisyStatisticsBeforeInterpolation.channelDeviations(evaluationChannels, :); - beforeDeviation = (beforeDeviationLevels - medOrig)./sdnOrig; - afterDeviation = (afterDeviationLevels - medRef)./sdnRef; - interpDeviation = (interpDeviationLevels - medInterp)./sdnInterp; - medianDeviationsOrig = median(beforeDeviationLevels(:)); - sdDeviationsOrig = mad(beforeDeviationLevels(:), 1)*1.4826; - medianDeviationsRef = median(afterDeviationLevels(:)); - sdDeviationsRef = mad(afterDeviationLevels(:), 1)*1.4826; - thresholdName = 'Deviation score'; - theTitle = {char(noiseDetection.name); char([ thresholdName ' distribution'])}; - showCumulativeDistributions({beforeDeviation(:), interpDeviation(:), afterDeviation(:)}, ... - thresholdName, colors, theTitle, legendStrings, [-5, 5]); - beforeDeviationCounts = ... - sum(beforeDeviation >= noisyStatisticsOriginal.robustDeviationThreshold); - afterDeviationCounts = ... - sum(afterDeviation >= noisyStatistics.robustDeviationThreshold); - interpDeviationCounts = ... - sum(interpDeviation >= noisyStatisticsBeforeInterpolation.robustDeviationThreshold); - beforeTimeScale = (0:length(beforeDeviationCounts)-1)*noisyStatisticsOriginal.correlationWindowSeconds; - afterTimeScale = (0:length(afterDeviationCounts)-1)*noisyStatistics.correlationWindowSeconds; - interpTimeScale = (0:length(interpDeviationCounts)-1)* ... - noisyStatisticsBeforeInterpolation.correlationWindowSeconds; - fractionBefore = mean(beforeDeviationCounts)/numberEvaluationChannels; - fractionAfter = mean(afterDeviationCounts)/numberEvaluationChannels; - counts = {beforeDeviationCounts, interpDeviationCounts, afterDeviationCounts}; - timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; - showBadWindows(counts, timeScales, colors, symbols, ... - numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); - reports = cell(19, 1); - reports{1} = ['Deviation window statistics (over ' ... - num2str(size(noisyStatistics.channelDeviations, 2)) ' windows)']; - reports{2} = 'Large deviation channel fraction:'; - reports{3} = [indent ' [before=', ... - num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; - reports{4} = ['Median channel deviation: [before=', ... - num2str(noisyStatisticsOriginal.channelDeviationMedian) ... - ', after=' num2str(noisyStatistics.channelDeviationMedian) ']']; - reports{5} = ['SD channel deviation: [before=', ... - num2str(noisyStatisticsOriginal.channelDeviationSD) ... - ', after=' num2str(noisyStatistics.channelDeviationSD) ']']; - reports{6} = ['Max raw deviation level [before=', ... - num2str(max(beforeDeviationLevels(:))) ', after=' ... - num2str(max(afterDeviationLevels(:))) ']']; - reports{7} = ['Average fraction ' num2str(fractionBefore) ... - ' (' num2str(mean(beforeDeviationCounts)) ' channels)']; - reports{8}= [indent ' not meeting threshold before in each window']; - reports{9} = ['Average fraction ' num2str(fractionAfter) ... - ' (' num2str(mean(afterDeviationCounts)) ' channels)']; - reports{10} = [ indent ' not meeting threshold after in each window']; - quarterChannels = round(length(evaluationChannels)*0.25); - halfChannels = round(length(evaluationChannels)*0.5); - reports{11} = 'Windows with > 1/4 deviation channels:'; - reports{12} = [indent '[before=' ... - num2str(sum(beforeDeviationCounts > quarterChannels)) ... - ', after=' num2str(sum(afterDeviationCounts > quarterChannels)) ']']; - reports{13} = 'Windows with > 1/2 deviation channels:'; - reports{14} = [indent '[before=', ... - num2str(sum(beforeDeviationCounts > halfChannels)) ... - ', after=' num2str(sum(afterDeviationCounts > halfChannels)) ']']; - reports{15} = ['Median window deviations: [before=', ... - num2str(medianDeviationsOrig) ', after=' num2str(medianDeviationsRef) ']']; - reports{16} = ['SD window deviations: [before=', ... - num2str(sdDeviationsOrig) ', after=' num2str(sdDeviationsRef) ']']; - if isfield(noisyStatistics, 'dropOuts') - drops = sum(noisyStatistics.dropOuts, 2)'; - indexDrops = find(drops > 0); - dropList = [indexDrops; drops(indexDrops)]; - if ~isempty(indexDrops) > 0 - reportString = sprintf('%g[%g drops] ', dropList(:)'); - else - reportString = 'None'; - end - reports{17} = ['Channels with dropouts: ' reportString]; - end - fprintf(consoleFID, '%s:\n', reports{1}); - for k = 2:length(reports) - fprintf(consoleFID, '%s\n', reports{k}); - end - writeSummaryHeader(summaryFile, 'Deviation statistics summary', 'h4'); - writeHtmlList(summaryFile, {reports{1}, reports{2}, reports{3}}, 'both'); -end - -%% Median max abs correlation (referenced) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping median max absoluted correlation (referenced)\n'); -else - tString = 'Median max correlation'; - dataReferenced = noisyStatistics.medianMaxCorrelation; - dataReferenced = dataReferenced(evaluationChannels); - clim = [0, 1]; - plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) -end - -%% Median max abs correlation (original) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping median max abs correlation (original)\n'); -else - tString = 'Median max correlation'; - dataOriginal = noisyStatisticsOriginal.medianMaxCorrelation; - dataOriginal = dataOriginal(evaluationChannels); - clim = [0, 1]; - plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) -end - -%% Median max abs correlation (interpolated) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, ... - 'Skipping median max abs correlation (marking interpolated)\n'); -else - tString = 'Median max correlation'; - dataBeforeInterpolation = ... - noisyStatisticsBeforeInterpolation.medianMaxCorrelation; - dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); - clim = [0, 1]; - plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) -end - -%% Mean max abs correlation (referenced) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, ... - 'Skipping median max abs correlation (referenced)\n'); -else - tString = 'Mean max correlation'; - dataReferenced = mean(noisyStatistics.maximumCorrelations, 2); - dataReferenced = dataReferenced(evaluationChannels); - clim = [0, 1]; - plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) -end - -%% Mean max abs correlation (original) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping mean max abs correlation (original)\n'); -else - tString = 'Mean max correlation'; - dataOriginal = mean(noisyStatisticsOriginal.maximumCorrelations, 2); - dataOriginal = dataOriginal(evaluationChannels); - clim = [0, 1]; - plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) -end - -%% Mean max abs correlation (interpolated) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, ... - 'Skipping mean max abs correlation (marking interpolated)\n'); -else - tString = 'Mean max correlation'; - dataBeforeInterpolation = ... - mean(noisyStatisticsBeforeInterpolation.maximumCorrelations, 2); - dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); - clim = [0, 1]; - plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) -end - -%% Bad min max correlation fraction (referenced) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping bad min max correlation (referenced)\n'); -else - tString = 'Min max corr fraction'; - thresholdedCorrelations = noisyStatistics.maximumCorrelations ... - < noisyStatistics.correlationThreshold; - dataReferenced = mean(thresholdedCorrelations, 2); - dataReferenced = dataReferenced(evaluationChannels); - thresholdedCorrelations = noisyStatisticsOriginal.maximumCorrelations ... - < noisyStatisticsOriginal.correlationThreshold; - dataOriginal = mean(thresholdedCorrelations, 2); - dataOriginal = dataOriginal(evaluationChannels); - thresholdedCorrelations = noisyStatisticsBeforeInterpolation.maximumCorrelations ... - < noisyStatisticsBeforeInterpolation.correlationThreshold; - dataBeforeInterpolation = mean(thresholdedCorrelations, 2); - dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); - clim = [0, 2*reference.badTimeThreshold]; - plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... - showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(referenced)']) -end -%% Bad min max correlation fraction(original) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping median max abs correlation (original)\n'); -else - plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... - showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(original)']) -end - -%% Bad min max correlation fraction (interpolated) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, ... - 'Skipping bad min max correlation fraction (marking interpolated)\n'); -else - plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... - showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(marking interpolated)']) -end - -%% Correlation window statistics -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping correlation window statistics\n'); -else - beforeCorrelationLevels = ... - noisyStatisticsOriginal.maximumCorrelations(evaluationChannels, :); - afterCorrelationLevels = ... - noisyStatistics.maximumCorrelations(evaluationChannels, :); - interpCorrelationLevels = ... - noisyStatisticsBeforeInterpolation.maximumCorrelations(evaluationChannels, :); - thresholdName = 'Maximum correlation'; - theTitle = {char(noiseDetection.name); char([thresholdName ' distribution'])}; - showCumulativeDistributions( ... - {beforeCorrelationLevels(:),interpCorrelationLevels(:), afterCorrelationLevels(:)}, ... - thresholdName, colors, theTitle, legendStrings, [0, 1]); - beforeCorrelationCounts = sum(beforeCorrelationLevels <= ... - noisyStatisticsOriginal.correlationThreshold); - afterCorrelationCounts = sum(afterCorrelationLevels <= ... - noisyStatistics.correlationThreshold); - interpCorrelationCounts = sum(interpCorrelationLevels <= ... - noisyStatisticsBeforeInterpolation.correlationThreshold); - beforeTimeScale = (0:length(beforeCorrelationCounts)-1)* ... - noisyStatisticsOriginal.correlationWindowSeconds; - afterTimeScale = (0:length(afterCorrelationCounts)-1)* ... - noisyStatistics.correlationWindowSeconds; - interpTimeScale = (0:length(interpCorrelationCounts)-1)* ... - noisyStatisticsBeforeInterpolation.correlationWindowSeconds; - counts = {beforeCorrelationCounts, interpCorrelationCounts, afterCorrelationCounts}; - timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; - showBadWindows(counts, timeScales, colors, symbols, ... - numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); - fractionBefore = mean(beforeCorrelationCounts)/numberEvaluationChannels; - fractionAfter = mean(afterCorrelationCounts)/numberEvaluationChannels; - reports = cell(10, 1); - reports{1} = ['Max correlation window statistics (over ' ... - num2str(size(noisyStatistics.maximumCorrelations, 2)) ' windows)']; - reports{2} = ['Overall median maximum correlation [before=', ... - num2str(median(noisyStatisticsOriginal.medianMaxCorrelation(:))) ... - ', after=' num2str(median(noisyStatistics.medianMaxCorrelation(:))) ']']; - reports{3} = ['Low max correlation fraction [before=', ... - num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; - reports{4} = ['Minimum max correlation level [before=', ... - num2str(min(beforeCorrelationLevels(:))) ', after=' ... - num2str(min(afterCorrelationLevels(:))) ']']; - reports{5} = ['Average fraction ' num2str(fractionBefore) ... - ' (' num2str(mean(beforeCorrelationCounts)) ' channels):']; - reports{6} = [indent ' not meeting threshold before in each window']; - reports{7} = ['Average fraction ' num2str(fractionAfter) ... - ' (' num2str(mean(afterCorrelationCounts)) ' channels):']; - reports{8} = [indent ' not meeting threshold after in each window']; - quarterChannels = round(length(evaluationChannels)*0.25); - halfChannels = round(length(evaluationChannels)*0.5); - reports{9} = ['Windows with > 1/4 bad channels: [before=', ... - num2str(sum(beforeCorrelationCounts > quarterChannels)) ... - ', after=' num2str(sum(afterCorrelationCounts > quarterChannels)) ']']; - reports{10} = ['Windows with > 1/2 bad channels: [before=', ... - num2str(sum(beforeCorrelationCounts > halfChannels)) ... - ', after=' num2str(sum(afterCorrelationCounts > halfChannels)) ']']; - fprintf(consoleFID, '%s:\n', reports{1}); - for k = 2:length(reports) - fprintf(consoleFID, '%s\n', reports{k}); - end - writeSummaryHeader(summaryFile, 'Correlation statistics summary', 'h4'); - writeHtmlList(summaryFile, {reports{1}, reports{2}}, 'both'); -end - -%% Bad ransac fraction (referenced) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping bad ransac fraction (referenced)\n'); -else - tString = 'Ransac fraction failed'; - dataReferenced = noisyStatistics.ransacBadWindowFraction; - dataReferenced = dataReferenced(evaluationChannels); - clim = [0, 1]; - plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... - showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(referenced)']) -end - -%% Bad ransac fraction (original) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping bad ransac fraction (original)\n'); -else - dataOriginal = noisyStatisticsOriginal.ransacBadWindowFraction; - dataOriginal = dataOriginal(evaluationChannels); - plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... - showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(original)']) -end - -%% Bad ransac fraction (interpolated) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping bad ransac fraction (marking interpolated)\n'); -else - dataBeforeInterpolation = noisyStatisticsBeforeInterpolation.ransacBadWindowFraction; - dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); - plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... - showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(marking interpolated)']) -end - -%% Channels with poor ransac correlations -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping channels with poor ransac correlations\n'); -else - beforeRansacLevels = ... - noisyStatisticsOriginal.ransacCorrelations(evaluationChannels, :); - afterRansacLevels = ... - noisyStatistics.ransacCorrelations(evaluationChannels, :); - interpRansacLevels = ... - noisyStatisticsBeforeInterpolation.ransacCorrelations(evaluationChannels, :); - thresholdName = 'Ransac correlation'; - theTitle = {char([noiseDetection.name ': ' thresholdName ' distribution'])}; - showCumulativeDistributions({beforeRansacLevels(:), ... - interpRansacLevels(:), afterRansacLevels(:)}, ... - thresholdName, colors, theTitle, legendStrings, [0, 1]); - - beforeRansacCounts = sum(beforeRansacLevels <= ... - noisyStatisticsOriginal.ransacCorrelationThreshold); - afterRansacCounts = sum(afterRansacLevels <= ... - noisyStatistics.ransacCorrelationThreshold); - interpRansacCounts = sum(interpRansacLevels <= ... - noisyStatisticsBeforeInterpolation.ransacCorrelationThreshold); - beforeTimeScale = (0:length(beforeRansacCounts)-1)* ... - noisyStatisticsOriginal.ransacWindowSeconds; - afterTimeScale = (0:length(afterRansacCounts)-1)* ... - noisyStatistics.ransacWindowSeconds; - interpTimeScale = (0:length(interpRansacCounts)-1)* ... - noisyStatisticsBeforeInterpolation.ransacWindowSeconds; - counts = {beforeRansacCounts, interpRansacCounts, afterRansacCounts}; - timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; - showBadWindows(counts, timeScales, colors, symbols, ... - numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); - - fractionBefore = mean(beforeRansacCounts)/numberEvaluationChannels; - fractionAfter = mean(afterRansacCounts)/numberEvaluationChannels; - reports = cell(9, 0); - reports{1} = ['Ransac window statistics (over ' ... - num2str(size(afterRansacLevels, 2)) ' windows)']; - reports{2} = ['Low ransac channel fraction [before=', ... - num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; - reports{3} = ['Minimum ransac correlation [before=', ... - num2str(min(beforeRansacLevels(:))) ', after=' ... - num2str(min(afterRansacLevels(:))) ']']; - reports{4} = ['Average fraction ' num2str(fractionBefore) ... - ' (' num2str(mean(beforeRansacCounts)) ' channels):']; - reports{5} = [indent ' not meeting threshold before in each window']; - reports{6} = ['Average fraction ' num2str(fractionAfter) ... - ' (' num2str(mean(afterRansacCounts)) ' channels):']; - reports{7} = [indent ' not meeting threshold after in each window']; - quarterChannels = round(length(evaluationChannels)*0.25); - halfChannels = round(length(evaluationChannels)*0.5); - reports{8} = ['Windows with > 1/4 bad ransac channels: [before=', ... - num2str(sum(beforeRansacCounts > quarterChannels)) ... - ', after=' num2str(sum(afterRansacCounts > quarterChannels)) ']']; - reports{9} = ['Windows with > 1/2 bad ransac channels: [before=', ... - num2str(sum(beforeRansacCounts > halfChannels)) ... - ', after=' num2str(sum(afterRansacCounts > halfChannels)) ']']; - fprintf(consoleFID, '%s:\n', reports{1}); - for k = 2:length(reports) - fprintf(consoleFID, '%s\n', reports{k}); - end - writeSummaryHeader(summaryFile, 'Ransac statistics summary', 'h4'); - writeHtmlList(summaryFile, {reports{1}, reports{2}}, 'both'); -end -%% HF noise Z-score (referenced) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping HF noise Z-score (referenced)\n'); -else - tString = 'Z-score HF SNR'; - dataReferenced = noisyStatistics.zscoreHFNoise; - dataReferenced = dataReferenced(evaluationChannels); - dataOriginal = noisyStatisticsOriginal.zscoreHFNoise; - dataOriginal = dataOriginal(evaluationChannels); - dataBeforeInterpolation = noisyStatisticsBeforeInterpolation.zscoreHFNoise; - dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); - scale = max(max(abs(dataOriginal)), max(max(abs(dataReferenced)), ... - max(abs(dataBeforeInterpolation)))); - % scale = max(max(abs(dataOriginal), abs(dataReferenced))); - clim = [-scale, scale]; - plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) -end - - -%% HF noise Z-score (original) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping HF noise Z-score (original)\n'); -else - plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) -end - -%% HF noise Z-score (interpolated) -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping HF noise Z-score (marking interpolated)\n'); -else - plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... - showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) -end - -%% HF noise window stats -if isempty(reference) || ~fullInformation - fprintf(consoleFID, 'Skipping HF window stats\n'); -else - beforeNoiseLevels = noisyStatisticsOriginal.noiseLevels(evaluationChannels, :); - afterNoiseLevels = noisyStatistics.noiseLevels(evaluationChannels, :); - interpNoiseLevels = ... - noisyStatisticsBeforeInterpolation.noiseLevels(evaluationChannels, :); - medianNoiseOrig = median(beforeNoiseLevels(:)); - sdNoiseOrig = mad(beforeNoiseLevels(:), 1)*1.4826; - medianNoiseRef = median(afterNoiseLevels(:)); - sdNoiseRef = mad(afterNoiseLevels(:), 1)*1.4826; - medianNoiseInterp = median(interpNoiseLevels(:)); - sdNoiseInterp = mad(interpNoiseLevels(:), 1)*1.4826; - beforeNoise = (beforeNoiseLevels - medianNoiseOrig)./sdNoiseOrig; - afterNoise = (afterNoiseLevels - medianNoiseRef)./sdNoiseRef; - interpNoise = (interpNoiseLevels - medianNoiseInterp)./sdNoiseInterp; - thresholdName = 'HF noise'; - theTitle = {char(noiseDetection.name); [thresholdName ' HF noise distribution']}; - showCumulativeDistributions({beforeNoise(:), interpNoise(:), afterNoise(:)}, ... - thresholdName, colors, theTitle, legendStrings, [-5, 5]); - beforeNoiseCounts = sum(beforeNoise >= ... - noisyStatisticsOriginal.highFrequencyNoiseThreshold); - afterNoiseCounts = sum(afterNoise >= ... - noisyStatistics.highFrequencyNoiseThreshold); - interpNoiseCounts = sum(interpNoise >= ... - noisyStatisticsBeforeInterpolation.highFrequencyNoiseThreshold); - beforeTimeScale = (0:length(beforeNoiseCounts)-1)* ... - noisyStatisticsOriginal.correlationWindowSeconds; - afterTimeScale = (0:length(afterNoiseCounts)-1)* ... - noisyStatistics.correlationWindowSeconds; - interpTimeScale = (0:length(interpNoiseCounts)-1)* ... - noisyStatisticsBeforeInterpolation.correlationWindowSeconds; - counts = {beforeNoiseCounts, interpNoiseCounts, afterNoiseCounts}; - timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; - showBadWindows(counts, timeScales, colors, symbols, ... - numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); - - fractionBefore = mean(beforeNoiseCounts)/numberEvaluationChannels; - fractionAfter = mean(afterNoiseCounts)/numberEvaluationChannels; - reports = cell(17,0); - reports{1} = ['Noise window statistics (over ' ... - num2str(size(noisyStatistics.noiseLevels, 2)) ' windows)']; - reports{2} = 'Channel fraction with HF noise:'; - reports{3} = [indent '[before=', ... - num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; - reports{4} = ['Median noisiness: [before=', ... - num2str(noisyStatisticsOriginal.noisinessMedian) ... - ', after=' num2str(noisyStatistics.noisinessMedian) ']']; - reports{5} = ['SD noisiness: [before=', ... - num2str(noisyStatisticsOriginal.noisinessSD) ... - ', after=' num2str(noisyStatistics.noisinessSD) ']']; - reports{6} = ['Max HF noise levels [before=', ... - num2str(max(beforeNoiseLevels(:))) ', after=' ... - num2str(max(afterNoiseLevels(:))) ']']; - reports{7} = ['Average fraction ' num2str(fractionBefore) ... - ' (' num2str(mean(beforeNoiseCounts)) ' channels):']; - reports{8} = [indent ' not meeting threshold before in each window']; - reports{9} = ['Average fraction ' num2str(fractionAfter) ... - ' (' num2str(mean(afterNoiseCounts)) ' channels):']; - reports{10} = [indent ' not meeting threshold after in each window']; - reports{11} = [indent ' not meeting threshold after relative to before in each window']; - quarterChannels = round(length(evaluationChannels)*0.25); - halfChannels = round(length(evaluationChannels)*0.5); - reports{12} = 'Windows with > 1/4 HF channels:'; - reports{13} = [indent '[before=', ... - num2str(sum(beforeNoiseCounts > quarterChannels)) ... - ', after=' num2str(sum(afterNoiseCounts > quarterChannels)) ']']; - reports{14} = 'Windows with > 1/2 HF channels:'; - reports{15} = [indent '[before=', ... - num2str(sum(beforeNoiseCounts > halfChannels)) ... - ', after=' num2str(sum(afterNoiseCounts > halfChannels)) ']']; - reports{16} = ['Median window HF: [before=', ... - num2str(medianNoiseOrig) ', after=' num2str(medianNoiseRef) ']']; - reports{17} = ['SD window HF: [before=', ... - num2str(sdNoiseOrig) ', after=' num2str(sdNoiseRef) ']']; - fprintf(consoleFID, '%s:\n', reports{1}); - for k = 2:length(reports) - fprintf(consoleFID, '%s\n', reports{k}); - end - writeSummaryHeader(summaryFile, 'HF statistics summary', 'h4'); - writeHtmlList(summaryFile, {reports{1}, reports{2}, reports{3}}, 'both'); -end - - -%% Noisy average vs robust average reference -if isempty(reference) || ~fullInformation || ... - ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) - fprintf(consoleFID, 'Skipping noisy vs robust average reference\n'); -else - corrAverage = corr(reference.referenceSignal(:), ... - reference.referenceSignalOriginal(:)); - tString = { noiseDetection.name, ... - ['Comparison of reference signals (corr=' num2str(corrAverage) ')']}; - figure('Name', tString{2}) - plot(reference.referenceSignal, reference.referenceSignalOriginal, '.k'); - xlabel('Robust average reference') - ylabel('Ordinary average reference'); - title(tString, 'Interpreter', 'None'); - corrString = ['Ordinary vs robust average reference (unfiltered) correlation: ' ... - num2str(corrAverage)]; - writeSummaryHeader(summaryFile, corrString, 'h4'); -end - -%% Noisy and robust average reference by time -if isempty(reference) || ~fullInformation || ... - ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) - fprintf(consoleFID, 'Skipping noisy and robust average reference by time\n'); -else - tString = { noiseDetection.name, 'ordinary - robust average reference signals'}; - t = (0:length(reference.referenceSignal) - 1)/EEG.srate; - figure('Name', tString{2}) - plot(t, reference.referenceSignalOriginal - reference.referenceSignal, '.k'); - xlabel('Seconds') - ylabel('Original - robust'); - title(tString, 'Interpreter', 'None'); -end - -%% Noisy vs robust average reference (filtered) -if isempty(reference) || ~fullInformation || ... - ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) - fprintf(consoleFID, 'Skipping noisy vs robust average reference (filtered)\n'); -else - EEGTemp = eeg_emptyset(); - EEGTemp.nbchan = 2; - a = reference.referenceSignal; - b = reference.referenceSignalOriginal; - EEGTemp.pnts = length(a); - EEGTemp.data = [a(:)'; b(:)']; - EEGTemp.srate = EEG.srate; - EEGTemp = pop_eegfiltnew(EEGTemp, noiseDetection.detrend.detrendCutoff, []); - corrAverage = corr(EEGTemp.data(1, :)', EEGTemp.data(2, :)'); - tString = { noiseDetection.name, ... - ['Comparison of reference signals (corr=' num2str(corrAverage) ')']}; - figure('Name', tString{2}) - plot(EEGTemp.data(1, :), EEGTemp.data(2, :), '.k'); - xlabel('Robust average reference') - ylabel('Ordinary average reference'); - title(tString, 'Interpreter', 'None'); - corrString = ['Ordinary vs robust average reference (filtered) correlation: ' ... - num2str(corrAverage)]; - writeSummaryHeader(summaryFile, corrString, 'h4'); -end -%% Noisy minus robust average reference by time -if isempty(reference) || ~fullInformation || ... - ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) - fprintf(consoleFID, 'Skipping noisy minus robust average reference by time\n'); -else - tString = { noiseDetection.name, 'ordinary - robust average reference signals'}; - t = (0:length(EEGTemp.data(2, :)) - 1)/EEG.srate; - figure('Name', tString{2}) - plot(t, EEGTemp.data(2, :) - EEGTemp.data(1, :), '.k'); - xlabel('Seconds') - ylabel('Average - robust'); - title(tString, 'Interpreter', 'None'); -end +function prepReport(EEG, summaryFile, consoleFID, relativeReportLocation) +%% Visualize the EEG output from the PREP processing pipeline. +% +% Calling directly: +% prepReport +% +% This helper reporting script expects that EEG will have an +% EEG.etc.noiseDetection structure containing the report. +% The reporting function appends a summary to the summary report. +% +% Usually the prepReport is called through the function: +% +% publishPrepReport +% + +%% Write data status and report header +reference = struct(); +version = ''; +fullInformation = false; +numbersPerRow = 10; +indent = ' '; + +if isfield(EEG, 'etc') && isfield(EEG.etc, 'noiseDetection') + noiseDetection = EEG.etc.noiseDetection; +else + error('prepReport:NoNoiseDetection', ... + 'PREP reporting relies on EEG.etc.noiseDetection which does not exist'); +end +if isfield(noiseDetection, 'reference') + reference = noiseDetection.reference; +end +if isfield(noiseDetection, 'version') + version = EEG.etc.noiseDetection.version; +end +if isfield(noiseDetection, 'fullReferenceInfo') + fullInformation = EEG.etc.noiseDetection.fullReferenceInfo; +end + +[channels, frames] = size(EEG.data); + +fprintf('Summary file path: %s\n', summaryFile) +fprintf('relative report location: %s\n', relativeReportLocation) +fprintf(consoleFID, '%s\nChannels: %d\nFrames: %d\n', ... + noiseDetection.name, channels, frames); +summaryHeader = [noiseDetection.name '[' ... + num2str(channels) ' channels, ' num2str(frames) ' frames]']; +summaryHeader = [summaryHeader ' Report details']; +writeSummaryHeader(summaryFile, summaryHeader); +originalChannelLabels = noiseDetection.originalChannelLabels; +currentChannelLabels = {EEG.chanlocs.labels}; +[~, iorig, ~] = ... + intersect(originalChannelLabels, currentChannelLabels); +currentChannelsInOriginal = sort(iorig); + +% Write overview status +[errorStatus, errors] = getErrors(noiseDetection); +writeSummaryHeader(summaryFile, errorStatus, 'h4'); +writeHtmlList(summaryFile, errors, 'both'); +fprintf(consoleFID, '%s\n', errorStatus); +writeTextList(consoleFID, errors); + +% Versions +writeSummaryHeader(summaryFile, ['Prep version:' version], 'h4'); +fprintf(consoleFID, 'Prep version: %s\n', version); + +% Events +summaryMsg = ['Data summary: sampling rate ' num2str(EEG.srate) 'Hz']; +writeSummaryHeader(summaryFile, summaryMsg, 'h4'); +fprintf(consoleFID, '%s\n', summaryMsg); +[summary, ~] = reportEvents(consoleFID, EEG); +writeHtmlList(summaryFile, summary, 'both'); + +% Interpolated channels for referencing +if isfield(noiseDetection, 'reference') + writeSummaryHeader(summaryFile, 'Interpolated channels', 'h4'); + removedChannels = getFieldIfExists(noiseDetection, 'removedChannelNumbers'); + interpolatedChannels = getFieldIfExists(noiseDetection, 'interpolatedChannelNumbers'); + stillNoisyChannels = getFieldIfExists(noiseDetection, 'stillNoisyChannelNumbers'); + summaryItem = {['Channels interpolated during reference: [' ... + num2str(interpolatedChannels) ']']; ... + ['Channels still noisy after reference: [' ... + num2str(stillNoisyChannels) ']']; ... + ['Channels removed during post-process: [' ... + num2str(removedChannels) ']' ]}; + writeHtmlList(summaryFile, summaryItem, 'both'); + fprintf(consoleFID, 'Channels interpolated during reference:\n'); + printList(consoleFID, interpolatedChannels, numbersPerRow, indent); + fprintf(consoleFID, 'Channels still noisy after reference:\n'); + printList(consoleFID, stillNoisyChannels, numbersPerRow, indent); + fprintf(consoleFID, 'Channels removed during post-process:\n'); + printList(consoleFID, removedChannels, numbersPerRow, indent); +end + +% Setup visualization parameters + +colors = [0, 0, 0; 0, 1, 0; 1, 0, 0]; +legendStrings = {'Original', 'Before interp' 'Final'}; +symbols = {'+', 'x', 'o'}; +scalpMapInterpolation = 'v4'; +darkElementColor = [0.5, 0.5, 0.5]; +headColor = [0.95, 0.95, 0.95]; +elementColor = [0, 0, 0]; + +%% Line noise removal step +writeSummaryHeader(summaryFile, 'Line noise removal summary', 'h4'); +summary = reportLineNoise(consoleFID, noiseDetection, numbersPerRow, indent); +writeHtmlList(summaryFile, summary, 'both'); + +%% Initial detrend for reference calculation +writeSummaryHeader(summaryFile, 'Detrend summary', 'h4'); +summary = reportDetrend(consoleFID, noiseDetection, numbersPerRow, indent); +writeHtmlList(summaryFile, summary, 'both'); + +%% Spectrum after line noise and detrend +if ~isfield(noiseDetection, 'lineNoise') + fprintf(consoleFID, 'Skipping line noise and detrend\n'); +else + lineChannels = noiseDetection.lineNoise.lineNoiseChannels; + [~, iorig, ~] = intersect(currentChannelsInOriginal, lineChannels); + actualLineChannels = sort(iorig); + channelLabels = {EEG.chanlocs(actualLineChannels).labels}; + tString = noiseDetection.name; + if isfield(noiseDetection, 'detrend') + detrend = noiseDetection.detrend; + detrendChannels = detrend.detrendChannels; + [~, iorig] = intersect(currentChannelsInOriginal, detrendChannels); + isort = sort(iorig); + detrend.detrendChannels = isort(:)'; + EEGNew = removeTrend(EEG, detrend); + else + EEGNew = EEG; + end + [~, ~, badSpectraChannels] = showSpectrum(EEGNew, channelLabels, ... + actualLineChannels, actualLineChannels, tString, 20); + clear EEGNew; + if ~isempty(badSpectraChannels) + badString = ['Channels with no spectra: ' getListString(badSpectraChannels)]; + fprintf(consoleFID, '%s\n', badString); + writeHtmlList(summaryFile, {badString}, 'both'); + end +end + +%% Referencing step +writeSummaryHeader(summaryFile, 'Reference summary', 'h4'); +summary = reportReference(consoleFID, reference, numbersPerRow, indent); +writeHtmlList(summaryFile, summary, 'both'); + +%% Robust channel deviation (referenced) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping robust channel deviation\n'); +else + noisyStatistics = reference.noisyStatistics; + showColorbar = true; + channelInformation = reference.channelInformation; + nosedir = channelInformation.nosedir; + channelLocations = reference.channelLocations; + + [referencedLocations, evaluationChannels, noiseLegendString]= ... + getReportChannelInformation(channelLocations, ... + noisyStatistics.evaluationChannels, noisyStatistics.noisyChannels); + + + interpolatedLocations = getReportChannelInformation(channelLocations, ... + noisyStatistics.evaluationChannels, reference.badChannels); + % Original locations + if ~isfield(reference, 'noisyStatisticsOriginal') || ... + isempty(reference.noisyStatisticsOriginal) + noisyStatisticsOriginal = noisyStatistics; + fprintf(consoleFID, 'No original statistics --- using final for both\n'); + else + noisyStatisticsOriginal = reference.noisyStatisticsOriginal; + end + % Original locations + if ~isfield(reference, 'noisyStatisticsBeforeInterpolation') || ... + isempty(reference.noisyStatisticsBeforeInterpolation) + noisyStatisticsBeforeInterpolation = noisyStatisticsOriginal; + fprintf(consoleFID, ... + 'No statistics before interpolation --- using original for both\n'); + else + noisyStatisticsBeforeInterpolation = ... + reference.noisyStatisticsBeforeInterpolation; + end + originalLocations = getReportChannelInformation(channelLocations, ... + noisyStatistics.evaluationChannels, noisyStatisticsOriginal.noisyChannels); + numberEvaluationChannels = length(evaluationChannels); + + tString = 'Robust channel deviation'; + dataReferenced = noisyStatistics.robustChannelDeviation; + dataReferenced = dataReferenced(evaluationChannels); + dataOriginal = noisyStatisticsOriginal.robustChannelDeviation; + dataOriginal = dataOriginal(evaluationChannels); + dataBeforeInterpolation = ... + noisyStatisticsBeforeInterpolation.robustChannelDeviation; + dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); + medRef = noisyStatistics.channelDeviationMedian; + sdnRef = noisyStatistics.channelDeviationSD; + + medOrig = noisyStatisticsOriginal.channelDeviationMedian; + sdnOrig = noisyStatisticsOriginal.channelDeviationSD; + + medInterp = noisyStatisticsBeforeInterpolation.channelDeviationMedian; + sdnInterp = noisyStatisticsBeforeInterpolation.channelDeviationSD; + + scale = max(max(abs(dataOriginal)), max(max(abs(dataBeforeInterpolation)), ... + max(abs(dataReferenced)))); + clim = [-scale, scale]; + fprintf(consoleFID, '\nNoisy channel legend: '); + for j = 1:length(noiseLegendString) + fprintf(consoleFID, '%s\n', noiseLegendString{j}); + end + fprintf(consoleFID, '\n\n'); + plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) +end + +%% Robust channel deviation (original) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping robust channel deviation (original)\n'); +else + plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) +end + +%% Robust channel deviation (interpolated) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping robust channel deviation (marking interpolated)\n'); +else + plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) +end + +%% Robust deviation window statistics +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping robust deviation window statistics\n'); +else + beforeDeviationLevels = noisyStatisticsOriginal.channelDeviations(evaluationChannels, :); + afterDeviationLevels = noisyStatistics.channelDeviations(evaluationChannels, :); + interpDeviationLevels = ... + noisyStatisticsBeforeInterpolation.channelDeviations(evaluationChannels, :); + beforeDeviation = (beforeDeviationLevels - medOrig)./sdnOrig; + afterDeviation = (afterDeviationLevels - medRef)./sdnRef; + interpDeviation = (interpDeviationLevels - medInterp)./sdnInterp; + medianDeviationsOrig = median(beforeDeviationLevels(:)); + sdDeviationsOrig = mad(beforeDeviationLevels(:), 1)*1.4826; + medianDeviationsRef = median(afterDeviationLevels(:)); + sdDeviationsRef = mad(afterDeviationLevels(:), 1)*1.4826; + thresholdName = 'Deviation score'; + theTitle = {char(noiseDetection.name); char([ thresholdName ' distribution'])}; + showCumulativeDistributions({beforeDeviation(:), interpDeviation(:), afterDeviation(:)}, ... + thresholdName, colors, theTitle, legendStrings, [-5, 5]); + beforeDeviationCounts = ... + sum(beforeDeviation >= noisyStatisticsOriginal.robustDeviationThreshold); + afterDeviationCounts = ... + sum(afterDeviation >= noisyStatistics.robustDeviationThreshold); + interpDeviationCounts = ... + sum(interpDeviation >= noisyStatisticsBeforeInterpolation.robustDeviationThreshold); + beforeTimeScale = (0:length(beforeDeviationCounts)-1)*noisyStatisticsOriginal.correlationWindowSeconds; + afterTimeScale = (0:length(afterDeviationCounts)-1)*noisyStatistics.correlationWindowSeconds; + interpTimeScale = (0:length(interpDeviationCounts)-1)* ... + noisyStatisticsBeforeInterpolation.correlationWindowSeconds; + fractionBefore = mean(beforeDeviationCounts)/numberEvaluationChannels; + fractionAfter = mean(afterDeviationCounts)/numberEvaluationChannels; + counts = {beforeDeviationCounts, interpDeviationCounts, afterDeviationCounts}; + timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; + showBadWindows(counts, timeScales, colors, symbols, ... + numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); + reports = cell(19, 1); + reports{1} = ['Deviation window statistics (over ' ... + num2str(size(noisyStatistics.channelDeviations, 2)) ' windows)']; + reports{2} = 'Large deviation channel fraction:'; + reports{3} = [indent ' [before=', ... + num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; + reports{4} = ['Median channel deviation: [before=', ... + num2str(noisyStatisticsOriginal.channelDeviationMedian) ... + ', after=' num2str(noisyStatistics.channelDeviationMedian) ']']; + reports{5} = ['SD channel deviation: [before=', ... + num2str(noisyStatisticsOriginal.channelDeviationSD) ... + ', after=' num2str(noisyStatistics.channelDeviationSD) ']']; + reports{6} = ['Max raw deviation level [before=', ... + num2str(max(beforeDeviationLevels(:))) ', after=' ... + num2str(max(afterDeviationLevels(:))) ']']; + reports{7} = ['Average fraction ' num2str(fractionBefore) ... + ' (' num2str(mean(beforeDeviationCounts)) ' channels)']; + reports{8}= [indent ' not meeting threshold before in each window']; + reports{9} = ['Average fraction ' num2str(fractionAfter) ... + ' (' num2str(mean(afterDeviationCounts)) ' channels)']; + reports{10} = [ indent ' not meeting threshold after in each window']; + quarterChannels = round(length(evaluationChannels)*0.25); + halfChannels = round(length(evaluationChannels)*0.5); + reports{11} = 'Windows with > 1/4 deviation channels:'; + reports{12} = [indent '[before=' ... + num2str(sum(beforeDeviationCounts > quarterChannels)) ... + ', after=' num2str(sum(afterDeviationCounts > quarterChannels)) ']']; + reports{13} = 'Windows with > 1/2 deviation channels:'; + reports{14} = [indent '[before=', ... + num2str(sum(beforeDeviationCounts > halfChannels)) ... + ', after=' num2str(sum(afterDeviationCounts > halfChannels)) ']']; + reports{15} = ['Median window deviations: [before=', ... + num2str(medianDeviationsOrig) ', after=' num2str(medianDeviationsRef) ']']; + reports{16} = ['SD window deviations: [before=', ... + num2str(sdDeviationsOrig) ', after=' num2str(sdDeviationsRef) ']']; + if isfield(noisyStatistics, 'dropOuts') + drops = sum(noisyStatistics.dropOuts, 2)'; + indexDrops = find(drops > 0); + dropList = [indexDrops; drops(indexDrops)]; + if ~isempty(indexDrops) > 0 + reportString = sprintf('%g[%g drops] ', dropList(:)'); + else + reportString = 'None'; + end + reports{17} = ['Channels with dropouts: ' reportString]; + end + fprintf(consoleFID, '%s:\n', reports{1}); + for k = 2:length(reports) + fprintf(consoleFID, '%s\n', reports{k}); + end + writeSummaryHeader(summaryFile, 'Deviation statistics summary', 'h4'); + writeHtmlList(summaryFile, {reports{1}, reports{2}, reports{3}}, 'both'); +end + +%% Median max abs correlation (referenced) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping median max absoluted correlation (referenced)\n'); +else + tString = 'Median max correlation'; + dataReferenced = noisyStatistics.medianMaxCorrelation; + dataReferenced = dataReferenced(evaluationChannels); + clim = [0, 1]; + plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) +end + +%% Median max abs correlation (original) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping median max abs correlation (original)\n'); +else + tString = 'Median max correlation'; + dataOriginal = noisyStatisticsOriginal.medianMaxCorrelation; + dataOriginal = dataOriginal(evaluationChannels); + clim = [0, 1]; + plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) +end + +%% Median max abs correlation (interpolated) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, ... + 'Skipping median max abs correlation (marking interpolated)\n'); +else + tString = 'Median max correlation'; + dataBeforeInterpolation = ... + noisyStatisticsBeforeInterpolation.medianMaxCorrelation; + dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); + clim = [0, 1]; + plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) +end + +%% Mean max abs correlation (referenced) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, ... + 'Skipping median max abs correlation (referenced)\n'); +else + tString = 'Mean max correlation'; + dataReferenced = mean(noisyStatistics.maximumCorrelations, 2); + dataReferenced = dataReferenced(evaluationChannels); + clim = [0, 1]; + plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) +end + +%% Mean max abs correlation (original) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping mean max abs correlation (original)\n'); +else + tString = 'Mean max correlation'; + dataOriginal = mean(noisyStatisticsOriginal.maximumCorrelations, 2); + dataOriginal = dataOriginal(evaluationChannels); + clim = [0, 1]; + plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) +end + +%% Mean max abs correlation (interpolated) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, ... + 'Skipping mean max abs correlation (marking interpolated)\n'); +else + tString = 'Mean max correlation'; + dataBeforeInterpolation = ... + mean(noisyStatisticsBeforeInterpolation.maximumCorrelations, 2); + dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); + clim = [0, 1]; + plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) +end + +%% Bad min max correlation fraction (referenced) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping bad min max correlation (referenced)\n'); +else + tString = 'Min max corr fraction'; + thresholdedCorrelations = noisyStatistics.maximumCorrelations ... + < noisyStatistics.correlationThreshold; + dataReferenced = mean(thresholdedCorrelations, 2); + dataReferenced = dataReferenced(evaluationChannels); + thresholdedCorrelations = noisyStatisticsOriginal.maximumCorrelations ... + < noisyStatisticsOriginal.correlationThreshold; + dataOriginal = mean(thresholdedCorrelations, 2); + dataOriginal = dataOriginal(evaluationChannels); + thresholdedCorrelations = noisyStatisticsBeforeInterpolation.maximumCorrelations ... + < noisyStatisticsBeforeInterpolation.correlationThreshold; + dataBeforeInterpolation = mean(thresholdedCorrelations, 2); + dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); + clim = [0, 2*reference.badTimeThreshold]; + plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... + showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(referenced)']) +end +%% Bad min max correlation fraction(original) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping median max abs correlation (original)\n'); +else + plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... + showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(original)']) +end + +%% Bad min max correlation fraction (interpolated) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, ... + 'Skipping bad min max correlation fraction (marking interpolated)\n'); +else + plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... + showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(marking interpolated)']) +end + +%% Correlation window statistics +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping correlation window statistics\n'); +else + beforeCorrelationLevels = ... + noisyStatisticsOriginal.maximumCorrelations(evaluationChannels, :); + afterCorrelationLevels = ... + noisyStatistics.maximumCorrelations(evaluationChannels, :); + interpCorrelationLevels = ... + noisyStatisticsBeforeInterpolation.maximumCorrelations(evaluationChannels, :); + thresholdName = 'Maximum correlation'; + theTitle = {char(noiseDetection.name); char([thresholdName ' distribution'])}; + showCumulativeDistributions( ... + {beforeCorrelationLevels(:),interpCorrelationLevels(:), afterCorrelationLevels(:)}, ... + thresholdName, colors, theTitle, legendStrings, [0, 1]); + beforeCorrelationCounts = sum(beforeCorrelationLevels <= ... + noisyStatisticsOriginal.correlationThreshold); + afterCorrelationCounts = sum(afterCorrelationLevels <= ... + noisyStatistics.correlationThreshold); + interpCorrelationCounts = sum(interpCorrelationLevels <= ... + noisyStatisticsBeforeInterpolation.correlationThreshold); + beforeTimeScale = (0:length(beforeCorrelationCounts)-1)* ... + noisyStatisticsOriginal.correlationWindowSeconds; + afterTimeScale = (0:length(afterCorrelationCounts)-1)* ... + noisyStatistics.correlationWindowSeconds; + interpTimeScale = (0:length(interpCorrelationCounts)-1)* ... + noisyStatisticsBeforeInterpolation.correlationWindowSeconds; + counts = {beforeCorrelationCounts, interpCorrelationCounts, afterCorrelationCounts}; + timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; + showBadWindows(counts, timeScales, colors, symbols, ... + numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); + fractionBefore = mean(beforeCorrelationCounts)/numberEvaluationChannels; + fractionAfter = mean(afterCorrelationCounts)/numberEvaluationChannels; + reports = cell(10, 1); + reports{1} = ['Max correlation window statistics (over ' ... + num2str(size(noisyStatistics.maximumCorrelations, 2)) ' windows)']; + reports{2} = ['Overall median maximum correlation [before=', ... + num2str(median(noisyStatisticsOriginal.medianMaxCorrelation(:))) ... + ', after=' num2str(median(noisyStatistics.medianMaxCorrelation(:))) ']']; + reports{3} = ['Low max correlation fraction [before=', ... + num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; + reports{4} = ['Minimum max correlation level [before=', ... + num2str(min(beforeCorrelationLevels(:))) ', after=' ... + num2str(min(afterCorrelationLevels(:))) ']']; + reports{5} = ['Average fraction ' num2str(fractionBefore) ... + ' (' num2str(mean(beforeCorrelationCounts)) ' channels):']; + reports{6} = [indent ' not meeting threshold before in each window']; + reports{7} = ['Average fraction ' num2str(fractionAfter) ... + ' (' num2str(mean(afterCorrelationCounts)) ' channels):']; + reports{8} = [indent ' not meeting threshold after in each window']; + quarterChannels = round(length(evaluationChannels)*0.25); + halfChannels = round(length(evaluationChannels)*0.5); + reports{9} = ['Windows with > 1/4 bad channels: [before=', ... + num2str(sum(beforeCorrelationCounts > quarterChannels)) ... + ', after=' num2str(sum(afterCorrelationCounts > quarterChannels)) ']']; + reports{10} = ['Windows with > 1/2 bad channels: [before=', ... + num2str(sum(beforeCorrelationCounts > halfChannels)) ... + ', after=' num2str(sum(afterCorrelationCounts > halfChannels)) ']']; + fprintf(consoleFID, '%s:\n', reports{1}); + for k = 2:length(reports) + fprintf(consoleFID, '%s\n', reports{k}); + end + writeSummaryHeader(summaryFile, 'Correlation statistics summary', 'h4'); + writeHtmlList(summaryFile, {reports{1}, reports{2}}, 'both'); +end + +%% Bad ransac fraction (referenced) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping bad ransac fraction (referenced)\n'); +else + tString = 'Ransac fraction failed'; + dataReferenced = noisyStatistics.ransacBadWindowFraction; + dataReferenced = dataReferenced(evaluationChannels); + clim = [0, 1]; + plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... + showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(referenced)']) +end + +%% Bad ransac fraction (original) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping bad ransac fraction (original)\n'); +else + dataOriginal = noisyStatisticsOriginal.ransacBadWindowFraction; + dataOriginal = dataOriginal(evaluationChannels); + plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... + showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(original)']) +end + +%% Bad ransac fraction (interpolated) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping bad ransac fraction (marking interpolated)\n'); +else + dataBeforeInterpolation = noisyStatisticsBeforeInterpolation.ransacBadWindowFraction; + dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); + plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... + showColorbar, headColor, darkElementColor, clim, nosedir, [tString '(marking interpolated)']) +end + +%% Channels with poor ransac correlations +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping channels with poor ransac correlations\n'); +else + beforeRansacLevels = ... + noisyStatisticsOriginal.ransacCorrelations(evaluationChannels, :); + afterRansacLevels = ... + noisyStatistics.ransacCorrelations(evaluationChannels, :); + interpRansacLevels = ... + noisyStatisticsBeforeInterpolation.ransacCorrelations(evaluationChannels, :); + thresholdName = 'Ransac correlation'; + theTitle = {char([noiseDetection.name ': ' thresholdName ' distribution'])}; + showCumulativeDistributions({beforeRansacLevels(:), ... + interpRansacLevels(:), afterRansacLevels(:)}, ... + thresholdName, colors, theTitle, legendStrings, [0, 1]); + + beforeRansacCounts = sum(beforeRansacLevels <= ... + noisyStatisticsOriginal.ransacCorrelationThreshold); + afterRansacCounts = sum(afterRansacLevels <= ... + noisyStatistics.ransacCorrelationThreshold); + interpRansacCounts = sum(interpRansacLevels <= ... + noisyStatisticsBeforeInterpolation.ransacCorrelationThreshold); + beforeTimeScale = (0:length(beforeRansacCounts)-1)* ... + noisyStatisticsOriginal.ransacWindowSeconds; + afterTimeScale = (0:length(afterRansacCounts)-1)* ... + noisyStatistics.ransacWindowSeconds; + interpTimeScale = (0:length(interpRansacCounts)-1)* ... + noisyStatisticsBeforeInterpolation.ransacWindowSeconds; + counts = {beforeRansacCounts, interpRansacCounts, afterRansacCounts}; + timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; + showBadWindows(counts, timeScales, colors, symbols, ... + numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); + + fractionBefore = mean(beforeRansacCounts)/numberEvaluationChannels; + fractionAfter = mean(afterRansacCounts)/numberEvaluationChannels; + reports = cell(9, 0); + reports{1} = ['Ransac window statistics (over ' ... + num2str(size(afterRansacLevels, 2)) ' windows)']; + reports{2} = ['Low ransac channel fraction [before=', ... + num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; + reports{3} = ['Minimum ransac correlation [before=', ... + num2str(min(beforeRansacLevels(:))) ', after=' ... + num2str(min(afterRansacLevels(:))) ']']; + reports{4} = ['Average fraction ' num2str(fractionBefore) ... + ' (' num2str(mean(beforeRansacCounts)) ' channels):']; + reports{5} = [indent ' not meeting threshold before in each window']; + reports{6} = ['Average fraction ' num2str(fractionAfter) ... + ' (' num2str(mean(afterRansacCounts)) ' channels):']; + reports{7} = [indent ' not meeting threshold after in each window']; + quarterChannels = round(length(evaluationChannels)*0.25); + halfChannels = round(length(evaluationChannels)*0.5); + reports{8} = ['Windows with > 1/4 bad ransac channels: [before=', ... + num2str(sum(beforeRansacCounts > quarterChannels)) ... + ', after=' num2str(sum(afterRansacCounts > quarterChannels)) ']']; + reports{9} = ['Windows with > 1/2 bad ransac channels: [before=', ... + num2str(sum(beforeRansacCounts > halfChannels)) ... + ', after=' num2str(sum(afterRansacCounts > halfChannels)) ']']; + fprintf(consoleFID, '%s:\n', reports{1}); + for k = 2:length(reports) + fprintf(consoleFID, '%s\n', reports{k}); + end + writeSummaryHeader(summaryFile, 'Ransac statistics summary', 'h4'); + writeHtmlList(summaryFile, {reports{1}, reports{2}}, 'both'); +end +%% HF noise Z-score (referenced) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping HF noise Z-score (referenced)\n'); +else + tString = 'Z-score HF SNR'; + dataReferenced = noisyStatistics.zscoreHFNoise; + dataReferenced = dataReferenced(evaluationChannels); + dataOriginal = noisyStatisticsOriginal.zscoreHFNoise; + dataOriginal = dataOriginal(evaluationChannels); + dataBeforeInterpolation = noisyStatisticsBeforeInterpolation.zscoreHFNoise; + dataBeforeInterpolation = dataBeforeInterpolation(evaluationChannels); + scale = max(max(abs(dataOriginal)), max(max(abs(dataReferenced)), ... + max(abs(dataBeforeInterpolation)))); + % scale = max(max(abs(dataOriginal), abs(dataReferenced))); + clim = [-scale, scale]; + plotScalpMap(dataReferenced, referencedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(referenced)']) +end + + +%% HF noise Z-score (original) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping HF noise Z-score (original)\n'); +else + plotScalpMap(dataOriginal, originalLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(original)']) +end + +%% HF noise Z-score (interpolated) +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping HF noise Z-score (marking interpolated)\n'); +else + plotScalpMap(dataBeforeInterpolation, interpolatedLocations, scalpMapInterpolation, ... + showColorbar, headColor, elementColor, clim, nosedir, [tString '(marking interpolated)']) +end + +%% HF noise window stats +if isempty(reference) || ~fullInformation + fprintf(consoleFID, 'Skipping HF window stats\n'); +else + beforeNoiseLevels = noisyStatisticsOriginal.noiseLevels(evaluationChannels, :); + afterNoiseLevels = noisyStatistics.noiseLevels(evaluationChannels, :); + interpNoiseLevels = ... + noisyStatisticsBeforeInterpolation.noiseLevels(evaluationChannels, :); + medianNoiseOrig = median(beforeNoiseLevels(:)); + sdNoiseOrig = mad(beforeNoiseLevels(:), 1)*1.4826; + medianNoiseRef = median(afterNoiseLevels(:)); + sdNoiseRef = mad(afterNoiseLevels(:), 1)*1.4826; + medianNoiseInterp = median(interpNoiseLevels(:)); + sdNoiseInterp = mad(interpNoiseLevels(:), 1)*1.4826; + beforeNoise = (beforeNoiseLevels - medianNoiseOrig)./sdNoiseOrig; + afterNoise = (afterNoiseLevels - medianNoiseRef)./sdNoiseRef; + interpNoise = (interpNoiseLevels - medianNoiseInterp)./sdNoiseInterp; + thresholdName = 'HF noise'; + theTitle = {char(noiseDetection.name); [thresholdName ' HF noise distribution']}; + showCumulativeDistributions({beforeNoise(:), interpNoise(:), afterNoise(:)}, ... + thresholdName, colors, theTitle, legendStrings, [-5, 5]); + beforeNoiseCounts = sum(beforeNoise >= ... + noisyStatisticsOriginal.highFrequencyNoiseThreshold); + afterNoiseCounts = sum(afterNoise >= ... + noisyStatistics.highFrequencyNoiseThreshold); + interpNoiseCounts = sum(interpNoise >= ... + noisyStatisticsBeforeInterpolation.highFrequencyNoiseThreshold); + beforeTimeScale = (0:length(beforeNoiseCounts)-1)* ... + noisyStatisticsOriginal.correlationWindowSeconds; + afterTimeScale = (0:length(afterNoiseCounts)-1)* ... + noisyStatistics.correlationWindowSeconds; + interpTimeScale = (0:length(interpNoiseCounts)-1)* ... + noisyStatisticsBeforeInterpolation.correlationWindowSeconds; + counts = {beforeNoiseCounts, interpNoiseCounts, afterNoiseCounts}; + timeScales = {beforeTimeScale, interpTimeScale, afterTimeScale}; + showBadWindows(counts, timeScales, colors, symbols, ... + numberEvaluationChannels, legendStrings, noiseDetection.name, thresholdName); + + fractionBefore = mean(beforeNoiseCounts)/numberEvaluationChannels; + fractionAfter = mean(afterNoiseCounts)/numberEvaluationChannels; + reports = cell(17,0); + reports{1} = ['Noise window statistics (over ' ... + num2str(size(noisyStatistics.noiseLevels, 2)) ' windows)']; + reports{2} = 'Channel fraction with HF noise:'; + reports{3} = [indent '[before=', ... + num2str(fractionBefore) ', after=' num2str(fractionAfter) ']']; + reports{4} = ['Median noisiness: [before=', ... + num2str(noisyStatisticsOriginal.noisinessMedian) ... + ', after=' num2str(noisyStatistics.noisinessMedian) ']']; + reports{5} = ['SD noisiness: [before=', ... + num2str(noisyStatisticsOriginal.noisinessSD) ... + ', after=' num2str(noisyStatistics.noisinessSD) ']']; + reports{6} = ['Max HF noise levels [before=', ... + num2str(max(beforeNoiseLevels(:))) ', after=' ... + num2str(max(afterNoiseLevels(:))) ']']; + reports{7} = ['Average fraction ' num2str(fractionBefore) ... + ' (' num2str(mean(beforeNoiseCounts)) ' channels):']; + reports{8} = [indent ' not meeting threshold before in each window']; + reports{9} = ['Average fraction ' num2str(fractionAfter) ... + ' (' num2str(mean(afterNoiseCounts)) ' channels):']; + reports{10} = [indent ' not meeting threshold after in each window']; + reports{11} = [indent ' not meeting threshold after relative to before in each window']; + quarterChannels = round(length(evaluationChannels)*0.25); + halfChannels = round(length(evaluationChannels)*0.5); + reports{12} = 'Windows with > 1/4 HF channels:'; + reports{13} = [indent '[before=', ... + num2str(sum(beforeNoiseCounts > quarterChannels)) ... + ', after=' num2str(sum(afterNoiseCounts > quarterChannels)) ']']; + reports{14} = 'Windows with > 1/2 HF channels:'; + reports{15} = [indent '[before=', ... + num2str(sum(beforeNoiseCounts > halfChannels)) ... + ', after=' num2str(sum(afterNoiseCounts > halfChannels)) ']']; + reports{16} = ['Median window HF: [before=', ... + num2str(medianNoiseOrig) ', after=' num2str(medianNoiseRef) ']']; + reports{17} = ['SD window HF: [before=', ... + num2str(sdNoiseOrig) ', after=' num2str(sdNoiseRef) ']']; + fprintf(consoleFID, '%s:\n', reports{1}); + for k = 2:length(reports) + fprintf(consoleFID, '%s\n', reports{k}); + end + writeSummaryHeader(summaryFile, 'HF statistics summary', 'h4'); + writeHtmlList(summaryFile, {reports{1}, reports{2}, reports{3}}, 'both'); +end + + +%% Noisy average vs robust average reference +if isempty(reference) || ~fullInformation || ... + ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) + fprintf(consoleFID, 'Skipping noisy vs robust average reference\n'); +else + corrAverage = corr(reference.referenceSignal(:), ... + reference.referenceSignalOriginal(:)); + tString = { noiseDetection.name, ... + ['Comparison of reference signals (corr=' num2str(corrAverage) ')']}; + figure('Name', tString{2}) + plot(reference.referenceSignal, reference.referenceSignalOriginal, '.k'); + xlabel('Robust average reference') + ylabel('Ordinary average reference'); + title(tString, 'Interpreter', 'None'); + corrString = ['Ordinary vs robust average reference (unfiltered) correlation: ' ... + num2str(corrAverage)]; + writeSummaryHeader(summaryFile, corrString, 'h4'); +end + +%% Noisy and robust average reference by time +if isempty(reference) || ~fullInformation || ... + ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) + fprintf(consoleFID, 'Skipping noisy and robust average reference by time\n'); +else + tString = { noiseDetection.name, 'ordinary - robust average reference signals'}; + t = (0:length(reference.referenceSignal) - 1)/EEG.srate; + figure('Name', tString{2}) + plot(t, reference.referenceSignalOriginal - reference.referenceSignal, '.k'); + xlabel('Seconds') + ylabel('Original - robust'); + title(tString, 'Interpreter', 'None'); +end + +%% Noisy vs robust average reference (filtered) +if isempty(reference) || ~fullInformation || ... + ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) + fprintf(consoleFID, 'Skipping noisy vs robust average reference (filtered)\n'); +else + EEGTemp = eeg_emptyset(); + EEGTemp.nbchan = 2; + a = reference.referenceSignal; + b = reference.referenceSignalOriginal; + EEGTemp.pnts = length(a); + EEGTemp.data = [a(:)'; b(:)']; + EEGTemp.srate = EEG.srate; + EEGTemp = pop_eegfiltnew(EEGTemp, noiseDetection.detrend.detrendCutoff, []); + corrAverage = corr(EEGTemp.data(1, :)', EEGTemp.data(2, :)'); + tString = { noiseDetection.name, ... + ['Comparison of reference signals (corr=' num2str(corrAverage) ')']}; + figure('Name', tString{2}) + plot(EEGTemp.data(1, :), EEGTemp.data(2, :), '.k'); + xlabel('Robust average reference') + ylabel('Ordinary average reference'); + title(tString, 'Interpreter', 'None'); + corrString = ['Ordinary vs robust average reference (filtered) correlation: ' ... + num2str(corrAverage)]; + writeSummaryHeader(summaryFile, corrString, 'h4'); +end +%% Noisy minus robust average reference by time +if isempty(reference) || ~fullInformation || ... + ~isfield(reference, 'referenceSignal') || isempty(reference.referenceSignal) + fprintf(consoleFID, 'Skipping noisy minus robust average reference by time\n'); +else + tString = { noiseDetection.name, 'ordinary - robust average reference signals'}; + t = (0:length(EEGTemp.data(2, :)) - 1)/EEG.srate; + figure('Name', tString{2}) + plot(t, EEGTemp.data(2, :) - EEGTemp.data(1, :), '.k'); + xlabel('Seconds') + ylabel('Average - robust'); + title(tString, 'Interpreter', 'None'); +end diff --git a/PrepPipeline/preplicense.txt b/PrepPipeline/preplicense.txt index 2234892..412e512 100644 --- a/PrepPipeline/preplicense.txt +++ b/PrepPipeline/preplicense.txt @@ -1,236 +1,236 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - ADDITIONAL NOTE - -The PREP Pipeline is designed and distributed for research purposes only and -should not be used for medical purposes. The authors accept no responsibility -for its use in this manner. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + ADDITIONAL NOTE + +The PREP Pipeline is designed and distributed for research purposes only and +should not be used for medical purposes. The authors accept no responsibility +for its use in this manner. diff --git a/PrepPipeline/publishPrepReport.m b/PrepPipeline/publishPrepReport.m index 70db1c5..2e6ef3b 100644 --- a/PrepPipeline/publishPrepReport.m +++ b/PrepPipeline/publishPrepReport.m @@ -1,134 +1,134 @@ -function [] = publishPrepReport(EEG, summaryFilePath, sessionFilePath, ... - consoleFID, publishOn) -% Create a published report from the PREP pipeline. -% -% Note: In addition to creating a report for the EEG, it appends a -% summary of the file to an existing summary file. This enables the -% function to be called successfully on a collection and creates a summary -% of the collection. -% -% Parameters: -% EEG EEGLAB structure with the EEG.etc.noiseDetection -% structure created by the PREP pipeline -% summaryFilePath File name including path of the summary file -% sessionFilePath File name including path of the individual report -% consoleID Open file descriptor for echoing output (usually 1 -% indication the Command Window). -% publishOn If true (default), report is published and -% figures are closed. If false, output and figures -% are displayed in the normal way. The figures -% are not closed. This option is useful when -% you want to manipulate the figures in some way. -% -% Output: -% If the publish option is on, this function will create a report -% for the EEG and will append a summary to a specified summary file. -% If the publish option is off, the function will just run the -% prepReport script. -% -% Author: Kay Robbins, UTSA, March 2015. -% -% -%% Handle the parameters - if (nargin < 4) - error('publishPrepReport:NotEnoughParameters', ... - ['Usage: publishPrepReport(EEG, summaryFilePath, ' ... - 'sessionFilePath, consoleFID, publishOn)']); - elseif nargin < 5 || isempty(publishOn) - publishOn = true; - end - -%% Make sure that EEGLAB is working in double precision - [backupOptionsFile, currentOptionsFile, warningsState] = setupForEEGLAB(); - finishup = onCleanup(@() cleanup(backupOptionsFile, currentOptionsFile, ... - warningsState)); - -%% Setup up files and assign variables needed for publish in base workspace - wrapperScriptName = 'prepReportWrapper'; - [summaryFolder, summaryName, summaryExt] = fileparts(summaryFilePath); - [sessionFolder, sessionName, sessionExt] = fileparts(sessionFilePath); - summaryReportLocation = fullfile(summaryFolder, [summaryName summaryExt]); - sessionReportLocation = fullfile(sessionFolder, [sessionName sessionExt]); - tempReportLocation = fullfile(sessionFolder, [wrapperScriptName '.pdf']); - relativeReportLocation = getRelativePath(summaryFolder, sessionFolder, sessionName, sessionExt); - - fprintf('Summary: %s session: %s\n', summaryFolder, sessionFolder); - fprintf('Relative report location %s \n', relativeReportLocation); - - if isempty(EEG) || ~isfield(EEG, 'etc') || ~isfield(EEG.etc, 'noiseDetection') - error('publishPrepReport:PrepNotRun', ... - ['EEG.etc must contain PREP informational structures to ' ... - 'run reports --- run PREP first']); - end - - if publishOn - % Save variables to temp file - dataPath = fullfile(tempdir, 'prepReportData.mat'); - save(dataPath, 'EEG', 'consoleFID', 'relativeReportLocation', 'summaryReportLocation'); - - % Write wrapper script - - wrapperPath = fullfile(tempdir, [wrapperScriptName '.m']); - fid = fopen(wrapperPath, 'w'); - fprintf(fid, 'load(''%s'');\n', dataPath); - fprintf(fid, 'summaryFile = fopen(summaryReportLocation, ''a+'', ''n'', ''UTF-8'');\n'); - fprintf(fid, 'prepReport(EEG, summaryFile, consoleFID, relativeReportLocation);\n'); - fprintf(fid, 'fclose(summaryFile);\n'); - fprintf(fid, 'clear consoleFID relativeReportLocation summaryReportLocation summaryFile tmpEEG;\n'); - fclose(fid); - - % Add tempdir to MATLAB path so publish can find the script - addpath(tempdir); - - % Set publish options - publish_options.outputDir = sessionFolder; - publish_options.maxWidth = 800; - publish_options.format = 'pdf'; - publish_options.showCode = false; - - % Publish report - publish(wrapperScriptName, publish_options); - - rmpath(tempdir); - movefile(tempReportLocation, sessionReportLocation); - close all; - delete(wrapperPath); - delete(dataPath); - else - summaryFile = fopen(summaryReportLocation, 'a+', 'n', 'UTF-8'); - if summaryFile == -1 - error('publishPrepReport:BadSummaryFile', ... - 'Failed to open summary file %s', summaryReportLocation); - end - prepReport(EEG, summaryFile, consoleFID, relativeReportLocation); - fclose(summaryFile); - clear consoleFID relativeReportLocation summaryReportLocation summaryFile tmpEEG; - end - -end - -function relativePath = getRelativePath(summaryFolder, sessionFolder, sessionName, sessionExt) - relativePath = relativize(getCanonicalPath(summaryFolder), ... - getCanonicalPath(sessionFolder)); - relativePath = getCanonicalPath(relativePath); - while true - relativePathNew = strrep(relativePath, '\\', '\'); - if length(relativePathNew) == length(relativePath) - break; - end - relativePath = relativePathNew; - end - relativePath = strrep(relativePath, '\', '/'); - relativePath = [relativePath sessionName sessionExt]; -end - -function canonicalPath = getCanonicalPath(canonicalPath) - if canonicalPath(end) ~= filesep - canonicalPath = [canonicalPath, filesep]; - end -end - -function cleanup(backupFile, currentFile, warningsState) - restoreEEGOptions(backupFile, currentFile); - warning(warningsState); -end +function [] = publishPrepReport(EEG, summaryFilePath, sessionFilePath, ... + consoleFID, publishOn) +% Create a published report from the PREP pipeline. +% +% Note: In addition to creating a report for the EEG, it appends a +% summary of the file to an existing summary file. This enables the +% function to be called successfully on a collection and creates a summary +% of the collection. +% +% Parameters: +% EEG EEGLAB structure with the EEG.etc.noiseDetection +% structure created by the PREP pipeline +% summaryFilePath File name including path of the summary file +% sessionFilePath File name including path of the individual report +% consoleID Open file descriptor for echoing output (usually 1 +% indication the Command Window). +% publishOn If true (default), report is published and +% figures are closed. If false, output and figures +% are displayed in the normal way. The figures +% are not closed. This option is useful when +% you want to manipulate the figures in some way. +% +% Output: +% If the publish option is on, this function will create a report +% for the EEG and will append a summary to a specified summary file. +% If the publish option is off, the function will just run the +% prepReport script. +% +% Author: Kay Robbins, UTSA, March 2015. +% +% +%% Handle the parameters + if (nargin < 4) + error('publishPrepReport:NotEnoughParameters', ... + ['Usage: publishPrepReport(EEG, summaryFilePath, ' ... + 'sessionFilePath, consoleFID, publishOn)']); + elseif nargin < 5 || isempty(publishOn) + publishOn = true; + end + +%% Make sure that EEGLAB is working in double precision + [backupOptionsFile, currentOptionsFile, warningsState] = setupForEEGLAB(); + finishup = onCleanup(@() cleanup(backupOptionsFile, currentOptionsFile, ... + warningsState)); + +%% Setup up files and assign variables needed for publish in base workspace + wrapperScriptName = 'prepReportWrapper'; + [summaryFolder, summaryName, summaryExt] = fileparts(summaryFilePath); + [sessionFolder, sessionName, sessionExt] = fileparts(sessionFilePath); + summaryReportLocation = fullfile(summaryFolder, [summaryName summaryExt]); + sessionReportLocation = fullfile(sessionFolder, [sessionName sessionExt]); + tempReportLocation = fullfile(sessionFolder, [wrapperScriptName '.pdf']); + relativeReportLocation = getRelativePath(summaryFolder, sessionFolder, sessionName, sessionExt); + + fprintf('Summary: %s session: %s\n', summaryFolder, sessionFolder); + fprintf('Relative report location %s \n', relativeReportLocation); + + if isempty(EEG) || ~isfield(EEG, 'etc') || ~isfield(EEG.etc, 'noiseDetection') + error('publishPrepReport:PrepNotRun', ... + ['EEG.etc must contain PREP informational structures to ' ... + 'run reports --- run PREP first']); + end + + if publishOn + % Save variables to temp file + dataPath = fullfile(tempdir, 'prepReportData.mat'); + save(dataPath, 'EEG', 'consoleFID', 'relativeReportLocation', 'summaryReportLocation'); + + % Write wrapper script + + wrapperPath = fullfile(tempdir, [wrapperScriptName '.m']); + fid = fopen(wrapperPath, 'w'); + fprintf(fid, 'load(''%s'');\n', dataPath); + fprintf(fid, 'summaryFile = fopen(summaryReportLocation, ''a+'', ''n'', ''UTF-8'');\n'); + fprintf(fid, 'prepReport(EEG, summaryFile, consoleFID, relativeReportLocation);\n'); + fprintf(fid, 'fclose(summaryFile);\n'); + fprintf(fid, 'clear consoleFID relativeReportLocation summaryReportLocation summaryFile tmpEEG;\n'); + fclose(fid); + + % Add tempdir to MATLAB path so publish can find the script + addpath(tempdir); + + % Set publish options + publish_options.outputDir = sessionFolder; + publish_options.maxWidth = 800; + publish_options.format = 'pdf'; + publish_options.showCode = false; + + % Publish report + publish(wrapperScriptName, publish_options); + + rmpath(tempdir); + movefile(tempReportLocation, sessionReportLocation); + close all; + delete(wrapperPath); + delete(dataPath); + else + summaryFile = fopen(summaryReportLocation, 'a+', 'n', 'UTF-8'); + if summaryFile == -1 + error('publishPrepReport:BadSummaryFile', ... + 'Failed to open summary file %s', summaryReportLocation); + end + prepReport(EEG, summaryFile, consoleFID, relativeReportLocation); + fclose(summaryFile); + clear consoleFID relativeReportLocation summaryReportLocation summaryFile tmpEEG; + end + +end + +function relativePath = getRelativePath(summaryFolder, sessionFolder, sessionName, sessionExt) + relativePath = relativize(getCanonicalPath(summaryFolder), ... + getCanonicalPath(sessionFolder)); + relativePath = getCanonicalPath(relativePath); + while true + relativePathNew = strrep(relativePath, '\\', '\'); + if length(relativePathNew) == length(relativePath) + break; + end + relativePath = relativePathNew; + end + relativePath = strrep(relativePath, '\', '/'); + relativePath = [relativePath sessionName sessionExt]; +end + +function canonicalPath = getCanonicalPath(canonicalPath) + if canonicalPath(end) ~= filesep + canonicalPath = [canonicalPath, filesep]; + end +end + +function cleanup(backupFile, currentFile, warningsState) + restoreEEGOptions(backupFile, currentFile); + warning(warningsState); +end diff --git a/PrepPipeline/utilities/blasstLineNoise.m b/PrepPipeline/utilities/blasstLineNoise.m index 5eda0fe..bc2e6fb 100644 --- a/PrepPipeline/utilities/blasstLineNoise.m +++ b/PrepPipeline/utilities/blasstLineNoise.m @@ -1,67 +1,67 @@ -function [signal, lineNoiseOut] = blasstLineNoise(signal, lineNoiseIn) -% Remove sharp spectral peaks from signal using Sleppian filters -% -% Usage: -% signal = cleanLineNoise(signal) -% [signal, lineNoiseOut] = hcleanLineNoise(signal, lineNoiseIn) -% -% Parameters: -% signal Structure with .data and .srate fields -% lineNoiseIn Input structure with fields described below -% -% Structure parameters (lineNoiseIn): -% fPassBand Frequency band used (default [0, Fs/2] = entire band) -% Fs Sampling frequency -% fScanBandWidth +/- bandwidth centered on each f0 to scan for significant -% lines (TM) -% lineFrequencies Line frequencies to be removed (default -% [60, 120, 180, 240, 300]) -% lineNoiseChannels Channels to remove line noise from (default -% size(data, 1)) -% maximumIterations Maximum times to iterate removal (default = 10) -% p Significance level cutoff (default = 0.01) -% pad FFT padding factor ( -1 corresponds to no padding, -% 0 corresponds to padding to next highest power of 2 -% etc.) (default is 0) -% pnts -% tapers Precomputed tapers from dpss -% taperBandWidth Taper bandwidth (default 2 Hz) -% taperWindowSize Taper sliding window length (default 4 sec) -% taperWindowStep Sliding window step size (default 4 sec = no overlap) -% tau Window overlap smoothing factor (default 100) -% -% This function is based on code originally written by Tim Mullen in a -% package called tmullen-cleanline which is based on the chronux_2 -% libraries. -% - -lineNoiseOut = lineNoiseIn; -%% Remove line frequencies that are greater than Nyquist frequencies -tooLarge = lineNoiseOut.lineFrequencies >= lineNoiseOut.Fs/2; -if any(tooLarge) - warning('cleanLineNoise:LineFrequenciesTooLarge', ... - 'Eliminating frequencies greater than half the sampling rate'); - lineNoiseOut.lineFrequencies(tooLarge) = []; - lineNoiseOut.lineFrequencies = squeeze(lineNoiseOut.lineFrequencies); -end - -%% Set up parameters for blassting the line noise -fRange = lineNoiseOut.fScanBandWidth; -frequencyRanges = repmat(fRange, length(lineNoiseOut.lineFrequencies)); -sRate = lineNoiseOut.Fs; -lineFrequencies = lineNoiseOut.lineFrequencies; -maxIterations = lineNoiseOut.maximumIterations; - -%% Perform the calculation for each channel separately -signal.data = double(signal.data); -chans = lineNoiseOut.lineNoiseChannels; -data = signal.data(chans, :); -parfor ch = 1:size(data, 1) - data(ch, :) = blasst(squeeze(data(ch, :)), lineFrequencies, ... - frequencyRanges, sRate, ... - 'MaximumIterations', maxIterations, ... - 'Verbose', 0); -end -signal.data(chans, :) = data; -clear data; - +function [signal, lineNoiseOut] = blasstLineNoise(signal, lineNoiseIn) +% Remove sharp spectral peaks from signal using Sleppian filters +% +% Usage: +% signal = cleanLineNoise(signal) +% [signal, lineNoiseOut] = hcleanLineNoise(signal, lineNoiseIn) +% +% Parameters: +% signal Structure with .data and .srate fields +% lineNoiseIn Input structure with fields described below +% +% Structure parameters (lineNoiseIn): +% fPassBand Frequency band used (default [0, Fs/2] = entire band) +% Fs Sampling frequency +% fScanBandWidth +/- bandwidth centered on each f0 to scan for significant +% lines (TM) +% lineFrequencies Line frequencies to be removed (default +% [60, 120, 180, 240, 300]) +% lineNoiseChannels Channels to remove line noise from (default +% size(data, 1)) +% maximumIterations Maximum times to iterate removal (default = 10) +% p Significance level cutoff (default = 0.01) +% pad FFT padding factor ( -1 corresponds to no padding, +% 0 corresponds to padding to next highest power of 2 +% etc.) (default is 0) +% pnts +% tapers Precomputed tapers from dpss +% taperBandWidth Taper bandwidth (default 2 Hz) +% taperWindowSize Taper sliding window length (default 4 sec) +% taperWindowStep Sliding window step size (default 4 sec = no overlap) +% tau Window overlap smoothing factor (default 100) +% +% This function is based on code originally written by Tim Mullen in a +% package called tmullen-cleanline which is based on the chronux_2 +% libraries. +% + +lineNoiseOut = lineNoiseIn; +%% Remove line frequencies that are greater than Nyquist frequencies +tooLarge = lineNoiseOut.lineFrequencies >= lineNoiseOut.Fs/2; +if any(tooLarge) + warning('cleanLineNoise:LineFrequenciesTooLarge', ... + 'Eliminating frequencies greater than half the sampling rate'); + lineNoiseOut.lineFrequencies(tooLarge) = []; + lineNoiseOut.lineFrequencies = squeeze(lineNoiseOut.lineFrequencies); +end + +%% Set up parameters for blassting the line noise +fRange = lineNoiseOut.fScanBandWidth; +frequencyRanges = repmat(fRange, length(lineNoiseOut.lineFrequencies)); +sRate = lineNoiseOut.Fs; +lineFrequencies = lineNoiseOut.lineFrequencies; +maxIterations = lineNoiseOut.maximumIterations; + +%% Perform the calculation for each channel separately +signal.data = double(signal.data); +chans = lineNoiseOut.lineNoiseChannels; +data = signal.data(chans, :); +parfor ch = 1:size(data, 1) + data(ch, :) = blasst(squeeze(data(ch, :)), lineFrequencies, ... + frequencyRanges, sRate, ... + 'MaximumIterations', maxIterations, ... + 'Verbose', 0); +end +signal.data(chans, :) = data; +clear data; + diff --git a/PrepPipeline/utilities/cleanLineNoise.m b/PrepPipeline/utilities/cleanLineNoise.m index 4c6e48f..f7bcb88 100644 --- a/PrepPipeline/utilities/cleanLineNoise.m +++ b/PrepPipeline/utilities/cleanLineNoise.m @@ -1,63 +1,63 @@ -function [signal, lineNoiseOut] = cleanLineNoise(signal, lineNoiseIn) -% Remove sharp spectral peaks from signal using Sleppian filters -% -% Usage: -% signal = cleanLineNoise(signal) -% [signal, lineNoiseOut] = hcleanLineNoise(signal, lineNoiseIn) -% -% Parameters: -% signal Structure with .data and .srate fields -% lineNoiseIn Input structure with fields described below -% -% Structure parameters (lineNoiseIn): -% fPassBand Frequency band used (default [0, Fs/2] = entire band) -% Fs Sampling frequency -% fScanBandWidth +/- bandwidth centered on each f0 to scan for significant -% lines (TM) -% lineFrequencies Line frequencies to be removed (default -% [60, 120, 180, 240, 300]) -% lineNoiseChannels Channels to remove line noise from (default -% size(data, 1)) -% maximumIterations Maximum times to iterate removal (default = 10) -% p Significance level cutoff (default = 0.01) -% pad FFT padding factor ( -1 corresponds to no padding, -% 0 corresponds to padding to next highest power of 2 -% etc.) (default is 0) -% pnts -% tapers Precomputed tapers from dpss -% taperBandWidth Taper bandwidth (default 2 Hz) -% taperWindowSize Taper sliding window length (default 4 sec) -% taperWindowStep Sliding window step size (default 4 sec = no overlap) -% tau Window overlap smoothing factor (default 100) -% -% This function is based on code originally written by Tim Mullen in a -% package called tmullen-cleanline which is based on the chronux_2 -% libraries. -% - -lineNoiseOut = lineNoiseIn; -%% Remove line frequencies that are greater than Nyquist frequencies -tooLarge = lineNoiseOut.lineFrequencies >= lineNoiseOut.Fs/2; -if any(tooLarge) - warning('cleanLineNoise:LineFrequenciesTooLarge', ... - 'Eliminating frequencies greater than half the sampling rate'); - lineNoiseOut.lineFrequencies(tooLarge) = []; - lineNoiseOut.lineFrequencies = squeeze(lineNoiseOut.lineFrequencies); -end - -%% Set up multi-taper parameters -hbw = lineNoiseOut.taperBandWidth/2; % half-bandwidth -lineNoiseOut.taperTemplate = [hbw, lineNoiseOut.taperWindowSize, 1]; -Nwin = round(lineNoiseOut.Fs*lineNoiseOut.taperWindowSize); % number of samples in window -lineNoiseOut.tapers = checkTapers(lineNoiseOut.taperTemplate, Nwin, lineNoiseOut.Fs); - -%% Perform the calculation for each channel separately -signal.data = double(signal.data); -chans = lineNoiseOut.lineNoiseChannels; -data = signal.data(chans, :); -parfor ch = 1:size(data, 1) - data(ch, :) = removeLinesMovingWindow(squeeze(data(ch, :)), lineNoiseOut); -end -signal.data(chans, :) = data; -clear data; - +function [signal, lineNoiseOut] = cleanLineNoise(signal, lineNoiseIn) +% Remove sharp spectral peaks from signal using Sleppian filters +% +% Usage: +% signal = cleanLineNoise(signal) +% [signal, lineNoiseOut] = hcleanLineNoise(signal, lineNoiseIn) +% +% Parameters: +% signal Structure with .data and .srate fields +% lineNoiseIn Input structure with fields described below +% +% Structure parameters (lineNoiseIn): +% fPassBand Frequency band used (default [0, Fs/2] = entire band) +% Fs Sampling frequency +% fScanBandWidth +/- bandwidth centered on each f0 to scan for significant +% lines (TM) +% lineFrequencies Line frequencies to be removed (default +% [60, 120, 180, 240, 300]) +% lineNoiseChannels Channels to remove line noise from (default +% size(data, 1)) +% maximumIterations Maximum times to iterate removal (default = 10) +% p Significance level cutoff (default = 0.01) +% pad FFT padding factor ( -1 corresponds to no padding, +% 0 corresponds to padding to next highest power of 2 +% etc.) (default is 0) +% pnts +% tapers Precomputed tapers from dpss +% taperBandWidth Taper bandwidth (default 2 Hz) +% taperWindowSize Taper sliding window length (default 4 sec) +% taperWindowStep Sliding window step size (default 4 sec = no overlap) +% tau Window overlap smoothing factor (default 100) +% +% This function is based on code originally written by Tim Mullen in a +% package called tmullen-cleanline which is based on the chronux_2 +% libraries. +% + +lineNoiseOut = lineNoiseIn; +%% Remove line frequencies that are greater than Nyquist frequencies +tooLarge = lineNoiseOut.lineFrequencies >= lineNoiseOut.Fs/2; +if any(tooLarge) + warning('cleanLineNoise:LineFrequenciesTooLarge', ... + 'Eliminating frequencies greater than half the sampling rate'); + lineNoiseOut.lineFrequencies(tooLarge) = []; + lineNoiseOut.lineFrequencies = squeeze(lineNoiseOut.lineFrequencies); +end + +%% Set up multi-taper parameters +hbw = lineNoiseOut.taperBandWidth/2; % half-bandwidth +lineNoiseOut.taperTemplate = [hbw, lineNoiseOut.taperWindowSize, 1]; +Nwin = round(lineNoiseOut.Fs*lineNoiseOut.taperWindowSize); % number of samples in window +lineNoiseOut.tapers = checkTapers(lineNoiseOut.taperTemplate, Nwin, lineNoiseOut.Fs); + +%% Perform the calculation for each channel separately +signal.data = double(signal.data); +chans = lineNoiseOut.lineNoiseChannels; +data = signal.data(chans, :); +parfor ch = 1:size(data, 1) + data(ch, :) = removeLinesMovingWindow(squeeze(data(ch, :)), lineNoiseOut); +end +signal.data(chans, :) = data; +clear data; + diff --git a/PrepPipeline/utilities/findNoisyChannels.m b/PrepPipeline/utilities/findNoisyChannels.m index e91b406..0cdf5b2 100644 --- a/PrepPipeline/utilities/findNoisyChannels.m +++ b/PrepPipeline/utilities/findNoisyChannels.m @@ -1,388 +1,388 @@ -function noisyOut = findNoisyChannels(signal, noisyIn) -% Identify bad channels in EEG using a two-stage approach -% -% reference = findNoisyChannels(signal) -% reference = findNoisyChannels(signal, reference) -% -% First remove bad channels by amplitude, noise level, and correlation -% Apply ransac after these channels have been removed. -% -% Input parameters: -% signal - structure with srate, chanlocs, chaninfo, and data fields -% noisyIn - structure with input parameters -% -% Notes: the signal is assumed to be high-passed. Removing line noise -% is a good idea too. -% -% noisyIn: (fields are filled in on input if not present and propagated to output) -% name - name of the input file -% srate - sample rate in HZ -% samples - number of samples in the data -% evaluationChannels - a vector of channels to use -% channelLocations - a structure of EEG channel locations -% chaninfo - standard EEGLAB chaninfo (nose direction is relevant) -% chanlocs - standard EEGLAB chanlocs structure -% robustDeviationThreshold - z score cutoff for robust channel deviation -% highFrequencyNoiseThreshold - z score cutoff for SNR (signal above 50 Hz) -% correlationWindowSeconds - correlation window size in seconds (default = 1 sec) -% correlationThreshold - correlation below which window is bad (default = 0.4) -% badTimeThreshold - cutoff fraction of bad corr windows (default = 0.01) -% ransacSampleSize - samples for computing ransac (default = 50) -% ransacChannelFraction - fraction of channels for robust reconstruction (default = 0.25) -% ransacCorrelationThreshold - cutoff correlation for abnormal wrt neighbors(default = 0.75) -% ransacUnbrokenTime - cutoff fraction of time channel can have poor ransac predictability (default = 0.4) -% ransacWindowSeconds - correlation window for ransac (default = 5 sec) -% -% Output parameters (c channels, w windows): -% ransacPerformed - true if there were enough good channels to do ransac -% noisyChannels - list of identified bad channel numbers -% badChannelsFromCorrelation - list of bad channels identified by correlation -% badChannelsFromDeviation - list of bad channels identified by amplitude -% badChannelsFromHFNoise - list of bad channels identified by SNR -% badChannelsFromRansac - list of channels identified by ransac -% fractionBadCorrelationWindows - c x 1 vector with fraction of bad correlation windows -% robustChannelDeviation - c x 1 vector with robust measure of average channel deviation -% zscoreHFNoise - c x 1 vector with measure of channel noise level -% maximumCorrelations - w x c array with max window correlation -% ransacCorrelations = c x wr array with ransac correlations -% -% This function uses 4 methods for detecting bad channels after removing -% from consideration channels that have NaN data or channels that are -% identically constant. -% -% Method 1: too low or high amplitude. If the z score of robust -% channel deviation falls below robustDeviationThreshold, the channel is -% considered to be bad. -% Method 2: too low an SNR. If the z score of estimate of signal above -% 50 Hz to that below 50 Hz above highFrequencyNoiseThreshold, the channel -% is considered to be bad. -% -% Method 3: low correlation with other channels. Here correlationWindowSize is the window -% size over which the correlation is computed. If the maximum -% correlation of the channel to the other channels falls below -% correlationThreshold, the channel is considered bad in that window. -% If the fraction of bad correlation windows for a channel -% exceeds badTimeThreshold, the channel is marked as bad. -% -% After the channels from methods 2 and 3 are removed, method 4 is -% computed on the remaining signals -% -% Method 4: each channel is predicted using ransac interpolation based -% on a ransac fraction of the channels. If the correlation of -% the prediction to the actual behavior is too low for too -% long, the channel is marked as bad. -% -% Assumptions: -% - The signal is a structure of continuous data with data, srate, chanlocs, -% and chaninfo fields. -% - The signal.data has been high pass filtered. -% - No segments of the EEG data have been removed - -% Methods 1 and 4 are adapted from code by Christian Kothe and Methods 2 -% and 3 are adapted from code by Nima Bigdely-Shamlo -% -%% Check the incoming parameters -if nargin < 1 - error('findNoisyChannels:NotEnoughArguments', 'requires at least 1 argument'); -elseif isstruct(signal) && ~isfield(signal, 'data') - error('findNoisyChannels:NoDataField', 'requires a structure data field'); -elseif size(signal.data, 3) ~= 1 - error('findNoisyChannels:DataNotContinuous', 'data must be a 2D array'); -elseif nargin < 2 || ~exist('noisyIn', 'var') || isempty(noisyIn) - noisyIn = struct(); -end - -%% Set the defaults and initialize as needed -noisyOut = getNoisyStructure(); -defaults = getPrepDefaults(signal, 'reference'); -[noisyOut, errors] = checkPrepDefaults(noisyIn, noisyOut, defaults); -if ~isempty(errors) - error('findNoisyChannels:BadParameters', ['|' sprintf('%s|', errors{:})]); -end -%% Fix the channel locations -channelLocations = noisyOut.channelLocations; -evaluationChannels = sort(noisyOut.evaluationChannels); % Make sure channels are sorted -evaluationChannels = evaluationChannels(:)'; % Make sure row vector -noisyOut.evaluationChannels = evaluationChannels; -originalChannels = 1:size(signal.data, 1); - -%% Extract the data required -data = signal.data; -originalNumberChannels = size(data, 1); % Save the original channels -data = double(data(evaluationChannels, :))'; % Remove the unneeded channels -signalSize = size(data, 1); -correlationFrames = noisyOut.correlationWindowSeconds * signal.srate; -correlationWindow = 0:(correlationFrames - 1); -correlationOffsets = 1:correlationFrames:(signalSize-correlationFrames); -WCorrelation = length(correlationOffsets); -ransacFrames = noisyOut.ransacWindowSeconds*noisyOut.srate; -ransacWindow = 0:(ransacFrames - 1); -ransacOffsets = 1:ransacFrames:(signalSize-ransacFrames); -WRansac = length(ransacOffsets); -noisyOut.zscoreHFNoise = zeros(originalNumberChannels, 1); -noisyOut.noiseLevels = zeros(originalNumberChannels, WCorrelation); -noisyOut.maximumCorrelations = ones(originalNumberChannels, WCorrelation); -noisyOut.dropOuts = zeros(originalNumberChannels, WCorrelation); -noisyOut.correlationOffsets = correlationOffsets; -noisyOut.channelDeviations = zeros(originalNumberChannels, WCorrelation); -noisyOut.robustChannelDeviation = zeros(originalNumberChannels, 1); -noisyOut.ransacCorrelations = ones(originalNumberChannels, WRansac); -noisyOut.ransacOffsets = ransacOffsets; - -%% Detect constant or NaN channels and remove from consideration -nanChannelMask = sum(isnan(data), 1) > 0; -noSignalChannelMask = mad(data, 1, 1) < 10e-10 | std(data, 1, 1) < 10e-10; -noisyOut.noisyChannels.badChannelsFromNaNs = evaluationChannels(nanChannelMask); -noisyOut.noisyChannels.badChannelsFromNoData = evaluationChannels(noSignalChannelMask); -evaluationChannels = setdiff(evaluationChannels, ... - union(noisyOut.noisyChannels.badChannelsFromNaNs, ... - noisyOut.noisyChannels.badChannelsFromNoData)); -data = signal.data; -data = double(data(evaluationChannels, :))'; -[signalSize, numberChannels] = size(data); - -%% Method 1: Unusually high or low amplitude (using robust std) -channelDeviation = 0.7413 *iqr(data); % Robust estimate of SD -channelDeviationSD = 0.7413 * iqr(channelDeviation); -channelDeviationMedian = nanmedian(channelDeviation); -noisyOut.robustChannelDeviation(evaluationChannels) = ... - (channelDeviation - channelDeviationMedian) / channelDeviationSD; - -% Find channels with unusually high deviation -badChannelsFromDeviation = ... - abs(noisyOut.robustChannelDeviation) > ... - noisyOut.robustDeviationThreshold | ... - isnan(noisyOut.robustChannelDeviation); -badChannelsFromDeviation = originalChannels(badChannelsFromDeviation); -noisyOut.noisyChannels.badChannelsFromDeviation = badChannelsFromDeviation(:)'; -noisyOut.channelDeviationMedian = channelDeviationMedian; -noisyOut.channelDeviationSD = channelDeviationSD; - -%% Method 2: Compute the SNR (based on Christian Kothe's clean_channels) -% Note: RANSAC uses the filtered values X of the data -if noisyOut.srate > 100 - % Remove signal content above 50Hz and below 1 Hz - B = design_fir(100,[2*[0 45 50]/noisyOut.srate 1],[1 1 0 0]); - X = zeros(signalSize, numberChannels); - parfor k = 1:numberChannels % Could be changed to parfor - X(:,k) = filtfilt_fast(B, 1, data(:, k)); end - % Determine z-scored level of EM noise-to-signal ratio for each channel - noisiness = mad(data- X, 1)./mad(X, 1); - noisinessMedian = nanmedian(noisiness); - noisinessSD = mad(noisiness, 1)*1.4826; - zscoreHFNoiseTemp = (noisiness - noisinessMedian) ./ noisinessSD; - noiseMask = (zscoreHFNoiseTemp > noisyOut.highFrequencyNoiseThreshold) | ... - isnan(zscoreHFNoiseTemp); - % Remap channels to original numbering - badChannelsFromHFNoise = evaluationChannels(noiseMask); - noisyOut.noisyChannels.badChannelsFromHFNoise = badChannelsFromHFNoise(:)'; -else - X = data; - noisinessMedian = 0; - noisinessSD = 1; - zscoreHFNoiseTemp = zeros(numberChannels, 1); - noisyOut.noisyChannels.badChannelsFromHFNoise = []; -end - -% Remap the channels to original numbering for the zscoreHFNoise -noisyOut.zscoreHFNoise(evaluationChannels) = zscoreHFNoiseTemp; -noisyOut.noisinessMedian = noisinessMedian; -noisyOut.noisinessSD = noisinessSD; - -%% Method 3: Global correlation criteria (from Nima Bigdely-Shamlo) -channelCorrelations = ones(WCorrelation, numberChannels); -noiseLevels = zeros(WCorrelation, numberChannels); -channelDeviations = zeros(WCorrelation, numberChannels); -n = length(correlationWindow); -xWin = reshape(X(1:n*WCorrelation, :)', numberChannels, n, WCorrelation); -dataWin = reshape(data(1:n*WCorrelation, :)', numberChannels, n, WCorrelation); -parfor k = 1:WCorrelation - eegPortion = squeeze(xWin(:, :, k))'; - dataPortion = squeeze(dataWin(:, :, k))'; - windowCorrelation = corrcoef(eegPortion); - abs_corr = abs(windowCorrelation - diag(diag(windowCorrelation))); - channelCorrelations(k, :) = quantile(abs_corr, 0.98); - noiseLevels(k, :) = mad(dataPortion - eegPortion, 1)./mad(eegPortion, 1); - channelDeviations(k, :) = 0.7413 *iqr(dataPortion); -end -dropOuts = isnan(channelCorrelations) | isnan(noiseLevels); -channelCorrelations(dropOuts) = 0.0; -noiseLevels(dropOuts) = 0.0; -clear xWin; -clear dataWin; -noisyOut.maximumCorrelations(evaluationChannels, :) = channelCorrelations'; -noisyOut.noiseLevels(evaluationChannels, :) = noiseLevels'; -noisyOut.channelDeviations(evaluationChannels, :) = channelDeviations'; -noisyOut.dropOuts(evaluationChannels, :) = dropOuts'; -thresholdedCorrelations = ... - noisyOut.maximumCorrelations < noisyOut.correlationThreshold; -fractionBadCorrelationWindows = mean(thresholdedCorrelations, 2); -fractionBadDropOutWindows = mean(noisyOut.dropOuts, 2); - -% Remap channels to their original numbers -badChannelsFromCorrelation = find(fractionBadCorrelationWindows > noisyOut.badTimeThreshold); -noisyOut.noisyChannels.badChannelsFromCorrelation = badChannelsFromCorrelation(:)'; -badChannelsFromDropOuts = find(fractionBadDropOutWindows > noisyOut.badTimeThreshold); -noisyOut.noisyChannels.badChannelsFromDropOuts = badChannelsFromDropOuts(:)'; -noisyOut.medianMaxCorrelation = median(noisyOut.maximumCorrelations, 2); - -%% Bad so far by amplitude and correlation (take these out before doing ransac) -noisyChannels = union(noisyOut.noisyChannels.badChannelsFromDeviation, ... - union(noisyOut.noisyChannels.badChannelsFromCorrelation, ... - noisyOut.noisyChannels.badChannelsFromDropOuts)); - -%% Method 4: Ransac corelation (may not be performed) -% Setup for ransac (if a 2-stage algorithm, remove other bad channels first) -if noisyOut.ransacOff - noisyOut.ransacBadWindowFraction = 0; - noisyOut.ransacPerformed = false; -elseif isempty(channelLocations) - warning('findNoisyChannels:noChannelLocation', ... - 'ransac could not be computed because there were no channel locations'); - noisyOut.ransacBadWindowFraction = 0; - noisyOut.ransacPerformed = false; -else % Set up parameters and make sure enough good channels to proceed - [ransacChannels, idiff] = setdiff(evaluationChannels, noisyChannels); - X = X(:, idiff); - - % Calculate the parameters for ransac - ransacSubset = round(noisyOut.ransacChannelFraction*size(data, 2)); - if noisyOut.ransacUnbrokenTime < 0 - error('find_noisyChannels:BadUnbrokenParameter', ... - 'ransacUnbrokenTime must be greater than 0'); - elseif noisyOut.ransacUnbrokenTime < 1 - ransacUnbrokenFrames = signalSize*noisyOut.ransacUnbrokenTime; - else - ransacUnbrokenFrames = srate*noisyOut.ransacUnbrokenTime; - end - - nchanlocs = channelLocations(ransacChannels); - if length(nchanlocs) ~= size(nchanlocs, 2) - nchanlocs = nchanlocs'; - end - if length(nchanlocs) < ransacSubset + 1 || length(nchanlocs) < 3 || ... - ransacSubset < 2 - warning('find_noisyChannels:NotEnoughGoodChannels', ... - 'Too many channels have failed quality tests to perform ransac'); - noisyOut.ransacBadWindowFraction = 0; - noisyOut.ransacPerformed = false; - end -end - -if noisyOut.ransacPerformed - try - % Calculate all-channel reconstruction matrices from random channel subsets - locs = [cell2mat({nchanlocs.X}); cell2mat({nchanlocs.Y});cell2mat({nchanlocs.Z})]; - catch err - error('findNoisyChannels:NoXYZChannelLocations', ... - 'Must provide valid channel locations'); - end - if isempty(locs) || size(locs, 2) ~= length(ransacChannels) ... - || any(isnan(locs(:))) - error('find_noisyChannels:EmptyChannelLocations', ... - 'The signal chanlocs must have valid X, Y, and Z components'); - end - P = hlp_microcache('cleanchans', @calc_projector, locs, ... - noisyOut.ransacSampleSize, ransacSubset); - ransacCorrelationsT = zeros(length(locs), WRansac); - - % Calculate each channel's correlation to its RANSAC reconstruction for each window - n = length(ransacWindow); - m = length(ransacChannels); - p = noisyOut.ransacSampleSize; - Xwin = reshape(X(1:n*WRansac, :)', m, n, WRansac); - parfor k = 1:WRansac - ransacCorrelationsT(:, k) = ... - calculateRansacWindow(squeeze(Xwin(:, :, k))', P, n, m, p); - end - clear Xwin; - noisyOut.ransacCorrelations(ransacChannels, :) = ransacCorrelationsT; - flagged = noisyOut.ransacCorrelations < noisyOut.ransacCorrelationThreshold; - badChannelsFromRansac = find(sum(flagged, 2)*ransacFrames > ransacUnbrokenFrames)'; - noisyOut.noisyChannels.badChannelsFromRansac = badChannelsFromRansac(:)'; - noisyOut.ransacBadWindowFraction = sum(flagged, 2)/size(flagged, 2); -end - -% Combine bad channels detected from all methods -noisy = noisyOut.noisyChannels; -noisyOut.noisyChannels.badChannelsFromLowSNR = ... - intersect(noisy.badChannelsFromHFNoise, noisy.badChannelsFromCorrelation); -noisyChannels = union(noisyChannels, ... - union(union(noisy.badChannelsFromRansac, ... - noisy.badChannelsFromHFNoise), ... - union(noisy.badChannelsFromNaNs, ... - noisy.badChannelsFromNoData))); -noisyOut.noisyChannels.all = noisyChannels(:)'; -noisyOut.medianMaxCorrelation = median(noisyOut.maximumCorrelations, 2); - -%% Helper functions for findNoisyChannels -function P = calc_projector(locs, numberSamples, subsetSize) -% Calculate a bag of reconstruction matrices from random channel subsets - -[permutedLocations, subsets] = getRandomSubsets(locs, subsetSize, numberSamples); -randomSamples = cell(1, numberSamples); -parfor k = 1:numberSamples - tmp = zeros(size(locs, 2)); - slice = subsets(k, :); - tmp(slice, :) = real(spherical_interpolate(permutedLocations(:, :, k), locs))'; - randomSamples{k} = tmp; -end -P = horzcat(randomSamples{:}); - -function [permutedLocations, subsets] = getRandomSubsets(locs, subsetSize, numberSamples) - stream = RandStream('mt19937ar', 'Seed', 435656); - numberChannels = size(locs, 2); - permutedLocations = zeros(3, subsetSize, numberSamples); - subsets = zeros(numberSamples, subsetSize); - for k = 1:numberSamples - subset = randsample(1:numberChannels, subsetSize, stream); - subsets(k, :) = subset; - permutedLocations(:, :, k) = locs(:, subset); - end - -function Y = randsample(X, num, stream) -Y = zeros(1, num); -for k = 1:num - pick = round(1 + (length(X)-1).*rand(stream)); - Y(k) = X(pick); - X(pick) = []; -end - -function rX = calculateRansacWindow(XX, P, n, m, p) - YY = sort(reshape(XX*P, n, m, p),3); - YY = YY(:, :, round(end/2)); - rX = sum(XX.*YY)./(sqrt(sum(XX.^2)).*sqrt(sum(YY.^2))); - -function noisyOut = getNoisyStructure() - noisyOut = struct('srate', [], ... - 'samples', [], ... - 'evaluationChannels', [], ... - 'channelLocations', [], ... - 'robustDeviationThreshold', [], ... - 'highFrequencyNoiseThreshold', [], ... - 'correlationWindowSeconds', [], ... - 'correlationThreshold', [], ... - 'badTimeThreshold', [], ... - 'ransacSampleSize', [], ... - 'ransacChannelFraction', [], ... - 'ransacCorrelationThreshold', [], ... - 'ransacUnbrokenTime', [], ... - 'ransacWindowSeconds', [], ... - 'noisyChannels', getBadChannelStructure(), ... - 'ransacPerformed', true, ... - 'channelDeviationMedian', [], ... - 'channelDeviationSD', [], ... - 'channelDeviations', [], ... - 'robustChannelDeviation', [], ... - 'noisinessMedian', [], ... - 'noisinessSD', [], ... - 'zscoreHFNoise', [], ... - 'noiseLevels', [], ... - 'maximumCorrelations', [], ... - 'dropOuts', [], ... - 'medianMaxCorrelation', [], ... - 'correlationOffsets', [], ... - 'ransacCorrelations', [], ... - 'ransacOffsets', [], ... - 'ransacBadWindowFraction', []); - +function noisyOut = findNoisyChannels(signal, noisyIn) +% Identify bad channels in EEG using a two-stage approach +% +% reference = findNoisyChannels(signal) +% reference = findNoisyChannels(signal, reference) +% +% First remove bad channels by amplitude, noise level, and correlation +% Apply ransac after these channels have been removed. +% +% Input parameters: +% signal - structure with srate, chanlocs, chaninfo, and data fields +% noisyIn - structure with input parameters +% +% Notes: the signal is assumed to be high-passed. Removing line noise +% is a good idea too. +% +% noisyIn: (fields are filled in on input if not present and propagated to output) +% name - name of the input file +% srate - sample rate in HZ +% samples - number of samples in the data +% evaluationChannels - a vector of channels to use +% channelLocations - a structure of EEG channel locations +% chaninfo - standard EEGLAB chaninfo (nose direction is relevant) +% chanlocs - standard EEGLAB chanlocs structure +% robustDeviationThreshold - z score cutoff for robust channel deviation +% highFrequencyNoiseThreshold - z score cutoff for SNR (signal above 50 Hz) +% correlationWindowSeconds - correlation window size in seconds (default = 1 sec) +% correlationThreshold - correlation below which window is bad (default = 0.4) +% badTimeThreshold - cutoff fraction of bad corr windows (default = 0.01) +% ransacSampleSize - samples for computing ransac (default = 50) +% ransacChannelFraction - fraction of channels for robust reconstruction (default = 0.25) +% ransacCorrelationThreshold - cutoff correlation for abnormal wrt neighbors(default = 0.75) +% ransacUnbrokenTime - cutoff fraction of time channel can have poor ransac predictability (default = 0.4) +% ransacWindowSeconds - correlation window for ransac (default = 5 sec) +% +% Output parameters (c channels, w windows): +% ransacPerformed - true if there were enough good channels to do ransac +% noisyChannels - list of identified bad channel numbers +% badChannelsFromCorrelation - list of bad channels identified by correlation +% badChannelsFromDeviation - list of bad channels identified by amplitude +% badChannelsFromHFNoise - list of bad channels identified by SNR +% badChannelsFromRansac - list of channels identified by ransac +% fractionBadCorrelationWindows - c x 1 vector with fraction of bad correlation windows +% robustChannelDeviation - c x 1 vector with robust measure of average channel deviation +% zscoreHFNoise - c x 1 vector with measure of channel noise level +% maximumCorrelations - w x c array with max window correlation +% ransacCorrelations = c x wr array with ransac correlations +% +% This function uses 4 methods for detecting bad channels after removing +% from consideration channels that have NaN data or channels that are +% identically constant. +% +% Method 1: too low or high amplitude. If the z score of robust +% channel deviation falls below robustDeviationThreshold, the channel is +% considered to be bad. +% Method 2: too low an SNR. If the z score of estimate of signal above +% 50 Hz to that below 50 Hz above highFrequencyNoiseThreshold, the channel +% is considered to be bad. +% +% Method 3: low correlation with other channels. Here correlationWindowSize is the window +% size over which the correlation is computed. If the maximum +% correlation of the channel to the other channels falls below +% correlationThreshold, the channel is considered bad in that window. +% If the fraction of bad correlation windows for a channel +% exceeds badTimeThreshold, the channel is marked as bad. +% +% After the channels from methods 2 and 3 are removed, method 4 is +% computed on the remaining signals +% +% Method 4: each channel is predicted using ransac interpolation based +% on a ransac fraction of the channels. If the correlation of +% the prediction to the actual behavior is too low for too +% long, the channel is marked as bad. +% +% Assumptions: +% - The signal is a structure of continuous data with data, srate, chanlocs, +% and chaninfo fields. +% - The signal.data has been high pass filtered. +% - No segments of the EEG data have been removed + +% Methods 1 and 4 are adapted from code by Christian Kothe and Methods 2 +% and 3 are adapted from code by Nima Bigdely-Shamlo +% +%% Check the incoming parameters +if nargin < 1 + error('findNoisyChannels:NotEnoughArguments', 'requires at least 1 argument'); +elseif isstruct(signal) && ~isfield(signal, 'data') + error('findNoisyChannels:NoDataField', 'requires a structure data field'); +elseif size(signal.data, 3) ~= 1 + error('findNoisyChannels:DataNotContinuous', 'data must be a 2D array'); +elseif nargin < 2 || ~exist('noisyIn', 'var') || isempty(noisyIn) + noisyIn = struct(); +end + +%% Set the defaults and initialize as needed +noisyOut = getNoisyStructure(); +defaults = getPrepDefaults(signal, 'reference'); +[noisyOut, errors] = checkPrepDefaults(noisyIn, noisyOut, defaults); +if ~isempty(errors) + error('findNoisyChannels:BadParameters', ['|' sprintf('%s|', errors{:})]); +end +%% Fix the channel locations +channelLocations = noisyOut.channelLocations; +evaluationChannels = sort(noisyOut.evaluationChannels); % Make sure channels are sorted +evaluationChannels = evaluationChannels(:)'; % Make sure row vector +noisyOut.evaluationChannels = evaluationChannels; +originalChannels = 1:size(signal.data, 1); + +%% Extract the data required +data = signal.data; +originalNumberChannels = size(data, 1); % Save the original channels +data = double(data(evaluationChannels, :))'; % Remove the unneeded channels +signalSize = size(data, 1); +correlationFrames = noisyOut.correlationWindowSeconds * signal.srate; +correlationWindow = 0:(correlationFrames - 1); +correlationOffsets = 1:correlationFrames:(signalSize-correlationFrames); +WCorrelation = length(correlationOffsets); +ransacFrames = noisyOut.ransacWindowSeconds*noisyOut.srate; +ransacWindow = 0:(ransacFrames - 1); +ransacOffsets = 1:ransacFrames:(signalSize-ransacFrames); +WRansac = length(ransacOffsets); +noisyOut.zscoreHFNoise = zeros(originalNumberChannels, 1); +noisyOut.noiseLevels = zeros(originalNumberChannels, WCorrelation); +noisyOut.maximumCorrelations = ones(originalNumberChannels, WCorrelation); +noisyOut.dropOuts = zeros(originalNumberChannels, WCorrelation); +noisyOut.correlationOffsets = correlationOffsets; +noisyOut.channelDeviations = zeros(originalNumberChannels, WCorrelation); +noisyOut.robustChannelDeviation = zeros(originalNumberChannels, 1); +noisyOut.ransacCorrelations = ones(originalNumberChannels, WRansac); +noisyOut.ransacOffsets = ransacOffsets; + +%% Detect constant or NaN channels and remove from consideration +nanChannelMask = sum(isnan(data), 1) > 0; +noSignalChannelMask = mad(data, 1, 1) < 10e-10 | std(data, 1, 1) < 10e-10; +noisyOut.noisyChannels.badChannelsFromNaNs = evaluationChannels(nanChannelMask); +noisyOut.noisyChannels.badChannelsFromNoData = evaluationChannels(noSignalChannelMask); +evaluationChannels = setdiff(evaluationChannels, ... + union(noisyOut.noisyChannels.badChannelsFromNaNs, ... + noisyOut.noisyChannels.badChannelsFromNoData)); +data = signal.data; +data = double(data(evaluationChannels, :))'; +[signalSize, numberChannels] = size(data); + +%% Method 1: Unusually high or low amplitude (using robust std) +channelDeviation = 0.7413 *iqr(data); % Robust estimate of SD +channelDeviationSD = 0.7413 * iqr(channelDeviation); +channelDeviationMedian = nanmedian(channelDeviation); +noisyOut.robustChannelDeviation(evaluationChannels) = ... + (channelDeviation - channelDeviationMedian) / channelDeviationSD; + +% Find channels with unusually high deviation +badChannelsFromDeviation = ... + abs(noisyOut.robustChannelDeviation) > ... + noisyOut.robustDeviationThreshold | ... + isnan(noisyOut.robustChannelDeviation); +badChannelsFromDeviation = originalChannels(badChannelsFromDeviation); +noisyOut.noisyChannels.badChannelsFromDeviation = badChannelsFromDeviation(:)'; +noisyOut.channelDeviationMedian = channelDeviationMedian; +noisyOut.channelDeviationSD = channelDeviationSD; + +%% Method 2: Compute the SNR (based on Christian Kothe's clean_channels) +% Note: RANSAC uses the filtered values X of the data +if noisyOut.srate > 100 + % Remove signal content above 50Hz and below 1 Hz + B = design_fir(100,[2*[0 45 50]/noisyOut.srate 1],[1 1 0 0]); + X = zeros(signalSize, numberChannels); + parfor k = 1:numberChannels % Could be changed to parfor + X(:,k) = filtfilt_fast(B, 1, data(:, k)); end + % Determine z-scored level of EM noise-to-signal ratio for each channel + noisiness = mad(data- X, 1)./mad(X, 1); + noisinessMedian = nanmedian(noisiness); + noisinessSD = mad(noisiness, 1)*1.4826; + zscoreHFNoiseTemp = (noisiness - noisinessMedian) ./ noisinessSD; + noiseMask = (zscoreHFNoiseTemp > noisyOut.highFrequencyNoiseThreshold) | ... + isnan(zscoreHFNoiseTemp); + % Remap channels to original numbering + badChannelsFromHFNoise = evaluationChannels(noiseMask); + noisyOut.noisyChannels.badChannelsFromHFNoise = badChannelsFromHFNoise(:)'; +else + X = data; + noisinessMedian = 0; + noisinessSD = 1; + zscoreHFNoiseTemp = zeros(numberChannels, 1); + noisyOut.noisyChannels.badChannelsFromHFNoise = []; +end + +% Remap the channels to original numbering for the zscoreHFNoise +noisyOut.zscoreHFNoise(evaluationChannels) = zscoreHFNoiseTemp; +noisyOut.noisinessMedian = noisinessMedian; +noisyOut.noisinessSD = noisinessSD; + +%% Method 3: Global correlation criteria (from Nima Bigdely-Shamlo) +channelCorrelations = ones(WCorrelation, numberChannels); +noiseLevels = zeros(WCorrelation, numberChannels); +channelDeviations = zeros(WCorrelation, numberChannels); +n = length(correlationWindow); +xWin = reshape(X(1:n*WCorrelation, :)', numberChannels, n, WCorrelation); +dataWin = reshape(data(1:n*WCorrelation, :)', numberChannels, n, WCorrelation); +parfor k = 1:WCorrelation + eegPortion = squeeze(xWin(:, :, k))'; + dataPortion = squeeze(dataWin(:, :, k))'; + windowCorrelation = corrcoef(eegPortion); + abs_corr = abs(windowCorrelation - diag(diag(windowCorrelation))); + channelCorrelations(k, :) = quantile(abs_corr, 0.98); + noiseLevels(k, :) = mad(dataPortion - eegPortion, 1)./mad(eegPortion, 1); + channelDeviations(k, :) = 0.7413 *iqr(dataPortion); +end +dropOuts = isnan(channelCorrelations) | isnan(noiseLevels); +channelCorrelations(dropOuts) = 0.0; +noiseLevels(dropOuts) = 0.0; +clear xWin; +clear dataWin; +noisyOut.maximumCorrelations(evaluationChannels, :) = channelCorrelations'; +noisyOut.noiseLevels(evaluationChannels, :) = noiseLevels'; +noisyOut.channelDeviations(evaluationChannels, :) = channelDeviations'; +noisyOut.dropOuts(evaluationChannels, :) = dropOuts'; +thresholdedCorrelations = ... + noisyOut.maximumCorrelations < noisyOut.correlationThreshold; +fractionBadCorrelationWindows = mean(thresholdedCorrelations, 2); +fractionBadDropOutWindows = mean(noisyOut.dropOuts, 2); + +% Remap channels to their original numbers +badChannelsFromCorrelation = find(fractionBadCorrelationWindows > noisyOut.badTimeThreshold); +noisyOut.noisyChannels.badChannelsFromCorrelation = badChannelsFromCorrelation(:)'; +badChannelsFromDropOuts = find(fractionBadDropOutWindows > noisyOut.badTimeThreshold); +noisyOut.noisyChannels.badChannelsFromDropOuts = badChannelsFromDropOuts(:)'; +noisyOut.medianMaxCorrelation = median(noisyOut.maximumCorrelations, 2); + +%% Bad so far by amplitude and correlation (take these out before doing ransac) +noisyChannels = union(noisyOut.noisyChannels.badChannelsFromDeviation, ... + union(noisyOut.noisyChannels.badChannelsFromCorrelation, ... + noisyOut.noisyChannels.badChannelsFromDropOuts)); + +%% Method 4: Ransac corelation (may not be performed) +% Setup for ransac (if a 2-stage algorithm, remove other bad channels first) +if noisyOut.ransacOff + noisyOut.ransacBadWindowFraction = 0; + noisyOut.ransacPerformed = false; +elseif isempty(channelLocations) + warning('findNoisyChannels:noChannelLocation', ... + 'ransac could not be computed because there were no channel locations'); + noisyOut.ransacBadWindowFraction = 0; + noisyOut.ransacPerformed = false; +else % Set up parameters and make sure enough good channels to proceed + [ransacChannels, idiff] = setdiff(evaluationChannels, noisyChannels); + X = X(:, idiff); + + % Calculate the parameters for ransac + ransacSubset = round(noisyOut.ransacChannelFraction*size(data, 2)); + if noisyOut.ransacUnbrokenTime < 0 + error('find_noisyChannels:BadUnbrokenParameter', ... + 'ransacUnbrokenTime must be greater than 0'); + elseif noisyOut.ransacUnbrokenTime < 1 + ransacUnbrokenFrames = signalSize*noisyOut.ransacUnbrokenTime; + else + ransacUnbrokenFrames = srate*noisyOut.ransacUnbrokenTime; + end + + nchanlocs = channelLocations(ransacChannels); + if length(nchanlocs) ~= size(nchanlocs, 2) + nchanlocs = nchanlocs'; + end + if length(nchanlocs) < ransacSubset + 1 || length(nchanlocs) < 3 || ... + ransacSubset < 2 + warning('find_noisyChannels:NotEnoughGoodChannels', ... + 'Too many channels have failed quality tests to perform ransac'); + noisyOut.ransacBadWindowFraction = 0; + noisyOut.ransacPerformed = false; + end +end + +if noisyOut.ransacPerformed + try + % Calculate all-channel reconstruction matrices from random channel subsets + locs = [cell2mat({nchanlocs.X}); cell2mat({nchanlocs.Y});cell2mat({nchanlocs.Z})]; + catch err + error('findNoisyChannels:NoXYZChannelLocations', ... + 'Must provide valid channel locations'); + end + if isempty(locs) || size(locs, 2) ~= length(ransacChannels) ... + || any(isnan(locs(:))) + error('find_noisyChannels:EmptyChannelLocations', ... + 'The signal chanlocs must have valid X, Y, and Z components'); + end + P = hlp_microcache('cleanchans', @calc_projector, locs, ... + noisyOut.ransacSampleSize, ransacSubset); + ransacCorrelationsT = zeros(length(locs), WRansac); + + % Calculate each channel's correlation to its RANSAC reconstruction for each window + n = length(ransacWindow); + m = length(ransacChannels); + p = noisyOut.ransacSampleSize; + Xwin = reshape(X(1:n*WRansac, :)', m, n, WRansac); + parfor k = 1:WRansac + ransacCorrelationsT(:, k) = ... + calculateRansacWindow(squeeze(Xwin(:, :, k))', P, n, m, p); + end + clear Xwin; + noisyOut.ransacCorrelations(ransacChannels, :) = ransacCorrelationsT; + flagged = noisyOut.ransacCorrelations < noisyOut.ransacCorrelationThreshold; + badChannelsFromRansac = find(sum(flagged, 2)*ransacFrames > ransacUnbrokenFrames)'; + noisyOut.noisyChannels.badChannelsFromRansac = badChannelsFromRansac(:)'; + noisyOut.ransacBadWindowFraction = sum(flagged, 2)/size(flagged, 2); +end + +% Combine bad channels detected from all methods +noisy = noisyOut.noisyChannels; +noisyOut.noisyChannels.badChannelsFromLowSNR = ... + intersect(noisy.badChannelsFromHFNoise, noisy.badChannelsFromCorrelation); +noisyChannels = union(noisyChannels, ... + union(union(noisy.badChannelsFromRansac, ... + noisy.badChannelsFromHFNoise), ... + union(noisy.badChannelsFromNaNs, ... + noisy.badChannelsFromNoData))); +noisyOut.noisyChannels.all = noisyChannels(:)'; +noisyOut.medianMaxCorrelation = median(noisyOut.maximumCorrelations, 2); + +%% Helper functions for findNoisyChannels +function P = calc_projector(locs, numberSamples, subsetSize) +% Calculate a bag of reconstruction matrices from random channel subsets + +[permutedLocations, subsets] = getRandomSubsets(locs, subsetSize, numberSamples); +randomSamples = cell(1, numberSamples); +parfor k = 1:numberSamples + tmp = zeros(size(locs, 2)); + slice = subsets(k, :); + tmp(slice, :) = real(spherical_interpolate(permutedLocations(:, :, k), locs))'; + randomSamples{k} = tmp; +end +P = horzcat(randomSamples{:}); + +function [permutedLocations, subsets] = getRandomSubsets(locs, subsetSize, numberSamples) + stream = RandStream('mt19937ar', 'Seed', 435656); + numberChannels = size(locs, 2); + permutedLocations = zeros(3, subsetSize, numberSamples); + subsets = zeros(numberSamples, subsetSize); + for k = 1:numberSamples + subset = randsample(1:numberChannels, subsetSize, stream); + subsets(k, :) = subset; + permutedLocations(:, :, k) = locs(:, subset); + end + +function Y = randsample(X, num, stream) +Y = zeros(1, num); +for k = 1:num + pick = round(1 + (length(X)-1).*rand(stream)); + Y(k) = X(pick); + X(pick) = []; +end + +function rX = calculateRansacWindow(XX, P, n, m, p) + YY = sort(reshape(XX*P, n, m, p),3); + YY = YY(:, :, round(end/2)); + rX = sum(XX.*YY)./(sqrt(sum(XX.^2)).*sqrt(sum(YY.^2))); + +function noisyOut = getNoisyStructure() + noisyOut = struct('srate', [], ... + 'samples', [], ... + 'evaluationChannels', [], ... + 'channelLocations', [], ... + 'robustDeviationThreshold', [], ... + 'highFrequencyNoiseThreshold', [], ... + 'correlationWindowSeconds', [], ... + 'correlationThreshold', [], ... + 'badTimeThreshold', [], ... + 'ransacSampleSize', [], ... + 'ransacChannelFraction', [], ... + 'ransacCorrelationThreshold', [], ... + 'ransacUnbrokenTime', [], ... + 'ransacWindowSeconds', [], ... + 'noisyChannels', getBadChannelStructure(), ... + 'ransacPerformed', true, ... + 'channelDeviationMedian', [], ... + 'channelDeviationSD', [], ... + 'channelDeviations', [], ... + 'robustChannelDeviation', [], ... + 'noisinessMedian', [], ... + 'noisinessSD', [], ... + 'zscoreHFNoise', [], ... + 'noiseLevels', [], ... + 'maximumCorrelations', [], ... + 'dropOuts', [], ... + 'medianMaxCorrelation', [], ... + 'correlationOffsets', [], ... + 'ransacCorrelations', [], ... + 'ransacOffsets', [], ... + 'ransacBadWindowFraction', []); + diff --git a/PrepPipeline/utilities/getPrepVersion.m b/PrepPipeline/utilities/getPrepVersion.m index 875cc12..213f08b 100644 --- a/PrepPipeline/utilities/getPrepVersion.m +++ b/PrepPipeline/utilities/getPrepVersion.m @@ -1,95 +1,95 @@ -function [currentVersion, changeLog, markdown] = getPrepVersion() - - changeLog = getChangeLog(); - currentVersion = ['PrepPipeline' changeLog(end).version]; - markdown = getMarkdown(changeLog); -end - -function changeLog = getChangeLog() - changeLog(7) = ... - struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); - changeLog(7).version = '0.57.0'; - changeLog(7).status = 'Released'; - changeLog(7).date = '3/31/2025'; - changeLog(7).changes = {'Updated the interface for new EEGLAB'}; - - changeLog(6) = ... - struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); - - changeLog(6).version = '0.56.0'; - changeLog(6).status = 'Released'; - changeLog(6).date = '8/01/2021'; - changeLog(6).changes = { ... - 'Corrected parfor failure when channel number not consecutive'; - 'Fixed missing badChannelsFromDropout in updateBadChannels issue#28'... - }; - - changeLog(5) = ... - struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); - - changeLog(5).version = '0.55.4'; - changeLog(5).status = 'Released'; - changeLog(5).date = '7/26/2020'; - changeLog(5).changes = { ... - 'Correctly restored EEGLAB options after execution'; ... - 'Added functions to output errors from etc.noiseDetection'; ... - 'Corrected findpeaks naming conflict in Chronux'; ... - 'Post process does not execute if Prep had errors'}; - - changeLog(4) = ... - struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); - - changeLog(4).version = '0.55.3'; - changeLog(4).status = 'Released'; - changeLog(4).date = '10/19/2017'; - changeLog(4).changes = { ... - 'Fixed issue with interpolated channels when interpolation order is pre-process'; ... - 'Fixed issue with correct removal of interpolated channels during post-processing'; ... - 'Reordered preprocessing and report buttons on master GUI'}; - - changeLog(3).version = '0.55.2'; - changeLog(3).status = 'Released'; - changeLog(3).date = '08/18/2017'; - changeLog(3).changes = { ... - 'Fixed undefined reference to referenceOut in prepPipeline post process'}; - - changeLog(2).version = '0.55.1'; - changeLog(2).status = 'Released'; - changeLog(2).date = '06/03/2017'; - changeLog(2).changes = { ... - 'Wrote printListCompressed to display channels more compactly'; ... - 'Put in a MATLAB version check because legend titles not supported in 2014b'; ... - 'Fixed spacing on output of interpolated channel numbers'}; - - changeLog(1).version = '0.55.0'; - changeLog(1).status = 'Released'; - changeLog(1).date = '05/29/2017'; - changeLog(1).changes = { ... - ['Changed the EEG.etc.noiseDetection structure to contain ' ... - 'removed channels and interpolated channels for easier access ']; ... - 'Fixed reporting to work when bad channels have been removed'; ... - ['Added original channel labels to EEG.etc.noiseDetection for ' ... - 'ease in reporting']; ... - 'Added Blasst as an unsupported line noise removal option'; ... - 'Moved legend of spectrum to right, put in checks for removed channels'; ... - 'Corrected bug in smoothing in cleanline'; ... - 'Corrected several reporting issues'; - 'Default behavior now outputs errors to command line in addition to logging'; ... - 'Renamed several functions to make naming scheme consistent'; ... - 'Started supporting changelog in versions'; ... - 'Fixed bug in struct2str and improved com return on pop_prepPipeline'}; -end - -function markdown = getMarkdown(changeLog) - markdown = ''; - for k = length(changeLog):-1:1 - tString = sprintf('Version %s %s %s\n', changeLog(k).version, ... - changeLog(k).status, changeLog(k).date); - changes = changeLog(k).changes; - for j = 1:length(changes) - cString = sprintf('* %s\n', changes{j}); - tString = [tString cString]; %#ok<*AGROW> - end - markdown = [markdown tString sprintf(' \n')]; - end +function [currentVersion, changeLog, markdown] = getPrepVersion() + + changeLog = getChangeLog(); + currentVersion = ['PrepPipeline' changeLog(end).version]; + markdown = getMarkdown(changeLog); +end + +function changeLog = getChangeLog() + changeLog(7) = ... + struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); + changeLog(7).version = '0.57.0'; + changeLog(7).status = 'Released'; + changeLog(7).date = '3/31/2025'; + changeLog(7).changes = {'Updated the interface for new EEGLAB'}; + + changeLog(6) = ... + struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); + + changeLog(6).version = '0.56.0'; + changeLog(6).status = 'Released'; + changeLog(6).date = '8/01/2021'; + changeLog(6).changes = { ... + 'Corrected parfor failure when channel number not consecutive'; + 'Fixed missing badChannelsFromDropout in updateBadChannels issue#28'... + }; + + changeLog(5) = ... + struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); + + changeLog(5).version = '0.55.4'; + changeLog(5).status = 'Released'; + changeLog(5).date = '7/26/2020'; + changeLog(5).changes = { ... + 'Correctly restored EEGLAB options after execution'; ... + 'Added functions to output errors from etc.noiseDetection'; ... + 'Corrected findpeaks naming conflict in Chronux'; ... + 'Post process does not execute if Prep had errors'}; + + changeLog(4) = ... + struct('version', '0', 'status', 'Unreleased', 'date', '', 'changes', ''); + + changeLog(4).version = '0.55.3'; + changeLog(4).status = 'Released'; + changeLog(4).date = '10/19/2017'; + changeLog(4).changes = { ... + 'Fixed issue with interpolated channels when interpolation order is pre-process'; ... + 'Fixed issue with correct removal of interpolated channels during post-processing'; ... + 'Reordered preprocessing and report buttons on master GUI'}; + + changeLog(3).version = '0.55.2'; + changeLog(3).status = 'Released'; + changeLog(3).date = '08/18/2017'; + changeLog(3).changes = { ... + 'Fixed undefined reference to referenceOut in prepPipeline post process'}; + + changeLog(2).version = '0.55.1'; + changeLog(2).status = 'Released'; + changeLog(2).date = '06/03/2017'; + changeLog(2).changes = { ... + 'Wrote printListCompressed to display channels more compactly'; ... + 'Put in a MATLAB version check because legend titles not supported in 2014b'; ... + 'Fixed spacing on output of interpolated channel numbers'}; + + changeLog(1).version = '0.55.0'; + changeLog(1).status = 'Released'; + changeLog(1).date = '05/29/2017'; + changeLog(1).changes = { ... + ['Changed the EEG.etc.noiseDetection structure to contain ' ... + 'removed channels and interpolated channels for easier access ']; ... + 'Fixed reporting to work when bad channels have been removed'; ... + ['Added original channel labels to EEG.etc.noiseDetection for ' ... + 'ease in reporting']; ... + 'Added Blasst as an unsupported line noise removal option'; ... + 'Moved legend of spectrum to right, put in checks for removed channels'; ... + 'Corrected bug in smoothing in cleanline'; ... + 'Corrected several reporting issues'; + 'Default behavior now outputs errors to command line in addition to logging'; ... + 'Renamed several functions to make naming scheme consistent'; ... + 'Started supporting changelog in versions'; ... + 'Fixed bug in struct2str and improved com return on pop_prepPipeline'}; +end + +function markdown = getMarkdown(changeLog) + markdown = ''; + for k = length(changeLog):-1:1 + tString = sprintf('Version %s %s %s\n', changeLog(k).version, ... + changeLog(k).status, changeLog(k).date); + changes = changeLog(k).changes; + for j = 1:length(changes) + cString = sprintf('* %s\n', changes{j}); + tString = [tString cString]; %#ok<*AGROW> + end + markdown = [markdown tString sprintf(' \n')]; + end end \ No newline at end of file diff --git a/PrepPipeline/utilities/robustReference.m b/PrepPipeline/utilities/robustReference.m index 2a40bb0..9c301b7 100644 --- a/PrepPipeline/utilities/robustReference.m +++ b/PrepPipeline/utilities/robustReference.m @@ -1,90 +1,90 @@ -function referenceOut = robustReference(signal, referenceOut) -% Robustly estimate of bad channels by iteratively interpolating channels -% -% This function finds bad channels by iteratively interpolating the -% bad list so far and calculating a mean of good signals. It assumes -% that defaults have already been checked on referenceIn. -% -% Parameters (input): -% signal structure with data field (assumes unfiltered) -% referenceOut structure with reference parameters with reference -% parameters in it. -% -% Parameters (output): -% referenceOut the referenceOut structure filled in - - -%% Warn if evaluation and reference channels are not the same for robust -if ~isempty( ... - setdiff(referenceOut.evaluationChannels,referenceOut.referenceChannels)) ... - || ~isempty( ... - setdiff(referenceOut.referenceChannels, referenceOut.evaluationChannels)) - warning('robustReference:EvaluationChannels', ... - 'Reference and evaluation channels should be same for robust reference'); -end - -%% Determine unusable channels and remove them from the reference channels -signal = removeTrend(signal, referenceOut); -referenceOut.noisyStatisticsOriginal = findNoisyChannels(signal, referenceOut); -referenceOut.noisyStatistics = referenceOut.noisyStatisticsOriginal; -[badChannelsFromNaNs, badChannelsFromNoData] = ... - findUnusableChannels(signal, referenceOut.referenceChannels); -noisy = referenceOut.noisyStatisticsOriginal.noisyChannels; -badChannelsFromLowSNR = noisy.badChannelsFromLowSNR; -unusableChannels = union(badChannelsFromNaNs, ... - union(badChannelsFromNoData, badChannelsFromLowSNR)); -unusableChannels = unusableChannels(:)'; -referenceOut.badChannels.badChannelsFromNaNs = ... - badChannelsFromNaNs(:)'; -referenceOut.badChannels.badChannelsFromNoData = ... - badChannelsFromNoData(:)'; -referenceOut.badChannels.badChannelsFromLowSNR = ... - badChannelsFromLowSNR(:)'; -referenceChannels = setdiff(referenceOut.referenceChannels, unusableChannels); - -%% Get initial estimate of the mean by the specified method -if strcmpi(referenceOut.meanEstimateType, 'median') - refTemp = median(signal.data(referenceChannels, :), 1); - signalTmp = removeReference(signal, refTemp, referenceChannels); -elseif strcmpi(referenceOut.meanEstimateType, 'mean') - refTemp = mean(signal.data(referenceChannels, :), 1); - signalTmp = removeReference(signal, refTemp, referenceChannels); -elseif strcmpi(referenceOut.meanEstimateType, 'huber') - signalTmp = removeHuberMean(signal, referenceChannels); -else - signalTmp = signal; -end - -%% Remove reference from signal iteratively interpolating bad channels -iterations = 0; -noisyChannelsOld = []; -while true % Do at least 1 iteration - noisyStatistics = findNoisyChannels(signalTmp, referenceOut); - referenceOut.badChannels = ... - updateBadChannels(referenceOut.badChannels, noisyStatistics.noisyChannels); - noisyChannels = referenceOut.badChannels.all(:)'; - if (iterations > 1 && (isempty(noisyChannels) ||... - (isempty(setdiff(noisyChannels, noisyChannelsOld)) ... - && isempty(setdiff(noisyChannelsOld, noisyChannels))))) || ... - iterations > referenceOut.maxReferenceIterations - break; - end - noisyChannelsOld = noisyChannels; - sourceChannels = setdiff(referenceOut.referenceChannels, noisyChannels); - if length(sourceChannels) < 2 - error('robustReference:TooManyBad', ... - 'Could not perform a robust reference -- not enough good channels'); - end - if ~isempty(noisyChannels) - signalTmp = interpolateChannels(signal, noisyChannels, sourceChannels); - else - signalTmp = signal; - end - referenceSignal = nanmean(signalTmp.data(referenceChannels, :), 1); - signalTmp = removeReference(signal, referenceSignal, referenceChannels); - iterations = iterations + 1; - fprintf('Iteration: %d\n', iterations); -end -referenceOut.actualReferenceIterations = iterations; -referenceOut.noisyStatistics = noisyStatistics; -fprintf('Robust reference done\n'); +function referenceOut = robustReference(signal, referenceOut) +% Robustly estimate of bad channels by iteratively interpolating channels +% +% This function finds bad channels by iteratively interpolating the +% bad list so far and calculating a mean of good signals. It assumes +% that defaults have already been checked on referenceIn. +% +% Parameters (input): +% signal structure with data field (assumes unfiltered) +% referenceOut structure with reference parameters with reference +% parameters in it. +% +% Parameters (output): +% referenceOut the referenceOut structure filled in + + +%% Warn if evaluation and reference channels are not the same for robust +if ~isempty( ... + setdiff(referenceOut.evaluationChannels,referenceOut.referenceChannels)) ... + || ~isempty( ... + setdiff(referenceOut.referenceChannels, referenceOut.evaluationChannels)) + warning('robustReference:EvaluationChannels', ... + 'Reference and evaluation channels should be same for robust reference'); +end + +%% Determine unusable channels and remove them from the reference channels +signal = removeTrend(signal, referenceOut); +referenceOut.noisyStatisticsOriginal = findNoisyChannels(signal, referenceOut); +referenceOut.noisyStatistics = referenceOut.noisyStatisticsOriginal; +[badChannelsFromNaNs, badChannelsFromNoData] = ... + findUnusableChannels(signal, referenceOut.referenceChannels); +noisy = referenceOut.noisyStatisticsOriginal.noisyChannels; +badChannelsFromLowSNR = noisy.badChannelsFromLowSNR; +unusableChannels = union(badChannelsFromNaNs, ... + union(badChannelsFromNoData, badChannelsFromLowSNR)); +unusableChannels = unusableChannels(:)'; +referenceOut.badChannels.badChannelsFromNaNs = ... + badChannelsFromNaNs(:)'; +referenceOut.badChannels.badChannelsFromNoData = ... + badChannelsFromNoData(:)'; +referenceOut.badChannels.badChannelsFromLowSNR = ... + badChannelsFromLowSNR(:)'; +referenceChannels = setdiff(referenceOut.referenceChannels, unusableChannels); + +%% Get initial estimate of the mean by the specified method +if strcmpi(referenceOut.meanEstimateType, 'median') + refTemp = median(signal.data(referenceChannels, :), 1); + signalTmp = removeReference(signal, refTemp, referenceChannels); +elseif strcmpi(referenceOut.meanEstimateType, 'mean') + refTemp = mean(signal.data(referenceChannels, :), 1); + signalTmp = removeReference(signal, refTemp, referenceChannels); +elseif strcmpi(referenceOut.meanEstimateType, 'huber') + signalTmp = removeHuberMean(signal, referenceChannels); +else + signalTmp = signal; +end + +%% Remove reference from signal iteratively interpolating bad channels +iterations = 0; +noisyChannelsOld = []; +while true % Do at least 1 iteration + noisyStatistics = findNoisyChannels(signalTmp, referenceOut); + referenceOut.badChannels = ... + updateBadChannels(referenceOut.badChannels, noisyStatistics.noisyChannels); + noisyChannels = referenceOut.badChannels.all(:)'; + if (iterations > 1 && (isempty(noisyChannels) ||... + (isempty(setdiff(noisyChannels, noisyChannelsOld)) ... + && isempty(setdiff(noisyChannelsOld, noisyChannels))))) || ... + iterations > referenceOut.maxReferenceIterations + break; + end + noisyChannelsOld = noisyChannels; + sourceChannels = setdiff(referenceOut.referenceChannels, noisyChannels); + if length(sourceChannels) < 2 + error('robustReference:TooManyBad', ... + 'Could not perform a robust reference -- not enough good channels'); + end + if ~isempty(noisyChannels) + signalTmp = interpolateChannels(signal, noisyChannels, sourceChannels); + else + signalTmp = signal; + end + referenceSignal = nanmean(signalTmp.data(referenceChannels, :), 1); + signalTmp = removeReference(signal, referenceSignal, referenceChannels); + iterations = iterations + 1; + fprintf('Iteration: %d\n', iterations); +end +referenceOut.actualReferenceIterations = iterations; +referenceOut.noisyStatistics = noisyStatistics; +fprintf('Robust reference done\n'); diff --git a/PrepPipeline/utilities/struct2str.m b/PrepPipeline/utilities/struct2str.m index 21afed6..001bce7 100644 --- a/PrepPipeline/utilities/struct2str.m +++ b/PrepPipeline/utilities/struct2str.m @@ -1,47 +1,47 @@ -function [str] = struct2str(theStruct) -% Converts a struct into a string -str = ''; -fNames = fieldnames(theStruct); -if isempty(fNames) - return; -end -str = 'struct('; -for a = 1:length(fNames) - if ischar(theStruct.(fNames{a})) - strVal = getStr(a); - elseif islogical(theStruct.(fNames{a})) - strVal = getLogical(a); - else - strVal = getNumerical(a); - end - str = [str '''' fNames{a} ''', ' strVal]; %#ok -end -if strcmpi(str(end-1), ',') - str = str(1:end-2); -end -str = [str ')']; - - function strVal = getLogical(indx) - % Appends a logical structure field to the string - if theStruct.(fNames{indx}) - strVal = 'true, '; - else - strVal = 'false, '; - end - end % getLogical - - function strVal = getNumerical(indx) - % Appends a numerical structure field to the string - if isscalar(theStruct.(fNames{indx})) - strVal = [num2str(theStruct.(fNames{indx})) ', ']; - else - strVal = ['[' num2str(theStruct.(fNames{indx})) '], ']; - end - end % getNumerical - - function strVal = getStr(indx) - % Appends a string structure field to the string - strVal = ['''' theStruct.(fNames{indx}) ''', ']; - end % handleStr - +function [str] = struct2str(theStruct) +% Converts a struct into a string +str = ''; +fNames = fieldnames(theStruct); +if isempty(fNames) + return; +end +str = 'struct('; +for a = 1:length(fNames) + if ischar(theStruct.(fNames{a})) + strVal = getStr(a); + elseif islogical(theStruct.(fNames{a})) + strVal = getLogical(a); + else + strVal = getNumerical(a); + end + str = [str '''' fNames{a} ''', ' strVal]; %#ok +end +if strcmpi(str(end-1), ',') + str = str(1:end-2); +end +str = [str ')']; + + function strVal = getLogical(indx) + % Appends a logical structure field to the string + if theStruct.(fNames{indx}) + strVal = 'true, '; + else + strVal = 'false, '; + end + end % getLogical + + function strVal = getNumerical(indx) + % Appends a numerical structure field to the string + if isscalar(theStruct.(fNames{indx})) + strVal = [num2str(theStruct.(fNames{indx})) ', ']; + else + strVal = ['[' num2str(theStruct.(fNames{indx})) '], ']; + end + end % getNumerical + + function strVal = getStr(indx) + % Appends a string structure field to the string + strVal = ['''' theStruct.(fNames{indx}) ''', ']; + end % handleStr + end % struct2str \ No newline at end of file diff --git a/PrepPipeline/utilities/updateBadChannels.m b/PrepPipeline/utilities/updateBadChannels.m index 98abf6e..d79b947 100644 --- a/PrepPipeline/utilities/updateBadChannels.m +++ b/PrepPipeline/utilities/updateBadChannels.m @@ -1,30 +1,30 @@ -function ref = updateBadChannels(ref, noisy) -% Update the bad channel lists from ref based on bad channels in noisy - ref.badChannelsFromNaNs = union(ref.badChannelsFromNaNs, ... - noisy.badChannelsFromNaNs); - ref.badChannelsFromNoData = union(ref.badChannelsFromNoData, ... - noisy.badChannelsFromNoData); - ref.badChannelsFromHFNoise = union(ref.badChannelsFromHFNoise, ... - noisy.badChannelsFromHFNoise); - ref.badChannelsFromCorrelation = union(ref.badChannelsFromCorrelation, ... - noisy.badChannelsFromCorrelation); - ref.badChannelsFromDeviation = union(ref.badChannelsFromDeviation, ... - noisy.badChannelsFromDeviation); - ref.badChannelsFromRansac = union(ref.badChannelsFromRansac, ... - noisy.badChannelsFromRansac); - ref.badChannelsFromDropOuts = union(ref.badChannelsFromDropOuts, ... - noisy.badChannelsFromDropOuts); - ref.all = union(... - union(ref.badChannelsFromNaNs, ... - ref.badChannelsFromNoData), ... - union( ... - union(ref.badChannelsFromHFNoise, ... - ref.badChannelsFromCorrelation), ... - union( ... - union(ref.badChannelsFromDeviation, ... - ref.badChannelsFromRansac), ... - ref.badChannelsFromDropOuts ... - ) ... - ) ... - ); +function ref = updateBadChannels(ref, noisy) +% Update the bad channel lists from ref based on bad channels in noisy + ref.badChannelsFromNaNs = union(ref.badChannelsFromNaNs, ... + noisy.badChannelsFromNaNs); + ref.badChannelsFromNoData = union(ref.badChannelsFromNoData, ... + noisy.badChannelsFromNoData); + ref.badChannelsFromHFNoise = union(ref.badChannelsFromHFNoise, ... + noisy.badChannelsFromHFNoise); + ref.badChannelsFromCorrelation = union(ref.badChannelsFromCorrelation, ... + noisy.badChannelsFromCorrelation); + ref.badChannelsFromDeviation = union(ref.badChannelsFromDeviation, ... + noisy.badChannelsFromDeviation); + ref.badChannelsFromRansac = union(ref.badChannelsFromRansac, ... + noisy.badChannelsFromRansac); + ref.badChannelsFromDropOuts = union(ref.badChannelsFromDropOuts, ... + noisy.badChannelsFromDropOuts); + ref.all = union(... + union(ref.badChannelsFromNaNs, ... + ref.badChannelsFromNoData), ... + union( ... + union(ref.badChannelsFromHFNoise, ... + ref.badChannelsFromCorrelation), ... + union( ... + union(ref.badChannelsFromDeviation, ... + ref.badChannelsFromRansac), ... + ref.badChannelsFromDropOuts ... + ) ... + ) ... + ); \ No newline at end of file diff --git a/README.md b/README.md index 8a85df4..63a81cb 100644 --- a/README.md +++ b/README.md @@ -1,231 +1,36 @@ -EEG-Clean-Tools -=============== - -Contains tools for the PREP pipeline for standardized preprocessing of EEG. You can -find user documention at: - http://vislab.github.io/EEG-Clean-Tools/ - -**Note:** For convenience, EEGLABPlugin directory contains the latest released version of the -PREP that can be unzipped into your EEGLAB plugins directory. - -### Citing the PREP pipeline -The PREP pipeline is freely available under the GNU General Public License. -Please cite the following publication if using: -> Bigdely-Shamlo N, Mullen T, Kothe C, Su K-M and Robbins KA (2015) -> The PREP pipeline: standardized preprocessing for large-scale EEG analysis -> Front. Neuroinform. 9:16. doi: 10.3389/fninf.2015.00016 - -### People -The PREP pipeline incorporates many algorithms that were developed at -USCS SCCN over many years by Nima Bigdely-Shamlo, Tim Mullen and Christian Kothe. -Kyung Min Su performed most of the machine learning evaluation of PREP. Cassidy -Matousek and Jeremy Cockfield worked on the interfaces for the EEGLAB plugin as -well as associated visualization tools. Kay Robbins of UTSA is the lead developer and -maintainer of PREP. - -### Support: -This research was sponsored by the Army Research Laboratory and was accomplished -under Cooperative Agreement Number W911NF-10-2-0022. The views and conclusions -contained in this document/software are those of the authors and should not be interpreted -as representing the official policies, either expressed or implied, of the -Army Research Laboratory or the U.S. Government. The U.S. Government is -authorized to reproduce and distribute reprints for Government purposes -notwithstanding any copyright notation herein. - -### Releases -Version 0.57.0 Released 3/30/2025 -* Modified to work with modified EEGLAB GUI Builder -* Modified reporting to not clutter workspace - -Version 0.56.0 Released 8/01/2021 -* Corrected parfor failure when channel number not consecutive -* Fixed missing badChannelsFromDropout in updateBadChannels issue#28 - -Version 0.55.4 Released 7/26/2020 -* Correctly restored EEGLAB options after execution -* Added functions to output errors from etc.noiseDetection -* Corrected findpeaks naming conflict in Chronux -* Post process does not execute if Prep had errors - -Version 0.55.3 Released 10/19/2017 -* Fixed issue with interpolated channels when interpolation order is pre-process -* Fixed issue with correct removal of interpolated channels during post-processing -* Reordered preprocessing and report buttons on master GUI - -Version 0.55.2 Released 08/18/2017 -* Fixed undefined reference to referenceOut in prepPipeline post process - -Version 0.55.1 Released 06/03/2017 -* Wrote printListCompressed to display channels more compactly -* Put in a MATLAB version check because legend titles not supported in 2014b -* Fixed spacing on output of interpolated channel numbers - -Version 0.55.0 Released 05/29/2017 -* Changed the EEG.etc.noiseDetection structure to contain removed channels and interpolated channels for easier access -* Fixed reporting to work when bad channels have been removed -* Added original channel labels to EEG.etc.noiseDetection for ease in reporting -* Added Blasst as an unsupported line noise removal option -* Moved legend of spectrum to right, put in checks for removed channels -* Corrected bug in smoothing in cleanline -* Corrected several reporting issues -* Default behavior now outputs errors to command line in addition to logging -* Renamed several functions to make naming scheme consistent -* Started supporting changelog in versions -* Fixed bug in struct2str and improved com return on pop_prepPipeline - -Version 0.52 Released -* Modified code to handle EEG structures with empty EEG.error. -* Performed additional minor cleanup. - -Version 0.51 Not released -* Developing bad window visualization plugin for EEGLAB - -Version 0.50 Released -* Made several cleanup modifications to ready for release. - -Version 0.48 (Not released -- version 0.47 with EEGLAB integration) -* Integrated EEGLAB plugin -* Changed the default structure value field name from defaults.default to - default.value and propagated the change -* Changed default names of line noise and global trend to linenoise and - globaltrend -* Modified the resampling step to allow an option low pass filter to remove - downsampling artifacts just below Nyquist frequency. - - Version 0.47 (Not released -- version 0.46 with additional changes) -* Minor refactoring of performReference to avoid 1 extra filtering operation --- - should not reflect results. -* Also added average and specific referencing methods -- not tested as yet. - -Version 0.46 (Not released - version 0.45 with additional changes) -* Fixed remapping of bad evaluation channels into original channel numbers - (relevant when there are none EEG channels interspersed in the channel - locations. -* Passed detrend information in reference structure to allow detrending - with other than the defaults -* Corrected several channel mapping issues in the reporting. - -Version 0.45 (Not released - version 0.44 with additional changes) -* Refactored report to allow statistics to be gathered from noisy structures - -Version 0.44 (Not released - version 0.43 with additional changes) -* Corrected a minor issue with reporting -- difference between robust - and ordinary reference had axes reversed. -* Updated to run with plotting compatible with MATLAB 2014b -* Added box on to cummulative plots. - -Version 0.43 (Not released - version 0.42 with additional changes) -* Corrected a minor issue with reporting -- mean scalp correlation map for - beforeInterpolation was plotting the Original data rather than the - beforeInterpolation data. - -Version 0.42 (Not released - version 0.41 with additional changes) -* Added default line frequencies as multiples of 60 up to half nyquist. - -Version 0.41 (Not released - version 0.40 with additional changes) -* Replaced default method with channel forgetting and median initialization -* Converted EEG to double at the beginning of the pipeline -* Added a noisyStatisticsForInterpolation field to the reference reporting - structure. - -Version 0.40 (Not yet released - major change in strategy) -* Changed the name from StandardLevel2 to PrepPipeline -* Implemented the HP filter-free strategy -* Added a keepFiltered version -- if false (the default) the data in the - repository is not high pass filtered -* Added an option for removing global trend -* Incorporated the different reference schemes into a single performReference - -Version 0.28 (Not yet released) -* Changed the name of the noisyParameter structure in EEG.etc to - noiseDetection. This is a major change with corresponding change - in ESS. -* Added a specificReferenceChannels field to reference structure -* Changed the averageReference field name to referenceSignal in reference - structure -* Included a referenceType field in the reference structure (this - can be 'robust', 'average', or 'specific') -* Eliminated the don't interpolateHFChannels flag. -* Added routines to do specificReference (mastoid or average) -* Modified showSpectrum to return the spectra of all of the channels. -* Detrending at 0.2 Hz has replaced FIR filtering as default trend removal. - -Version 0.27 Released 1/7/2015 -* Correct version of bug fix in cleanLineNoise -- watch that single - precision conversion! - -Version 0.26 Released 1/7/2015 -* Release to fix bug in cleanLineNoise --- channels that are not - lineNoiseChannels were set to zero rather than being carried forward. - - Version 0.25 Released 1/5/2015 (major) -* Removed saving of temporary file after line noise removal -* Fixed report of relative reference -* Modified findNoisyChannels to exclude NaN and constant channels - from noisyChannel thresholding, but to designate them as bad channels -* Moved resampling step before high pass filter -* Assigned return values in a separate step -* Put error check in ShowSpectrum when invalid data is invalid -* Correct minor issues with PlotScalpMap -* Added extractReferenceStatistics -- which extracts summary statistics - for an entire archive. -* Added iterations on the remove robust reference -* Added a summary reporting scheme for spotting problematic datasets. - -Version 0.24 Released 12/7/2104 (major) -* Fixed channel selection bug in showSpectrum -* Added error handling for failures in standardLevel2Pipeline -* Added error reporting for failures -* Corrected time scale on visualization of difference between - robust and mean reference -* Added channel labels as well as numbers to spectrum visualization -* Fixed major bug in robustReference so that original signal is rereferenced -* Revised and expanded the reporting - -Version 0.23 Released 11/13/2014 - -* Removed the channel locations and channel information from noisyOut - because it is already in the reference structure at top level. -* Added reporting of average fraction of channels bad in windows. -* Added first version of hdf5support -- rewrites the noisyParameters - to an HDF5 file. - -Version 0.22 Released 11/9/2014 - -* Revised the method of computing the windowed channel deviations -* Added summary reporting functions -* Added a check to only perform ransac when sufficiently good channels - are available -* Added check to only perform ransac when channel locations are available -* Fixed the input parameter structure on findNoisyChannels -* Added the infrastructure for the summary of all datasets - -Version 0.21 Released 10/30/2104 - -* Removed any reference to chanlocs in highPassFilter -* Full integration with ESS Study Level 2 code -* Preliminary version of Standard Level 2 Report finalized (gives pdf) - -Version 0.20 Released 10/18/2014 - -* Converted standardLevel2Pipeline to a function -* Moved the computationTimes structure to standardLevel2Pipeline so that -it is returned. - -Version 0.19 Released 10/16/2014 - -* Refactored name is also included in the params structure. -* Renamed rereferencedChannels as channelsToBeReferenced to agree with ESS. - -Version 0.18 Released 10/15/2014 - -* Refactored so that all input to the pipeline is in a single params structure. -* Fixed the HF noise reporting windows and several minor bugs -* Added visualizations to show number of bad channels in each window - - - - - - - +EEG-Clean-Tools +=============== + +Contains tools for the PREP pipeline for standardized preprocessing of EEG. You can +find user documention at: + http://vislab.github.io/EEG-Clean-Tools/ + +**Note:** For convenience, EEGLABPlugin directory contains the latest released version of the +PREP that can be unzipped into your EEGLAB plugins directory. + +### Citing the PREP pipeline +The PREP pipeline is freely available under the GNU General Public License. +Please cite the following publication if using: +> Bigdely-Shamlo N, Mullen T, Kothe C, Su K-M and Robbins KA (2015) +> The PREP pipeline: standardized preprocessing for large-scale EEG analysis +> Front. Neuroinform. 9:16. doi: 10.3389/fninf.2015.00016 + +### People +The PREP pipeline incorporates many algorithms that were developed at +USCS SCCN over many years by Nima Bigdely-Shamlo, Tim Mullen and Christian Kothe. +Kyung Min Su performed most of the machine learning evaluation of PREP. Cassidy +Matousek and Jeremy Cockfield worked on the interfaces for the EEGLAB plugin as +well as associated visualization tools. Kay Robbins of UTSA is the lead developer and +maintainer of PREP. + +### Support: +This research was sponsored by the Army Research Laboratory and was accomplished +under Cooperative Agreement Number W911NF-10-2-0022. The views and conclusions +contained in this document/software are those of the authors and should not be interpreted +as representing the official policies, either expressed or implied, of the +Army Research Laboratory or the U.S. Government. The U.S. Government is +authorized to reproduce and distribute reprints for Government purposes +notwithstanding any copyright notation herein. + +### Releases +Release history: [CHANGELOG.md](CHANGELOG.md). From 8be565a92b3db68a57f02cd894977296c3ee4798 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Thu, 24 Sep 2026 11:27:03 -0500 Subject: [PATCH 02/17] Replace hardcoded data paths in the two general example scripts runVEPPrepPipeline.m and runVEPPrepReport.m now use named placeholders with a comment to set them; the shared PATH_TO_PREP_OUTPUT chains the pipeline output into the report script. Co-Authored-By: Claude Opus 5.5 --- PrepPipeline/examples/runVEPPrepPipeline.m | 5 +++-- PrepPipeline/examples/runVEPPrepReport.m | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/PrepPipeline/examples/runVEPPrepPipeline.m b/PrepPipeline/examples/runVEPPrepPipeline.m index dd14575..3a976f5 100644 --- a/PrepPipeline/examples/runVEPPrepPipeline.m +++ b/PrepPipeline/examples/runVEPPrepPipeline.m @@ -2,8 +2,9 @@ %% Set up the input and the output directories basename = 'vep'; -indir = 'F:\DataPool\CTADATA\VEP\BiosemiOriginalSetCorrected'; -outdir = 'F:\TempData'; +% Set these to your own folders before running. +indir = 'PATH_TO_INPUT_EEG_FILES'; % folder of EEGLAB .set files to process +outdir = 'PATH_TO_PREP_OUTPUT'; % folder for the PREP-processed files %% Make the output directory if needed if ~exist(outdir, 'dir') diff --git a/PrepPipeline/examples/runVEPPrepReport.m b/PrepPipeline/examples/runVEPPrepReport.m index bcd35fd..12e4720 100644 --- a/PrepPipeline/examples/runVEPPrepReport.m +++ b/PrepPipeline/examples/runVEPPrepReport.m @@ -2,8 +2,9 @@ % and produces reports. %% Read in the file and set the necessary parameters -dataDir = 'F:\TempData'; -summaryFolder = 'F:\TempDataReports'; +% Set these to your own folders before running. +dataDir = 'PATH_TO_PREP_OUTPUT'; % folder of PREP-processed .set files +summaryFolder = 'PATH_TO_REPORTS'; % folder for the summary and session reports publishOn = true; %% Get the directory list From d816494c1534868b138e725574ce6fe41478f923 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Thu, 24 Sep 2026 14:48:59 -0500 Subject: [PATCH 03/17] Remove the three ESS example scripts ESS is retired, so runESSPrepPipeline.m, runESSResampleAndDealias.m and runESSLevel1ResampleAndDealias.m can no longer run: they drive ESS study classes that are not part of this repository. Co-Authored-By: Claude Opus 5.5 --- .../examples/runESSLevel1ResampleAndDealias.m | 30 ------------------- PrepPipeline/examples/runESSPrepPipeline.m | 12 -------- .../examples/runESSResampleAndDealias.m | 29 ------------------ 3 files changed, 71 deletions(-) delete mode 100644 PrepPipeline/examples/runESSLevel1ResampleAndDealias.m delete mode 100644 PrepPipeline/examples/runESSPrepPipeline.m delete mode 100644 PrepPipeline/examples/runESSResampleAndDealias.m diff --git a/PrepPipeline/examples/runESSLevel1ResampleAndDealias.m b/PrepPipeline/examples/runESSLevel1ResampleAndDealias.m deleted file mode 100644 index 6add2f8..0000000 --- a/PrepPipeline/examples/runESSLevel1ResampleAndDealias.m +++ /dev/null @@ -1,30 +0,0 @@ -%% Example using ESS for the BCIT to resample -% ess2Dir = 'D:\TestData\BCITV2\Data\STDL2\ARL_BCIT_CalibrationDriving_Dataset_level_2'; -% ess2File = [ess2Dir filesep 'studyLevel2_description.xml']; -% level2File = 'level2Derived_description.xml'; -% levelDerivedDir = 'D:\TestData\BCITV2\Data\STDL2_256Hz\ARL_BCIT_CalibrationDriving_Dataset_level_2'; - -ess1Dir = 'D:\temp\VEPESS'; -ess1File = [ess1Dir filesep 'study_description.xml']; -level1File = 'level1Derived_description.xml'; -outputDir = 'D:\temp\VEPESSDown'; -mkdir(outputDir); - -%% Check to make sure level 1 study validates -obj1 = level1Study('essFilePath', ess1File); -obj1.validate(); - -%% Get the files from the level1 study -fileNames = getFilename(obj1); -params.resampleOff = false; -params.resampleFrequency = 128; -params.lowPassFrequency = 0; - -for k = 1:length(fileNames) - EEG = pop_loadset(fileNames{k}); - [~, theName, ~] = fileparts(fileNames{k}); - [EEG, resampling] = resampleEEG(EEG, params); - EEG.etc.resampleAndDealias = resampling; - save([outputDir filesep theName '_downSampled.set'], 'EEG', '-v7.3'); -end - diff --git a/PrepPipeline/examples/runESSPrepPipeline.m b/PrepPipeline/examples/runESSPrepPipeline.m deleted file mode 100644 index 3a7a90e..0000000 --- a/PrepPipeline/examples/runESSPrepPipeline.m +++ /dev/null @@ -1,12 +0,0 @@ -%% Example using ESS -ess1Dir = 'D:\TestData\LargData\UCSD_RSVP\Level_1'; -ess2Dir = 'D:\TestData\LargData\UCSD_RSVP\Level_2'; -%% Validate level 1 -ess1File = [ess1Dir filesep 'study_description.xml']; -obj1 = level1Study(ess1File); -obj1.validate(); - -clear obj1; -%% Create a level 2 study -obj2 = level2Study('level1XmlFilePath', ess1File); -obj2.createLevel2Study(ess2Dir); diff --git a/PrepPipeline/examples/runESSResampleAndDealias.m b/PrepPipeline/examples/runESSResampleAndDealias.m deleted file mode 100644 index fdba3fc..0000000 --- a/PrepPipeline/examples/runESSResampleAndDealias.m +++ /dev/null @@ -1,29 +0,0 @@ -%% Example using ESS for the BCIT to resample -ess2Dir = 'D:\TestData\LargData\NCTU\NCTU_DAS\Level2'; -levelDerivedDir = 'D:\TestData\LargData\NCTU\NCTU_DAS\Level2_256Hz'; - -% ess2Dir = 'D:\TestData\BCITV2\Data\STDL2\ARL_BCIT_CalibrationDriving_Dataset_level_2'; -% levelDerivedDir = 'D:\TestData\BCITV2\Data\STDL2_256Hz\ARL_BCIT_CalibrationDriving_Dataset_level_2'; - -% ess2Dir = 'D:\TestData\BCITV2\Data\STDL2\ARL_BCIT_TrafficComplexity_Dataset_level_2'; -% levelDerivedDir = 'D:\TestData\BCITV2\Data\STDL2_256Hz\ARL_BCIT_TrafficComplexity_Dataset_level_2'; - -% ess2Dir = 'D:\TestData\LargData\VEP\ARL_VEP_v1.1.0_Level2'; -% levelDerivedDir = 'D:\TestData\LargData\VEP\ARL_VEP_v1.1.0_Level2_256Hz'; - -% ess2Dir = 'D:\TestData\LargData\UCSD_RSVP\Level_2'; -% levelDerivedDir = 'D:\TestData\LargData\UCSD_RSVP\Level_2_256Hz'; - -%% Check to make sure level 2 study validates -ess2File = [ess2Dir filesep 'studyLevel2_description.xml']; -obj1 = level2Study('level2XmlFilePath', ess2File); -obj1.validate(); - -%% Create a level 2 derived study -obj = levelDerivedStudy('parentStudyXmlFilePath', ess2File); -callbackAndParameters = {@resampleAndDealias, {'resampleOff', false, ... - 'resampleFrequency', 256, 'lowPassFrequency', 100}}; -obj = obj.createLevelDerivedStudy(callbackAndParameters, ... - 'filterDescription', 'Downsample and then low pass to remove alias', ... - 'filterLabel', 'resample', 'levelDerivedFolder', levelDerivedDir); - From 762e415e929ae897fc0e3b1bd198d4215612ca35 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Fri, 25 Sep 2026 10:39:20 -0500 Subject: [PATCH 04/17] Move documentation from gh-pages to a Sphinx site in docs/ Patterned on hed-matlab: furo theme, MyST markdown, and sphinxcontrib-matlabdomain for an API page generated from the .m help text, deployed to GitHub Pages by .github/workflows/deploy-docs.yaml on pushes to master. - docs/user_guide.md is the gh-pages index.md with headings normalized, image links made relative, and the dead EEGLAB link fixed - docs/conf.py reads the version from getPrepVersion.m - pyproject.toml declares only the docs toolchain - AGENTS.md, .claude/settings.json and .gitignore cover the docs build Co-Authored-By: Claude Opus 5.5 --- .claude/settings.json | 2 + .github/workflows/deploy-docs.yaml | 84 ++++ .gitignore | 6 + AGENTS.md | 4 + README.md | 6 +- docs/_static/custom.css | 267 +++++++++++++ docs/_static/gh_icon_fix.js | 39 ++ docs/_static/images/MATLABWorkspace.png | Bin 0 -> 124388 bytes docs/_static/images/ParallelProcessing.png | Bin 0 -> 52724 bytes docs/_static/images/PrepBoundary.png | Bin 0 -> 16405 bytes docs/_static/images/PrepDetrend.png | Bin 0 -> 24083 bytes docs/_static/images/PrepFinalSave.png | Bin 0 -> 34482 bytes docs/_static/images/PrepFromEEGLAB.png | Bin 0 -> 41495 bytes .../images/PrepLineNoiseParameters.png | Bin 0 -> 39962 bytes docs/_static/images/PrepMainMenu.png | Bin 0 -> 20757 bytes docs/_static/images/PrepPostProcess.png | Bin 0 -> 23345 bytes .../images/PrepReferenceParameters.png | Bin 0 -> 51537 bytes docs/_static/images/PrepReportParameters.png | Bin 0 -> 30185 bytes .../_static/images/PrepResampleParameters.png | Bin 0 -> 59245 bytes docs/_templates/quicklinks.html | 9 + docs/api.rst | 53 +++ docs/conf.py | 95 +++++ docs/index.rst | 34 ++ docs/patch_matlabdomain.py | 126 ++++++ docs/user_guide.md | 359 ++++++++++++++++++ pyproject.toml | 34 ++ 26 files changed, 1115 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/deploy-docs.yaml create mode 100644 docs/_static/custom.css create mode 100644 docs/_static/gh_icon_fix.js create mode 100644 docs/_static/images/MATLABWorkspace.png create mode 100644 docs/_static/images/ParallelProcessing.png create mode 100644 docs/_static/images/PrepBoundary.png create mode 100644 docs/_static/images/PrepDetrend.png create mode 100644 docs/_static/images/PrepFinalSave.png create mode 100644 docs/_static/images/PrepFromEEGLAB.png create mode 100644 docs/_static/images/PrepLineNoiseParameters.png create mode 100644 docs/_static/images/PrepMainMenu.png create mode 100644 docs/_static/images/PrepPostProcess.png create mode 100644 docs/_static/images/PrepReferenceParameters.png create mode 100644 docs/_static/images/PrepReportParameters.png create mode 100644 docs/_static/images/PrepResampleParameters.png create mode 100644 docs/_templates/quicklinks.html create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/patch_matlabdomain.py create mode 100644 docs/user_guide.md create mode 100644 pyproject.toml diff --git a/.claude/settings.json b/.claude/settings.json index 4d3406c..d1c37d2 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -4,6 +4,8 @@ "allow": [ "Bash(python --version)", "Bash(unzip -l *)", + "Bash(python -m sphinx *)", + "Bash(python docs/patch_matlabdomain.py)", "Bash(git status)", "Bash(git status *)", "Bash(git log)", diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml new file mode 100644 index 0000000..7427a92 --- /dev/null +++ b/.github/workflows/deploy-docs.yaml @@ -0,0 +1,84 @@ +name: Deploy Documentation + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0 + with: + python-version: '3.10' + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" + + - name: Create virtual environment + run: | + uv venv --clear .venv + echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH + + - name: Install dependencies + run: uv pip install -e ".[docs]" + + - name: Patch sphinxcontrib-matlabdomain + run: python docs/patch_matlabdomain.py + + - name: Configure Git for GitHub Pages + run: | + git config user.name github-actions + git config user.email github-actions@github.com + + - name: Build documentation + run: | + sphinx-build -b html docs docs/_build/html + + - name: Setup Pages + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6 + + - name: Upload artifact + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5 + with: + path: ./docs/_build/html + + #------------------------------------------------ + # Deploy Job: Deploys the built site + #------------------------------------------------ + deploy: + # This job depends on the 'build' job completing successfully + needs: build + # Only deploy when pushing to main branch, not on pull requests + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + permissions: + pages: write + id-token: write + runs-on: ubuntu-latest + + # Specify the deployment environment + environment: + name: github-pages + # The URL will be automatically set by the deployment step's output + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy to GitHub Pages + # This is the official action for deploying the artifact to GitHub Pages + id: deployment + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5 diff --git a/.gitignore b/.gitignore index 98a7093..1bd5509 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,9 @@ CLAUDE.local.md # The consequence is that it lives on one machine only and is absent from # fresh clones and from `claude --worktree` worktrees. .status/ + +# Documentation build and its Python toolchain +docs/_build/ +.venv/ +__pycache__/ +*.egg-info/ diff --git a/AGENTS.md b/AGENTS.md index 8c08dee..7180aae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,8 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla - Run standalone: add `PrepPipeline` and its subfolders to the MATLAB path, then `[EEG, params, computationTimes] = prepPipeline(EEG, params)` on an EEGLAB `EEG` structure with channel locations. Needs EEGLAB and the Signal Processing Toolbox on the path. - Run as a plugin: unzip `EEGLABPlugin/PrepPipeline.zip` into EEGLAB's `plugins/` folder; the menu entry is Tools -> Run PREP pipeline. - Check the plugin zip: `unzip -l EEGLABPlugin/PrepPipeline.zip` +- Install the docs toolchain: `uv venv --clear .venv`, activate it, then `uv pip install -e ".[docs]"` and `python docs/patch_matlabdomain.py` (required after every install of `sphinxcontrib-matlabdomain`; it fixes a Sphinx 7+ incompatibility in that package). Run the Python tools from the activated `.venv`, not through `uvx`. +- Build docs: `python -m sphinx -b html docs docs/_build/html` - `.github/workflows/deploy-docs.yaml` runs the same build and publishes it to GitHub Pages on pushes to `master` ## Layout @@ -21,6 +23,7 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla - `PrepPipeline/interface/` - the EEGLAB parameter GUIs - `PrepPipeline/derived/`, `PrepPipeline/examples/`, `PrepPipeline/extracted/` - scripts built on the pipeline - `EEGLABPlugin/` - the released plugin as a zip +- `docs/` - Sphinx source for the documentation site (MyST markdown and `.rst`); images in `docs/_static/images/`. `pyproject.toml` exists only to declare the docs toolchain. - `CHANGELOG.md` - release history - `.status/` - working notes. Gitignored; local to each machine. @@ -36,6 +39,7 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla - The version exists in three places that must agree: `PrepPipeline/utilities/getPrepVersion.m` (the change log that `getPrepVersion` returns), the zip name under `EEGLABPlugin/`, and `CHANGELOG.md`. Change all three together. - Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/` or `PrepPipeline/utilities/blasst/`. - `PrepPipeline/reporting/showPipelineDefaults.m` declares `function showPrepDefaults`; MATLAB calls a file by its file name, so the callable name is `showPipelineDefaults`, not the `showPrepDefaults` that the `prepPipeline` help text names. `outputPrepDefaults(EEG)` prints the same defaults. +- `docs/api.rst` pulls each function's help text from its `.m` file, and Sphinx parses that text as reStructuredText. Help text that indents a continuation line without a blank line before it produces docutils warnings in the build. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives. - Do not change the signature of an entry-point function without discussion; EEGLAB and user scripts call them directly, and `pop_prepPipeline` writes the call into EEGLAB history. ## Related repositories diff --git a/README.md b/README.md index 63a81cb..cd1d013 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ EEG-Clean-Tools =============== Contains tools for the PREP pipeline for standardized preprocessing of EEG. You can -find user documention at: - http://vislab.github.io/EEG-Clean-Tools/ - +find the user documentation at +[https://vislab.github.io/EEG-Clean-Tools/](https://vislab.github.io/EEG-Clean-Tools/). + **Note:** For convenience, EEGLABPlugin directory contains the latest released version of the PREP that can be unzipped into your EEGLAB plugins directory. diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000..2501b20 --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,267 @@ +/* Custom styles for the PREP pipeline docs - Furo theme */ + +/* Project name styling below logo */ +.sidebar-brand-text { + font-size: 1.5rem !important; + font-weight: 600 !important; + color: #0969da !important; + margin-top: 0.5rem !important; + text-align: center !important; +} + +html[data-theme="dark"] .sidebar-brand-text, +body[data-theme="dark"] .sidebar-brand-text { + color: #58a6ff !important; +} + +/* Make all heading levels match sidebar title blue color */ +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6, +article h1, +article h2, +article h3, +article h4, +article h5, +article h6 { + color: #0969da !important; +} + +body[data-theme="dark"] .content h1, +body[data-theme="dark"] .content h2, +body[data-theme="dark"] .content h3, +body[data-theme="dark"] .content h4, +body[data-theme="dark"] .content h5, +body[data-theme="dark"] .content h6, +body[data-theme="dark"] article h1, +body[data-theme="dark"] article h2, +body[data-theme="dark"] article h3, +body[data-theme="dark"] article h4, +body[data-theme="dark"] article h5, +body[data-theme="dark"] article h6 { + color: #58a6ff !important; +} + +@media (prefers-color-scheme: dark) { + body:not([data-theme="light"]) .content h1, + body:not([data-theme="light"]) .content h2, + body:not([data-theme="light"]) .content h3, + body:not([data-theme="light"]) .content h4, + body:not([data-theme="light"]) .content h5, + body:not([data-theme="light"]) .content h6, + body:not([data-theme="light"]) article h1, + body:not([data-theme="light"]) article h2, + body:not([data-theme="light"]) article h3, + body:not([data-theme="light"]) article h4, + body:not([data-theme="light"]) article h5, + body:not([data-theme="light"]) article h6 { + color: #58a6ff !important; + } +} + +/* Quick Links sidebar styling - darker gray box with proper padding */ +.sidebar-quicklinks { + margin: 1rem 0.75rem 1rem 0.75rem; + padding: 0.75rem 1rem; + background-color: #e8e8e8; + border-radius: 0.25rem; + border: 1px solid #d0d0d0; +} + +/* Style RST sidebar directive boxes to match left sidebar styling */ +.sidebar, +aside.sidebar { + background-color: #e8e8e8 !important; + border: 1px solid #d0d0d0 !important; + border-radius: 0.25rem !important; + padding: 0.75rem 1rem !important; +} + +body[data-theme="dark"] .sidebar, +body[data-theme="dark"] aside.sidebar { + background-color: #0d0d0d !important; + border-color: #1a1a1a !important; +} + +@media (prefers-color-scheme: dark) { + body:not([data-theme="light"]) .sidebar, + body:not([data-theme="light"]) aside.sidebar { + background-color: #0d0d0d !important; + border-color: #1a1a1a !important; + } +} + +.sidebar p.sidebar-title, +aside.sidebar p.sidebar-title { + font-weight: 600 !important; + margin-bottom: 0.5rem !important; +} + +/* Dark mode styling for quick links - much darker background */ +body[data-theme="dark"] .sidebar-quicklinks { + background-color: #0d0d0d; + border-color: #1a1a1a; +} + +/* Auto mode with dark system preference */ +@media (prefers-color-scheme: dark) { + body:not([data-theme="light"]) .sidebar-quicklinks { + background-color: #0d0d0d; + border-color: #1a1a1a; + } +} + +.sidebar-quicklinks h3 { + margin-top: 0; + margin-bottom: 0.5rem; + color: var(--color-sidebar-link-text); + font-size: var(--sidebar-item-font-size); + font-weight: 600; +} + +.sidebar-quicklinks ul { + list-style: none; + padding-left: 0; + margin-bottom: 0; +} + +.sidebar-quicklinks li { + margin-bottom: 0.25rem; +} + +.sidebar-quicklinks a { + color: var(--color-link) !important; + text-decoration: none; + font-size: var(--sidebar-item-font-size); + display: block; + padding: 0.25rem 0; + transition: color 0.2s ease; +} + +.sidebar-quicklinks a:hover { + color: var(--color-link-hover) !important; + text-decoration: underline; +} + +/* External link icon for quick links */ +.sidebar-quicklinks a[target="_blank"]::after { + content: " \2197"; + font-size: 0.7em; + opacity: 0.6; +} + +/* Make search field more visible with same background as quick links */ +.sidebar-search-container { + margin: 0 0.75rem; +} + +.sidebar-search-container input { + background-color: #e8e8e8 !important; + border: 1px solid #d0d0d0 !important; + color: var(--color-foreground-primary) !important; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23666666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E") !important; + background-repeat: no-repeat !important; + background-position: right 0.5rem center !important; + background-size: 1.2em !important; + padding-right: 2.5rem !important; +} + +body[data-theme="dark"] .sidebar-search-container input { + background-color: #0d0d0d !important; + border: 1px solid #1a1a1a !important; + color: #e8e8e8 !important; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23cccccc' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E") !important; +} + +/* Auto mode with dark system preference */ +@media (prefers-color-scheme: dark) { + body:not([data-theme="light"]) .sidebar-search-container input { + background-color: #0d0d0d !important; + border: 1px solid #1a1a1a !important; + color: #e8e8e8 !important; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23cccccc' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E") !important; + } +} + +/* Make search icon visible */ +.sidebar-search-container input::placeholder { + color: var(--color-foreground-muted) !important; + opacity: 1 !important; +} + +body[data-theme="dark"] .sidebar-search-container input::placeholder { + color: #999999 !important; +} + +/* Style search icon/button visibility */ +.sidebar-search .icon { + color: var(--color-foreground-muted) !important; +} + +body[data-theme="dark"] .sidebar-search .icon { + color: #999999 !important; +} + +/* Hide view source and edit buttons in top-right header */ +a.muted-link[href*="_sources"], +a.muted-link[title*="Edit this page"], +a.muted-link[title*="View page source"], +.content-icon-container a[href*="_sources"] { + display: none !important; +} + +/* Ensure GitHub repository button shows as icon in top-right header */ + +/* Target the hijacked link using the class added by JS, OR the href attribute as fallback */ +.content-icon-container a.github-repo-link, +.content-icon-container a[href*="github.com"]:not([href*="/edit/"]) { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + width: 2rem !important; + height: 2rem !important; + + /* GitHub Icon */ + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12' fill='%23666666'/%3E%3C/svg%3E") !important; + background-repeat: no-repeat !important; + background-position: center !important; + background-size: 1.2rem !important; + + color: transparent !important; /* Hide any text */ + overflow: hidden !important; +} + +/* Hide the original SVG icon inside the link */ +.content-icon-container a.github-repo-link svg, +.content-icon-container a[href*="github.com"] svg { + display: none !important; +} + +/* Dark mode icon color */ +body[data-theme="dark"] .content-icon-container a.github-repo-link, +body[data-theme="dark"] .content-icon-container a[href*="github.com"]:not([href*="/edit/"]) { + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12' fill='%23cccccc'/%3E%3C/svg%3E") !important; +} + +/* Explicitly hide the edit link via CSS as well */ +.content-icon-container a[href*="/edit/"] { + display: none !important; +} + +/* Logo styling - ensure visibility in both light and dark modes */ +.sidebar-brand-text { + font-weight: 600; +} + +/* Adjust logo size if needed */ +.sidebar-logo { + margin-bottom: 0.5rem; +} + +.sidebar-logo img { + max-width: 100%; + height: auto; +} diff --git a/docs/_static/gh_icon_fix.js b/docs/_static/gh_icon_fix.js new file mode 100644 index 0000000..82ade25 --- /dev/null +++ b/docs/_static/gh_icon_fix.js @@ -0,0 +1,39 @@ +document.addEventListener("DOMContentLoaded", function() { + // Function to fix the icons + function fixGitHubIcons() { + // Furo puts icons in .content-icon-container + // We look for links that point to GitHub + const links = document.querySelectorAll(".content-icon-container a"); + + links.forEach(link => { + const href = link.getAttribute("href"); + if (!href) return; + + // Check if it's a GitHub link (edit or blob/view) + if (href.includes("github.com")) { + + // If it's the Edit link, hide it + if (href.includes("/edit/")) { + link.style.display = "none"; + link.classList.add("hidden-edit-link"); // Marker for CSS + } + // If it's the View/Blob link, hijack it + else if (href.includes("/blob/") || href.includes("/tree/")) { + // Change URL to repo root + link.href = "https://github.com/VisLab/EEG-Clean-Tools"; + link.title = "Go to repository"; + link.setAttribute("aria-label", "Go to repository"); + + // Remove any text content (like "View source") to ensure only icon shows + // But keep the SVG if we were using the original, but we are replacing it via CSS. + // Safest is to empty the text content but keep the element structure if needed. + // Actually, Furo puts an SVG inside. We want to hide that SVG and show our own background. + link.classList.add("github-repo-link"); // Add class for CSS targeting + link.style.display = "inline-flex"; + } + } + }); + } + + fixGitHubIcons(); +}); diff --git a/docs/_static/images/MATLABWorkspace.png b/docs/_static/images/MATLABWorkspace.png new file mode 100644 index 0000000000000000000000000000000000000000..71090389c70a79f740aed6d22400ffc2f5b31b48 GIT binary patch literal 124388 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
5`+&k7WcajfbFB6qj|3{)0I$2s&YMDX3G%r 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 LTj{fcRq+1-J>0w1 literal 0 HcmV?d00001 diff --git a/docs/_static/images/ParallelProcessing.png b/docs/_static/images/ParallelProcessing.png new file mode 100644 index 0000000000000000000000000000000000000000..4b4929e5df9d44dcba46c5f5c870473cf84720a0 GIT binary patch literal 52724 zcmY&+{Fx3=wfKfmu^@BDMFOp?i5=gdj& zWO7F;D@q~4;lY7`fFR09i>rcwfDM6wfF8qu{nIS)jaB@6(7Q@#yNdm^FmbhZbO5MZ z+gpIBxj6t>*#IJLmH<{}77hRl3oi>hFFOZ-ftiz;8IQA4^j|+{R~ZEf=p7hr6lC~q z2g#^^Dp)6J9TyM~q<>=|4S|k1R+#@&LRg9@h=71J#>0P@K!Sk$mXi?|QTJTG=z;as z(#quWpZOi1khVbSid)N&#!$AZ%||WP5Dga`DUV++KKh4r-g%Iiz=-5qx1760Q}RrX zuf(s~cyGa&Q0N9}C$=$q)V66ytce0ZoN zU+}eGQ4ao?KP|Mv?kxJHLMnu?Rggg3$NDqNa8ZcZ-o(aue-fGI7{tA-qG z$|PYfK3F&$i$cqkkko!E(@AQYB^#VxqzJXhu~~GH!z?tC=qv>fj(I<%>XKvP#JQtN zfXj(tv_Ei!dK*i&J0%v{NEguL)6M;uDxVd(C__)+Ls8Yd^f-14E9Oi(LYTG%CbdF= z;_bh?#=$W~y!ug$;2?(evi;2UxtOknU=oH{t+ljdkGC$1gp>chG)U}VTlc;l|NY$K zM#cB`#@G%;u_KY>2V0gPok#G!zOvER;=Vn2{_S+L{KgQ_a2P|8wcv~LIORY@?c;fb zkCGBkPM;IOz)@UM#7#yE{)|SVax2PH=~=`)c%K-6au*pGBMn2^Srx`|Qgjfcq<;Ar zgrt7lt2AtI6PZdu((7n?DhC9#;qf~frTo>Ljhc-@*;QoMFez%%Xxr%p*No`>D~P_= zoSX4}w&^tpD-3}vio^vdw8WVnKH~?Locd(;TTALc!>w>XZI;F-=5QF~?t5hk#=in2 z{Ur9GuyG&{8^jiJsI_mQ2tHo;@?Gz__I|~w$0sb;M<P7XE42zpDcv_rGboLc zz!Lr5|E@^59sV7E#sIc^i~D(e&%eqtDu^s|wx~gnOjR{x=;QL|6X$YO6l0} zP#0{v7rJi$GAI8!pUGC?D*Wj8e z!=#I3)ei>S7D-j0HHg3V>0aw2kQU6rXnq#RMDDVL8EV&z@<=GQ*C6l>bR%`*4)=zh zsq+WCs{o0M#X97P6MF)I`53T5&lOTRc*AHt+@$;{Jeyst4u^;_Qr{W8J$BqUjETh87mAf@xGFhe7CY`BKzD}Q$=!y#K0 z81UXx4^!J++6iwDv@8K#1UE{6&Ch>}5^hiff^)zGVA;JiRg`d3(gqix0zG??PL-a1 zX&2v(N>>dJWz3k z_m>q@i1?%w=cJ_P5IqhW>W{yl7HtfMkcd`gi1e$B7MAfoIGiWG_$6#)85X%E0n=F8 zmRqUjk_g-eXSfI<7@QZw(EzRpOPlVViw*d&x1rE5^n^-ale*$d+hAgtu^1ulIHbSB zxbDCukfXDC-Y-{D=-bsw{?#!Bs$~S4qLv~8U?dLFMR>QaEva>f*8-cd9gYUL_@}VW z@uM#t^(N(jCuFE)-{aTKM@U(&)Q!Vk4u9coBYn`w(O0-ue4X|kGU*_&gH{}?6bEV%bBF0g+T(G97P_m?m-ok#`5oS8v!q#Z_nnGzK zQ(3b!W;;_`dbu?FqfT7`Xdq|N?-39!2JU!OT_ZIopF-6w62Bh~1~A#O!o^|5Pt!UZuA&6^^L$t}<-a|%e6ZE4`% z-XnMFiJypdjlMS^|9o%seQw}8_9X!fpudl~G%xvHs`dhR>j^oD$zl<UG?Q)Ux45 zpEBlT>?lvg82&mnkMGJP;OdFt*6Ot2x&&6}fdgEiLwrSj=UeOso*JgVfK@8i5cy8y zKpz9mw1~!XpDDLkFx^OXQ*EIb>c@JY**(`;>g>BwMpNiPP#W13G@gDc^<(J)s)T!o zRS8%uKnu7$ksPQ*^F`V^T#YL(KVkaG8y%?>J!x(IwoPW`URxB`Nqwbshy(-wlLn=Y zHr=S0^f@O1NpR@2g&Rt~jFN56(s6^(>GQ+W#umwqXy~N{3lv!%K4HuAyWi(0yVCKK z$YSt}?K4DQwU_)V`fTS!AODB$ zRCO+?;#wEHkig~Ezj77foyX!%Y|53$8Y@EPO^3Ub;URRhfnaLf5y}1zvF~SqzuU6? zP6rbUk;nHXVsx9pTX!KsqO7bGVNgjYDA{ikZpVZ^FQRuhoG=r#AV;qg4d)SQjM)eU z#gt)}eevT$db|%}zMJF+!09uUb&JfxGfkp2rQeS@vmeOQ%a}QAi)te-iKW0fgS=`k zSHFZKxK=E&WE}G~$_R?{)D@8`7Q1<(+?7U6oUIVQ<+>0+c4roATt__U1}ck@OYo+Y zxr5(}VApy9wSmYDjcIj>aA)8oBRE*1>VaPOsQRBe7pTJogdzFDMsnIHtBk1`hPJWH zac>Jd^bQs4Y`$;~bXN_GI`5$IrPCUG)dYy7EYZZ}-N7qV)lKValON*=t`%>j_r*AU z_!(GY_zZO78=UIe8TpXI*#aq)QIy=iVAl5#3)n-xVEcg!q&EoWS}Xz*g6gD?aA``O?+Ujc@R3mvXy>Da_&;zfUY0aIHPlDy67H7>JP^29HQ({E#avuCA|6 zQ*#ILE}$-M3yCf^9sO4G<)0qedfbtJsbOu6JZu?<8JMI|EX(ri={U*B(2GJFSba&Qvvf(ud5g%^n=f_;s$ zazWZ0Z!L zWCQ|JFmRdYT%XxCroct#Y{?&_LoOF2fx@*I=)v=rsM57Z8t=tU_6ZE7oZOpg%=SoykA@H8)6{(%QbGW@||Bh~M`Dr>n^L_UCZH*~; zOM4emzSg1{rVSPi1jyt`&`|%tK2+-jRDgdjk3}p-tV!&@E9C(`bnpZq_6=2$NnU`a zX!P9$<$P<4WW&H0eq|l60WZsW?nvY77UR?Mgr?rcVxe5$y~ro}KCP!yAj0bY6$qm;_&lL~_?!P%EfbTxIL2o>wT!B-+t zBVnK1q)Q6O4J>43)sG{Lu^HHd7zX0i%P&4e9Ambh7D}NsMOO^`5Zl*ug0un_p5QI*&gp_xO9%wwT^6`oL+xjVbV-DjblM@OkZ>4epn z)A594<2Mrx|41-2UOx;5@2@leWAr65wtZJH6`lptNJC$Q*JRL-_5kkPWMB{0=!{BkD1{tB=+&4%EI6I9f)HTFL@dg z49o{6Gn_mN=tu_oEVK-V_#)CW5`5I=p%ogd9T;6P?>6$&LUU1$RO>h^_9LtV*nBdF zV6?k5Ksv>XZZlkv9Y0+QH!>@DQ4c|lZwZE*JkjWU5X3kHiv$W?t=Y(&5t-?hHdSOz zf$#coXDs=iLzlj|(P(0$7z8O|s4I`S@=5s(wREBPIJ0D+VCe)MMH2;zjtX=a3_nOE zZfYP*SCi^>l4lDv><{Bt3kXuHJ{}A@Q#ILb;nBoeVt)wgpZ(1kI`s(aX=Tg1*%&vj zh$S$+IBEWab#=wZ%JOp#|9m6odj6^RuN!)QOA4wJi$pp#+lG~KU`!kwGed)d zmjG*}D*&d*8u}Aq8W5Et2{~vpiGCFxr6DUk(838U{277;62L}~uTzlHP&p{`9O9Zm z*Tymhm%d}Da2^LL4qAtrHflfeKJ9$_?Z1>*68U*h@cPj9TK(CtpMIw=M-@fh^-{$b zQQGl63h=w=il3?x{CSLSlzHr(S`c+?plyauQXnnE6EsS&PUSN{gvsgQPl*lCq@YWb9LpZ5e=tsn$5!6BJ z$EqH&2Rg1QiZc~@d(_O9h@IL2q}~z+I*Y&Sp}4r%al!jpHNq9sCn5{dZ`bomI`B2uzpHvZzs zk4!8Ch)KBB#7tv=Kc`7l!0UekzW@=={6F)85BHN;@&V+7p}|)9u=LBN+FlFB9Ef3l zd(-H1F~T&M30a1P)1=yGvMj`yW)jKlu}(xy6+uj6lTlJP+PzNM1xD_Ch$7)eo|h9n zv%3jyAp}CQZ;EA}^oC!<&dFsPTUn7$=GXJ-h5?5yT;J!&vm5bZ=G$9a42q{3*_G!v zI|85G#Rw*{aocsr$T^;`hv`rJj7I_9n_R&^BY|UKov;T~?f#SlBOO!%EDA>gq3MOc zc1oj-N$S5}e;*q)ylbhx$~@c$2>!D2lmWi`9r2I|u$;1Gcp4tlG4T4KEn(H#;xxxNs^VHA#SuLJ-*HY4)%vpKC7r!G4JDixW z`L)mS{knfFBtXU;MCs|;ep|g`^iuoX{w$X4+MrPOZ-XDNez}{_)Rg%p24mC=RE5s3 zCRi0P;m^>_uC?5APKRxkhPFlBN70=cxdtl5gp+B;h(uaiiUMKPt6(f3oR5s!@!;t; zGdM+~;M54sGQ*(dW~G4({>CgEO?VB3@1-_D_v1RQo&FK*%t90q&_Dln z`7avSIZQ1@Y3{?BPbQhCOZXP>JT;3EeWSI|=lJ~-l`U_O18_}_W*j0CEM_bGJ5L_! ztP5PUAUlRFQC8_-9$}%T2)f~gmL?JU1QuctL&{I68v>%L&}LJBv&MCBGFt_>aIuPU z@tr|wq+O>DY9!QzaFQcbjFLtw=MZJ{OtYaqF0q9K%!~Nlz=IyebkDIU^ zVtWvb#Ox`vxLzh(r0?NUn*c$a`ZddS1IOji)3uoF7{OdCv0qgX48V)<3mFYt|;~1p11L z$sWyEZ2hw~Vlk_oC}@Q+_de>bzgz&s3`Zf+)3R$+;;0_sHxI>__0{nW8EbeVgT(pl zmuoJJWh0RveNVGHm=cRd-ty@B8_wF6;jzd$4iQv8ew&t*7G))w35gjh6ek1R+f{Od z+|=&)J(e9If01?GhsSSw@0cxn%1sFQDJ|Wsy!-13K(>6H6>}vp?YtFV?@SE#c3*z0 zr#5u?y_5&MQxF9UzW%OtBhGsGs24PRHB&kJzMo^FjYjp}&wL~{`X+~enKtPAUZqG% zHI-P2baVXO%&~D&Faatwrw*b+C%fj3SfT@odK;OXeFzei5=-_2(|%tU)hU%ZWsubz zC0kJYqu&HPOcB7Rs7(aLBOYj_UCxbRN)h=bDH){Q0XYa}N(K-$P0$1_l2UT#V}+O? zKPy-j8^ufvXn2(J$Bm%#ZbW0e!zb1?5p6-x`xubUkHDl{QbLemhKV%;LzKP@#7*|G z3!=I5(KV@uNxC8_9#vdu<>>JrR0W58g@CK1*5~4qBP~`I5?(pW6kkTF9>~$<75y`l z6-uI*Xb7=L&qO~>xEVQEMi#6TfrCS*5sGE0zxrl6S0O*@n+V(Tg=b>((9JIg#HDpP zTdG|0$n5}I{}i5KPWaAF?a}!7`ubYJJvux*HB*t`G~r+hLW}GU_GMh_0FK!;g&E@L zr3_Kop>(;Z;yEApZPUZ`JQ`i~7QMU40<2&JO8eLk);$bWlt81UMkCU_EE1_4lk{IF zr<4Fp$TC0UmxfH*_)tISvndp`rQ6ayG4q!bhp|5@Tb^&e4j`}nmF|XzU1nzQSxzM& zkG(^skUvW6@iB84x-v_U$Gw?xJP0-)tzM}#4I*w_U^uw=-;48wysk^QXsTa^){R;M zPJDq#DzfvQhv-ID4#S7b2k%P0CbTx6ha5(oA78C{>`yQHK=hp#VOl&d&-j?TY z<^FzSp@iR$%h_M6H^0W}7(`E~OEL70?dnY_zE0jxdUphc4xRRk0$v7s<9FT*XGSHA zyx;VYTV}Gz*S+j^2vh$B?4FRN(ck11qu=ij_4U3_pGpC~eiLc={`Qhd43xXi&-kb2 z0qiN2!VHnLA6TNJkZ72D=Oe`!3=Is}5lrRQ?_3aC834&cV})U5a!Dci!;DCTHsSHQ zB=7qcqQNro;V)L;1AL+^3|(&&#$zH9xr(zhcPr6(Sy+?6YGP!v7s0{-;X;zQCNpii zNEJgE#MwrUs;};lEQV{E@Jz#E9!69u<%XZ=j}b6b`I20Gohf^=DSgwep>h0dk^2PA z$~}Z6#Myh82QLqlOwKK3Jzn)ZmSLJWCBPp~lY3Z<(jsQ0wAQX{Uy`K=jwmIR!>t0^ zq8H%MBFjQA#z0q%5pS_Qr6$F&P4@6hL*@vTxC_5VuVGJ=Rd9$hDH97Li((^EvE0o) zm4D0A2F;c4_KOR8q;n^BR0uy);@B}8ymvd@96>*P z|M2xLj`Cr1-5D}3otY34A9_^$uMWv0sDNPI-&kk#&hC$^?Sq%wuVMV&hqL`}uhV}4 z@x}9ctcWv4fbp*UHQWu`Gd3&uHl~&y!0oE1ou;(q{fPK+Kz!}^e)EdcBY`~ir|ydy z^W3pT=z-hD|04{S*8erz(a)snN;_N^@8lXg3V-P^DbA;SF`D>&_l^f!Rn)1y16N?( zcd_{FPRi)I;*IX@Sx%@fPxMTeLC^WcCP`QAh%4WR}5#a_BEQSBck4|s{CJdRs;Gdg_Lhm;o<^oU~LqD zlTEodv-NCH)9nRIiaC>-(*&1025PW^`XQO(EyPjra=glKz7v|u{jalBV<`I)>0Vax zS0vn+qVmbFko_jP*b~n;{M{yqcL(!~00Jo6r3eQE5^m9=_B1)YR}=(wY>aq!Uz${6 z;CM1MA<(Tp##SPwFSPWbx%MM&g-lf`Rc4AXy;40Ydb~@}7`FbAIaAZcY64hl5XG#j zmpFJGE-)e%aQ8s=>pHczYVHfQiVTLu&bM)iHzQ1gv@`_}C0QYXcG*?*K-xspiX?7P zOCgY`i%Rb<=}J?VMP5~1o=R%7P<(M3q#pu~*)oxbq#DyuA zh=Qm+ex1^@%r%iSb-l}&PXoh?^y(d z5tu$obT_}d5SlM-*!pjHKZ4wnZ*@r+%p5T#p)f^LI-t{;F!DR>AxwMzI3&v3@}e1? zk#m?x^_~JZ-)=11(|toe1YD1Pt^rkN6y<{|>RP_M@<6~@uFLwfVcH*;t2K$^ykz4q zLYcDU3{UrMuK?dO^@;G|^-55+_f5Y1_CrTTv?)0l5B=8+30&g$`M!YnPMI=S(QQ@2 zoKKzmp0Vn3UCeV&@HMl`>hC|v-|{-4xGAk!%}*CnTQ*)#6OIm*W}Cq%jTIE1xxSxQ zw!M7U@H>(k=K_-_R(xQnC96+%e=$*$rTzArATXRF zg+=!ZQwbztNt6g>gC2r3d8y+MLs?YX`{o-jWGni`t?s%FOfG*& zN*T2rgnyNXaxZaJeEHtb_)DBVE|;UBY`kO3G}k6p*4YoX$(!&aVic474t_LWuL2Pl zmOj1oj-N9mBpH_2N1lO1gN%j`4k@3O=JfUFO!=4qPou^}_ph1TS>ESv7pe#cGOZWo zO7wY1)Vv%^+oL61*%qQZuQ{XYU9 zm)9EG0UIsHO>UJXUV~J}M$Y$(J6X=lZF%-xS4pSz%GW!!{>e55mnDvrHfW@|x(Y)S z(Fwm8QZA%gZ3diB!wIZ7phU-W;ceIv;{?HV4#W&Z-|EQ1J#?}lhiKVNBAVQRGis|G zdr8A#W2&+AyJxU9W;whflxs*modB%Fw{((8E^(3t9qbqKR=PlXGJF z`I6zVFaHT8KGob6+?~=Ir_JAkPoe6;yv6VQOK%}3vIr#+FKKkJ1lexp#RTco3I1Rs z?aJZ~2O{daMQdS)mvy1Y14x(+Iqv4p>%ycR5JvH$%7{vt+X?LM=iBo) z^7n3-#14A83!xv=&wJUU-PzC?J9RM@yv(e&a%R1LkCI*PswiIpCegQ z0fVLtAL-ENU)~m0S;}qy| zo$FX(qd!cg8Z^sH|t7FHp?{#trraVltVWBxC(Zeks+UX@@G2@!=BC0zHu6hOpXL{ z9WI@3rS~e;D3u00Y8v{+D|f`F-*)_fI%frYjgJA0KqUzt`XHbMT9 z>3e8@cx9v)%(k3%cQYINw3~5Gx`ibgS7Sl?Yda=KzD1kb33__TCLXAw^0HD-NVK48 zl`S!D6?`GrY2B(_iY(~5VGfYe)ID4rB``#1 zz;-4^{;6FGjgI6bD_M-wW+{e@y@8*2k#QR8AhWj}VLMxzSb*|6P~S5lhv2|Ls8SBI7Ay%%!KVI0 zg)z(NsTub=-{=0#61nenvQh15m*gRJ*57LVy-r%(c0&DcI|N(1d=~fZW3P2`U54{K zu#Y@Gxm?k}ufz$dYysO#0)6&W;fYwt!#j_OJ64x0m5wrlRdkvpk*~XyaFDl&RuzjU zMdXLX;HivyT5DK78&huxwnwUQ-v#lazyp8vcRuFcJ z&sBw*NJR;^c)UoAe%{|-@uD4@an(#&ed(cwtXF{BO+O|)NB%E6hb**dHAb*oTr>E| zb`e!desw+T3b3WdD66CF zD6o{TDYT5ijzuj{o5LSj^Y8(JdEe8vM!HX#@KKcQWzgl%dhEJfL%q0+eNI=pFblfq zV&`n<@8Is#vTgT9@}bK+X_j9f<$c|?pM^0~^pX1etPi`iGW5BM1@ehI0OP^iptR@e6m! z!eRIoxP|WK`}ibOVOCWpS^KImTK#&-DzzJUrJ_z$+S(o%U0m~uNs6r_FLGt=2{f*1 zOgJlN~bQg0+A*JB4jy zc`HM*KAq8KA5=%xf-d%Owz=$N)P2PfEF6qo{PnYY&Ux77PnXM!Dkvb9n9{cT80B)Z zy3b5|je}e-0VCu@cTzA+p!k<2N7|H9#?c#uP{2cMEB5xB8gui^P;xb$Y&l%b0~HR5 zZ7&qoNX698U2wPMm5EFdUt>k78+A*2p~8e*3KY;W1LhG)!x&Pg!ZBiHnl(m_&f<~U zGNHsE}h7CbPRK64GR$e~D4!(rg&R8cj3Q(^f>5Hx5xGIZSsTqs1UJP_J_1dVVL8 z1d%5qv&L4E9^DBa2p3S5Mx=`T4g)chO9-_oud=Mwxbznxh0H>ij*ydX-T>m(Nt|-& zum2k$f}B)^%&&UY(>f!OTk$+35o+yWsXv`Av~Vd{&644+6lcw zK!gy=fEs3=Km-+Pp75awBkB~*FS!91sS*=2(i&VPT7udQvXP?ssb~1-tb~SI%;Ca% zx2q!cg&Wn9XrlD$S7VO;WUz1RdlEsFmox)%+_JcO{4;zb=gy~xMpjbgNPd3Z;}WX%fULd6~YL1&0!aq^I}|yTa;FhPTA7cS!v|L?b3m> z`&=lr=TtmRV@qbPl5SIK=*^=IV~3apJe)@-Zsoj;`Z!fmK6pJS3nUU<&hf{%SXr<& zI;HVR9z1hC{PQ2&F`?S1H8lN+XGrIi?L~aS-R&GKEh1@1LK}SfYxI9elvT{7 z231JWB&w*39IzXSal@51Ii@x%OJ}f&^{PvCvZpKemhRGRSlAhQAdyGP#?bfbiguk9 zMfwi%bi?&Le3!LGdb4BfUw)Rs2s9g#v`n(Ezz0;lRwNmskZO7=a}pe^{L60wxAde> zLK`Fk0?*LLO3X`RdQ2Cf#(|8a4@Y#D!TZjc!7|$P=p;N{^WN8M`6W!$lwEtjWICt+ zl4L+^U`8{^oLP@0;E=^K?64*|RXGgDn$f)FqUbfFk%e@894#0sPNMjyPuh#F^0D{K z`J?S=*P!>PqWbJ3*JquOVh=rBj@g z8k90&A>X4%xN1~<{u$tj84v?H#nOn^VCP0A@7hR%x!n!(N`UiUl;(Bo!3!rOFq(!~ zHktO;UjWs2$%xXRe(yHtuX(I!V^alNZ33k_>~Z<$`*-ALm@F%2UB~AqlAJ$iaFBRRxgs^)OO@QEHdJ3gMZBwOLI_?BsQJ% zT-cLg;txxzCbM>nePGfY*nkY?1Dg0!zUT7v8NfrE=%F34+99|*Ab^gUgY2r1aHy@R z;Q<{YlY=zTY)knnSv->`Ls@!YRjqn*h`*whSns-Plv%7`Gs?lNVsn8bl-c(*|UC0#ip z3f?t}kpZ+M{kX9}q$yQKUN`)^fGFPTB>6-o?|&bOdXYvyA5_OXxIV3t*|mk76Hz6u zW~KZC9w0ETaR`}83L2NV*T{3K8XihC6k*j9hKS297C@@nbU1dRkj1EM0{skn zI_2i43k%P)h$Bf@xiY4+aX_qHgIgYtz{(7 zm)t{~hO6+ds~r&l4j6JK<&fF;1*jA?eL;W11FuY{|>En)>#IdMj&uha~vr9XNxkjT!ap_R9N^@ zVC^iV2=vka_6HTS!-9j(MZ_?pG+H2n5YAs-5ZX(OieD;T`BSKzpj${9p3o?{H$@&*ETBlqd!f4^6AyDGrk!EPS_@fplA6VUW9A8I=8oEn zIWL-@#zpH{Xwh5TcqD&>_g69@bkWcaOL#jXANA~O%ObLu} z8?D6*SU;4(Y|?JPhk{P7Mw?eV_Vke%^FwRJX)^^VsF?E?JB<&+$!xGHX$BKxW#(sI zvU7LMB5`iRZ=U%4x&k~>h;3>S#0C{9w^d}x{NKmpet#ROb>fYtrinaHQOja&ai_`( zqE}`;TJnTISXZ$bXK|hZz5Gm7(LMCJjA0_ zzy)6i=hyCdCS~(Mge3G`b<2E?xP=ICC4Odzwtu9Vf6{Lqv|#H{^|Dkxh{eQ&;yA6! z_rHqb$iOcnLLPZA`T74TRLO*~O5BFBU;~9&K>tq#fV3<63km+eYLLN*+l{4_*a7+f z=s5qxDQqyWOQZ;!<3ASAKS6*Z;RhNWzMPjqAAkInu zuPhxYU5C=biV4~S@qday7$67o3XsbH4>(thXpIG z|0qaM!4t_;mT14%3e+IaQY)bfsUZp8C&QV+@o^cWLfyXOMvZIhOzix)FBi zJb_5iacH+@$b~I8U298xx!!JNNK~Wd3u8N$Vf(+9&J7yKKU(Yvx{97MAd-7WYTHmF ze_Q$7TWMMMXEb^vul@3td=bs?{lC`Clm!z&LnYDg1E6YAkAsfAHF_nJ7ATvhl#J>B z%!$DcT7j)lQFrHwI~4f6r=?|0-i^zR?ltP$(7cTI>Vz!{6H3HBFG?D-Y|48jbsmZ= zM#=KD)p2A$S#5<>`YE6z)>sKMw2V~EWt1@(DTbuaN0nn0^<8sLKpMjyJ$ldG(uPO%|1NPv-RCtF)Z%t|>1K;8{Sy?2`;MEj@8d2wI5;aS3p5!v zjIOWM@B8Z$-%vhCQw(OS{r0JP zO)mC!SuXuYGvEqvyL1))z|#BXE&?Li{Wuf@Dlm7d#z=VS4Gtm(o-Kc3Faj&-EfC34 zpDeuT64U`togQLgrNV=}95kX=wSE>ZIi@7_9^u1HZ_A)inoM1P?10tHtHFV5=~zI( z7i}xokuTA+iiEvFx>X#Z-a$sW+MDvRW|Lves`i}x3`#Uo}>q4%XcWLI@rC<0y;nVh~ zs;S2dd{@fh9{iQb8OkayQ#JtB_I(+w)+F@Ea7{y+SqAhF8qsAQW0rC8WYoF<{elp( zXXq#-s>IY?h1PgBzk-@K@((Mk{-Um@b04(V7uE@?E24FlTxgr}7Crnw)F1#tJ3n#b z7CTN|5n1dY7m52(JJ4!2n39rW{zK<=m|c0ZB<9&^Z?A4%2@X?Q<{??4NhX6u@-S&+ z7fG^Ac}`Bwupz%4Vrln(@#A`EaPfCE*4L`{A;>a~lyS69-|G*Tzx$R_J^F3k$NEz~ zWA5~JuWjCIH@b-|vm7mE@9$Ww*-d|rRNQfim?f zT=ad~$meZd8vc>kbo*B0@?GqxMP}q50SDZI@ z1y1_w{&Ix!(*7Hil%6w&{c4rh@fmb!RmsfO_}{DRO%No0jM(zw&NQ&#x-I8|?UFHH zkCoQeGA9NHfr5$w1~04g$lE6nUx)R;oSEvkyeK((> ziy7Ll7E(~{VBQDB=!6msAFhN%CAJQXRSPTHA37nmR2LfFD;?ojIQ`&i%M)T!0*(2- zCS9|F)THoaJ!#XfQ<_V|jWL~1PNtLyyLRL{BDq`r8f$V*g8m7FksDofxmt8*(ypiZdHc0Y5zey2i zdXRaakkH!AEdzKYVG`phlerH{i%<7~M2%sWL8xls)z3;MX9Sa!f(`e!v-}ON&5oD= zDUk;w1+4Cz956VT;Munb!=L!fVZs&de~G<#Z!q%T?Z{vnSlIE6x4%o8pM`Gv~79LKidB=b(GV&(h8j6VKmm{8WBG^U9jPH z99r{E|B^s;YpCLl3}d+>P_Iz$b(~y@wGHvdvbOi*!8tHB#EVBicZ6)_?>vG=jB$%AeD1-HatjS^cdva{{vVQ-smZdw>nI>$WT5>&Bo+e& z<8|j>hxj5|mWK5Y=4cjJ2C)*6{P1w%1x@(}#1QyaeMjhG7X(Zd|G_Gp>N%$BU)Aj& z@Jy`#0NX%%Rkz>CFTyfkP>_BnQsI}&9${EiqD-D&T`?`cpE5SY`Pv(~1ln2`oa&=& zYdCN@!QBy||68#uf?&!D#)A*_N2Q^#qkNAno; zTHDUqT5F-R$6*(_|AT?dD?zX9N=2)p#>Ak56A(oAvXM`n7A>U;!*+uIR{S%TUg?Ji zv>rOG_a}x@d`>_I6SqK!!2e;_1hRg45)&p|l1yNe*Wb<6f#}N#++Te_i(BOt2nTqp zwB3c6zZv2Sl5={tr~e-o(q`#ZqtX&%K2!cjaa2E}MrvtINt+f<`%RiXEq2*5O`V1c zl$`R<|M`EYXfR^cL(B-Uu)~Tyjc=NL4aiEzW-}Pc``PIKu-VCJ!emuxptmeJjzFtN zcN2tokub3q)s?LU-y@j?V?)J&*W7w_^fQx+t!_zgUjDSk3CPLqW|kf2$S3|yWFw)$ zHA~G@sk5;jH;HU-TTzK{nVM*Fq;gqXQ@OgLZ=|SZ$EO^IWantq;|)c{lr8n1nfo4|m zC?5dE!vB4zeiU%J-yu)QfDTa-oFDX6v}|9|LrA=v^}-6%jlb;0)&O_Ati2tNfhb?o zoAaH1Cl=l8#muCQ6*;NSFcJTU;KRVQE6MG8LQK(cj+X=t?LfAbO@3F~)mPgdK|z$@ zNcwE8hv67S**RCVNSq*M1V~27rlzzmZEw}U#YYGqdfQ7dB=N%F^%b^i^2(IhJF6ef zUmmKPuC0T|@XzgoZ(KP*lK$sv;6RAgeW=CB+zR3W%Vkdm*wkFn zPogUD=r;A1W#r$!ad+8V1?#4ovbO(^t+$M-qlvafH^g(jMdo^sL_^sh1R5h@X)Y=h|4VH*8ZUi{iI-Jl;8yhg*uS86sF zxp`$$NRQXwec`58@ zfM(|M5eyOf$m$vx(ayWZh^Fk!ynC>a2O*29GO`8$JA zKd-s&r5Q4cQ4`!^+wl;@BDkc_FE6LK?%HP;?2u=v498n&GJYRPRog=WVKuwtZCTl! zr)R?)k4NDx-zxuRq+{hVf+-1|!#rEAwEsBQPnD0qSYtbv+)CY5MWbP>q(sb}tW=Zl zX0gY8MZqqISyJybGb+$*MyVM75y!;Z^=SAcqjYr~#A0H>ZnOOQt2y-^ocDoX%sICb3 z_vMJG%CE|jwvG?ETHJ#TZmXy-`x&$STdzo*;?qFG5dH=<0)V&k6Ev@aW)IOEDfcl zrKL=Sbag?0g0pVe-$!dO@nVGm-n%YeWHooK2&7-o_6!0wwD1@bOdnCm;@C2H%H-|F z(gcY`W;Q%Dv_|uD5S#Tk!{^w|MNJF-N_Y1hN1rNR&m-ec zmYkd0Uus>*%r)B&L_@yjpaOosgMff-t|Nr&%N#=8C)0AI|M9ZNB@6~Eq7EHcQ0d=g z`r9@GszYFVAC4WLoK#iVXUWwyXGCR+OG;b`7Hf-5?TC3x*OnOK`qUI#T`DgdN|)=c zH%iq4EM{&vVl)UgOxf^&83<;3X9o!2J$W%E;i@8j-Qv@1^{ca{5BCom{WigXQ1{tn zdrYP7mG4uv-_G!!eoMeyIQQej-8pejQ6Qtz3Ng48G%4cqXe`)D{Kj@)xrk973OMkp zaD9bGl9+;Cr?z}d{40ZQqT}J?GyEB>x4WDRB&|o&u+ zOv^|JXll#ixN-w+Hnp^6e)Iw_cJ;9=c#OSGRQ7(0b!- z41tb7*$&WF2f{I@XP$}VA-bu1G5X4Jdj<1o>=K(2yCacb#bJv=cvwnc3t+f!j}d_W zf3h&2D45b`nv)lvIO(pf`4&xjE7=iMMWwk4D`YyR=wsqT{^k*mh=;5+^IPPfA*f*McM;!{^{HDjPfR;0(F5 z*#J+YAFaU%A81ubW`$ve6M1xugk4rehWb+?KBe@-l^3)r*3eOkVQ+j^aq=*34QV$RU+J1ZU^`&5HUdAeJ$I~ zn2nb#C4RlG(0SFF?I{EWFWp~Bgqlbt@l2O~*1O#vK*;;gVI#j&u7h)T5BG2IeP~h? zJ%0BlLIDTT`?-iXGRd=dL$!D#ZBaPY|EXbN^!GgocHTGQ2{8W0jx-MDgO(srWcB^% z>1W|)Snp}1j6~;AQ@Xm1)~{>X0hFh_we;zC>In;dgHCQpdcGmvei}*> z%QV7Sx-FG%wb%`tbssSI5;S^Vimti9tC=t3uF!Mfma>Qwx}xYSld4$80MV0x0E^tl zm}$fLVoxVqy97CgN6S!Z`nxUfx(?||m(vJ-lr|RxipgnKM*m2?;POSu5II43Nb>P= zMC~}%4$($a7kxa?=|BNN>neCWO7c`wsb>8tDEgI;;T{*%h^U)K3|V&;Sm1~k?l@bk|{t-36K~_DyWm6To>XGv%A3)uhZf!q#I*pvx$Mz=|piljmB%Nlx*gh8QN) z3U^AYW^`5U&#!aDeqSu_tMqRBi}A1J`N_q_#mP#?-DI_Z5+@ETP#aAK%fA!_R6StmHEG$mcsrAp4`;t%OgrlZXKe$0;WJWzbr2KzdrxcwHu*qc$1 zv081q!KA1(=#LNxLNrRJDS$O+k-*O6qFr=Dnc9lphK|!6$&AOFu{995y4+lvBC9G$ z66pb z-NM-Vq`{=^5nocH(SAwvNfQn%^E;I0<2tb4lQdJy{bs1c?#QHqdQbD@>gvhUbz+fi z&-<*jG_9Zy^*Z*0q)EHOVvq`7Rq+$dq3AZqeSYligt&I!#F*M}%hu^JsSs79^;*pL z<>xTSjaq$O?Z~mtu?*W$P_7jQPRgSZhoNSGIOJSK!56R2+prM!8gH{d+8K+Nq3E zb7de#(dMMZglAe6t&DE1-e8Bh^ryRwZqMX{4hVpT1}EaydsK z+OWW;DqgO)F{7Y~!n8=PF{at~8zk>QA{3(Vo<^0lA74_YD8<*9)Z$H4pRcSERe^-{8bmk5wUt#mei=EDscMJ^Z7Ux!cn{Z09vL3fN=TJ zZtT?;FoZun4PKo?zT5v0{~^%aHhJ4yoPkT~E6l8P%rQdP(Cc`#aFHdzt9H5W$vkGt z%g|BLO@?4c=ne8;iHsl-%b+5B1YEoOmo?V{4M^jJqV$77ZX&@%?Idw^ZhJ?gVr{;*G`EcwaGSe zF@lnM?T@?xcQl2`m?@zv?BDxC`(%Hi-ujkrSU*0t??kOhb<>Q>*~!WJD;2qxYAw|> zSV)-zuH|?3yI)+Puq3aO8PpFb4;xo#oHnhnQ3Imh)5um(yl(Wc7sdFJT2`4S7wbcF6M539*@$98LNG7pXr+W2S;VO*=#C zhzFdiMb%8IEaGfyDrQmqkC5(tWcbn3uGRF+0v{$1gU3uiJ) zxsBQICp%7mO=YeuqZehgynL4fMhfnu+V-J&Jt%oO`>;0@0zYA8bE5K>%SLJTS3Bw9 z-+Bch12M?pCx?pw(64lmR@%cNC+jrPt{?>~e^8#D=T)(Dg=xO;{KfY2#B#i}o%iR} zulwwe;h?jc>FkHs`!b8R^BXDjjF#u-eLp3oPlAF-F#g_RAW-**V^{Mt+MBwK=ir3r zhrGQMuP3gYYY*|;Ac@~sgE(#X$LSdbqh~;DPrJ%+(6pWsvoqgyuI|Uy$67;ULjqlr ziRo#FTxJ?Dy;}`q)WaM7@c&lifC{cb|^m#GS*ZSs26Fs;Sf#s24AEv6-OY*cb`@`gXJPrQE~#!-3f{AA{Jy-?D$ zv|c=4a4liUU#`=f*Lt{VWRz*jvvt~Q7UQ*F8uS0$U!0}83R&Vy^M=oVZ`tNP^ZcYE z1I}>tJ_TaP8GU@Cx5)lI@@-rqA87sC`!7RapvHG2t(19$5Q|UN(4QsCqd*b>+`_{4 z&cW4%euG8cW$%cu4Q29TIy~-Z^D72sT{8jjQ$H~|d69nCvbU3&!h}vLzPXCfN)^V z?u~N&N~{CF3MjGHKMUX8S@15i8?0MP=S&S1*Wir$pv5lD z6!^tv+u9TnxD?c(>yyL@sr+YKQ>O7Ou)mezpuMF<^P+)zIde|JNW-@ZJJwFa<-^A? zA+-9nd<>)2u(}VE*|U~X5`p<2XH)eo1VDU%G6_Im7XNP0r%CaC3*L(LhKR|(G>_`q&I>QO4oJ|ETLZV*@!EGg@Y@Q$?zH^O$c-HeB;~9 znZ`cia6x%7kyPUJ*-<60@pA+k*hIT?vqgOF&})`Zv^qR0r=dn8n5V32#wz1T)S*G* z-Ap!f=%$!ak`dU4;J$HoReAi6O+(b0*5{|H50lj)?{n*!Lv5atAjOG+z+$!H23Z6S z0J+azt?i6<3Tzs(>?gC^wJLP=?Cb_7bJWf<-&jNe)U|eID*b?7ydjFKtD6NKok%PS z4Fis0NO*eyy1s-!ljIvcm0aahuqr~?jV41|>1{408T!0{Cb+Pht?p4jQ!bR^N>Hq& zJU?~^zMB7|uP!iodCHubh$^RQ=P7`8E9hGFL;#<5esD5TZUJNGT5?e#F`v|Ec^nIu z)5&rwSM0|xo9W*f$46&pCnuba#ZM>ozj^E~Ps+FCaC=XG#ShTdd^ zeQy+hNN4|Zt}T^=V7RVXc^qEh=HjG|d$BJ#iZ%84Dh{R2ITX@!<6>3V3~6Z5xv6!Jfa(T_#$vCkol3_@1BxO@>&L=pIz zOx8NtIx6)hP)>6Mly(R*i#&|tAWl?z0qP<|G&p<><6>M1#Npo{Ef)pO<)$>1Eg9jU zAS1AI)uJ%l-{})6qvLQ4(8%C})|50$DG3Yx)8B=J3+$Sze3MO-lP_j|RkSZ5yuXfS zWLdCYn?p*1W$7WmZFJ4-UFpJf>)iPo!#uAvhejz8i0G~DT1gtpfen{nIr@moF&K9krkG;IX;l2q(`jmiN6o#~Kh!?JNz)}pK( z6$rC}`TD2tCV};u!``l6$qRacAZeo{5kyjk5B_jMTpjI}51^V;M#k$bO~@v%2@!c3 z=_4zZkDM7Ntyay+7Xgvew}~Et>hHKT!S$aa{8{ybOBe@IC-TX<0avp<6bAwcqxs{mHDt=_clLE#u7+rp+)tXS&?qXY(`gS1ocz`yIO2d+!mGYBDLjm~ zBIDpW6JLzOr==>9guY;<{qn0K@{b06`rTSjahF5j*}?g$trE4MgcQBbm zF(o6r5)_~FZk+MK;_GSfR1({G0(OWGEcgkjl9{Q%vPRJM$9)xdy~)x#4Gs==4%RHZ z=IRl>+#X6A)Bh!R85cWg*W)!zJMIkejiCr!R(US8enk~yBrjI%TwZ#UJ#`wU<0sjF z-p{y033Emt-By0RD-*C#M#CM`9sHs`aoGDXtHH_Xd@GqT*<5{P$lA)r{-o<8P$#V$|13vCfD(bkc-H@HK_M+ycC!`er(pAKPRs&`sp(ow@NDAQ#BPTlTb z9a<3Pgbp%*@OMd3MjKgKGP_wzM?P&;Q2C~EhF8BO8aNpPaoWmcl5NFFeOTeHY2avc zBg*P%=}m!~+f1s1o+5Dejxy|{uo~cng~3IfESEJ}O|_35-3`r{7vfpAO8}kdFuOP0 z^ekCT2;jPeMadYdwG!Ck%4O;V(rK2?gNe)vc!dT_WSCxU5|}+y$PC*E=qIVoC$QQm z#;A7I<_&gASxVgm5!UMK+6amSLctf|aWE&33syf{2Au}Fq^vfQjTf3`k__bw2Oge+ zyqFSUHMnVZ6uvN8FR!h|w$AV4Tvu8lJRI7fl!THoXSc9a!v;KfgD*8i)Aa3hC$DJlw^bKo>P|Q5GIO-f!r~j&|`=R{f znJt|Z+$=b1^7t#JC9cisET2u6DBlU$80K*N-jIwh_Ls<&Z^Tdy{)?oX_NZ1Mf&_YR z{@2r|xrr!({??3pL9PBdb7uF|Hbf3qLq?mqx*^XI<*7>avfm`{E!;m~*c=)Ph&-LF zH5+p;%}h>qqW2t@P#TO0G^>p3523`516&)EZsnU{I<4v6klBW~5SrEO6~0B`5Hkau zn&Q*cvwMSG5XNMKwu-UTGEut5%^J&HKwDPlV}A{IonU=sr@xcuah=GTAwH4KJXuW{ z!#4*;Gp$3(DoV7i2fxw^hYSU-fJLv{R_EvBBpW!3f!2)uUq>#@>WP%62PT(Zb6;L# zn_Rd95DCt7FN(&-oOeYA^NCYnKbqXpq=pQi?9~~~td)i*~NQpdtExxLUTSmx}6~&}Xr(-*vAo^H~OkyLY@oBuw`Byf9k7%8ghEL+EJ7)<0(}9EiKik{9^7aYpg_7Ytkxqu zrHs(tWkGKv$HK9IF{2xF_-XZa0cExCJ*x2R=e9g%K_#cd5RGC13P$s{j-vZ!I+gDQ z>)v|o{)@NS&KEzw`DTQZ{2Dc251nT0_cGZg2^D7G-Fn=lahaI)>Lj-mEG>G>&UA^J z%{a}hEB5u;KsMgUllWcd&B$kiJI{AaHV1W_G3Ptf`sN?Pa~0J`j!J}GH{0IjOnx!sIHT#0ROeBgHXzkXVcAN}N?;ppoX zZap|TIiz!4+OUO(cgeyKw_o~1$8Tcs6TV96(W%9`(}!vtY85s-W?45HL1q!6BZ%5) z8%=feDS69*;mJ;e>1}Niy@=(>o8(-%=54vLG_AF^PB!uNGRH}D@^1p1u5eQMxZmK6 zb)yjDprrE(`Kzz8nWq7zNyLnh$V|-D=g+&7b*)`P-IOsKqj0mlLd)Hx`Q@&MkN#WJ zk*yY|TlGf+`8VM{_tYT?owSznj*ofxY8-X^op%Xo;0hn>~C+FIqxz6=qg z!MT8uw&XP_zpOq8o??D)ytQ+4EvhPfIJ`K9%ZN}EL`npf1IhIV|MZ6}N$OIFa%RAs zii`q+lNo36u8-iu*{C|xRXn-^Lwb9m`r{k}1p5_EqS4b`ukP&#Yd#IR{_SN`M~Cz_ zvAI%@^-)S`MCzRT5K zH{|v^J~d4y2ZPo3lv?y&A=gss^FS3a?zbkPbN@0~J=Rt~(IueFbZ#;YNGQGw%&z=e zKlD@$exqcf6e9=j=>RFEo9c6(S80}Z1xWmTB|)#Y=i{5HfimB1+7a{dHEM_`Cyowp*$E!r+H1;UGRLNm3$TVo=97VSlfTm-=!^Fc4jTXCgwuD# zN!Kyg-!onx|6_%NYirscPg<_XPR(4a%l@G+xn&q`&LieN7z9Fx?-sqLfjrY`V+|mU!`a zm1~c4)oz&inU5G`fvd81OmvF-~! zMG%jG>Cb;e^5gkmE&$LVSpC<}FEVrz0shjy^{)35p=FoLW z3(nmW(6T`4=qQ=7|IU%% z4oEsb+u8yt8F_^R*!2^7z9h_a^JG8CYLqVnQ=kNKu$e z-`zp~Z3b2%A~{zRYq!PLz zDvJr9x!VO(rO?<)K}yo?h^>Ep@ug-8%wlc5QN^K012D~^bx7nwGa}fVoeQ-d5sP0b z8b0^4bqGc|tMY%#_u*TbKy$-=tD%a+8?ssRHV+lhh7Z#-)QuFyJ9*1uO{2F7o3tkX zTi5`lQ!SH`+4&xl$(%V0_5e%GPPf~b=Vwk!Tew`>P$UW%9dtK|xA4Ey0cZ>((9!$9C*D2)B8G+i&%Ce& zkfA2T{-;!w8x$Qb+`lt|kAiNQjkHsBz- z2mj3(K?<(u>Rj!l3PMAP#otfsgfXLx!*L7rxd4J>$>y>^_H;?88sB0dYg}R7f<6KV z*Up&E--o0w>as?hmv1%({>#5|S1z&Y7(Rj`cv(EfF+y180|UcSZ#kQ4=eY065NUoJ z0`x5tieFEfqXd?qNt_N!8?DZh{jfuehs^6)<`eu-!0EqzalIrVMICQ{egxDvAi|jS zH@$KuMNrG?=VTpunrmN;Q#Z>)YJuE|3#oI?L1Vf2Q7`zn&%SCWv;5v8ke(w}l090?tY)e<8RhV_%5 zJv{nSpUYSG)s>L!;y{*eyXht~w%R2yV!$aTyXEs>S zqU&_@|Jf4+)c^Na&~?l}RNjBg>YzV z8}3XoVIcvDqbyy@%9s1F+4@<3T5upTUqoeHwAO6ZAH`H+3RpuEQGzr2Pa!E_B?Vi- zsGJ?v7GGSBmO{rtC@^7TK%G$}M-nZSD+Pq&JjmV5jk)ilR#`Ysy8TN~Iw=$=2|X@} zVvp>`!5mYsRuMcbOi8dqDD$V@;l8rd)HQ4d#HhsH8fQ#+XXLs$eg+s^Pxd={AT?sP zysWt~wFLjG>0(u@Crj&0o2P`tsMXHmE+WVflF}NGs4Ksm9v;GKa#xvoZXFMKDlA?+ zyK``!@%!>wJ`c5^Q`??(ZhEGrO)u>HC_!AvQ(LCIb39HB94uFYW5Y|TCsOMDHp#W| zaY)%|tUr~TdZodpKcuKR2Rse;+uv#l$;gaD$7Tn29~1!ckIgcDeFN6~B~yNS#jkik zG3ss*(gY&{!$O4qh)jkQOH`VqR1=6dG5H!|mUskmDw8)4T0AnW0isoT*AY#Jn*d#^ zsh2aBVA4iYDAJwWrk7Ingqzo0H>0vC(yo3X=MIDYmL#b7_UMJL{GId%B(^DBcFEQ8y@clQEcS?}39BqN1a#8VPurn69+W+!939YRzYt zYAt@dAAK6H5MFFax92xQQO^Z}leF`{J)c{)(P{gZ#N>Ff*=x$8SFPbRWjXr0cNE#J;QcH$3BMBCAK5%1 zw6mho=)J`!aiC8hLBSHTj)FKZBQez*r7NLJpi6}z6Fy-{%* z=5X-e1l9uR2DySw)i=u@9-Cb-DdiMY*V@+~6LXj9LX47{RNSEN_-wW4xGz(=h(L>5bjs_!GZ5O}{>cV;H-7ZNxnINo zKBgsE&|1NHaf8Tc&Lj-}L-eaMOX`;*YQkF5IH9j``vJ|3&^j4<9ni`QI>cD<)IYzzM;uxVxl9aKXs8-?h+b_MB+d_ z5nk)Ly6D&z3FTM(VYLeO%I)5n6@e8}SQs3^F9bF-s0crTT!NmwGk?Y94h`rKQN?Jr z7y@cCl?t;8(NgDuff2GYgrzAcX<<8T_7>+ey=(=N#}CdIT*O*IPgD}m;+@i2KV|cB z;yyH}%7%DB!n>(Or3FTjd6w z69)=c(`XpBz77yYrEqJ~ioFoQIm_O=@(0tgj9S4~-Jp!5PIep(m6g_1irs58QRNX8 z#i5%PlLT%3yLTA$CnYZI+Xm(yC=&_dUSuVzZFLK4YeRZ8OEZSu3JOyx#`mNun1+nz zRv+IcvR#_wastw3XJTs0V8Ll=x@Mg>;Ml==n2&s=8nceWbB~x%Yp_KbnK@u@BURiN z?`Mz>?<-Ci&&}~(kT!6B4mIm?KZ37@B(@*XI;mPOmET$O5&ed)*CN0&A)RYgpLG?3 zj~kK%Ec)ewYmOw*{cAijA_U17_V5BSvd^DoSfU>fcPkW#TaD)9>y*ApO5!iK@rx&0 zZD)&iMR?cfJu%QKw7e6OO`Mt#qr4I0*2+z5H=oy9REN|e3KrW1kVK+LuF6?4);<@< zDXC3_jb}@5I1`r7wjcLWZ7f#wbL~(}dV6j(JmSxhT=huk5rY&+kXSH1+Wd^Am{i$8 z-cO4WW7(!6;ZBBZ)tR_z>gsap(PKp!x_S>&ihxmm4QxdG(#dDq z9@9?NlnM8^V$)f5&g|H(gI$_FLCisS$3Sc?)~H|e%;4baiRmnGV#9OS>Qt48Rps;4 zgex5p`{PMaQaGqv`v))&H7**vA7?seV3RLX{GE$OWvh3V>;Lr8NNEI(QOL39MerM9 zaxWi^8;^gAOjK>xGDQKAKyHn?FWHO3PWKl05b9iQZ?}2aw>T-7z&L-G{1#cz9*>{6 zU$~AKWw6XvVW#BlBy-i!W(ijPygV=9a9MSuhDV*3^sR1Dlv(x@_3i`J`@_4trcnfl z8+XyG9>~!}}*;5!Y!y!rj9YV>I-&~sUp90KT!?$Tl_S#@^LN2 zy$teqE|bC$ws1~u7wMii#OK(%NM#`QxokRKwH5r#0C>_LbjKITJj<0SSCi(m7rzeC zwmJz}AUx4NL8r5#)$-y@iL4xEQLu+|UM`C5s#YRPa!Z+*F`|8QXx?UKbr@8Wt`!oX zAoT;xrW}^m*k!O5Q5nsF;;WIB+SxER?37sqB?@V?cp)VN*-tqkU!{H3;H<_mbJj)5 zWS?f2nzB0ilDy8ReHf;%ADPO(Am(c;Ha`z=aegb;ft|J>(rhR4#j0IL zXf}KgObr)8YIa56G1%lM83-9GsFBmt)W#aW#`o`2Gk?C!#bmtEOQk>wc)0%J;-)D_ z(!#*k4!wnTM2@ec#=a<7Tvq2P<45llxy*B4{ayOL(tiA`vhIiQ@;%EajkqfKKC)Qg zgLGVMvLN|dy|^|(b}a{2&;5^Akhed{YJ$)b?&##H$*RLttk2`i3SkE>ReEmLhpj_P z_m*J|WS^$k*s)WHXS=zaZLXAa^$7|GnUqU%%K#n`CZ%k*4@xAg6t(ubZHEYmh!}C%c+xj4&}gA`yHx zE<_#-SiO>#H%GB_7rWR+T3%7_Yfjf~BNBTDwp+7i*ql4|QtB=p-<7~JAv2qs+Z98P zQ|wG3G}ZJYfrye_jsF%hqfhgT4|x8wd$gt@4hjw9oK*9mn@Y`3e1lnDm$vHt2@#U_l4-;!$M>#yltF*VYYW=ms2anEQQn8aqW03Kc&AOK55He{ zc{Kir|7Pn_cG(_?D$}U%`z64wBrjlW`(E8&*|v|x+qE|U4igcNqsD5PwKz^%N=jz( zPrA5Wj>_Xpz_sU4G0IVtAP-AU=8jaA?g6&EL&BFT^!^^hr-9-9HVEBU$^ zzZv9YtZ!`_Np7ZdhOCp)Zq6*t&mVF*qBYcjk2+l|7D<0d#xn;XUPDykFII0gBzqq- z4k399lvj+!LM-}ufim_4scdCgc2hGux=f27Wqr&kEI=E)-@)m8CZsC)$3HdssAYj4 zK_lS9R6KtnA&A}qKH0x12NDOTm zwWechIUzVpLf~!~AN!0F#EQk}m3(neKN1pm`!y!v%qsRSW%|GDZpWi*V2CUjS(xvI zk^@)|XHFMs4t$o$Lmb8_;z2p2;v?z^OVx@-OmedNw1woNy)89IO8>`0pO*Pw-qLQS(YDdc2TYZ`p^bdOH z-I!yjvNe&uTgY|b+ep%1&$C7ZLPFhVs@=ewFq|W~SBGi6>T0hJwxtt%u&oP~>f+I` zzNN2b5N!i`e0me=yM!!7R%xZ8ise*G^My7=qQ#ub*--H>YkW3R2dc`iQgqdwbNO*~ zQ9h4SW?h8Y!vtgVg1YX!t1Ut|&Z@}ceBpKaJ9 zpYu?naoC_Kk+_LW3e!9OcU{Vo zcTg+%>L1vZlsd0f>EKiJCG8PfoeNEU=)0tzWPhVj=u^kc>)M*@;BVh0$o-rwGs3;- zX|reIlqARtX3yn;>BRrID>H+U30R*)T8P^ILtlxFtj`B)|r~jth7n4T9G)M z3B8*KXO(9*o4Y74K5APWbzbSXzts`>Gk#Si^O1T*MEeX_*Z#lYDRL~QwCf@#t*3rX zJ$T9JA9~8|-horP%J{-pf(juG$BAHh<>Gcjt3*krqS0G^h!DMPbbfM~*gxUo>DKss z+%8KQwUZotoLmz(S_cPo5S~9YhmnI>QoHq@1;J+M;to|h6Uk17TN&WTB_;b{L43X) z)}@LG3%{B8*aF?;?Ar9zuV1@$V(O|Eo%nIi&;8b06<8DEcaCno2m_iEt=jcbG@=*D zz&g4Jze9Oi7KI;+&tt}rqx2;)(#p#Js2>3ha67Qo0Tgv43ru+xQ^iYOdPL0 zFcV~#FN(H1y|sQ|Clqzw2TJ=IHXWMd;(@(o$W+9-I9LWDQ9FfaxiNb0a)nQcnU>K4 z-{y|Lg*7&+yM)>XBUMMQb?){~c!DBGFPKqU=OUG@l=;_lAQ>1R3g0K&;*=uVEFP!a zr>YGAubSkUKGM%S~@?QUzmX&mz zOK&-=3_P^6Uqxocq{Bgu5&4R+c7T_WWLzNzDl0es;nF7(kIP$SF=?@@NyKdRk6b@Q zW??vzu<%b#-p5;fdAf5prf?#r^Rvn5XAJC<3z;auAk<5)7}#BtqVrp?Rk%%ve>l4J zAPnd{pi0;1F<#6{fnkOLMMFHUESB=nGJJ=+Dzg`$V)B84T_9Tb-GnuYN*>@lWDC#| z+$}OoSh@s4dS+&3vz^pCQKkYnlM&P`OrCSyVR&_BRxySXJVCOcY-Rr6&9kauHX`(q`5;r4N@f};|pQdFD1 zndRQq0K4}1m)&~aSzA0${dsN{@PK-kR0eFHm;Tdp9U=uS1u|a!ja*-WT#wgrm8}yJ zvw6eo-R?J?1*$^Ot3V~Q~O9EKPs##F2!lN+zck~9M@! zhNuPpTKmb-nLg!RyxQ6GDkwtx~wQ z$u|Y*N_=%?n~{o)D{~F_l$CD`_uF5+g`@mLjD`~(?V+YaS{;PAJR~IztAHOcJ({g$ zaV~}#^=2cp^?LV5(&|#a2Z)YBj)Jao#KcU^wdK+pB>cS5bMLDm1U!2Ua*u4=H{XGE zh;O>E!QNJ8`k|uYpp@c$)v0jhN_$tZFApA! ze`a2?(Cdq~taK{o{m7l06ogN5GyCXkS)yz5Rso2OrO1a57k3$@YY64y+j9YyNUzc2 zQ{v1w9lud5hpviF!vQ$m$Y)<2G)!8gL&6sYsw0VY7C0>vQ|S7>%S>s;F0Ofav5tgo zx*V%a7flrcv$(EG-`3CP`^&7%SOXGF`pJ$i=JAIoR;xHysSK>N|o3bi=G%&bzZS#T@Bol~b|~zCF1$a*0y%F=td5x*y>5e`KXl zvf3>GDF-VtJ-z%XCGFweyA;;dS0EpLes8$tAfIJkJH_es`9U=_x;-aq#ztI1q6E=+ zCs9URp5MDnalx>fXsV7tfplr9Dg@on$6}`(eB1C{iJ6~S%3(DJ{4NyR-&eP|OCQ{d z0~&d!fLqDBSq6Oh-Q$4Ka=Cl8O1y*UasQX#zn{VZYi z7bcBLxiCbF4L%WY*J$6vQ(gSK^tLB=4zxXAcRISM8 z&Is&16Me0&y%!tTt_3}jFwV0?I%z``QDL%Fe9;R#6(==iFY9z}pf02mO;JW|5`&o2 z2ilq_G7^KHAgDpfuLNc7w~#n!iIydVVtL`8tpHf{YTQf6o!|DM=cl0I#nfN>2slg* z8lhpK^bRRY7EF>Xz&8Z5m7|P+LH>Q`R6UJ1;Ma2D6-8rBAfRn~yLywn&I>(DDNB8& zj@!q^M|%W!aE5mlY7ouNIE2kgN*5wbSQcwclkqi!dal>+(lrjzOh~3e-wGUS1Fi>= zutaNnPLO^sBkTMY)RfJU-BK78m9(m^Qa}@(`6>9(tzR^g|Hc(-kjCQdi0nNlq|)cY zh|Ka`MID%pKq%$zecN_=w+b|+uoY!LI6pRo0)WhVyAD2ISpQ(x+$%j#P((qI$9kr* zbcJ+g*yqAhHf^iw=AocNQk<86Jgic(5qe|g(KDkK=Vz8gD|T``sbvc!g{PBgWnY;5 z8T?qJX3$FCLb&uc^g%1@BzB5+yAg#5o`x2LTGCW%&+%KbJZjbqEK!ZsKGJF#e?*Fg>E9lBGb#!T@DJ3&du_v_)+M6o>xBVgo&1 zV!K^JXRvR*;tfqoOVNgaE))|1@$ScJ{REp5p~^poBg7j=n0?^ zCQ9BHGmA}NRFR4SJDw${r(1jOCb>iSp&rw({cv)ToG&k6ESyT2g1aqH7;r~$fj=_Y-d^>Z_x%dA1s%}?xRXVNRyLaz*tw(EB z-GMTu%G=T@#6ohJl9TMANcaV*$CYxi)3}Ha(_8fFOOXU;8FG~(lCLE(iYIZWr&f@G z(=o9tHZWzI=$7N!sxh}Q{0{dRjvZ=W$M0JX?vKE+T4PC7o$S_|A=3E*?dLy}*#-Ie zRy02Ei;kw5SJC*VB&1v}2|x?AeVK$y0{)UlQEvvr(Rq8B1cMQq4?EjddvuQ<-9!K; z=n?4w+UMPLvO-2m#J29JQT!=~U%eJ*1m}!lXD0ALrDptG#>wPNF2-KDy;I|yuopmV zwqdEpJB4K44(5HGdYs?yw&!(xw^!0;&0SYxeJ=lqEWgxp|0NH+xe+XsfWWSLOZ|?6 zkKtXzT2UVf1DvgqeGqKam5UE&6OWXqP={Sh4KC0|A)lKZ(s=uhj+j}oO0|_L>CEQC z<@GI9c}rzLyxDG1!x}9t8oy;nkgx7C-)Rq4ZITq3+gX7N4;Mo2tyvbBnCgEtzWy?y zGV>9qyZW^STsq|7mI}DL46edl$_qG7C5sxNr}s7%8gWo}xXW2GQR$t=(d#AsWdn`G zn^l_HnRGJI5T-#%j!sV^v&*PM_Y2{Be9E zSkzh~l_9MKphHAf`-d9ajd0w(O-LG&-7I_{;=QHVs&9(>CB83X(LT4jrd_MobqOek z+i%N-MMYIwtuvj!n>i^Ul>c;73Y<6kg+@WeO}kkbIOu*#*J7JBb(WHnk&(%;z@em3 z8ZG)C^|1VpdQcCF3IlNLtu>8m+qi=2Y0tF&>sx0F>&ayU$IZeqpg7F%28}EEHLFA= zj$P)eT<=T)uhZYQMCqbzDLc}LsvsZirPS{I=me!t?yk!TfjX=$v*zmP0}Et+baTv# z9ijTyY5Ry&e`f7#xlMB&mBvUewC)nvubS;W+~7~5TYbA?Z}j$;=MFw5Q7i9a)#A*p z$_EG&j(ID^!OK_X_(M-N7HiKy&vzLf9g)IKR9r~cRjk(Qn|X!OXcr@fR8LEw;Fy$? z@n-9)b+2n7KI|+9+Uv}T$3Ba^{1DOYT)J5psc?;#SDF z5tNvM6)EA><3s8XLVLu>$nh-jtCC%3PepBS*Y7fGcCT0PyT)?TTVYfux(jn7Gja8P zrARszzW`cAK|W&~t^jt~RoRTKG~=&{kAL1hWgIb{=HsPYj4bRH1y>{wQ;U*Mz2lEH z`RQBpP3Wt(yp;IXAS~CN;F1`#t(wjYaCQw}^YnvIO-GlU`D4*VwtFoW&hYe-) zHqy&J{!zNeQuZw}y=?i~*tEDgMKdRdPFv2v0SoDkV@t)7CQdCK-1Ev6xj|Wb8nWzc zSs7o#g`3YDd4%Zqxx;d>Fx{hB-e4tz-x<49Ea3#{??6nZxE4*_^OkmO2urIQkvS3} zKr_QnnzL=!5^Uxm{AzqmT@;L@kn!Nuq;j2q^0=Ebrh9~C}FRFVPYe!jJK;AN243pTKz#VKh+>ck$-!KkJ0D@cbbWZOaE)2BbS7LFw8 z+%DBIYhTD8^6X_2pr_JLqW%&-QM0eLn-2U9JeL8j@KW%0x}C@%^v%aj*k#}p2^c+h zZ$1gMx8)=Z{Y8vNwP}e&o-W=HUPm?(B4RiB-2;{3k9JemUlJ|0QC5;!kU^BtKVBX< z+&q-j;pIlrjEW58aEmKNk zH8NsNQ|lXqkBO&5l^mw=94s10B7NG9idLyLtF>^sVW-9gQYHs$HP%$w1avr!oVzi-^`eRtC0cuuWhWE#)DHv2c? z*HA^+`c=SeJa1ibbRctdgI+6L__sUxyCc!`D<=#v4?n9Qv7D8I~@24!$KhB7+U>!acNv2x;RlV2FTT_T8 z8LAP+POa9CC5t&ooa0ke%jvyLPiP@cAE!Z^`HB`9^#>L*PBHTe~JK723kOs*ZrktDkxD>01jb<9lEXkcLnKDf$!mCQ)}ilJoK2qBo3N zV!C)go%Q4UZY6fw22>mk#1d;Z*<|M6L}XEx5HfwEhOs*Re2BId#II*aBZ~vM!_Y6J zds<9e)PQCA2IHc}n;{Lx1ej*j7|q#K2Ga1Xrsi#sM(!3$i2rudhX%RU2^9&;%aIE` z5M`1&h?75K`c|0w*lqp^1TsOamm4GB2BoE?!z)S-qyK*}c3S)^uz%1CTv26?mBc0Q7IWlK+TVRtlXw>llR)+^cnm*=w`_B|+}^UArD>nbl5Sa*LiY+8>+zHi1J zsgUc3OBobM4Sh}RS@Q8oT;+D2sens=S;37N-Pe~dQZp*q`6Z4CC}c$H4<%UT|M6a7 zI454i)Lf*LC`6M!w~x5Uw>u{A$&w0P2;Dglo|}o3I5Z@NhKl|`E_Sa%=;NOk6~y-6 zO$!!QaJ`yQpdj86HnfYiC39@yps@VC{1A z_nwPqvu9cMUD;hw#}#sf{zt4KJy|V(1{n--8?W;rD7u`H=O*n3YaF0kVSI_X$Wgv zuJk~he)h2YA;53;hU7!1AB2AJ@4Ds2m+(T*w@XU7 zg($4x+dN<(KQMBz2=imieV~qpQ#tYNjw0BcE;u`fLmJQT8zlB9X^}D;(OPasIl9BY=?t(sI43{i^Fik{?N#m(-|UtDf#Bahq4FgN%wAOt32 z!ivNptx`mOYoCo7$iTcad1!ou3qJp5Ld8Wsi{*yJ+^I?BAkI~Q1R-dNouQ#M7MGc6 z;%5pQon?&p&H-p%Y&mOB&duz<%K?q%gx=t9B7}Vm#Q_gpI3{NLDAxvev%mtFnRC7|xc>C} z`V#4FXLyi3j<~0*pev%{Tp5x$vK-E7M!XMl5xnowRoF+$bR) z22p{!`9Cts0Fo%_A&Y_jEK6EyT#FTX0@M>TpkB_8_w6JUlCC1L-ms)#H9sa-AF2#% zR5)utAVRem?L!?3lXRpaDiESjkd~m_;(-JCk&;6jEmsdI0DyyE0fzPQTn^!4h0KyR zy{<)?Xz2U*Qvl%{llRaYY-C_+YWPN{2;0lU$SwAyIjgpnUmK8YAGBMwLZd~eLDfq; zDHkb1|4j6jbS5KCsjwaTWdl+YBFB15=-VQaVTfoz+Deh^BzbYyt@#|kND$F-QyD3O z8Pt!*2rPSl>m-^INXW+QUa@%qEFOWeIVp#+R9S>V@DE0Mrifs#fuM+rj3w=nre(o*{6IfeXmf|HMPCx zYLEarVZy-9Ey&1NCte0#6r>Wjd#;48#$11;VucI~907EHUa=h5D6Sd}K7ghW=%KTu zr68nBU+9FYJR@c!Fy*!%zNW~VgS$P8VYA)33MG}z^&FCeqj)BN!Ne6s%NJ)*Q<#Ts z_U!a*!x0+X;Vo$&mq|r(!+MMb2+OT=@xt|3MN(dks?CeCA+4L{J`gc6h=e}GxZ2Es zGa5NOJf$@IKBmBmP^%j8CP?VYaSIMf*;& z@q=xvbL-!UNgM_^`v8{_8~}6#E-4)U(f*q|g}2BtNT5(CJ`Y^z(T4X(FF*yXCTirO6}A^Q z__`^r|0ooLP!>!rH@EQJwRzPt*R13EKoEJrznBWDBtgvnWcd|Z(pY-#oPW!%VuQI) zFY-dOnlF~?5x^qDDTbn;sCbFYcgYmS=cnpqYcCcj6F;G|F4l{ROzg+aSrS^br*&VR z7-rAUs`>eD?zT)LXs7I$ydqQHBT%TkFaZD;Q9AAg_wWh@e&m}tI3V!>ua@_K>?WY# zD-L!ZL_6VjvJQINdtm2lv-h1y3WA%Im`U^YQ^Z#+6d_P)l8QSzlMl%CoM#{mmoK>t z3A4|*eoKG zva&pQDc}xPlq*>w0jD#G5-|(oyQe}42Q_gR*4@j9h#{L%>TZ+{MDe!o#EWHr4c| z=mG_lzy7zbqESFQ_DRz}R+HDc^rXwzAE8g-a&gG>IQwCR%6lC`b2gS(w9#f2>l`jr zB)+F@x8>qM5jTer8Trs_9|)xkhQnQIv`(iUb{ayPW_@LF-y7MLOy*ETr_;$_R4G?m zoA<3PuRrMxMZVeMbKHw)Xk3@d7}jW7x=Vl_$Kw=c2{l}Q_2a!PRA%dBWW?lteJa8q z&~_HmkhDA&g6A#HERdB4=qB?5>la0leqjQDYm#2x=5ls=2BXNGs{r)E^`&Ww;#y;5 zGU$I?DmY)=Mm#oa1Y4Sdv^6*)m0DpHi~wdH9GcBJiQU_{(lnFN4YO=2PXZ*Ef?l7zlhMAr)yNP>4IwmYISLxp>It^Ud0W(LBY)FL~NR&xZ2a=6jIDDv#UG zT5)*BbqxVC>g$P3sf@I%Mn(8@|B3&qZf`HWuS;{Td@-f-ZQ;IOrsHb4M%9;2{VWK% z22i<(W4F`KthVONqiJvPUwbc@>b>7h7PR;4zj$6JVDFB5= zB&`rEM%B5nT3r?ooXf+e$y)Sk<+?M&Z)9ShZ)V*Z#j<9?}{Qr-DyghFdiP3g3f^! z)jLgzy>%HafL>8{uJrawm%$$W1^W*uQ z1TzsjjC9nTDnkACS077a_anHU*r&%vzGeNjBtXdBA@VT^EN(DkroSl^(C zjQlk~1d0Ngl}mB(ge%5AJ3K*aFX1ioZ;?zB zH%%wC-%{F^Gbilm+ecTeceb}3x~$B>nQ~7sXWAC!3cbQ$2Q<ZoGA_wX;Vz0BAN`x?fIO#(SbhRYT!cY3 zzv8qM10svesDsY*n^+O$2gF91Q`(!t{T($F5cPYg1TcudBpeI9&DX zYPg$YCo3ecA1XE1xlA4P?g`;yp#`s%^la&*gUf5DHNK`u~Wrqk`km}-U#62b<`f*M|7}~_#OsgNc0m}0EM>7KhgZlMEhttnu-_~Qk z_wyatdAfHw9k5xF%4UNamb$&gg8Tlxzc_tj2-s85sZaD{{~z!xLx1?{ky8GSITP8r zhO+lb@iON4F=1OhdTQtjBeVR^-u;TJtNdwt7A&F<1!BlPoT~o^u-3Lu%KAqyGXS0B zGJ8Q*&{RYrHw+pW4%XTB>32JT8$i!%c^kiJ^IpzBnQGRO5*6j=Yt<}#k54%;Q&Q!N zz&*vW)6x5m|2nf@4M=!sM~j|o0ad+Ypi#TKY!?w0!TIpqP z>AO&-Hw7XDy3fU&0=MX%$Lb5izr*P_!34^Oese;~iXDw5#a%rWi@`0D2o8O{fdtv= z-4daQx9!w9@?8(UW<^;^yxeS$Xd2JraXrfv3AbVnt)F`OMSohzz;{t|ifwhAU*`jK zWQ_`0b>T|iLP7gb_X_Mrx>s^`%E?GI#z5|~s<%dHa1nfrO%gHW$`NkdimeCYL%DoK ziE~B^3zWg@GVpz^NiT`WE0$aHCp{$Ay_0`#=8q0Ssa)&l^o$4h@pJ`uwn1($)t}Dq z+^mX_q|Dbi97NqRPEVlynvyvwm1&~E+he>Si0Bvi%^3Rs1%AoV|?epIP$RT|v8RL4^UVN;KduCqg|QhIHmYDs5Vlo4BRHx}myD1p~>{ zl+s@6T?|<6Mgmi((xEi7-M@D+p@tCKuiQ~yx~_b-H;r;BeA1J@Mell;QO$Z!b> zb9`)C_Ypl12rir^6N6d*y1T5zX4Kek^;40|fVCwVJj{@(rZ!pG(o`h@RKe8KN(f%< zTMBCr+%WfYBK!~bpIu~hW|SfF=nUU4GzgAK+zzZrqfkL0{{SoI!ESbg9#OA>P72@h z0Ke9fv?Nsl-!WT3pZC|jY1s|T8g9Wyax@)_XK@7MU1P5V$ft!&^aFX9it-~!YrWK> z?}zyuDD@i&7KQEnza)k+mY$|05hUkq01Hjc8&d{?}5e*V^+y zu11F?=qf`cOS9e_i0AT*(@RR|5aLjTK=j=*9~TXCMwjdqWilFTQdme5g4 zx|{uw-?usegLJthliOA4SFb2t*jC#hM!63puSK4ZO1~0`hkja9+$|eEGZ8=Cscy~q zL54?*T+`Dg?%$&{AZWUugi1B`^FtThDQQwH$N{kFBxDN4ss1)p(vrwLp0ftr{)c%b z(LJPpeHQQj;A(PMBcBGbedva9!;EI`oK2a!-Nfvh6@ltl&2A-CLWKwC^4fNXH54e2 z<0Nv&u20Ki;Pb2@mN@MG7ON-%+i#W4UDNI zdsoI^@yl;MI?$ojCyLpnUO>05vN~h^WOkGnqjsEZO}A2$JWV_4;`aZd+>TA3KDr*G z@5`wY(i5ll6b7~=dkUek{CJtGz1xnJo^wMs{dPDl(gNUy0!A3=8=v@>0A&_2y|)y5 z3fM=lCLEUkDhi_#D)*IcwjwrWsrR57i7f*Y(oN#KN#hZr-WpUcS=?Mu>f^opqKq@C zun?86&iFUlVHTBxiLbNx#Zh@nvLpDbSPgduWDZNDSaQiJ4piZeh-dRLZQ}BR76QO~ zY=zWPBywPxefybQEk3}YMDp1kppCjJxe;K}XFrs|$%23~*dYF*=>ip%Ix?Ze=Mx|b z9z1X!EvSq{Yt;IIZbU_>fs*}2lXGKw*3?{38~O+^Qu-iF_g6H6h&Yg zsU6(zasHx-((Q4CRx_aj`y`xWDV4J6VUmvbcIK_ee`Vu0rTgBcr)}436pA*U`g5$- z%}Ir#7yjVL%K_j~`{5pi0li2q-`9bhQ!&tLH zO2=<#8dC*}8ziUnvCcsD{^!|`(VMHV+LY5I0bAAljYD91I#*kl=YZ$1#rVT?f%fSK zD7>$$P@L2qnE18Lz*7XskNmtFL%^G$UwQB#quMS4Xb=+a`DdHB1OFmi*@Q4t*yQ{R zG>Uc^7+hkA4WUER`>=`QpA{v9q}&4RZFI9+4S0` z8rz)xq6h21sqjhYzQ1gCuXJrDGseda*{-_5Ta75 z_hCGg_rY?rqOZM*tE6B=D$$O5tt#M}nrM}E{QYQg3~APWO0mElzcuuT;;_n+;PeGf z`e?`^N}dpOoY|F73*{xYpbF#s45Q~+@c^2<%eu2{Zm@WZ-PxsNB{vlcTt0e-9H z>S{Gr82HNSAl=bcU0b@^n2%_d#R#fGoS@T%&Zq;viu)TZO!XCyk7d|eZ1TV2O>r|Y zOv+FGQvr~ZlOJC4zrN|zMZ5gsRXg!YBK%jz?=WYjrL3s(&xS8aXOiBeBoYDo0DzVU zY>giU^a3abwUg7iZx|mq1p_nW9eakhs*>rI)rB#Xw19OKvl=A#MghBK-Uh}HT^Q}6RK}0K){(Q@~|8c>$|;hrHf^bZvnH%XDACS8|8096CllXSsnZYh}p>+<;VmXC?OBYcA??Bc@t5zYy?LzfdGw?h^2-ZQ$sbs|e9^h_zuk;B$$4YI|~=7(=h7`0qzO*>9;66kl!I3O9)3#nYs zBdCkLkZei^t`^I80R^nu%B>cwwTsXw7L*yk3Dh@Bonnq)jeDD#iW>XItxem|UsT)H zl6zoajlK{9S`7$u=1kdpeho}2}e>JW$@kAAbgMwYG;ItNl+wza=+7d-Sv#No;BDP~Im z(0V1aMtj~w-~a~%8e>pET@6Q{hyTlmo(u#U60v=_7J>*Wtrs7dDzg51<_hq8VXo+( zQV5$?93(%MhaV*TkY6cTYm{`?LVl77%!cd&G?`}O2vY68@g~=BEUnE1P*k*Z+O1#z ztrKbx%^jeeWgdWyAHkNha~jnaST3MH54uHs2+P1(_t$a%{Ra@?>Mg5b<%RW!CgbUr z*}u=rjyEI8t8m#38gAXppJzKiGX=5m_Nd%`VI#imfWZPbSWo0crl1p<^3Y{??yb+o zF%xxa`7<&ZM+|I(CLlJ*|2@^(KVG>$KgQ{dnXxzBY#3 zAd0dUln8dT@d~V`VBvGWt^)Fz@5|+LpXsnVm!J-GuvAg>`*Ac7!JqA^UO~@B(w?6` zEld+`p+j#(Pm%>UdojZPs{RIhwH@?aUwNPfqwBkJQp8@N{NP;03xU0fi*(y=61Sou zydUbv2Pgx7M6vg;imBIs3oK)<;(eaoKYuX$)HGpP_}u03RyN*AS^MflLVSVMM6i&l zOeqhq<3aR3AgYVjq99C06a6!x_8=<4nH6cWHd6hx{bX1+id(4^a^hh&;fL8>&dvCR zkjKmP->NR*2uvLx2l;JNsh^?SA{0t@cU>Az{qc0+l?SBn=G%vYhM9!opD9GM*amGe z`As)7J@PG=s9PEao`QAF#tnXU-1G`joc;rP;si^S46$}p%ML65+ z`!S!jW?9*8!~R<>=WgoeX4|4Xk7Rz`+ZIYFesi`mTu4IggP*C6`_);D+dBL#LGv!} zdts5R*Lg{sbk*2ky_G~U-x0XC{j#C;ROS9Rp01;MVG6x$r?v*2&(`BCpEAVxSNUr#Dz8-Z2}u*3L#@8(&^%aIQY|sF z$#sz1JLYE)ekA0;%o3xs)f9F4(Z6^^_wl~hx6!^Qcitq=Z_l>Krp}8iV;0~ox0$}z z6j|Z+@11`Aq1r;XMV#~AN83&7w7e$sUeD(ZwL}bUh1chf>VzE`jr}(ptm~GtYd+5e zmUizTbk5i0yxM8k3#XaXhi?kXjn5MJWjVQ+`Lz{HO+H;f%x7Vt)149rz%1_uYLR%oD}uk|JSE<36&FWLH7ACEdJzWVaM59qe5 z*gMSohP(3ZQ%NS%@;Dt#G+HkgYjCr$@QA!;7xER6k7u;}PiKx!tEKjTH_?C~d^zAb zy`9(9ZFjo_yeA3@f@9!leTqr9pJ++O9$z4P34FP}FP zL$QX6griMzRq4!uLO7z0q^7>CcP(?MuuE8Kj}o<)V~4l}fqWex_>@ zjV9VT?_Xqd`7mZf@@Sr}N10K7U2LR&{MW0R=&HY5A5Pq@h*6a!`D$@3-`Cr&*Hc^c zMwl%j!0fYce ztJ#haaA<e zg^CE`x>LRS(0SyykKAH3kC(2Gb^5thhSwdmkinX4j^|*qJj{2+ypzbeg0JRN&2iL&IWOl`#4AAWeT=@m%^>#ZAQ z;$sZDH2$m;fe*tyHdL^dKy_MxhTIQ2fFhlhx*lX5Ot|Iaz z%~|@@66`FeCH_uMN}`fNdI3I1l_)-F{(3yhovl`zL{O}-Xyz9Fp3}g){f3cqiARtv z-7*YgiAGTj{K@h1h+?Kp=8(ShC;_r(Hn5A_z>bF#b%M#&WL~tc0m$3|8}vjjjh*{9 z(#)}Aa+c(XpZ};kHv46^(+2-A`P|~!0!P3HEzFW-G;>x7z!$euKt$M&z&{^ZOLoA7 zYN$&^{^>l0u0~B4t^2~EVryH@ljD<18_*P%SEtI|y2_qX5!O#^6OaqGoU#o;Zzrok zy;&X3ufXt`S#yeV^19P73x>Vr=~A)d|c` z6B81WIBJ#jwmV9gXjyn>^Y~m!ku^HLNB&9=CAirrq7$ zRp0klB|oxu!6Smy?fyT+8ut5iTBq0J6|S#jgp ze6o%~6?bdV^Q>N%B~`AFt{~IoX?Pvp)2FV&gr)3&DN8*m5KG|wcVUt7Q?kqBRl)E} zomj$clmH+$uqrz>)fj*b#ZR)R=7yPKGqs|!6y0&wO0h)qgj<=|-XdUUAl z8omjpE~3_SK~<(S@)&qKTZje@gn~-3$fjzbPsP&@n4p3Z3?_IY(26Zaj636h*8&AR zK7WB6F4-*Z$G_>e&8B%4y|8R62)6n_h!{5+%TK!wfuvorNCei4QzB+68bEh|@n5~w z4X=p7y=u`Tigu~TeHwPK&TCcrRArPau+T$eX-fYTHVd{BDEr7#k>5L6tW!#T*sbzK zw*@{EbOLNy5>lnOnyiRqB!qIY84^;rAR|aFgfdy}Tt!q93gE~X0yMb?fmVz(fkxL zaaM7-R7fk6q9PHVECxAGZx*Pis0cJVwT*pslR}NxiL=z7xB|Iu^rc|9aCweDWuk1; z=*07n8wu!=tt0N<}vh}h3L=P8J#tX}4 zK^mAADawVvbN3^VMNx8rwhf>SQ$EOl7#Y6#7RoHnWgGQ4_}L1p$E5bRrhkt{rF;R9 z+BDBCc!I@3q8?B`2#bV+=y%ULRw$hgc=IeREz{G}t#wPF!sUqZd>-_ZpKBM2)6Zvl z0(}u*^YT=ijUBbEBM*Na`koem+oRKAzE4siVyXQUIu`+EHKM&T?9qV@k^|18bYTg!h0Y&$%6)D81s+h1 zFiVf=A-DFnqdS1P8mXPYvdWLRr(D5kla3t9<9(ZZrm(_7wfyE5Co+&#%GvQkfQf+U zMny>p6eW!ww?~?`9f@x!IoCuL62*v0#E;i}?~d~kSM>{VoO#A9B+T!u08D;jz-FT$7tpPDH0zN{kDh< zVTA%sv&fvK`4{H1QyBTK=RFb;3goBED1VYFFSh^;71odBnxNk@?^1IX4zd#R%At%c z_ezgPVv2GVo$q$@5b;tA*elq(5}|DvoR62BqFYF}5ba7dK!EJd-RW{w*V_{i`8Pyb z)G8xr|MKO0!rqbNx7&9p#WXtItgN5XM3d?*4!adiYpwb{V3)$=j&O7Va6EDCNv`ACe!i{yP`nuU2jSJBhsaJyan_WSa5N>^bgEm-dAFHOXF z&|IO>aFk|Q$$%?28wpSGmx*z~V0c)uHsY2vR7OQ*?r64*iHYgZxsvCo7jCe6kMWF( z_SE!!!V)zJQ2Y+F!qeoqubr^l2#Tv*8PONC*>?_F@1(dB<%^CYywJYuM z(_-f=J@|2SJ_d2U2>z+G4(oQRPrdhq2I zK(9Zqh%p;#3NDfmEysBfLWA!8T(ng=f7PZwrQs^=EZ1te8f9A-lhzi<2Gx%T3H=S2 zC$!vm6PcF7V0$sdMI$uoquUt5=dyj@9v4F3__wy}H5;Ls$NxrR%VzWPaByJSUVf5O z14fHQvaN-r&_N0=&7G*JlgLtifsFvCnHVmL{1K}pJ>@vQ^02XNA(oT5n zQ(6p!TkieCw)i2$@mq&o`izMxjy~?@-}>^OSR)e|!BZ(v5C~c6<-z&2I67&Xa-Q-^ zwS?L`4RhT-?X$(Dr6r%|<#io3J#1Ya;1idrRFoDM1K4u6{U?7WY(StGBgOsFfIM8O z`f*gLdznO9*v?>omM;44?*br6UJDem*Yll?C#7W;mX@lOT9~%(hC?4~?S8gz zmP^-DtZ2rSo*g~AN;^yx?w&Sn&c}z_vhm)gjspPhn9uh0&Q|Mjr6*9MX8AmsCv#)q0N0EV345MD)#XUQ9vKXvjz3nR z)+D8&Uk4*B%RjuPN@_W8b~~IR^)S^ki~qFy@_1dpeI~y}&ra)3&t6v!{_(6Jshz;b zw8Y@~84;3UH)t&I_8Tn!?JRKrZQOXzFTf+j!Ct@Uq;&W5+_~$If#Y>=H2>&M{fNjD zV{9fvv?bh{v6o$>$7lEd*jwAU(_;F3K$1r^g?bbKdGt4~JDnzOt!Lw5$Ria+pS0g) zB~1bnkSPVbUFr8%F_?f$Mn)&blqft!#XRuFd;cvYznd@52 ztUfo{gW0%l3EWQylb51+;<7ISA-fw3casJ4kSqjutAwmk-+`7~jQ3!KXaD_sTN@Z} z!(&ft!mSAYpu4eA0&ZOcmgw7)P$oX#T0JS+p8-T_*jON$7|*x4;y{Si)ZcDHkg;U5y2nTQ?&TD5 zFNqniQ_y7~WrZBLV^H4vttNvzub8p~T$JvIV%W6uCbO0!p{B4{uUXW_Zy;?Z&fZ?* zZMB7CJxPL>PIqmmZEt1foMbnPa9Gi_=Q=5l$W6byZ%E$9sKO_rI-%c2c{}pZ-Q1-z(eYh&pIaT`zV3O1novCtjobLf zptXCwSnpQS(lFfvS&k{*BjtHkKd$$2|GUs$9X%na$C zwzE>}lGaEYpMg8>ebTnf`x2S#*)-O@I$E-wk#GyLP)q~2=}ECX&52NsSWBNiR+`v&K!?BVWe=a=&NBJE13woQ$6dagkX zYs`PA1%tZmfcD0VPU-B|HOXgQx9l{B*8R33AT+#!n!5UmW)^(O*ZsUBy29Og-8bIA z-amX-^Vb#4z4y**WWUba7SBD_v@XvLI{fhX%T@Ay%>2^ZmaiF@!6lWGR6TQtlJDCH z9rhWlxd~l5Q=cA8-V(;?AErpYN65vD$>IWB&%t0T3T0nkD}Msg&F@gh9F z3qH6G4`>dAmx1+)$bPO=$C=#D?LCw&2jvAg)repct@)WREjPj8 zaN?HQkG4kkrWjCT_`5#lval2CaNboc^Lp-}cVXgL*y?>3m8vyrl$1~@dcTb(sD|fqV=iZlitU8#6*`p&b@RO=fBctTh%fikR zbIMAlMk=vMo6grqf(`<4D^*a}c72*UsHvf2JF54m!LDC^7zvR%c1a2Do_Ti|KG(}y zw1Ki1f-dzhcQG$g<);}=m+e%%9e#@%qgn1`sHeG!<>^$9Kl8CTuxYFgxLtSRzDY=n zEWik#YIRCA6lMk&J+0`JnG;F6;_=9M+u(!cAvIq`AMY#_-VUk3l;HO0x8mQkv7_G3 zf6sNMs8kI!Zbtet=vAG9kU|V1MrK6Ruh(b%xptsO0#ZLOswv<8ciSgE);|LhmT{iB zIWv~yEa0vzK0cgBmNdu~cJ0F_yjt>BX29%HYb40LYY-5em_em2?#$j8k(ws)5iWyb zaRe^uKX2No>Tk1rHdH=OMZPbomHTtmk@H)Q?&LFx+l*R}5bf9?B$zi~OdSNyK6KZ^ zBdp7*;BHkB`}F=CMPp^3{7)SM?q_0g@KK^#K~!nNN?m8K$hl42K*S?K zYK+3b&hoido7ZCCpyNk1qm}Bxa@xG})VPSMss$(@?Wd*LUZ3W>qMF(nj7QU0>bL8k zKgx!WYb+}AvmMbH7>M}LQNi@O`=u%V`b(#!st&fTy1|g9?Yo~#hRZtXIDo0YpW6Di zm6uy>|Bbv%Vk^wxa18o)A3Rt;DRA{fTzpb&G(!^Ki20=VXI1bV z(3jU)m74|a{RjpH;om8Cf&7nG1tMCFKz~K@r+%I|bv?`G$GsOpkx@Th|AUz$icgX{ zSyBQO9dP;06!*y9M~JTVuO_YTzKJrGSorrh$mdY8T6tx)@~n@?fk1?hB!U!nC=VrO~W$gBM00UdElNR!uWP%m18>&XwgYb^?cZI|q(k7Hg zaf)2dj4(;OV98o(XX-6$XrJNgv0~`8p|rN`iV`u;ora=VXyVc0gy1<-PJ+5h%2w|i zf=%jo1MY;BJ`tkD!qGDsTmf^sxb@WMZl-;;m^xtW20qOmco3A&%aE*nr+!-17RoTj_2vV{JXB$9oxHX~{rjwu9%7O{K^Ay2~f z!re$(>ZFxvHpPkAXgfvlaBt5&vbkqmFwYei8HWTGO**5$D)RT?&Vgj$shAy)4Q9du-LY>}e}oL1In@CjWVFc8qz<9Uzzq|; z^A>F6s;_}8&7gRr5%89P@`O0`4JO!vBlO_*{{)K!bo&cqx=t3!w&P}1#j$seV`AyC zu^q{MI&!)d*(yqrsO;v-9L0#_+1GXwcA8mC-@M_gvG&xp*~qReV@})OrXNnKm7M}; zH;|HtCnQrI+b`7i8Z{JjYRP^G+>|D#`G`24cO3#^{F>bpy%copqifDE;>|YtqmyCG zi)h3+rn1YQjN4twRFlV9Ow!Fsp~=bSZ*MlUqAIrKLi&I08a-=NL2&0T$p!BcEJRE& zB9U~WC|Fr2BBFwbVhVpm#LhP^Soy}Nhd`r!PtJ5+uf6QyWCPms zt1HX*Z=Geft`}4gMlj4`jz525=u+15iUEsY2uH7IGKJP$ECMJT%tfR6g*gIGG?h)u z9!h}JgF(GYDJ>J1vo}WVwb$F3)R;#g+K21QNIQ_|EM*!f9YCaKN~zReT}8wICJgkO zBB1xkzpY0ZZBJg876H91KcXR~LY%u7H4k|hHzWp9)0Ams{LA&k0V)d zKqhp)5%`@}N?;h}xp4YGAg+iTx}khSH*NMyIx|qCM%FeP!zjRvAy6}vT3~_t6g1By z=H~}X*^|JJWLb0Gdd2wn7>%kfh^6?IiIOD&4WmY`qk z^9C`*fF!;aGP2eqn{D7mJ!!x)gvvVdx^JU=Hc$x{&?aR_ubw~l`Qw}JPQTIWG}V!d zedxS@`~3QqbGL6^`nl0v-{^|OMP1QsijoBr8qWY)BLdR?*Mdx0hH_`lZzgbOT(P4V zay#8bVV*88^5&4g+VzfnnCm3BH!|`RQ{XHOFbwv9o8<*k4ZWM#)gBoLgiI14*oD22 z*jaZ^X@lDdd_o|94WK&Q!VXv9ut?$x9PT%n+?JdV`b@Z7SS*lI=M;)KP+`ivGk`lH zQA05$3}KOAk42M&We%m1R5}8eHwaFPx(w$I%B@6DWC1-GeE~O05y9(f<}-&+i2Y%> z;G{wZ?fKcx;^h-RzPx+-eQ0@v#EanYHfd-Znu^mJiK%FUJNfb)UYO! z9%}jjn6Nz&W=3Kxg6rB3n0?B1?dKnML-a)ql*BIy+p6Oz*{*SkqZHeMuy22P3oS}V zVF^qXh$#}KUf`&ojkXA^0=XQlq7axvdQ!rHwO21zSC+ST`u)zr*!1z{#1z27Bi!9m z{Vmm7SN-jY-nVMy_#YTZsP(L(vgv|)@=+7&4uYe|u%jBbN+)^K5u1pHO14fEH&rf{ zP@cG6C)Z1Dy-2lb`WI=ACvbOiuGyLz>&!OB+l`6#U!1c8Y+d|-J^%m!07*qoM6N<$ Eg5b5;u>b%7 literal 0 HcmV?d00001 diff --git a/docs/_static/images/PrepBoundary.png b/docs/_static/images/PrepBoundary.png new file mode 100644 index 0000000000000000000000000000000000000000..e6827621348d44275c520d318757ffc1eaad5c92 GIT binary patch literal 16405 zcmXAw19T)$6ToBJwrwYO8{4++i*4JwIJwxiZ5tQc)|dN#``+%()O1%@SNBZ)UQKtn zysQ{J3=Rws5D>hCxUeD+5U}_6aWNFg_itVlaQl{fs=)uEs?T?jR}yF zffErkI}yOyjEIHl`+$jyhl!PkiGzrqiGzu0{NhK%cRffa2^kT{Z74)|P{I?%gpzL& zw7s~-FCZX<{(oQKVY?FJZy}f&Kn4H=R2vKXZU7Dh^ut<07@+L7cHs@}hc>wCqB4Dc z4n4B#Ujb9%2LXPfRHAirE=AWJ7H zu~~wl576cAq5%wCi_fatw@xp)Q}9=um8PeroYd5o7ggjsue8LBHg0s2-eY{k9eK;P zQdT|*%Gj@UyNMY~GS#gzbrmzSE+*;rvb29m0$1JOYk;g5DDNZ55+q;q}fKv7EWQMLY7wi6n~57yGNQqE{OY&P<7ADnqFCWG9cOS1$|n1VjS zai`D=+TV}Xqey4a1gu^vP=|LA%b<~+QrZMaAh=6FTKfXKW(`@j5MXj2E;hc+&90qGA&?kqt*I}tpFell^YsDx! ztoTH{#_=SCaiG9a&;x|C!5KvwWRJ;pK=c($Fh@j$--9(P5hQ{vatuMJ{KeOL1c=qQ zq;h@jCkjZ`e%oWZoTHv3Li~NZCAgrP+NBrgCNY*>rEg0!ZZk<)&=?5@iwrYo6c*5V z4QA)omq+DV@OK)1#keu^sv$%iH@Z0*CB+Va6b#%AQ@UW7BSBz-(omDaPqOd31#{2t zP!Wrm;B0G=YmZ&Fe#yUGhtG5o{$-8173Oyonq=LOYp( zMp<6A%A7d;>pju>Pe)yhu(EH^UrJRF0 zKoY?wQIdSw_PJ_NfnU$(BWqdD-QeBENGTK{pWtuds}{{kRJXTas4V|sM$Cg&EV5#etVV;g(>y2u@y({0CH& zIgQ|%`EvW{Pgg!}jwg{nT2DgYw2m^$oBC64fD_aW8Z+^abjc!hq_5T2eq5<#{Q1VK z3)Gn)UWy`wQ*5#JFQLWqEpLdg?RUBQ6bvH#NC!S^58~N$^$)~CVjUiKw7(J_D0Sqy zP%JRRChmPHlqe1mWSAAWWWX}ZZWO{EsE&!Icd*A_G}u#O0w{K*7E>zkF+$S6!4>5AOTZEchUiMIl!*g!r?~=} zod;2$X4|gY-s@fEQ3Y25ou=mxQa>~?>0+IlxU7RjCRqrM2;{QHKr0a9$bqzC8Prw= z;^(2EH>KymVN1#IetSbxTP}b4IT@`QKtWBh`FKO_gY@w7R0`(IZI>pc{$)Y@Fjz>; zFFN_7p7`*Bs%n^!Pmn`p*-%6v0Si_rUv9({NtVwB1)Ma%FRqayjx52U0tN+PG{WAL zmG2e>R!DF|di8{aEQqdJLA;McE;s6RvsRQ2ZV?VGQ41kDsUF3Quw2Hr{7h1*$k@xX z906xyJes7ZVzY(xBbk_p|>6#Rb~EeE%^to zBS5lhRx`PnAjdVO>$J^FlvmK>i5GC<*q6=1C{&8 zxPk-x5k7P;7y^wAU+yyrBLv>unG*7+#1c|b914J6)DSVvu}}`>`bvlp z>zFH~kEtbcrGv;DWXQlNF+$>0Vs5Um57CBL0vX+oILXVnxe|=QP(7+A2$wukoM0B7 zOY<))@iBxoD-FjO6q<&gwhOYwvsvzk4)TQncGE<5X>P_img(KpLaZ!k# z#iHSOr)<=wq>8IRhB4ycyZJFtq7uKb&Jqe`EYwuk;~oI8qvl5;icyav1XSKVLFZLv zO@hK<2~+jcd=Ybd9kA8!7SjU2!^&lG%H+`K?k-meuQ4cUh7q<2b1g!A5APz0#b^aa zh5kxoZck?yGY98FnUQSYT>`xA^waUSO_7x=jgrx&$*jA;mvVhgg+DQ|&I39MI*^cX}k@YzFbZ#3GtGh~icK>S(?h~?ohLduOX!x5x7JCf-!RQFXHFFFR@^l7g|gTMrLC`z2`c9lZH~85vCtG{ zLAiyf;XE_<60yzxOL;N5VjITYC_SqlkLk-G441PySKj1gM51Jjl82UVS!||J2pU?) z^o;oa(?YC;@}-~%M6Pjevyb$(VG>fC4PRNxqHiO?&_gw$Fu_M9nTeQHHV+=DsL(=G znZSB2hanIEnKZ)#St>@Et2GbSDTywqmz>NcPExA)qWx(~{FaUEYo3!1@=7|Qfp%pq zoZoO>5~%f?|Gb`FEA!*BAAarkoJ(rLaUmI&wU;?Q8B<#HPHfGwK0aSV)6A<7pJ3@wKbeFSgUL{^h_SZ! zow{9rcqlFAggD^dt&Knu5*#J2iOym8SbIRAaU4}Vaz!ybfRssc$^Yr>2;3Nh8rNbd zPB1Lqfp5^pufc0lG9l=`!1$A$w9bf#nV7-j#(>wBTXDIDgRXynG?e^G@V50?Ws#ac zD{{Y6+=Rz`J3K?l@ba)h2$z@jCg{WatrAbW8q2c8Y-vKQk+(rY3KIvf{!r1SLUCHq zK9=!|c~;_j!DtrJjGY!~;qJMiUBs^&kGah%@mXUMRREExhj%Wi-=K( zg}FEf1d+KuevLf8wSyR9?ll375%VFfK!scSju5$Ig5iX05XD)#2AtrTV4M?(Qy|b@ z&jfB?tz7(Uo-uswh1!^pAq>bfvu1o9SbCl-Rk(R}3a}f6LK+ZH91HS36kYVsEP*tV zp^7VYD_5_i%|xRMag^|V#@{~Aalx|YtAPVhL~?tnXB-TlJFtbx6h(#J?T>+FzqMEw z&id+1>DLJUSf*G*kDlIEFWJ3%>@^(_R*DiEm@QZ7{MCOEv$Dr|@_S&{(0RBRi*5RP z+t&Lq0z`?S8gbx@);XB!Q#kB@Kjeno_ta3`;aoU*(9rR73-lSbE4JTX)N0+lVQbww9Ikrl#p`T#F9)g;Oy3Gv5vbnQ z)wBjws>CIXSXC*G5--z!w52{k4He?@2b>4PSOMhnrK;$mCM%VmtPFA4Ys_T4D(L zStN9jQ0T>uMs;EIDkhtbYEp#;_lvYJJ694)0txLqw%hkS(_6vKy5L)j#zCiT82*A! zfJq%|&n5~a-=n$#kM{!%t4ETHy>w;$Dg(zR-y>pbB9B}@0+fgU*f>9^MS3noCwX9@ z3-u=tewmP*gqDOuD!bPwJwHw`=$8|u{EI%Kq$(xJqcPc|EFU0=d}tYuRa{Xe3(PE@ z-=>L^xy;}T4^gyP-r5o9KQ|yKrK&_ts-hBH6JMh)%cmeA60Mj#a9Os!Ry`Ri0=eP4 zedUC9|J=6yo(&`4*;F;l4vLfDO(M-lP=B@3(J!4KAeV;|H-}s_Umsx8SfO<#O8LZX z|H~xQcY@#k3v3IE)>ibjfoicMFfNOe=J3|da*68AYV8oML698_m511ck4%Y$&O#>? zlm|+`jRjgzvZ^5Grf{g&3s4fYfyT?o^114ua z!Zx(-7%2F(q((=r$jN9@C#48JUkqap>REqF!CcbsChWr9Dei(U1Q8klhHW6S?lb{} z#ZhoAJkd4lPn&<|9gwV}Aq_TQ36ZN$QDER8kbr!r@QYBHp9zdY=q|iON!t6a0i*O6 zGL|0@YK{T=RvUX_2j~^3oHV;QJFRAG9qH`zRl3_RB0L!wo}WbZBkr`y2Rn8MTQXvL_! zy}P77^{(>m1w81x2zH+jesbH4Uj|b1b!lJp3{)bNY!S_Lv>-2yl5)t1uaA+KTPN;Q z$dfN4ub8Y5Sxa3%zTlqMB@gRNb_eFGw3>qTHrwnCW6x4-2Ga;TuA9#w?#<@KHjt5$kE1~zyJ8XS^x3#_tcf9Q zAyRZ8A}|u#{Nfir#1GEB!7VZP!yHVtyQMCVq18Wxg92uF-jBa%SB+`24xTgtq zW{*Oink%yAk0_6_sRdY<>*%Vo)|>afCNtc1w_}c;&YnYyGR^-|KApK4e8n(UA}5TF zr9$HUV*Y^RJN&y^&ZPM;rg8KIo%iG8bw%pZ=SVB6TgPX4@)P#GUf}v}k&!$(Qft&X!Z#WiE|2`(;fTi_}BoAa6)b_A$i%t|kxHvXF{vKWbpD-qJPK>*b7A7MP(gM;GUR3{tG!_g%6l5E4OHig@NliGaMy^!*W(PrhC-_TNW^HGwhZeXPB znLlQbU@V+!^T8PMZz$v?`e?%n)PF`NI&<102vON?6slDBPI|)I&J{ZJdR8lU--#wx zck|_vZMCq;x)5jem<&EgVHAC}8>Jdz*_b76+{J2nW=;CMZGL*inl4z32k`nj93{(D zm2m~|`0$)9^tuu}K5Y|V16<#)4=f1fzU~4bXY#FlUFJ8~VHvjVW*gPyTHQ|r^cn!T zsZqS{#HSNR7xK0GCgA1dIbAbGBdn1STXWHyd>_PY2N%gU7Igd}bkBwdt75tIIg=ho zr{%V`Rqgz`nFJYO1aD4LyXKSi_*K}CKZd8;J05mZJGrh{R2NmJu~^<`4>~Ccr{86~ zKqftQqaPPuo|k`MPuPAs$>#jZ)?wM<)KUNWbqCdUwHTX8*J`k`l6_Rr)%`Ip*8Tj> zNt&(ec1w@1y6wJYhPvhPI)D+zvfy{8EAW`}aU>uGksbQSYo(f<#Om|uXpeW3`zTuo zX|3tLSWnLPsCO3M+)FC^`Fd?QHP*c0b`iG9>eq6<6ryP$=%PgTTEcpmVyU#$vSM>S zkQ5Xeg$kE5(wH@caS0CqUwn*8-a^;|B(LF4@K@b^#XhP2M7+=$78$5TIj-ms8JRL) z5vs*h{~-tYCKh5BiZ0Xgh;UU=wn>x#k=i>Y7gz{jN+o^B=sVT{rQH%sc!H-st$4yvhLQ$hv3=&0R8y{3H@?4vUaO=-4|GQX zBx4iAQwDkl{8DQGM4rQ-0z2;(9tG(#pKBLe$%Y@r9tS@V*FO(@$jPH6`Ap>_cIPOz z{63^|G}{L&AZq$wPrf`orJp{G*0<7~WZ#_SD$W~UTERPuzfPeXYK5jfUw^#LQFga+ z{8lObx~jgIjrt^TzqB}Roy>%^^*K3Ex*}K)5t+*$FKW(hF6>_PwefvGbN4E1bAR4T zZ8pF@nK`j!vwZ0e!P_!?7-vN6c5tD4y)18-fKYp3i5&htv0=m;e5`Iq*_pnb+GB)u@@FyS>y1_3xY59}$ZUJ~qmt;y2tuhiWdoI1=TVyaCbPo_fX5{)%!fRmgMi3bCJ57}xUB#FcTR+`Nl zg6i2HVd0^|LYvOH1yTv&n86Za8rvs;DU*!3ZkMJaaa}cr*>hJC5vWDYC!OxCSO1F`jj$o@r1xw`qa{))P>;^Nxh~pdWs2W5*}h$B;-` z=}a`k>D;S(+i9V_&p@c$B}nK)uA^6L`=@Xi%3H;cKR2$aaon8+Dq|9kwiBB(yflKU0e^W)#uNz zGj6$Pl}4*2!Rb^$1e7wG_wf4_|L+Wv#e6D`NUu>XpO2asD=13^10^NGH1vk^xCFiZZ(v+=uBemqxxTH)$;&=xp1?d zemx2BnE>88k~Wwr3+O0#TP8Rx77~8*2o^2Ar+79>^!`dpXmCO4b3?`*G|bRO0h==a z%gh9~;=Xunq|_LUp>elp5wC9vM9A$R0?QvXqX&Za#}8bvusy}jS4igJj_2ANt^`^lMxJXD^9UjO8F3D+Lsb1y&)5ZS2`+2o7XX9ID zHrX9M#@Kwh?DPxr{`RcgmcN!)^L2O^R=#`w#V_=99Gx-#Bl8Q45s&|=??nRbCV5&;#GYa<}jwX>B*Gkvy`-(pb6+ z?w4m%Sx(Yn%JXoJKQZUyidYs0R{9hUV#mph~^Yh$=h>D*mn8YIj(3 zLVy?kZx(uc#T^K_HTtXN&jlsj-}lEm)CF=DAUKq~`ly`ph&Z2aO?IY@MSZKu?jv9kRNFY@=oGz#qo*X{&mHc`Xz>zw=KsZ@J;-n+ z;TXD+6V%QnTu$6u|xX|2p`;BTmLpI7f#;PoBitoE);Z`-atY5%49Js+s@)Io%H zzX**yRj=)3yR3TSn(l$KUGDap;oc9Z9W|#H8Q=BPXeb)EoE5(;jMDa~)wUhSw5}pk zNHcP;tc)lvye|W82!HdGQJUbfS?^E6{;7ktnbP61?JG6oCqwmusSEeZ6UprQnDJU@ zxB0c*{Oabi1lsA)tDwzDco(`XYa>0=^M|nYD!(8nY4-tL2FGD5PsD3v)o#@4-mWUa@P+5pwWpvvoPPRi_#7GG?#j4MqJ=5ElKl|&;v`?e9y@utl# zh{)uAw!F&ZvIu?fUU$Z=fJP|80b)_ygrCGebC`yitGR<0#3n^b8>-)rCs$N}# zRXBWS;i3mt0$8JHq$oUZ_&=zmJ@db@AK`B@j;!M zg3{VUruO(5BCLnfn9vee+XPqaTh@Q*Bdfm-%F$zE`5Q~|KT@Z^_BoGR&60PAP(<~w zANerJ+=qft)gNt=gzTYAXUrL?4C8~^q~c+7m=X*{4~!F5lM_poRoFS1D3#9Jc~t1n zCnXO38TW33k;sx5btt&%a>;h)Amndfr^3d1uaPu znJb7|t}w52t4}NYc9x)^PJV0MPf(>3FryA6_D?RU!rY(jV2J4Ou;skmZ!G3`H+Y|M z^2q(ncmR_gzsduz@LW4Wa;ENxJ8h*~?kGP)!7!*C?>b$FbhJ8vMLPWh9!l-H5 zpix{=4rw6C`3VSGcuD&V5*}k=qSwfJkbAJR6KW8q+on6_bHG1lyMmulQLK`JkbUsL zj$t)X6Ui;4n@6I|PI{d2gs&z+qj`xMEq`N%iL83ppybD4^(&6k-T(b1Ff9)woQ*NK zfWvC>zMEoM%OO==k~@6HdGWdvdBxCPn6I%H3AiBwGe`ATg*iF{dnZM67V2?5=Ii5} zax+o|(z4T}C^vA!A@r(EPLNHyS4(`*Sl;Zml{T3WVGo2c-rW!$0lwN~sK-heJ4p%J37asm?T`9D9Ko(TKWY;tMS;eas zw-C$_mfKV?qRx{B7lnvIoPj6HkE*Z=2_-Hs?ABT~nSxw=SkA$*ltVE#!4>VKaAC}O zn-kSVI>es`MWgQ|S1dl@GaQ{G$s5EKNl0MmzCh*sje`LzsCFq=%B&c3PKOAB|4H~f z3lcMII?$TZBmVTUTh|shT>5XOTtCb$ccE(Vf~Tt0nvZe#^+{V7%ZQ2%QglgOD1iH< z(r0h86Z5`FxrIOZ)shO839<34F3u+u&SOE;5>*0dGH>|b#)`-s5RitS@NQ-oAv)Mq zEL(Zg+(Y|WXr`>-ifI;F_pN8YJ$4*{X%#Hy#$v_SqG33+cuk0|D>+FdD~ z^)sLm&YPWh0QU*$C#DNz{BC`x_&d==0CQE4F|?X8D-zHo;h*wxH}}Z99YS8c6nz1t zV1y6x3;q0L(YDER9i&3TKi^>)6rm#$xjq(i$8aBdK$@*i{Z&o)zg-~0gk>SaVVIC@ zTS$?VNzaOu5UCi(Hj89U@m%sV$Ug+JJ}{bPqPazH>TJ>Ibcf3g(@fmyw|%H3%#^;$?_G2j@78J&a#nzJp3oFv-%d>8(w@#U37y)XpA`8mp+;! z$Y>OSaeaG-lgk0o(AJkn?x>LxgC!@U?1Lda)e*kt$${Y@nqKD7jRk_PhXExCuEJRI zC=hfLkQ90Z4A`;6&nV)hQu0_JLd5{3S!(~O1sg&)c8_#NFg_Cp?6}w@Uh%@PpENQF zl3r5+)`(1bF5#fWdxeT~v53)Qb-|YDlw>tvvi2F}r@e{SNVd7%3*0pq6DfbunKppA z!HI>aa(%z8ukdl)kPMg7WKpa@!xEo&Ohing_^6~wdA|uyAuY5#{1tQE05*Vna^~-D z6G7qelcA#z7MNT1Xv92TSc)H56atqh!w|O8tX_&g!$U9>ERStkDMuIw98ffpyNJ9e zCb0XHU3#3?z+bzWgc!ZPPbR%_uxlC&H`b2uWHmGEbaAd%M4T#(86`PD92n9k_|HBV zgZT{@j(l$Wtrfh0lDB-Yf^_L>O^sgy@b5yO6s zg~gsW>>cLi44Phh-5%D{>U52!Fe?)$x#Tz5l5#qs&@h(0V3l{Kx z0t4!1BK>kt$;?*6S{mKAnQ#h$>=PxL4$EquNl_|}KGVYkJcdoU?U2%2`SOqgz@eyz z>q;fU1-zu3eABUWi!<8@s4x7)mB*3sy9thl6>FfQdIQ-`OZkN-kBRmF5d7wqwRAj; ziI<6MaFTkfm5jXZ5fyrdajSIcMM46YlH_Zu(k#$4QuH^N6gkx@d&EKQs$Q#4co?DD z89n(CCO3G5 zn!#TSLO|`96uAcOdcCVvRENwBVMjp@Go>gEB`#lHxkM*fgjQckcCQ*iaifokTmdUw zZaK67C&k$(DSe+(-9svR9loRgVpc$qSVXXpne&ZBg8x4pKYw4%IJsq-ucV~DMGBH!2E-JV( zUYKPTfVGT5Rf)M!xiG~(9D^}*2w4?YAUUK!n%$6-i0Y3xE#fXGRXfY=qFY9FaR-&j zWT9YJl3*e!Bfg%58jC@1z)Z1YveI+UfMpXDusAU#khl@EYbSwJ9uV81Up#fHa8kH1;)W@iTE z!OqT(JZKO+DaKVC6!b=KZ*Lw(PhXX_LS)_5g7X?49v%+O&P=CYDBcMd7uUIWkG~T2 z)k^T_wjo$s=SgM&AV>YVbS37PK zP~1~p-=@Yfx=B~eYS|dU9sCYxakmTI!-s?n_3}TJb1)D{v6C|iRotSc3OlJ>!oT|V z%RX$9T$*|T&^=uc%1xSX5W;ZNx7m1>prvLK`sQjw4wYuaW@;|>_9M7VsjeVb+3_7p zY5I_g(-GWT4&PElLyHGGM0E6_2U|UR>+eqV=5ge9D5)a}S#|^D#(9-%C2L?4+_H35 zzk8y0&8LinXxl^jKsUzw3O~<0%=_IDcMx=|lI$YAX9kMvkr&?J@U%zFUmhvnZp!|7 zfK@D8nrdcLVg}t_cYEYE{H^83H6J7J#hB(6V5N?nm;ImP^sWa4up(4;Je&l4#om4m z{PP2)zSUMP8G)S6^#G-4X{pN{#HeSij?-FD>Zi`~Dth?K)5f|iF7rjFH-ViuO;AR$ z*EVLV5GaIR?Bz*pdUyTaTiHuw_r6cs%^$5lw{5azLYsbZ=<{NXqOOSLXF*(Sc<*Ls zRft_H5lJD7Z_g>>*HcxSqti>aa}Y-^m0AnVir*6e2aIX0tKQ>cnRfwu>$fyH2r@N;IXZ3S4o>IC%WW^L1~s|V2rNRGrJpA3F{JBM zDVDPgj=A>`&x#Z}8k-oq(PALb7R{OTUv#dw(pT zcNf~Bhk^G#RdB(y^4zl(Y8F$R?w}qw7e*D_Vi`)th#ytu?xfFBatTKn3z%Y=JWcr- zGvax?IxDr^UCRpVZ|&ymV{(-jUx+$!dvQd#-MM$34yzV*%T+0B2!cbbE~kT*HtS=!RgZcTF;&v!K1aDU#^na#p%^l#Fc-qRX$ zU!*;hE?pqr5rWwAaa~qXI_x4$20p$hQ#k0;T56diCRsiCB{|<}Ec-GONk)3CC2dY4 zRu$GJ8AgqPU3r=}d&#|n#GRVkszX;07A4|Bls&y!d2_GAIBsnI zBzW!+_#(Xf>N?&W9KXd&6#4+JeI`fPgui7DJUXjhQi^YF$`j^*4tHi{Y;`O?2q@rN z4*<3DJhLKL&OSm@S8t1Qx4f>9GW~Nmx)M>YX>McEp53n{$`^c02RoH9ph911oafw% zsQ{J+^!#{npcb3CAhgzfYrf50xKWr-L_kXwMhRnhwp?8;5{K}BB3=7am^EVSUNGmc z*Ep|sT?sqQvcfExjFJWW0+u8Tc&{+lMf4~IRojbkgcag1NtK$=Kgy3w6qaT2CQxDF zSd6wM+6vr%I^LAxIc^&?RI`Fpn4<0UrCQ@%8ouzv5SVPa^-mjs-_tZo>Bf}_dR%*= zzL#l=^1mlG=i;A2E{xZzCkNhv6nR4?$ZoAr7Czgz5|NI@UTq<(_%!ymFl9h#@?fc> zPFA5p_W)_&1i>WIGyDp_n-qB4RM&;x5O4{_T(xY+=*s#_cjR0a*DVuCY|)@ z6y`n2Qy>^eMK<|1SbpBZ_q}^9pGO%JPcHH>tI7hCk5X9^z!`tFWFM3ZXqrV$a+_&n zgdWlX0%em9FEY@OgT!_1$Jm=OM4W)w#rOycgq0MNsA#Dzl{K|B6}6Ss)#c^om6fwX zXYfyn$@cVRm>lfszh?1W=Np*KgIJK9uz$IxN)#xJM`gfW3Y;^#7DS5aamVH~;X1#~ zTbLd+bnG;Zn)f{n`4B^%_aF1020bxgGqbYoQRrw#B@p0ftQws@@ayh3N1gL~SRQ7r zg zeEgZOCv%s<_MLt2O(e_@zFQeQ+`?;II~TX#qLv;&1&6CmQ?8DPh`2>+6Z!zZ7&0*# z{LZZDRlFX5VqcLkK+j$go8dW+9;j`yhTpaw;j@bCBOMz{qjy zc=YF85JEES0{?q0=+jGCW;3G+X9yFS^?r&J^1TM3Y%gvUv9ZG2`~h1eI6NAc6CH!a z$dM#I7i^&EClM9Kua0?rVzCL(z2id`W+A;s$ejIC49+7k$$3-^xQa97b67lY<jEn3CcK+u7Plle0qXoNQ`(+C8Gy}mI9XNp%7$_w0o@(+F70|u$zR{|+xeto0LEv&h~ z(u@ZNAg`7G-^CX{Hp2Bcc$8M%-$=v%B<|n}dZLHKid#3(E$YiBPWJZZ;&JDO6m&*L z+sqUH=q~OD5|f-5Q=t>ax9^WNGmCF!tCzT@xq%*6Xyd~qB7*zMx3-GgO!{`my62Zk;`J@gI% z&{eG$bs6@L&$^L3lH4s)yyR-PUdJmDod3Db>qgf}xy|=_$=#L!f6^u0_`k6Z(nf>P-T1SWbAUVNQ1KrRkA(X_GVrE&4?q2O|5xuT zHy}EcDa0d1ohL7fw{Z<}uTSZW$G5S6B%ah$o3F7-=|(R)L^IfD;ib%^UA|vMv;z4q z8M@(WL2;|JqN3tVaZRY>jQjJMo&Y_&IAXL zYj0M5`IfXNvm1KIJmeArf5(m#n*M|F!6|6OZquOY*LOI)M=UE{ji$o&I0bJyu_&$2 z)5G}b4(^)D4A}9c`#6OBB+^HGRXKg|z&fGBh~}%CL(uwEZRY1PQV{YP_l3e+@@Ymy z)@pIz&0=l(p1Q+hEO)VsK!vNVb+Pd(*dsS$m3-#C{IOa`>tHki&dJj{;|VvjCkuTd z-68#K!v}Eaj2c*$P+auOKOyBu+bR_UV0mP$jbvRS|rC2T2(r{ zH9%-}JwTi-eD$9dGhHAHM8YkpI3$vvm=QdCl9+H@L0muc*7E`#@>Z<`oQ5Eq_l zss4sh1zR~Hr)OLq<-7u6R9&xWilLnlQCg4uf4t7`&QuepfKd@qZD>;DfB)@j`Xv@q z+F`xKHand-eYBOSw4yB(Zl3c{{#U`mwg-K7qTO`SW8osGD|B3`m>_%gc7)bckIT?j zPv4;$_u7C~j$rK(zgA#e4L5tqg2m>Z`-Vl!K_+l)wHhmPJM$Mt|M1QXQ%@92q^Vpj zJH8+XA4|NzWv_Ksr1P5&F!)jooGCq!b)t$lAJXyr zr=U8Q?&~BdCob6x>8`d<1F^Ra?0|B(t>Ua%I+op9@nL(h%e>a~}O{*Gav zM_EeDXS?eU1RJHL@2Q;xZ|j4%8}1)M@)l%$&rArJ#bTddZ>~7A+To7M)n4Q>Ha|XM zttEGZm$RrGl_~v{KpspnJ-Pv0wPxGNv?^=W8Jrhoac|g&vg>fN78-U0=v}Gs?sfYS z3?-XAYAy&HX4I}Fv|&e|p=@e_D#5CSkc06R^h|LLt|m^18(NU$_QGpr1maqUb!%oj ze-X4$PRo}A#-1LT$75EW#_1FPg;(0=F1^188uq`z5=eEZySN&vP)~MtX6%WcKI+h^ zKR)TWqf`(tcs1ey=iiV~VkG{Cfa=kZ>7!SYJ*K^`zHxbs&?qcaDG1N5w@RcjEH)76 zVp6z8PV>ZF3H}s?$6Z&&dUCxd`u`FdfQg~Y5Dm7gFpJ=UD5neLjqyKtmf_#=2FJ3? z(I1cesL2FuIrtR#{*}XSrnh%@7`}C#$X8D9`a{Ez7LL_(Smxnh=miZ+agFnlVODT4 zAwN3ba}WHEl3(!hb~HnRv$y+Y^y~q;Z%=5v->D#=l0`NB37E^kJQv=|Gh-euE;g)^ z2aTX^LLI^N_TRNRCBkMWS9|;C8zER$kA6U7HE*BCwCjJ!_2OSg zd8RRL&+oA?#rNGHLSslB73;CTQzwDWMcnTBe*rEqJq=WB{NPP+urzx>Y1!i2T5~A* zdXT`qPJuUVm!9o&f15;f{5=ASEb6gUS@;kC5?JuU8nLwm zTPz+ae@TXU{xN`vdg^VK#CNDSAQYO!WrIokmvZyb-^wz%i~Jsp`I>)xg=oVPvS74j z1nT{*gliwsQfR*CZ8koW?jP#-rZpbUMC?gmKL3Y@oLIWE3wyx~JpTt%LEQ5m_dxNu zsY9FXuLjRl+-jckD%5td9_&~a?!}#3g3UHcgTLBW{hl7R6s2RAn2>UoSr&L)0~(!x z=s*QYF93QZAU1^_d2*Y)pv&N{wp z<$7P&nZ9>aFbAqYTyvbVdz9L8%n0p4nW7`)QeNy!JY01a__O~v%)=xG!D{t5_~@%G z6V0=w;Q|y{MT*h@n~Sr&9F@Q*^CNU$8qbPpr6b~K&X z%$=zAG>V-pFH~md(<59w#`o&xklg8GL4d;69AA^Rt_492)zg*F2f9@4>st&$1U)N#?%Nzh2aLrug2gwXx&ZgbE!-yxo zYeV$BU-c$pRJq~;y%sUSc3Co}1MxXjTEc}!KPLh`QeK39{vB@0F7l>QffEkuhh$<` z9SWTD(R79}W9X|;U6^Q7f7BPWl&VQ8KuLm6S7FU!;Is+I zBmPvOsVXxO=$ZTLz%)bB3|p!k+a#9c-F~i!eTHhS$@0CPHTKzbaeZ$%S|$Y_pOzPE zkF;83aeekOI{Y^w8-vvQYsMK3&D8wUazDWNdO^Y#Lmuw4J|w$*xQE^|Dfk_8YzvCF zd$h#n^>IbJO|4GDi12~zccYZ3tt+VB^ykCLf4jI5M^FFuW1pZU8EougB)ub$yXNbB z0ZM;|Z{}RJZ+wN-i1iabFqXD@jG3i8I^tG<-<)qks`Yo}W-sPz&*xaxmBVT=Ka|}Q g;W74t7kv@7faTMBDu#!B@52X@5RnzG64Vd)AEex@G5`Po literal 0 HcmV?d00001 diff --git a/docs/_static/images/PrepDetrend.png b/docs/_static/images/PrepDetrend.png new file mode 100644 index 0000000000000000000000000000000000000000..7ded9b9d7033e0b08c36c3ae3d43abeb6f859661 GIT binary patch literal 24083 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 b&TcXMwyjQlqkr??`AP*@Rhcp=lYsvLx`{#9 literal 0 HcmV?d00001 diff --git a/docs/_static/images/PrepFinalSave.png b/docs/_static/images/PrepFinalSave.png new file mode 100644 index 0000000000000000000000000000000000000000..b86765278304f1a7bf3975d4cfec428fd737bfc2 GIT binary patch literal 34482 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

Quick links

+ + diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..5fe2509 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,53 @@ +API reference +============= + +The public entry points of the PREP pipeline. Each function's documentation is +taken from the help text in its ``.m`` file. + +Running the pipeline +-------------------- + +.. mat:currentmodule:: . + +.. mat:autofunction:: prepPipeline + +.. mat:autofunction:: pop_prepPipeline + +.. mat:autofunction:: prepPostProcess + +Pipeline steps +-------------- + +.. mat:autofunction:: utilities.removeTrend + +.. mat:autofunction:: utilities.cleanLineNoise + +.. mat:autofunction:: utilities.performReference + +.. mat:autofunction:: utilities.findNoisyChannels + +.. mat:autofunction:: utilities.robustReference + +.. mat:autofunction:: utilities.interpolateChannels + +Defaults and version +-------------------- + +.. mat:autofunction:: utilities.getPrepDefaults + +.. mat:autofunction:: utilities.outputPrepDefaults + +.. mat:autofunction:: utilities.getPrepVersion + +Reporting +--------- + +.. mat:currentmodule:: . + +.. mat:autofunction:: prepReport + +.. mat:autofunction:: publishPrepReport + +.. mat:autofunction:: reporting.extractReferenceStatistics + +.. mat:autofunction:: reporting.createCollectionStatistics diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..fd15da2 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,95 @@ +# Configuration file for the Sphinx documentation builder. +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +import os +import re +from datetime import datetime, timezone + +DOCS_DIR = os.path.dirname(os.path.abspath(__file__)) +PREP_DIR = os.path.abspath(os.path.join(DOCS_DIR, "..", "PrepPipeline")) + + +def _prep_version(): + """Return the newest version listed in PrepPipeline/utilities/getPrepVersion.m.""" + path = os.path.join(PREP_DIR, "utilities", "getPrepVersion.m") + with open(path, encoding="utf-8") as f: + entries = re.findall(r"changeLog\((\d+)\)\.version\s*=\s*'([^']+)'", f.read()) + return max(entries, key=lambda e: int(e[0]))[1] if entries else "unknown" + + +# -- Project information ----------------------------------------------------- + +project = "PREP pipeline" +copyright = f"2014-{datetime.now(timezone.utc).year}, Kay Robbins and the PREP contributors" +author = "Kay Robbins" +version = _prep_version() +release = version + +# -- General configuration --------------------------------------------------- + +extensions = [ + "myst_parser", + "sphinxcontrib.matlab", + "sphinx.ext.autodoc", + "sphinx_copybutton", +] + +# MATLAB source: the pipeline folder. Vendored third-party code is parsed but +# not documented; api.rst lists only PREP's own entry points. +matlab_src_dir = PREP_DIR +matlab_short_links = True +matlab_auto_link = True +matlab_keep_package_prefix = False + +primary_domain = "mat" +add_module_names = False +myst_heading_anchors = 4 +myst_enable_extensions = [ + "colon_fence", + "deflist", + "html_image", + "linkify", + "substitution", +] + +templates_path = ["_templates"] +source_suffix = {".rst": "restructuredtext", ".md": "markdown"} +master_doc = "index" +exclude_patterns = ["_build", "_templates", "Thumbs.db", ".DS_Store"] + +# -- Options for HTML output ------------------------------------------------- + +pygments_style = "sphinx" +pygments_dark_style = "monokai" + +html_theme = "furo" +html_title = f"PREP pipeline {version}" + +html_theme_options = { + "light_css_variables": { + "color-brand-primary": "#0969da", + "color-brand-content": "#0969da", + }, + "dark_css_variables": { + "color-brand-primary": "#58a6ff", + "color-brand-content": "#58a6ff", + }, + "source_repository": "https://github.com/VisLab/EEG-Clean-Tools/", + "source_branch": "master", + "source_directory": "docs/", +} + +html_sidebars = { + "**": [ + "sidebar/brand.html", + "sidebar/search.html", + "sidebar/scroll-start.html", + "sidebar/navigation.html", + "quicklinks.html", + "sidebar/scroll-end.html", + ] +} + +html_static_path = ["_static"] +html_css_files = ["custom.css"] +html_js_files = ["gh_icon_fix.js"] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..463ef4a --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,34 @@ +PREP pipeline +============= + +The PREP pipeline is a standardized early-stage EEG processing pipeline. It +identifies bad channels, removes line noise without committing to a filtering +strategy, and computes a robust average reference, with an extensive reporting +facility. It runs fully automated, as a MATLAB toolbox or as an EEGLAB plugin. + +If you use PREP, please cite: + + | Bigdely-Shamlo N, Mullen T, Kothe C, Su K-M and Robbins KA (2015). + | The PREP pipeline: standardized preprocessing for large-scale EEG analysis. + | *Front. Neuroinform.* 9:16. `doi:10.3389/fninf.2015.00016 `_ + +Getting started +--------------- + +.. toctree:: + :maxdepth: 2 + + User guide + +API documentation +----------------- + +.. toctree:: + :maxdepth: 2 + + API reference + +Index +----- + +* :ref:`genindex` diff --git a/docs/patch_matlabdomain.py b/docs/patch_matlabdomain.py new file mode 100644 index 0000000..4961256 --- /dev/null +++ b/docs/patch_matlabdomain.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +""" +Patch sphinxcontrib-matlabdomain to fix compatibility issues with Sphinx 7.x+ + +This script fixes a bug in sphinxcontrib-matlabdomain where it uses +inspect.get_members() which doesn't exist in Python's standard library. +The correct function is inspect.getmembers(), but it has a different signature. +""" + +import importlib.util +import sys +from pathlib import Path + + +def find_matlabdomain_file(): + """Find the mat_documenters.py file in installed packages.""" + try: + # Use importlib to find the module spec + spec = importlib.util.find_spec("sphinxcontrib.mat_documenters") + + if spec and spec.origin: + return Path(spec.origin) + + # If that doesn't work, try to find the sphinxcontrib package directory + import sphinxcontrib + + # sphinxcontrib is often a namespace package, so __file__ might be None + if hasattr(sphinxcontrib, "__path__"): + # Iterate through the package paths + for pkg_path in sphinxcontrib.__path__: + pkg_path = Path(pkg_path) + + # Check for mat_documenters.py directly in sphinxcontrib + mat_doc_file = pkg_path / "mat_documenters.py" + if mat_doc_file.exists(): + return mat_doc_file + + # Check in nested matlab subdirectory + mat_doc_file = pkg_path / "matlab" / "mat_documenters.py" + if mat_doc_file.exists(): + return mat_doc_file + + print(f"Searched in: {pkg_path}") + + print("Could not locate mat_documenters.py") + return None + + except ImportError as e: + print(f"Error importing sphinxcontrib: {e}") + return None + except (AttributeError, FileNotFoundError, ModuleNotFoundError) as e: + print(f"Error finding sphinxcontrib.mat_documenters: {e}") + return None + + +def patch_file(file_path): + """Apply the patch to fix the inspect.get_members issue and MatScript args.""" + if not file_path: + print("mat_documenters.py not found - this may be expected for newer versions") + print("Skipping patch (not required)") + return True # Return success since patch may not be needed + + if not file_path.exists(): + print("Error: mat_documenters.py not found") + return True # Return success since file doesn't exist + + print(f"Checking {file_path}...") + + try: + content = file_path.read_text(encoding="utf-8") + patched = False + + # Patch 1: Fix inspect.get_members issue + if "for name in dir(self.object)" in content: + print("Already patched (inspect.get_members), skipping.") + else: + old_code = "members = inspect.get_members(self.object, attr_getter=self.get_attr)" + if old_code in content: + new_code = "members = [(name, self.get_attr(self.object, name)) for name in dir(self.object)]" + content = content.replace(old_code, new_code) + print("OK: Patch 1 applied: Fixed inspect.get_members") + patched = True + else: + print("OK: Bug not found in current version - patch 1 not needed") + + # Patch 2: Fix MatScript args formatting warning + if "hasattr(self.object, 'args')" not in content: + # Find the format_args method and add a check for MatScript + old_pattern = "def format_args(self, **kwargs):" + if old_pattern in content: + # Add check for scripts (which don't have args attribute) + lines = content.split("\n") + new_lines = [] + for i, line in enumerate(lines): + new_lines.append(line) + if old_pattern in line and i + 1 < len(lines): + # Find the indentation of the next line + next_line = lines[i + 1] + indent = len(next_line) - len(next_line.lstrip()) + # Insert check for args attribute (for MatScript objects) + new_lines.append(" " * indent + "if not hasattr(self.object, 'args'):") + new_lines.append(" " * indent + " return ''") + content = "\n".join(new_lines) + print("OK: Patch 2 applied: Fixed MatScript args warning") + patched = True + else: + print("OK: format_args method not found - patch 2 not needed") + else: + print("Already patched (MatScript args), skipping.") + + if patched: + file_path.write_text(content, encoding="utf-8") + print("OK: All patches applied successfully!") + else: + print("OK: No patches needed") + + return True + except OSError as e: + print(f"Error applying patch: {e}") + return False + + +if __name__ == "__main__": + file_path = find_matlabdomain_file() + success = patch_file(file_path) + sys.exit(0 if success else 1) diff --git a/docs/user_guide.md b/docs/user_guide.md new file mode 100644 index 0000000..1c90c65 --- /dev/null +++ b/docs/user_guide.md @@ -0,0 +1,359 @@ +# PREP user guide + +## Introduction to the PREP pipeline +The PREP pipeline is a standardized early-stage EEG processing pipeline that focuses on the identification of bad channels and the calculation of a robust average reference. PREP also has an extensive reporting facility. It is designed to be run in a completely automated way. The major sections of this document are: +* Introduction (requirements, citing, installation) +* Algorithm (steps, meaning of parameters for each step) +* Running as an EEGLAB plug-in +* Running as a script + +### Requirements +The PREP pipeline relies on the MATLAB Signal Processing toolbox and EEGLAB, a freely-available MATLAB toolbox for processing EEG. EEGLAB is available from [https://sccn.ucsd.edu/eeglab](https://sccn.ucsd.edu/eeglab). PREP assumes that the EEG data is provided as an EEGLAB EEG structure and that channel locations are provided in the EEG.chanlocs structure. + +### Citing the PREP pipeline +The PREP pipeline is freely available under the GNU General Public License. +Please cite the following publication if using: +> Bigdely-Shamlo N, Mullen T, Kothe C, Su K-M and Robbins KA (2015)\ +> The PREP pipeline: standardized preprocessing for large-scale EEG analysis\ +> Front. Neuroinform. 9:16. doi: 10.3389/fninf.2015.00016 + +### Installation +The PREP pipeline can be run in two ways --- as a standalone toolbox or as an EEGLAB plugin. To run in standalone mode, simply download the EEG-Clean-Tools +repository from https://github.com/VisLab/EEG-Clean-Tools. Unzip if necessary and then add the PrepPipeline directory and all of its subdirectories to your +MATLAB path. + +## PREP as an EEGLAB plugin +You can install PREP as an EEGLAB plugin by unzipping the PREPPipeline directory into the plugins directory of +your EEGLAB installation. + +## Using parallel processing with PREP +The PREP pipeline can execute fairly slowly on headsets with a lot of channels. However, many of the steps are embarassingly parallel --- that is the PREP can perform operations separately on individual channels or individual windows. +If you have the MATLAB Parallel Processing Toolbox, you just need to make sure that it is enabled. The following screenshot +of the MATLAB IDE shows the Parallel Processing Toolbox icon on the lower left of the status bar at the very bottom of the window. +![MATLAB IDE](_static/images/MATLABWorkspace.png) + +To configure your parallel processing toolbox, you should start the worker pool or set MATLAB to automatically start +the pool when needed: +![MATLAB Parallel processing](_static/images/ParallelProcessing.png) + +## Running the PREP pipeline from EEGLAB +Load an EEG dataset to be processed using the Load dataset submenu under the File menu of EEGLAB. The PREP pipeline +can be found under the EEGLAB Tools submenu: +![PREP from EEGLAB](_static/images/PrepFromEEGLAB.png) + +**[MAIN MENU]** The PREP main menu shows the processing steps: +![PREP main menu for EEGLAB plugin](_static/images/PrepMainMenu.png)\ +Each button allows you to override the default parameters. When you press +the Ok button (or unfortunately the Cancel button), PREP runs. Use the x button on the upper right to quit without +running the pipeline. + +**[BOUNDARY MENU]** Normally, PREP won't run on data sets that contain Boundary events. The PREP boundary menu allows you to change how PREP handles boundary events: +![PREP boundary menu](_static/images/PrepBoundary.png)\ +EEGLAB inserts Boundary events to mark discontinuities in the data from epoch rejection or when the data is imported. +Some, but not all, EEGLAB functions respect discontinuities. PREP expects the data set to be continuous. You should not +override this default setting unless the Boundary events in your data set do not mark discontinuous recording, but +other features. + +**[DETREND MENU]** PREP must detrend or high pass filter the data prior detecting line noise or referencing in order to properly calibrate the thresholds: +![PREP detrend menu](_static/images/PrepDetrend.png)\ +By default, PREP uses a high pass filter at 1 Hz for this purpose and does not retain the filtered version of the final +output. This allows you to defer the decision of filtering strategy to downstream processing. Normally, the only default you might need to over ride is what channels to filter. By default, PREP filters all of the channels. + +**[LINE NOISE MENU]** PREP tries to remove sharp spectral peaks representing line noise at this step: +![PREP line noise menu](_static/images/PrepLineNoiseParameters.png)\ +By default, PREP tries to remove multiples of 60 Hz up to the Nyquist frequency (half of the sampling frequency). +You may need to override this if your data set has unusual spectral features. You might also need to specify +which channels should have line noise removed. By default, PREP tries to remove line noise from all channels. + +**[REFERENCE MENU]** The PREP settings for the actual reference step are: +![PREP reference menu](_static/images/PrepReferenceParameters.png)\ +Normally, the only things you will need to override are the channel specifications. Usually, the reference channels and +the evaluation channels correspond to brain EEG channels. The re-referenced channels may include additional channels +such as EOG channels and mastoids. + +**[REPORT MENU]** You can choose to generate a report using the PREP report generation facility: +![PREP report menu](_static/images/PrepReportParameters.png)\ +By default, the PREP does not produce a report. If you generate choose to generate a report, you can choose to +publish it in PDF format. Otherwise, PREP displays the report on the command line. You can also choose to +return at a later step and generate the report only. + +**[POSTPROCESS MENU]** After running the PREP pipeline, you can perform additional processing steps: +![PREP post process menu](_static/images/PrepPostProcess.png) + +**[SAVE MENU]** After PREP completes processing, reporting, and post processing, you are given the option of saving +the processed EEG data set: +![PREP final save menu](_static/images/PrepFinalSave.png) + +## PREP overview +This section discusses the algorithm and the meaning of the various parameters. + +### Processing steps +1. Handle boundary events prior to processing +1. Remove trend (high pass) temporarily to properly compute thresholds +1. Remove line noise without committing to a filtering strategy +1. Robustly reference the signal relative to an estimate of the "true" average reference +1. Detect and interpolate bad channels relative to this reference +1. Produce reports if desired +1. Post process if desired + +### Boundary marker handling +PREP is meant to work on data obtained from a continuous recording session. However, sometimes researchers record multiple sessions in the same file (such as by temporarily suspending and resuming recording). EEGLAB uses boundary events to mark +these discontinuities. Some (but not all) EEGLAB functions respect these boundary markers. By default, the PREP pipeline will not process data sets with boundary markers. Because some researchers use these boundary markers for other purposes than for marking discontinuities, PREP allows the option of temporarily removing boundary markers before processing and then reinserting afterwards. **You should not disregard boundary markers unless you are absolutely sure that these markers** +**to not represent discontinuities.** + +### Detrend (high pass filtering) +High pass filtering of some sort is needed in order for many of the algorithms, including line noise removal and +referencing to perform correctly. However, the exact cutoff may dramatically effect downstream algorithms. By default, +PREP uses a 1 Hz cutoff, but only temporarily filters, so that the final signal is not high-pass filtered. This +allows you to defer the final choice of high pass cutoff for downstream processing to later. + +#### Calling sequence for removing trends +The `removeTrend` function takes two structures in and produces two output structures. The `signal` structure +includes a `.data` field and an `.srate` field. The `signal` structure is compatible with an EEGLAB EEG structure, but does not rely on any of the other EEGLAB fields. The `.data` field should be channels x frames. + +As with all functions in the pipeline, the algorithm parameters are passed in a structure: +> `[signal, detrendOut] = removeTrend(signal)`\ +> `[signal, detrendOut] = removeTrend(signal, detrendIn)` + +The output structure contains all of the input structure fields plus additional fields including a string representation +of the actual command used. Usually, the only field that a user might need to provide is `detrendChannels` if the `signal` structure contains extra channels that represent items other than EEG signals. + +**Example:** +> `detrendIn = struct('detrendChannels', [1:32, 40:60], 'detrendCutoff', 0.5);` + +#### Parameters for removing the trend +The following parameters appear as fields in the `detrendIn` structure: + +**`detrendChannels`**\ + A row vector specifying the channel numbers of the channels to remove the trend from.\ +By default, PREP uses all of the channels (`1:size(signal.data, 1)`). +If your signal has extraneous or unused channels, you should specify which channels to use. + +**`detrendType`**\ +The type of detrending operation to perform. At this time the options are `'high pass'`, `'high pass sinc'`, `'linear'`, and +`'none'`. By default, PREP uses `'high pass'` by calling `pop_eegfiltnew` with the default settings. The +`'high pass sinc'` setting calls `pop_firws` with a Blackman window type. The `'linear'` filter is adapted +from the Chronux toolbox and uses local linear regression. The window size is `1.5/detrendCutoff`. Generally, +the `'linear'` option is much slower than simple high pass filtering and gives very similar results. +Usually you don't have to specify this parameter. + +**`detrendCutoff`**\ +The cutoff frequency in Hz for high pass filtering or local detrending. By default, PREP uses 1 Hz. Usually you don't have to specify this parameter. + +**`detrendStepSize`**\ +The amount in seconds to slide the local detrending window when local linear regression is used for detrending. +By default, PREP uses 0.02 seconds. This parameter is not used unless the `detrendType` is `'linear'`. + +### Line noise removal +We use an iterative version of a method that estimates the amplitude and size of a deterministic sinusoid at a specified frequency embedded in locally white noise. The model is applied in sliding windows to adjust for non stationarity. The algorithm requires a rough guess of the frequencies to be removed. By default, PREP uses multiples of 60 Hz. If the data set was recorded in a place where 50 Hz alternating current is used, you will need to provide the `lineFrequencies` parameter. Sometimes unusual frequencies appear due to aliasing and other recording artifacts. For example, a frequency spike at 212 Hz might appear as an aliasing artifact in a signal recorded at 512 Hz (212 = 512 - 300). You might need to rerun with different frequencies if unusual spectral peaks are visible in the reports. + +#### Calling sequence for line noise removal +The `cleanLineNoise` function takes two structures in and produces two output structures. The `signal` structure +includes a `.data` field and an `.srate` field. The `signal` structure is compatible with an EEGLAB EEG structure, but does not rely on any of the other EEGLAB fields. The data field should be channels x frames. + +As with all functions in the pipeline, the algorithm parameters are passed in a structure. +> `[signal, lineNoiseOut] = cleanLineNoise(signal)`\ +> `[signal, lineNoiseOut] = cleanLineNoise(signal, lineNoiseIn)` + +The output structure contains all of the input structure fields plus additional fields containing information on the +tapers used to compute the spectral components and additional fields including a string representation +of the actual command used. + +**Example:** +> `lineNoiseIn = struct('lineNoiseChannels', [1:32, 40:60], 'lineFrequencies', [60, 120, 180, 212, 240]);` + +#### Parameters for line noise removal +The following parameters appear as fields in the `lineNoiseIn` structure: + +**`lineNoiseChannels`**\ + A row vector specifying the channel numbers of the channels to remove line noise from. By default, PREP uses all of the channels (`1:size(signal.data, 1)`). If your signal has extraneous or unused channels, you should specify which channels to use. + +**`Fs`**\ +The sampling frequency of the signal in Hz. By default, PREP uses the sampling rate specified in `signal.srate`. Usually you don't have to specify this parameter. + +**`lineFrequencies`**\ +A vector of frequencies in Hz of the approximate locations of the line noise peaks to remove. By default, PREP removes multiples of 60 Hz up to the Nyquist frequency (which is half of the sampling frequency). After looking at the spectrum in the report, you may need to redo PREP with additional frequencies. If the data was recorded in a location using a 50 Hz power, you will also need to override. + +The clean line noise procedure used in PREP can only remove sharp peaks with minimal spectral distortion. It does not remove broad peaks. If the PREP reports show that line noise has not been removed to a sufficient extent, you may have to perform additional filtering for a particular application. + +**`p`**\ +A significance cutoff level for removing a spectral peak. By default, PREP uses a p-value of 0.01. The clean line noise +procedure applies an F-test to determine whether a particular spectral peak is significantly higher than the background level in a small window. You should not have to override this parameter. + +**`fScanBandWidth`**\ +Half of the width of the frequency band centered on each line frequency. This band is used to search +for the exact value of the maximum amplitude frequency peak near the specified frequencies to be removed. +By default, PREP uses 2 Hz. You should not have to override this parameter. + +**`taperBandWidth`**\ +Bandwidth in Hz for the Sleppian tapers used to estimate the spectrum. By default, PREP uses 2 Hz. You should not have to override this parameter. + +**`taperWindowSize`**\ +Taper sliding window length in seconds. By default, PREP uses 4 seconds. You should not have to override this parameter. + +**`taperWindowStep`**\ +Taper sliding window step length in seconds. By default, PREP uses 1 second. You should not have to override this parameter. + +**`tau`**\ +The window overlap smoothing factor used in the exponent of the signmoidal smoothing functions. This sigmoidal smoothing function is used to patch results from sliding windows back together. By default, PREP uses a value of 100. You should not have to override this parameter. + +**`pad`**\ +Padding factor for FFTs (-1= no padding, 0 = pad to next power of 2, 1 = pad to power of two after, etc.). By default, PREP uses a pad factor of 0. A larger positive value gives better spectral results, but requires much greater computation time. Using a pad value of -1 is not recommended. You should not have to override this parameter. + +**`fPassBand`**\ +The frequency band (in units of Hz) used to compute the spectral background. By default, PREP +uses `[0, Fs/2]`. You may need to adjust this range to get better spectral estimates. + +**`maximumIterations`**\ +The maximum number of times that PREP applies the cleaning process to remove line noise. When a particular peak +is not significantly above the background, it is removed from consideration. When no significant peaks remain, PREP +stops the procedure. Most of the time, only a few iterations are required. You should not have to override this parameter. + +### Robust referencing +Referencing is the process of subtracting a common reference signal from all of the channels. Data sets collected +from Biosemi headsets require referencing of some sort. Other headsets benefit as well. When comparing results across +data sets, it is important to use the same referencing strategy. + +The PREP pipeline using robust average reference. This process is the same as average referencing (subtracting +the average of the channels from each channel in each frame) provided the data set does not have any bad +channels. However, if even if just a single channel has artifacts, the average reference can introduce +errors. To address this, the robust reference iteratively detects and interpolates bad channels to arrive at an +average reference that is not affected by artifacts. + +#### Calling sequence for referencing +The `performReference` function takes two structures in and produces two output structures. The signal structure +includes a `.data` field and an `.srate` field. The `signal` structure is compatible with an EEGLAB EEG structure, but does not rely on any of the other EEGLAB fields. The data field should be channels x frames. On output, PREP stores metadata about the referencing in the `.etc.noiseDetection` field. + +As with all functions in the pipeline, the algorithm parameters are passed in a structure: +> `[signal, referenceOut] = performReference(signal)`\ +> `[signal, referenceOut] = performReference(signal, referenceIn)` + +The output structure contains all of the input structure fields plus many additional fields containing the +reports of the output of the bad channel detection. Details in the document on PREP reporting. + +**Example:** +> `referenceIn = struct('referenceeChannels', [1:32, 40:60]);` + +#### Parameters for referencing +The following parameters appear as fields in the `referenceIn` structure: + +**`referenceChannels`**\ + A row vector specifying the channel numbers of the channels to use for referencing. By default, PREP uses all of the channels (`1:size(signal.data, 1)`). If your signal has extraneous or unused channels, you should specify which channels to use. For standard robust referencing, you should specify only the EEG channels and not EOG channels or mastoids. All of the reference channels will be used to compute the reference. If the channel is bad, PREP uses its interpolated value. + +**`evaluationChannels`**\ + A row vector specifying the channel numbers of the channels to use for evaluating noisy channels. By default, PREP uses all of the channels (`1:size(signal.data, 1)`). If your signal has extraneous or unused channels, you should specify which channels to use. These channels should only be EEG channels. These channels are used to compute thresholds and to perform +estimates in the RANSAC algorithm. Often the reference channels and the evaluation channels are the same. However, if an EEG channel has NaNs or other unusable data, it will still be used as a reference channel, but will be excluded from the evaluation channels. + +**`rereference`**\ + A row vector specifying the channel numbers of the channels from which to subtract the computed reference. By default, PREP uses all of the channels (`1:size(signal.data, 1)`). If your signal has extraneous or unused channels, you should specify which channels to use. Channels such as mastoids and EOG channels are usually re-referenced but are not used to +compute the robust reference. + +**`referenceType`**\ +The type of reference to be performed. By default, PREP uses ``robust'`, which computes an average reference with +iterative detection and interpolation of bad channels. Other options include `'average'`, `'specific'`, and `'none'`. +The `'average'` type removes the average of the reference channels with no interpolation, while `'specific'` removes +the average of the specified channels with no interpolation. If you mean to run the standardized PREP pipeline, +you don't need to specify this field. + +**`interpolationOrder`**\ +Specifies whether PREP performs final channel interpolation. By default, PREP uses `'post-reference'`. +In this case, after a final robust reference is computed, the channels are re-interpolated and the reference is corrected. +In `'pre-reference'`, PREP incrementally adds to the bad channel list and interpolates before computing the reference. +If the initial estimate of the reference is poor, this is not a good approach. In the `'none'` option, PREP removes the +reference but does not interpolate. Bad channels remain in the signal. You may choose to remove them during post-processing. If you mean to run the standardized PREP pipeline, you don't need to specify this field. + +**`meanEstimateType`**\ +The method used to estimate the initial mean reference. By default, PREP takes the median of the channel values +in each frame. Other options include `'mean'`, which is prone to outliers, `'huber'` which is computationally expensive, +or `'none'`. If you mean to run the standardized PREP pipeline, you don't need to specify this field. + +**`channelLocations`**\ +A structure containing the channel locations in EEGLAB `chanlocs` format. By default, PREP uses the `signal.chanlocs` +structure unless this field is used to over ride. PREP must have channel locations in order to work. + +**`channelInfo`**\ +A structure containing channel information in EEGLAB `chaninfo` format. By default, PREP uses the `signal.chaninfo` +structure unless this field is used to over ride. PREP uses the nose direction for display purposes in the reports. + +**`srate`**\ +The sampling frequency of the signal in Hz. By default, PREP uses the sampling rate specified in `signal.srate`. Usually you don't have to specify this parameter. + +**`samples`**\ +The number of frames to use for the computation. By default, PREP uses `size(signal.data, 2)`. Usually you don't have to specify this parameter. + +**`robustDeviationThreshold`**\ +Z-score cutoff for robust channel deviation. If a channel has a robust deviation z-score above this value, +PREP considers the channel to be bad in that window. By default, PREP uses 5. Usually you don't have to specify this parameter. + +**`highFrequencyNoiseThreshold`**\ +Z-score cutoff for SNR (signal above 50 Hz). If a channel has a z-score of the ratio of signal above 50 Hz to that below 50 Hz, PREP considers the channel to be bad in that window. By default, PREP uses 5. Usually you don't have to specify this parameter. + +**`correlationWindowSeconds`**\ +Window size in seconds for computing correlations and other window values. By default, PREP uses 1. Usually you don't have to specify this parameter. + +**`correlationThreshold`**\ +Max correlation absolute threshold for channel being bad in a window. In each window, PREP computes the maximum of the absolute value of the correlation with other channels and compares to this threshold. If the correlation falls below this threshold, PREP considers the channel to be bad-by-correlation in this window. PREP also uses this window size to evaluate windowed absolute deviation and SNR. By default, PREP uses 0.4. Usually you don't have to specify this parameter. + +**`badTimeThreshold`**\ +Threshold fraction of bad correlation windows for designating a channel to be bad-by-correlation. By default, PREP uses 0.01. Usually you don't have to specify this parameter. + +**`ransacOff`**\ +If true, RANSAC is not used for bad channel detection (useful for small headsets). By default, PREP uses false. Usually you don't have to specify this parameter. + +**`ransacSampleSize`**\ +Number of random matrices sampled to estimate RANSAC. By default, PREP uses 50 sample matrices. Usually you don't have to specify this parameter. + +**`ransacChannelFraction`**\ +Fraction of evaluation channels RANSAC uses to predict a channel. By default, PREP uses 0.25 of the evaluation channels. Usually you don't have to specify this parameter. + +**`ransacCorrelationThreshold`**\ +Cutoff correlation for unpredictability by neighbors. If the absolute correlation of the channel with its RANSAC prediction in a window falls below this threshold, the channel is designated as bad in this window. By default, PREP uses 0.75. Usually you don't have to specify this parameter. + +**`ransacUnbrokenTime`**\ +Threshold fraction of windows that a channel must be bad before it is designated as a channel that is bad-by-RANSAC. By default, PREP uses 0.4. Usually you don't have to specify this parameter. + +**`ransacWindowSeconds`**\ +Size of windows in seconds over which to compute RANSAC predictions. By default, PREP uses 5. Usually you don't have to specify this parameter. + +**`maxReferenceIterations`**\ +Maximum number of iterations in the reference-bad channel detection-interpolation cycle. +By default, PREP uses 4. If the actual iterations is 4, you may need to increase this or look carefully at your data set. + +**`reportingLevel`**\ +How much information to store about referencing in the EEG structure. By default, PREP uses `'verbose'`, which causes +the data structure to contain all of the window information for later processing and reporting. If you use `'minimum'`, you will not be able to run the PREP reports. Alternatively, you can choose to clean up the data structure +after running the reports. + +### Reporting +PREP has an extensive report facility that can be used provided that your reporting level was `'verbose'`. The GUI version of the PREP pipeline (`pop_prepPipeline'`) has options in the report GUI for you to select whether or not to run the report. If the report mode is `'normal'` (the default), then PREP runs the processing pipeline followed by the report, followed by the post processing. If the report mode is `'skip'`, then PREP runs the processing pipeline followed by the post processing. If the report mode is `'reportOnly'`, then PREP only runs the report and skips both the processing and the post processing. + +#### Calling sequence for reporting +The `publishPrepReport` function takes an EEG structure that has been run through the PREP pipeline with +report level of verbose. You need to furnish a summary directory name and a summary file name for an HTML file with summary information for all of the stuff. + +As with all functions in the pipeline, the algorithm parameters are passed in a structure. +> `publishPrepReport(signal, summaryFilePath, sessionFilePath, consoleFID, publishOn);` + +The output structure contains all of the input structure fields plus additional fields including a string representation +of the actual command used. + +**Example:**\ +The following produces an HTML-formatted summary report in the current directory and publishes a detailed report in the s1 sub directory. +> `publishPrepReport(EEG, 'vepSummary.html', '.\s1\vep01.pdf', 1, true);` + +#### Parameters for reporting + +**`signal`**\ +The `signal` structure includes a `.data` field and an `.srate` field. The `signal` structure is compatible with an EEGLAB EEG structure, but does not rely on any of the other EEGLAB fields. The data field should be channels x frames. In order to get reports, the EEG structure must have the `.etc.noiseDetection` as PREP generates the report from information stored there. + +**`summaryFilePath`**\ +The file name for the HTML summary file that PREP produces for the report. The name should include path information when needed. If `publishOn` is `false`, PREP writes the summary information to the file indicated by `consoleFID`. + +**`sessionFilePath`**\ +The file name for the detailed PDF report that PREP produces. The name should include path information when needed. If `publishOn` is `false`, PREP doesn't produce a report. + +**`consoleFID`**\ +An open file descriptor for writing reporting information. Usually, this is 1, indicating that output should be directed to the command window. Give an open file descriptor to another file to record the report in a log. + +**`PublishOn`**\ +If `true` (the default) PREP produces a published PDF Report and an HTML summary. If `false`, the PREP runs reporting, but keeps the figures displayed and outputs the reporting information to the command window. This mode is useful for closer examination of the figures. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d49964e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +# This repository is MATLAB code. pyproject.toml exists only to declare the +# Python toolchain that builds the documentation in docs/. It is not a Python +# package and is never published; the PREP version lives in +# PrepPipeline/utilities/getPrepVersion.m, and docs/conf.py reads it from there. + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "eeg-clean-tools-docs" +version = "0.0.0" +description = "Documentation toolchain for the PREP pipeline (MATLAB)" +readme = "README.md" +requires-python = ">=3.10" +license = {text = "GPL-2.0-or-later"} + +[project.urls] +Homepage = "https://github.com/VisLab/EEG-Clean-Tools" +Documentation = "https://vislab.github.io/EEG-Clean-Tools/" +"Bug Tracker" = "https://github.com/VisLab/EEG-Clean-Tools/issues" + +[project.optional-dependencies] +docs = [ + "sphinx>=7.1.0,<10.0", + "furo>=2024.1.29", + "sphinx-copybutton>=0.5.2", + "myst-parser>=3.0.0", + "sphinxcontrib-matlabdomain==0.22.1", + "linkify-it-py>=2.0.3", +] + +[tool.setuptools] +packages = [] From 179a3dc838249ba6467894b8837476476260acc1 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sat, 26 Sep 2026 09:08:43 -0500 Subject: [PATCH 05/17] Document building and publishing the docs in README.md Adds how to build and view the Sphinx site locally, and how and when the deploy workflow publishes it to GitHub Pages. Co-Authored-By: Claude Opus 5.5 --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index cd1d013..59942a3 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,41 @@ find the user documentation at **Note:** For convenience, EEGLABPlugin directory contains the latest released version of the PREP that can be unzipped into your EEGLAB plugins directory. +### Building the documentation +The documentation source is in `docs/` (Sphinx, with MyST markdown). To build and +view it locally, set up the toolchain once from the repository root: + +```shell +uv venv --clear .venv +# activate: .venv\Scripts\activate (Windows) or source .venv/bin/activate (Linux/macOS) +uv pip install -e ".[docs]" +python docs/patch_matlabdomain.py +``` + +`docs/patch_matlabdomain.py` must be rerun after every reinstall of +`sphinxcontrib-matlabdomain`. Then, with the environment activated, build and serve: + +```shell +python -m sphinx -b html docs docs/_build/html +python -m http.server -d docs/_build/html 8000 +``` + +Open http://localhost:8000 in a browser. Opening `docs/_build/html/index.html` +directly also works, but search does not. + +### Publishing the documentation +The site at https://vislab.github.io/EEG-Clean-Tools/ is served by GitHub Pages. +Pushing any branch other than `master` publishes nothing. The workflow +`.github/workflows/deploy-docs.yaml` builds the docs on pull requests to `master` +without deploying, and builds and deploys them on pushes to `master`. + +That deploy only reaches the site when the repository's Pages source (Settings -> +Pages -> Source) is "GitHub Actions". While the source is "Deploy from a branch: +gh-pages", the live site is the one on the `gh-pages` branch, and the workflow +cannot replace it. Do not switch the source, or delete `gh-pages`, until the +contents of `docs/` are ready to go live: the first Actions deployment overwrites +the site at the same address. + ### Citing the PREP pipeline The PREP pipeline is freely available under the GNU General Public License. Please cite the following publication if using: From 5f6e74556a0b96a07ff76e7a259d7f639c4e7f5d Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sat, 26 Sep 2026 09:52:24 -0500 Subject: [PATCH 06/17] Use pip, not uv, in the local docs setup instructions uv misbehaves on Windows. README.md and AGENTS.md now set up the docs toolchain with python -m venv and python -m pip; the GitHub Actions workflow keeps using uv. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7180aae..9f30469 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla - Run standalone: add `PrepPipeline` and its subfolders to the MATLAB path, then `[EEG, params, computationTimes] = prepPipeline(EEG, params)` on an EEGLAB `EEG` structure with channel locations. Needs EEGLAB and the Signal Processing Toolbox on the path. - Run as a plugin: unzip `EEGLABPlugin/PrepPipeline.zip` into EEGLAB's `plugins/` folder; the menu entry is Tools -> Run PREP pipeline. - Check the plugin zip: `unzip -l EEGLABPlugin/PrepPipeline.zip` -- Install the docs toolchain: `uv venv --clear .venv`, activate it, then `uv pip install -e ".[docs]"` and `python docs/patch_matlabdomain.py` (required after every install of `sphinxcontrib-matlabdomain`; it fixes a Sphinx 7+ incompatibility in that package). Run the Python tools from the activated `.venv`, not through `uvx`. +- Install the docs toolchain: `python -m venv --clear .venv`, activate it, then `python -m pip install -e ".[docs]"` and `python docs/patch_matlabdomain.py` (required after every install of `sphinxcontrib-matlabdomain`; it fixes a Sphinx 7+ incompatibility in that package). Locally, use pip and the activated `.venv`, never `uv` or `uvx`: uv misbehaves on Windows. The GitHub Actions workflows use uv, and that stays. - Build docs: `python -m sphinx -b html docs docs/_build/html` - `.github/workflows/deploy-docs.yaml` runs the same build and publishes it to GitHub Pages on pushes to `master` ## Layout diff --git a/README.md b/README.md index 59942a3..b8a8a27 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ The documentation source is in `docs/` (Sphinx, with MyST markdown). To build an view it locally, set up the toolchain once from the repository root: ```shell -uv venv --clear .venv +python -m venv --clear .venv # activate: .venv\Scripts\activate (Windows) or source .venv/bin/activate (Linux/macOS) -uv pip install -e ".[docs]" +python -m pip install -e ".[docs]" python docs/patch_matlabdomain.py ``` From b9db4f3f93211f1f30ec36c1b0f4765acead821d Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sat, 26 Sep 2026 10:02:11 -0500 Subject: [PATCH 07/17] Show Windows and Linux/macOS docs setup separately in README.md Co-Authored-By: Claude Opus 5.5 --- README.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b8a8a27..1d0ef10 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,47 @@ PREP that can be unzipped into your EEGLAB plugins directory. ### Building the documentation The documentation source is in `docs/` (Sphinx, with MyST markdown). To build and -view it locally, set up the toolchain once from the repository root: +view it locally, set up a Python virtual environment once, from the repository root. +Python 3.10 or later is required. -```shell +**Windows (PowerShell):** + +```powershell python -m venv --clear .venv -# activate: .venv\Scripts\activate (Windows) or source .venv/bin/activate (Linux/macOS) +.venv\Scripts\Activate.ps1 +python -m pip install -e ".[docs]" +python docs/patch_matlabdomain.py +``` + +If PowerShell refuses to run `Activate.ps1`, allow local scripts for your account +once with `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`. In `cmd.exe`, activate +with `.venv\Scripts\activate.bat` instead. + +**Linux and macOS:** + +```bash +python3 -m venv --clear .venv +source .venv/bin/activate python -m pip install -e ".[docs]" python docs/patch_matlabdomain.py ``` +On Debian and Ubuntu, `python3 -m venv` needs the `python3-venv` package +(`sudo apt install python3-venv`). + `docs/patch_matlabdomain.py` must be rerun after every reinstall of -`sphinxcontrib-matlabdomain`. Then, with the environment activated, build and serve: +`sphinxcontrib-matlabdomain`. In later sessions, only the activation line is needed. + +With the environment activated, build and serve the site. These commands are the +same on every platform: ```shell python -m sphinx -b html docs docs/_build/html python -m http.server -d docs/_build/html 8000 ``` -Open http://localhost:8000 in a browser. Opening `docs/_build/html/index.html` -directly also works, but search does not. +Open http://localhost:8000 in a browser, and stop the server with Ctrl+C. Opening +`docs/_build/html/index.html` directly also works, but search does not. ### Publishing the documentation The site at https://vislab.github.io/EEG-Clean-Tools/ is served by GitHub Pages. From 7bb8d88846a7603215e8dc139714150038ee3dd8 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sat, 26 Sep 2026 10:29:03 -0500 Subject: [PATCH 08/17] Bind the local docs server to 127.0.0.1 in README.md The default binding listens on all addresses and prints http://[::]:8000/, which is not an address a browser can open. Binding to 127.0.0.1 prints a usable URL and keeps the server local. Co-Authored-By: Claude Opus 5.5 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1d0ef10..95153c8 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,12 @@ same on every platform: ```shell python -m sphinx -b html docs docs/_build/html -python -m http.server -d docs/_build/html 8000 +python -m http.server 8000 --bind 127.0.0.1 -d docs/_build/html ``` -Open http://localhost:8000 in a browser, and stop the server with Ctrl+C. Opening +The server prints `Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/)`. +Open that address in a browser, and stop the server with Ctrl+C. The +`--bind 127.0.0.1` keeps the server reachable only from your own machine. Opening `docs/_build/html/index.html` directly also works, but search does not. ### Publishing the documentation From bea47ca67a5016836aaf2941fa7ee6a0e6227e02 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sat, 26 Sep 2026 12:17:43 -0500 Subject: [PATCH 09/17] Render MATLAB help text in the API docs as preformatted text PREP's help text is written for MATLAB's help command, not as reStructuredText, so Sphinx mis-rendered it and emitted 15 docutils warnings. docs/conf.py now wraps each docstring in a literal block, laid out as help prints it. No .m file changes. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- docs/conf.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 9f30469..2c8b81e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,7 +39,7 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla - The version exists in three places that must agree: `PrepPipeline/utilities/getPrepVersion.m` (the change log that `getPrepVersion` returns), the zip name under `EEGLABPlugin/`, and `CHANGELOG.md`. Change all three together. - Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/` or `PrepPipeline/utilities/blasst/`. - `PrepPipeline/reporting/showPipelineDefaults.m` declares `function showPrepDefaults`; MATLAB calls a file by its file name, so the callable name is `showPipelineDefaults`, not the `showPrepDefaults` that the `prepPipeline` help text names. `outputPrepDefaults(EEG)` prints the same defaults. -- `docs/api.rst` pulls each function's help text from its `.m` file, and Sphinx parses that text as reStructuredText. Help text that indents a continuation line without a blank line before it produces docutils warnings in the build. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives. +- `docs/api.rst` pulls each function's help text from the comment block right after its `function` line. `docs/conf.py` shows that text preformatted, exactly as MATLAB `help` prints it, so write help for `help`, not as reStructuredText. Help placed above the `function` line (as in `pop_prepPipeline.m`) does not appear. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives. - Do not change the signature of an entry-point function without discussion; EEGLAB and user scripts call them directly, and `pop_prepPipeline` writes the call into EEGLAB history. ## Related repositories diff --git a/docs/conf.py b/docs/conf.py index fd15da2..6eae7bd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -93,3 +93,22 @@ def _prep_version(): html_static_path = ["_static"] html_css_files = ["custom.css"] html_js_files = ["gh_icon_fix.js"] + + +# -- MATLAB help text --------------------------------------------------------- + +# PREP's help text is written for MATLAB's `help` command: aligned columns and +# indented continuation lines, not reStructuredText. Parsed as reST it renders +# badly and produces docutils warnings, so show each docstring as a literal +# block - the same text, laid out exactly as `help` prints it. + + +def _help_text_as_literal(app, what, name, obj, options, lines): + if not any(line.strip() for line in lines): + return + body = [(" " + line) if line.strip() else "" for line in lines] + lines[:] = ["::", "", *body, ""] + + +def setup(app): + app.connect("autodoc-process-docstring", _help_text_as_literal) From 3d7b37b55226dde181bd7b19745cb182872c58aa Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sat, 26 Sep 2026 12:51:52 -0500 Subject: [PATCH 10/17] Fix file/function name mismatches - Rename reporting/showPipelineDefaults.m to showPrepDefaults.m so the file matches its function name and the prepPipeline help text that tells users to call showPrepDefaults(EEG) - Delete derived/highPassAndICALinux.m, an unused copy of highPassAndICA.m hardwired to cudaica; highPassAndICA takes 'icatype', 'cudaica' instead The docs build now completes with no warnings. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 1 - PrepPipeline/derived/highPassAndICALinux.m | 39 ------------------- ...wPipelineDefaults.m => showPrepDefaults.m} | 0 3 files changed, 40 deletions(-) delete mode 100644 PrepPipeline/derived/highPassAndICALinux.m rename PrepPipeline/reporting/{showPipelineDefaults.m => showPrepDefaults.m} (100%) diff --git a/AGENTS.md b/AGENTS.md index 2c8b81e..59d1dc3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,7 +38,6 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla - The version exists in three places that must agree: `PrepPipeline/utilities/getPrepVersion.m` (the change log that `getPrepVersion` returns), the zip name under `EEGLABPlugin/`, and `CHANGELOG.md`. Change all three together. - Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/` or `PrepPipeline/utilities/blasst/`. -- `PrepPipeline/reporting/showPipelineDefaults.m` declares `function showPrepDefaults`; MATLAB calls a file by its file name, so the callable name is `showPipelineDefaults`, not the `showPrepDefaults` that the `prepPipeline` help text names. `outputPrepDefaults(EEG)` prints the same defaults. - `docs/api.rst` pulls each function's help text from the comment block right after its `function` line. `docs/conf.py` shows that text preformatted, exactly as MATLAB `help` prints it, so write help for `help`, not as reStructuredText. Help placed above the `function` line (as in `pop_prepPipeline.m`) does not appear. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives. - Do not change the signature of an entry-point function without discussion; EEGLAB and user scripts call them directly, and `pop_prepPipeline` writes the call into EEGLAB history. diff --git a/PrepPipeline/derived/highPassAndICALinux.m b/PrepPipeline/derived/highPassAndICALinux.m deleted file mode 100644 index fad8057..0000000 --- a/PrepPipeline/derived/highPassAndICALinux.m +++ /dev/null @@ -1,39 +0,0 @@ -function EEG = highPassAndICA(EEG, varargin) -% Perform a high-pass filter and ICA on data that has been Prepped -try - params = vargin2struct(varargin); - if isfield(EEG.etc, 'noiseDetection') && ... - isfield(EEG.etc.noiseDetection, 'detrend') && ... - ~isfield(params, 'detrendChannels') - params.detrendChannels = EEG.etc.noiseDetection.detrend.detrendChannels; - end - [EEG, detrend] = removeTrend(EEG, params); - EEG.etc.highPassAndICA.detrend = detrend; - - %% Perform ICA on the data - if isfield(EEG.etc, 'noiseDetection') && ... - isfield(EEG.etc.noiseDetection, 'reference') - referenceChannels = ... - EEG.etc.noiseDetection.reference.referenceChannels; - interpolatedChannels = ... - EEG.etc.noiseDetection.reference.interpolatedChannels.all; - channelsLeft = setdiff(referenceChannels, interpolatedChannels); - pcaDim = length(channelsLeft) - 1; - fprintf('%s: pcaDim = %d\n', EEG.setname, pcaDim); - EEG = pop_runica(EEG, 'icatype', 'cudaica', 'extended', 1, ... - 'chanind', referenceChannels, 'pca', pcaDim); - EEG.etc.highPassAndICA.ICA = ... - ['extended infomax with pca Dim ' num2str(pcaDim)]; - EEG.etc.noiseDetection.reference = ... - cleanupReference(EEG.etc.noiseDetection.reference); -% else -% EEG = pop_runica(EEG, 'icatype', 'runica', 'extended', 1); -% EEG.etc.highPassAndICA.ICA = 'runica extended infomax'; - end - -catch mex - errorMessages.highPassAndICA = ['failed highPassAndICA: ' getReport(mex)]; - errorMessages.status = 'unprocessed'; - EEG.etc.highPassAndICA.errors = errorMessages; - fprintf(2, '%s\n', errorMessages.highPassAndICA); -end \ No newline at end of file diff --git a/PrepPipeline/reporting/showPipelineDefaults.m b/PrepPipeline/reporting/showPrepDefaults.m similarity index 100% rename from PrepPipeline/reporting/showPipelineDefaults.m rename to PrepPipeline/reporting/showPrepDefaults.m From 229db5b819fa29dd344885892006b675e098975d Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sat, 26 Sep 2026 13:09:37 -0500 Subject: [PATCH 11/17] Address Copilot review of PR 38 - deploy-docs.yaml: read-only by default; only the deploy job gets pages: write and id-token: write - CHANGELOG.md: 0.57.0 release date 3/31/2025, matching getPrepVersion.m - pop_prepPipeline.m: help block moved after the function line so the API docs pick it up (help output unchanged); conf.py strips the link markup matlabdomain adds to "See also" lines - user_guide.md: plugin install names the zip in EEGLABPlugin/, fix referenceChannels and rereferencedChannels names, correct where the report summary is written Co-Authored-By: Claude Opus 5.5 --- .github/workflows/deploy-docs.yaml | 10 +++++++--- AGENTS.md | 2 +- CHANGELOG.md | 2 +- PrepPipeline/pop_prepPipeline.m | 2 +- docs/conf.py | 9 ++++++++- docs/user_guide.md | 13 +++++++------ 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index 7427a92..656399b 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -6,10 +6,11 @@ on: pull_request: branches: [ master ] +# Read-only by default. Only the deploy job, which runs only on pushes to +# master, gets the Pages and OIDC write permissions; the build job runs code +# from the branch (docs/conf.py, the patch script) and stays read-only. permissions: contents: read - pages: write - id-token: write concurrency: group: "pages" @@ -18,6 +19,9 @@ concurrency: jobs: build: runs-on: ubuntu-latest + permissions: + contents: read + pages: read # actions/configure-pages reads the Pages site settings steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -64,7 +68,7 @@ jobs: deploy: # This job depends on the 'build' job completing successfully needs: build - # Only deploy when pushing to main branch, not on pull requests + # Only deploy when pushing to master, not on pull requests if: github.event_name == 'push' && github.ref == 'refs/heads/master' permissions: pages: write diff --git a/AGENTS.md b/AGENTS.md index 59d1dc3..10926e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,7 +38,7 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla - The version exists in three places that must agree: `PrepPipeline/utilities/getPrepVersion.m` (the change log that `getPrepVersion` returns), the zip name under `EEGLABPlugin/`, and `CHANGELOG.md`. Change all three together. - Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/` or `PrepPipeline/utilities/blasst/`. -- `docs/api.rst` pulls each function's help text from the comment block right after its `function` line. `docs/conf.py` shows that text preformatted, exactly as MATLAB `help` prints it, so write help for `help`, not as reStructuredText. Help placed above the `function` line (as in `pop_prepPipeline.m`) does not appear. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives. +- `docs/api.rst` pulls each function's help text from the comment block right after its `function` line. `docs/conf.py` shows that text preformatted, exactly as MATLAB `help` prints it, so write help for `help`, not as reStructuredText. Help placed above the `function` line does not appear there, though MATLAB `help` still finds it. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives. - Do not change the signature of an entry-point function without discussion; EEGLAB and user scripts call them directly, and `pop_prepPipeline` writes the call into EEGLAB history. ## Related repositories diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e6801f..8c9069b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Release history of the PREP pipeline, newest first. The current version is also reported by `getPrepVersion` (`PrepPipeline/utilities/getPrepVersion.m`). -## Version 0.57.0 - Released 3/30/2025 +## Version 0.57.0 - Released 3/31/2025 * Modified to work with modified EEGLAB GUI Builder * Modified reporting to not clutter workspace diff --git a/PrepPipeline/pop_prepPipeline.m b/PrepPipeline/pop_prepPipeline.m index a08058c..31193c4 100644 --- a/PrepPipeline/pop_prepPipeline.m +++ b/PrepPipeline/pop_prepPipeline.m @@ -1,3 +1,4 @@ +function [EEG, com] = pop_prepPipeline(EEG, params) % pop_prepPipeline() - runs the early stage pipeline to reference and to % detect bad channels % @@ -31,7 +32,6 @@ % along with this program; if not, write to the Free Software % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -function [EEG, com] = pop_prepPipeline(EEG, params) com = ''; % Return something if user presses the cancel button if nargin < 1 %% display help if not enough arguments help pop_prepPipeline; diff --git a/docs/conf.py b/docs/conf.py index 6eae7bd..2f85586 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -103,10 +103,17 @@ def _prep_version(): # block - the same text, laid out exactly as `help` prints it. +_ROLE = re.compile(r":[a-z]+:`([^`]+)`") +_LITERAL = re.compile(r"``([^`]+)``") + + def _help_text_as_literal(app, what, name, obj, options, lines): if not any(line.strip() for line in lines): return - body = [(" " + line) if line.strip() else "" for line in lines] + # matlabdomain rewrites "See also" names into :func:`x` / ``x`` markup, + # which a literal block would show verbatim; restore the plain names. + plain = [_LITERAL.sub(r"\1", _ROLE.sub(r"\1", line)) for line in lines] + body = [(" " + line) if line.strip() else "" for line in plain] lines[:] = ["::", "", *body, ""] diff --git a/docs/user_guide.md b/docs/user_guide.md index 1c90c65..0f68a42 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -23,8 +23,9 @@ repository from https://github.com/VisLab/EEG-Clean-Tools. Unzip if necessary an MATLAB path. ## PREP as an EEGLAB plugin -You can install PREP as an EEGLAB plugin by unzipping the PREPPipeline directory into the plugins directory of -your EEGLAB installation. +You can install PREP as an EEGLAB plugin by unzipping `EEGLABPlugin/PrepPipeline.zip` from this +repository into the `plugins` directory of your EEGLAB installation. The zip holds a single +`PrepPipeline` folder; restart EEGLAB and PREP appears under the Tools menu. ## Using parallel processing with PREP The PREP pipeline can execute fairly slowly on headsets with a lot of channels. However, many of the steps are embarassingly parallel --- that is the PREP can perform operations separately on individual channels or individual windows. @@ -232,7 +233,7 @@ The output structure contains all of the input structure fields plus many additi reports of the output of the bad channel detection. Details in the document on PREP reporting. **Example:** -> `referenceIn = struct('referenceeChannels', [1:32, 40:60]);` +> `referenceIn = struct('referenceChannels', [1:32, 40:60]);` #### Parameters for referencing The following parameters appear as fields in the `referenceIn` structure: @@ -244,7 +245,7 @@ The following parameters appear as fields in the `referenceIn` structure: A row vector specifying the channel numbers of the channels to use for evaluating noisy channels. By default, PREP uses all of the channels (`1:size(signal.data, 1)`). If your signal has extraneous or unused channels, you should specify which channels to use. These channels should only be EEG channels. These channels are used to compute thresholds and to perform estimates in the RANSAC algorithm. Often the reference channels and the evaluation channels are the same. However, if an EEG channel has NaNs or other unusable data, it will still be used as a reference channel, but will be excluded from the evaluation channels. -**`rereference`**\ +**`rereferencedChannels`**\ A row vector specifying the channel numbers of the channels from which to subtract the computed reference. By default, PREP uses all of the channels (`1:size(signal.data, 1)`). If your signal has extraneous or unused channels, you should specify which channels to use. Channels such as mastoids and EOG channels are usually re-referenced but are not used to compute the robust reference. @@ -325,7 +326,7 @@ the data structure to contain all of the window information for later processing after running the reports. ### Reporting -PREP has an extensive report facility that can be used provided that your reporting level was `'verbose'`. The GUI version of the PREP pipeline (`pop_prepPipeline'`) has options in the report GUI for you to select whether or not to run the report. If the report mode is `'normal'` (the default), then PREP runs the processing pipeline followed by the report, followed by the post processing. If the report mode is `'skip'`, then PREP runs the processing pipeline followed by the post processing. If the report mode is `'reportOnly'`, then PREP only runs the report and skips both the processing and the post processing. +PREP has an extensive report facility that can be used provided that your reporting level was `'verbose'`. The GUI version of the PREP pipeline (`pop_prepPipeline`) has options in the report GUI for you to select whether or not to run the report. If the report mode is `'normal'` (the default), then PREP runs the processing pipeline followed by the report, followed by the post processing. If the report mode is `'skip'`, then PREP runs the processing pipeline followed by the post processing. If the report mode is `'reportOnly'`, then PREP only runs the report and skips both the processing and the post processing. #### Calling sequence for reporting The `publishPrepReport` function takes an EEG structure that has been run through the PREP pipeline with @@ -347,7 +348,7 @@ The following produces an HTML-formatted summary report in the current directory The `signal` structure includes a `.data` field and an `.srate` field. The `signal` structure is compatible with an EEGLAB EEG structure, but does not rely on any of the other EEGLAB fields. The data field should be channels x frames. In order to get reports, the EEG structure must have the `.etc.noiseDetection` as PREP generates the report from information stored there. **`summaryFilePath`**\ -The file name for the HTML summary file that PREP produces for the report. The name should include path information when needed. If `publishOn` is `false`, PREP writes the summary information to the file indicated by `consoleFID`. +The file name for the HTML summary file that PREP produces for the report. The name should include path information when needed. PREP appends the summary for this dataset to this file whether or not `publishOn` is `true`, so calling it for each dataset in a collection builds one collection summary. `consoleFID` receives the console output, not the summary. **`sessionFilePath`**\ The file name for the detailed PDF report that PREP produces. The name should include path information when needed. If `publishOn` is `false`, PREP doesn't produce a report. From 7bc5473b2944996af6bf3e923034880aeae6502f Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sun, 27 Sep 2026 06:10:16 -0500 Subject: [PATCH 12/17] Add root GPL license and licensing table; remove BLASST - LICENSE: complete GPL v2 text; PREP is GPL-2.0-or-later - README.md: licensing table for every component with its own license, including the CC BY 4.0 example data, plus the research-use disclaimer - docs/license_hed_matlab.txt: MIT notice for files copied from hed-matlab - Remove the unsupported BLASST line-noise option (Apache-2.0 code) and its lineNoiseMethod branch Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 6 +- CHANGELOG.md | 5 + LICENSE | 340 ++++++++++++++++++ PrepPipeline/utilities/blasst/README.md | 4 - PrepPipeline/utilities/blasst/blasst.m | 227 ------------ .../utilities/blasst/blasst_documentation.tex | 163 --------- .../utilities/blasst/blasst_internal.m | 227 ------------ PrepPipeline/utilities/blasst/blasst_test.m | 135 ------- PrepPipeline/utilities/blasst/pop_blasst.m | 86 ----- PrepPipeline/utilities/blasstLineNoise.m | 67 ---- PrepPipeline/utilities/getPrepDefaults.m | 2 +- PrepPipeline/utilities/removeLineNoise.m | 2 - README.md | 24 +- docs/license_hed_matlab.txt | 21 ++ 14 files changed, 393 insertions(+), 916 deletions(-) create mode 100644 LICENSE delete mode 100644 PrepPipeline/utilities/blasst/README.md delete mode 100644 PrepPipeline/utilities/blasst/blasst.m delete mode 100644 PrepPipeline/utilities/blasst/blasst_documentation.tex delete mode 100644 PrepPipeline/utilities/blasst/blasst_internal.m delete mode 100644 PrepPipeline/utilities/blasst/blasst_test.m delete mode 100644 PrepPipeline/utilities/blasst/pop_blasst.m delete mode 100644 PrepPipeline/utilities/blasstLineNoise.m create mode 100644 docs/license_hed_matlab.txt diff --git a/AGENTS.md b/AGENTS.md index 10926e0..f87f4c3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ Not in scope: EEGLAB itself, which PREP runs inside and depends on, and the down ## Commands -Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/blasst_test.m` is a vendored demo script, not a test. Do not add a suite as a side effect of other work. +Test framework: none. There is no test suite. Do not add a suite as a side effect of other work. - Smoke check (no EEGLAB needed): `matlab -batch "addpath(genpath('PrepPipeline')); disp(getPrepVersion())"` - prints the version string, for example `PrepPipeline0.57.0` - Run standalone: add `PrepPipeline` and its subfolders to the MATLAB path, then `[EEG, params, computationTimes] = prepPipeline(EEG, params)` on an EEGLAB `EEG` structure with channel locations. Needs EEGLAB and the Signal Processing Toolbox on the path. @@ -18,7 +18,7 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla ## Layout - `PrepPipeline/` - entry points: `prepPipeline.m`, `pop_prepPipeline.m` (EEGLAB GUI wrapper), `prepPostProcess.m`, `prepReport.m`, `publishPrepReport.m`, `eegplugin_prepPipeline.m` (EEGLAB menu registration) -- `PrepPipeline/utilities/` - the algorithms (`removeTrend`, `cleanLineNoise`, `performReference`, `findNoisyChannels`, defaults, version); `chronux_2_modified/` and `blasst/` are vendored third-party code +- `PrepPipeline/utilities/` - the algorithms (`removeTrend`, `cleanLineNoise`, `performReference`, `findNoisyChannels`, defaults, version); `chronux_2_modified/` is vendored third-party code (GPL v2; see the licensing table in `README.md`) - `PrepPipeline/reporting/` - report and collection-statistics functions - `PrepPipeline/interface/` - the EEGLAB parameter GUIs - `PrepPipeline/derived/`, `PrepPipeline/examples/`, `PrepPipeline/extracted/` - scripts built on the pipeline @@ -37,7 +37,7 @@ Test framework: none. There is no test suite; `PrepPipeline/utilities/blasst/bla ## Rules that are easy to get wrong - The version exists in three places that must agree: `PrepPipeline/utilities/getPrepVersion.m` (the change log that `getPrepVersion` returns), the zip name under `EEGLABPlugin/`, and `CHANGELOG.md`. Change all three together. -- Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/` or `PrepPipeline/utilities/blasst/`. +- Do not reformat, lint, or ASCII-clean vendored code under `PrepPipeline/utilities/chronux_2_modified/`. - `docs/api.rst` pulls each function's help text from the comment block right after its `function` line. `docs/conf.py` shows that text preformatted, exactly as MATLAB `help` prints it, so write help for `help`, not as reStructuredText. Help placed above the `function` line does not appear there, though MATLAB `help` still finds it. Functions at the root of `PrepPipeline/` need `.. mat:currentmodule:: .` before their `mat:autofunction` directives. - Do not change the signature of an entry-point function without discussion; EEGLAB and user scripts call them directly, and `pop_prepPipeline` writes the call into EEGLAB history. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c9069b..e575e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ Release history of the PREP pipeline, newest first. The current version is also reported by `getPrepVersion` (`PrepPipeline/utilities/getPrepVersion.m`). +## Unreleased + +* Removed the unsupported BLASST line-noise option (`lineNoiseMethod` 'blasst') +* Added a root LICENSE (GPL-2.0-or-later) and a licensing table in README.md + ## Version 0.57.0 - Released 3/31/2025 * Modified to work with modified EEGLAB GUI Builder diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/PrepPipeline/utilities/blasst/README.md b/PrepPipeline/utilities/blasst/README.md deleted file mode 100644 index 5430be1..0000000 --- a/PrepPipeline/utilities/blasst/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# blasst -BLASST: Band Limited Atomic Sampling with Spectral Tuning Toolbox - -This option is still under testing. diff --git a/PrepPipeline/utilities/blasst/blasst.m b/PrepPipeline/utilities/blasst/blasst.m deleted file mode 100644 index 3a00a1a..0000000 --- a/PrepPipeline/utilities/blasst/blasst.m +++ /dev/null @@ -1,227 +0,0 @@ -function [x,varargout] = blasst(x,lineFrequencies,frequencyRanges,samplingRate,varargin) -% blasst(): EEGLAB helper function for OCW line noise removal. -% Takes as input an array of signals x, along with relevant parameters, and -% performs BLASST filtering at specified frequencies. For each specified -% frequency, blasst() iteratively calls blasst_internal() and then uses -% blasst_test() to test for convergence based on the distributions of -% convolution coefficients in the target and surrounding frequency bands. -% -% INPUT: -% x an [n,N] array of n signals of length N. -% lineFrequencies an array of target frequencies (not normalized). -% frequenyRanges an array of target frequency ranges. Must be the same -% size as lineFrequencies. -% samplingRate the sampling rate of the signal. -% varargin optional 'key',value pairs: -% 'key' -% [default] purpose -% 'Scale' -% [2^(log2(samplingRate)+2)] Manually set the scale that -% indicates the spread of Gabor atoms. -% 'ContinuousEpochs' -% [0] If x is epoched, but epochs are temporally adjacent, -% setting to 1 will flatten x for processing. Otherwise, -% blasst is run on individual epochs. -% 'Verbose' -% [1] When on, progress is printed on command line. -% 'Resolution' -% [2] May be an integer value >= 1, sets 'resolution' in blasst. -% Specificies density of Gabor atoms. May also be an array of -% integer values of size(lineFrequencies). -% 'MaxIterations' -% [50] Maximum number of external iterations of blasst run on -% each frequency. May be either a scalar integer or array of -% integers of size(lineFrequencies). -% 'ManualOffset' -% [log2(scaleBases)+1] A scalar value that offsets the -% arrangement of Gabor atoms at each iteration of blasst. -% 'Channels' -% [1:size(x,1)] An array of integers indexing channels to -% be computed. Allows manually selection of channels for -% processing. -% -% OUTPUT: -% x the processed, or ``cleaned'' signal. -% varargout{1} the aggregate of the target signal feature removed, an -% array of size(x). -% -% DEPENDENCIES: -% blasst_internal() primary line noise removal algorithm. -% blasst_test() convergence test for iterative blasst algorithm. -% -% EXAMPLE: -% Suppose we wish to remove line noise frequency at 60 and 120 Hz, and the -% noise is mostly stationary at 120 Hz but non-stationary varying by about -% 2 Hz, around 60 Hz. Then we might call the method as: -% >> x = blasst(x,[60,120],[2,.25],); -% -% If we want to use more densely packed Gabor atoms, we could call: -% >> x = blasst(x,[60,120],[2,.25],,'Resolution',4); -% -% AUTHOR: Kenneth Ball, 2015. -% -% IF YOU FIND BLASST USEFUL IN YOUR WORK, PLEASE CITE: -% -% Ball, K. R., Hairston, W. D., Franaszczuk, P. J., Robbins, K. A., -% BLASST: Band Limited Atomic Sampling with Spectral Tuning with -% Applications to Utility Line Noise Filtering, [Under Review]. -% -% Copyright 2015 Kenneth Ball -% -% Licensed under the Apache License, Version 2.0 (the "License"); -% you may not use this file except in compliance with the License. -% You may obtain a copy of the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -% See the License for the specific language governing permissions and -% limitations under the License. - -% Set defaults: -flattenData = 0; -verbose = 1; -resolution = 2; -maxIterations = 50; % Default is high so that the method generally will generally converge. -channels = 1:size(x,1); - -% Adjust for optional inputs -if (nargin> 1) - if (nargin> 4 && rem(nargin,2) == 1) - if length(varargin) == 1 - varargin = varargin{1}; - else - fprintf('blasst(): Optional key and value pairs do not match.') - return - end - end - - for ii = 1:2:length(varargin) - key = varargin{ii}; - val = varargin{ii+1}; - switch key - case 'SamplingRate' - samplingRate = val; - case 'Scale' - scale = val; - case 'ContinuousEpochs' - flattenData = val; - case 'Verbose' - verbose = val; - case 'Resolution' - resolution = val; - case 'MaxIterations' - maxIterations = val; - case 'ManualOffset' - manualOffset = val; - case 'Channels' - channels = val; - end - end -end - -if length(frequencyRanges) == 1 - frequencyRanges = ones(size(lineFrequencies))*frequencyRanges; -elseif length(frequencyRanges) ~= length(lineFrequencies) - error('Number of specified noise frequencies does not match number of specified range values.'); -end - -if length(resolution) == 1 - resolution = ones(size(lineFrequencies))*resolution; -elseif length(resolution) ~= length(lineFrequencies) - error('Number of specified noise resolutions does not match number of specified range values.'); -end - -if length(maxIterations) == 1 - maxIterations = ones(size(lineFrequencies))*maxIterations; -elseif length(maxIterations) ~= length(lineFrequencies) - error('Number of specified max iterations does not match number of specified range values.'); -end - -if ~exist('samplingRate','var') - error('No sampling rate specified.'); -elseif isempty(samplingRate) || samplingRate == 0 - error('Null or zero sampling rate specified.'); -end - -if ~exist('scaleBases','var') - scale = 2^(log2(samplingRate)+2); -end - -if ~exist('manualOffset','var') - manualOffset = log2(scale)+1; -end - -if flattenData - dataSizeTemp = size(x); - x = reshape(x,size(x,1),size(x,2)*size(x,3)); -end - -% Initialize holders for fitted noise and cleaned signals. -y = zeros(size(x)); -yTemp = zeros(size(x)); -xTemp = zeros(size(x)); - -countTrack = 1; - -for ii = 1:length(lineFrequencies) - - if verbose - fprintf(1,'Frequency: %d Hz\n',lineFrequencies(ii)); - end - for jj = channels - if verbose - fprintf(1,'Computing Channel: '); - end - % BDist = Inf; - [BDist,~,~] = blasst_test(reshape(x(jj,:,:),1,size(x,2)*size(x,3)),lineFrequencies(ii),frequencyRanges(ii),samplingRate,scale); - for mm = 1:maxIterations(ii) - if verbose - fprintf(1,'\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b% 3i Pass: % 3i',jj,mm); - end - % Run blasst_internal for each epoch of data in the EEG struct. - for kk = 1:size(x,3) - [yTemp(jj,:,kk),xTemp(jj,:,kk)] = blasst_internal(x(jj,:,kk),lineFrequencies(ii),frequencyRanges(ii),samplingRate,scale,resolution(ii),(mm-1)*manualOffset); - % y(jj,:,kk) = y(jj,:,kk) + tempY; - % EEG.data(jj,:,kk) = tempX; - end - - % Compute Bhatt. distance for testing convergence. Overwrite - % BDist1, then compare to BDist (from the last pass). If BDist1 - % exceeds BDist, we presume we have passed the minimum distance - % between the distributions, and we should halt the algorithm - % for this channel and frequency, retaining only previous - % passes. - - [BDist1,maxFlag,~] = blasst_test(reshape(xTemp(jj,:,:),1,size(xTemp,2)*size(xTemp,3)),lineFrequencies(ii),frequencyRanges(ii),samplingRate,scale); - countTrack = countTrack+1; - if BDist1 >= BDist && ~maxFlag % && mm > 1 - if verbose - fprintf(1,'\nBreak at pass % 2i\n',mm-1); - end - break - else - BDist = BDist1; - y(jj,:,:) = y(jj,:,:) + yTemp(jj,:,:); - x(jj,:,:) = xTemp(jj,:,:); - end - end - if verbose - fprintf(1,'\n'); - end - end - if verbose - fprintf(1,'\n'); - end - -end - -varargout{1} = y; % This is the aggregate of all line noise that was removed. That is, y+EEG.data is the original dataset. - -if flattenData - x = reshape(x,dataSizeTemp(1),dataSizeTemp(2),dataSizeTemp(3)); -end - -end diff --git a/PrepPipeline/utilities/blasst/blasst_documentation.tex b/PrepPipeline/utilities/blasst/blasst_documentation.tex deleted file mode 100644 index 13a933e..0000000 --- a/PrepPipeline/utilities/blasst/blasst_documentation.tex +++ /dev/null @@ -1,163 +0,0 @@ -\documentclass[11pt]{article} - -\usepackage{amsmath} -\usepackage{amsfonts} -\usepackage{amsthm} -\usepackage{fullpage} -\usepackage[square,numbers]{natbib} -\usepackage{hyperref} - -\usepackage[percent]{overpic} -\usepackage{graphicx} -\usepackage{cmap} - - -%%Theorems -\newtheorem{theorem}{Theorem}[section] -\newtheorem{corollary}[theorem]{Corollary} -\newtheorem{proposition}[theorem]{Proposition} -\newtheorem{claim}[theorem]{Claim} -\newtheorem{lemma}[theorem]{Lemma} -\newtheorem{definition}[theorem]{Definition} -\newtheorem{axiom}[theorem]{Axiom} -\newtheorem{problem}[theorem]{Problem} - -\theoremstyle{remark} -\newtheorem*{remark}{Remark} - -%%% Comments and Todos -\newcommand{\ppar}[1]{\noindent{\em{#1}}} -\newcommand{\comment}[1]{\par\noindent{\raggedright\texttt{#1} -\par\marginpar{\textsc{Comment}}}} -\newcommand{\todo}[1]{\vspace{5 mm}\par \noindent -\marginpar{\textsc{ToDo}} -\framebox{\begin{minipage}[c]{0.98\columnwidth} -\tt #1 \end{minipage}}\vspace{5 mm}\par} - -%\usepackage{parskip} - -\title{BLASST: A MATLAB toolbox for filtering long-time, nonstationary signal artifacts.} -\author{Kenneth Ball} -\date{September, 2015} - -\begin{document} - -\maketitle - -\section*{Preliminaries} -If you find this tool/algorithm useful, please cite our associated paper: \\ \ \\ - -\noindent Ball, K.~R., Hairston, W.~D., Franaszczuk, P.~J., Robbins, K.~A., BLASST: Band Limited Atomic Sampling with Spectral Tuning with Applications to Utility Line Noise Filtering, [Under Review]. \\ \ \\ - -\section{Overview of BLASST} -Band Limited Atomic Sampling with Spectral Tuning (BLASST) is an algorithm developed to filter relatively long-time, non-stationary signal features, especially the 50/60 Hz utility line noise that is almost ubiquitious in sensitive experiments. Our motivating use case is ambulatory EEG experiments where gamma-range neural features may be of interest. We observe that most experimenters either notch filter about 50/60 Hz, or low-pass filter signals at some threshold below 50/60 Hz in order to remove the often dominant line noise features from their collected signals. Especially in cases where the utility line noise is highly non-stationary, due to either fluctuations in power generation atht the utility level or non-linear effects caused by interfering electronics in the laboratory, a relatively wide notch-filter may be required to completely remove the noise, which in turn causies a complete loss of otherwise useful information in the targeted spectral band. - -BLASST attempts to fit non-stationary line-noise more flexibly by attempting to reconstruct the dominant signal in the supplied target frequency range with a set of Gabor atoms arranged in time so that their envoloping Gaussian functions (approximately) add up to a partition of unity. BLASST iterates this fitting approach until a stopping criterion is reached. Information from spectral bands outside of the user-supplied target bands is leveraged to (1) modulate the fit at each iteration and (2) determine a stopping criterion based on the distribution of the amplitudes of complex Gabor atoms drawn from inside and outside the target frequency spectrum. - -\section{blasst():} -\verb![signalOut , varargout] = blasst(signalIn , lineFrequencies , frequencyRanges , ...! \verb!samplingRate , varargin);! - -\subsection{Input:} -\begin{itemize} -\item \verb!signalIn! :: an $n\times N$ array of signal data. The signals are of length $N$, and there are $n$ total channels of data (so generally $n << N$). - -\item \verb!lineFrequencies! :: an array of target frequencies (not normalized). - -\item \verb!frequencyRanges! :: an array of target frequency ranges. Must be the same size as \verb!lineFrequencies!. - -\item \verb!samplingRate! :: the sampling rate of the signal. - -\item \verb!varargin! :: optional 'key',value pairs: - -\begin{itemize} - -\item \verb~'key'~ - [default] purpose -\item \verb~'Scale'~ - [\verb~2^(log2(samplingRate)+2)~] Manually set the scale that - indicates the spread of Gabor atoms. -\item \verb~'ContinuousEpochs'~ - [\verb~0~] If x is epoched, but epochs are temporally adjacent, - setting to 1 will flatten x for processing. Otherwise, - blasst is run on individual epochs. -\item \verb~'Verbose'~ - [\verb~1~] When on, progress is printed on command line. -\item \verb~'Resolution'~ - [\verb~2~] May be an integer value $>= 1$, sets 'resolution' in blasst. - Specificies density of Gabor atoms. May also be an array of - integer values of \verb~size(lineFrequencies)~. -\item \verb~'MaxIterations'~ - [\verb~50~] Maximum number of external iterations of blasst run on - each frequency. May be either a scalar integer or array of - integers of \verb~size(lineFrequencies)~. -\item \verb~'ManualOffset'~ - [\verb~log2(scaleBases)+1~] A scalar value that offsets the - arrangement of Gabor atoms at each iteration of blasst. -\item \verb~'Channels'~ - [\verb~1:size(x,1)~] An array of integers indexing channels to - be computed. Allows manually selection of channels for - processing. - -\end{itemize} - -\end{itemize} - -\subsection{Output} -\begin{itemize} - -\item \verb!signalOut! :: the processed, or ``cleaned'' signal, an array of \verb~size(signalIn)~. -\item \verb!varargout{1}! :: the aggregate of the target signal feature removed, an array of \verb!size(signalIn)!. - -\end{itemize} - -\subsection{Basic Use Cases} - -\begin{itemize} -\item Suppose you have collected signals sampled at 512 Hz, and you wish to identify and remove 60 Hz line noise. The line noise seems to be relatively stationary in frequency, so you only target features between 59.75 and 60.25 Hz. Then you may run: - -\verb~ >>signalOut = blasst(signalIn,60,0.25,512);~ - -If you would like to return the noise removed \verb~noise~, you may run: - -\verb~ >>[signalOut,noise] = blasst(signalIn,60,0.25,512);~ - -\item If you would like to remove harmonics of 60 Hz up to 256 Hz (the Nyquist frequency), you may run: - -\verb~ >>signalOut = blasst(signalIn,[60,120,180,240],0.25,512);~ - -\item Suppose you observe high non-stationarity in frequency at 120 Hz, but tight bounds on spectral power at the other harmonics of 60 Hz. Increasing the target frequency range increases the time required to search for best fit Gabor atoms, and also increases the possiblity of overfitting. Thus, it is preferable to only increase the target frequency range value for target frequencies where it is requried. In this case, you may run: - -\verb~ >>signalOut = blasst(signalIn,[60,120,180,240],[0.25,2,0.25,0.25],512);~ - -In this case, blasst() will fit features between $60\pm 0.25$, $120 \pm 2$, $180\pm 0.25$, and $240\pm 0.25$ Hz. - -\end{itemize} - -\subsection{Advanced Use Cases} - -\begin{itemize} - -\item The resolution of the fit at each iteration can be increased (or decreased) from the default value of $k = 2$ by using a key value pair \verb~ 'Resolution',k~ where \verb~k~ is an integer value greather than or equal to 1. In theory, higher resoltuion arrangements of Gabor atoms (more tightly packed) should allow for my fine tuned temporal flexibility. Of course, the Gabor atoms themselves should already be very spread out, so this may not do much. To increase the resolution to $k = 4$, you may run: - -\verb~ >>signalOut = blasst(signalIn,60,0.25,512,'Resolution',4);~ - -\item The scales of Gabor atoms can be manually adjusted. Higher scales allow for more specificity in frequencies, but less resolution in time; lower scales allow for more specificity in time, but less resolution in frequency. Scales are input as the number of time samples; the default value corresponds to 4 seconds (regardless of sampling rate). Suppose you wanted to use Gabor atoms with scales of 3 seconds, and the sampling rate was 250 Hz. Then you could run: - -\verb~ >>signalOut = blasst(signalIn,60,0.25,256,'Scale',3*256);~ - -\end{itemize} - -\subsection{Alterantive Use Cases} - -\begin{itemize} - -\item We believe BLASST may be useful for other types of filtering were flexibility and robustness to short-time signal features is desirable. For example suppose you were interested in the appearance of alpha spindles in a 512 Hz EEG signal between 9 and 14 Hz. Further, suppose you expect such features to appear in bursts of at least 0.5 seconds. Then you could run: - -\verb~ >>[signalOut,alpha] = blasst(signalIn,11.5,2.5,512,'Scale',512/2,'Resolution',4);~ - -Then \verb~alpha~ will be returned as the alpha features. Theoretically, BLASST should return the desired alpha features while reducing artifacts in the target spectrum caused by spectral bleed of more temporally localized spikes of activity, such as muscle movements. - - -\end{itemize} - -\end{document} diff --git a/PrepPipeline/utilities/blasst/blasst_internal.m b/PrepPipeline/utilities/blasst/blasst_internal.m deleted file mode 100644 index a3527c0..0000000 --- a/PrepPipeline/utilities/blasst/blasst_internal.m +++ /dev/null @@ -1,227 +0,0 @@ -function [y,x] = blasst_internal(x,f,r,sR,scale,rez,manualOffset) -% blasst_internal(): An iteration of BLASST feature fitting. -% Takes a 1-dimensional signal and fits a series of Gabor atoms (arranged -% so that the enveloping Gaussian functions add up to a partition of unity) -% to try and remove the target frequency (f) within a range (\pm r), while -% respecting the time-frequency power distribution of the surrounding -% spectral bands. -% -% INPUT: -% x is [1,N] time series signal. -% f is the target frequency (a scalar, NOT normalized) ex. 60 for 60 Hz -% r is the target frequency range within which we seek to remove noise. -% For example, if line noise is not stationary, we might seek to -% remove noise between 57 and 63 Hz, in which case r = 3. -% sR is the sampling rate of the signal. -% scale is the integer scale for Gabor atoms. -% rez is an integer >= 1 that specifies the density of Gabor atoms in the -% partition of unity. rez must take an integer value so that Gaussian -% functions add up to unity. -% manualOffset is a scalar that offsets the centers of atoms at the outset -% of computation. -% -% OUTPUT: -% y the [1,N] time course of signal features removed from x. -% x the [1,N] transformed signal. -% -% AUTHOR: Kenneth Ball, 2015. -% -% IF YOU FIND BLASST USEFUL IN YOUR WORK, PLEASE CITE: -% -% Ball, K. R., Hairston, W. D., Franaszczuk, P. J., Robbins, K. A., -% BLASST: Band Limited Atomic Sampling with Spectral Tuning with -% Applications to Utility Line Noise Filtering, [Under Review]. -% -% Copyright 2015 Kenneth Ball -% -% Licensed under the Apache License, Version 2.0 (the "License"); -% you may not use this file except in compliance with the License. -% You may obtain a copy of the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -% See the License for the specific language governing permissions and -% limitations under the License. - - - -N = length(x); -y = zeros(size(x)); -n2 = scale*2; % half-window length -n = 2*n2+1; % win length -win = (-n2):(n2); - -% timejump and estimate amplitude scaling for partition of unity of Gaussian functions: -timeJump = scale/rez/sqrt(pi); -R = -(rez*10):(rez*10); -scalingFactor = 1./sum(exp(-R.^2/rez.^2)); - -% spec frequency ranges: -fs = sR*(0:(1/(2*scale)):.5); -fs = fs(intersect(find(fs>=(f-r)),find(fs<=(f+r)))); - -wavelets = complexGabor(fs/sR,scale,0,win); % wavelets is [length(fs),length(win)] = [length(fs),n]; - -% spec test frequency ranges: -tfs = sR*(0:(1/(2*scale)):.5); -tfsl = tfs(intersect(find(tfs>=(f-3*r)),find(tfs<=(f-2*r)))); -tfsr = tfs(intersect(find(tfs>=(f+2*r)),find(tfs<=(f+3*r)))); -tfs = [tfsl,tfsr]; -testWavelets = complexGabor(tfs/sR,scale,0,win); - -modulator = buildPowerModulator([zeros(1,2*n),x,zeros(1,2*n)],testWavelets,fs,tfs); - -% Build the windowed signal array: -centers = (1+manualOffset):timeJump:N; % Interior centers. -leftCenters = centers(1); -centers = centers(2:end); -while round(leftCenters(1)) > -n2+1 - leftCenters = cat(2,leftCenters(1)-timeJump,leftCenters); -end -while round(leftCenters(end)) < n2 - leftCenters = cat(2,leftCenters,leftCenters(end)+timeJump); - centers = centers(2:end); -end -rightCenters = centers(end); -centers = centers(1:end-1); -while round(rightCenters(end)) < N+n2 - rightCenters = cat(2,rightCenters,rightCenters(end)+timeJump); -end -while round(rightCenters(1)) > N-n2 - rightCenters = cat(2,rightCenters(1)-timeJump,rightCenters); - centers = centers(1:end-1); -end -centers = round([leftCenters,centers,rightCenters]); -weights = scale/2*(erf(sqrt(pi)*(N-centers)/scale)-erf(sqrt(pi)*(1-centers)/scale))/scale; -weights = weights( 2:(end-1) ); -centers = centers( 2:(end-1) ); - -% Pad x and y, initialize X (holder of signal sections on temporal support of atoms), and initizlize the power modulator: -x = [zeros(1,2*n),x,zeros(1,2*n)]; -y = [zeros(1,2*n),y,zeros(1,2*n)]; -X = zeros(length(centers),n); -pM = zeros(size(X,1),size(modulator,1)); % the power modulator - -% Adjust the "centers" index to account for the padding by n -centers = centers + 2*n; - -% Build X and the power modulator. -for jj = 1:length(centers) - X(jj,:) = x((-n2:n2)+centers(jj)); - pM(jj,:) = mean(modulator(:,round( ((-scale/(2*rez)):(scale/(2*rez)))+centers(jj))),2)'; -end - -% Calculate phase and amplitude of fitted gabor atoms: -phase = -angle( X*wavelets.'); %[windowCount,length(fs)] -realWavelets = zeros(size(X,1),n,length(fs)); -amplitude = zeros(size(phase)); -for jj = 1:length(fs) - % Compute the real wavelets. - realWavelets(:,:,jj) = real(exp(1i*phase(:,jj))*wavelets(jj,:)); %[windowCount,1]*[1,n] = [windowCounts,n] - % Modify the real wavelet amplitude by the power modulator. - amplitude(:,jj) = sum(X.*squeeze(realWavelets(:,:,jj)),2)-pM(:,jj) ; - % Set negative modified amplitudes to zero. - amplitude(:,jj) = amplitude(:,jj).*(amplitude(:,jj) > 0); -end -% Find the best fit atom after power modulation. -[amps,whichFreq] = max(amplitude,[],2); % amps and whichFreq are [windowCount,1] - - -% Put the best fit atom into the the 1st instance of the 3rd index of -% realWavelets. -for jj = 1:size(X,1) - realWavelets(jj,:,1) = realWavelets(jj,:,whichFreq(jj)); -end -% Retain only the best fit atoms. -realWavelets = squeeze(realWavelets(:,:,1)); - -% Rescale atoms according to partition of unity. -realWavelets = bsxfun(@times,scalingFactor*amps./weights',realWavelets)*sqrt(2); - -% Update x and y with computed realWavelets atoms: -for jj = 1:length(centers) - x((-n2:n2)+centers(jj)) = x((-n2:n2)+centers(jj)) - realWavelets(jj,:); - y((-n2:n2)+centers(jj)) = y((-n2:n2)+centers(jj)) + realWavelets(jj,:); -end -x = x((2*n+1):(end-2*n)); -y = y((2*n+1):(end-2*n)); - -end - -function modulator = buildPowerModulator(x,testDict,targetFreqs,testFreqs) - -C = multiConvolveFFT(testDict,x); - -CmodWeights = zeros(length(targetFreqs),length(testFreqs)); - -for kk = 1:length(targetFreqs) - CmodWeights(kk,:) = targetFreqs(kk)-testFreqs; -end -CmodWeights = abs(1./CmodWeights); -CmodWeights = bsxfun(@times,CmodWeights,1./sum(CmodWeights,2)); - -modulator = sqrt(exp( CmodWeights*log(abs(C).^2) )); - - -end - -function gaborFun = complexGabor(f,s,u,time) -% can output multiple gaborFuns: gaborFun is [p,n] -% time is [1,n] -% s,f are [1,p] -s = s'; % [p,1] -f = f'; % [p,1] -if ~isinf(s) - g1 = bsxfun(@times,2^(1/4)/sqrt(s),exp(-pi/s^2*(time-u).^2)); -% goo = length(time)-(s+1); -% g1 = [zeros(1,goo/2),dpss(s+1,3,1)',zeros(1,goo/2)]; - gaborFun = bsxfun(@times,g1,exp(1i*2*pi*f*(time-u))); -else - gaborFun = exp(1i*2*pi*f*(time-u)); -end -% gaborFun = bsxfun(@times,gaborFun,(1./sqrt(sum(abs(gaborFun.').^2)))'); -% if f == 0 -% gaborFun = gaborFun./sqrt(2); -% end - -end - -function [C,varargout] = multiConvolveFFT(filters,y) -% filters are an [k,n] bank of functions, where n is length of each filter -% and k is the number of filters. y is a [1,N] signal vector. -% returns [k,N] (absolute value) convolutions. -% optionaly returns the phase, or argument, of the complex convolutions. - -% pad = size(filters,2); -% nn = 3*pad+length(y)-1; -% yF = fft([zeros(1,pad),y,zeros(1,pad)],nn); -% fF = fft(filters,nn,2); -% C = ifft(bsxfun(@times,yF,fF),[],2); - -% % Convolve left and right edges: -% n2 = ceil((size(filters,2)-1)/2); -% n = size(filters,2); -% yL = [-fliplr(y(1:n)),y(1:n)]; -% yR = [y((end-n+1):end),-fliplr(y((end-n+1):end))]; -% for ii = 1:n2 -% conL(:,ii) = sum(bsxfun(@times,filters,yL( (ii+n2):(ii+n2-1+n) )),2); -% conR(:,ii) = sum(bsxfun(@times,filters,yR( (ii):(ii-1+n) )),2); -% end - - - nn = size(filters,2) + length(y) ; % nn = n+N - yF = fft(y,nn); - fF = fft(filters,nn,2); - C = ifft(bsxfun(@times,yF,fF),[],2); - C = C(:,( floor(size(filters,2)/2+1) + (0:(length(y)-1)) )); - -% C(:,1:n2) = conL; -% C(:,(end-n2+1):end) = conR; - - varargout{1} = angle(C); - %C = abs(C); - -end diff --git a/PrepPipeline/utilities/blasst/blasst_test.m b/PrepPipeline/utilities/blasst/blasst_test.m deleted file mode 100644 index daf2d90..0000000 --- a/PrepPipeline/utilities/blasst/blasst_test.m +++ /dev/null @@ -1,135 +0,0 @@ -function [BDist,maxFlag,varargout] = blasst_test(x,f,r,sR,scale) -% blasst_test(): Tests for convergence of the BLASST line noise removal -% approach by comparing the Bhattahcharya distance between the target and -% test frequency bands. -% -% INPUT: -% x is [1,N] time series signal. -% f is the target frequency (a scalar, NOT normalized) ex. 60 for 60 Hz -% r is the target frequency range within which we seek to remove noise. -% For example, if line noise is not stationary, we might seek to -% remove noise between 57 and 63 Hz, in which case r = 3. -% sR is the sampling rate of the signal. -% scale is the scale of Gabor atoms to be compared. -% -% OUTPUT: -% BDist is the Bhattacharya distance between the weighted average -% probability distributions of the test and target bands. -% maxFlag is a flag that specifies whether or not the target -% distribution is trivially way to the right of the test. -% Helps to avoid numerical errors in convergence at early -% iterations of OCW_LNR. -% varargout{1} is a struct with fields: -% 'CHists' is the the probability distributions of the target -% spectral band. -% 'DHists' is the the probability distributions of the test spectral -% band. -% -% AUTHOR: Kenneth Ball, 2015. -% -% IF YOU FIND BLASST USEFUL IN YOUR WORK, PLEASE CITE: -% -% Ball, K. R., Hairston, W. D., Franaszczuk, P. J., Robbins, K. A., -% BLASST: Band Limited Atomic Sampling with Spectral Tuning with -% Applications to Utility Line Noise Filtering, [Under Review]. -% -% Copyright 2015 Kenneth Ball -% -% Licensed under the Apache License, Version 2.0 (the "License"); -% you may not use this file except in compliance with the License. -% You may obtain a copy of the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -% See the License for the specific language governing permissions and -% limitations under the License. - -N = size(x,2); -n2 = scale*2; % half-window length -win = (-n2):(n2); - -% spec frequency ranges: -fs = sR*(0:(1/(2*scale)):.5); -fs = fs(intersect(find(fs>=(f-r)),find(fs<=(f+r)))); - -wavelets = complexGabor(fs/sR,scale,0,win); % wavelets is [length(fs),length(win)] = [length(fs),n]; - -% spec test frequency ranges: -tfs = sR*(0:(1/(2*scale)):.5); -tfsl = tfs(intersect(find(tfs>=(f-3*r)),find(tfs<=(f-2*r)))); -tfsr = tfs(intersect(find(tfs>=(f+2*r)),find(tfs<=(f+3*r)))); -tfs = [tfsl,tfsr]; -testWavelets = complexGabor(tfs/sR,scale,0,win); - -C = multiConvolveFFT(wavelets,x); -D = multiConvolveFFT(testWavelets,x); - -testWeights = zeros(length(fs),length(tfs)); %[targetFrequencies,testFrequencies] -for kk = 1:length(fs) - testWeights(kk,:) = fs(kk)-tfs; -end -testWeights = abs(1./testWeights); -testWeights = bsxfun(@times,testWeights,1./sum(testWeights,2)); - -D = abs(D); -C = abs(C); % Now C and D are real and same size: [ - -binCount = ceil(2*N^(1/3)); -[DHists,centers] = hist(log((abs(D).^2)'),binCount); %[binCount,lenegth(tfs)] vectors -DHists = DHists/N; - -DCompare = mean(DHists*testWeights',2); -CHists = hist(log((abs(C).^2)'),centers); -CHists = CHists/N; -CCompare = mean(CHists,2); - -[~,CMaxInd] = max(CCompare); -if CMaxInd == binCount % Presumably, CCompare distribution is somewhat skewed to the right and we are nowhere near convergence. - maxFlag = 1; -else - maxFlag = 0; -end - -BDist = -log(sum(sqrt(DCompare.*CCompare))+1e-8); -varargout{1}.CHist = CHists; -varargout{1}.DHist = DHists; -varargout{1}.CCompare = CCompare; -varargout{1}.DCompare = DCompare; -varargout{1}.centers = centers; - - -end - -function gaborFun = complexGabor(f,s,u,time) -% can output multiple gaborFuns: gaborFun is [p,n] -% time is [1,n] -% s,f are [1,p] -s = s'; % [p,1] -f = f'; % [p,1] -if ~isinf(s) - g1 = bsxfun(@times,2^(1/4)/sqrt(s),exp(-pi/s^2*(time-u).^2)); -% goo = length(time)-(s+1); -% g1 = [zeros(1,goo/2),dpss(s+1,3,1)',zeros(1,goo/2)]; - gaborFun = bsxfun(@times,g1,exp(1i*2*pi*f*(time-u))); -else - gaborFun = exp(1i*2*pi*f*(time-u)); -end - -end - -function [C,varargout] = multiConvolveFFT(filters,y) -% filters are an [k,n] bank of functions, where n is length of each filter -% and k is the number of filters. y is a [1,N] signal vector. -% returns [k,N] (absolute value) convolutions. -% optionaly returns the phase, or argument, of the complex convolutions. - nn = size(filters,2) + length(y); % nn = n+N - yF = fft(y,nn); - fF = fft(filters,nn,2); - C = ifft(bsxfun(@times,yF,fF),[],2); - C = C(:,( floor(size(filters,2)/2+1) + (0:(length(y)-1)) )); - varargout{1} = angle(C); - -end diff --git a/PrepPipeline/utilities/blasst/pop_blasst.m b/PrepPipeline/utilities/blasst/pop_blasst.m deleted file mode 100644 index f350c8c..0000000 --- a/PrepPipeline/utilities/blasst/pop_blasst.m +++ /dev/null @@ -1,86 +0,0 @@ -function [EEG,com] = pop_blasst(EEG,lineFrequencies,frequencyRanges,varargin) -% pop_blasst(): EEGLAB helper function for blasst filtering. -% Takes as input an EEGLAB EEG struct, along with relevant parameters, and -% calls blasst() for BLASST fitlering at specified frequencies. For each -% specified frequency, blasst() iteratively calls blasst_internal() and -% then uses blasst_test() to test for convergence based on the -% distributions of convolution coefficients in the target and surrounding -% frequency bands. -% -% AUTHOR: Kenneth Ball, 2015. -% -% IF YOU FIND BLASST USEFUL IN YOUR WORK, PLEASE CITE: -% -% Ball, K. R., Hairston, W. D., Franaszczuk, P. J., Robbins, K. A., -% BLASST: Band Limited Atomic Sampling with Spectral Tuning with -% Applications to Utility Line Noise Filtering, [Under Review]. -% -% Copyright 2015 Kenneth Ball -% -% Licensed under the Apache License, Version 2.0 (the "License"); -% you may not use this file except in compliance with the License. -% You may obtain a copy of the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -% See the License for the specific language governing permissions and -% limitations under the License. - -com = ''; - -if nargin < 1 - help pop_blasst; - return -end -if ~isfield(EEG,'data') - error('Must specify signal(s) in EEG struct as: \n >>EEG.data = ;') -elseif isempty(EEG.data) - error('We can not BLASST nothing! EEG.data is empty.'); -end - -if isempty(lineFrequencies) - error('BLASST requires input target frequencies.'); -end - -if isempty(frequencyRanges) - error('BLASST requires input frequency ranges.'); -end - -if ~isfield(EEG,'srate') - error('Must specify sampling rate in input EEG struct as: \n >>EEG.srate = ;') -elseif isempty(EEG.srate) - error('BLASST must have a sampling rate! EEG.srate is empty.') -end - -EEG.data = blasst(EEG.data,lineFrequencies,frequencyRanges,EEG.srate,varargin); -foo = []; -% Process varargin cell into string of name value pairs. -if ~isempty(varargin) - if ~rem(length(varargin),2) - foo = ','; - if ischar(varargin{2}) - foo = [foo,'''',varargin{1}, ''', ''',varargin{2},'''']; - else - foo = [foo,'''',varargin{1}, ''', ',num2str(varargin{2})]; - end - for ii = 3:2:length(varargin); - if ischar(varargin{ii+1}) - foo = [foo, ', ''', varargin{ii}, ''', ''', varargin{ii+1},'''']; - else - foo = [foo, ', ''', varargin{ii}, ''', ', num2str(varargin{ii+1})]; - end - end -% foo = [foo,' }']; - else - error('Name value pairs do not match.') - end -end - -com = sprintf('%s = pop_blasst(%s,%s,%s%s);',inputname(1),inputname(1),mat2str(lineFrequencies),mat2str(frequencyRanges),foo); - - - -end diff --git a/PrepPipeline/utilities/blasstLineNoise.m b/PrepPipeline/utilities/blasstLineNoise.m deleted file mode 100644 index bc2e6fb..0000000 --- a/PrepPipeline/utilities/blasstLineNoise.m +++ /dev/null @@ -1,67 +0,0 @@ -function [signal, lineNoiseOut] = blasstLineNoise(signal, lineNoiseIn) -% Remove sharp spectral peaks from signal using Sleppian filters -% -% Usage: -% signal = cleanLineNoise(signal) -% [signal, lineNoiseOut] = hcleanLineNoise(signal, lineNoiseIn) -% -% Parameters: -% signal Structure with .data and .srate fields -% lineNoiseIn Input structure with fields described below -% -% Structure parameters (lineNoiseIn): -% fPassBand Frequency band used (default [0, Fs/2] = entire band) -% Fs Sampling frequency -% fScanBandWidth +/- bandwidth centered on each f0 to scan for significant -% lines (TM) -% lineFrequencies Line frequencies to be removed (default -% [60, 120, 180, 240, 300]) -% lineNoiseChannels Channels to remove line noise from (default -% size(data, 1)) -% maximumIterations Maximum times to iterate removal (default = 10) -% p Significance level cutoff (default = 0.01) -% pad FFT padding factor ( -1 corresponds to no padding, -% 0 corresponds to padding to next highest power of 2 -% etc.) (default is 0) -% pnts -% tapers Precomputed tapers from dpss -% taperBandWidth Taper bandwidth (default 2 Hz) -% taperWindowSize Taper sliding window length (default 4 sec) -% taperWindowStep Sliding window step size (default 4 sec = no overlap) -% tau Window overlap smoothing factor (default 100) -% -% This function is based on code originally written by Tim Mullen in a -% package called tmullen-cleanline which is based on the chronux_2 -% libraries. -% - -lineNoiseOut = lineNoiseIn; -%% Remove line frequencies that are greater than Nyquist frequencies -tooLarge = lineNoiseOut.lineFrequencies >= lineNoiseOut.Fs/2; -if any(tooLarge) - warning('cleanLineNoise:LineFrequenciesTooLarge', ... - 'Eliminating frequencies greater than half the sampling rate'); - lineNoiseOut.lineFrequencies(tooLarge) = []; - lineNoiseOut.lineFrequencies = squeeze(lineNoiseOut.lineFrequencies); -end - -%% Set up parameters for blassting the line noise -fRange = lineNoiseOut.fScanBandWidth; -frequencyRanges = repmat(fRange, length(lineNoiseOut.lineFrequencies)); -sRate = lineNoiseOut.Fs; -lineFrequencies = lineNoiseOut.lineFrequencies; -maxIterations = lineNoiseOut.maximumIterations; - -%% Perform the calculation for each channel separately -signal.data = double(signal.data); -chans = lineNoiseOut.lineNoiseChannels; -data = signal.data(chans, :); -parfor ch = 1:size(data, 1) - data(ch, :) = blasst(squeeze(data(ch, :)), lineFrequencies, ... - frequencyRanges, sRate, ... - 'MaximumIterations', maxIterations, ... - 'Verbose', 0); -end -signal.data(chans, :) = data; -clear data; - diff --git a/PrepPipeline/utilities/getPrepDefaults.m b/PrepPipeline/utilities/getPrepDefaults.m index 70cb4a6..2805116 100644 --- a/PrepPipeline/utilities/getPrepDefaults.m +++ b/PrepPipeline/utilities/getPrepDefaults.m @@ -93,7 +93,7 @@ defaults = struct( ... 'lineNoiseMethod', ... getRules('clean', {'char'}, {}, ... - 'Method for removing line noise (clean or blasst or none)'), ... + 'Method for removing line noise (clean or none)'), ... 'lineNoiseChannels', ... getRules(1:size(signal.data, 1), {'numeric'}, ... {'row', 'positive', 'integer', '<=', size(signal.data, 1)}, ... diff --git a/PrepPipeline/utilities/removeLineNoise.m b/PrepPipeline/utilities/removeLineNoise.m index 3c7fe1e..7f1c47f 100644 --- a/PrepPipeline/utilities/removeLineNoise.m +++ b/PrepPipeline/utilities/removeLineNoise.m @@ -65,8 +65,6 @@ if strcmpi(lineNoiseOut.lineNoiseMethod, 'clean') [signal, lineNoiseOut] = cleanLineNoise(signal, lineNoiseOut); -elseif strcmpi(lineNoiseOut.lineNoiseMethod, 'blasst') - [signal, lineNoiseOut] = blasstLineNoise(signal, lineNoiseOut); elseif ~strcmpi(lineNoiseOut.lineNoiseMethod, 'none') error('removeLineNoise:BadLineNoiseMethod', ... 'Unrecognized line noise removal method'); diff --git a/README.md b/README.md index 95153c8..429d5da 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,34 @@ contents of `docs/` are ready to go live: the first Actions deployment overwrite the site at the same address. ### Citing the PREP pipeline -The PREP pipeline is freely available under the GNU General Public License. +The PREP pipeline is freely available under the GNU General Public License (see License below). Please cite the following publication if using: > Bigdely-Shamlo N, Mullen T, Kothe C, Su K-M and Robbins KA (2015) > The PREP pipeline: standardized preprocessing for large-scale EEG analysis > Front. Neuroinform. 9:16. doi: 10.3389/fninf.2015.00016 +### License +The PREP pipeline is licensed under the GNU General Public License, version 2 or +(at your option) any later version. The full text is in [LICENSE](LICENSE), and a +copy is kept with the plugin as `PrepPipeline/preplicense.txt`. Parts of the +repository come from other projects and keep their own licenses: + +| Component | Location | License | Copyright | +| --- | --- | --- | --- | +| PREP pipeline | everything not listed below | GPL-2.0-or-later ([LICENSE](LICENSE)) | Kay Robbins, with contributions from Nima Bigdely-Shamlo, Christian Kothe, Tim Mullen, Jeremy Cockfield, and Cassidy Matousek | +| Chronux 2, modified | `PrepPipeline/utilities/chronux_2_modified/` | GPL-2.0 (`License.txt` in that folder) | The Chronux developers ([chronux.org](http://www.chronux.org/)) | +| Line-noise removal and local detrending, adapted from cleanline and Chronux | `PrepPipeline/utilities/cleanLineNoise.m` and the functions it calls (`removeLinesMovingWindow.m`, `fitSignificantFrequencies.m`, `calculateSegmentSpectrum.m`, `private/checkTapers.m`); `PrepPipeline/utilities/localDetrend.m` | GPL, as the code they adapt | cleanline by Tim Mullen, which builds on Chronux; adaptations by Kay Robbins | +| Spherical interpolation | `PrepPipeline/utilities/private/spherical_interpolate.m` | Permissive: use, copy, and modify, keeping the copyright notice and noting changes (file header) | Jason D.R. Farquhar; modified by Kay Robbins | +| Helpers from EEGLAB | `PrepPipeline/reporting/calculateSpectrum.m`, `reporting/helpers/finputcheck.m`, `reporting/helpers/matsel.m` | GPL-2.0-or-later (file headers) | Scott Makeig, Arnaud Delorme, and Marissa Westerfield, SCCN, UCSD | +| Filter helpers | `PrepPipeline/utilities/private/design_fir.m`, `filter_fast.m`, `filtfilt_fast.m`, `hlp_microcache.m` | GPL-2.0-or-later (file headers) | Christian Kothe, SCCN, UCSD; `filter_fast.m` includes `fftfilt.m` from Octave by John W. Eaton | +| Documentation styling and build helper | `docs/_static/custom.css`, `docs/_static/gh_icon_fix.js`, `docs/patch_matlabdomain.py`, and parts of `docs/conf.py` | MIT ([docs/license_hed_matlab.txt](docs/license_hed_matlab.txt)) | HED Standard Working Group (from hed-matlab) | +| Example EEG data | `PrepPipeline/examples/data/` | Creative Commons Attribution 4.0 International ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)) | U.S. Army Research Laboratory and the authors of the dataset; cite Robbins, Su, and Hairston, "An 18-subject EEG data collection using a visual-oddball task, designed for benchmarking algorithms and headset performance comparisons", *Data in Brief* ([article](https://www.sciencedirect.com/science/article/pii/S2352340917306285), [full data on NITRC](https://www.nitrc.org/projects/vep_eeg_raw/)) | +| Released plugin | `EEGLABPlugin/PrepPipeline.zip` | As its contents, above | Each zip is a snapshot of `PrepPipeline/` at its release | + +The PREP pipeline is designed and distributed for research purposes only and +should not be used for medical purposes. The authors accept no responsibility +for its use in this manner. + ### People The PREP pipeline incorporates many algorithms that were developed at USCS SCCN over many years by Nima Bigdely-Shamlo, Tim Mullen and Christian Kothe. diff --git a/docs/license_hed_matlab.txt b/docs/license_hed_matlab.txt new file mode 100644 index 0000000..39c6a36 --- /dev/null +++ b/docs/license_hed_matlab.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020- HED Standard Working Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 2b01182fcdd745aa11806b9a898f47d7b56ac776 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sun, 27 Sep 2026 06:45:29 -0500 Subject: [PATCH 13/17] Point the example scripts at examples/data; add Zenodo DOI badge - runVEPPrepPipeline.m and runVEPPrepReport.m default to examples/data and examples/output, and convert the data to double after pop_loadset (.fdt files hold 32-bit samples) - .gitattributes: *.set and *.fdt are binary; .gitignore: examples/output - README.md: Zenodo DOI badge; citation line breaks that survive trailing-whitespace trimming The example data itself is not in this commit. Co-Authored-By: Claude Opus 5.5 --- .gitattributes | 2 ++ .gitignore | 3 +++ PrepPipeline/examples/runVEPPrepPipeline.m | 11 ++++++++--- PrepPipeline/examples/runVEPPrepReport.m | 15 ++++++++++----- README.md | 15 +++++++++------ 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/.gitattributes b/.gitattributes index a52d90c..85d8836 100644 --- a/.gitattributes +++ b/.gitattributes @@ -29,6 +29,8 @@ *.ico binary *.pdf binary *.zip binary +*.set binary +*.fdt binary *.gz binary *.tar binary *.mp3 binary diff --git a/.gitignore b/.gitignore index 1bd5509..24c1e80 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ docs/_build/ .venv/ __pycache__/ *.egg-info/ + +# Output of the example scripts +PrepPipeline/examples/output/ diff --git a/PrepPipeline/examples/runVEPPrepPipeline.m b/PrepPipeline/examples/runVEPPrepPipeline.m index 3a976f5..7031a2d 100644 --- a/PrepPipeline/examples/runVEPPrepPipeline.m +++ b/PrepPipeline/examples/runVEPPrepPipeline.m @@ -2,9 +2,11 @@ %% Set up the input and the output directories basename = 'vep'; -% Set these to your own folders before running. -indir = 'PATH_TO_INPUT_EEG_FILES'; % folder of EEGLAB .set files to process -outdir = 'PATH_TO_PREP_OUTPUT'; % folder for the PREP-processed files +% By default this runs on the three example recordings in examples/data and +% writes to examples/output (not tracked by git). Change these to use your own. +exampleDir = fileparts(mfilename('fullpath')); +indir = fullfile(exampleDir, 'data'); % folder of EEGLAB .set files to process +outdir = fullfile(exampleDir, 'output'); % folder for the PREP-processed files %% Make the output directory if needed if ~exist(outdir, 'dir') @@ -35,6 +37,9 @@ for k = 1:length(fileList) [~, thisName, ~] = fileparts(fileList{k}); EEG = pop_loadset(fileList{k}); + % .fdt files store 32-bit samples, and EEGLAB loads them as single unless + % its double-precision option is set. PREP needs double: convert here. + EEG.data = double(EEG.data); params.name = thisName; [EEG, params, computationTimes] = prepPipeline(EEG, params); fprintf('Computation times (seconds):\n %s\n', ... diff --git a/PrepPipeline/examples/runVEPPrepReport.m b/PrepPipeline/examples/runVEPPrepReport.m index 12e4720..d2fd85b 100644 --- a/PrepPipeline/examples/runVEPPrepReport.m +++ b/PrepPipeline/examples/runVEPPrepReport.m @@ -2,9 +2,14 @@ % and produces reports. %% Read in the file and set the necessary parameters -% Set these to your own folders before running. -dataDir = 'PATH_TO_PREP_OUTPUT'; % folder of PREP-processed .set files -summaryFolder = 'PATH_TO_REPORTS'; % folder for the summary and session reports +% By default this reports on the output of runVEPPrepPipeline in +% examples/output. Change these to use your own folders. +exampleDir = fileparts(mfilename('fullpath')); +dataDir = fullfile(exampleDir, 'output'); % PREP-processed .set files +summaryFolder = fullfile(exampleDir, 'output', 'reports'); % summary and session reports +if ~exist(summaryFolder, 'dir') + mkdir(summaryFolder); +end publishOn = true; %% Get the directory list @@ -29,8 +34,8 @@ continue; end sessionReportName = [theName '.pdf']; - fname = [dataDir filesep inNames{k}]; - load(fname, '-mat'); + EEG = pop_loadset('filename', inNames{k}, 'filepath', dataDir); + EEG.data = double(EEG.data); % PREP reporting needs double precision sessionFileName = [summaryFolder filesep sessionReportName]; consoleFID = 1; publishPrepReport(EEG, summaryFileName, sessionFileName, consoleFID, publishOn); diff --git a/README.md b/README.md index 429d5da..f8a73dc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22983431.svg)](https://doi.org/10.5281/zenodo.22983431) + EEG-Clean-Tools =============== @@ -6,7 +8,7 @@ find the user documentation at [https://vislab.github.io/EEG-Clean-Tools/](https://vislab.github.io/EEG-Clean-Tools/). **Note:** For convenience, EEGLABPlugin directory contains the latest released version of the -PREP that can be unzipped into your EEGLAB plugins directory. +PREP that can be unzipped into your EEGLAB plugins directory. ### Building the documentation The documentation source is in `docs/` (Sphinx, with MyST markdown). To build and @@ -69,10 +71,11 @@ the site at the same address. ### Citing the PREP pipeline The PREP pipeline is freely available under the GNU General Public License (see License below). -Please cite the following publication if using: -> Bigdely-Shamlo N, Mullen T, Kothe C, Su K-M and Robbins KA (2015) -> The PREP pipeline: standardized preprocessing for large-scale EEG analysis -> Front. Neuroinform. 9:16. doi: 10.3389/fninf.2015.00016 +Please cite the following publication if using: + +> Bigdely-Shamlo N, Mullen T, Kothe C, Su K-M and Robbins KA (2015)\ +> The PREP pipeline: standardized preprocessing for large-scale EEG analysis\ +> Front. Neuroinform. 9:16. doi: 10.3389/fninf.2015.00016 ### License The PREP pipeline is licensed under the GNU General Public License, version 2 or @@ -104,7 +107,7 @@ Matousek and Jeremy Cockfield worked on the interfaces for the EEGLAB plugin as well as associated visualization tools. Kay Robbins of UTSA is the lead developer and maintainer of PREP. -### Support: +### Support: This research was sponsored by the Army Research Laboratory and was accomplished under Cooperative Agreement Number W911NF-10-2-0022. The views and conclusions contained in this document/software are those of the authors and should not be interpreted From 9003d4519377602c2103483146f1575211cb6361 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sun, 27 Sep 2026 07:48:08 -0500 Subject: [PATCH 14/17] Address second Copilot review of PR 38 - docs/api.rst: list reporting.showPrepDefaults with the other defaults functions - docs/user_guide.md: report mode 'skipReport' (not 'skip'), matching pop_prepPipeline; fix the 'robust' literal, the publishOn heading, and the spelling of "embarrassingly" Co-Authored-By: Claude Opus 5.5 --- docs/api.rst | 2 ++ docs/user_guide.md | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 5fe2509..949a570 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -37,6 +37,8 @@ Defaults and version .. mat:autofunction:: utilities.outputPrepDefaults +.. mat:autofunction:: reporting.showPrepDefaults + .. mat:autofunction:: utilities.getPrepVersion Reporting diff --git a/docs/user_guide.md b/docs/user_guide.md index 0f68a42..6d66974 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -28,7 +28,7 @@ repository into the `plugins` directory of your EEGLAB installation. The zip hol `PrepPipeline` folder; restart EEGLAB and PREP appears under the Tools menu. ## Using parallel processing with PREP -The PREP pipeline can execute fairly slowly on headsets with a lot of channels. However, many of the steps are embarassingly parallel --- that is the PREP can perform operations separately on individual channels or individual windows. +The PREP pipeline can execute fairly slowly on headsets with a lot of channels. However, many of the steps are embarrassingly parallel --- that is the PREP can perform operations separately on individual channels or individual windows. If you have the MATLAB Parallel Processing Toolbox, you just need to make sure that it is enabled. The following screenshot of the MATLAB IDE shows the Parallel Processing Toolbox icon on the lower left of the status bar at the very bottom of the window. ![MATLAB IDE](_static/images/MATLABWorkspace.png) @@ -250,7 +250,7 @@ estimates in the RANSAC algorithm. Often the reference channels and the evaluati compute the robust reference. **`referenceType`**\ -The type of reference to be performed. By default, PREP uses ``robust'`, which computes an average reference with +The type of reference to be performed. By default, PREP uses `'robust'`, which computes an average reference with iterative detection and interpolation of bad channels. Other options include `'average'`, `'specific'`, and `'none'`. The `'average'` type removes the average of the reference channels with no interpolation, while `'specific'` removes the average of the specified channels with no interpolation. If you mean to run the standardized PREP pipeline, @@ -326,7 +326,7 @@ the data structure to contain all of the window information for later processing after running the reports. ### Reporting -PREP has an extensive report facility that can be used provided that your reporting level was `'verbose'`. The GUI version of the PREP pipeline (`pop_prepPipeline`) has options in the report GUI for you to select whether or not to run the report. If the report mode is `'normal'` (the default), then PREP runs the processing pipeline followed by the report, followed by the post processing. If the report mode is `'skip'`, then PREP runs the processing pipeline followed by the post processing. If the report mode is `'reportOnly'`, then PREP only runs the report and skips both the processing and the post processing. +PREP has an extensive report facility that can be used provided that your reporting level was `'verbose'`. The GUI version of the PREP pipeline (`pop_prepPipeline`) has options in the report GUI for you to select whether or not to run the report. If the report mode is `'normal'` (the default), then PREP runs the processing pipeline followed by the report, followed by the post processing. If the report mode is `'skipReport'`, then PREP runs the processing pipeline followed by the post processing. If the report mode is `'reportOnly'`, then PREP only runs the report and skips both the processing and the post processing. #### Calling sequence for reporting The `publishPrepReport` function takes an EEG structure that has been run through the PREP pipeline with @@ -356,5 +356,5 @@ The file name for the detailed PDF report that PREP produces. The name should in **`consoleFID`**\ An open file descriptor for writing reporting information. Usually, this is 1, indicating that output should be directed to the command window. Give an open file descriptor to another file to record the report in a log. -**`PublishOn`**\ +**`publishOn`**\ If `true` (the default) PREP produces a published PDF Report and an HTML summary. If `false`, the PREP runs reporting, but keeps the figures displayed and outputs the reporting information to the command window. This mode is useful for closer examination of the figures. From e57910fb89c7fd932a84b1c17098565a4c5e6ea6 Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Sun, 27 Sep 2026 08:19:19 -0500 Subject: [PATCH 15/17] Fence Claude's reads to the working directories - .claude/settings.json: permissions.blockReadsOutsideWorkingDirectories is true, so Claude's file tools refuse reads outside the working directories. Programs a session runs, such as matlab -batch, are not affected; to let Claude read EEGLAB's source, start with --add-dir. - Drop the git show, grep, head, tail, and cat allow rules: Claude Code already runs these read-only commands without a prompt, so the rules granted nothing. Co-Authored-By: Claude Opus 5.5 --- .claude/settings.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index d1c37d2..c0da300 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,6 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-settings.json", "permissions": { + "blockReadsOutsideWorkingDirectories": true, "allow": [ "Bash(python --version)", "Bash(unzip -l *)", @@ -12,15 +13,10 @@ "Bash(git log *)", "Bash(git diff)", "Bash(git diff *)", - "Bash(git show *)", "Bash(git branch)", "Bash(git ls-files *)", - "Bash(grep *)", "Bash(ls)", "Bash(ls *)", - "Bash(head *)", - "Bash(tail *)", - "Bash(cat *)", "Bash(wc *)" ], "ask": [ From 08c2f81be44e9ad89322840aff453c9bd2f3d53d Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Mon, 28 Sep 2026 04:37:43 -0500 Subject: [PATCH 16/17] Address third Copilot review of PR 38 - pyproject.toml: require setuptools>=61, the first release that supports PEP 621 [project] metadata - docs/user_guide.md: the default report mode 'normal' generates and publishes a report; show removeLineNoise, which fills in defaults, for line-noise removal; channelInformation, not channelInfo; describe the files publishPrepReport writes instead of a return value - docs/api.rst: list removeLineNoise Co-Authored-By: Claude Opus 5.5 --- docs/api.rst | 2 ++ docs/user_guide.md | 22 +++++++++++++--------- pyproject.toml | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 949a570..60f80f9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -20,6 +20,8 @@ Pipeline steps .. mat:autofunction:: utilities.removeTrend +.. mat:autofunction:: utilities.removeLineNoise + .. mat:autofunction:: utilities.cleanLineNoise .. mat:autofunction:: utilities.performReference diff --git a/docs/user_guide.md b/docs/user_guide.md index 6d66974..328cec6 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -74,9 +74,10 @@ such as EOG channels and mastoids. **[REPORT MENU]** You can choose to generate a report using the PREP report generation facility: ![PREP report menu](_static/images/PrepReportParameters.png)\ -By default, the PREP does not produce a report. If you generate choose to generate a report, you can choose to -publish it in PDF format. Otherwise, PREP displays the report on the command line. You can also choose to -return at a later step and generate the report only. +By default, PREP generates a report after processing (report mode `'normal'`) and publishes it in PDF format +(`publishOn` is true). If you turn publishing off, PREP displays the report on the command line instead. +Choose `'skipReport'` to run the pipeline without a report, or `'reportOnly'` to return at a later step and +generate the report only. **[POSTPROCESS MENU]** After running the PREP pipeline, you can perform additional processing steps: ![PREP post process menu](_static/images/PrepPostProcess.png) @@ -149,12 +150,14 @@ By default, PREP uses 0.02 seconds. This parameter is not used unless the `detre We use an iterative version of a method that estimates the amplitude and size of a deterministic sinusoid at a specified frequency embedded in locally white noise. The model is applied in sliding windows to adjust for non stationarity. The algorithm requires a rough guess of the frequencies to be removed. By default, PREP uses multiples of 60 Hz. If the data set was recorded in a place where 50 Hz alternating current is used, you will need to provide the `lineFrequencies` parameter. Sometimes unusual frequencies appear due to aliasing and other recording artifacts. For example, a frequency spike at 212 Hz might appear as an aliasing artifact in a signal recorded at 512 Hz (212 = 512 - 300). You might need to rerun with different frequencies if unusual spectral peaks are visible in the reports. #### Calling sequence for line noise removal -The `cleanLineNoise` function takes two structures in and produces two output structures. The `signal` structure +The `removeLineNoise` function takes two structures in and produces two output structures. It fills in the +defaults for any parameters you leave out, checks them, and then calls `cleanLineNoise`, which does the work +and requires the full parameter structure. The `signal` structure includes a `.data` field and an `.srate` field. The `signal` structure is compatible with an EEGLAB EEG structure, but does not rely on any of the other EEGLAB fields. The data field should be channels x frames. As with all functions in the pipeline, the algorithm parameters are passed in a structure. -> `[signal, lineNoiseOut] = cleanLineNoise(signal)`\ -> `[signal, lineNoiseOut] = cleanLineNoise(signal, lineNoiseIn)` +> `[signal, lineNoiseOut] = removeLineNoise(signal)`\ +> `[signal, lineNoiseOut] = removeLineNoise(signal, lineNoiseIn)` The output structure contains all of the input structure fields plus additional fields containing information on the tapers used to compute the spectral components and additional fields including a string representation @@ -272,7 +275,7 @@ or `'none'`. If you mean to run the standardized PREP pipeline, you don't need t A structure containing the channel locations in EEGLAB `chanlocs` format. By default, PREP uses the `signal.chanlocs` structure unless this field is used to over ride. PREP must have channel locations in order to work. -**`channelInfo`**\ +**`channelInformation`**\ A structure containing channel information in EEGLAB `chaninfo` format. By default, PREP uses the `signal.chaninfo` structure unless this field is used to over ride. PREP uses the nose direction for display purposes in the reports. @@ -335,8 +338,9 @@ report level of verbose. You need to furnish a summary directory name and a summ As with all functions in the pipeline, the algorithm parameters are passed in a structure. > `publishPrepReport(signal, summaryFilePath, sessionFilePath, consoleFID, publishOn);` -The output structure contains all of the input structure fields plus additional fields including a string representation -of the actual command used. +`publishPrepReport` returns nothing; its results are files. It appends a summary of this dataset to the HTML file +at `summaryFilePath`, whether or not `publishOn` is true, and when `publishOn` is true it publishes the detailed +report as a PDF at `sessionFilePath`. **Example:**\ The following produces an HTML-formatted summary report in the current directory and publishes a detailed report in the s1 sub directory. diff --git a/pyproject.toml b/pyproject.toml index d49964e..e805da3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ # PrepPipeline/utilities/getPrepVersion.m, and docs/conf.py reads it from there. [build-system] -requires = ["setuptools>=45", "wheel"] +requires = ["setuptools>=61", "wheel"] build-backend = "setuptools.build_meta" [project] From 1353c57a83ff21ae0108f13a92e782423fe92ffc Mon Sep 17 00:00:00 2001 From: Kay Robbins Date: Mon, 28 Sep 2026 08:48:53 -0500 Subject: [PATCH 17/17] Fix off-by-one in the robust reference iteration limit robustReference checked iterations > maxReferenceIterations before each pass, so the default limit of 4 allowed five passes and recorded actualReferenceIterations = 5. It now uses >=, so it performs at most maxReferenceIterations passes. Results change for recordings that never converge. CHANGELOG.md: record the fix under Unreleased, and drop the BLASST and license entries (the changelog records pipeline changes only). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 +-- PrepPipeline/utilities/robustReference.m | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e575e1c..6a86606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,7 @@ Release history of the PREP pipeline, newest first. The current version is also ## Unreleased -* Removed the unsupported BLASST line-noise option (`lineNoiseMethod` 'blasst') -* Added a root LICENSE (GPL-2.0-or-later) and a licensing table in README.md +* Fixed an off-by-one in robust referencing: it now performs at most `maxReferenceIterations` passes; it used to perform one more, so results change for recordings that never converge ## Version 0.57.0 - Released 3/31/2025 diff --git a/PrepPipeline/utilities/robustReference.m b/PrepPipeline/utilities/robustReference.m index 9c301b7..5693b61 100644 --- a/PrepPipeline/utilities/robustReference.m +++ b/PrepPipeline/utilities/robustReference.m @@ -66,7 +66,7 @@ if (iterations > 1 && (isempty(noisyChannels) ||... (isempty(setdiff(noisyChannels, noisyChannelsOld)) ... && isempty(setdiff(noisyChannelsOld, noisyChannels))))) || ... - iterations > referenceOut.maxReferenceIterations + iterations >= referenceOut.maxReferenceIterations break; end noisyChannelsOld = noisyChannels;

R<>I6v)s=J+SNDFC*3bQguf@?G(+q*w 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 literal 0 HcmV?d00001 diff --git a/docs/_static/images/PrepReferenceParameters.png b/docs/_static/images/PrepReferenceParameters.png new file mode 100644 index 0000000000000000000000000000000000000000..5c8db8b787df314ea1e0cf02453666721f407078 GIT binary patch literal 51537 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 L36%ZN@%=vlz^Ci1 literal 0 HcmV?d00001 diff --git a/docs/_static/images/PrepResampleParameters.png b/docs/_static/images/PrepResampleParameters.png new file mode 100644 index 0000000000000000000000000000000000000000..71f3f14cb985cc72f977cc5cbc6e253961d7d9a1 GIT binary patch literal 59245 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 z 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 +