├── .gitignore ├── plot_hht.m ├── README.md ├── simulate.m └── emd_time_frequency_tutorial.m /.gitignore: -------------------------------------------------------------------------------- 1 | dataFIC.mat 2 | -------------------------------------------------------------------------------- /plot_hht.m: -------------------------------------------------------------------------------- 1 | function plot_hht( imf, sample_rate, ax, max_freq ) 2 | %% function plot_hht( imf, sample_rate, ax, max_freq ) 3 | % 4 | % A function to compute and plot Hilbert-Huang Transforms 5 | % based on signalwavelet.internal.guis.plot.hhtPlot 6 | 7 | %% 8 | % Housekeeping variables 9 | if nargin < 4 || isempty(max_freq) 10 | max_freq = 75; 11 | end 12 | 13 | if nargin < 3 || isempty(ax) 14 | figure 15 | subplot(111); 16 | ax = gca; 17 | hold on 18 | end 19 | 20 | seconds = size(imf,1)/sample_rate; 21 | time = linspace(0,seconds,seconds*sample_rate); 22 | 23 | %% 24 | % compute instantaneous stats 25 | [~,~,~,insf,inse] = hht(imf,sample_rate,'FrequencyLimits',[1,max_freq],'FrequencyResolution',.5); 26 | 27 | %% 28 | % Plot HHT 29 | for ii = 1:size(insf,2) 30 | patch( ax,[nan;time';nan],[nan;insf(:,ii);nan],[nan;sqrt(inse(:,ii));nan],... 31 | 'EdgeColor','interp','EdgeAlpha','interp',... 32 | 'FaceColor', 'none', 'FaceVertexAlphaData',[nan;sqrt(inse(:,ii));nan],... 33 | 'LineWidth', 2, 'FaceAlpha', 'interp'); 34 | end 35 | 36 | ylim(ax,[0 max_freq]); 37 | xlim(ax,[0,seconds]); 38 | xlabel('Time (seconds)') 39 | ylabel('Frequency (Hz)') 40 | colorbar('North') 41 | grid on 42 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Time-Frequency analysis with Empirical Mode Decomposition 2 | 3 | This tutorial introduces two concepts: 4 | 1) High-resolution time-frequency analysis using Instantaneous Frequency 5 | estimation 6 | 2) Separation of a complicated signal into simpler oscillatory 7 | components. 8 | 9 | Taken together, these steps allow us to compute time-frequency plots at a 10 | much higher resolution than available with most standard methods. 11 | 12 | Here, we will analyse at some simulations and some example data from the 13 | fieldtrip toolbox 14 | 15 | # Getting started 16 | 17 | To run this tutorial you will need to be running matlab R2018a or more 18 | recent with the signal processing toolbox. 19 | The Empirical Mode Decomposition functions used here are not available 20 | in older versions. 21 | 22 | Next, you will need a recent version of Fieldtrip on the matlab path. 23 | Fieldtrip and a host of tutorials are available here: 24 | 25 | http://www.fieldtriptoolbox.org 26 | 27 | Fieldtrip is used to compute a Hanning-Taper time-frequency transform for 28 | comparison with the Empirical Mode Decomposition. 29 | 30 | Finally, the ```simulate.m``` function provided here is an abridged version of the simulation 31 | functions in the CFC toolbox. For more details see: 32 | 33 | https://github.com/AJQuinn/cfc 34 | 35 | # The Tutorial 36 | 37 | To run the tutorial, open ```emd_time_frequency_tutorial.m``` in MatLab and run 38 | through the script one cell at a time. 39 | -------------------------------------------------------------------------------- /simulate.m: -------------------------------------------------------------------------------- 1 | function obj = simulate(S) 2 | %%function obj = cfc.simulate(S) 3 | % Generate a simulated signal with a known cross frequency coupling 4 | % 5 | % Abridged version of the function available in the CFC toolbox 6 | % https://github.com/AJQuinn/cfc 7 | % https://github.com/AJQuinn/cfc/blob/master/%2Bcfc/simulate.m 8 | % 9 | % Requried fields in S 10 | % 11 | % S.seconds - Duration of signal 12 | % S.sample_rate - Sample rate of signal 13 | % S.modulating_freq - Frequency of low frequency modulating signal (in Hz) 14 | % S.modulating_amp - Amplitude of low frequency modulating signal (default = 2) 15 | % S.modulated_freq - Frequency of high frequency modulating signal (in Hz) 16 | % S.modulated_amp - Amplitude of high frequency modulating signal (default = 1) 17 | % S.phase_lag - Phase lag between modulating signal and modulated envelope (default = 0) 18 | % S.method - Method for generating signal (Default = 'basic') 19 | % S.noise_ratio - Noise in dB (default = -40) 20 | 21 | %% set defaults 22 | if ~isfield(S,'modulating_amp') 23 | S.modulating_amp = 2; 24 | end 25 | 26 | if ~isfield(S,'modulated_amp') 27 | S.modulated_amp = 1; 28 | end 29 | 30 | if ~isfield(S,'phase_lag') 31 | S.phase_lag = 0; % Lock to peak of theta wave 32 | end 33 | 34 | if ~isfield(S,'noise_level') 35 | S.noise_level = -50; 36 | end 37 | 38 | if ~isfield(S,'method') 39 | S.method = 'basic'; 40 | end 41 | 42 | %% Housekeeping 43 | obj.time_vect = [0:1/S.sample_rate:S.seconds]; 44 | obj.sr = S.sample_rate; 45 | 46 | if isfield(S, 'switching_freq') 47 | obj.switching_freq = S.switching_freq; 48 | state_switching = square(sin(2*pi*S.switching_freq*obj.time_vect)); 49 | else 50 | S.switching_freq = 0; 51 | state_switching = zeros(size(obj.time_vect)); 52 | end 53 | 54 | if ~isfield(S,'randseed') 55 | rng('shuffle'); 56 | end 57 | 58 | %% Development note 59 | % 60 | % Each function below should populate obj with a modulated and a modulating 61 | % time-series along with any relevant state switching. These will be combined 62 | % along with any scaled noise at the end. 63 | % 64 | % obj.modulating_ts 65 | % obj.modulated_ts 66 | % 67 | % Any additional arguments required for the function should be commented here 68 | 69 | %% Main loop 70 | if strcmp(S.method,'none') 71 | % two uncoupled signals, tonic low and high frequency oscillations 72 | 73 | obj.modulated_ts = S.modulated_amp*sin(2*pi*S.modulated_freq*obj.time_vect); 74 | obj.modulating_ts = S.modulating_amp*sin(2*pi*S.modulating_freq*obj.time_vect); 75 | 76 | % ensure this is tonic 77 | state_switching = ones(size(modulating_ts,1),size(modulating_ts,2)); 78 | 79 | obj.modulating_ts = obj.modulating_ts.*state_switching; 80 | 81 | elseif strcmp(S.method,'basic') 82 | % A simple phase amplitude coupling between low and high frequency signals 83 | 84 | obj.modulated_ts = S.modulated_amp*sin(2*pi*S.modulated_freq*obj.time_vect); 85 | obj.modulating_ts = S.modulating_amp*sin(2*pi*S.modulating_freq*obj.time_vect); 86 | 87 | state_switching = ( 1 + square(sin(2*pi*S.switching_freq*obj.time_vect)) ) / 2; 88 | 89 | % Create modulation 90 | am = sin(2*pi*S.modulating_freq*obj.time_vect + S.phase_lag) + 1; 91 | obj.am = am; 92 | am = ( am ./ max(am) ) .* state_switching; 93 | am(state_switching == 0) = 1; 94 | 95 | % modulate signal 96 | obj.modulated_ts = (obj.modulated_ts.*am); 97 | 98 | elseif strcmp(S.method,'Abreu2010') 99 | % An analytical formulation of a nonlinear near-bed wave 100 | % 101 | % The degree of nonlinearity is specified by S.nonlin_deg which is strictly [-1 < x < 1] 102 | % 103 | % Analytical approximate wave form for asymmetric waves - Abreu et al 2010 104 | % http://doi.org/10.1016/j.coastaleng.2010.02.005 105 | % Equation 7. 106 | 107 | if ~isfield(S,'nonlin_deg') 108 | error('Please make sure S.nonlin_deg is defined to use this simulation') 109 | end 110 | 111 | if abs(S.nonlin_deg) >= 1 112 | warning(sprintf('Please ensure that S.nonlin_deg is [-1 < x < 1], current setting is S.nonlin_deg = %d',S.nonlin_deg)) 113 | end 114 | 115 | if ~isfield(S,'nonlin_phi') 116 | S.nonlin_phi = -pi/2; 117 | end 118 | 119 | % create nonlinear low frequency signal 120 | factor = sqrt(1-S.nonlin_deg.^2); 121 | num = sin(2*pi*S.modulating_freq*obj.time_vect) + ( S.nonlin_deg*sin(S.nonlin_phi) / (1+sqrt(1-S.nonlin_deg^2)) ); 122 | denom = 1- S.nonlin_deg*cos(2*pi*S.modulating_freq.*obj.time_vect + S.nonlin_phi); 123 | obj.modulating_ts = S.modulating_amp.*factor.*( num ./ denom ); 124 | 125 | % create simple high frequency signal 126 | obj.modulated_ts = S.modulated_amp*sin(2*pi*S.modulated_freq*obj.time_vect); 127 | 128 | state_switching = ( 1 + square(sin(2*pi*S.switching_freq*obj.time_vect)) ) / 2; 129 | 130 | % Create modulation 131 | am = sin(2*pi*S.modulating_freq*obj.time_vect + S.phase_lag) + 1; 132 | am(am < 0) = 0; 133 | obj.am = am; 134 | am = ( am ./ max(am) ) .* state_switching; 135 | am(state_switching == 0) = 1; 136 | 137 | % modulate signal 138 | obj.modulated_ts = (obj.modulated_ts.*am); 139 | 140 | elseif strcmp(S.method,'Abreu2010am') 141 | % An analytical formulation of a nonlinear near-bed wave 142 | % 143 | % The degree of nonlinearity is specified by S.nonlin_deg which is strictly [-1 < x < 1] 144 | % 145 | % The direction of skewness is determined by nonlin_phi, pi/2 is a 146 | % central. 147 | % 148 | % Analytical approximate wave form for asymmetric waves - Abreu et al 2010 149 | % http://doi.org/10.1016/j.coastaleng.2010.02.005 150 | % Equation 7. 151 | 152 | if ~isfield(S,'nonlin_deg') 153 | error('Please make sure S.nonlin_deg is defined to use this simulation') 154 | end 155 | 156 | if abs(S.nonlin_deg) >= 1 157 | warning(sprintf('Please ensure that S.nonlin_deg is [-1 < x < 1], current setting is S.nonlin_deg = %d',S.nonlin_deg)) 158 | end 159 | 160 | if ~isfield(S,'nonlin_phi'); 161 | S.nonlin_phi = -pi/2; 162 | end 163 | 164 | obj.modulating_ts = S.modulating_amp*sin(2*pi*S.modulating_freq*obj.time_vect); 165 | 166 | % create simple high frequency signal 167 | obj.modulated_ts = S.modulated_amp*sin(2*pi*S.modulated_freq*obj.time_vect); 168 | 169 | state_switching = ( 1 + square(sin(2*pi*S.switching_freq*obj.time_vect)) ) / 2; 170 | 171 | % Create modulation 172 | % create nonlinear amplitude modulation signal 173 | factor = sqrt(1-S.nonlin_deg.^2); 174 | num = sin(2*pi*S.modulating_freq*obj.time_vect) + ( S.nonlin_deg*sin(S.nonlin_phi) / (1+sqrt(1-S.nonlin_deg^2)) ); 175 | denom = 1- S.nonlin_deg*cos(2*pi*S.modulating_freq.*obj.time_vect + S.nonlin_phi); 176 | am = -factor.*( num ./ denom ); 177 | 178 | am = am + abs(min(am)).*S.modulated_amp; 179 | am(am < 0) = 0; 180 | obj.am = am; 181 | am = ( am ./ max(am) ) .* state_switching; 182 | am(state_switching == 0) = 1; 183 | 184 | % modulate signal 185 | obj.modulated_ts = (obj.modulated_ts.*am); 186 | 187 | elseif strcmp(S.method,'modal') 188 | % Generate a phase-amplitude-coupling signal between an autoregressive 189 | % oscillator and a high frequency signal. The low frequency signal has 190 | % a bandwidth introducing more realistic features such as envelope 191 | % dynamics. 192 | 193 | % Define AR oscillator 194 | r = .995; 195 | wr = 2*pi*S.modulating_freq/S.sample_rate; 196 | a1 = [1 -2*r*cos(wr) (r^2)]; 197 | 198 | time_vect = 0:1/S.sample_rate:S.seconds; 199 | obj.modulating_ts = randn(1,length(time_vect)); 200 | 201 | % Generate signal from model and parameters 202 | for idx = 4:length(time_vect) 203 | for ilag = 2:3 204 | obj.modulating_ts(idx) = obj.modulating_ts(idx) - squeeze(a1(ilag))*obj.modulating_ts(idx-ilag+1); 205 | end 206 | end 207 | 208 | % Normalise 209 | obj.modulating_ts = ( obj.modulating_ts - mean(obj.modulating_ts) ) / std(obj.modulating_ts); 210 | 211 | % Generate modulated signal 212 | obj.modulated_ts = S.modulated_amp.*sin(2*pi*S.modulated_freq*obj.time_vect); 213 | 214 | state_switching = ( square(sin(2*pi*S.switching_freq*obj.time_vect))+1 )/ 2; 215 | 216 | % Modulate high frequency amplitude by magnitude of low frequency 217 | t = obj.modulating_ts + 2; 218 | t(t<0) = 0; 219 | t = t./max(t); 220 | obj.modulated_ts = (obj.modulated_ts.*t.*state_switching); 221 | 222 | else 223 | warning('S.method not recognised'); 224 | end 225 | 226 | 227 | %% Add noise scaled to the modulating time-series and return 228 | obj.noise = scalesignal(randn(size(obj.modulated_ts,1),size(obj.modulated_ts,2)),... 229 | S.noise_level,... 230 | obj.modulated_ts+zscore(obj.modulating_ts)); 231 | 232 | obj.signal = obj.modulated_ts + zscore(obj.modulating_ts) + obj.noise; 233 | 234 | obj.state_switching = state_switching; 235 | 236 | end 237 | 238 | 239 | function [signal,resig_db,accuracy] = scalesignal(signal, diff_db, ref_signal); 240 | 241 | % Get original signal power 242 | sig_db = 20 * log10( sqrt ( sum ( power(signal,2) ) ) ); 243 | 244 | % Calculate actual dB scaling if passed a reference signal 245 | if nargin == 3 246 | ref_db = 20 * log10( sqrt ( sum ( power(ref_signal,2) ) ) ); 247 | diff_db = ref_db - sig_db + diff_db; 248 | end 249 | 250 | % Calculate scaling factor 251 | scale_factor = 10 ^ ( diff_db / 20); 252 | 253 | % Scale signal 254 | signal = scale_factor * signal; 255 | 256 | % Calculate scaled signal db 257 | resig_db = 20 * log10( sqrt ( sum ( power(signal,2) ) ) ); 258 | 259 | % Estimate accuracy 260 | accuracy = (sig_db - diff_db) / resig_db; 261 | end 262 | 263 | -------------------------------------------------------------------------------- /emd_time_frequency_tutorial.m: -------------------------------------------------------------------------------- 1 | %% Time-Frequency analysis with Empirical Mode Decomposition 2 | % 3 | % This tutorial introduces two concepts: 4 | % 1) High-resolution time-frequency analysis using Instantaneous Frequency 5 | % estimation 6 | % 2) Separation of a complicated signal into simpler oscillatory 7 | % components. 8 | % 9 | % Taken together, these steps allow us to compute time-frequency plots at a 10 | % much higher resolution than available with most standard methods. 11 | % 12 | % Here we will analyse at some simulations and some example data using the 13 | % fieldtrip toolbox 14 | 15 | %% Getting started 16 | % 17 | % To run this tutorial you will need to be running matlab R2018a or more 18 | % recent with the signal processing toolbox. 19 | % The Empirical Mode Decomposition functions used here are not available 20 | % in older versions. 21 | % 22 | %---------- 23 | % 24 | % Next, you will need a recent version of Fieldtrip on the matlab path. 25 | % Fieldtrip and a host of tutorials are available here: 26 | % 27 | % http://www.fieldtriptoolbox.org 28 | % 29 | % Fieldtrip is used to compute a Hanning-Taper time-frequency transform for 30 | % comparison with the Empirical Mode Decomposition. 31 | % 32 | %---------- 33 | % 34 | % Finally, the simulate.m function provided here is an abridged 35 | % version of the simulation functions in the CFC toolbox. For more details see: 36 | % 37 | % https://github.com/AJQuinn/cfc 38 | 39 | %% Getting Started 40 | % 41 | % Add relevant toolboxes to path 42 | 43 | addpath /path/to/fieldtrip % Update to match your folder structure 44 | addpath /path/to/emd_hht_tutorial % Update to match your folder structure 45 | 46 | %% Sanity Checking 47 | % 48 | % If you can run this cell with no warnings you should be good to go! 49 | % 50 | % If you get warnings, they should help identify what you are missing. 51 | 52 | % Is matlab recent enough? 53 | if verLessThan('matlab','9.4') 54 | warning('Matlab is older than R2018a - the tutorial might not run...'); 55 | end 56 | 57 | % Do we have fieldtrip? 58 | if isempty( which('ft_freqanalysis') ) 59 | warning('Fieldtrip function is missing!'); 60 | end 61 | 62 | % Do we have the signal processing toolbox and EMD functions? 63 | if ~license( 'test', 'Signal_Toolbox' ) 64 | warning('Signal Processing Toolbox not found - the tutorial might not run...'); 65 | end 66 | 67 | if isempty( which('emd') ) 68 | warning('emd function is missing!'); 69 | end 70 | if isempty( which('hht') ) 71 | warning('hht function is missing!'); 72 | found = false; 73 | end 74 | 75 | 76 | %% Some simulations 77 | % 78 | % Here we will generate some simulated time-series to look at in the 79 | % frequency domain. 80 | % 81 | % simple_signal contains a simple sinusoidal 10Hz modulating signal with a 82 | % 53Hz amplitude modulated signal 83 | % 84 | % nonlin_signal is similar to simple_signal but the 10Hz oscillation has a 85 | % non-sinusoidal waveform shape 86 | % 87 | % nonstat_signal is similar to simple_signal but the 10Hz oscillation has 88 | % quickly changing dynamics 89 | 90 | % Define a simple signal 91 | S = []; 92 | S.sample_rate = 1024; 93 | S.seconds = 1; 94 | S.method = 'basic'; 95 | S.modulating_freq = 10; 96 | S.modulated_freq = 53; 97 | simple_signal = simulate(S); 98 | 99 | % Define a signal with a non-sinusoidal low frequency component 100 | S.method = 'Abreu2010'; 101 | S.nonlin_deg = .5; 102 | nonlin_signal = simulate(S); 103 | 104 | % Define a signal with a non-stationary low frequency component 105 | % this signal will be different each time unless a random seed is set. 106 | S.method = 'modal'; 107 | S.nonlin_deg = .5; 108 | nonstat_signal = simulate(S); 109 | 110 | % Concatenate the low and high frequency components of each signal 111 | imf1 = [simple_signal.modulated_ts; simple_signal.modulating_ts]'; 112 | imf2 = [nonlin_signal.modulated_ts; nonlin_signal.modulating_ts]'; 113 | imf3 = [nonstat_signal.modulated_ts; nonstat_signal.modulating_ts]'; 114 | 115 | %% Hilbert-Huang Transform 116 | % 117 | % Here we will compute and plot the Hilbert-Huang Transform for each signal 118 | % using the plot_hht function. This calls hht internally and creates a 119 | % simple visualisation. Note, these plots show amplitude rather than 120 | % power (power = amplitude.^2) 121 | % 122 | % The HHT plot is a sparse distribution of the instantaneous amplitude and 123 | % frequency of each component in the signal. The instantaneous amplitude (IA) 124 | % is computed from the abs of the Hilbert Transform. The instantaneous 125 | % frequency (IF) is computed from the derivative of the instataneous phase. 126 | 127 | % We compute a value for each signal at every time-point. Next we build a 128 | % sparse matrix with an entry in every column for each signal component 129 | % (here we have 2 components). We define a set of frequency bins and enter 130 | % each IA value into the bin corresponding to its IF value. 131 | % 132 | % 133 | % The first signal shows constant amplitude at 10Hz and modulated power at 53Hz 134 | % 135 | % The second signal shows rapid frequency variation in 10Hz component. The 136 | % HHT has sufficient resolution to resolve frequency variation within 137 | % single cycles. The modulation in frequency within cycles relates to which 138 | % parts of the cycle are progressing faster or slower. This signal has a 139 | % narrow/fast peak with a frequency around 12Hz and a wide/slow trough with 140 | % a frequency around 8Hz. 141 | % 142 | % The third signal shows rapid changes in time. Here both the frequency and 143 | % amplitude of both signals shows rapid changes. Note that the frequency 144 | % estimates are very noisy when the signal is not a relatively simple 145 | % mono-component oscillation. 146 | 147 | figure('Position',[100 100 1024 512]) 148 | subplot(3,3,[1,4]) 149 | plot_hht(imf1,S.sample_rate,gca) 150 | title('Standard PAC') 151 | subplot(3,3,7) 152 | plot(simple_signal.time_vect,imf1) 153 | xlabel('Time (seconds)') 154 | grid on 155 | 156 | subplot(3,3,[2,5]) 157 | plot_hht(imf2,S.sample_rate,gca) 158 | title('Non-sinusoidal PAC') 159 | subplot(3,3,8) 160 | plot(nonlin_signal.time_vect,imf2) 161 | xlabel('Time (seconds)') 162 | grid on 163 | 164 | subplot(3,3,[3,6]) 165 | plot_hht(imf3,S.sample_rate,gca) 166 | title('Non-stationary PAC') 167 | subplot(3,3,9) 168 | plot(nonstat_signal.time_vect,imf3) 169 | xlabel('Time (seconds)') 170 | grid on 171 | 172 | %% Empirical Mode Decomposition 173 | % 174 | % The Hilbert-Huang Transform allows us to compute a high-resolution 175 | % time-frequency transform BUT requires a set of well-behaved 176 | % relatively-monocomponent set of signals to operate on. The Empirical Mode 177 | % Decomposition is able to split a complicated signal into such a set of 178 | % components using the Sift algorithm 179 | % 180 | % Here we look at an example from the matlab emd documentation. A composite 181 | % signal is defined from a number of different signals which change over time. 182 | % 183 | % This signal is then split into a set of Intrinsic Mode Functions (IMFs) 184 | % using the emd. Note that the IMFs are relatively clear oscillatory 185 | % signals but they retain the rapid dynamics in the signal. These IMFs are 186 | % appropriate inputs into the Hilbert-Huang Transform. 187 | % 188 | % The first IMF contains the fastest dynamics in the signal. Each 189 | % successive IMF isolates slower and slower dynamics until the final IMF 190 | % contains only a non-oscillatory trend. 191 | % 192 | % The top of the figure output contains the mixed signal and the second 193 | % plot contains first 4 Intrinsic Mode Functions. The bottom figure 194 | % contains the HHT. 195 | 196 | % Define our signal 197 | fs = S.sample_rate; 198 | t = (0:1/S.sample_rate:4)'; 199 | x1 = sin(2*pi*50*t) + sin(2*pi*200*t) + 0.1*randn(length(t),1); 200 | x2 = sin(2*pi*50/2*t) + sin(2*pi*200/2*t) + sin(2*pi*250*t) + 0.1*randn(length(t),1); 201 | x = [x1;x2]; 202 | time_vect = linspace(0,8,size(x,1)); 203 | 204 | % Compute Intrinsic Mode Functions with Empirical Mode Decomposition 205 | imf = emd(x); 206 | 207 | % Plot the simulated signal and the IMFs 208 | figure 209 | subplot(7,1,1) 210 | plot(time_vect,x) 211 | title('Time Series') 212 | xlim([3 5]) 213 | subplot(7,1,[2 3 4]) 214 | n_imfs = 4; 215 | plot(time_vect,imf(:,1:n_imfs) - 2*repmat(1:n_imfs,size(imf,1),1)) 216 | yticks(-2 * (7:-1:1)) 217 | yticklabels({'IMF7','IMF6','IMF5','IMF4','IMF3','IMF2','IMF1'}) 218 | title('Intrinsic Mode Functions') 219 | xlim([3 5]) 220 | ax = subplot(7,1,[5 6 7]); 221 | plot_hht( imf, S.sample_rate, ax, 350); 222 | title('Hilbert-Huang Transform') 223 | xlim([3 5]) 224 | 225 | %% Fieldtrip Example Data 226 | % 227 | % Here we load in some fieldtrip example data from this tutorial: 228 | % 229 | % http://www.fieldtriptoolbox.org/tutorial/timefrequencyanalysis 230 | % 231 | % Please download this file and make sure it is in the current folder 232 | % 233 | % ftp://ftp.fieldtriptoolbox.org/pub/fieldtrip/tutorial/timefrequencyanalysis/dataFIC.mat 234 | % 235 | % We take a signal trial from one channel from this data and apply the EMD 236 | % to extract IMFs. 237 | % 238 | % Again, note that the IMFs are relatively narrow-band oscillations with 239 | % the fastest dynamics in the first IMF. They are highly dynamic within an 240 | % IMF as the EMD does not supress non-linearity or non-stationarity in a 241 | % signal. 242 | 243 | % Load data 244 | load dataFIC.mat 245 | channel = 9; % MLC24 246 | 247 | % Compute IMFs with EMD. 248 | [imf, residual, info] = emd(dataFIC.trial{1}(channel,:)); 249 | 250 | % Summary figure 251 | figure 252 | subplot(4,1,1) 253 | plot(dataFIC.time{1},dataFIC.trial{1}(channel,:)) 254 | title('Time Series') 255 | grid on 256 | 257 | subplot(4,1,[2 3 4]) 258 | plot(dataFIC.time{1},imf - 1e-12*repmat(1:5,size(imf,1),1)) 259 | yticks(-1e-12 * (5:-1:1)) 260 | yticklabels({'IMF5','IMF4','IMF3','IMF2','IMF1'}) 261 | grid on 262 | title('Intrinsic Mode Functions') 263 | 264 | 265 | %% Compute a Hanning Taper Time-Frequency Analysis 266 | % 267 | % This is a standard Hanning-Taper analysis based on this tutorial 268 | % 269 | % http://www.fieldtriptoolbox.org/tutorial/timefrequencyanalysis/ 270 | % 271 | % Please check this page for a more detailed discussion of Hanning-Taper TF 272 | % estimation. 273 | % 274 | % We will use this as a comparison for the EMD & HHT approach. 275 | load dataFIC.mat 276 | 277 | cfg = []; 278 | cfg.output = 'pow'; 279 | cfg.channel = 'MEG'; 280 | cfg.method = 'mtmconvol'; 281 | cfg.taper = 'hanning'; 282 | cfg.foi = 2:1:40; % analysis 2 to 30 Hz in steps of 2 Hz 283 | cfg.t_ftimwin = ones(length(cfg.foi),1).*0.5; % length of time window = 0.5 sec 284 | cfg.toi = -1:0.05:2; % time window "slides" from -0.5 to 1.5 sec in steps of 0.05 sec (50 ms) 285 | cfg.pad = 'nextpow2'; % Faster FFT computation 286 | TFRhann = ft_freqanalysis(cfg, dataFIC); 287 | 288 | %% Compute a EMD + HHT Time Frequency analysis 289 | % 290 | % Here we compute a EMD+HHT analysis on the example data. This cell 291 | % performs several operations 292 | % 293 | % 1) Preallocate arrays 294 | % 2) Define channel and smoothing filter 295 | % 3) Loop through trials 296 | % 3.1) Compute IMFs from trial using EMD 297 | % 3.2) Compute instantaneous frequency and energy from IMFs for this trial 298 | % 3.3) Loop through IMFs 299 | % 3.3.1) Create sparse HHT array 300 | % 3.3.2) Smooth sparse HHT array 301 | 302 | 303 | % 1) Preallocate arrays 304 | % 305 | % we have: 306 | % * 77 trials 307 | % * 900 time-points 308 | % * 10 IMFs 309 | % * 100 Frequencies 310 | 311 | erf = zeros(77,900); % Evoked Field 312 | imfs = zeros(77,900,10); % Intrinsic Mode Functions 313 | hhts = zeros(77,100,900,10); % Hilbert-Huang Transform 314 | 315 | % 2) Define channel and smoothing filter 316 | channel = 9; % MLC24 317 | h = fspecial('gauss', 12,2); 318 | 319 | % 3) Loop through trials 320 | for ii = 1:77 321 | 322 | % 3.1) Compute IMFs from trial using EMD 323 | erf(ii,:) = dataFIC.trial{ii}(channel,:); 324 | tmp = emd(erf(ii,:),'Interpolation','pchip',... 325 | 'SiftRelativeTolerance',.02,... 326 | 'Display',0); 327 | imfs(ii,:,1:size(tmp,2)) = tmp; 328 | 329 | % 3.2) Compute instantaneous frequency and energy from IMFs for this trial 330 | [P1,F1,T1,insf,inse] = hht(tmp,dataFIC.fsample,'FrequencyLimits',[0,75],'FrequencyResolution',.5); 331 | 332 | % 3.3) Loop through IMFs 333 | for jj = 1:size(tmp,2) 334 | % 3.3.1) Create sparse HHT array 335 | freq_bins = discretize(insf(:,jj),0:100); 336 | good_bins = find(isfinite(freq_bins)); 337 | s = sparse(good_bins,freq_bins(good_bins),inse(good_bins,jj)); 338 | % 3.3.2) Smooth sparse HHT array 339 | s = filter2(h,full(s)'); 340 | hhts(ii,1:size(s,1),1:size(s,2),jj) = s; 341 | end 342 | 343 | end 344 | 345 | %% Method comparison figure 346 | % 347 | % Here create a figure containing: 348 | % * The Evoked Field 349 | % * The trial-averaged Hanning-Taper time-frequency representation 350 | % * The trial-averaged Hilbert-Huang time-frequency representation 351 | % 352 | % Note that both transforms pick up on similar features, though the EMD+HHT 353 | % approach has less blurring across time and frequency. For instance, the 354 | % increase in beta power around 1 second is quite gradual in the 355 | % Hanning-Taper but almost instantanous for the HHT. 356 | 357 | figure 358 | subplot(511) 359 | plot(dataFIC.time{1},mean(erf),'k') 360 | title('Evoked Field') 361 | grid on; 362 | 363 | subplot(5,1,[2 3]) 364 | contourf(TFRhann.time,TFRhann.freq, squeeze(TFRhann.powspctrm(channel,:,:)),32,'linestyle','none') 365 | title('Hanning Tapers') 366 | ylabel('Frequency (Hz)'); 367 | grid on 368 | 369 | subplot(5,1,[4 5]) 370 | toplot = squeeze(sum(sum(hhts(:,:,:,1:5),1),4)); 371 | contourf(dataFIC.time{1},1:100,toplot,linspace(0,10e-25,36),'linestyle','none'); 372 | title('HHT') 373 | grid on 374 | xlabel('Time (seconds') 375 | ylabel('Frequency (Hz)'); 376 | ylim([0 40]) 377 | 378 | 379 | 380 | %% Method comparison figure 381 | % 382 | % Here create a figure containing: 383 | % * The Evoked Field 384 | % * The baseline-corrected trial-averaged Hanning-Taper time-frequency representation 385 | % * The baseline-corrected Hilbert-Huang time-frequency representation 386 | % using a baseline of -.5 to -.1 seconds 387 | % 388 | % The picture is similar to above, again note the increased sharpness of 389 | % the beta onset around 1 second 390 | 391 | figure 392 | subplot(511) 393 | plot(dataFIC.time{1},mean(erf),'k') 394 | grid on; 395 | 396 | baseline_inds = TFRhann.time > -.5 & TFRhann.time < -.1; 397 | ax = subplot(5,1,[2 3]); 398 | toplot = squeeze(TFRhann.powspctrm(channel,:,:)); 399 | toplot = toplot - mean(toplot(:,baseline_inds),2); 400 | contourf(TFRhann.time,TFRhann.freq, toplot,linspace(-3e-27,3e-27,36),'linestyle','none') 401 | grid on; 402 | grid(gca,'minor') 403 | colorbar('NorthOutside') 404 | caxis([-3e-27,3e-27]) 405 | set(ax,'XMinorTick','on'); 406 | ax.XAxis.MinorTickValues = TFRhann.time; 407 | title('Hanning Tapers') 408 | xlabel('Time (seconds') 409 | ylabel('Frequency (Hz)') 410 | 411 | baseline_inds = dataFIC.time{1} > -.5 & dataFIC.time{1} < -.1; 412 | subplot(5,1,[4 5]) 413 | toplot = squeeze(mean(sum(hhts(:,:,:,1:7),4),1)); 414 | toplot = toplot - mean(toplot(:,baseline_inds),2); 415 | contourf(dataFIC.time{1},1:100,toplot,linspace(-3e-27,3e-27,36),'linestyle','none'); 416 | title('HHT') 417 | grid on;caxis([-3e-27,3e-27]) 418 | ylim([0 40]) 419 | colorbar('NorthOutside') 420 | xlabel('Time (seconds') 421 | ylabel('Frequency (Hz)') 422 | 423 | %% Advanced points 424 | % 425 | % There are two disadvantages of the EMD+HHT you may have noticed. These 426 | % can both be greatly in other implementations of the EMD. We have used the 427 | % matlab version here for simplicity but strongly recommmend considering these 428 | % factors if using the EMD in your own future applications. 429 | % 430 | % Firstly,is that the instantanous frequency estimation can be quite noisy. 431 | % This arises as it is computed from the differential of the instantaneous 432 | % phase. Differentiation is challenging in a noisy signal and can lead to 433 | % increased noise levels in the differentiated signal. This can be greatly 434 | % improved by using smoothing filters on the phase prior to computing 435 | % frequency. 436 | % 437 | % Secondly, mode-mixing can be a major problem in the EMD. this occurs when 438 | % a signal component isn't cleanly isolated into a single IMF. This is 439 | % particularly common for transient signals which come on and off quickly 440 | % in a noisy signal. This problem can be reduced by using the Ensemble EMD 441 | % or the Masked EMD to ensure a cleaner separation of signals. There is no 442 | % clear way to guarantee that signals will be separated a priori for a new signal 443 | % 444 | % We strongly recommend that each EMD project should explore the masked and 445 | % ensemble EMD options to make sure that decompositions are of a high 446 | % quality 447 | 448 | %% Further Reading 449 | % 450 | % The original EMD paper - 92 pages and 12,000 citations... 451 | % 452 | % Huang, N. E., Shen, Z., Long, S. R., Wu, M. C., Shih, H. H., Zheng, 453 | % Q., ? Liu, H. H. (1998). The empirical mode decomposition and the Hilbert 454 | % spectrum for nonlinear and non-stationary time series analysis. Proceedings 455 | % of the Royal Society of London. Series A: Mathematical, Physical and 456 | % Engineering Sciences, 454(1971), 903?995. 457 | % https://doi.org/10.1098/rspa.1998.0193 458 | % 459 | % 460 | % Some more detailed discussion on instantaneous frequency and how to 461 | % estimate it accurately 462 | % 463 | % Huang, N. E., Wu, Z., Long, S. R., Arnold, K. C., Chen, X., & Blank, 464 | % K. (2009). On Instantaneous Frequency. Advances in Adaptive Data Analysis, 465 | % 1(2), 177?229. https://doi.org/10.1142/s1793536909000096 466 | % 467 | % 468 | % A discussion on mode-mixing and how masked EMD can reduce the problem 469 | % 470 | % Ryan Deering, & James F. Kaiser. (2005). The Use of a Masking Signal 471 | % to Improve Empirical Mode Decomposition. In Proceedings. (ICASSP ?05). IEEE 472 | % International Conference on Acoustics, Speech, and Signal Processing, 2005. 473 | % IEEE. https://doi.org/10.1109/icassp.2005.1416051 474 | % 475 | % A frank discussion of some problems with the EMD and how you might solve 476 | % them. Very practical paper focused on implementation details 477 | % 478 | % Rato, R. T., Ortigueira, M. D., & Batista, A. G. (2008). On the HHT, 479 | % its problems, and some solutions. Mechanical Systems and Signal 480 | % Processing, 22(6), 1374?1394. https://doi.org/10.1016/j.ymssp.2007.11.028 481 | --------------------------------------------------------------------------------