├── cca.m ├── ssvep-switch-train-12Hz-Shiva-[2017.01.10-20.45.21].gdf ├── ssvep-switch-train-15Hz-Shiva-[2017.01.10-21.33.50].gdf ├── ssvep-switch-train-8Hz-Shiva-[2017.01.10-21.26.42].gdf ├── ssvep-switch-train-8Hz-Indra-train-[2016.12.07-18.19.00].gdf ├── ssvep-switch-train-12Hz-Indra-train-[2016.12.07-15.11.04].gdf ├── ssvep-switch-train-15Hz-Indra-train-[2016.12.07-16.03.31].gdf ├── bandfiltfilt.m ├── epoch.m ├── ccaResult.m ├── .gitattributes ├── ck_signal_windowed.m ├── actTime.m └── actTime_FP.m /cca.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lol/ssvepSwitch/master/cca.m -------------------------------------------------------------------------------- /ssvep-switch-train-12Hz-Shiva-[2017.01.10-20.45.21].gdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lol/ssvepSwitch/master/ssvep-switch-train-12Hz-Shiva-[2017.01.10-20.45.21].gdf -------------------------------------------------------------------------------- /ssvep-switch-train-15Hz-Shiva-[2017.01.10-21.33.50].gdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lol/ssvepSwitch/master/ssvep-switch-train-15Hz-Shiva-[2017.01.10-21.33.50].gdf -------------------------------------------------------------------------------- /ssvep-switch-train-8Hz-Shiva-[2017.01.10-21.26.42].gdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lol/ssvepSwitch/master/ssvep-switch-train-8Hz-Shiva-[2017.01.10-21.26.42].gdf -------------------------------------------------------------------------------- /ssvep-switch-train-8Hz-Indra-train-[2016.12.07-18.19.00].gdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lol/ssvepSwitch/master/ssvep-switch-train-8Hz-Indra-train-[2016.12.07-18.19.00].gdf -------------------------------------------------------------------------------- /ssvep-switch-train-12Hz-Indra-train-[2016.12.07-15.11.04].gdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lol/ssvepSwitch/master/ssvep-switch-train-12Hz-Indra-train-[2016.12.07-15.11.04].gdf -------------------------------------------------------------------------------- /ssvep-switch-train-15Hz-Indra-train-[2016.12.07-16.03.31].gdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lol/ssvepSwitch/master/ssvep-switch-train-15Hz-Indra-train-[2016.12.07-16.03.31].gdf -------------------------------------------------------------------------------- /bandfiltfilt.m: -------------------------------------------------------------------------------- 1 | function sig = bandfiltfilt(s, fs, order, lowBand, highBand) 2 | 3 | lowFreq = lowBand * (2/fs); 4 | highFreq = highBand * (2/fs); 5 | 6 | [B A] = butter(order, [lowFreq highFreq]); 7 | 8 | sig = filtfilt(B, A, s); 9 | 10 | end -------------------------------------------------------------------------------- /epoch.m: -------------------------------------------------------------------------------- 1 | function mat = epoch(signal, window_time, fs, overlap_factor) 2 | mat = buffer(signal, window_time*fs, ceil(overlap_factor * window_time * fs)); %create a matrix with overlapped segments, also called 'Time Based Epoching'. 1 sec segments every 0.1 secs. 3 | end -------------------------------------------------------------------------------- /ccaResult.m: -------------------------------------------------------------------------------- 1 | function result = ccaResult(x, y) 2 | 3 | %idx = ccaResult(SSVEPdata(:, 1:TW_p(tw_length), run, j), refSignals(:, 1:TW_p(tw_length), :), n_sti); 4 | n_sti = size(y, 3); 5 | for i = 1:n_sti 6 | [wx(:, :, i), wy(:, :, i), r(:, i)] = cca(x, y(:, :, i)); 7 | end 8 | 9 | if(size(r, 1) == 1) 10 | [v, result] = max(r); 11 | else 12 | [v, result] = max(max(r)); 13 | end 14 | 15 | end -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /ck_signal_windowed.m: -------------------------------------------------------------------------------- 1 | function refSignal = ck_signal_windowed(sti_f, windowTime, fs) 2 | %% constract first and second harmonic wave 3 | TP = 1/fs : 1/fs : windowTime; 4 | % h means harmonics 5 | for j=1:length(sti_f) 6 | Sin_h1 = sin(2*pi*sti_f(j)*TP); 7 | Cos_h1 = cos(2*pi*sti_f(j)*TP); 8 | Sin_h2 = sin(2*pi*2*sti_f(j)*TP); 9 | Cos_h2 = cos(2*pi*2*sti_f(j)*TP); 10 | refSignal(:, :, j) = [Sin_h1; Cos_h1; Sin_h2; Cos_h2]; 11 | %refSignal(:, :, j) = [Sin_h1; Cos_h1]; 12 | end -------------------------------------------------------------------------------- /actTime.m: -------------------------------------------------------------------------------- 1 | clc; 2 | clear all; 3 | close all; 4 | 5 | file = 'ssvep-switch-train-15Hz-Indra-train-[2016.12.07-16.03.31].gdf'; 6 | %file = 'ssvep-switch-train-12Hz-Indra-train-[2016.12.07-15.11.04].gdf'; 7 | %file = 'ssvep-switch-train-8Hz-Indra-train-[2016.12.07-18.19.00].gdf'; 8 | sti_f = 15; 9 | %sti_f = 12; 10 | %sti_f = 60/7; 11 | % 5s NC, 5s rest, 5s IC, 5s rest 12 | % 32779+33024/25 at the same time, 32780 after 5s 13 | [s, h] = sload(file); 14 | fs = h.SampleRate; 15 | SSVEPdata = bandfiltfilt(s(h.EVENT.POS(2):end, :), fs, 4, 1, 40); 16 | numChannels = size(SSVEPdata, 2); 17 | 18 | 19 | stimCodes = [33024, 33025]; 20 | 21 | stimCodeSubset = find(ismember(h.EVENT.TYP, [33024, 33025])); 22 | 23 | h.EVENT.TYP = h.EVENT.TYP(stimCodeSubset); 24 | h.EVENT.POS = h.EVENT.POS(stimCodeSubset) - h.EVENT.POS(2) + 1; 25 | 26 | numTotalSamples = length(SSVEPdata); 27 | 28 | windowTime = 2; % in seconds 29 | jumpTime = 0.1; % in seconds 30 | jump = jumpTime * fs; %In samples, time in seconds * fs 31 | windowSize = windowTime * fs; %In samples 32 | %numWindows = 1 + floor((length(SSVEPdata) - windowSize) / jump); 33 | overlapFactor = 1 - (jumpTime / windowTime); 34 | 35 | 36 | for i = 1:numChannels 37 | SSVEPdataEpoch(i, :, :) = epoch(SSVEPdata(:, i), windowTime, fs, overlapFactor); 38 | end 39 | %SSVEPdataEpoch = 4 x 500 x 2994 40 | 41 | numWindows = size(SSVEPdataEpoch, 3); 42 | %skipSampleCount = fs; 43 | %skipSampleCount = 0; 44 | %firstIC_sampleStart = 10*fs + skipSampleCount + 1 ;% first IC start 2501+250 45 | %firstIC_sampleEnd = 15*fs - windowSize ;% first IC end 3750-500 46 | 47 | h.EVENT.WIN_NUM = 1 + (h.EVENT.POS - 1) / jump; 48 | 49 | % generate reference signals from 1 to 20 Hz. 50 | %sti_f_ref = 1:20; 51 | sti_f_ref = 60 ./ [4:9]; 52 | targetFlickerIndex = find(ismember(sti_f_ref, sti_f)); 53 | refSignals = ck_signal_windowed(sti_f_ref, windowTime, fs); 54 | 55 | IC_marker = h.EVENT.WIN_NUM(h.EVENT.TYP == 33025); 56 | IC_marker = [IC_marker; numWindows + 1]; 57 | 58 | 59 | % 5 is the IC time duration 60 | %numWindowsInOneTrial = (5 + windowTime) / jumpTime; %5 second trial, 2 second 61 | numWindowsInOneTrial = 5 / jumpTime; 62 | j = 1; 63 | k = 1; 64 | startDetection = 0; 65 | alreadyDetected = 0; 66 | %startFlicker(1) = 1; 67 | for i = 1:numWindows 68 | if i >= IC_marker(j) && i < IC_marker(j) + numWindowsInOneTrial 69 | true_label(i) = sti_f_ref(targetFlickerIndex); 70 | if alreadyDetected == 0 && startDetection == 0 71 | startDetection = 1; 72 | startFlickerWin(j) = i; % Window# of flicker start 73 | end 74 | else 75 | true_label(i) = 0; 76 | startDetection = 0; 77 | alreadyDetected = 0; 78 | if i == IC_marker(j) + numWindowsInOneTrial 79 | j = j + 1; 80 | end 81 | end 82 | 83 | % pass chunks from SSVEPdataEpoch(,) to ccaResult 84 | % ccaResult returns the resIndex 85 | % sti_f_result(resIndex) goes into result(i) 86 | resIndex = ccaResult(SSVEPdataEpoch(:, :, i), refSignals(:, :, :)); 87 | result(i) = sti_f_ref(resIndex); 88 | 89 | if alreadyDetected == 0 && startDetection == 1 && all(result(i-4:i) == sti_f) && i-4 >= startFlickerWin(j) 90 | detectionWin(k) = i; 91 | alreadyDetected = 1; 92 | k = k + 1; 93 | end 94 | 95 | end 96 | 97 | %startFlickerWin 98 | %detectionWin 99 | compareTrueWithResult = [true_label', result']; 100 | 101 | 102 | % there can be a dimension mismatch between the number of detections and 103 | % IC_marker 104 | act_time = 0.1 * (detectionWin' - IC_marker(1:end-1)) + jumpTime 105 | % act_Time = 0.1 * (detectionWin'+20 - IC_marker(1:end-1)) 106 | 107 | -------------------------------------------------------------------------------- /actTime_FP.m: -------------------------------------------------------------------------------- 1 | clc; 2 | clear all; 3 | close all; 4 | 5 | %% 20 Hz 6 | sti_f_ref = 60 ./ [3:5, 7:8]; 7 | %file = 'ssvep-switch-train-20Hz-Shiva-[2017.01.12-19.08.41].gdf'; sti_f = 20; 8 | %file = 'ssvep-switch-train-20Hz-Shiva-[2017.01.12-20.39.58].gdf'; sti_f = 20; 9 | %file = 'ssvep-switch-train-20Hz-Shiva-[2017.01.12-20.47.47].gdf'; sti_f = 20; 10 | %file = 'ssvep-switch-train-20Hz-Shiva-[2017.01.12-20.59.43].gdf'; sti_f = 20; 11 | 12 | %% 15 Hz 13 | %sti_f_ref = 60 ./ [3:12]; 14 | %file = 'ssvep-switch-train-15Hz-Indra-train-[2016.12.07-16.03.31].gdf'; sti_f = 15; 15 | %file = 'ssvep-switch-train-15Hz-Shiva-[2017.01.10-21.18.54].gdf'; sti_f = 15; 16 | %file = 'ssvep-switch-train-15Hz-Shiva-[2017.01.10-21.33.50].gdf'; sti_f = 15; 17 | %file = 'ssvep-switch-train-15Hz-Shiva-[2017.01.12-19.59.36].gdf'; sti_f = 15; 18 | 19 | %% 12 Hz 20 | %sti_f_ref = 60 ./ [3:12]; 21 | %file = 'ssvep-switch-train-12Hz-Indra-train-[2016.12.07-15.11.04].gdf'; sti_f = 12; 22 | %file = 'ssvep-switch-train-12Hz-Shiva-[2017.01.10-20.45.21].gdf'; sti_f = 12; 23 | %file = 'ssvep-switch-train-12Hz-Shiva-[2017.01.12-20.24.08].gdf'; sti_f = 12; 24 | %file = 'ssvep-switch-train-12Hz-Shiva-[2017.01.12-19.16.35].gdf'; sti_f = 12; % Not Good 25 | %file = 'ssvep-switch-train-12Hz-Shiva-[2017.01.12-19.32.10].gdf'; sti_f = 12; 26 | %file = 'ssvep-switch-train-12Hz-Shiva-mag[2017.01.12-19.24.04].gdf'; sti_f = 12; % Not Good 27 | 28 | %% 29 | % 5s NC, 5s rest, 5s IC, 5s rest 30 | % 32779+33024/25 at the same time, 32780 after 5s 31 | %sti_f_ref = 60 ./ [3:12]; 32 | % 1 to 100 = Look for False Positives (NC) 33 | % 101 to 150 = Look for Detections (IC) 34 | % 151 to 170 = Classification NOT accounted. 35 | % 171 to 300 = Look for False Positives (NC) 36 | % 301 to 350 = Look for Detections (IC) 37 | % 351 to 370 = Classification NOT accounted. 38 | % 370 to 500 = Look for False Positives (NC) 39 | 40 | [s, h] = sload(file); 41 | fs = h.SampleRate; 42 | SSVEPdata = bandfiltfilt(s(h.EVENT.POS(2):end, :), fs, 4, 1, 40); 43 | numChannels = size(SSVEPdata, 2); 44 | 45 | 46 | stimCodes = [33024, 33025]; 47 | 48 | stimCodeSubset = find(ismember(h.EVENT.TYP, [33024, 33025])); 49 | 50 | h.EVENT.TYP = h.EVENT.TYP(stimCodeSubset); 51 | h.EVENT.POS = h.EVENT.POS(stimCodeSubset) - h.EVENT.POS(2) + 1; 52 | 53 | numTotalSamples = length(SSVEPdata); 54 | 55 | windowTime = 2; % in seconds 56 | jumpTime = 0.1; % in seconds 57 | jump = jumpTime * fs; %In samples, time in seconds * fs 58 | windowSize = windowTime * fs; %In samples 59 | %numWindows = 1 + floor((length(SSVEPdata) - windowSize) / jump); 60 | overlapFactor = 1 - (jumpTime / windowTime); 61 | 62 | 63 | for i = 1:numChannels 64 | SSVEPdataEpoch(i, :, :) = epoch(SSVEPdata(:, i), windowTime, fs, overlapFactor); 65 | end 66 | %SSVEPdataEpoch = 4 x 500 x 2994 67 | 68 | numWindows = size(SSVEPdataEpoch, 3); 69 | %skipSampleCount = fs; 70 | %skipSampleCount = 0; 71 | %firstIC_sampleStart = 10*fs + skipSampleCount + 1 ;% first IC start 2501+250 72 | %firstIC_sampleEnd = 15*fs - windowSize ;% first IC end 3750-500 73 | 74 | h.EVENT.WIN_NUM = 1 + (h.EVENT.POS - 1) / jump; 75 | 76 | % generate reference signals from 1 to 20 Hz. 77 | %sti_f_ref = 1:20; 78 | %sti_f_ref = 60 ./ [4:10, 14]; % use this only for 60/7 = 8.57 Hz flicker 79 | targetFlickerIndex = find(ismember(sti_f_ref, sti_f)); 80 | refSignals = ck_signal_windowed(sti_f_ref, windowTime, fs); 81 | 82 | IC_marker = h.EVENT.WIN_NUM(h.EVENT.TYP == 33025); 83 | IC_marker = [IC_marker; IC_marker(end) + IC_marker(2) - IC_marker(1)]; 84 | IC_endMarker = [IC_marker + 70]; 85 | 86 | %NC_marker = h.EVENT.WIN_NUM(h.EVENT.TYP == 33024); 87 | %NC_marker = [NC_marker; numWindows + 1]; 88 | 89 | % 130 Windows before the next IC 90 | NC_period_start = IC_marker - 130; 91 | 92 | 93 | % 5 is the IC time duration 94 | %numWindowsInOneTrial = (5 + windowTime) / jumpTime; %5 second trial, 2 second 95 | numWindowsInOneTrial = 5 / jumpTime; 96 | j = 1; 97 | k = 1; 98 | m = 1; 99 | n = 1; 100 | prevFP = 0; 101 | startDetection = 0; 102 | alreadyDetected = 0; 103 | falsePositive = []; 104 | %startFlicker(1) = 1; 105 | for i = 1:numWindows 106 | if i >= IC_marker(j) && i < IC_marker(j) + numWindowsInOneTrial 107 | true_label(i) = sti_f_ref(targetFlickerIndex); 108 | if alreadyDetected == 0 && startDetection == 0 109 | startDetection = 1; 110 | startFlickerWin(j) = i; % Window# of flicker start 111 | n = n + 1; % for False positive checking in NC period. Look for NC_period_start(n) 112 | end 113 | else 114 | % Place a zero in the detectionWin array if no detection was made 115 | if startDetection == 1 && alreadyDetected == 0 116 | detectionWin(k) = 0; 117 | k = k + 1; 118 | end 119 | true_label(i) = 0; 120 | startDetection = 0; 121 | alreadyDetected = 0; 122 | if i == IC_marker(j) + numWindowsInOneTrial + 20 123 | j = j + 1; 124 | end 125 | end 126 | 127 | % pass chunks from SSVEPdataEpoch(,) to ccaResult 128 | % ccaResult returns the resIndex 129 | % sti_f_result(resIndex) goes into result(i) 130 | resIndex = ccaResult(SSVEPdataEpoch(:, :, i), refSignals(:, :, :)); 131 | result(i) = sti_f_ref(resIndex); 132 | 133 | if alreadyDetected == 0 && startDetection == 1 && all(result(i-4:i) == sti_f) && i-4 >= startFlickerWin(j) 134 | detectionWin(k) = i; 135 | alreadyDetected = 1; 136 | k = k + 1; 137 | elseif i-4 > 0 && i-4 > NC_period_start(n) && startDetection == 0 && all(result(i-4:i) == sti_f) && prevFP < i-4 138 | if i ~= prevFP + 5 139 | falsePositive(m) = i; 140 | end 141 | prevFP = i; 142 | m = m + 1; 143 | end 144 | 145 | end 146 | 147 | %startFlickerWin 148 | %detectionWin 149 | compareTrueWithResult = [true_label', result']; 150 | 151 | falsePositive 152 | 153 | numFalsePositives = length(falsePositive(falsePositive > 0)); 154 | fprintf(1, 'Number of False Positives = %d\n\n', numFalsePositives); 155 | 156 | % Negative act_time means missed detection for that trial. 157 | act_time = 0.1 * (detectionWin - IC_marker(1:end-1)') + jumpTime 158 | 159 | numDetections = length(act_time(act_time > 0)); 160 | fprintf(1, 'Number of Detections = %d\n\n', numDetections); 161 | if(numDetections ~= 15) 162 | fprintf(1, 'Number of Detections missed = %d\n\n', 15 - numDetections); 163 | end 164 | --------------------------------------------------------------------------------