Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
d6db9d4
changed dimensionality index of trials
sbstnbosse Aug 8, 2018
40b88b7
added noise generation for mulitple trials
sbstnbosse Aug 8, 2018
987d0aa
considering more than one trial
sbstnbosse Aug 8, 2018
a1e28a3
added visualization in component space
sbstnbosse Aug 8, 2018
4e3ed2a
added pca
sbstnbosse Aug 10, 2018
6ae80a8
added ssd
sbstnbosse Aug 10, 2018
35656dc
added examples script
sbstnbosse Aug 10, 2018
7c6b2e2
update plot function and add between trials signal variation
EBarzegaran Aug 24, 2018
92c6546
update plot function
EBarzegaran Aug 24, 2018
7700fb4
improved visualization in component space
sbstnbosse Sep 11, 2018
f547aee
bugfix: corrected indices of frequency bands
sbstnbosse Sep 11, 2018
99f3bde
minor changes to improve speed
sbstnbosse Sep 12, 2018
18644e1
unified parameters for function
sbstnbosse Sep 17, 2018
13d8d69
unified function parameters
sbstnbosse Sep 17, 2018
7950568
corrected typo
sbstnbosse Sep 17, 2018
3d49970
initial commit
sbstnbosse Sep 17, 2018
788de29
examples for pca, ssd, csp and rca
sbstnbosse Sep 17, 2018
e4e2b76
bugfix: noise is now variant across trials
sbstnbosse Sep 17, 2018
a65c98a
preallocation of noise array
sbstnbosse Sep 17, 2018
4084996
removed debug output
sbstnbosse Sep 17, 2018
d1346b7
added keyboard control for component visualization
sbstnbosse Sep 17, 2018
362dd21
added W to calls of PlotEEG
sbstnbosse Sep 17, 2018
16f3c2c
Amplitude of the source signal should not change over trials
sbstnbosse Sep 24, 2018
8b8c9c9
added comment
sbstnbosse Sep 26, 2018
7f4fb3a
bugfix
sbstnbosse Oct 12, 2018
bdcf290
also return noise-free projection of the simulated signal
sbstnbosse Nov 5, 2018
be1c788
return projection of noise-free simulated signal
sbstnbosse Nov 5, 2018
481f308
minor bugfixes
sbstnbosse Nov 5, 2018
a486a8c
added svd-based decomposition for increased numerical stability and t…
sbstnbosse Nov 5, 2018
e561cd2
speedup by hemisphere-wise coherency of pink noise (assuming no coher…
sbstnbosse Nov 5, 2018
d09f4db
enable multiple interactive plots
sbstnbosse Nov 7, 2018
70cff72
do not calculate spatial distances of sources if not need (i.e. when …
sbstnbosse Nov 13, 2018
9acfbb8
added loadobj and saveobj to axx-class
sbstnbosse Nov 21, 2018
2098b33
minor change
sbstnbosse Nov 21, 2018
17c1358
added saveobj and loadobj methods to ROIs.m
sbstnbosse Nov 21, 2018
d4dcda1
correction for loadobj
sbstnbosse Nov 22, 2018
0f5264a
consistent normalization
sbstnbosse Nov 22, 2018
14e3cd3
some more checks on consistency of params
sbstnbosse Nov 22, 2018
51d7b2e
adding noise and signal in channel space
sbstnbosse Nov 22, 2018
1fc08b7
plotting function for simple scalp plots
sbstnbosse Nov 22, 2018
cdfc4da
definitions in channel space
sbstnbosse Nov 22, 2018
4438cfd
correction in normalization. Still not considering spectral normaliza…
sbstnbosse Nov 22, 2018
3736f21
simplified indexing of spectral components
sbstnbosse Dec 11, 2018
6e91571
some updates
sbstnbosse Dec 11, 2018
34b1db4
added script to test spatial filters
sbstnbosse Dec 11, 2018
69de919
fix a few things
EBarzegaran Dec 14, 2018
b119221
added option for no mixing on source level
Dec 19, 2018
61d05de
bugfix
Dec 19, 2018
44f8b60
bugfix
Dec 19, 2018
381ac92
added plotting
sebosse Dec 20, 2018
97766af
Add two source example for spatial filters
EBarzegaran Dec 20, 2018
e0b2b18
Merge branch 'spatial_filters' of https://github.com/svndl/mrC into s…
EBarzegaran Dec 20, 2018
88b1860
minor changes (allow for different folder setups and reduced number o…
sebosse Jan 4, 2019
9763f70
minor change to improve readability
sebosse Jan 4, 2019
78a4b88
corrected narrow-band normalization
sebosse Jan 4, 2019
1407a10
minor fixes
sebosse Jan 6, 2019
eadcd5c
changed range of input snr
sebosse Jan 6, 2019
0061731
aligned number of harmonics used to define input snr and to estimate …
sebosse Jan 8, 2019
14b8420
minor change for easier debugging
sebosse Jan 8, 2019
16d13ce
bugfix: removed factor 2 from snr calculation
sebosse Jan 8, 2019
cb15ae6
corrections for residual calculation
sebosse Jan 9, 2019
e1bc834
corrections in values of coherence; aligned data structure of coheren…
sebosse Jan 24, 2019
5cbcd51
more appropriate fitting functions for estimating spatial decay of co…
sebosse Jan 29, 2019
8f17ed9
spatial coherence models base on fig.3 supplementary in kellis et al.…
sebosse Feb 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion +mrC/+Simulate/CreateAxx.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
%==========================================================

% Author: Elham Barzegaran, 3/26/2018
% modified by sebastian bosse 8/4/2018

%%
EEGAxx = mrC.axx();
Expand Down Expand Up @@ -47,7 +48,7 @@
EEGAxx.dTms = 1000/opt.signalsf;

% calculate spectrum
EEGAxx.nTrl = size(EEGAxx.Wave,4);
EEGAxx.nTrl = size(EEGAxx.Wave,3);
NumEp = floor(size(EEGData,1)/WLF);
EEGSPEC = fft(squeeze(permute(mean(reshape(EEGData(1:NumEp*WLF,:),[WLF NumEp size(EEGData,2) size(EEGData,3)]),2),[1 3 2 4])),WLF,1);
f = opt.signalsf*(0:(WLF/2))/WLF; % frequencies
Expand Down
37 changes: 31 additions & 6 deletions +mrC/+Simulate/GenerateMixingData.m
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
function noise_mixing_data = GenerateMixingData(spat_dists)
function noise_mixing_data = GenerateMixingData(spat_dists, decomp_algo)
% Syntax: noise_mixing_data = GenerateMixingData(spat_dists)
% Description: This function get the spatial distance between sources and loads the
% spatial decay model for coherenc and generates the mixing matrix for noise
% see 10.1121/1.2987429.
% We use the Cholesky decomposition due to complexity considerations. This
% is appropriate as all sources have (approx.) identical PSD and no
% 'perceptual effects' are expected

%% --------------------------------------------------------------------------

if ~exist('decomp_algo','var') || isempty(decomp_algo)
decomp_algo = 'cholesky';
end


% preparing mixing matrices
load('spatial_decay_models_coherence')% this is located in simulate/private folder, it can be obtained by run the code 'spatial_decay_of_coherence.m'

Expand All @@ -15,15 +24,31 @@
noise_mixing_data.band_freqs = band_freqs ;
noise_mixing_data.mixing_type = 'coh' ;

% we are assuming isolation between hemispheres: calculate mixing
% matrices seperately

hWait = waitbar(0,'Calculating mixing matrices ... ');

for freq_band_idx = 1:length(band_freqs)
this_spatial_decay_model = best_model.(band_names{freq_band_idx}) ;
this_coh = this_spatial_decay_model.fun(this_spatial_decay_model.model_params,spat_dists);
this_coh = min(max(this_coh,0),1) ;
mixing_data = chol(this_coh) ; % this matrix should become sparse, to be saved

noise_mixing_data.matrices{freq_band_idx} = sparse(mixing_data .* (mixing_data>0.01)); %make the matrix sparse for saving
mixing_matrix = zeros(size(spat_dists,1),size(spat_dists,2)/2) ;
for hemisphere_idx = 1:2
this_spat_dists = spat_dists((hemisphere_idx-1)*size(spat_dists,1)/2+(1:size(spat_dists,1)/2),...
(hemisphere_idx-1)*size(spat_dists,2)/2+(1:size(spat_dists,2)/2));
this_coh = this_spatial_decay_model.fun(this_spatial_decay_model.model_params,this_spat_dists);
this_coh = min(max(this_coh,0),1) ;

if strcmpi(decomp_algo,'cholesky')
this_mixing_matrix = chol(this_coh) ;
elseif strcmpi(decomp_algo,'eigenvalue')
[V,D] =eig(this_coh);
this_mixing_matrix = sqrt(D)*V' ;
else
error('decomposition method not implemented')
end
mixing_matrix((hemisphere_idx-1)*size(spat_dists,1)/2+(1:size(spat_dists,1)/2),:) = this_mixing_matrix;
end
noise_mixing_data.matrices{freq_band_idx} = mixing_matrix;
waitbar(freq_band_idx/length(band_freqs));
end
close(hWait);
Expand Down
140 changes: 108 additions & 32 deletions +mrC/+Simulate/GenerateNoise.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function [noise, pink_noise, pink_noise_uncoh, alpha_noise] = GenerateNoise(f_sampling, n_samples, n_nodes, mu, alpha_nodes, noise_mixing_data, spatial_normalization_type)
% Syntax: [noise, pink_noise, pink_noise_uncoh, alpha_noise] = GenerateNoise(f_sampling, n_samples, n_nodes, mu, alpha_nodes, noise_mixing_data, spatial_normalization_type)
function [noise, pink_noise, alpha_noise,sensor_noise] = GenerateNoise(f_sampling, n_samples, n_nodes, NoiseParams, noise_mixing_data, spatial_normalization_type,fwdMatrix)
% Syntax: [noise, pink_noise, pink_noise_uncoh, alpha_noise] = GenerateNoise(f_sampling, n_samples, n_nodes, mu, alpha_nodes, noise_mixing_data, spatial_normalization_type,fwdMatrix)
% Desciption: GENERATE_NOISE Returns noise of unit variance as a combination of alpha
% activity (bandpass filtered white noise) and spatially coherent pink
% noise (spectrally shaped white noise)
Expand All @@ -15,6 +15,10 @@
% different noises to
% 'all_nodes': normalize to total number of nodes
% 'active_nodes': normalize only to nodes where a specific noise has any activity
% fwdMatrix: Matrix of forward model. If given,
% noise is return projected to channel
% space. This reduces computational
% complexity in during noise generation
% OUTPUT:
% noise: returns a matrix of size [n_samples,n_nodes]
% pink_noise
Expand All @@ -25,75 +29,148 @@

%% ---------------------------- generate alpha noise------------------------
%
if ~exist('fwdMatrix','var')|| isempty(fwdMatrix)
doFwdProjection = false;
else
doFwdProjection = true ;
end

alpha_noise = zeros(n_samples,n_nodes);
alpha_noise(:,alpha_nodes) = repmat(GetAlphaActivity(n_samples,f_sampling,[8,12]),[1,length(alpha_nodes )]);
alpha_noise(:,NoiseParams.AlphaSrc) = repmat(GetAlphaActivity(n_samples,f_sampling,[8,12]),[1,length(NoiseParams.AlphaSrc )]);

if doFwdProjection
alpha_noise = alpha_noise*fwdMatrix' ;
end

if strcmp(spatial_normalization_type,'active_nodes')
if doFwdProjection
warning('spatial normalization with active nodes does not really make sense in channel space!')
end
n_active_nodes_alpha = sum(sum(abs(alpha_noise))~=0) ;
alpha_noise = n_active_nodes_alpha*alpha_noise/norm(alpha_noise,'fro') ;
elseif strcmp(spatial_normalization_type,'all_nodes')
alpha_noise = alpha_noise/norm(alpha_noise,'fro') ;

else
error('%s is not implemented as spatial normalization method', spatial_normalization_type)
end



%% -----------------------------generate pink noise------------------------
pink_noise = GetPinkNoise(n_samples, n_nodes );pink_noise_uncoh = pink_noise;

pink_noise = GetPinkNoise(n_samples, n_nodes);
% impose coherence on pink noise
if strcmp(noise_mixing_data.mixing_type,'coh') % just in case we want to add other mixing mechanisms
% force noise to be spatially coherent within 'hard' frequency
% ranges
% for details see: DOI: 10.1121/1.2987429
f = [-0.5:1/n_samples:0.5-1/n_samples]*f_sampling; % frequncy range

% for details see: DOI:10.1121/1.2987429
f = fftshift([-0.5:1/n_samples:0.5-1/n_samples]*f_sampling); % frequncy range
pink_noise_spec = fft(pink_noise,[],1);

if doFwdProjection
pink_noise_spec_coh = zeros(size(pink_noise_spec,1), size(fwdMatrix,1)) ;
else
pink_noise_spec_coh = zeros(size(pink_noise_spec));
end
for band_idx = 1:length(noise_mixing_data.band_freqs)
% calc coherence for band
C = noise_mixing_data.matrices{band_idx};
freq_bin_idxs = (noise_mixing_data.band_freqs{band_idx}(1)<abs(f))&(abs(f)<noise_mixing_data.band_freqs{band_idx}(2));
pink_noise_spec(freq_bin_idxs,:) = (C' * pink_noise_spec(freq_bin_idxs,:)')';
if doFwdProjection
C = noise_mixing_data.matrices_chanSpace{band_idx};
else
C = noise_mixing_data.matrices{band_idx};
end
freq_bin_idxs = (noise_mixing_data.band_freqs{band_idx}(1)<=abs(f))&(abs(f)<noise_mixing_data.band_freqs{band_idx}(2));
for hemi = 1:2 % hemisphere by hemisphere

if doFwdProjection
source_idxs = (hemi-1)*size(C,1)/2+1:hemi*size(C,1)/2 ;
pink_noise_spec_coh(freq_bin_idxs,:) = pink_noise_spec_coh(freq_bin_idxs,:)+pink_noise_spec(freq_bin_idxs,source_idxs)*C(source_idxs,:);
else
source_idxs = (hemi-1)*size(C,2)+1:hemi*size(C,2) ;
pink_noise_spec_coh(freq_bin_idxs,source_idxs) = pink_noise_spec(freq_bin_idxs,source_idxs)*C(source_idxs,:);
end
end
end

pink_noise = real(ifft(pink_noise_spec,[],1));
else
pink_noise = real(ifft(pink_noise_spec_coh,[],1));
elseif ~strcmp(noise_mixing_data.mixing_type,'none')
error('%s is not implemented as a mixing method',noise_mixing_data.mixing_type)
end

if false
close all
n_samples = size(pink_noise_spec_coh) ;
normed_pink_noise_spec_coh = pink_noise_spec_coh/norm(pink_noise_spec_coh,'fro');
normed_pink_noise_spec = pink_noise_spec*fwdMatrix';
normed_pink_noise_spec = normed_pink_noise_spec/norm(normed_pink_noise_spec,'fro') ;
loglog(mean(abs(normed_pink_noise_spec(1:n_samples/2,:)).^2,2),'b')
hold on
loglog(mean(abs(normed_pink_noise_spec_coh(1:n_samples/2,:)).^2,2),'r')



end

if strcmp(spatial_normalization_type,'active_nodes')
if doFwdProjection
warning('spatial normalization with active nodes does not really make sense in channel space!')
end
n_active_nodes_pink = sum(sum(abs(pink_noise))~=0) ;
pink_noise = n_active_nodes_pink * pink_noise/norm(pink_noise,'fro') ;
elseif strcmp(spatial_normalization_type,'all_nodes')
pink_noise = pink_noise/norm(pink_noise,'fro') ;
else
error('%s is not implemented as spatial normalization method', spatial_normalization_type)
end

sensor_noise = randn(n_samples, size(fwdMatrix,1)) ;
sensor_noise = sensor_noise/norm(sensor_noise,'fro');
%% --------------------combine different types of noise--------------------
noise = sqrt(mu/(1+mu))*pink_noise + sqrt(1/(1+mu))*alpha_noise ;
noise = noise/norm(noise,'fro') ;% pink noise and alpha noise are correlated randomly. dirty hack: normalize sum
norm_factor = sqrt(NoiseParams.mu.pink^2+NoiseParams.mu.alpha^2+NoiseParams.mu.sensor^2) ;
noise = NoiseParams.mu.pink/norm_factor*pink_noise + NoiseParams.mu.alpha/norm_factor*alpha_noise + NoiseParams.mu.sensor/norm_factor*sensor_noise ;
noise = noise/norm(noise,'fro') ;
%% ---------------------------show resulting noise-------------------------
if false % just to take a look at the noise components
if false % just to take a look at the noise components, averaged over all channels for power spectrum
f = [-0.5:1/n_samples:0.5-1/n_samples]*f_sampling; % frequncy range
t = [0:n_samples-1]/f_sampling ;
subplot(3,2,1)
subplot(4,3,1)
title('pink noise')
plot(t, pink_noise(:,1:50:end));xlim([0 2]);
subplot(3,2,2)
plot(f, abs(fftshift(fft(pink_noise(:,1:50:end)))));xlim([0 max(f)]);
subplot(4,3,2)
plot(f, mean(abs(fftshift(fft(pink_noise))),2) );xlim([0 max(f)]);
subplot(4,3,3)
loglog(f, mean(abs(fftshift(fft(pink_noise))),2) );xlim([0 max(f)]);
%ylim([0 .2]);

subplot(3,2,3)
subplot(4,3,4)
title('alpha noise')
plot(t, alpha_noise(:,1:50:end));xlim([0 2]);
subplot(3,2,4)
plot(f, abs(fftshift(fft(alpha_noise(:,1:50:end)))));xlim([0 max(f)]);
subplot(4,3,5)
plot(f, mean(abs(fftshift(fft(alpha_noise))),2) );xlim([0 max(f)]);
subplot(4,3,6)
loglog(f, mean(abs(fftshift(fft(alpha_noise))),2) );xlim([0 max(f)]);

%plot(f, abs(fftshift(fft(alpha_noise(:,1:50:end)))));xlim([0 max(f)]);
%ylim([0 .2]);


subplot(4,3,7)
title('sensor noise')
plot(t, sensor_noise(:,1:50:end));xlim([0 2]);
subplot(4,3,8)
plot(f, abs(fftshift(fft(sensor_noise(:,1:50:end)))));xlim([0 max(f)]);
subplot(4,3,9)
loglog(f, mean(abs(fftshift(fft(sensor_noise))),2) );xlim([0 max(f)]);

subplot(3,2,5)

subplot(4,3,10)
title(sprintf('combined noise with [%1.2f, %1.2f, %1.2f]', NoiseParams.mu.pink, NoiseParams.mu.alpha, NoiseParams.mu.sensor));
plot(t, noise(:,1:50:end)); xlim([0 2]);
subplot(3,2,6)
plot(f, abs(fftshift(fft(noise(:,1:50:end)))));xlim([0 max(f)]);
%ylim([0 .2]);
subplot(4,3,11)
plot(f, mean(abs(fftshift(fft(noise))),2) );xlim([0 max(f)]);
subplot(4,3,12)
loglog(f, mean(abs(fftshift(fft(noise))),2) );xlim([0 max(f)]);

end
end

Expand All @@ -115,9 +192,9 @@
[b,a] = butter(3, freq_band/sampling_freq*2);
y = filter(b,a, x);

% ensure zero mean value
y = y - repmat(mean(y),[n_samples,1]) ;

%normalize to unit variance
y = y./sqrt(mean(abs(y).^2));
end

function pink_noise = GetPinkNoise(n_samples,n_nodes)
Expand All @@ -130,10 +207,9 @@
M = n_samples + rem(n_samples,2) ;
n = 1:M ;
scalings = sqrt(1./n);
scalings = repmat(scalings,[n_nodes,1])';

noise_spec = fft(randn(M,n_nodes)).*scalings ;
noise_spec = fft(randn(M,n_nodes)).*scalings' ;
pink_noise = real(ifft(noise_spec)) ;
pink_noise = pink_noise(1:n_samples,:) ;
pink_noise = pink_noise./norm(pink_noise,'fro');
end

Loading