% run_pressproc_GEauto.m
% Jamie Near, McGill University 2014.
%
% USAGE:
% [out,out_w,out_noproc,out_w_noproc]=run_pressproc_GEauto(filestring,aaDomain,tmaxin,iterin);
%
% DESCRIPTION:
% Fully automated processing script for GE PRESS MRS data in p-file
% format (GE raw data).  Includes combination of reciever channels,
% removal of bad averages, freqeuncy drift correction, and leftshifting.
% This pipeline requires no user interaction. This function automatically
% generates an html report to describe the results of each processing step.
%
% INPUTS:
% filestring    = String variable for the name of the p-file
%                   the water suppressed data;
% aaDomain      = (Optional) Perform the spectral registration (drift correction) using
%                   the full spectrum ('t'), or only a limited frequency
%                   range ('f').  Default is 'f'.
% tmaxin        = (Optional).  Duration (in sec.) of the time domain signal
%                   used in the spectral registration (drift correction).
%                   Default is 0.2 sec.
% iterin        = (Optional).  Maximum number of allowed iterations for the spectral
%                   registration to converge. Default is 20.
%
% OUTPUTS:
% out           = Fully processed, water suppressed output spectrum.
% out_w         = Fully processed, water unsuppressed output spectrum.
% out_noproc    = Water suppressed output spectrum without pre-
%                   processing (No bad-averages removal, no frequency drift
%                   correction).
% out_w_noproc  = Water unsuppressed output spectrum without pre-
%                   processing.

function [out,outw,out_noproc,outw_noproc]=run_pressproc_GEauto(filestring,aaDomain,tmaxin,iterin);

if nargin<4
    iterin=20;
    if nargin<3
        tmaxin=0.2;
        if nargin<2
            aaDomain='f';
        end
    end
end

%make a new directory for the output report and figures:
mkdir(['./report']);
mkdir(['./report/figs']);

% %read in both datasets:
[raw,raww]=io_loadspec_GE(filestring,1);
raw=op_complexConj(raw);

if isstruct(raww)
    water=true;
    raww=op_complexConj(raww);
end

%first step should be to combine coil channels.  To do this find the coil
%phases from the water unsuppressed data.
if water
    coilcombos=op_getcoilcombos(raww,1);
    [outw_cc,fidw_pre,specw_pre,phw,sigw]=op_addrcvrs(raww,1,'w',coilcombos);
else
    coilcombos=op_getcoilcombos(op_averaging(raw),1);
end
[out_cc,fid_pre,spec_pre,ph,sig]=op_addrcvrs(raw,1,'w',coilcombos);
[out_av_cc,fid_av_pre,spec_av_pre]=op_addrcvrs(op_averaging(raw),1,'w',coilcombos);
raw_av=op_averaging(raw);

%generate unprocessed spectrum:
out_noproc=op_averaging(out_cc);
if water
    outw_noproc=op_averaging(outw_cc);
end

%Generate plots showing coil channels before and after phase alignment
%figure('position',[0 50 560 420]);
h=figure('visible','off');
subplot(1,2,1);
plot(raw_av.ppm,real(raw_av.specs(:,:,1)));xlim([1 5]);
set(gca,'FontSize',8);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude (a.u.)','FontSize',10);
title('Before correction','FontSize',12);
box off;
subplot(1,2,2);
plot(raw_av.ppm,real(spec_av_pre(:,:,1)));xlim([1 5]);
set(gca,'FontSize',12);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude(a.u.)','FontSize',10);
title('After correction','FontSize',12);
box off;
set(h,'PaperUnits','centimeters');
set(h,'PaperPosition',[0 0 20 10]);
saveas(h,['./report/figs/coilReconFig'],'jpg');
saveas(h,['./report/figs/coilReconFig'],'fig');
close(h);

%%%%%%%%OPTIONAL REMOVAL OF BAD AVERAGES FROM FIRST DATASET%%%%%%%%%%%%%%%%%%%%
close all;
out_cc2=out_cc;
nBadAvgTotal=0;
nbadAverages=1;
rmbadav='y';
close all;
if rmbadav=='n' || rmbadav=='N'
    out_rm=out_cc;
    nsd='N/A';
else
    sat='n'
    while sat=='n' || sat=='N'
        nsd=4; %Setting the number of standard deviations;
        iter=1;
        nbadAverages=1;
        nBadAvgTotal=0;
        out_cc2=out_cc;
        while nbadAverages>0
            [out_rm,metric{iter},badAverages]=op_rmbadaverages(out_cc2,nsd,'t');
            badAverages;
            nbadAverages=length(badAverages);
            nBadAvgTotal=nBadAvgTotal+nbadAverages;
            out_cc2=out_rm;
            iter=iter+1;
            disp([num2str(nbadAverages) ' bad averages removed on this iteration.']);
            disp([num2str(nBadAvgTotal) ' bad averages removed in total.']);
            close all;
        end
        %figure('position',[0 50 560 420]);
        %Make figure to show pre-post removal of averages
        h=figure('visible','off');
        subplot(1,2,1);
        plot(out_cc.ppm,real(out_cc.specs(:,:)));xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Before','FontSize',12);
        box off;
        subplot(1,2,2);
        plot(out_rm.ppm,real(out_rm.specs(:,:)));xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('After','FontSize',12);
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 15]);
        saveas(h,['./report/figs/rmBadAvg_prePostFig'],'jpg');
        saveas(h,['./report/figs/rmBadAvg_prePostFig'],'fig');
        close(h);

        %figure('position',[0 550 560 420]);
        h=figure('visible','off');
        plot([1:length(metric{1})],metric{1},'.r',[1:length(metric{iter-1})],metric{iter-1},'x','MarkerSize',16);
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Deviation Metric','FontSize',10);
        legend('Before rmBadAv','After rmBadAv');
        legend boxoff;
        title('Deviation Metric','FontSize',12);
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 10]);
        saveas(h,['./report/figs/rmBadAvg_scatterFig'],'jpg');
        saveas(h,['./report/figs/rmBadAvg_scatterFig'],'fig');
        close(h);

        %sat1=input('are you satisfied with the removal of bad averages? ','s');
        sat='y';

    end
end

%NOW ALIGN AVERAGES:  A.K.A. Frequency Drift Correction.
driftCorr='y';
if driftCorr=='n' || driftCorr=='N'
    out_av=op_averaging(out_rm);
    if water
        outw_av=op_averaging(outw_cc);
    end
    fs=0;
    phs=0;
else
    if water
        outw_aa=op_alignAverages(outw_cc,0.2,'n');
    end
    sat='n';
    out_rm2=out_rm;
    while sat=='n' || sat=='N'
        fsPoly=100;
        phsPoly=1000;
        fscum=zeros(out_rm2.sz(out_rm2.dims.averages),1);
        phscum=zeros(out_rm2.sz(out_rm2.dims.averages),1);
        iter=1;
        while (abs(fsPoly(1))>0.001 || abs(phsPoly(1))>0.01) && iter<iterin
            iter=iter+1
            close all
            tmax=0.25+0.03*randn(1);
            ppmmin=1.6+0.1*randn(1);
            ppmmaxarray=[3.5+0.1*randn(1,2),4+0.1*randn(1,3),5.5+0.1*randn(1,1)];
            ppmmax=ppmmaxarray(randi(6,1));
            switch aaDomain
                case 't'
                    [out_aa,fs,phs]=op_alignAverages(out_rm2,tmax,'y');
                case 'f'
                    [out_aa,fs,phs]=op_alignAverages_fd(out_rm2,ppmmin,ppmmax,tmax,'y');
                otherwise
                    error('ERROR: avgAlignDomain not recognized!');
            end

            fsPoly=polyfit([1:out_aa.sz(out_aa.dims.averages)]',fs,1)
            phsPoly=polyfit([1:out_aa.sz(out_aa.dims.averages)]',phs,1)
            iter

            fscum=fscum+fs;
            phscum=phscum+phs;

            if driftCorr=='y' || driftCorr=='Y'
                out_rm2=out_aa;
            end
        end
        h=figure('visible','off');
        subplot(1,2,1);
        plot(out_rm.ppm,real(out_rm.specs(:,:)));xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Before','FontSize',12);
        box off;
        subplot(1,2,2);
        plot(out_aa.ppm,real(out_aa.specs(:,:)));xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('After','FontSize',12);
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 15]);
        saveas(h,['./report/figs/alignAvgs_prePostFig'],'jpg');
        saveas(h,['./report/figs/alignAvgs_prePostFig'],'fig');
        close(h);

        h=figure('visible','off');
        plot([1:out_aa.sz(out_aa.dims.averages)],fscum,'.-','LineWidth',2);
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Frequency Drift [Hz]','FontSize',10);
        box off;
        legend('Frequency Drift','Location','SouthEast');
        legend boxoff;
        title('Estimated Freqeuncy Drift','FontSize',12);
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 10 10]);
        saveas(h,['./report/figs/freqDriftFig'],'jpg');
        saveas(h,['./report/figs/freqDriftFig'],'fig');
        close(h);

        h=figure('visible','off');
        plot([1:out_aa.sz(out_aa.dims.averages)],phscum,'.-','LineWidth',2);
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Phase Drift [Deg.]','FontSize',10);
        box off;
        legend('Phase Drift','Location','SouthEast');
        legend boxoff;
        title('Estimated Phase Drift','FontSize',12);
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 10 10]);
        saveas(h,['./report/figs/phaseDriftFig'],'jpg');
        saveas(h,['./report/figs/phaseDriftFig'],'fig');
        close(h);

        sat='y';
        if sat=='n'
            iter=0;
            p1=100;
            fscum=zeros(out_rm.sz(2:end));
            phscum=zeros(out_rm.sz(2:end));
            fs2cum=zeros(out_cc.sz(2:end));
            phs2cum=zeros(out_cc.sz(2:end));
            out_rm2=out_rm;
            out_cc2=out_cc;
        end
        totalFreqDrift=mean(max(fscum)-min(fscum));
        totalPhaseDrift=mean(max(phscum)-min(phscum));
        close all
    end
    %now combine the averages averages
    out_av=op_averaging(out_aa);
    if water
        outw_av=op_averaging(outw_aa);
    end
end

%now leftshift
out_ls=op_leftshift(out_av,out_av.pointsToLeftshift);
if water
    outw_ls=op_leftshift(outw_av,outw_av.pointsToLeftshift);
end

%now do automatic zero-order phase correction (Use water Peak):
out_ls_zp=op_zeropad(out_ls,16);
%index=find(abs(out_ls_zp.specs)==max(abs(out_ls_zp.specs(out_ls_zp.ppm>2.9 & out_ls_zp.ppm<3.1))));
%ph0=-phase(out_ls_zp.specs(index))*180/pi;
[out_ls_zp_ph,ph0]=op_autophase(out_ls_zp,4,5.5);
out_ph=op_addphase(out_ls,ph0);
%And now for water unsuppressed data:
if water
    outw_ls_zp=op_zeropad(outw_ls,16);
    %indexw=find(abs(outw_ls_zp.specs)==max(abs(outw_ls_zp.specs(outw_ls_zp.ppm>4 & outw_ls_zp.ppm<5.5))));
    %ph0w=-phase(outw_ls_zp.specs(indexw))*180/pi;
    outw_ph=op_addphase(outw_ls,ph0);
    outw_ls_zp_ph=op_addphase(outw_ls_zp,ph0);
end

%do same phase corection on unprocessed data
out_noproc=op_addphase(op_leftshift(out_noproc,out_noproc.pointsToLeftshift),ph0);
if water
    outw_noproc=op_addphase(op_leftshift(outw_noproc,outw_noproc.pointsToLeftshift),ph0);
end

%Frequency shift all spectra so that Creatine appears at 3.027 ppm:
[~,frqShift]=op_ppmref(out_ls_zp_ph,2.9,3.1,3.027);
out=op_freqshift(out_ph,frqShift);
out_noproc=op_freqshift(out_noproc,frqShift);
%And now for water unsuppressed data (user water peak and set to 4.65 ppm):
if water
    [~,frqShiftw]=op_ppmref(outw_ls_zp_ph,4,5.5,4.65);
    outw=op_freqshift(outw_ph,frqShiftw);
    outw_noproc=op_freqshift(outw_noproc,frqShiftw);
end

%Make figure to show the final spectrum:
h=figure('visible','off');
plot(out.ppm,out.specs,'linewidth',2);xlim([0.2 5.2]);
set(gca,'FontSize',8);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude(a.u.)','FontSize',10);
legend('diff');
legend boxoff;
box off;
title('Result: Final Spectrum','FontSize',12);
set(h,'PaperUnits','centimeters');
set(h,'PaperPosition',[0 0 20 10]);
saveas(h,['./report/figs/finalSpecFig'],'jpg');
saveas(h,['./report/figs/finalSpecFig'],'fig');

% writ=input('Write? (y or n):  ','s');
writ='y';
if writ=='y' || writ=='Y'
     RF=io_writelcm(out,['./' filestring '_lcm'],out.te);
    if water
        RF=io_writelcm(outw,['./' filestring '_w_lcm'],outw.te);
    end
end

close all;

%write an html report:
fid=fopen(['./report/report.html'],'w+');
fprintf(fid,'<!DOCTYPE html>');
fprintf(fid,'\n');
logoPath=which('FID-A_LOGO.jpg');
fprintf(fid,'\n',logoPath);
fprintf(fid,'\n
FID-A Processing Report
');
fprintf(fid,'\n
Processing pipeline applied to PRESS data using run_pressproc_auto.m
');
fprintf(fid,'\n
FILENAME: %s/%s
',pwd,filestring);
fprintf(fid,'\n
DATE: %s
',date);
fprintf(fid,'\n\n

');
fprintf(fid,'\n\n
Results of multi-coil combination:
');
fprintf(fid,'\n',pwd);
fprintf(fid,'\n\n

');
fprintf(fid,'\n\n
Results of removal of bad averages:
');
fprintf(fid,'\n
Original number of averages: \t%5.6f
',raw.sz(raw.dims.averages));
fprintf(fid,'\n
Number of bad Averages removed:  \t%5.6f
',nBadAvgTotal);
fprintf(fid,'\n
Number of remaining averages in processed dataset:  \t%5.6f
',out_rm.sz(out_rm.dims.averages));
fprintf(fid,'\n
Bad Averages Removal Threshold was:  \t%2.2f
',nsd);
fprintf(fid,'\n',pwd,pwd);
fprintf(fid,'\n\n

');
fprintf(fid,'\n\n

');
fprintf(fid,'\n\n
Results of spectral registration:
');
fprintf(fid,'\n
Total frequency drift was: \t%5.6f
',max(totalFreqDrift));
fprintf(fid,'\n
Total phase drift was: \t%5.6f
',max(totalPhaseDrift));
fprintf(fid,'\n',pwd);
fprintf(fid,'\n\n

');
fprintf(fid,'\n',pwd,pwd);
fprintf(fid,'\n\n

');
fprintf(fid,'\n\n
Final Result:
');
fprintf(fid,'\n',pwd);
fclose(fid);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


function outputStr = formatRW(inputStr)
%adds whitespace after \reservedwordplot(1,1);

inputStrCell = cell(1,length(inputStr));

for c = 1:length(inputStr)
    inputStrCell{c} = inputStr(c);
end

rW = {'\\alpha','\\upsilon','\\sim','\\angle','\\phi','\\leq',...
    '\\ast','\\chi','\\infty','\\beta','\\psi','\\clubsuit',...
    '\\gamma','\\omega','\\diamondsuit','\\delta',...
    '\\Gamma','\\heartsuit','\\epsilon','\\Delta',...
    '\\spadesuit','\\zeta','\\Theta','\\leftrightarrow',...
    '\\eta','\\Lambda','\\leftarrow','\\theta','\\Xi',...
    '\\Leftarrow','\\vartheta','\\Pi','\\uparrow',...
    '\\iota','\\Sigma','\\rightarrow','\\kappa',...
    '\\Upsilon','\\Rightarrow','\\lambda','\\Phi',...
    '\\downarrow','\\mu','\\Psi','\\circ','\\nu',...
    '\\Omega','\\pm','\\xi','\\forall','\\geq','\\pi',...
    '\\exists','\\propto','\\rho','\\ni','\\partial',...
    '\\sigma','\\cong','\\bullet','\\varsigma',...
    '\\approx','\\div','\\tau','\\Re','\\neq','\\equiv',...
    '\\oplus','\\aleph','\\Im','\\cup','\\wp','\\otimes',...
    '\\subseteq','\\oslash','\\cap','\\in','\\supseteq',...
    '\\supset','\\lceil','\\subset','\\int','\\cdot','\\o',...
    '\\rfloor','\\neg','\\nabla','\\lfloor','\\times','\\lots',...
    '\\perp','\\surd','\\prime','\\wedge','\\varpi','\\0',...
    '\\rceil','\\rangle','\\mid','\\vee','\\copyright','\\langle'};

for w = 1:length(rW)
    [startInd endInd] = regexp(inputStr,rW{w});
    for ind = 1:length(endInd)
        %add space at end of reserved words
        inputStrCell{endInd(ind)}  = [inputStrCell{endInd(ind)} ' '];
    end
end

outputStr = [inputStrCell{:}];

end


%  This program is free software; you can redistribute it and/or modify
%  it under the terms of the GNU General Public License as published by
%  the Free Software Foundation; either version 2 of the License, or
%  (at your option) any later version.
%
%  This program is distributed in the hope that it will be useful,
%  but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%  GNU General Public License for more details.
%
%  You should have received a copy of the GNU General Public License
%  along with this program; if not, write to the Free Software
%  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
%TFDEMO4 Cohen's class time-frequency distributions.
%	Time-Frequency Toolbox demonstration.
%
%	See also TFDEMO.

%	O. Lemoine - May 1996.
%	Copyright (c) CNRS.

clc; zoom on; clf;
echo on;

% The Wigner-Ville distribution
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% A time-frequency energy distribution which is particularly interesting
% is the Wigner-Ville distribution (WVD),which satisfies a large number
% of desirable mathematical properties. Let us see what we obtain on two
% particular synthetic signals :
% - the first signal is the academic linear chirp signal :

sig=fmlin(128);

% If we choose a 3-dimension plot to represent it, we can see that the WVD
% can take negative values, and that the localization obtained in the
% time-frequency plane for this signal is almost perfect.

tfrwv(sig);
% Press any key to continue...

pause ; clc;

% - the second one illustrates the Doppler effect, which expresses the
% dependence of the frequency received by an observer from a transmitter
% on the relative speed between the observer and the transmitter :

[fm,am,iflaw]=doppler(128,50,13,10,200);
sig=am.*fm;
tfrwv(sig);
% Looking at this time-frequency distribution, we notice that the energy is
% not distributed as we could expect for this signal. Although the signal
% term is well localized in the time-frequency plane, numerous other terms
% (the interference terms, due to the bilinearity of the WVD) are present at
% positions in time and frequency where the energy should be null.
%
% Press any key to continue...

pause; clc; close

% Interference geometry of the WVD
%""""""""""""""""""""""""""""""""""
% The rule of interference construction of the WVD can be summarized as
% follows : two points of the time-frequency plane interfere to create a
% contribution on a third point which is located at their geometrical
% midpoint. Besides, these interference terms oscillate perpendicularly to
% the line joining the two points interfering, with a frequency
% proportional to the distance between these two points.
%  This can be seen on the following example : we consider two atoms in
% the time-frequency plane, analyzed by the WVD, whose relative distance
% is increasing from one realization to the other, and then decreasing.
% The WVDs were calculated and saved on the file movieat.mat. We load them
% and run the sequence using the function movie :

M=movwv2at(128,15); movie(M,10); clear M;

% We can notice, from this movie, the evolution of the interferences
% when the distance between the two interfering terms changes, and in
% particular the change in the period and the direction of the oscillations.
%
% Press any key to continue...

pause; clc;

% The pseudo-WVD
%""""""""""""""""
%   As the analyzed signal is not known from -infinity to +infinity in
% pratice, one often consider a windowed version of the WVD, called the
% pseudo-WVD. The time-windowing operated has the effect of smoothing the
% WVD in frequency. Thus, because of their oscillating nature, the
% interferences will be attenuated in the pseudo-WVD compared to the WVD.
% However, the consequence of this improved readability is that many
% properties of the WVD are lost.
% If we consider a signal composed of four gaussian atoms, each localized
% at a corner of a rectangle,

sig=atoms(128,[32,.15,20,1;96,.15,20,1;32,.35,20,1;96,.35,20,1]);

% and compute its WVD

tfrwv(sig);
% we can see the four signal terms, along with six interference terms (two of
% them are superimposed). If we now compute the pseudo-WVD,

figure
tfrpwv(sig);
% we can note the important attenuation of the interferences oscillating
% perpendicularly to the frequency axis, and in return the spreading in
% frequency of the signal terms.
%
% Press any key to continue...

pause; clc; close;

% Importance of the analytic signal
%"""""""""""""""""""""""""""""""""""
%   Due to the quadratic nature of the WVD, its discrete version may be
% affected by a spectral aliasing, in particular if the signal x is
% real-valued and sampled at the Nyquist rate. A solution to this problem
% consists in using the analytic signal. Indeed, as its bandwidth is half the
% one of the real signal, the aliasing will not take place in the useful
% spectral domain [0,1/2] of this signal. This solution presents a second
% advantage : since the spectral domain is divided by two, the number of
% components in the time-frequency plane is also divided by two.
% Consequently, the number of interference terms decreases significantly.
% Here is an illustration : we first consider the WVD of the real part of
% a signal composed of two atoms :

sig=atoms(128,[32,0.15,20,1;96,0.32,20,1]);
tfrwv(real(sig));
% We can see that four signal terms are present instead of two, due to the
% spectral aliasing. Besides, because of the components located at negative
% frequencies (between -1/2 and 0), additional interference terms are
% present.
%
% Press any key to continue...

pause;

% If we now consider the WVD of the same signal, but in its complex
% analytic form,

tfrwv(sig);
% the aliasing effect has disappeared, as well as the terms corresponding to
% interferences between negative- and positive- frequency components.
%
% Press any key to continue...

pause; clc;

% The Cohen's class
%~~~~~~~~~~~~~~~~~~~
%  The Cohen's class gather all the time-frequency energy distributions which
% are covariant by translations in time and in frequency. It can be expressed
% as a 2-D correlation between a function PI(t,nu) and the WVD of the
% analyzed signal. The WVD is the element of the Cohen's class for which PI
% is a double Dirac, and the spectrogram is th element for which PI is the
% WVD of the short time window h. We consider in the following other elements
% of this class
%
% The smoothed pseudo-WVD
%"""""""""""""""""""""""""
% If we consider a separable smoothing function PI(t,nu)=g(t)H(-nu) (where
% H(nu) is the Fourier transform of a smoothing window h(t)), we allow a
% progressive and independent control, in both time and frequency, of the
% smoothing applied to the WVD. The obtained distribution is known as the
% smoothed-pseudo WVD.
% For example,let's consider a signal composed of two components : the first
% one is a complex sinusoid (normalized frequency 0.15) and the second one
% is a Gaussian signal shifted in time and frequency :

sig=sigmerge(fmconst(128,.15),amgauss(128).*fmconst(128,0.4),5);

% If we display the WVD, the pseudo-WV and the smoothed-pseudo-WVD of it,

tfrwv(sig);
figure; tfrpwv(sig);
figure; tfrspwv(sig);
% we can make the following remarks : from the WVD, we can see the two
% signal terms located at the right positions in the time-frequency plane,
% as well as the interference terms between them. As these interference
% terms oscillate globally perpendicularly to the time-axis, the frequency
% smoothing done by the pseudo-WVD degrades the frequency resolution without
% really attenuating the interferences. On the other hand, the time-smoothing
% carried out by the smoothed-pseudo-WVD considerably reduces these
% interferences.
%
% Press any key to continue...

pause; clc; close; close; close

%   An interresting property of the smoothed-pseudo WVD is that it allows a
% continuous passage from the spectrogram to the WVD, under the condition
% that the smoothing functions g and h are gaussian. This is clearly
% illustrated by the function movsp2wv.m, which considers different
% transitions, on a signal composed of four atoms :

M=movsp2wv(128,15); movie(M,10); clear M;

% This movie shows the effect of a (time/frequency) smoothing on the
% interferences and on the resolutions : the WVD gives the best resolutions
% (in time and in frequency), but presents the most important interferences,
% whereas the spectrogram gives the worst resolutions, but with nearly no
% interferences ; and the smoothed-pseudo WVD allows to choose the best
% compromise between these two extremes.
%
% Press any key to continue...

pause; clc;

% The narrow-band ambiguity function
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%  The narrow-band ambiguity function, often used in radar signal processing,
% is the two-dimensional Fourier transform of the WVD. This property can be
% used to attenuate some of the interference terms. Indeed, in the case of
% a multi-component signal, the elements of the AF corresponding to the
% signal components (denoted as the AF-signal terms) are mainly located
% around the origin, whereas the elements corresponding to interferences
% between the signal components (AF-interference terms) appear at a distance
% from the origin which is proportional to the time-frequency distance
% between the involved components. This can be noticed on a simple example :
% We apply consider a signal composed of two linear FM signals with
% gaussian amplitudes :

N=64; sig1=fmlin(N,0.2,0.5).*amgauss(N);
sig2=fmlin(N,0.3,0).*amgauss(N);
sig=[sig1;sig2];

% Let us first have a look at the WVD :

tfrwv(sig);
% We have two distinct signal terms, and some interferences oscillating in
% the middle.
%
% Press any key to continue...

pause;

% If we look at the ambiguity function of this signal,

clf; ambifunb(sig);

% we have around the origin (in the middle of the image) the AF-signal
% terms, whereas the AF-interference terms are located away from the origin.
%
% Press any key to continue...

pause; clc;

% Other important energy distributions
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%
% The Rihaczek and Margenau-Hill distributions
% """""""""""""""""""""""""""""""""""""""""""""
%  The Rihaczek distribution, defined as
%	Rx(t,nu)=x(t)X*(nu)e^{-j2pi nu t},
% is a complex energy density at point (t,nu). This distribution, which
% corresponds to the element of the Cohen's class for which
% f(xi,tau)=e^{jpi xi tau}, verifies many good properties. However, it is
% complex valued, which can be awkward in practice. The real part of the
% Rihaczek distribution is also a time-frequency distribution of the
% Cohen's class (f(xi,tau)=cos(pi xi tau)), known as the Margenau-Hill
% distribution
%   The interference structure of the Rihaczek and Margenau-Hill
% distributions is different from the Wigner-Ville one : the interference
% terms corresponding to two points located on (t1,nu1) and (t2,nu2) are
% positioned at the coordinates (t1,nu2) and (t2,nu1). This can be seen on
% the following example :

sig=atoms(128,[32,0.15,20,1;96,0.32,20,1]);
tfrmh(sig);
% Thus, the use of the Rihaczek (or Margenau-Hill) distribution for signals
% composed of multi-components located at the same position in time or in
% frequency is no advised, since the interference terms will then be
% superposed to the signal terms.
%
% Press any key to continue...

pause; clc; close

% The Choi-Williams distribution
% """""""""""""""""""""""""""""""
%  An example of reduced interference distribution is given by the
% Choi-Williams distribution, defined as
% CWx(t,nu)=sqrt(2/pi)\int\int sigma/|tau| e^{-2sigma^2(s-t)^2/tau^2}
%	    x(s+tau/2)x*(s-tau/2) e^{-j2pi nu tau} ds dtau
% Note that when sigma->+infty, we obtain the WVD. Inversely, the smaller
% sigma, the better the reduction of the interferences.
%   The "cross"-shape of the parametrization function of the Choi-Williams
% distribution implies that the efficiency of this distribution strongly
% depends on the nature of the analyzed signal. For instance, if the signal
% is composed of synchronized components in time or in frequency, the
% Choi-Williams distribution will present strong interferences. This can be
% observed on the following example : we analyze four gaussian atoms
% positionned at the corners of a rectangle rotating around the center of
% the time-frequency plane :

M=movcw4at(128); movie(M,5); clear M;

% When the time/frequency supports of the atoms overlap, some
% AF-interference terms are not be completly attenuated (those present
% around the axes of the ambiguity plane), and the efficiency of the
% distribution is quite poor.
%
% Press any key to continue...

pause; clc;

% Comparison of the parametrization functions
% """"""""""""""""""""""""""""""""""""""""""""
%  To illustrate the differences between some of the presented
% distributions, we represent their weighting (parametrization) function in
% the ambiguity plane, along with the result obtained by applying them on a
% two-component signal embedded in white gaussian noise : the signal is the
% sum of two linear FM signals, the first one with a frequency going from
% 0.05 to 0.15, and the second one from 0.2 to 0.5. The signal to noise
% ratio is 10 dB.

% On the left-hand side of the figures, the parametrization functions are
% represented in a schematic way by the bold contour lines (the weighting
% functions are mainly non-zeros inside these lines), superimposed to the
% ambiguity function of the signal. The AF-signal terms are in the middle of
% the ambiguity plane, whereas the AF-interference terms are distant from the
% center. On the right-hand side, the corresponding time-frequency
% distributions are represented.

paramfun

% From these plots, we can conclude that the ambiguity plane is very
% enlightening with regard to interference reduction in the case of
% multicomponent signals. On this example, we notice that the
% smoothed-pseudo-WVD is a particularly convenient and versatile
% candidate. This is due to the fact that we can adapt independently the
% time-width and frequency-width of its weighting function. But in the
% general case, it is interesting to have several distributions at our
% disposal since each one is well adapted to a certain type of
% signal. Besides, for a given signal, as a result of the different
% interference geometries, these distributions offer complementary
% descriptions of this signal.
%
% Press any key to end this demonstration

pause;
echo off

close;


function y = tfocs_round(x)
if iscell( x ),
    y = cellfun( @tfocs_round, x, 'UniformOutput', false );
else
    y = round( x );
end

% TFOCS v1.3 by Stephen Becker, Emmanuel Candes, and Michael Grant.
% Copyright 2013 California Institute of Technology and CVX Research.
% See the file LICENSE for full license information.


% brainstorm2eeglab - convert Brainstorm structures to EEGLAB dataset
%
% EEG = brainstorm2eeglab(bst, epochType);
%
% Inputs:
%    bst - Brainstorm string for a folder containing data epoch files or Brainstorm
%          data epoch structure
%    epochType - [string] epoch type. Only import these epochs which
%                Comment field contains this string
%
% Limitations:
%   - Only import data epochs
%   - Cannot import linked file
%
% Output:
%    EEG     - EEGLAB structure
%
% Author: Arnaud Delorme, UCSD

% Copyright (C) Arnaud Delorme, UCSD 2022
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function EEG = brainstorm2eeglab(bst, epochType)

if nargin < 1
    help brainstorm2eeglab;
    return;
end
if nargin < 2
    epochType = '';
end

if ischar(bst)
    oriBst = bst;
    bst = dir(fullfile(bst, '*.mat'));
    if isempty(bst)
        error('Files not found')
    end
else
    oriBst = '';
    % Need brainstrorm
    try
        prot = bst_get('ProtocolInfo');
    catch
        brainstorm nogui
        try
            prot = bst_get('ProtocolInfo');
        catch
            error('Brainstorm not found. Brainstorm must be installed and running.')
        end
    end
    brainstorm_path = bst_get('BrainstormDbDir');
    protocol = prot.Comment;

    for iEpoch = 1:length(bst)
        bst(iEpoch).name = bst(iEpoch).FileName;
        bst(iEpoch).folder = fullfile(brainstorm_path, protocol, 'data');
    end
end

disp('Reading data epochs...');
allEpochs = {};
for iEpoch = 1:length(bst)
    if ~isequal(bst(iEpoch).name, 'brainstormstudy.mat')
        epochStruct = load('-mat', fullfile(bst(iEpoch).folder, bst(iEpoch).name));
        % epochStruct.Comment(1) contain the type of event
        % events not imported (in epochStruct.event)
        if ~isempty(epochType)
            if contains(epochStruct.Comment, epochType)
                allEpochs{end+1} = epochStruct.F;
            end
        else
            allEpochs{end+1} = epochStruct.F;
        end
    end
end
data = reshape([allEpochs{:}], size(allEpochs{1},1), size(allEpochs{1},2), length(allEpochs));

% create EEG structure
EEG = eeg_emptyset;
EEG.srate = mean(1./diff(epochStruct.Time));
EEG.data  = data; % microvolts
EEG.xmin  = epochStruct.Time(1);

% channel locations
if isempty(oriBst)
    chanFile = fullfile(bst(1).folder, bst(1).ChannelFile);
else
    chanFile = fullfile(oriBst, '..', '@default_study', 'channel.mat');
end
if exist(chanFile, 'file')
    chans = load('-mat', chanFile);
    for iChan = 1:length(chans.Channel)
        EEG.chanlocs(iChan).labels = chans.Channel(iChan).Name;
        if ~isempty(chans.Channel(iChan).Loc)
            EEG.chanlocs(iChan).X      = chans.Channel(iChan).Loc(1);
            EEG.chanlocs(iChan).Y      = chans.Channel(iChan).Loc(2);
            EEG.chanlocs(iChan).Z      = chans.Channel(iChan).Loc(3);
        end
        EEG.chanlocs(iChan).type   = chans.Channel(iChan).Type;
    end
    EEG.chanlocs = convertlocs(EEG.chanlocs, 'cart2all');
end
EEG = eeg_checkset(EEG);

% rescale data to microvolts
if ~isempty(EEG.chanlocs)
   chanTypes = strmatch('EEG', { EEG.chanlocs.type });
   if ~isempty(chanTypes)
       EEG.data(chanTypes,:,:) = EEG.data(chanTypes,:,:)*1e6; % assumes volt (this code should not be reached as channel types should always be defined)
   end
else
    EEG.data = EEG.data*1e6; % assumes volt (this code should not be reached as channel types should always be defined)
end


function x = p02_start ( i, x )

%*****************************************************************************80
%
%% P02_START returns a starting point for problem 2.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    09 May 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer I, the index of the starting point.
%
%    Output, real X, the starting point.
%
  if ( i == 1 )
    x = 0.0;
  elseif ( i == 2 )
    x = 1.0;
  elseif ( i == 3 )
    x = -5.0;
  elseif ( i == 4 )
    x = 10.0;
  else
    fprintf ( 1, '\n' );
    fprintf ( 1, 'P02_START - Fatal error!\n' );
    fprintf ( 1, '  Illegal starting point index = %d\n', i );
    error ( 'P02_START - Fatal error!' );
  end

  return
end


function s = y_to_s_islamic ( y )

%*****************************************************************************80
%
%% Y_TO_S_ISLAMIC writes an Islamic year into a string.
%
%  Format:
%
%    YearNumber AH
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 March 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer Y, the year.
%
%    Output, string S, a representation of the year.
%
  s = sprintf ( 'AH %d', y );

  return
end


function [dat, dimord] = read_shm_data(hdr, chanindx, begtrial, endtrial)

% READ_SHM_DATA reads the data in real-time from shared memory
% this is a helper function for READ_DATA

% Copyright (C) 2007-2009, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% this persistent variable is used for caching the 600 packets
% which inproves the throughput when reading overlapping data segments
persistent ctf_shm

% read the data from shared memory, first the meta information only
[msgType, msgId, sampleNumber, numSamples, numChannels] = read_ctf_shm;

if isempty(ctf_shm)
  ctf_shm.msgType      = nan(size(msgType));
  ctf_shm.msgId        = nan(size(msgId));
  ctf_shm.sampleNumber = nan(size(sampleNumber));
  ctf_shm.numSamples   = nan(size(numSamples));
  ctf_shm.numChannels  = nan(size(numChannels));
  ctf_shm.data = {};
  ctf_shm.hit   = 0;
  ctf_shm.mis   = 0;
end

% there seems to be a bug in Acq, causing the messageId to wrap around
% hence it cannot be used as index into the packets, so construct a new trial numbering vector
trlNum = nan(size(msgId));
trlNum(msgType==1) = sampleNumber(msgType==1)./numSamples(msgType==1) + 1;

% allocate memory for the data, fill with NaNs
dat = nan(length(chanindx), hdr.nSamples, endtrial-begtrial+1);

% determine which trials/packets to read
sel = find((trlNum>=begtrial) & (trlNum<=endtrial));

% this is for calibrating the integer values to physical values
if isfield(hdr, 'orig') && isfield(hdr.orig, 'gainV')
  gain = diag(hdr.orig.gainV(chanindx));
elseif isfield(hdr, 'orig') && isfield(hdr.orig, 'res4')
  gain = diag([hdr.orig.res4.senres(chanindx).gain]);
end

if length(chanindx)>1
  % this speeds up the multiplication, but would result in a sparse matrix when nchans=1
  gain = sparse(gain);
end

for i=1:length(sel)
  % nchan = numChannels(i);
  % nsmp  = numSamples(i);
  nchan = hdr.nChans;
  nsmp  = hdr.nSamples;
  % buf = read_ctf_shm(sel(i));
  % buf = buf(1:nchan*nsmp);
  if all([msgType(sel(i)) msgId(sel(i)) sampleNumber(sel(i)) numSamples(sel(i)) numChannels(sel(i))] ==  [ctf_shm.msgType(sel(i)) ctf_shm.msgId(sel(i)) ctf_shm.sampleNumber(sel(i)) ctf_shm.numSamples(sel(i)) ctf_shm.numChannels(sel(i))])
    % get the data from the cache
    buf = ctf_shm.data{sel(i)};
    ctf_shm.hit = ctf_shm.hit+1;
  else
    % read the data from shared memory, update the cache
    buf = read_ctf_shm(sel(i),nchan*nsmp);
    ctf_shm.data{sel(i)}         = buf;
    ctf_shm.msgType(sel(i))      = msgType(sel(i));
    ctf_shm.msgId(sel(i))        = msgId(sel(i));
    ctf_shm.sampleNumber(sel(i)) = sampleNumber(sel(i));
    ctf_shm.numSamples(sel(i))   = numSamples(sel(i));
    ctf_shm.numChannels(sel(i))  = numChannels(sel(i));
    ctf_shm.mis                  = ctf_shm.mis+1;
  end
  buf = reshape(buf, nchan, nsmp);
  thistrial = trlNum(sel(i));
  % apply calibration to the selected channels
  dat(:,:,thistrial-begtrial+1) = gain * double(buf(chanindx,:));
end

% if any(isnan(dat(:)))
%   ft_warning('data has been padded with NaNs');
%   fprintf('trials present   = %d - %d\n', min(trlNum), max(trlNum));
%   fprintf('trials requested = %d - %d\n', begtrial, endtrial);
% end

dimord = 'chans_samples_trials';