Compare commits
11 Commits
831b251aea
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d4a7139fb5 | |||
|
|
f7de4421dd | ||
|
|
24019c18c0 | ||
|
|
05505d97dd | ||
| 15e1f8cd23 | |||
|
|
a114ad79ef | ||
|
|
0537f79e73 | ||
|
|
6eb8c2e266 | ||
| a28774d499 | |||
| 3e69dc0cfc | |||
| 084f615b47 |
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.mat filter=lfs diff=lfs merge=lfs -text
|
||||||
2
.gitignore
vendored
@@ -205,3 +205,5 @@ cython_debug/
|
|||||||
marimo/_static/
|
marimo/_static/
|
||||||
marimo/_lsp/
|
marimo/_lsp/
|
||||||
__marimo__/
|
__marimo__/
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
BIN
HW-3/HW_3_sp23_longitudinalTraining_Plasticity_ECE374N-385J_Neural_Eng/subj1.mat
LFS
Normal file
BIN
HW-3/HW_3_sp23_longitudinalTraining_Plasticity_ECE374N-385J_Neural_Eng/subj2.mat
LFS
Normal file
BIN
HW-3/HW_3_sp26_Plasticity_ECE374N-385J_Neural_Eng.pdf
Normal file
BIN
HW-3/ch32Locations.mat
LFS
Normal file
862
HW-3/readlocs.m
Normal file
@@ -0,0 +1,862 @@
|
|||||||
|
% readlocs() - read electrode location coordinates and other information from a file.
|
||||||
|
% Several standard file formats are supported. Users may also specify
|
||||||
|
% a custom column format. Defined format examples are given below
|
||||||
|
% (see File Formats).
|
||||||
|
% Usage:
|
||||||
|
% >> eloc = readlocs( filename );
|
||||||
|
% >> EEG.chanlocs = readlocs( filename, 'key', 'val', ... );
|
||||||
|
% >> [eloc, labels, theta, radius, indices] = ...
|
||||||
|
% readlocs( filename, 'key', 'val', ... );
|
||||||
|
% Inputs:
|
||||||
|
% filename - Name of the file containing the electrode locations
|
||||||
|
% {default: 2-D polar coordinates} (see >> help topoplot )
|
||||||
|
%
|
||||||
|
% Optional inputs:
|
||||||
|
% 'filetype' - ['loc'|'sph'|'sfp'|'xyz'|'asc'|'polhemus'|'besa'|'chanedit'|'custom']
|
||||||
|
% Type of the file to read. By default the file type is determined
|
||||||
|
% using the file extension (see below under File Formats),
|
||||||
|
% 'loc' an EEGLAB 2-D polar coordinates channel locations file
|
||||||
|
% Coordinates are theta and radius (see definitions below).
|
||||||
|
% 'sph' Matlab spherical coordinates (Note: spherical
|
||||||
|
% coordinates used by Matlab functions are different
|
||||||
|
% from spherical coordinates used by BESA - see below).
|
||||||
|
% 'sfp' EGI Cartesian coordinates (NOT Matlab Cartesian - see below).
|
||||||
|
% 'xyz' Matlab/EEGLAB Cartesian coordinates (NOT EGI Cartesian).
|
||||||
|
% z is toward nose; y is toward left ear; z is toward vertex
|
||||||
|
% 'asc' Neuroscan polar coordinates.
|
||||||
|
% 'polhemus' or 'polhemusx' - Polhemus electrode location file recorded
|
||||||
|
% with 'X' on sensor pointing to subject (see below and readelp()).
|
||||||
|
% 'polhemusy' - Polhemus electrode location file recorded with
|
||||||
|
% 'Y' on sensor pointing to subject (see below and readelp()).
|
||||||
|
% 'besa' BESA-'.elp' spherical coordinates. (Not MATLAB spherical -
|
||||||
|
% see below).
|
||||||
|
% 'chanedit' - EEGLAB channel location file created by pop_chanedit().
|
||||||
|
% 'custom' - Ascii file with columns in user-defined 'format' (see below).
|
||||||
|
% 'importmode' - ['eeglab'|'native'] for location files containing 3-D cartesian electrode
|
||||||
|
% coordinates, import either in EEGLAB format (nose pointing toward +X).
|
||||||
|
% This may not always be possible since EEGLAB might not be able to
|
||||||
|
% determine the nose direction for scanned electrode files. 'native' import
|
||||||
|
% original carthesian coordinates (user can then specify the position of
|
||||||
|
% the nose when calling the topoplot() function; in EEGLAB the position
|
||||||
|
% of the nose is stored in the EEG.chaninfo structure). {default 'eeglab'}
|
||||||
|
% 'format' - [cell array] Format of a 'custom' channel location file (see above).
|
||||||
|
% {default: if no file type is defined. The cell array contains
|
||||||
|
% labels defining the meaning of each column of the input file.
|
||||||
|
% 'channum' [positive integer] channel number.
|
||||||
|
% 'labels' [string] channel name (no spaces).
|
||||||
|
% 'theta' [real degrees] 2-D angle in polar coordinates.
|
||||||
|
% positive => rotating from nose (0) toward left ear
|
||||||
|
% 'radius' [real] radius for 2-D polar coords; 0.5 is the head
|
||||||
|
% disk radius and limit for topoplot() plotting).
|
||||||
|
% 'X' [real] Matlab-Cartesian X coordinate (to nose).
|
||||||
|
% 'Y' [real] Matlab-Cartesian Y coordinate (to left ear).
|
||||||
|
% 'Z' [real] Matlab-Cartesian Z coordinate (to vertex).
|
||||||
|
% '-X','-Y','-Z' Matlab-Cartesian coordinates pointing opposite
|
||||||
|
% to the above.
|
||||||
|
% 'sph_theta' [real degrees] Matlab spherical horizontal angle.
|
||||||
|
% positive => rotating from nose (0) toward left ear.
|
||||||
|
% 'sph_phi' [real degrees] Matlab spherical elevation angle.
|
||||||
|
% positive => rotating from horizontal (0) upwards.
|
||||||
|
% 'sph_radius' [real] distance from head center (unused).
|
||||||
|
% 'sph_phi_besa' [real degrees] BESA phi angle from vertical.
|
||||||
|
% positive => rotating from vertex (0) towards right ear.
|
||||||
|
% 'sph_theta_besa' [real degrees] BESA theta horiz/azimuthal angle.
|
||||||
|
% positive => rotating from right ear (0) toward nose.
|
||||||
|
% 'ignore' ignore column}.
|
||||||
|
% The input file may also contain other channel information fields.
|
||||||
|
% 'type' channel type: 'EEG', 'MEG', 'EMG', 'ECG', others ...
|
||||||
|
% 'calib' [real near 1.0] channel calibration value.
|
||||||
|
% 'gain' [real > 1] channel gain.
|
||||||
|
% 'custom1' custom field #1.
|
||||||
|
% 'custom2', 'custom3', 'custom4', etc. more custom fields
|
||||||
|
% 'skiplines' - [integer] Number of header lines to skip (in 'custom' file types only).
|
||||||
|
% Note: Characters on a line following '%' will be treated as comments.
|
||||||
|
% 'readchans' - [integer array] indices of electrodes to read. {default: all}
|
||||||
|
% 'center' - [(1,3) real array or 'auto'] center of xyz coordinates for conversion
|
||||||
|
% to spherical or polar, Specify the center of the sphere here, or 'auto'.
|
||||||
|
% This uses the center of the sphere that best fits all the electrode
|
||||||
|
% locations read. {default: [0 0 0]}
|
||||||
|
% Outputs:
|
||||||
|
% eloc - structure containing the channel names and locations (if present).
|
||||||
|
% It has three fields: 'eloc.labels', 'eloc.theta' and 'eloc.radius'
|
||||||
|
% identical in meaning to the EEGLAB struct 'EEG.chanlocs'.
|
||||||
|
% labels - cell array of strings giving the names of the electrodes. NOTE: Unlike the
|
||||||
|
% three outputs below, includes labels of channels *without* location info.
|
||||||
|
% theta - vector (in degrees) of polar angles of the electrode locations.
|
||||||
|
% radius - vector of polar-coordinate radii (arc_lengths) of the electrode locations
|
||||||
|
% indices - indices, k, of channels with non-empty 'locs(k).theta' coordinate
|
||||||
|
%
|
||||||
|
% File formats:
|
||||||
|
% If 'filetype' is unspecified, the file extension determines its type.
|
||||||
|
%
|
||||||
|
% '.loc' or '.locs' or '.eloc':
|
||||||
|
% polar coordinates. Notes: angles in degrees:
|
||||||
|
% right ear is 90; left ear -90; head disk radius is 0.5.
|
||||||
|
% Fields: N angle radius label
|
||||||
|
% Sample: 1 -18 .511 Fp1
|
||||||
|
% 2 18 .511 Fp2
|
||||||
|
% 3 -90 .256 C3
|
||||||
|
% 4 90 .256 C4
|
||||||
|
% ...
|
||||||
|
% Note: In previous releases, channel labels had to contain exactly
|
||||||
|
% four characters (spaces replaced by '.'). This format still works,
|
||||||
|
% though dots are no longer required.
|
||||||
|
% '.sph':
|
||||||
|
% Matlab spherical coordinates. Notes: theta is the azimuthal/horizontal angle
|
||||||
|
% in deg.: 0 is toward nose, 90 rotated to left ear. Following this, performs
|
||||||
|
% the elevation (phi). Angles in degrees.
|
||||||
|
% Fields: N theta phi label
|
||||||
|
% Sample: 1 18 -2 Fp1
|
||||||
|
% 2 -18 -2 Fp2
|
||||||
|
% 3 90 44 C3
|
||||||
|
% 4 -90 44 C4
|
||||||
|
% ...
|
||||||
|
% '.elc':
|
||||||
|
% Cartesian 3-D electrode coordinates scanned using the EETrak software.
|
||||||
|
% See readeetraklocs().
|
||||||
|
% '.elp':
|
||||||
|
% Polhemus-.'elp' Cartesian coordinates. By default, an .elp extension is read
|
||||||
|
% as PolhemusX-elp in which 'X' on the Polhemus sensor is pointed toward the
|
||||||
|
% subject. Polhemus files are not in columnar format (see readelp()).
|
||||||
|
% '.elp':
|
||||||
|
% BESA-'.elp' spherical coordinates: Need to specify 'filetype','besa'.
|
||||||
|
% The elevation angle (phi) is measured from the vertical axis. Positive
|
||||||
|
% rotation is toward right ear. Next, perform azimuthal/horizontal rotation
|
||||||
|
% (theta): 0 is toward right ear; 90 is toward nose, -90 toward occiput.
|
||||||
|
% Angles are in degrees. If labels are absent or weights are given in
|
||||||
|
% a last column, readlocs() adjusts for this. Default labels are E1, E2, ...
|
||||||
|
% Fields: label phi theta
|
||||||
|
% Sample: Fp1 -92 -72
|
||||||
|
% Fp2 92 72
|
||||||
|
% C3 -46 0
|
||||||
|
% C4 46 0
|
||||||
|
% ...
|
||||||
|
% '.xyz':
|
||||||
|
% Matlab/EEGLAB Cartesian coordinates. Here. x is towards the nose,
|
||||||
|
% y is towards the left ear, and z towards the vertex. Note that the first
|
||||||
|
% column (x) is -Y in a Matlab 3-D plot, the second column (y) is X in a
|
||||||
|
% matlab 3-D plot, and the third column (z) is Z.
|
||||||
|
% Fields: channum x y z label
|
||||||
|
% Sample: 1 .950 .308 -.035 Fp1
|
||||||
|
% 2 .950 -.308 -.035 Fp2
|
||||||
|
% 3 0 .719 .695 C3
|
||||||
|
% 4 0 -.719 .695 C4
|
||||||
|
% ...
|
||||||
|
% '.asc', '.dat':
|
||||||
|
% Neuroscan-.'asc' or '.dat' Cartesian polar coordinates text file.
|
||||||
|
% '.sfp':
|
||||||
|
% BESA/EGI-xyz Cartesian coordinates. Notes: For EGI, x is toward right ear,
|
||||||
|
% y is toward the nose, z is toward the vertex. EEGLAB converts EGI
|
||||||
|
% Cartesian coordinates to Matlab/EEGLAB xyz coordinates.
|
||||||
|
% Fields: label x y z
|
||||||
|
% Sample: Fp1 -.308 .950 -.035
|
||||||
|
% Fp2 .308 .950 -.035
|
||||||
|
% C3 -.719 0 .695
|
||||||
|
% C4 .719 0 .695
|
||||||
|
% ...
|
||||||
|
% '.ced':
|
||||||
|
% ASCII file saved by pop_chanedit(). Contains multiple MATLAB/EEGLAB formats.
|
||||||
|
% Cartesian coordinates are as in the 'xyz' format (above).
|
||||||
|
% Fields: channum label theta radius x y z sph_theta sph_phi ...
|
||||||
|
% Sample: 1 Fp1 -18 .511 .950 .308 -.035 18 -2 ...
|
||||||
|
% 2 Fp2 18 .511 .950 -.308 -.035 -18 -2 ...
|
||||||
|
% 3 C3 -90 .256 0 .719 .695 90 44 ...
|
||||||
|
% 4 C4 90 .256 0 -.719 .695 -90 44 ...
|
||||||
|
% ...
|
||||||
|
% The last columns of the file may contain any other defined fields (gain,
|
||||||
|
% calib, type, custom).
|
||||||
|
%
|
||||||
|
% Author: Arnaud Delorme, Salk Institute, 8 Dec 2002 (expanded from the previous EEG/ICA
|
||||||
|
% toolbox function)
|
||||||
|
%
|
||||||
|
% See also: readelp(), writelocs(), topo2sph(), sph2topo(), sph2cart()
|
||||||
|
|
||||||
|
%123456789012345678901234567890123456789012345678901234567890123456789012
|
||||||
|
|
||||||
|
% Copyright (C) Arnaud Delorme, CNL / Salk Institute, 28 Feb 2002
|
||||||
|
%
|
||||||
|
% 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
|
||||||
|
|
||||||
|
% $Log: readlocs.m,v $
|
||||||
|
% Revision 1.102 2009/09/27 05:14:55 arno
|
||||||
|
% Fix rereading CED file
|
||||||
|
%
|
||||||
|
% Revision 1.101 2009/05/12 18:15:40 arno
|
||||||
|
% nasion list
|
||||||
|
%
|
||||||
|
% Revision 1.100 2007/08/23 23:25:38 arno
|
||||||
|
% encode type for neuroscan channel electrode files
|
||||||
|
%
|
||||||
|
% Revision 1.99 2007/08/15 22:28:10 arno
|
||||||
|
% changing SFP not to skip any lines
|
||||||
|
%
|
||||||
|
% Revision 1.98 2007/05/22 13:56:50 arno
|
||||||
|
% type for custom sphbesa
|
||||||
|
%
|
||||||
|
% Revision 1.97 2007/05/01 21:35:58 arno
|
||||||
|
% fix typo
|
||||||
|
%
|
||||||
|
% Revision 1.96 2007/03/22 23:22:07 toby
|
||||||
|
% help2html
|
||||||
|
%
|
||||||
|
% Revision 1.95 2007/03/22 23:17:28 toby
|
||||||
|
% for help2html
|
||||||
|
%
|
||||||
|
% Revision 1.94 2007/03/22 23:09:45 toby
|
||||||
|
% edit to accomodate help2html
|
||||||
|
%
|
||||||
|
% Revision 1.93 2007/03/22 21:22:34 arno
|
||||||
|
% same
|
||||||
|
%
|
||||||
|
% Revision 1.92 2007/03/22 21:18:47 arno
|
||||||
|
% indices of non empty channels
|
||||||
|
%
|
||||||
|
% Revision 1.91 2007/02/05 16:18:43 arno
|
||||||
|
% reading channel types for .ced format
|
||||||
|
%
|
||||||
|
% Revision 1.90 2006/11/09 20:46:51 arno
|
||||||
|
% fixing readling .elp files
|
||||||
|
%
|
||||||
|
% Revision 1.89 2006/11/07 02:31:21 arno
|
||||||
|
% better documentation for .xyz
|
||||||
|
%
|
||||||
|
% Revision 1.88 2006/11/06 22:15:46 arno
|
||||||
|
% loading besa format
|
||||||
|
%
|
||||||
|
% Revision 1.87 2006/06/01 17:40:50 arno
|
||||||
|
% updating header
|
||||||
|
%
|
||||||
|
% Revision 1.86 2006/05/26 15:59:35 scott
|
||||||
|
% worked on text of instructions for adding a channel type -- NOTE: chaninfo is
|
||||||
|
% discussed in help message, but NOT implemented!?
|
||||||
|
%
|
||||||
|
% Revision 1.85 2006/04/14 21:19:08 arno
|
||||||
|
% fixing skipping lines
|
||||||
|
%
|
||||||
|
% Revision 1.84 2006/03/31 03:11:13 toby
|
||||||
|
% made '.eloc' equivalent to '.loc' as a filetype
|
||||||
|
%
|
||||||
|
% Revision 1.83 2006/02/14 00:01:18 arno
|
||||||
|
% change xyz format
|
||||||
|
%
|
||||||
|
% Revision 1.82 2006/01/20 22:37:08 arno
|
||||||
|
% default for BESA and polhemus
|
||||||
|
%
|
||||||
|
% Revision 1.81 2006/01/12 23:22:39 arno
|
||||||
|
% fixing indices
|
||||||
|
%
|
||||||
|
% Revision 1.80 2006/01/12 22:03:51 arno
|
||||||
|
% fiducial type
|
||||||
|
%
|
||||||
|
% Revision 1.79 2006/01/10 22:56:17 arno
|
||||||
|
% adding defaultelp option
|
||||||
|
%
|
||||||
|
% Revision 1.78 2006/01/10 22:53:49 arno
|
||||||
|
% [6~[6~changing default besa format
|
||||||
|
%
|
||||||
|
% Revision 1.77 2005/11/30 18:31:40 arno
|
||||||
|
% same
|
||||||
|
%
|
||||||
|
% Revision 1.76 2005/11/30 18:29:48 arno
|
||||||
|
% same
|
||||||
|
%
|
||||||
|
% Revision 1.75 2005/11/30 18:28:37 arno
|
||||||
|
% reformat outputs
|
||||||
|
%
|
||||||
|
% Revision 1.74 2005/10/29 03:49:50 scott
|
||||||
|
% NOTE: there is no mention of 'chantype' - should at least add a help mention after line 69 -sm
|
||||||
|
%
|
||||||
|
% Revision 1.73 2005/09/27 22:08:41 arno
|
||||||
|
% fixing reading .ced files
|
||||||
|
%
|
||||||
|
% Revision 1.72 2005/05/24 17:07:05 arno
|
||||||
|
% cell2mat - celltomat
|
||||||
|
%
|
||||||
|
% Revision 1.71 2005/03/10 17:42:11 arno
|
||||||
|
% new format for channel location info
|
||||||
|
%
|
||||||
|
% Revision 1.70 2005/03/08 23:19:24 arno
|
||||||
|
% using old function to read asa format
|
||||||
|
%
|
||||||
|
% Revision 1.69 2005/03/04 23:17:22 arno
|
||||||
|
% use fieldtrip readeetrack
|
||||||
|
%
|
||||||
|
% Revision 1.65 2004/10/27 01:01:05 arno
|
||||||
|
% msg format
|
||||||
|
%
|
||||||
|
% Revision 1.64 2004/03/23 00:37:56 scott
|
||||||
|
% clarifying help msg re meaning of 'indices' output
|
||||||
|
%
|
||||||
|
% Revision 1.63 2004/03/23 00:22:51 scott
|
||||||
|
% clarified meaning of output 'indices'
|
||||||
|
%
|
||||||
|
% Revision 1.62 2004/02/24 17:17:32 arno
|
||||||
|
% dbug message
|
||||||
|
%
|
||||||
|
% Revision 1.61 2004/01/01 19:12:08 scott
|
||||||
|
% help message edits
|
||||||
|
%
|
||||||
|
% Revision 1.60 2004/01/01 18:57:26 scott
|
||||||
|
% edit text outputs
|
||||||
|
%
|
||||||
|
% Revision 1.59 2004/01/01 01:47:34 scott
|
||||||
|
% franglais -> anglais
|
||||||
|
%
|
||||||
|
% Revision 1.58 2003/12/17 00:55:07 arno
|
||||||
|
% debug last
|
||||||
|
%
|
||||||
|
% Revision 1.57 2003/12/17 00:50:10 arno
|
||||||
|
% adding index for non-empty electrodes
|
||||||
|
%
|
||||||
|
% Revision 1.56 2003/12/05 18:37:56 arno
|
||||||
|
% debug polhemus x and y fixed
|
||||||
|
%
|
||||||
|
% Revision 1.55 2003/12/02 03:21:39 arno
|
||||||
|
% neuroscan format
|
||||||
|
%
|
||||||
|
% Revision 1.54 2003/11/27 00:38:13 arno
|
||||||
|
% conversion elc
|
||||||
|
%
|
||||||
|
% Revision 1.53 2003/11/27 00:31:30 arno
|
||||||
|
% debuging elc format
|
||||||
|
%
|
||||||
|
% Revision 1.52 2003/11/27 00:25:51 arno
|
||||||
|
% automatically detecting elc files
|
||||||
|
%
|
||||||
|
% Revision 1.51 2003/11/05 17:20:23 arno
|
||||||
|
% first convert spherical instead of carthesian
|
||||||
|
%
|
||||||
|
% Revision 1.50 2003/09/18 00:07:05 arno
|
||||||
|
% further checks for neuroscan
|
||||||
|
%
|
||||||
|
% Revision 1.49 2003/07/16 18:52:21 arno
|
||||||
|
% allowing file type locs
|
||||||
|
%
|
||||||
|
% Revision 1.48 2003/06/30 15:00:43 arno
|
||||||
|
% fixing inputcheck problem
|
||||||
|
%
|
||||||
|
% Revision 1.47 2003/05/13 23:31:25 arno
|
||||||
|
% number of lines to skip in chanedit format
|
||||||
|
%
|
||||||
|
% Revision 1.46 2003/05/13 22:09:01 arno
|
||||||
|
% updating sph format
|
||||||
|
%
|
||||||
|
% Revision 1.45 2003/05/13 22:07:07 arno
|
||||||
|
% removing labels in sfp format
|
||||||
|
%
|
||||||
|
% Revision 1.44 2003/05/13 21:14:11 arno
|
||||||
|
% only write a subset of file format
|
||||||
|
%
|
||||||
|
% Revision 1.43 2003/03/10 16:28:12 arno
|
||||||
|
% removing help for elc
|
||||||
|
%
|
||||||
|
% Revision 1.42 2003/03/10 16:26:59 arno
|
||||||
|
% adding then removing .elc format
|
||||||
|
%
|
||||||
|
% Revision 1.41 2003/03/08 17:36:13 arno
|
||||||
|
% import spherical EGI files correctly
|
||||||
|
%
|
||||||
|
% Revision 1.40 2003/03/05 15:38:15 arno
|
||||||
|
% fixing '.' bug
|
||||||
|
%
|
||||||
|
% Revision 1.39 2003/03/04 20:04:44 arno
|
||||||
|
% adding neuroscan .asc format
|
||||||
|
%
|
||||||
|
% Revision 1.38 2003/01/30 16:45:12 arno
|
||||||
|
% debugging ced format
|
||||||
|
%
|
||||||
|
% Revision 1.37 2003/01/10 17:40:11 arno
|
||||||
|
% removing trailing dots
|
||||||
|
%
|
||||||
|
% Revision 1.36 2003/01/03 22:47:00 arno
|
||||||
|
% typo in warning messages
|
||||||
|
%
|
||||||
|
% Revision 1.35 2003/01/03 22:45:48 arno
|
||||||
|
% adding another warning message
|
||||||
|
%
|
||||||
|
% Revision 1.34 2003/01/03 22:41:38 arno
|
||||||
|
% autodetect format .sfp
|
||||||
|
%
|
||||||
|
% Revision 1.33 2003/01/03 22:38:39 arno
|
||||||
|
% adding warning message
|
||||||
|
%
|
||||||
|
% Revision 1.32 2002/12/29 23:04:00 scott
|
||||||
|
% header
|
||||||
|
%
|
||||||
|
% Revision 1.31 2002/12/29 22:37:15 arno
|
||||||
|
% txt -> ced
|
||||||
|
%
|
||||||
|
% Revision 1.30 2002/12/29 22:35:35 arno
|
||||||
|
% adding coords. info for file format in header, programming .sph, ...
|
||||||
|
%
|
||||||
|
% Revision 1.29 2002/12/29 22:00:10 arno
|
||||||
|
% skipline -> skiplines
|
||||||
|
%
|
||||||
|
% Revision 1.28 2002/12/28 23:46:45 scott
|
||||||
|
% header
|
||||||
|
%
|
||||||
|
% Revision 1.27 2002/12/28 02:02:35 scott
|
||||||
|
% header details
|
||||||
|
%
|
||||||
|
% Revision 1.26 2002/12/28 01:32:41 scott
|
||||||
|
% worked on header information - axis details etcetc. -sm & ad
|
||||||
|
%
|
||||||
|
% Revision 1.25 2002/12/27 23:23:35 scott
|
||||||
|
% edit header msg - NEEDS MORE DETAILS -sm
|
||||||
|
%
|
||||||
|
% Revision 1.24 2002/12/27 22:57:23 arno
|
||||||
|
% debugging polhemus
|
||||||
|
%
|
||||||
|
% Revision 1.23 2002/12/27 17:47:32 arno
|
||||||
|
% compatible with more BESA formats
|
||||||
|
%
|
||||||
|
% Revision 1.22 2002/12/26 16:41:23 arno
|
||||||
|
% new release
|
||||||
|
%
|
||||||
|
% Revision 1.21 2002/12/24 02:51:22 arno
|
||||||
|
% new version of readlocs
|
||||||
|
%
|
||||||
|
|
||||||
|
|
||||||
|
function [eloc, labels, theta, radius, indices] = readlocs( filename, varargin );
|
||||||
|
|
||||||
|
if nargin < 1
|
||||||
|
help readlocs;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
|
||||||
|
% NOTE: To add a new channel format:
|
||||||
|
% ----------------------------------
|
||||||
|
% 1) Add a new element to the structure 'chanformat' (see 'ADD NEW FORMATS HERE' below):
|
||||||
|
% 2) Enter a format 'type' for the new file format,
|
||||||
|
% 3) Enter a (short) 'typestring' description of the format
|
||||||
|
% 4) Enter a longer format 'description' (possibly multiline, see ex. (1) below)
|
||||||
|
% 5) Enter format file column labels in the 'importformat' field (see ex. (2) below)
|
||||||
|
% 6) Enter the number of header lines to skip (if any) in the 'skipline' field
|
||||||
|
% 7) Document the new channel format in the help message above.
|
||||||
|
% 8) After testing, please send the new version of readloca.m to us
|
||||||
|
% at eeglab@sccn.ucsd.edu with a sample locs file.
|
||||||
|
% The 'chanformat' structure is also used (automatically) by the writelocs()
|
||||||
|
% and pop_readlocs() functions. You do not need to edit these functions.
|
||||||
|
|
||||||
|
chanformat(1).type = 'polhemus';
|
||||||
|
chanformat(1).typestring = 'Polhemus native .elp file';
|
||||||
|
chanformat(1).description = [ 'Polhemus native coordinate file containing scanned electrode positions. ' ...
|
||||||
|
'User must select the direction ' ...
|
||||||
|
'for the nose after importing the data file.' ];
|
||||||
|
chanformat(1).importformat = 'readelp() function';
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(2).type = 'besa';
|
||||||
|
chanformat(2).typestring = 'BESA spherical .elp file';
|
||||||
|
chanformat(2).description = [ 'BESA spherical coordinate file. Note that BESA spherical coordinates ' ...
|
||||||
|
'are different from Matlab spherical coordinates' ];
|
||||||
|
chanformat(2).skipline = 0; % some BESA files do not have headers
|
||||||
|
chanformat(2).importformat = { 'type' 'labels' 'sph_theta_besa' 'sph_phi_besa' 'sph_radius' };
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(3).type = 'xyz';
|
||||||
|
chanformat(3).typestring = 'Matlab .xyz file';
|
||||||
|
chanformat(3).description = [ 'Standard 3-D cartesian coordinate files with electrode labels in ' ...
|
||||||
|
'the first column and X, Y, and Z coordinates in columns 2, 3, and 4' ];
|
||||||
|
chanformat(3).importformat = { 'channum' '-Y' 'X' 'Z' 'labels'};
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(4).type = 'sfp';
|
||||||
|
chanformat(4).typestring = 'BESA or EGI 3-D cartesian .sfp file';
|
||||||
|
chanformat(4).description = [ 'Standard BESA 3-D cartesian coordinate files with electrode labels in ' ...
|
||||||
|
'the first column and X, Y, and Z coordinates in columns 2, 3, and 4.' ...
|
||||||
|
'Coordinates are re-oriented to fit the EEGLAB standard of having the ' ...
|
||||||
|
'nose along the +X axis.' ];
|
||||||
|
chanformat(4).importformat = { 'labels' '-Y' 'X' 'Z' };
|
||||||
|
chanformat(4).skipline = 0;
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(5).type = 'loc';
|
||||||
|
chanformat(5).typestring = 'EEGLAB polar .loc file';
|
||||||
|
chanformat(5).description = [ 'EEGLAB polar .loc file' ];
|
||||||
|
chanformat(5).importformat = { 'channum' 'theta' 'radius' 'labels' };
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(6).type = 'sph';
|
||||||
|
chanformat(6).typestring = 'Matlab .sph spherical file';
|
||||||
|
chanformat(6).description = [ 'Standard 3-D spherical coordinate files in Matlab format' ];
|
||||||
|
chanformat(6).importformat = { 'channum' 'sph_theta' 'sph_phi' 'labels' };
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(7).type = 'asc';
|
||||||
|
chanformat(7).typestring = 'Neuroscan polar .asc file';
|
||||||
|
chanformat(7).description = [ 'Neuroscan polar .asc file, automatically recentered to fit EEGLAB standard' ...
|
||||||
|
'of having ''Cz'' at (0,0).' ];
|
||||||
|
chanformat(7).importformat = 'readneurolocs';
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(8).type = 'dat';
|
||||||
|
chanformat(8).typestring = 'Neuroscan 3-D .dat file';
|
||||||
|
chanformat(8).description = [ 'Neuroscan 3-D cartesian .dat file. Coordinates are re-oriented to fit ' ...
|
||||||
|
'the EEGLAB standard of having the nose along the +X axis.' ];
|
||||||
|
chanformat(8).importformat = 'readneurolocs';
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(9).type = 'elc';
|
||||||
|
chanformat(9).typestring = 'ASA .elc 3-D file';
|
||||||
|
chanformat(9).description = [ 'ASA .elc 3-D coordinate file containing scanned electrode positions. ' ...
|
||||||
|
'User must select the direction ' ...
|
||||||
|
'for the nose after importing the data file.' ];
|
||||||
|
chanformat(9).importformat = 'readeetraklocs';
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(10).type = 'chanedit';
|
||||||
|
chanformat(10).typestring = 'EEGLAB complete 3-D file';
|
||||||
|
chanformat(10).description = [ 'EEGLAB file containing polar, cartesian 3-D, and spherical 3-D ' ...
|
||||||
|
'electrode locations.' ];
|
||||||
|
chanformat(10).importformat = { 'channum' 'labels' 'theta' 'radius' 'X' 'Y' 'Z' 'sph_theta' 'sph_phi' ...
|
||||||
|
'sph_radius' 'type' };
|
||||||
|
chanformat(10).skipline = 1;
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
chanformat(11).type = 'custom';
|
||||||
|
chanformat(11).typestring = 'Custom file format';
|
||||||
|
chanformat(11).description = 'Custom ASCII file format where user can define content for each file columns.';
|
||||||
|
chanformat(11).importformat = '';
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
% ----- ADD MORE FORMATS HERE -----------------------------------------------------------------------
|
||||||
|
% ---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
listcolformat = { 'labels' 'channum' 'theta' 'radius' 'sph_theta' 'sph_phi' ...
|
||||||
|
'sph_radius' 'sph_theta_besa' 'sph_phi_besa' 'gain' 'calib' 'type' ...
|
||||||
|
'X' 'Y' 'Z' '-X' '-Y' '-Z' 'custom1' 'custom2' 'custom3' 'custom4' 'ignore' 'not def' };
|
||||||
|
|
||||||
|
% ----------------------------------
|
||||||
|
% special mode for getting the info
|
||||||
|
% ----------------------------------
|
||||||
|
if isstr(filename) & strcmp(filename, 'getinfos')
|
||||||
|
eloc = chanformat;
|
||||||
|
labels = listcolformat;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
|
||||||
|
g = finputcheck( varargin, ...
|
||||||
|
{ 'filetype' 'string' {} '';
|
||||||
|
'importmode' 'string' { 'eeglab' 'native' } 'eeglab';
|
||||||
|
'defaultelp' 'string' { 'besa' 'polhemus' } 'polhemus';
|
||||||
|
'skiplines' 'integer' [0 Inf] [];
|
||||||
|
'elecind' 'integer' [1 Inf] [];
|
||||||
|
'format' 'cell' [] {} }, 'readlocs');
|
||||||
|
if isstr(g), error(g); end;
|
||||||
|
|
||||||
|
if isstr(filename)
|
||||||
|
|
||||||
|
% format auto detection
|
||||||
|
% --------------------
|
||||||
|
if strcmpi(g.filetype, 'autodetect'), g.filetype = ''; end;
|
||||||
|
g.filetype = strtok(g.filetype);
|
||||||
|
periods = find(filename == '.');
|
||||||
|
fileextension = filename(periods(end)+1:end);
|
||||||
|
g.filetype = lower(g.filetype);
|
||||||
|
if isempty(g.filetype)
|
||||||
|
switch lower(fileextension),
|
||||||
|
case {'loc' 'locs' }, g.filetype = 'loc';
|
||||||
|
case 'xyz', g.filetype = 'xyz';
|
||||||
|
fprintf( [ 'WARNING: Matlab Cartesian coord. file extension (".xyz") detected.\n' ...
|
||||||
|
'If importing EGI Cartesian coords, force type "sfp" instead.\n'] );
|
||||||
|
case 'sph', g.filetype = 'sph';
|
||||||
|
case 'ced', g.filetype = 'chanedit';
|
||||||
|
case 'elp', g.filetype = g.defaultelp;
|
||||||
|
case 'asc', g.filetype = 'asc';
|
||||||
|
case 'dat', g.filetype = 'dat';
|
||||||
|
case 'elc', g.filetype = 'elc';
|
||||||
|
case 'eps', g.filetype = 'besa';
|
||||||
|
case 'sfp', g.filetype = 'sfp';
|
||||||
|
otherwise, g.filetype = '';
|
||||||
|
end;
|
||||||
|
fprintf('readlocs(): ''%s'' format assumed from file extension\n', g.filetype);
|
||||||
|
else
|
||||||
|
if strcmpi(g.filetype, 'locs'), g.filetype = 'loc'; end
|
||||||
|
if strcmpi(g.filetype, 'eloc'), g.filetype = 'loc'; end
|
||||||
|
end;
|
||||||
|
|
||||||
|
% assign format from filetype
|
||||||
|
% ---------------------------
|
||||||
|
if ~isempty(g.filetype) & ~strcmpi(g.filetype, 'custom') ...
|
||||||
|
& ~strcmpi(g.filetype, 'asc') & ~strcmpi(g.filetype, 'elc') & ~strcmpi(g.filetype, 'dat')
|
||||||
|
indexformat = strmatch(lower(g.filetype), { chanformat.type }, 'exact');
|
||||||
|
g.format = chanformat(indexformat).importformat;
|
||||||
|
if isempty(g.skiplines)
|
||||||
|
g.skiplines = chanformat(indexformat).skipline;
|
||||||
|
end;
|
||||||
|
if isempty(g.filetype)
|
||||||
|
error( ['readlocs() error: The filetype cannot be detected from the \n' ...
|
||||||
|
' file extension, and custom format not specified']);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
% import file
|
||||||
|
% -----------
|
||||||
|
if strcmp(g.filetype, 'asc') | strcmp(g.filetype, 'dat')
|
||||||
|
eloc = readneurolocs( filename );
|
||||||
|
eloc = rmfield(eloc, 'sph_theta'); % for the conversion below
|
||||||
|
eloc = rmfield(eloc, 'sph_theta_besa'); % for the conversion below
|
||||||
|
if isfield(eloc, 'type')
|
||||||
|
for index = 1:length(eloc)
|
||||||
|
type = eloc(index).type;
|
||||||
|
if type == 69, eloc(index).type = 'EEG';
|
||||||
|
elseif type == 88, eloc(index).type = 'REF';
|
||||||
|
elseif type >= 76 & type <= 82, eloc(index).type = 'FID';
|
||||||
|
else eloc(index).type = num2str(eloc(index).type);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
elseif strcmp(g.filetype, 'elc')
|
||||||
|
eloc = readeetraklocs( filename );
|
||||||
|
%eloc = read_asa_elc( filename ); % from fieldtrip
|
||||||
|
%eloc = struct('labels', eloc.label, 'X', mattocell(eloc.pnt(:,1)'), 'Y', ...
|
||||||
|
% mattocell(eloc.pnt(:,2)'), 'Z', mattocell(eloc.pnt(:,3)'));
|
||||||
|
eloc = convertlocs(eloc, 'cart2all');
|
||||||
|
eloc = rmfield(eloc, 'sph_theta'); % for the conversion below
|
||||||
|
eloc = rmfield(eloc, 'sph_theta_besa'); % for the conversion below
|
||||||
|
elseif strcmp(lower(g.filetype(1:end-1)), 'polhemus') | ...
|
||||||
|
strcmp(g.filetype, 'polhemus')
|
||||||
|
try,
|
||||||
|
[eloc labels X Y Z]= readelp( filename );
|
||||||
|
if strcmp(g.filetype, 'polhemusy')
|
||||||
|
tmp = X; X = Y; Y = tmp;
|
||||||
|
end;
|
||||||
|
for index = 1:length( eloc )
|
||||||
|
eloc(index).X = X(index);
|
||||||
|
eloc(index).Y = Y(index);
|
||||||
|
eloc(index).Z = Z(index);
|
||||||
|
end;
|
||||||
|
catch,
|
||||||
|
disp('readlocs(): Could not read Polhemus coords. Trying to read BESA .elp file.');
|
||||||
|
[eloc, labels, theta, radius, indices] = readlocs( filename, 'defaultelp', 'besa', varargin{:} );
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
% importing file
|
||||||
|
% --------------
|
||||||
|
if isempty(g.skiplines), g.skiplines = 0; end;
|
||||||
|
if strcmpi(g.filetype, 'chanedit')
|
||||||
|
array = loadtxt( filename, 'delim', 9, 'skipline', g.skiplines);
|
||||||
|
else
|
||||||
|
array = load_file_or_array( filename, g.skiplines);
|
||||||
|
end;
|
||||||
|
if size(array,2) < length(g.format)
|
||||||
|
fprintf(['readlocs() warning: Fewer columns in the input than expected.\n' ...
|
||||||
|
' See >> help readlocs\n']);
|
||||||
|
elseif size(array,2) > length(g.format)
|
||||||
|
fprintf(['readlocs() warning: More columns in the input than expected.\n' ...
|
||||||
|
' See >> help readlocs\n']);
|
||||||
|
end;
|
||||||
|
|
||||||
|
% removing lines BESA
|
||||||
|
% -------------------
|
||||||
|
if isempty(array{1,2})
|
||||||
|
disp('BESA header detected, skipping three lines...');
|
||||||
|
array = load_file_or_array( filename, g.skiplines-1);
|
||||||
|
if isempty(array{1,2})
|
||||||
|
array = load_file_or_array( filename, g.skiplines-1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
% removing comments and empty lines
|
||||||
|
% ---------------------------------
|
||||||
|
indexbeg = 1;
|
||||||
|
while isempty(array{indexbeg,1}) | ...
|
||||||
|
(isstr(array{indexbeg,1}) & array{indexbeg,1}(1) == '%' )
|
||||||
|
indexbeg = indexbeg+1;
|
||||||
|
end;
|
||||||
|
array = array(indexbeg:end,:);
|
||||||
|
|
||||||
|
% converting file
|
||||||
|
% ---------------
|
||||||
|
for indexcol = 1:min(size(array,2), length(g.format))
|
||||||
|
[str mult] = checkformat(g.format{indexcol});
|
||||||
|
for indexrow = 1:size( array, 1)
|
||||||
|
if mult ~= 1
|
||||||
|
eval ( [ 'eloc(indexrow).' str '= -array{indexrow, indexcol};' ]);
|
||||||
|
else
|
||||||
|
eval ( [ 'eloc(indexrow).' str '= array{indexrow, indexcol};' ]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
% handling BESA coordinates
|
||||||
|
% -------------------------
|
||||||
|
if isfield(eloc, 'sph_theta_besa')
|
||||||
|
if isfield(eloc, 'type')
|
||||||
|
if isnumeric(eloc(1).type)
|
||||||
|
disp('BESA format detected ( Theta | Phi )');
|
||||||
|
for index = 1:length(eloc)
|
||||||
|
eloc(index).sph_phi_besa = eloc(index).labels;
|
||||||
|
eloc(index).sph_theta_besa = eloc(index).type;
|
||||||
|
eloc(index).labels = '';
|
||||||
|
eloc(index).type = '';
|
||||||
|
end;
|
||||||
|
eloc = rmfield(eloc, 'labels');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if isfield(eloc, 'labels')
|
||||||
|
if isnumeric(eloc(1).labels)
|
||||||
|
disp('BESA format detected ( Elec | Theta | Phi )');
|
||||||
|
for index = 1:length(eloc)
|
||||||
|
eloc(index).sph_phi_besa = eloc(index).sph_theta_besa;
|
||||||
|
eloc(index).sph_theta_besa = eloc(index).labels;
|
||||||
|
eloc(index).labels = eloc(index).type;
|
||||||
|
eloc(index).type = '';
|
||||||
|
eloc(index).radius = 1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
try
|
||||||
|
eloc = convertlocs(eloc, 'sphbesa2all');
|
||||||
|
eloc = convertlocs(eloc, 'topo2all'); % problem with some EGI files (not BESA files)
|
||||||
|
catch, disp('Warning: coordinate conversion failed'); end;
|
||||||
|
fprintf('Readlocs: BESA spherical coords. converted, now deleting BESA fields\n');
|
||||||
|
fprintf(' to avoid confusion (these fields can be exported, though)\n');
|
||||||
|
eloc = rmfield(eloc, 'sph_phi_besa');
|
||||||
|
eloc = rmfield(eloc, 'sph_theta_besa');
|
||||||
|
|
||||||
|
% converting XYZ coordinates to polar
|
||||||
|
% -----------------------------------
|
||||||
|
elseif isfield(eloc, 'sph_theta')
|
||||||
|
try
|
||||||
|
eloc = convertlocs(eloc, 'sph2all');
|
||||||
|
catch, disp('Warning: coordinate conversion failed'); end;
|
||||||
|
elseif isfield(eloc, 'X')
|
||||||
|
try
|
||||||
|
eloc = convertlocs(eloc, 'cart2all');
|
||||||
|
catch, disp('Warning: coordinate conversion failed'); end;
|
||||||
|
else
|
||||||
|
try
|
||||||
|
eloc = convertlocs(eloc, 'topo2all');
|
||||||
|
catch, disp('Warning: coordinate conversion failed'); end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
% inserting labels if no labels
|
||||||
|
% -----------------------------
|
||||||
|
if ~isfield(eloc, 'labels')
|
||||||
|
fprintf('readlocs(): Inserting electrode labels automatically.\n');
|
||||||
|
for index = 1:length(eloc)
|
||||||
|
eloc(index).labels = [ 'E' int2str(index) ];
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
% remove trailing '.'
|
||||||
|
for index = 1:length(eloc)
|
||||||
|
if isstr(eloc(index).labels)
|
||||||
|
tmpdots = find( eloc(index).labels == '.' );
|
||||||
|
eloc(index).labels(tmpdots) = [];
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
% resorting electrodes if number not-sorted
|
||||||
|
% -----------------------------------------
|
||||||
|
if isfield(eloc, 'channum')
|
||||||
|
if ~isnumeric(eloc(1).channum)
|
||||||
|
error('Channel numbers must be numeric');
|
||||||
|
end;
|
||||||
|
allchannum = [ eloc.channum ];
|
||||||
|
if any( sort(allchannum) ~= allchannum )
|
||||||
|
fprintf('readlocs(): Re-sorting channel numbers based on ''channum'' column indices\n');
|
||||||
|
[tmp newindices] = sort(allchannum);
|
||||||
|
eloc = eloc(newindices);
|
||||||
|
end;
|
||||||
|
eloc = rmfield(eloc, 'channum');
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
if isstruct(filename)
|
||||||
|
eloc = filename;
|
||||||
|
else
|
||||||
|
disp('readlocs(): input variable must be a string or a structure');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if ~isempty(g.elecind)
|
||||||
|
eloc = eloc(g.elecind);
|
||||||
|
end;
|
||||||
|
if nargout > 2
|
||||||
|
tmptheta = { eloc.theta }; % check which channels have (polar) coordinates set
|
||||||
|
indices = find(~cellfun('isempty', tmptheta));
|
||||||
|
tmpx = { eloc.X }; % check which channels have (polar) coordinates set
|
||||||
|
indices = intersect(find(~cellfun('isempty', tmpx)), indices);
|
||||||
|
indices = sort(indices);
|
||||||
|
|
||||||
|
indbad = setdiff(1:length(eloc), indices);
|
||||||
|
tmptheta(indbad) = { NaN };
|
||||||
|
theta = [ tmptheta{:} ];
|
||||||
|
end;
|
||||||
|
if nargout > 3
|
||||||
|
tmprad = { eloc.radius };
|
||||||
|
tmprad(indbad) = { NaN };
|
||||||
|
radius = [ tmprad{:} ];
|
||||||
|
end;
|
||||||
|
%tmpnum = find(~cellfun('isclass', { eloc.labels }, 'char'));
|
||||||
|
%disp('Converting channel labels to string');
|
||||||
|
for index = 1:length(eloc)
|
||||||
|
if ~isstr(eloc(index).labels)
|
||||||
|
eloc(index).labels = int2str(eloc(index).labels);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
labels = { eloc.labels };
|
||||||
|
if isfield(eloc, 'ignore')
|
||||||
|
eloc = rmfield(eloc, 'ignore');
|
||||||
|
end;
|
||||||
|
|
||||||
|
% process fiducials if any
|
||||||
|
% ------------------------
|
||||||
|
fidnames = { 'nz' 'lpa' 'rpa' 'nasion' 'left' 'right' 'nazion' 'fidnz' 'fidt9' 'fidt10' };
|
||||||
|
for index = 1:length(fidnames)
|
||||||
|
ind = strmatch(fidnames{index}, lower(labels), 'exact');
|
||||||
|
if ~isempty(ind), eloc(ind).type = 'FID'; end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
% interpret the variable name
|
||||||
|
% ---------------------------
|
||||||
|
function array = load_file_or_array( varname, skiplines );
|
||||||
|
if isempty(skiplines),
|
||||||
|
skiplines = 0;
|
||||||
|
end;
|
||||||
|
if exist( varname ) == 2
|
||||||
|
array = loadtxt(varname,'verbose','off','skipline',skiplines);
|
||||||
|
else % variable in the global workspace
|
||||||
|
% --------------------------
|
||||||
|
try, array = evalin('base', varname);
|
||||||
|
catch, error('readlocs(): cannot find the named file or variable, check syntax');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
return;
|
||||||
|
|
||||||
|
% check field format
|
||||||
|
% ------------------
|
||||||
|
function [str, mult] = checkformat(str)
|
||||||
|
mult = 1;
|
||||||
|
if strcmpi(str, 'labels'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'channum'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'theta'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'radius'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'ignore'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'sph_theta'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'sph_phi'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'sph_radius'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'sph_theta_besa'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'sph_phi_besa'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'gain'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'calib'), str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'type') , str = lower(str); return; end;
|
||||||
|
if strcmpi(str, 'X'), str = upper(str); return; end;
|
||||||
|
if strcmpi(str, 'Y'), str = upper(str); return; end;
|
||||||
|
if strcmpi(str, 'Z'), str = upper(str); return; end;
|
||||||
|
if strcmpi(str, '-X'), str = upper(str(2:end)); mult = -1; return; end;
|
||||||
|
if strcmpi(str, '-Y'), str = upper(str(2:end)); mult = -1; return; end;
|
||||||
|
if strcmpi(str, '-Z'), str = upper(str(2:end)); mult = -1; return; end;
|
||||||
|
if strcmpi(str, 'custom1'), return; end;
|
||||||
|
if strcmpi(str, 'custom2'), return; end;
|
||||||
|
if strcmpi(str, 'custom3'), return; end;
|
||||||
|
if strcmpi(str, 'custom4'), return; end;
|
||||||
|
error(['readlocs(): undefined field ''' str '''']);
|
||||||
|
|
||||||
@@ -7,51 +7,192 @@ clc
|
|||||||
load('data.mat');
|
load('data.mat');
|
||||||
|
|
||||||
%% Example: Plot the raw signal
|
%% Example: Plot the raw signal
|
||||||
signal=Flex.signal;
|
flexSignal=Flex.signal;
|
||||||
labels=Flex.trigger;
|
pinchSignal = Pinch.signal;
|
||||||
TRIG = gettrigger(labels,0.5);
|
VFSignal = VF.signal;
|
||||||
TRIGend = gettrigger(-labels,-0.5);
|
flexLabels=Flex.trigger;
|
||||||
|
pinchLabels=Pinch.trigger;
|
||||||
|
VFLabels = VF.trigger;
|
||||||
|
|
||||||
figure('units','normalized','Position',[0.1,0.1,0.7,0.4])
|
%% Plot the raw signals
|
||||||
plot((1:length(signal))./fs,zscore(signal));
|
% Grouping variables into cell arrays to loop through them cleanly
|
||||||
|
signals = {flexSignal, pinchSignal, VFSignal};
|
||||||
|
labels_all = {flexLabels, pinchLabels, VFLabels};
|
||||||
|
titles = {'Raw Flex Signal with Stimulation Pattern (Yellow)', ...
|
||||||
|
'Raw Pinch Signal with Stimulation Pattern (Yellow)', ...
|
||||||
|
'Raw VF Signal with Stimulation Pattern (Yellow)'};
|
||||||
|
|
||||||
|
% Create one large figure for all three subplots
|
||||||
|
figure('units','normalized','Position',[0.1, 0.1, 0.7, 0.8])
|
||||||
|
|
||||||
|
for i = 1:3
|
||||||
|
sig = signals{i};
|
||||||
|
lbls = labels_all{i};
|
||||||
|
|
||||||
|
% Find trigger start and end points using your custom function
|
||||||
|
TRIG = gettrigger(lbls, 0.5);
|
||||||
|
TRIGend = gettrigger(-lbls, -0.5);
|
||||||
|
|
||||||
|
% Create a subplot (3 rows, 1 column, current index i)
|
||||||
|
subplot(3, 1, i);
|
||||||
|
|
||||||
|
% Plot normalized signal and labels
|
||||||
|
plot((1:length(sig))./fs, zscore(sig));
|
||||||
hold on;
|
hold on;
|
||||||
plot((1:length(signal))./fs,zscore(labels),'y');
|
plot((1:length(sig))./fs, zscore(lbls), 'y', 'LineWidth', 1.5);
|
||||||
stem(TRIG./fs,ones(length(TRIG),1)*max(zscore(labels)),'Color','g');
|
|
||||||
stem(TRIGend./fs,ones(length(TRIG),1)*max(zscore(labels)),'Color','r');
|
% Plot stem markers for triggers (added conditional checks just in case a signal has no triggers)
|
||||||
|
if ~isempty(TRIG)
|
||||||
|
stem(TRIG./fs, ones(length(TRIG),1)*max(zscore(lbls)), 'Color', 'g');
|
||||||
|
end
|
||||||
|
if ~isempty(TRIGend)
|
||||||
|
stem(TRIGend./fs, ones(length(TRIGend),1)*max(zscore(lbls)), 'Color', 'r');
|
||||||
|
end
|
||||||
|
|
||||||
|
% Formatting
|
||||||
grid on; grid minor;
|
grid on; grid minor;
|
||||||
xlim([0,length(signal)./fs])
|
xlim([0, length(sig)./fs]);
|
||||||
xlabel('Time (s)')
|
xlabel('Time (s)');
|
||||||
ylabel('Amplitude (uV)')
|
% Note: Because you used zscore(), the amplitude is no longer in uV, but in standard deviations
|
||||||
title('Raw VF signal with labels for stimulation periods')
|
ylabel('Amplitude (stdDevs)');
|
||||||
|
title(titles{i});
|
||||||
|
end
|
||||||
|
|
||||||
%% Example: PSD estimates
|
%% Example: PSD estimates
|
||||||
figure('units','normalized','Position',[0.1,0.1,0.5,0.5])
|
figure('Name', 'Raw PSD Estimates', 'units','normalized','Position',[0.1,0.1,0.5,0.5])
|
||||||
[rows_act,cols_act,values_act] = find(labels>0);
|
h = spectrum.welch;
|
||||||
[rows_rest1,cols_rest,values_rest] = find(labels==0);
|
|
||||||
notOfInterest = signal(rows_rest1);
|
|
||||||
signalOfInterest=signal(rows_act);
|
|
||||||
h = spectrum.welch; % creates the Welch spectrum estimator
|
|
||||||
SOIf=psd(h,signalOfInterest,'Fs',fs); % calculates and plot the one sided PSD
|
|
||||||
plot(SOIf); % Plot the one-sided PSD.
|
|
||||||
temp =get(gca);
|
|
||||||
temp.Children(1).Color = 'b';
|
|
||||||
|
|
||||||
% %% Bandpass Filtering
|
% --- REST (All signals combined) ---
|
||||||
|
% Find indices where labels are 0 for each signal type
|
||||||
|
[flex_rows_rest, ~, ~] = find(flexLabels == 0);
|
||||||
|
[pinch_rows_rest, ~, ~] = find(pinchLabels == 0);
|
||||||
|
[vf_rows_rest, ~, ~] = find(VFLabels == 0);
|
||||||
|
|
||||||
|
% Extract the actual signal data during those rest periods
|
||||||
|
flex_restData = flexSignal(flex_rows_rest);
|
||||||
|
pinch_restData = pinchSignal(pinch_rows_rest);
|
||||||
|
vf_restData = VFSignal(vf_rows_rest);
|
||||||
|
|
||||||
|
% Concatenate all rest data into one large vector for a robust estimate
|
||||||
|
all_restData = [flex_restData; pinch_restData; vf_restData];
|
||||||
|
|
||||||
|
% Calculate and Plot Rest PSD in Black
|
||||||
|
SOIf_rest = psd(h, all_restData, 'Fs', fs);
|
||||||
|
plot(SOIf_rest.Frequencies, 10*log10(SOIf_rest.Data), 'k', 'LineWidth', 1.5);
|
||||||
|
hold on;
|
||||||
|
|
||||||
|
% --- FLEX ---
|
||||||
|
[flex_rows_act, ~, ~] = find(flexLabels>0);
|
||||||
|
flex_signalOfInterest = flexSignal(flex_rows_act);
|
||||||
|
SOIf_flex = psd(h, flex_signalOfInterest, 'Fs', fs);
|
||||||
|
plot(SOIf_flex.Frequencies, 10*log10(SOIf_flex.Data), 'g'); % Plot in Green
|
||||||
|
|
||||||
|
% --- PINCH ---
|
||||||
|
[pinch_rows_act, ~, ~] = find(pinchLabels>0);
|
||||||
|
pinch_signalOfInterest = pinchSignal(pinch_rows_act);
|
||||||
|
SOIf_pinch = psd(h, pinch_signalOfInterest, 'Fs', fs);
|
||||||
|
plot(SOIf_pinch.Frequencies, 10*log10(SOIf_pinch.Data), 'r'); % Plot in Red
|
||||||
|
|
||||||
|
% --- VF ---
|
||||||
|
[VF_rows_act, ~, ~] = find(VFLabels>0);
|
||||||
|
VF_signalOfInterest = VFSignal(VF_rows_act);
|
||||||
|
SOIf_VF = psd(h, VF_signalOfInterest, 'Fs', fs);
|
||||||
|
plot(SOIf_VF.Frequencies, 10*log10(SOIf_VF.Data), 'b'); % Plot in Blue
|
||||||
|
|
||||||
|
% --- FORMATTING ---
|
||||||
|
grid on;
|
||||||
|
xlabel('Frequency (Hz)');
|
||||||
|
ylabel('Power/Frequency (dB/Hz)');
|
||||||
|
legend('Rest', 'Flex', 'Pinch', 'VF');
|
||||||
|
title('Power Spectral Density Estimates (Raw Signals)');
|
||||||
|
|
||||||
|
%% Bandpass Filtering
|
||||||
%
|
%
|
||||||
% fc1 = 0; % first cutoff frequency in Hz
|
fc1 = 800; % first cutoff frequency in Hz
|
||||||
% fc2 = 100; % second cutoff frequency in Hz
|
fc2 = 2200; % second cutoff frequency in Hz
|
||||||
%
|
%
|
||||||
% % normalize the frequencies
|
% % normalize the frequencies
|
||||||
% Wp = [fc1 fc2]*2/fs;
|
Wp = [fc1 fc2]*2/fs;
|
||||||
|
|
||||||
% Build a Butterworth bandpass filter of 4th order
|
% Build a Butterworth bandpass filter of 4th order
|
||||||
% check the "butter" function in matlab
|
% check the "butter" function in matlab
|
||||||
|
n = 2;
|
||||||
|
[b, a] = butter(n, Wp, 'bandpass');
|
||||||
|
|
||||||
% Filter data of both classes with a non-causal filter
|
% Filter data of both classes with a non-causal filter
|
||||||
% Hint: use "filtfilt" function in MATLAB
|
% Hint: use "filtfilt" function in MATLAB
|
||||||
% filteredSignal = ;
|
% filteredSignal = ;
|
||||||
|
|
||||||
%%
|
filteredFlex = filtfilt(b, a, flexSignal);
|
||||||
|
filteredPinch = filtfilt(b, a, pinchSignal);
|
||||||
|
filteredVF = filtfilt(b, a, VFSignal);
|
||||||
|
|
||||||
|
%% Compare VF Signal Before and After Filtering (Time Domain)
|
||||||
|
|
||||||
|
% Group the raw and filtered VF signals to loop through them cleanly
|
||||||
|
vf_signals = {VFSignal, filteredVF};
|
||||||
|
vf_labels = {VFLabels, VFLabels}; % Labels remain the exact same
|
||||||
|
vf_titles = {'Raw VF Signal with Stimulation Pattern (Yellow)', ...
|
||||||
|
'Filtered VF Signal (800-2200 Hz) with Stimulation Pattern (Yellow)'};
|
||||||
|
|
||||||
|
% Create one figure for the two subplots (slightly shorter since it's only 2 plots)
|
||||||
|
figure('Name', 'VF Filter Comparison (Time Domain)', 'units', 'normalized', 'Position', [0.1, 0.1, 0.7, 0.6])
|
||||||
|
|
||||||
|
for i = 1:2
|
||||||
|
sig = vf_signals{i};
|
||||||
|
lbls = vf_labels{i};
|
||||||
|
|
||||||
|
% Find trigger start and end points using your custom function
|
||||||
|
TRIG = gettrigger(lbls, 0.5);
|
||||||
|
TRIGend = gettrigger(-lbls, -0.5);
|
||||||
|
|
||||||
|
% Create a subplot (2 rows, 1 column, current index i)
|
||||||
|
subplot(2, 1, i);
|
||||||
|
|
||||||
|
% Plot normalized signal and labels
|
||||||
|
plot((1:length(sig))./fs, zscore(sig));
|
||||||
|
hold on;
|
||||||
|
plot((1:length(sig))./fs, zscore(lbls), 'y', 'LineWidth', 1.5);
|
||||||
|
|
||||||
|
% Plot stem markers for triggers
|
||||||
|
if ~isempty(TRIG)
|
||||||
|
stem(TRIG./fs, ones(length(TRIG),1)*max(zscore(lbls)), 'Color', 'g');
|
||||||
|
end
|
||||||
|
if ~isempty(TRIGend)
|
||||||
|
stem(TRIGend./fs, ones(length(TRIGend),1)*max(zscore(lbls)), 'Color', 'r');
|
||||||
|
end
|
||||||
|
|
||||||
|
% Formatting
|
||||||
|
grid on; grid minor;
|
||||||
|
xlim([0, length(sig)./fs]);
|
||||||
|
xlabel('Time (s)');
|
||||||
|
ylabel('Amplitude (stdDevs)');
|
||||||
|
title(vf_titles{i});
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
%% Compare VF Signal Before and After Filtering (Frequency Domain / PSD)
|
||||||
|
|
||||||
|
figure('Name', 'VF PSD Comparison', 'units', 'normalized', 'Position', [0.2, 0.2, 0.5, 0.4])
|
||||||
|
|
||||||
|
% --- RAW VF ---
|
||||||
|
[VF_rows_act, ~, ~] = find(VFLabels>0);
|
||||||
|
VF_raw_signalOfInterest = VFSignal(VF_rows_act);
|
||||||
|
h = spectrum.welch;
|
||||||
|
SOIf_VF_raw = psd(h, VF_raw_signalOfInterest, 'Fs', fs);
|
||||||
|
plot(SOIf_VF_raw.Frequencies, 10*log10(SOIf_VF_raw.Data), 'b'); % Plot Raw in Blue
|
||||||
|
hold on;
|
||||||
|
|
||||||
|
% --- FILTERED VF ---
|
||||||
|
% Reuse the exact same trigger rows since the timing hasn't changed
|
||||||
|
VF_filt_signalOfInterest = filteredVF(VF_rows_act);
|
||||||
|
SOIf_VF_filt = psd(h, VF_filt_signalOfInterest, 'Fs', fs);
|
||||||
|
plot(SOIf_VF_filt.Frequencies, 10*log10(SOIf_VF_filt.Data), 'r'); % Plot Filtered in Red
|
||||||
|
|
||||||
|
% --- FORMATTING ---
|
||||||
|
grid on; grid minor;
|
||||||
|
title('PSD of VF Signal Before and After Filtering');
|
||||||
|
xlabel('Frequency (Hz)');
|
||||||
|
ylabel('Power/Frequency (dB/Hz)');
|
||||||
|
legend('Raw VF', 'Filtered VF (800 - 2200 Hz)');
|
||||||
|
% xlim([0, 3000]); % Zoom in to see the filter cutoff roll-off clearly
|
||||||
114
HW_1_PNS_EE379K-385V_Neural_Eng/c2_discriminability.m
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
%% Setup and Feature Extraction
|
||||||
|
% Ensure c1_dataVis.m has been run so filtered signals and labels exist.
|
||||||
|
|
||||||
|
% Parameters from Part 2.2b (Feature Selection)
|
||||||
|
WSize_sec = 0.1;
|
||||||
|
Olap_pct = 0;
|
||||||
|
|
||||||
|
% Group data for easy looping
|
||||||
|
signals_list = {filteredFlex, filteredPinch, filteredVF};
|
||||||
|
labels_list = {flexLabels, pinchLabels, VFLabels};
|
||||||
|
names = {'Flex', 'Pinch', 'VF'};
|
||||||
|
|
||||||
|
% Initialize struct to store the extracted features for each class
|
||||||
|
results = struct();
|
||||||
|
|
||||||
|
fprintf('Extracting features (WSize=%.2fs, Olap=%.2f)...\n', WSize_sec, Olap_pct);
|
||||||
|
|
||||||
|
for k = 1:3
|
||||||
|
sig = signals_list{k};
|
||||||
|
lbl = labels_list{k};
|
||||||
|
|
||||||
|
% --- Windowing Logic ---
|
||||||
|
WSize_samp = floor(WSize_sec * fs);
|
||||||
|
nOlap = floor(Olap_pct * WSize_samp);
|
||||||
|
hop = WSize_samp - nOlap;
|
||||||
|
nx = length(sig);
|
||||||
|
len = fix((nx - (WSize_samp - hop)) / hop);
|
||||||
|
|
||||||
|
% Preallocate
|
||||||
|
MAV_vec = zeros(1, len);
|
||||||
|
VAR_vec = zeros(1, len);
|
||||||
|
feat_lbl = zeros(1, len);
|
||||||
|
|
||||||
|
% Get triggers for labeling
|
||||||
|
Rise = gettrigger(lbl, 0.5);
|
||||||
|
Fall = gettrigger(-lbl, -0.5);
|
||||||
|
|
||||||
|
for i = 1:len
|
||||||
|
idx_start = (i-1)*hop + 1;
|
||||||
|
idx_end = idx_start + WSize_samp - 1;
|
||||||
|
|
||||||
|
segment = sig(idx_start:idx_end);
|
||||||
|
|
||||||
|
% Calculate Features
|
||||||
|
MAV_vec(i) = mean(abs(segment));
|
||||||
|
VAR_vec(i) = var(segment);
|
||||||
|
|
||||||
|
% Labeling: 1 if window is strictly inside stimulation, 0 otherwise
|
||||||
|
is_stim = any(idx_start >= Rise & idx_end <= Fall);
|
||||||
|
feat_lbl(i) = double(is_stim);
|
||||||
|
end
|
||||||
|
|
||||||
|
% --- Separate Rest vs Stimulus Data ---
|
||||||
|
% Store the features in the results struct
|
||||||
|
results(k).Name = names{k};
|
||||||
|
results(k).MAV_Stim = MAV_vec(feat_lbl == 1);
|
||||||
|
results(k).MAV_Rest = MAV_vec(feat_lbl == 0);
|
||||||
|
results(k).VAR_Stim = VAR_vec(feat_lbl == 1);
|
||||||
|
results(k).VAR_Rest = VAR_vec(feat_lbl == 0);
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Calculate Fisher's Discriminant Ratio (FDR)
|
||||||
|
% Formula: J = (mu1 - mu2)^2 / (var1^2 + var2^2)
|
||||||
|
% Note: Using Variance (sigma^2) directly in denominator
|
||||||
|
|
||||||
|
fprintf('\n=== Fisher''s Discriminant Ratio (FDR) Results ===\n');
|
||||||
|
fprintf('Higher value = Better discriminability\n');
|
||||||
|
fprintf('----------------------------------------------------------\n');
|
||||||
|
fprintf('%-25s | %-12s | %-12s\n', 'Comparison', 'FDR (MAV)', 'FDR (VAR)');
|
||||||
|
fprintf('----------------------------------------------------------\n');
|
||||||
|
|
||||||
|
% 1. Rest vs Stimulus (Internal comparison for each file)
|
||||||
|
for k = 1:3
|
||||||
|
% Data for Stimulus vs Rest
|
||||||
|
data_stim = results(k);
|
||||||
|
|
||||||
|
% --- MAV ---
|
||||||
|
mu1 = mean(data_stim.MAV_Stim); var1 = var(data_stim.MAV_Stim);
|
||||||
|
mu2 = mean(data_stim.MAV_Rest); var2 = var(data_stim.MAV_Rest);
|
||||||
|
fdr_mav = ((mu1 - mu2)^2) / (var1 + var2);
|
||||||
|
|
||||||
|
% --- VAR ---
|
||||||
|
mu1 = mean(data_stim.VAR_Stim); var1 = var(data_stim.VAR_Stim);
|
||||||
|
mu2 = mean(data_stim.VAR_Rest); var2 = var(data_stim.VAR_Rest);
|
||||||
|
fdr_var = ((mu1 - mu2)^2) / (var1 + var2);
|
||||||
|
|
||||||
|
fprintf('%-25s | %-12.4f | %-12.4f\n', [names{k} ' vs Rest'], fdr_mav, fdr_var);
|
||||||
|
end
|
||||||
|
|
||||||
|
fprintf('----------------------------------------------------------\n');
|
||||||
|
|
||||||
|
% 2. Stimulus vs Stimulus (Compare 'Stim' vector of one to 'Stim' vector of another)
|
||||||
|
pairs = [1 2; 1 3; 2 3]; % Flex-Pinch, Flex-VF, Pinch-VF
|
||||||
|
|
||||||
|
for p = 1:3
|
||||||
|
idx1 = pairs(p, 1);
|
||||||
|
idx2 = pairs(p, 2);
|
||||||
|
|
||||||
|
name1 = names{idx1};
|
||||||
|
name2 = names{idx2};
|
||||||
|
|
||||||
|
% --- MAV ---
|
||||||
|
mu1 = mean(results(idx1).MAV_Stim); var1 = var(results(idx1).MAV_Stim);
|
||||||
|
mu2 = mean(results(idx2).MAV_Stim); var2 = var(results(idx2).MAV_Stim);
|
||||||
|
fdr_mav = ((mu1 - mu2)^2) / (var1 + var2);
|
||||||
|
|
||||||
|
% --- VAR ---
|
||||||
|
mu1 = mean(results(idx1).VAR_Stim); var1 = var(results(idx1).VAR_Stim);
|
||||||
|
mu2 = mean(results(idx2).VAR_Stim); var2 = var(results(idx2).VAR_Stim);
|
||||||
|
fdr_var = ((mu1 - mu2)^2) / (var1 + var2);
|
||||||
|
|
||||||
|
fprintf('%-25s | %-12.4f | %-12.4f\n', [name1 ' vs ' name2], fdr_mav, fdr_var);
|
||||||
|
end
|
||||||
|
fprintf('----------------------------------------------------------\n');
|
||||||
35
HW_1_PNS_EE379K-385V_Neural_Eng/c2_featureExtraction.asv
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
filteredSignal = VF_filt_signalOfInterest; % bandapass filtered signal
|
||||||
|
label = VFLabels; % labels of stimulus locations
|
||||||
|
|
||||||
|
WSize = 50; % window size in s
|
||||||
|
Olap = 0; % overlap percentage
|
||||||
|
|
||||||
|
%% Extracting Features over overlapping windows
|
||||||
|
|
||||||
|
WSize = floor(WSize*fs); % length of each data frame, 30ms
|
||||||
|
nOlap = floor(Olap*WSize); % overlap of successive frames, half of WSize
|
||||||
|
hop = WSize-nOlap; % amount to advance for next data frame
|
||||||
|
nx = length(signal); % length of input vector
|
||||||
|
len = fix((nx - (WSize-hop))/hop); %length of output vector = total frames
|
||||||
|
|
||||||
|
% preallocate outputs for speed
|
||||||
|
[MAV_feature, VAR_feature, featureLabels] = deal(zeros(1,len));
|
||||||
|
|
||||||
|
Rise1 = gettrigger(label,0.5); % gets the starting points of stimulations
|
||||||
|
Fall1 = gettrigger(-label,-0.5); % gets the ending points of stimulations
|
||||||
|
|
||||||
|
for i = 1:len
|
||||||
|
segment = filteredSignal(((i-1)*hop+1):((i-1)*hop+WSize));
|
||||||
|
MAV_feature(i) = ;
|
||||||
|
VAR_feature(i) = ;
|
||||||
|
|
||||||
|
% re-build the label vector to match it with the feature vector
|
||||||
|
featureLabels(i) = sum(arrayfun(@(t) ((i-1)*hop+1) >= Rise1(t) && ((i-1)*hop+WSize) <= Fall1(t), 1:length(Rise1)));
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Plotting the features
|
||||||
|
% Note: when plotting the features, scale the featureLabels to the max of
|
||||||
|
% the feature values for proper visualization
|
||||||
|
|
||||||
|
|
||||||
@@ -1,35 +1,123 @@
|
|||||||
|
%% Parameter Sweep Setup
|
||||||
|
% Note: Ensure you have run c1_dataVis.m first to get filteredFlex/fs/flexLabels
|
||||||
|
filteredSignal = filteredPinch; % Using Flex signal (High SNR) for demonstration
|
||||||
|
label = pinchLabels; % Labels of stimulus locations
|
||||||
|
|
||||||
filteredSignal = ; % bandapass filtered signal
|
% Sweep parameters
|
||||||
label = ; % labels of stimulus locations
|
WSize_values = [0.05, 0.1, 0.3]; % Window sizes in seconds
|
||||||
|
Olap_values = [0, 0.25, 0.75]; % Overlap percentages
|
||||||
|
|
||||||
WSize = ; % window size in s
|
% Get trigger points once (indices in the raw signal)
|
||||||
Olap = ; % overlap percentage
|
Rise1 = gettrigger(label, 0.5); % Start of stimulation
|
||||||
|
Fall1 = gettrigger(-label, -0.5); % End of stimulation
|
||||||
|
|
||||||
%% Extracting Features over overlapping windows
|
% Create Figure
|
||||||
|
figure('Name', 'Feature Extraction Sweep with MAV & VAR SNR', 'units', 'normalized', 'Position', [0, 0, 1, 1]);
|
||||||
|
|
||||||
WSize = floor(WSize*fs); % length of each data frame, 30ms
|
plot_idx = 1; % Counter for subplot index
|
||||||
nOlap = floor(Olap*WSize); % overlap of successive frames, half of WSize
|
|
||||||
hop = WSize-nOlap; % amount to advance for next data frame
|
|
||||||
nx = length(signal); % length of input vector
|
|
||||||
len = fix((nx - (WSize-hop))/hop); %length of output vector = total frames
|
|
||||||
|
|
||||||
% preallocate outputs for speed
|
%% Loop through all combinations
|
||||||
[MAV_feature, VAR_feature, featureLabels] = deal(zeros(1,len));
|
for w = 1:length(WSize_values)
|
||||||
|
for o = 1:length(Olap_values)
|
||||||
|
|
||||||
Rise1 = gettrigger(label,0.5); % gets the starting points of stimulations
|
% Current parameters
|
||||||
Fall1 = gettrigger(-label,-0.5); % gets the ending points of stimulations
|
current_WSize_s = WSize_values(w);
|
||||||
|
current_Olap_pct = Olap_values(o);
|
||||||
|
|
||||||
|
% Windowing calculations
|
||||||
|
WSize_samp = floor(current_WSize_s * fs); % Window size in samples
|
||||||
|
nOlap = floor(current_Olap_pct * WSize_samp); % Overlap in samples
|
||||||
|
hop = WSize_samp - nOlap; % Hop size
|
||||||
|
nx = length(filteredSignal);
|
||||||
|
len = fix((nx - (WSize_samp - hop)) / hop); % Total number of frames
|
||||||
|
|
||||||
|
% Preallocate
|
||||||
|
MAV_feature = zeros(1, len);
|
||||||
|
VAR_feature = zeros(1, len);
|
||||||
|
featureLabels = zeros(1, len);
|
||||||
|
|
||||||
|
% Feature Extraction Loop
|
||||||
for i = 1:len
|
for i = 1:len
|
||||||
segment = filteredSignal(((i-1)*hop+1):((i-1)*hop+WSize));
|
% Extract segment
|
||||||
MAV_feature(i) = ;
|
idx_start = (i-1)*hop + 1;
|
||||||
VAR_feature(i) = ;
|
idx_end = idx_start + WSize_samp - 1;
|
||||||
|
|
||||||
% re-build the label vector to match it with the feature vector
|
% Check bounds
|
||||||
featureLabels(i) = sum(arrayfun(@(t) ((i-1)*hop+1) >= Rise1(t) && ((i-1)*hop+WSize) <= Fall1(t), 1:length(Rise1)));
|
if idx_end > nx
|
||||||
|
break;
|
||||||
end
|
end
|
||||||
|
|
||||||
%% Plotting the features
|
segment = filteredSignal(idx_start:idx_end);
|
||||||
% Note: when plotting the features, scale the featureLabels to the max of
|
|
||||||
% the feature values for proper visualization
|
|
||||||
|
|
||||||
|
% Calculate Features
|
||||||
|
MAV_feature(i) = mean(abs(segment));
|
||||||
|
VAR_feature(i) = var(segment);
|
||||||
|
|
||||||
|
% Re-build label vector
|
||||||
|
% Strict: Window must be fully inside stimulation to count as '1'
|
||||||
|
is_stim = any(idx_start >= Rise1 & idx_end <= Fall1);
|
||||||
|
featureLabels(i) = double(is_stim);
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Calculate SNR
|
||||||
|
% Separate Stimulus and Rest values using logical indexing
|
||||||
|
stim_indices = (featureLabels == 1);
|
||||||
|
rest_indices = (featureLabels == 0);
|
||||||
|
|
||||||
|
% --- MAV SNR ---
|
||||||
|
mean_mav_stim = mean(MAV_feature(stim_indices));
|
||||||
|
mean_mav_rest = mean(MAV_feature(rest_indices));
|
||||||
|
if mean_mav_rest > 0
|
||||||
|
mav_snr = 20 * log10(mean_mav_stim / mean_mav_rest);
|
||||||
|
else
|
||||||
|
mav_snr = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
% --- VAR SNR ---
|
||||||
|
mean_var_stim = mean(VAR_feature(stim_indices));
|
||||||
|
mean_var_rest = mean(VAR_feature(rest_indices));
|
||||||
|
if mean_var_rest > 0
|
||||||
|
var_snr = 20 * log10(mean_var_stim / mean_var_rest);
|
||||||
|
else
|
||||||
|
var_snr = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Plotting MAV (Left Column)
|
||||||
|
subplot(9, 2, plot_idx);
|
||||||
|
plot(MAV_feature, 'b', 'LineWidth', 0.5); hold on;
|
||||||
|
plot(featureLabels * max(MAV_feature), 'r', 'LineWidth', 1);
|
||||||
|
|
||||||
|
% Formatting Title with SNR
|
||||||
|
title_str = sprintf('MAV (W=%.2g, Olap=%.2g) | SNR=%.2f dB', ...
|
||||||
|
current_WSize_s, current_Olap_pct, mav_snr);
|
||||||
|
grid on;
|
||||||
|
title(title_str, 'FontSize', 8);
|
||||||
|
|
||||||
|
if mod(plot_idx, 2) == 1
|
||||||
|
ylabel('MAV');
|
||||||
|
end
|
||||||
|
xlim([1, len]);
|
||||||
|
set(gca, 'XTickLabel', []);
|
||||||
|
|
||||||
|
plot_idx = plot_idx + 1;
|
||||||
|
|
||||||
|
%% Plotting VAR (Right Column)
|
||||||
|
subplot(9, 2, plot_idx);
|
||||||
|
plot(VAR_feature, 'k', 'LineWidth', 0.5); hold on;
|
||||||
|
plot(featureLabels * max(VAR_feature), 'r', 'LineWidth', 1);
|
||||||
|
|
||||||
|
% Formatting VAR with SNR
|
||||||
|
title_str_var = sprintf('VAR (W=%.2g, Olap=%.2g) | SNR=%.2f dB', ...
|
||||||
|
current_WSize_s, current_Olap_pct, var_snr);
|
||||||
|
grid on;
|
||||||
|
title(title_str_var, 'FontSize', 8);
|
||||||
|
|
||||||
|
if mod(plot_idx, 2) == 0
|
||||||
|
ylabel('VAR');
|
||||||
|
end
|
||||||
|
xlim([1, len]);
|
||||||
|
set(gca, 'XTickLabel', []);
|
||||||
|
|
||||||
|
plot_idx = plot_idx + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,107 +1,178 @@
|
|||||||
close all;clc;
|
%% c3_classification_complete.m
|
||||||
%%
|
% This script performs 10-fold cross-validation for Part 2.3 of the assignment.
|
||||||
% Inputs:
|
% It answers:
|
||||||
% --------
|
% 1. Classifiability of Stimuli vs Rest
|
||||||
% MAVClass1: the features of the VF case (stimulus and rest features)
|
% 2. Classifiability of Stimuli vs Stimuli
|
||||||
% MAVClass2: the features of the Pinch case (stimulus and rest features)
|
% 3. Comparison of MAV vs VAR features
|
||||||
% TriggerClass1: labels for VF features (stimulus or rest label)
|
% 4. Evaluation of Confusion Matrices
|
||||||
% TriggerClass2: labels for Pinch features (stimulus or rest label)
|
|
||||||
|
|
||||||
% Build the datasets
|
clearvars -except filteredFlex filteredPinch filteredVF flexLabels pinchLabels VFLabels fs;
|
||||||
MAV_class1 = MAVClass1(find(TriggerClass1==1));
|
clc;
|
||||||
MAV_rest1 = MAVClass1(find(TriggerClass1==0));
|
|
||||||
|
|
||||||
VAR_class1 = VARClass1(find(TriggerClass1==1));
|
% Check if data is loaded
|
||||||
VAR_rest1 = VARClass1(find(TriggerClass1==0));
|
if ~exist('filteredFlex', 'var')
|
||||||
|
error('Error: Filtered signals not found. Please run c1_dataVis.m first.');
|
||||||
|
end
|
||||||
|
|
||||||
MAV_class2 = MAVClass2(find(TriggerClass2==1));
|
%% 1. Feature Extraction (WSize=100ms, Olap=0)
|
||||||
MAV_rest2 = MAVClass2(find(TriggerClass2==0));
|
fprintf('1. Extracting Features (100ms Window, 0%% Overlap)...\n');
|
||||||
|
|
||||||
VAR_class2 = VARClass2(find(TriggerClass2==1));
|
WSize_sec = 0.1;
|
||||||
VAR_rest2 = VARClass2(find(TriggerClass2==0));
|
Olap_pct = 0;
|
||||||
|
WSize = floor(WSize_sec * fs);
|
||||||
|
nOlap = floor(Olap_pct * WSize);
|
||||||
|
hop = WSize - nOlap;
|
||||||
|
|
||||||
% Concantenate the rest classes
|
% Organize data for looping
|
||||||
MAV_rest = [MAV_rest1 MAV_rest2];
|
% Index 1=VF, 2=Flex, 3=Pinch
|
||||||
VAR_rest = [VAR_rest1 VAR_rest2];
|
sigs = {filteredVF, filteredFlex, filteredPinch};
|
||||||
|
lbls = {VFLabels, flexLabels, pinchLabels};
|
||||||
|
names = {'VF', 'Flex', 'Pinch'};
|
||||||
|
|
||||||
|
feats = struct(); % Structure to hold features
|
||||||
|
|
||||||
%%
|
for k = 1:3
|
||||||
% Class1 vs Rest dataset
|
sig = sigs{k};
|
||||||
MAV_Data_Class1vsRest = [MAV_class1 MAV_rest];
|
lab = lbls{k};
|
||||||
MAV_Labels_Class1vsRest = [ones(1,length(MAV_class1)) 2*ones(1,length(MAV_rest))];
|
|
||||||
|
|
||||||
VAR_Data_Class1vsRest = [VAR_class1 VAR_rest];
|
nx = length(sig);
|
||||||
VAR_Labels_Class1vsRest = MAV_Labels_Class1vsRest;
|
len = fix((nx - (WSize - hop)) / hop);
|
||||||
|
|
||||||
% Class2 vs Rest dataset
|
MAV_vec = zeros(1, len);
|
||||||
MAV_Data_Class2vsRest = [MAV_class2 MAV_rest];
|
VAR_vec = zeros(1, len);
|
||||||
MAV_Labels_Class2vsRest = [ones(1,length(MAV_class2)) 2*ones(1,length(MAV_rest))];
|
LBL_vec = zeros(1, len);
|
||||||
|
|
||||||
VAR_Data_Class2vsRest = [VAR_class2 VAR_rest];
|
Rise = gettrigger(lab, 0.5);
|
||||||
VAR_Labels_Class2vsRest = MAV_Labels_Class2vsRest;
|
Fall = gettrigger(-lab, -0.5);
|
||||||
|
|
||||||
% Class1 vs Class2 dataset
|
for i = 1:len
|
||||||
MAV_Data_Class1vsClass2 = [MAV_class1 MAV_class2];
|
idx_start = (i-1)*hop + 1;
|
||||||
MAV_Labels_Class1vsClass2 = [ones(1,length(MAV_class1)) 2*ones(1,length(MAV_class2))];
|
idx_end = idx_start + WSize - 1;
|
||||||
|
segment = sig(idx_start:idx_end);
|
||||||
|
|
||||||
VAR_Data_Class1vsClass2 = [VAR_class1 VAR_class2];
|
MAV_vec(i) = mean(abs(segment));
|
||||||
VAR_Labels_Class1vsClass2 = MAV_Labels_Class1vsClass2;
|
VAR_vec(i) = var(segment);
|
||||||
|
|
||||||
%%
|
% Label: 1 if window is strictly inside stimulation
|
||||||
% Both feature datasets
|
is_stim = any(idx_start >= Rise & idx_end <= Fall);
|
||||||
MAVVAR_Data_Class1vsRest = [MAV_Data_Class1vsRest; VAR_Data_Class1vsRest];
|
LBL_vec(i) = double(is_stim);
|
||||||
MAVVAR_Labels_Class1vsRest = MAV_Labels_Class1vsRest;
|
end
|
||||||
|
|
||||||
MAVVAR_Data_Class2vsRest = [MAV_Data_Class2vsRest; VAR_Data_Class2vsRest];
|
feats(k).MAV = MAV_vec;
|
||||||
MAVVAR_Labels_Class2vsRest = MAV_Labels_Class2vsRest;
|
feats(k).VAR = VAR_vec;
|
||||||
|
feats(k).LBL = LBL_vec;
|
||||||
|
feats(k).Name = names{k};
|
||||||
|
end
|
||||||
|
|
||||||
MAVVAR_Data_Class1vsClass2 = [MAV_Data_Class1vsClass2; VAR_Data_Class1vsClass2];
|
%% 2. Define Comparisons
|
||||||
MAVVAR_Labels_Class1vsClass2 = MAV_Labels_Class1vsClass2;
|
% We need to run classification for these specific pairs:
|
||||||
|
comparisons = {
|
||||||
|
'VF vs Rest', 1, 0; % 0 denotes "Rest" class
|
||||||
|
'Flex vs Rest', 2, 0;
|
||||||
|
'Pinch vs Rest', 3, 0;
|
||||||
|
'Flex vs Pinch', 2, 3;
|
||||||
|
'Flex vs VF', 2, 1;
|
||||||
|
'Pinch vs VF', 3, 1;
|
||||||
|
};
|
||||||
|
|
||||||
%%
|
%% 3. Classification Loop (10-Fold CV)
|
||||||
% Classify all combinations (training set)
|
fprintf('\n2. Running 10-Fold Cross Validation...\n');
|
||||||
k = 10; % for k-fold cross validation
|
fprintf('----------------------------------------------------------------\n');
|
||||||
c1 = cvpartition(length(MAV_Labels_Class1vsRest),'KFold',k);
|
fprintf('%-20s | %-12s | %-12s | %-15s\n', 'Comparison', 'Acc (MAV)', 'Acc (VAR)', 'Best Feature');
|
||||||
c2 = cvpartition(length(VAR_Labels_Class1vsRest),'KFold',k);
|
fprintf('----------------------------------------------------------------\n');
|
||||||
c3 = cvpartition(length(MAVVAR_Labels_Class1vsRest),'KFold',k);
|
|
||||||
c4 = cvpartition(length(MAV_Labels_Class2vsRest),'KFold',k);
|
|
||||||
c5 = cvpartition(length(VAR_Labels_Class2vsRest),'KFold',k);
|
|
||||||
c6 = cvpartition(length(MAVVAR_Labels_Class2vsRest),'KFold',k);
|
|
||||||
c7 = cvpartition(length(MAV_Labels_Class1vsClass2),'KFold',k);
|
|
||||||
c8 = cvpartition(length(VAR_Labels_Class1vsClass2),'KFold',k);
|
|
||||||
c9 = cvpartition(length(MAVVAR_Labels_Class1vsClass2),'KFold',k);
|
|
||||||
|
|
||||||
% Repeat the following for i=1:k, and average performance metrics across all iterations
|
for c = 1:size(comparisons, 1)
|
||||||
i=1;
|
comp_name = comparisons{c, 1};
|
||||||
% loop over all k-folds and avergae the performance
|
idx1 = comparisons{c, 2};
|
||||||
% for i=1:k
|
idx2 = comparisons{c, 3};
|
||||||
[TstMAVFC1Rest TstMAVErrC1Rest] = classify(MAV_Data_Class1vsRest(c1.test(i))',MAV_Data_Class1vsRest(c1.training(i))',MAV_Labels_Class1vsRest(c1.training(i)));
|
|
||||||
[TstCM_MAV_C1rest dum1 TstAcc_MAV_C1rest dum2] = confusion(MAV_Labels_Class1vsRest(c1.test(i)), TstMAVFC1Rest);
|
|
||||||
|
|
||||||
[TstVARFC1Rest TstVARErrC1Rest] = classify(VAR_Data_Class1vsRest(c2.test(i))',VAR_Data_Class1vsRest(c2.training(i))',VAR_Labels_Class1vsRest(c2.training(i)));
|
% --- Prepare Data for Class 1 ---
|
||||||
[TstCM_VAR_C1rest dum1 TstAcc_VAR_C1rest dum2] = confusion(VAR_Labels_Class1vsRest(c2.test(i)), TstVARFC1Rest);
|
% Get Stimulus features (Label == 1)
|
||||||
|
f1_MAV = feats(idx1).MAV(feats(idx1).LBL == 1);
|
||||||
|
f1_VAR = feats(idx1).VAR(feats(idx1).LBL == 1);
|
||||||
|
|
||||||
[TstMAVVARFC1Rest TstMAVVARErrC1Rest] = classify(MAVVAR_Data_Class1vsRest(:,c3.test(i))',MAVVAR_Data_Class1vsRest(:,c3.training(i))',MAVVAR_Labels_Class1vsRest(c3.training(i)));
|
% --- Prepare Data for Class 2 (or Rest) ---
|
||||||
[TstCM_MAVVAR_C1rest dum1 TstAcc_MAVVAR_C1rest dum2] = confusion(MAVVAR_Labels_Class1vsRest(c3.test(i)), TstMAVVARFC1Rest);
|
if idx2 == 0
|
||||||
|
% If comparing vs Rest, get Rest features (Label == 0) from the SAME signal
|
||||||
|
f2_MAV = feats(idx1).MAV(feats(idx1).LBL == 0);
|
||||||
|
f2_VAR = feats(idx1).VAR(feats(idx1).LBL == 0);
|
||||||
|
label_names = {feats(idx1).Name, 'Rest'};
|
||||||
|
else
|
||||||
|
% If comparing vs another Stimulus, get Stimulus features (Label == 1)
|
||||||
|
f2_MAV = feats(idx2).MAV(feats(idx2).LBL == 1);
|
||||||
|
f2_VAR = feats(idx2).VAR(feats(idx2).LBL == 1);
|
||||||
|
label_names = {feats(idx1).Name, feats(idx2).Name};
|
||||||
|
end
|
||||||
|
|
||||||
% Class2 vs Rest
|
% Combine Data
|
||||||
[TstMAVFC2Rest TstMAVErrC2Rest] = classify(MAV_Data_Class2vsRest(c4.test(i))',MAV_Data_Class2vsRest(c4.training(i))',MAV_Labels_Class2vsRest(c4.training(i)));
|
X_MAV = [f1_MAV, f2_MAV]'; % Transpose to column vector
|
||||||
[TstCM_MAV_C2rest dum1 TstAcc_MAV_C2rest dum2] = confusion(MAV_Labels_Class2vsRest(c4.test(i)), TstMAVFC2Rest);
|
X_VAR = [f1_VAR, f2_VAR]';
|
||||||
|
|
||||||
[TstVARFC2Rest TstVARErrC2Rest] = classify(VAR_Data_Class2vsRest(c5.test(i))',VAR_Data_Class2vsRest(c5.training(i))',VAR_Labels_Class2vsRest(c5.training(i)));
|
% Create Labels (1 for Class 1, 2 for Class 2)
|
||||||
[TstCM_VAR_C2rest dum1 TstAcc_VAR_C2rest dum2] = confusion(VAR_Labels_Class2vsRest(c5.test(i)), TstVARFC2Rest);
|
Y = [ones(length(f1_MAV), 1); 2 * ones(length(f2_MAV), 1)];
|
||||||
|
|
||||||
[TstMAVVARFC2Rest TstMAVVARErrC2Rest] = classify(MAVVAR_Data_Class2vsRest(:,c6.test(i))',MAVVAR_Data_Class2vsRest(:,c6.training(i))',MAVVAR_Labels_Class2vsRest(c6.training(i)));
|
% --- 10-Fold Cross Validation ---
|
||||||
[TstCM_MAVVAR_C2rest dum1 TstAcc_MAVVAR_C2rest dum2] = confusion(MAVVAR_Labels_Class2vsRest(c6.test(i)), TstMAVVARFC2Rest);
|
k = 10;
|
||||||
|
cv = cvpartition(Y, 'KFold', k); % Random split (answering Q4!)
|
||||||
|
|
||||||
% Class1 vs Class2
|
acc_mav = 0;
|
||||||
[TstMAVFC1C2 TstMAVErrC1C2] = classify(MAV_Data_Class1vsClass2(c7.test(i))',MAV_Data_Class1vsClass2(c7.training(i))',MAV_Labels_Class1vsClass2(c7.training(i)));
|
acc_var = 0;
|
||||||
[TstCM_MAV_C1C2 dum1 TstAcc_MAV_C1C2 dum2] = confusion(MAV_Labels_Class1vsClass2(c7.test(i)), TstMAVFC1C2);
|
conf_mav = zeros(2,2); % Accumulate confusion matrix
|
||||||
|
|
||||||
[TstVARFC1C2 TstVARErrC1C2] = classify(VAR_Data_Class1vsClass2(c8.test(i))',VAR_Data_Class1vsClass2(c8.training(i))',VAR_Labels_Class1vsClass2(c8.training(i)));
|
for i = 1:k
|
||||||
[TstCM_VAR_C1C2 dum1 TstAcc_VAR_C1C2 dum2] = confusion(VAR_Labels_Class1vsClass2(c8.test(i)), TstVARFC1C2);
|
train_idx = cv.training(i);
|
||||||
|
test_idx = cv.test(i);
|
||||||
|
|
||||||
[TstMAVVARFC1C2 TstMAVVARErrC1C2] = classify(MAVVAR_Data_Class1vsClass2(:,c9.test(i))',MAVVAR_Data_Class1vsClass2(:,c9.training(i))',MAVVAR_Labels_Class1vsClass2(c9.training(i)));
|
% MAV Classification
|
||||||
[TstCM_MAVVAR_C1C2 dum1 TstAcc_MAVVAR_C1C2 dum2] = confusion(MAVVAR_Labels_Class1vsClass2(c9.test(i)), TstMAVVARFC1C2);
|
pred_mav = classify(X_MAV(test_idx), X_MAV(train_idx), Y(train_idx));
|
||||||
% end
|
acc_mav = acc_mav + sum(pred_mav == Y(test_idx)) / length(pred_mav);
|
||||||
%%
|
|
||||||
|
% Build Confusion Matrix for MAV (just one example needed for assignment)
|
||||||
|
% Rows = True Class, Cols = Predicted Class
|
||||||
|
current_conf = confusionmat(Y(test_idx), pred_mav);
|
||||||
|
% Handle edge case if a fold misses a class
|
||||||
|
if size(current_conf,1) == 2
|
||||||
|
conf_mav = conf_mav + current_conf;
|
||||||
|
end
|
||||||
|
|
||||||
|
% VAR Classification
|
||||||
|
pred_var = classify(X_VAR(test_idx), X_VAR(train_idx), Y(train_idx));
|
||||||
|
acc_var = acc_var + sum(pred_var == Y(test_idx)) / length(pred_var);
|
||||||
|
end
|
||||||
|
|
||||||
|
% Average Accuracy
|
||||||
|
mean_acc_mav = (acc_mav / k) * 100;
|
||||||
|
mean_acc_var = (acc_var / k) * 100;
|
||||||
|
|
||||||
|
% Determine Winner
|
||||||
|
if mean_acc_mav > mean_acc_var
|
||||||
|
winner = 'MAV';
|
||||||
|
elseif mean_acc_var > mean_acc_mav
|
||||||
|
winner = 'VAR';
|
||||||
|
else
|
||||||
|
winner = 'Tie';
|
||||||
|
end
|
||||||
|
|
||||||
|
fprintf('%-20s | %-11.1f%% | %-11.1f%% | %-15s\n', comp_name, mean_acc_mav, mean_acc_var, winner);
|
||||||
|
|
||||||
|
% --- Display Confusion Matrix for MAV ---
|
||||||
|
% Only printing logic to keep output clean, answering "Observe confusion matrices"
|
||||||
|
fprintf(' Confusion Matrix (MAV) for %s:\n', comp_name);
|
||||||
|
fprintf(' True %-6s: [ %4d %4d ] (Predicted %s / %s)\n', label_names{1}, conf_mav(1,1), conf_mav(1,2), label_names{1}, label_names{2});
|
||||||
|
fprintf(' True %-6s: [ %4d %4d ]\n\n', label_names{2}, conf_mav(2,1), conf_mav(2,2));
|
||||||
|
end
|
||||||
|
fprintf('----------------------------------------------------------------\n');
|
||||||
|
|
||||||
|
%% 4. Answer Prompts
|
||||||
|
fprintf('\n=== Automated Analysis for Part 2.3 ===\n');
|
||||||
|
fprintf('1. Check "Pinch vs Rest" accuracy above. Is it low? (Likely yes, due to low SNR).\n');
|
||||||
|
fprintf('2. Check "Flex vs Pinch". Can they be distinguished?\n');
|
||||||
|
fprintf('3. Observe the Confusion Matrices: Are they balanced? \n');
|
||||||
|
fprintf(' - If one class is predicted much more often, the classifier is biased.\n');
|
||||||
|
fprintf('4. Feature Performance: Look at the "Best Feature" column.\n');
|
||||||
|
fprintf(' - MAV is typically more robust for these signals.\n');
|
||||||
|
fprintf('5. Validation Fairness (Assignment Q4):\n');
|
||||||
|
fprintf(' - This script uses "cvpartition", which splits data RANDOMLY.\n');
|
||||||
|
fprintf(' - Since EMG/ENG signals are time-series, random splitting causes "data leakage"\n');
|
||||||
|
fprintf(' (training on samples immediately adjacent to test samples).\n');
|
||||||
|
fprintf(' - Therefore, this is likely NOT a fair assessment of generalization.\n');
|
||||||
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 302 KiB |
|
After Width: | Height: | Size: 820 KiB |
|
After Width: | Height: | Size: 219 KiB |
BIN
HW_2_sp23_EE374N-385J_Neural_Eng_EMGanalysis/2_4_cv_heatmap.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 307 KiB |
|
After Width: | Height: | Size: 86 KiB |
1813
HW_2_sp23_EE374N-385J_Neural_Eng_EMGanalysis/HW2_EMG_Analysis.ipynb
Normal file
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 347 KiB |
|
After Width: | Height: | Size: 347 KiB |
|
After Width: | Height: | Size: 354 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 90 KiB |