%  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 s2 = s_escape_tex ( s1 )

%*****************************************************************************80
%
%% S_ESCAPE_TEX de-escapes TeX escape sequences.
%
%  Discussion:
%
%    In particular, every occurrence of the characters '\', '_',
%    '^', '{' and '}' will be replaced by '\\', '\_', '\^',
%    '\{' and '\}'.  A TeX interpreter, on seeing these character
%    strings, is then likely to return the original characters.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    09 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S1, the string to be de-escaped.
%
%    Output, string S2, a copy of the string, modified to avoid TeX escapes.
%
  s1_length = length ( s1 );

  s1_pos = 0;
  s2_pos = 0;
  s2 = [];

  while ( s1_pos < s1_length )

    s1_pos = s1_pos + 1;

    if ( s1(s1_pos) == '\' || ...
         s1(s1_pos) == '_' || ...
         s1(s1_pos) == '^' || ...
         s1(s1_pos) == '{' || ...
         s1(s1_pos) == '}' )
      s2_pos = s2_pos + 1;
      s2 = strcat ( s2, '\' );
    end

    s2_pos = s2_pos + 1;
    s2 = strcat ( s2, s1(s1_pos) );

  end

  return
end


function [ colptr, rowind ] = hb_structure_read ( input_unit, ncol, mxtype, ...
  nnzero, neltvl, ptrcrd, ptrfmt, indcrd, indfmt )

%*****************************************************************************80
%
%% HB_STRUCTURE_READ reads the structure of an HB matrix.
%
%  Discussion:
%
%    The user should already have opened the file, and positioned it
%    to just after the header records.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 April 2004
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Iain Duff, Roger Grimes, John Lewis,
%    User's Guide for the Harwell-Boeing Sparse Matrix Collection,
%    October 1992.
%
%  Parameters:
%
%    Input, integer INPUT_UNIT, the unit from which data is read.
%
%    Input, integer NCOL, the number of columns.
%
%    Input, character ( len = 3 ) MXTYPE, the matrix type.
%    First character is R for Real, C for complex, P for pattern only.
%    Second character is S for symmetric, U for unsymmetric, H for
%      Hermitian, Z for skew symmetric, R for rectangular.
%    Third character is A for assembled and E for unassembled
%      finite element matrices.
%
%    Input, integer NNZERO.  In the case of assembled sparse matrices,
%    this is the number of nonzeroes.  In the case of unassembled finite
%    element matrices, in which the right hand side vectors are also
%    stored as unassembled finite element vectors, this is the total
%    number of entries in a single unassembled right hand side vector.
%
%    Input, integer NELTVL, the number of finite element matrix entries,
%    set to 0 in the case of assembled matrices.
%
%    Input, integer PTRCRD, the number of pointer records.
%
%    Input, character ( len = 16 ) PTRFMT, the format for reading pointers.
%
%    Input, integer INDCRD, the number of index records.
%
%    Input, character ( len = 16 ) INDFMT, the format for reading indices.
%
%    Output, integer COLPTR(NCOL+1), COLPTR(I) points to the location of
%    the first entry of column I in the sparse matrix structure.
%
%    Output, integer ROWIND(NNZERO) or ROWIND(NELTVL), the row index of
%    each item.
%
  [ p, code, w, m ] = s_to_format ( ptrfmt );

  if ( mxtype(3) == 'A' )
    line_num = 1 + floor ( ( ( ncol + 1 ) - 1 ) / p );
  else
    line_num = 1 + floor ( ( ( ncol     ) - 1 ) / p );
  end

  jhi = 0;

  for i = 1 : line_num
    line = fgetl ( input_unit );
    jlo = jhi + 1;
    if ( mxtype(3) == 'A' )
      jhi = min ( jlo + p - 1, ncol + 1 );
    else
      jhi = min (  jlo + p - 1, ncol );
    end

    khi = 0;

    for j = jlo : jhi
      klo = khi + 1;
      khi = min ( klo + w - 1, length ( line ) );
      s = line(klo:khi);
      colptr(j) = s_to_i4 ( s );
    end

  end

  if ( mxtype(3) == 'A' )

    [ p, code, w, m ] = s_to_format ( indfmt );

    line_num = 1 + floor ( ( nnzero - 1 ) / p );

    jhi = 0;

    for i = 1 : line_num
      line = fgetl ( input_unit );
      jlo = jhi + 1;
      jhi = min ( jlo + p - 1, nnzero );

      khi = 0;

      for j = jlo : jhi
        klo = khi + 1;
        khi = min ( klo + w - 1, length ( line ) );
        s = line(klo:khi);
        rowind(j) = s_to_i4 ( s );
      end

    end

  elseif ( mxtype(3) == 'E' )

    [ p, code, w, m ] = s_to_format ( indfmt );
    number = colptr(ncol) - colptr(1);
    line_num = 1 + floor ( ( number - 1 ) / p );

    jhi = 0;

    for i = 1 : line_num
      line = fgetl ( input_unit );
      jlo = jhi + 1;
      jhi = min ( jlo + p - 1, number );

      khi = 0;

      for j = jlo : jhi
        klo = khi + 1;
        khi = min ( klo + w - 1, length ( line ) );
        s = line(klo:khi);
        rowind(j) = s_to_i4 ( s );
      end

    end

  else

    fprintf ( 1, '\n' );
    fprintf ( 1, 'HB_STRUCTURE_READ - Fatal error!\n' );
    fprintf ( 1, '  Illegal value of MXTYPE(3).\n' );
    return

  end

  return
end


function varargout = inviscidChannelGUI(varargin)
% INVISCIDCHANNELGUI MATLAB code for inviscidChannelGUI.fig
%      INVISCIDCHANNELGUI, by itself, creates a new INVISCIDCHANNELGUI or raises the existing
%      singleton*.
%
%      H = INVISCIDCHANNELGUI returns the handle to a new INVISCIDCHANNELGUI or the handle to
%      the existing singleton*.
%
%      INVISCIDCHANNELGUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in INVISCIDCHANNELGUI.M with the given input arguments.
%
%      INVISCIDCHANNELGUI('Property','Value',...) creates a new INVISCIDCHANNELGUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before inviscidChannelGUI_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to inviscidChannelGUI_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help inviscidChannelGUI

% Last Modified by GUIDE v2.5 20-Feb-2013 10:37:02

% Copyright 2013 The MathWorks, Inc.

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
    'gui_Singleton',  gui_Singleton, ...
    'gui_OpeningFcn', @inviscidChannelGUI_OpeningFcn, ...
    'gui_OutputFcn',  @inviscidChannelGUI_OutputFcn, ...
    'gui_LayoutFcn',  [] , ...
    'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before inviscidChannelGUI is made visible.
function inviscidChannelGUI_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to inviscidChannelGUI (see VARARGIN)

% Initialize the GUI layout
sketchplot(gcf);
set(get(handles.plotstreamline,'XLabel'),'String','x (m)')
set(get(handles.plotstreamline,'YLabel'),'String','y (m)')
set(get(handles.plotstreamline,'Title'),'String','Normalized Stream Function \Psi')
set(get(handles.plotvector,'XLabel'),'String','x (m)')
set(get(handles.plotvector,'YLabel'),'String','y (m)')
set(get(handles.plotvector,'Title'),'String','Velocity Profile')
% Create the help button on toolbar
[X, map] = imread(fullfile(...
    matlabroot,'toolbox','matlab','icons','csh_icon.gif'));
icon = ind2rgb(X,map);
uipushtool(handles.uitoolbar1,'CData',icon,...
    'TooltipString','Help',...
    'ClickedCallback',@AppHelp);

% Choose default command line output for inviscidChannelGUI
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes inviscidChannelGUI wait for user response (see UIRESUME)
% uiwait(handles.figure1);

% --- Outputs from this function are returned to the command line.
function varargout = inviscidChannelGUI_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

% --- Executes on button press in start.
function start_Callback(hObject, eventdata, handles)
% hObject    handle to start (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
inviscidchannel(handles);

function handles = getpara(handles)
% Get user input parameters from the GUI
handles.para.D1 = str2double(get(handles.D1,'String'));
handles.para.D2 = str2double(get(handles.D2,'String'));
handles.para.L1 = str2double(get(handles.L1,'String'));
handles.para.L2 = str2double(get(handles.L2,'String'));
handles.para.U1 = str2double(get(handles.U1,'String'));
handles.para.nx = str2double(get(handles.nx,'String'));
handles.para.ny = str2double(get(handles.ny,'String'));
handles.para.nmax = str2double(get(handles.nmax,'String'));
cla(handles.plotstreamline),cla(handles.plotvector),
axis([handles.plotstreamline handles.plotvector], [0 handles.para.L1+handles.para.L2 0 max(handles.para.D1,handles.para.D2)]),
% Draw the channel shape according to input
if handles.para.D1 < handles.para.D2 % converging channel
    cornerposition = [0 handles.para.D1 handles.para.L1 handles.para.D2-handles.para.D1];
elseif handles.para.D1 > handles.para.D2 % diverging channel
    cornerposition = [handles.para.L1 handles.para.D2 handles.para.L2 handles.para.D1-handles.para.D2];
else
    cornerposition = [0 handles.para.D1 1 1]; % straight channel
end
rectangle('Parent',handles.plotstreamline,'Position',cornerposition,'FaceColor',[0.94 0.94 0.94])
rectangle('Parent',handles.plotvector,'Position',cornerposition,'FaceColor',[0.94 0.94 0.94])

function inviscidchannel(handles)
%% Initialization
% get input parameters
handles = getpara(handles);
% store input parameters with shorter names
D1 = handles.para.D1;
D2 = handles.para.D2;
L1 = handles.para.L1;
L2 = handles.para.L2;
U1 = handles.para.U1;
nx = handles.para.nx;
ny = handles.para.ny;
nmax = handles.para.nmax;
% compute grid length
dx = (L1+L2)/(nx-1);
dy = max(D1,D2)/(ny-1);
% nondimensionalization
dX = dx/D1;
dY = dy/D1;
PSI = ones(ny,nx);
%% Boundary conditions
PSI(1,:) = 0; % bottom
PSI(floor(D1/dy+1),1:floor(L1/dx+1)) = 1; % left part of top
PSI(floor(D2/dy+1),floor(L1/dx+1):nx) = 1; % right part of top
for j = 1:floor(D1/dy+1)
    PSI(j,1) = (j-1)*dY; % inlet
end
for j = 1:floor(D2/dy+1)
    PSI(j,nx) = D1/D2*(j-1)*dY; % outlet
end
%% Iteration to solve for stream function PSI
err = 1e-6; % convergence criteria
n = 0;
while n < nmax
    n = n + 1;
    tempPSI = PSI;
    % left part of the channel
    for i = 2:floor(L1/dx+1)
        for j = 2:(floor(D1/dy+1)-1)
            PSI(j,i) = 1/(2/dX^2+2/dY^2)*((tempPSI(j,i+1)+PSI(j,i-1))/dX^2+(tempPSI(j+1,i)+PSI(j-1,i))/dY^2);
        end
    end
    % right part of the channel
    for i = floor(L1/dx+1):nx-1
        for j = 2:(floor(D2/dy+1)-1)
            PSI(j,i) = 1/(2/dX^2+2/dY^2)*((tempPSI(j,i+1)+PSI(j,i-1))/dX^2+(tempPSI(j+1,i)+PSI(j-1,i))/dY^2);
        end
    end
    % checking for convergence
    if max(max(abs(PSI-tempPSI))) <= err
        break
    end
end
%% Prompt convergence result in a message box
if n < nmax
    helpdlg({'Converged.';sprintf('Total number of iterations: %g\n',n)},...
        'Converged');
else
    warndlg({'NOT Converged!';sprintf('Change n_x, n_y, n_max or convergence criteria.\n')},...
        'NOT Converged');
end
%% Solve for velocity u and v from stream function
u = zeros(ny,nx);
v = zeros(ny,nx);
psi = PSI*U1*D1; % dimentionalization
for j = 2:ny-1
    u(j,:) = (psi(j+1,:)-psi(j-1,:))/2/dy;
end
for i = 2:nx-1
    v(:,i) = -(psi(:,i+1)-psi(:,i-1))/2/dx;
end
% velocity at boundaries (inviscid, so slip occurs)
u(1,:) = u(2,:);
u(ny,:) = u(ny-1,:);
v(:,1) = v(:,2);
v(:,nx) = v(:,nx-1);
%% Plot results
[X, Y] = meshgrid(0:dx:(L1+L2),0:dy:max(D1,D2));
% contour plot of stream function
contourf(handles.plotstreamline,X,Y,PSI),colormap(handles.plotstreamline,'jet')
% vector plot of velocity u and v
quiver(handles.plotvector,X,Y,u,v),
axis([handles.plotstreamline handles.plotvector],[0 L1+L2 0 max(D1,D2)]),
% plot the shape of the channel based on input parameters
if D1 < D2
    cornerposition = [0 D1 L1 D2-D1];
elseif D1 > D2
    cornerposition = [L1 D2 L2 D1-D2];
else
    cornerposition = [0 D1 1 1];
end
rectangle('Parent',handles.plotstreamline,'Position',cornerposition,'FaceColor',[0.94 0.94 0.94])
rectangle('Parent',handles.plotvector,'Position',cornerposition,'FaceColor',[0.94 0.94 0.94])
set(get(handles.plotstreamline,'XLabel'),'String','x (m)')
set(get(handles.plotstreamline,'YLabel'),'String','y (m)')
set(get(handles.plotstreamline,'Title'),'String','Normalized Stream Function \Psi')
set(get(handles.plotvector,'XLabel'),'String','x (m)')
set(get(handles.plotvector,'YLabel'),'String','y (m)')
set(get(handles.plotvector,'Title'),'String','Velocity Profile')

function sketchplot(h)
%Sketch the plot of the channel to show geometric parameters
% and governing equations. You can learn how to draw sketches
% programmatically and write LaTeX. Don't worry about it otherwise,
% since it has nothing to do with computation.

% Create doublearrow
d1 = annotation(h,'doublearrow',[0.06 0.06],...
    [0.6 0.8]);

% Create doublearrow
l1 = annotation(h,'doublearrow',[0.06 0.15],...
    [0.6 0.6]);

% Create doublearrow
l2 = annotation(h,'doublearrow',[0.15 0.3],...
    [0.6 0.6]);

% Create doublearrow
d2 = annotation(h,'doublearrow',[0.3 0.3],...
    [0.7 0.6]);

% Create arrow U
annotation(h,'arrow',[0.02 0.06],...
    [0.7 0.7],...
    'HeadStyle','Plain',...
    'HeadWidth',5);

% Create textbox U
annotation(h,'textbox',...
    [0.02 0.67 0.03 0.07],...
    'String',{'U'},...
    'FitBoxToText','off',...
    'LineStyle','none');

% Create textbox
annotation(h,'textbox',...
    [0.06 0.65 0.03 0.07],...
    'String',{'D1'},...
    'FitBoxToText','off',...
    'LineStyle','none');

% Create textbox
annotation(h,'textbox',...
    [0.1 0.58 0.03 0.07],...
    'String',{'L1'},...
    'FitBoxToText','off',...
    'LineStyle','none');

% Create textbox
annotation(h,'textbox',...
    [0.22 0.58 0.03 0.07],...
    'String',{'L2'},...
    'FitBoxToText','off',...
    'LineStyle','none');

% Create textbox
annotation(h,'textbox',...
    [0.25 0.61 0.03 0.07],...
    'String',{'D2'},...
    'FitBoxToText','off',...
    'LineStyle','none');

% Create channel
annotation(h,'line',...
    [0.05 0.15],[0.8 0.8],...
    'LineWidth',2);

% Create channel
annotation(h,'line',...
    [0.15 0.31],[0.7 0.7],...
    'LineWidth',2);

% Create channel
annotation(h,'line',...
    [0.15 0.15],[0.7 0.8],...
    'LineWidth',2);

% Create channel
annotation(h,'line',...
    [0.05 0.31],[0.58 0.58],...
    'LineWidth',2);

% Create textbox equations
annotation(h,'textbox',[0.32 0.73 0.03 0.07],...
    'String',{'\partial^2\psi/\partialx^2 + \partial^2\psi/\partialy^2 = 0','','u = \partial\psi/\partialy','', 'v = -\partial\psi/\partialx'},...
    'FitBoxToText','on',...
    'LineStyle','none');

function AppHelp(varargin)
% Create help message
dlgname = 'About Inviscid Channel App';
txt = {'Inviscid flow in a channel (Laplace equation of stream function)';
    'solved with explicit method';
    '';
    'Start - Start the simulation';
    'Solution Code - Open a pre-written script to show the solution code';
    '';
    '* Try D1 > D2, D1 < D2 or D1 = D2.';
    '* Use zoom, pan and data curser in the toolbar to interact with the';
    '  plots as usual';
    '* If result does not  converge, try:';
    '       a) increasing n_max (total number of iterations)';
    '       b) decreasing n_x, n_y (number of grids in each direction)';
    '';
    'Copyright 2013 The MathWorks, Inc.'};
helpdlg(txt,dlgname);

function D1_Callback(hObject, eventdata, handles)
% hObject    handle to D1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
getpara(handles);
% Hints: get(hObject,'String') returns contents of D1 as text
%        str2double(get(hObject,'String')) returns contents of D1 as a double

function D2_Callback(hObject, eventdata, handles)
% hObject    handle to D2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
getpara(handles);
% Hints: get(hObject,'String') returns contents of D2 as text
%        str2double(get(hObject,'String')) returns contents of D2 as a double

function L1_Callback(hObject, eventdata, handles)
% hObject    handle to L1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
getpara(handles);
% Hints: get(hObject,'String') returns contents of L1 as text
%        str2double(get(hObject,'String')) returns contents of L1 as a double

function L2_Callback(hObject, eventdata, handles)
% hObject    handle to L2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
getpara(handles);
% Hints: get(hObject,'String') returns contents of L2 as text
%        str2double(get(hObject,'String')) returns contents of L2 as a double

% --- Executes on button press in solution.
function solution_Callback(hObject, eventdata, handles)
% hObject    handle to solution (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
open('inviscidChannelScript.m');


function [stop reason] = stoppingcriterion(problem, x, options, info, last)
% Checks for standard stopping criteria, as a helper to solvers.
%
% function [stop reason] = stoppingcriterion(problem, x, options, info, last)
%
% Executes standard stopping criterion checks, based on what is defined in
% the info(last) stats structure and in the options structure.
%
% The returned number 'stop' is 0 if none of the stopping criteria
% triggered, and a (strictly) positive integer otherwise. The integer
% identifies which criterion triggered:
%  0 : Nothing triggered;
%  1 : Cost tolerance reached;
%  2 : Gradient norm tolerance reached;
%  3 : Max time exceeded;
%  4 : Max iteration count reached;
%  5 : Maximum number of cost evaluations reached;
%  6 : User defined stopfun criterion triggered.
%
% The output 'reason' is a string describing the triggered event.

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Dec. 30, 2012.
% Contributors:
% Change log:

    stop = 0;
    reason = '';

    stats = info(last);

    % Target cost attained
    if isfield(stats, 'cost') && isfield(options, 'tolcost') && ...
       stats.cost <= options.tolcost
        reason = 'Cost tolerance reached. See options.tolcost.';
        stop = 1;
        return;
    end

    % Target gradient norm attained
    if isfield(stats, 'gradnorm') && isfield(options, 'tolgradnorm') && ...
       stats.gradnorm < options.tolgradnorm
        reason = 'Gradient norm tolerance reached. See options.tolgradnorm.';
        stop = 2;
        return;
    end

    % Alloted time exceeded
    if isfield(stats, 'time') && isfield(options, 'maxtime') && ...
       stats.time >= options.maxtime
        reason = 'Max time exceeded. See options.maxtime.';
        stop = 3;
        return;
    end

    % Alloted iteration count exceeded
    if isfield(stats, 'iter') && isfield(options, 'maxiter') && ...
       stats.iter >= options.maxiter
        reason = 'Max iteration count reached. See options.maxiter.';
        stop = 4;
        return;
    end

    % Alloted function evaluation count exceeded
    if isfield(stats, 'costevals') && isfield(options, 'maxcostevals') && ...
       stats.costevals >= options.maxcostevals
        reason = 'Maximum number of cost evaluations reached. See options.maxcostevals.';
        stop = 5;
    end

    % Check whether the possibly user defined stopping criterion
    % triggers or not.
    if isfield(options, 'stopfun')
        userstop = options.stopfun(problem, x, info, last);
        if userstop
            reason = 'User defined stopfun criterion triggered. See options.stopfun.';
            stop = 6;
            return;
        end
    end

end


function varargout = gsynsq(varargin)
% function hgui = gsynsq()
%
% Runs the Synchrosqueezing toolbox GUI.
% Does not require input parameters.  Returns a handle to the GUI.
%
% This GUI tool can be used for speedy/rough Synchrosqueezing
% analysis, filtering, component extraction of signals.
%
%
%---------------------------------------------------------------------------------
%    Synchrosqueezing Toolbox
%    Authors: Eugene Brevdo (http://www.math.princeton.edu/~ebrevdo/)
%---------------------------------------------------------------------------------

% Begin initialization code - DO NOT EDIT
gui_Singleton = 0;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @gsynsq_OpeningFcn, ...
                   'gui_OutputFcn',  @gsynsq_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before gsynsq is made visible.
function gsynsq_OpeningFcn(hObj, eventdata, hs, varargin)
% This function has no output args, see OutputFcn.
% hObj    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
% varargin   command line arguments to gsynsq (see VARARGIN)

% Choose default command line output for gsynsq
hs.output = hObj;

% This sets up the initial plot - only do when we are invisible
% so window can get raised using gsynsq.
if strcmp(get(hObj,'Visible'),'off')
    set(hs.axesx, 'Visible', 'off');
    set(hs.axesTx, 'Visible', 'off');
    set(hs.axesrx, 'Visible', 'off');

    % Set up zoom
    zh = zoom;

    setAllowAxesZoom(zh, hs.axesx, true);
    setAllowAxesZoom(zh, hs.axesTx, true);
    setAllowAxesZoom(zh, hs.axesrx, true);

    % Link the axes w.r.t. zoom (just the t-axis component)
    linkaxes([hs.axesx hs.axesTx hs.axesrx], 'x');

    % Menu bar with "reset axes" does not work with linked axes
    set(zh, 'RightClickAction', 'InverseZoom');

    % Append any past ActionPostCallBack to run first
    %set(zh, 'ActionPostCallback', @postZoomUpdate);

    % Allow zoom
    set(zh, 'Enable', 'on');

    hs.zh = zh;

    % Initialize internal data structure placeholders
    hs = clear_data(hs);
end

% Update hs structure
guidata(hObj, hs);

% UIWAIT makes gsynsq wait for user response (see UIRESUME)
% uiwait(hs.figure1);

% --- Outputs from this function are returned to the command line.
function varargout = gsynsq_OutputFcn(hObj, eventdata, hs)
% varargout  cell array for returning output args (see VARARGOUT);
% hObj    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% Get default command line output from hs structure
varargout{1} = hs.output;

% --------------------------------------------------------------------
function FileMenu_Callback(hObj, eventdata, hs)
% hObj    handle to FileMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% --------------------------------------------------------------------
function OpenMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to OpenMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
file = uigetfile('*.mat');
if ~isequal(file, 0)
    try
        tx = load(file);
        assert(isfield(tx, 't'));
        assert(isfield(tx, 'x'));
        assert(length(tx.t) == length(tx.x));
    catch ME
        errordlg(sprintf('File load error: %s', ME.message), ...
                 'File Load Error');
        return;
    end

    load_data(hObj, eventdata, hs, file, tx);
end

function load_data(hObj, eventdata, hs, file, tx)
% Do something here about old data, if any?
if (hs.active && ~hs.saved)
end

% Load up the data.  First clear old data
hs = clear_data(hs);
hs.data.t = tx.t;
hs.data.x = tx.x - mean(tx.x);

[fpath, fname] = fileparts(file);
hs.prefix = fullfile(fpath, fname);

% Plot axes
plot(hs.axesx, hs.data.t, hs.data.x);
set(hs.axesx, 'Visible', 'on');
grid(hs.axesx, 'on');
axis(hs.axesx,'tight');
setAxesZoomMotion(hs.zh, hs.axesx, 'horizontal');
set(hs.axesx,'FontSize',10);

% Hide other axes and delete colorbars, if any
axes(hs.axesTx);
colorbar('delete');

cla(hs.axesTx);
set(hs.axesTx, 'Visible', 'off');
cla(hs.axesrx);
set(hs.axesrx, 'Visible', 'off');

% Save app data
guidata(hObj, hs);

% --------------------------------------------------------------------
function PrintMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to PrintMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
printdlg(hs.figure1)

% --------------------------------------------------------------------
function ExitMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to CloseMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
figure1_CloseRequestFcn(hObj, eventdata, hs);

% --- Executes when user attempts to close figure1.
function figure1_CloseRequestFcn(hObj, eventdata, hs)
% hObj    handle to figure1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% If the analysis has been modified but not been saved
if (hs.active && ~hs.saved)
    selection = questdlg(['Unsaved Analysis.  Close ' get(hs.figure1,'Name') '?'],...
        ['Close ' get(hs.figure1,'Name') '...'],...
        'Yes','No','Yes');
    if strcmp(selection,'No')
        return;
    end
end

% Closes the figure
delete(hs.output);

% Initializes / clears data from last job from hs
function hs = clear_data(hs)
% Is there an analysis active?
hs.active = 0;
% Has it been saved since the last change?
hs.saved = 0;

% What is the file prefix of the current analysis?
hs.prefix = '';

% Default parameters for the transforms
hs.padtype = 'symmetric';
% Number of voices
hs.nv = 32;
% Wavelet type and other options
hs.opt = struct('disp', 1, 'bd', 1, 'gamma', sqrt(eps), 'type', 'morlet');

% Placeholders for the input data
hs.data = [];

% Placeholders for the results of the analysis part
hs.data.as = [];
hs.data.fs = [];
hs.data.Wx = [];
hs.data.Tx = [];

% Placeholders for the results of the synthesis part
hs.data.rx = [];

% Placeholder for the results of synthesis after removing padding
%hs.data.rx0 = [];

% Placeholder for the extracted curves
hs.data.Cs = [];
hs.data.xrs = [];

% Attenuation filters
if isfield(hs, 'filters')
    for hi = 1:length(hs.filters)
        delete(hs.filters(hi).f);
    end
end
hs.filters = [];

% --------------------------------------------------------------------
function openpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to openpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
OpenMenuItem_Callback(hObj, eventdata, hs);

% --------------------------------------------------------------------
function savepush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to savepush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
SaveMenuItem_Callback(hObj, eventdata, hs);

% --------------------------------------------------------------------
function zoomtoggle_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to zoomtoggle (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% ZOOM

% --------------------------------------------------------------------
function zoomtoggle_OffCallback(hObj, eventdata, hs)
% hObj    handle to zoomtoggle (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% Disable zoom
zoom_disable(hs);

function zoom_disable(hs)
set(hs.zoomtoggle, 'State', 'off');
set(hs.ZoomMenuItem, 'Checked', 'off');
set(hs.zh, 'Enable', 'off');

% --------------------------------------------------------------------
function zoomtoggle_OnCallback(hObj, eventdata, hs)
% hObj    handle to zoomtoggle (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% Allow zoom
%if strcmp(get(hObj,'Visible'),'off')
zoom_enable(hs);
%end

function zoom_enable(hs)
set(hs.zoomtoggle, 'State', 'on');
set(hs.zh, 'Enable', 'on');
set(hs.ZoomMenuItem, 'Checked', 'on');

%%% NOT USED %%%
function postZoomUpdate(hObj, eventdata)
% Get the new horizontal values of the updated axes
XLim = get(eventdata.Axes, 'XLim');

% Update the values for each of the axes
hs = guidata(hObj);

XLim(1) = max(XLim(1), min(hs.data.t));
XLim(2) = min(XLim(2), max(hs.data.t));
set(eventdata.Axes, 'XLim', XLim);

if (eventdata.Axes ~= hs.axesTx), set(hs.axesTx, 'XLim', XLim); end
if (eventdata.Axes ~= hs.axesx), set(hs.axesx, 'XLim', XLim); end
if (eventdata.Axes ~= hs.axesrx), set(hs.axesrx, 'XLim', XLim); end

% --------------------------------------------------------------------
function SaveMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to SaveMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
if (~hs.active || hs.saved)
    return;
end

file = uiputfile(sprintf('%s_r.mat', hs.prefix));
if ~isequal(file, 0)
    try
        data = hs.data;
        save(file, '-struct', 'data');
    catch ME
        errordlg(sprintf('File save error: %s', ME.message), ...
            'File Save Error');
        return;
    end

    hs.saved = 1;
    guidata(hObj, hs);
end

% --------------------------------------------------------------------
function ExportMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to ExportMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
if (~hs.active), return; end
labels = {'Export t?', 'Export x(t)?', 'Export as?', 'Export Wx(a,t)?', ...
          'Export fs?', 'Export Tx(f,t)?', 'Export rx(t) (reconstructed)?', ...
          'Export filters?', 'Export identified contours?', ...
          'Export extracted components?'};
varnames = {'t', 'x', 'as', 'Wx', 'fs', 'Tx', 'rx', ...
            'filters', 'Cs', 'xrs'};
filters = hs.filters;
for fi=1:length(filters)
    filters(fi).bbox = filters(fi).f.getPosition();
    filters(fi).mask = filters(fi).f.createMask();
    % bbox = [tmin tmax fmin fmax]
    % filters(fi).bbox = [filters(fi).bbox([1 3])];
    tmin = filters(fi).bbox(1);
    tmax = tmin + filters(fi).bbox(3);
    flogmin = filters(fi).bbox(2);
    flogmax = flogmin + filters(fi).bbox(4);
    filters(fi).bbox = [tmin tmax 2^flogmin 2^flogmax];
end
if length(filters)>0, filters = rmfield(filters, 'f'); end
vars = {hs.data.t, hs.data.x, hs.data.as, ...
        hs.data.Wx, hs.data.fs, hs.data.Tx, hs.data.rx, ...
        filters, hs.data.Cs, hs.data.xrs};
selected = logical([0 0 0 0 0 0 0 0 0 0]);
export2wsdlg(labels, varnames, vars, 'Export to Workspace', selected);

% TODO - export to a variable with proper prefix and name of choice

% --------------------------------------------------------------------
function AnalysisMenu_Callback(hObj, eventdata, hs)
% hObj    handle to AnalysisMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% --------------------------------------------------------------------
function AnalysisMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to AnalysisMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_analysis(hObj, eventdata, hs);

function run_analysis(hObj, eventdata, hs)
if (isempty(hs.data))
    errordlg('Load a data file first', 'Error');
    return;
end

prompt = {'Wavelet [morlet,bump,hhhat,...]', ...
          'Number of Voices (nv)', ...
          'Pad type [symmetric, circular, replicate]', ...
          'Gamma (\gamma)'};
name = 'Synchrosqueezing Analysis Parameters';
answers = {hs.opt.type, num2str(hs.nv), hs.padtype, num2str(hs.opt.gamma)};
idata = inputdlg(prompt, name, 1, answers, struct('WindowStyle', 'normal'));
if isempty(idata)
    return
end

hs.opt.type = idata{1};
hs.nv = str2num(idata{2});
hs.padtype = idata{3};
hs.opt.gamma = str2num(idata{4});

[hs.data.Tx, hs.data.fs, hs.data.Wx, hs.data.as] = ...
    synsq_cwt_fw(hs.data.t, hs.data.x, hs.nv, hs.opt);

axes(hs.axesTx);
cla(hs.axesTx);
tplot(hs.data.Tx, hs.data.t, hs.data.fs, hs.opt);
set(hs.axesTx, 'Visible', 'on');
grid(hs.axesTx, 'on');
setAxesZoomMotion(hs.zh, hs.axesTx, 'vertical');
set(hs.axesTx,'FontSize',10);
hs.filters = [];

hs.active = 1;

% Store application parameters
guidata(hObj, hs);

% --------------------------------------------------------------------
function ReconMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to ReconMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_recon(hObj, eventdata, hs);

function run_recon(hObj, eventdata, hs);

if (~hs.active)
    errordlg('Run Analysis First', 'Error');
    return;
end

Tx = filter_Tx(hs);

hs.data.rx = synsq_cwt_iw(Tx, hs.data.fs, hs.opt);

plot(hs.axesrx, hs.data.t, hs.data.rx);
grid(hs.axesrx,'on');
set(hs.axesrx, 'Visible', 'on');
set(hs.axesrx,'FontSize',10);
axis(hs.axesrx, [min(hs.data.t) max(hs.data.t) min(hs.data.x) max(hs.data.x)]);

XLim = get(hs.axesx, 'XLim'); set(hs.axesrx, 'XLim', XLim);
setAxesZoomMotion(hs.zh, hs.axesrx, 'horizontal');
%zoom_enable(hs);

guidata(hObj, hs);

function Tx = filter_Tx(hs)
% Logic to see if any attenuation and/or passband filters are active
Tx = hs.data.Tx;
if ~isempty(hs.filters)
    % Step 1: combine all pass-band filters
    maskp = zeros(size(hs.data.Tx));
    maska = ones(size(hs.data.Tx));
    nmp = 0;
    for fi=1:length(hs.filters)
        mask = hs.filters(fi).f.createMask();
        if strcmpi(hs.filters(fi).type, 'atten')
            maska = maska & ~mask;
        else
            maskp = maskp | mask;
            nmp = nmp + 1;
        end
    end

    % Mask via the filters
    if (nmp > 0)
        Tx = Tx .* maskp .* maska;
    else
        Tx = Tx .* maska;
    end
end

% --------------------------------------------------------------------
function FiltersMenu_Callback(hObj, eventdata, hs)
% hObj    handle to FiltersMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% --------------------------------------------------------------------
function RectAtten_Callback(hObj, eventdata, hs)
% hObj    handle to RectAtten (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'recta')

% --------------------------------------------------------------------
function filter_enable(hObj, hs, type)

% Check to make sure analysis is active
if (~hs.active)
    return;
end

% Disable zoom icon
set(hs.zoomtoggle, 'State', 'off');

% Start filter selection - this disabled zoom
fhs = filter_construct(hs, type);

% Make sure that it's within the proper constraints at first
for fhi=1:length(fhs)
    fh = fhs(fhi);
    fh.setConstrainedPosition(fh.getPosition());

    if strcmpi(type(end), 'a')
        hs.filters = [hs.filters struct('type', 'atten', 'f', fh)];
    else
        hs.filters = [hs.filters struct('type', 'pass', 'f', fh)];
    end
end

% Save
guidata(hObj, hs);

function h = filter_construct(hs, type)
switch type
  case 'recta',
    h = imrect(hs.axesTx);
  case 'ellipsea',
    h = imellipse(hs.axesTx);
  case 'polya',
    h = impoly(hs.axesTx);
  case 'rectp',
    h = imrect(hs.axesTx);
  case 'bda',
    t = hs.data.t(:);
    fs = [hs.data.fs(1), hs.data.fs(2:hs.nv:end-1), hs.data.fs(end)]; % Subsample
    [Lbdi, Rbdi] = synsq_bd_loc(t, fs);
    fsbd = [fs(1); fs(:); fs(end)];
    Lbdi = [1; Lbdi(:); 1];
    Rbdi = [length(t); Rbdi(:); length(t)];

    hL = impoly(hs.axesTx, [t(Lbdi) log2(fsbd)]);
    hR = impoly(hs.axesTx, [t(Rbdi) log2(fsbd)]);
    h = [hL hR];
  otherwise,
    error('Unknown filter type');
end

for j=1:length(h)
    hj = h(j);

    cstrstr = class(hj);

    % Set constraints
    constrfcn = makeConstrainToRectFcn(cstrstr, ...
      [min(hs.data.t)-sqrt(eps) max(hs.data.t)], ...
      [min(log2(hs.data.fs))-sqrt(eps) max(log2(hs.data.fs))]);
    hj.setPositionConstraintFcn(constrfcn);

    % Set color
    if strcmpi(type(end), 'a')
        hj.setColor('r');
    else
        hj.setColor('g');
    end
end

% --- Executes on mouse press over axes background.
function axesTx_ButtonDownFcn(hObj, eventdata, hs)
% hObj    handle to axesTx (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% Is not called due to axis ownership by zoom object

% --------------------------------------------------------------------
function EllipseAtten_Callback(hObj, eventdata, hs)
% hObj    handle to EllipseAtten (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'ellipsea')

% --------------------------------------------------------------------
function PolyAtten_Callback(hObj, eventdata, hs)
% hObj    handle to PolyAtten (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'polya')

% --------------------------------------------------------------------
function filterselector_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to filterselector (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% FILTER SELECTOR

% --------------------------------------------------------------------
function filterselector_OffCallback(hObj, eventdata, hs)
% hObj    handle to filterselector (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% FILTER SELECTOR

% --------------------------------------------------------------------
function filterselector_OnCallback(hObj, eventdata, hs)
% hObj    handle to filterselector (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% FILTER SELECTOR

% --------------------------------------------------------------------
function rectattenpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to rectattenpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'recta')

% --------------------------------------------------------------------
function polyattenpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to polyattenpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'polya')

% --------------------------------------------------------------------
function ellipseattenpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to ellipseattenpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'ellipsea')

% --------------------------------------------------------------------
function analysispush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to analysispush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_analysis(hObj, eventdata, hs);

% --------------------------------------------------------------------
function reconpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to reconpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_recon(hObj, eventdata, hs);

% --------------------------------------------------------------------
function ZoomMenuItem_Callback(hObject, eventdata, handles)
% hObject    handle to ZoomMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if strcmpi(get(handles.zh, 'Enable'), 'on'),
    zoomtoggle_OffCallback(hObject, eventdata, handles);
else
    zoomtoggle_OnCallback(hObject, eventdata, handles);
end

% --------------------------------------------------------------------
function ToolsMenu_Callback(hObject, eventdata, handles)
% hObject    handle to ToolsMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --------------------------------------------------------------------
function ImportMenuItem_Callback(hObject, eventdata, handles)
% hObject    handle to ImportMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

prompts = {'Import t variable: ', 'Import x(t) variable: ', 'Prefix: '};
name = 'Import signal from workspace';
danswers = {'t', 'x', 'ws'};
idata = inputdlg(prompts, name, 1, danswers, struct('WindowStyle', 'normal'));

% Cancelled
if isempty(idata),
    return;
end

try
    tx = struct('t', evalin('base', idata{1}), ...
        'x', evalin('base', idata{2}));
    assert(length(tx.t) == length(tx.x));
catch ME
    errordlg(sprintf('Import error: %s', ME.message), ...
        'Import Error');
        return;
end

% Set some file prefix in the current directory
file = fullfile(pwd, idata{3});

load_data(hObject, eventdata, handles, file, tx);

% --------------------------------------------------------------------
function rectpasspush_ClickedCallback(hObj, eventdata, hs)
% hObject    handle to rectpasspush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'rectp');

% --------------------------------------------------------------------
function RectPass_Callback(hObj, eventdata, hs)
% hObject    handle to RectPass (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'rectp');

% --------------------------------------------------------------------
function ExtractMenuItem_Callback(hObject, eventdata, handles)
% hObject    handle to ExtractMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
extract_contour(hObject, handles);

% Extract a contour
function extract_contour(hObj, hs)

if (~hs.active)
    errordlg('Run Analysis First', 'Error');
    return;
end

prompt = {'Number of Curves to Extract', ...
          'Lambda (smoothness penalty)', ...
         'Extraction window (n_w)'};
name = 'Contour Extraction Parameters';
numlines = 1;
answers = {'2', '1e4', num2str(hs.nv/2)};

idata = inputdlg(prompt, name, numlines, answers, struct('WindowStyle', 'normal'));

% User cancelled
if isempty(idata),
    return;
end

nc = str2num(idata{1});
lambda = str2num(idata{2});
nw = str2num(idata{3});

Tx = filter_Tx(hs);
[na, N] = size(Tx);

% Extract curves for plotting
[hs.data.Cs,Es] = curve_ext_multi(Tx, log2(hs.data.fs), nc, lambda, nw);

% Extract functions for export
hs.data.xrs = curve_ext_recon(Tx, hs.data.fs, hs.data.Cs, hs.opt, nw);

% Save
guidata(hObj, hs);

figure;
hc = plot_ext_curves(hs.data.t, hs.data.x, ...
                     Tx, hs.data.fs, hs.data.Cs, Es, hs.opt);

% --------------------------------------------------------------------
function extractpush_ClickedCallback(hObject, eventdata, handles)
% hObject    handle to extractpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
extract_contour(hObject, handles);

% --------------------------------------------------------------------
function AttenBd_Callback(hObj, eventdata, hs)
% hObject    handle to AttenBd (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'bda');

% --------------------------------------------------------------------
function attenbdpush_ClickedCallback(hObj, eventdata, hs)
% hObject    handle to attenbdpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'bda');


function F = restrict(F, newDomain)
%RESTRICT   Restrict a CHEBFUN object to a subinterval.
%   G = RESTRICT(F, [S1, S2]) returns a CHEBFUN G defined on the interval [S1,
%   S2] which agrees with F on that interval. Any interior breakpoints in
%   F.DOMAIN within [S1, S2] are kept in G.DOMAIN.
%
%   G = RESTRICT(F, S), where S is a row vector, will introduce additional
%   interior breakpoints at S(2:end-2).
%
%   In both cases, if S(1) > S(end), S(1) < F.domain(1), or S(end) >
%   F.domain(end), then an error is returned. If S is empty or a scalar, then an
%   empty CHEBFUN G is returned.
%
%   Note that G will not be 'simplified'. If this is required, call G =
%   SIMPLIFY(RESTRICT(F)), or G = F{S}.
%
% See also OVERLAP, SUBSREF, DEFINE, SIMPLIFY.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Tweak the domain of a quasimatrix input:
[F, newDomain] = tweakDomain(F, newDomain);

% Loop over the columns:
for k = 1:numel(F)
    F(k) = columnRestrict(F(k), newDomain);
end

end

function f = columnRestrict(f, newDomain)

% Ignore duplicate entries in newDomain:
newDomain([false, ~diff(newDomain)]) = [];

% Empty case:
if ( isempty(f) )
    return
elseif ( isempty(newDomain) || numel(newDomain) == 1 )
    f = chebfun();
    return
end

% Cast a periodic chebfun to a regualr chebfun:
if ( isPeriodicTech(f) )
    f = chebfun(f);
end

% Grab domain from f:
oldDomain = f.domain;

% Trivial case:
if ( (numel(newDomain) == 2) && domainCheck(f, newDomain([1, end])) )
    % The domains are the same!
    return
end

% Check the domain is valid:
if ( (newDomain(1) < oldDomain(1)) || (newDomain(end) > oldDomain(end)) || ...
        any(diff(newDomain) < 0) )
    % newDom is not a valid subinterval of oldDom!
    error('CHEBFUN:CHEBFUN:restrict:subdom', 'Not a valid subdomain.');
end

% Obtain FUN cell and pointValues from f:
funs = f.funs;
pointValues = f.pointValues;

% Discard intervals to the left:
discardIntsLeft = oldDomain(2:end) < newDomain(1);
oldDomain([discardIntsLeft, false]) = [];
funs(discardIntsLeft) = [];
pointValues([discardIntsLeft, false],:,:) = [];

% Discard intervals to the right:
discardIntsRright = oldDomain(1:end-1) > newDomain(end);
oldDomain([false, discardIntsRright]) = [];
funs(discardIntsRright) = [];
pointValues([false, discardIntsRright],:,:) = [];

% Take the union of the new and old domains:
if ( ~isempty( oldDomain(2:end-1) ) ) % Required due to Matlab union() behavior.
    % NB: the old endpoints will either be in newDomain or are not required.
    newDomain = union(oldDomain(2:end-1), newDomain);
end
numFuns = numel(funs);

% Initialise storage for new FUN objects and pointValues:
newFuns = cell(1, numel(newDomain)-1);

% Loop through each fun and restrict as required:
l = 0;
for k = 1:numFuns
    % Find the breaks which correspond to the kth fun:
    subsIdx = (newDomain >= oldDomain(k)) & (newDomain <= oldDomain(k+1));
    if ( sum(subsIdx) == 2 )
        % This interval is already a FUN: (i.e., no new breaks to introduce)
        newFuns{l+1} = restrict(funs{k}, newDomain(subsIdx));
        l = l + 1;
    else
        numSubs = sum(subsIdx)-1;
        if ( numSubs > 0 )
            % Restrict the FUN at the corresponding break points:
            newFuns(l+(1:numSubs)) = restrict(funs{k}, newDomain(subsIdx));
            l = l + numSubs;
        end
    end
end

% Update the pointValues:
newPointValues = chebfun.getValuesAtBreakpoints(newFuns);
% Restore existing pointValues:
[mask, locB] = ismember(oldDomain, newDomain);
locB(~logical(locB)) = [];
newPointValues(locB,:) = pointValues(mask,:);

% Attach data to CHEBFUN to return as output:
f.domain = newDomain;
f.funs = newFuns;
f.pointValues = newPointValues;

end


function [Lmk, Frm, Fac] = makeMeasFactor(Lmk, Obs, Frm, Fac)

% MAKEMEASFACTOR Make measurement factor.
%   [Lmk, Frm, Fac] = MAKEMEASFACTOR(Lmk, Obs, Frm, Fac) creates a new
%   measurement factor Fac linking frame Frm to landmark Lmk, using the
%   observation informaiton in Obs.

%   Copyright 2015-     Joan Sola @ IRI-UPC-CSIC

Fac.used = true; % Factor is being used ?
Fac.id = newId; % Factor unique ID

Fac.type = 'measurement'; % {'motion','measurement','absolute'}
Fac.rob = Frm.rob;
Fac.sen = Obs.sen;
Fac.lmk = Obs.lmk;
Fac.frames = Frm.frm;

% Ranges
Fac.state.r1 = Frm.state.r;
Fac.state.r2 = Lmk.state.r;

% Measurement is the straight data
Fac.meas.y = Obs.meas.y;
Fac.meas.R = Obs.meas.R;
Fac.meas.W = Obs.meas.R^-1; % measurement information matrix

% Expectation has zero covariance -- and info in not defined
Fac.exp.e = Fac.meas.y; % expectation
Fac.exp.E = zeros(size(Obs.meas.R)); % expectation cov
%     Fac.exp.W = Fac.meas.W; % expectation information matrix

% Error is zero at this stage, and takes covariance and info from measurement
Fac.err.z     = zeros(size(Fac.meas.y)); % error or innovation (we call it error because we are on graph SLAM)
Fac.err.Z     = Fac.meas.R;              % error cov matrix
Fac.err.W     = Fac.meas.W;              % error information matrix
Fac.err.Wsqrt = chol(Fac.err.W);

% Jacobians are zero at this stage. Just make size correct.
Fac.err.J1 = Obs.Jac.E_r; % Jac. of error wrt. node 1 - robot pose
Fac.err.J2 = Obs.Jac.E_l; % Jac. of error wrt. node 2 - lmk state

Fac.err.size  = numel(Fac.err.z);

% Append factor to Frame's and Lmk's factors lists.
Frm.factors = [Frm.factors Fac.fac];
Lmk.factors = [Lmk.factors Fac.fac];

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB 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.
%
%   SLAMTB 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 SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


function mm_header_print ( input_filename, id, type, rep, field, symm )

%*****************************************************************************80
%
%% MM_HEADER_PRINT prints header information from a Matrix Market file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    02 May 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character ( len = * ) INPUT_FILENAME, the input file name.
%
%    Input, character ( len = 14 ) ID, the Matrix Market identifier.
%    This value must be '%%MatrixMarket'.
%
%    Input, character ( len = 6 ) TYPE, the Matrix Market type.
%    This value must be 'matrix'.
%
%    Input, character ( len = 10 ) REP, the Matrix Market 'representation'
%    indicator.  Possible values include:
%    'coordinate'   (for sparse data)
%    'array'        (for dense data)
%    'elemental'    (to be added)
%
%    Input, character ( len = 7 ) FIELD, the Matrix Market 'field'.
%    Possible values include:
%    'real'
%    'double'
%    'complex'
%    'integer'
%    'pattern'
%
%    Input, character ( len = 19 ) SYMM, the Matrix Market symmetry.
%    Possible values include:
%    'symmetric'
%    'hermitian'
%    'skew-symmetric'
%    'general'
%
  fprintf ( 1, '\n');
  fprintf ( 1, 'MM_HEADER_PRINT:\n' );
  fprintf ( 1, '  Header information from Matrix Market file "%s".\n', ...
    input_filename );
  fprintf ( 1, '\n');
  fprintf ( 1, '  Matrix Market ID = "%s".\n', id );
  fprintf ( 1, '    "%%%%MatrixMarket" is only allowed value.\n' );
  fprintf ( 1, '\n');
  fprintf ( 1, '  Matrix Market TYPE = "%s".\n', type );
  fprintf ( 1, '    "matrix" is only allowed value.\n' );
  fprintf ( 1, '\n');
  fprintf ( 1, '  Representation type REP = "%s".\n', rep );
  fprintf ( 1, '    "coordinate" for sparse matrices,\n' );
  fprintf ( 1, '    "array"      for dense matrices,\n' );
  fprintf ( 1, '    "elemental"  for unassembled finite element matrices.\n' );
  fprintf ( 1, '\n');
  fprintf ( 1, '  Numeric FIELD = "%s".\n', field );
  fprintf ( 1, '    "integer" for integer values,\n' );
  fprintf ( 1, '    "real"    for real values,\n' );
  fprintf ( 1, '    "double"  for double precision real values,\n' );
  fprintf ( 1, '    "complex" for complex values,\n' );
  fprintf ( 1, '    "pattern" for nonzero pattern only.\n' );
  fprintf ( 1, '\n');
  fprintf ( 1, '  Symmetry type SYMM = "%s".\n', symm );
  fprintf ( 1, '    "general"         no symmetry,\n' );
  fprintf ( 1, '    "symmetric"       A(I,J) = A(J,I),\n' );
  fprintf ( 1, '                      input only lower triangle.\n' );
  fprintf ( 1, '    "skew-symmetric"  A(I,J) = - A(J,I),\n' );
  fprintf ( 1, '                      input only strict lower triangle.\n' );
  fprintf ( 1, '    "Hermitian"       A(I,J) = A*(J,I),\n' );
  fprintf ( 1, '                      input only lower triangle.\n' );

  return
end


classdef Scalar3d
    % SCALAR3D represents a 3D arary of scalar values.  It can represents a single
    % directional component of a 3D vector field (e.g. Ex of E).
	% The fields are always evaluated at the primary grid points (grid kind =
	% GT.prim)

    properties (SetAccess = immutable)
		% numerical properties
        array  % 3D array
        grid3d  % instance of Grid3d
		gt_array  % [x_gridtype y_gridtype z_gridtype]: array of instances of GT
        osc  % instance of Oscillation

		% extra properties
		physQcell  % {PhysQ, int; PhysQ, int; ...}: representation of physical quanity
		unitvalue  % unit value of physical quantity
		name  % name of this quantity: "E_x", "H_y", etc. in LaTeX equation format
	end

    methods
        function this = Scalar3d(array, grid3d, gt_array, osc, physQcell, name)
			chkarg(istypesizeof(grid3d, 'Grid3d'), '"grid3d" should be instance of Grid3d.');
			this.grid3d = grid3d;

			chkarg(istypesizeof(gt_array, 'GT', [1 Axis.count]), ...
				'"gt_array" should be length-%d row vector with GT as elements.', Axis.count);
			this.gt_array = gt_array;

			N = grid3d.N + int(gt_array);
			chkarg(istypesizeof(array, 'complex', N), ...
				'"array" should be %d-by-%d-by-%d array with complex elements.', N(Axis.x), N(Axis.y), N(Axis.z));
			this.array = array;

			chkarg(istypesizeof(osc, 'Oscillation'), '"osc" should be instance of Oscillation.');
			this.osc = osc;

			if nargin < 5  % no physQ
				physQcell = PhysQ.arbitrary;
			end
			chkarg(istypesizeof(physQcell, 'PhysQ') || isphysQcell(physQcell), ...
				'"physQcell" should be instance of PhysQ, or cell array {PhysQ, int; PhysQ, int; ...}.');
			if istypesizeof(physQcell, 'PhysQ')
				physQcell = {physQcell, 1};
			end
			this.physQcell = physQcell;
			this.unitvalue = osc.unit.value(this.physQcell);

			if nargin < 6  % no name
				name = '';
			end
			chkarg(ischar(name), '"name" should be string.');
            this.name = name;
		end

		function l_cell = lall(this)
			l_cell = this.grid3d.lall(Axis.elems + Axis.count*subsindex(this.gt_array));
		end

		function l_cell = lg(this)
			l_cell = this.grid3d.lg(Axis.elems + Axis.count*subsindex(this.gt_array));
		end

		function l_cell = l(this)
			l_cell = this.grid3d.l(Axis.elems + Axis.count*subsindex(this.gt_array));
		end

		function [array, l_cell] = data_expanded(this)
			l_cell = this.lall;
			array = this.array;
		end

		function [array, l_cell] = data_ghost_expanded(this)
			l_cell = this.lg;
			ind = cell(1, Axis.count);

			for w = Axis.elems
				ind{w} = 1:(this.grid3d.N(w)+1);
			end
			array = this.array(ind{:});
		end

		function [array, l_cell] = data_original(this)
			l_cell = this.l;
			ind = cell(1, Axis.count);

			for w = Axis.elems
				if this.gt_array(w) == GT.prim
					ind{w} = 1:this.grid3d.N(w);
				else  % this.gt_array(w) == GT.dual
					ind{w} = 2:(this.grid3d.N(w)+1);
				end
			end
			array = this.array(ind{:});
		end

		function [val, loc] = value(this, x, y, z)
			% Return the values of this Scalar3d along a line.  The line should
			% be along one of the x, y, z directions, so at least two of the
			% given x, y, z should be scalar.
			loc = {x, y, z};
			lavail = this.l;  % available locations
			num_len1 = 0;  % at least two of x, y, z should be scalar
			for w = Axis.elems
				coord_w = loc{w};
				chkarg(isempty(coord_w) || isvector(coord_w) && istypeof(coord_w, 'real'), ...
				'"%s" should be empty or real vector.', char(w));
				if isempty(coord_w)
					coord_w = lavail{w};  % if empty, use all available locations
				else
					chkarg(this.grid3d.comp(w).contains(coord_w), '"%s" should be inside grid.', char(w));
					coord_w = sort(coord_w);
				end
				loc{w} = coord_w;
				if length(coord_w) == 1
					num_len1 = num_len1 + 1;
				end
			end
			chkarg(num_len1 >= 2, 'Points should be along Cartesian direction.');

			lall = this.lall;
			ind = cell(1, Axis.count);

			for w = Axis.elems
				loc_min = loc{w}(1);
				indw_min = ismembc2(loc_min, lall{w});
				if indw_min == 0
					indw_min = find(lall{w} < loc_min, 1, 'last');
				end

				loc_max = loc{w}(end);
				indw_max = ismembc2(loc_max, lall{w});
				if indw_max == 0
					indw_max = find(lall{w} > loc_max, 1, 'first');
				end

				ind{w} = indw_min:indw_max;
			end

			isindlen1 = [length(ind{Axis.x}), length(ind{Axis.y}), length(ind{Axis.z})] == 1;
			if all(isindlen1)
				val = this.array(ind{:});
			else
				for w = Axis.elems
					if isindlen1(w) % ndgrid() needs two data points to interpolate with
						indw = ind{w};
						if indw >= 2
							ind{w} = [indw - 1, indw];
						else
							ind{w} = [indw, indw + 1];
						end
					end
				end

				l = cell(1, Axis.count);
				for w = Axis.elems
					l{w} = lall{w}(ind{w}(1):ind{w}(end));
				end
				[X, Y, Z] = ndgrid(l{:});
				V = this.array(ind{:});
				[Xi, Yi, Zi] = ndgrid(loc{:});
				val = interpn(X, Y, Z, V, Xi, Yi, Zi);
			end
		end
	end
end


function test_example_nirs_fingertapping

% MEM 4gb
% WALLTIME 00:10:00

%
%% Analyzing NIRS data recorded during unilateral finger- and foot-tapping
%
% This example script demonstrates the analysis of data that is shared by Sujin Bak, Jinwoo Park, Jaeyoung Shin, and Jichai Jeong: _[Open Access fNIRS Dataset for Classification of Unilateral Finger- and Foot-Tapping](https://doi.org/10.6084/m9.figshare.9783755.v2)_.
%
% The following links point to the shared data, to the PDF manuscript that explains the shared data, and to a GitHub repository from one of the authors that contains some example analyses on the data.
%
%* <https://doi.org/10.6084/m9.figshare.9783755.v2>
%* <https://doi.org/10.3390/electronics8121486>
%* <https://github.com/JaeyoungShin/fNIRS-dataset>
%
% The data includes the concentration changes of oxygenated/reduced hemoglobin ∆HbO/HbR, trigger information, and fNIRS channel information. The recordings were done for 30 subjects that were performing a finger- and foot-tapping task.
%
% The data were recorded by a three-wavelength continuous-time multi-channel fNIRS system (LIGHTNIRS, Shimadzu, Kyoto, Japan) consisting of eight light sources (Tx) and eight detectors (Rx). Four each of Tx and Rx were placed around C3 on the left hemisphere, and the rest were placed around C4 on the right hemisphere.
%
% The temporal structure of each trial is
%
%* instruction for 2s
%* fixation cross, this indicates that the task has to be executed, for 10s
%* "stop" for 2s
%* fixation cross indicating rest period, for 15-17 seconds
%
% The total duration of a trial, including the instruction, ranges from 2+10+2+15=29 to 2+10+2+17=31 seconds.
%
%% # Building a MATLAB analysis script
%

% for now, getting the data from the below URL and unzipping it, works (JM added 20211201)
t = tempdir;
filenames = unzip('https://figshare.com/ndownloader/files/18069143', t);

% filenames = {
%   'v2/fNIRS 01.mat'
%   'v2/fNIRS 02.mat'
%   'v2/fNIRS 03.mat'
%   'v2/fNIRS 04.mat'
%   'v2/fNIRS 05.mat'
%   'v2/fNIRS 06.mat'
%   'v2/fNIRS 07.mat'
%   'v2/fNIRS 08.mat'
%   'v2/fNIRS 09.mat'
%   'v2/fNIRS 10.mat'
%   'v2/fNIRS 11.mat'
%   'v2/fNIRS 12.mat'
%   'v2/fNIRS 13.mat'
%   'v2/fNIRS 14.mat'
%   'v2/fNIRS 15.mat'
%   'v2/fNIRS 16.mat'
%   'v2/fNIRS 17.mat'
%   'v2/fNIRS 18.mat'
%   'v2/fNIRS 19.mat'
%   'v2/fNIRS 20.mat'
%   'v2/fNIRS 21.mat'
%   'v2/fNIRS 22.mat'
%   'v2/fNIRS 23.mat'
%   'v2/fNIRS 24.mat'
%   'v2/fNIRS 25.mat'
%   'v2/fNIRS 26.mat'
%   'v2/fNIRS 27.mat'
%   'v2/fNIRS 28.mat'
%   'v2/fNIRS 29.mat'
%   'v2/fNIRS 30.mat'
%   };

% this allows switching easily between subjects, and eventually looping over all 30 subjects
filename = filenames{1};

%% # Exploring the MATLAB files that hold the NIRS data
%
% The data is shared by the authors in the form of MATLAB files. Each file contains a bunch of separate variables, you can load it like this to represent it as a structure:
%
nirs = load(filename);

% >> nirs
% nirs =
%   struct with fields:
%      mrk: [1x1 struct]
%      mnt: [1x1 struct]
%      nfo: [1x1 struct]
%      ch1: [30003x1 double]
%      ch2: [30003x1 double]
%      ch3: [30003x1 double]
%      ch4: [30003x1 double]
%      ch5: [30003x1 double]
%      ch6: [30003x1 double]
%      ch7: [30003x1 double]
%      ch8: [30003x1 double]
%      ch9: [30003x1 double]
%     ch10: [30003x1 double]
%     ch11: [30003x1 double]
%     ch12: [30003x1 double]
%     ch13: [30003x1 double]
%     ch14: [30003x1 double]
%     ch15: [30003x1 double]
%     ch16: [30003x1 double]
%     ch17: [30003x1 double]
%     ch18: [30003x1 double]
%     ch19: [30003x1 double]
%     ch20: [30003x1 double]
%     ch21: [30003x1 double]
%     ch22: [30003x1 double]
%     ch23: [30003x1 double]
%     ch24: [30003x1 double]
%     ch25: [30003x1 double]
%     ch26: [30003x1 double]
%     ch27: [30003x1 double]
%     ch28: [30003x1 double]
%     ch29: [30003x1 double]
%     ch30: [30003x1 double]
%     ch31: [30003x1 double]
%     ch32: [30003x1 double]
%     ch33: [30003x1 double]
%     ch34: [30003x1 double]
%     ch35: [30003x1 double]
%     ch36: [30003x1 double]
%     ch37: [30003x1 double]
%     ch38: [30003x1 double]
%     ch39: [30003x1 double]
%     ch40: [30003x1 double]
%      dat: [1x1 struct]

% Contrary to the description in the accompanying publication (see table 1 in the PDF manuscript) and in the GitHub repository, the MATLAB files do _not_ contain the variables |cntHb|, |clab|, etc. However, it is not so hard to make sense of the data: each channel is represented as a vector, there is header information, there is information about the optode montage, and there is information about the events.
%
% >> nirs.nfo
% ans =
%   struct with fields:
%             fs: 13.3333
%           clab: {1x40 cell}
%              T: 30003
%        nEpochs: 1
%         length: 2.2502e+03
%         format: 'DOUBLE'
%     resolution: [1x40 double]
%           file: 'D:\Experimental data\NIRS motor imagery\matdata\fNIRS 01'
%        nEvents: 75
%       nClasses: 3
%      className: {'RIGHT'  'LEFT'  'FOOT'}

%
% >> nirs.mnt
% ans =
%  struct with fields:
%               x: [40x1 double]
%               y: [40x1 double]
%          pos_3d: [3x40 double]
%            clab: {1x40 cell}
%             box: [2x41 double]
%          box_sz: [2x41 double]
%       scale_box: [2x1 double]
%    scale_box_sz: [2x1 double]

%
% >> nirs.mrk
% ans =
%  struct with fields:
%        event: [1x1 struct]
%         time: [1x75 double]
%            y: [3x75 double]
%    className: {'RIGHT'  'LEFT'  'FOOT'}

%% Convering the MATLAB structure to a FieldTrip raw data structure
%
% Since the data is not stored on disk in a [dataformat](/faq/dataformat/) that FieldTrip can directly read, we will circumvent the FieldTrip reading functions as outlined in [this frequenly asked question](/faq/how_can_i_import_my_own_dataformat/#circumvent-the-fieldtrip-reading-functions).
%
% We start with constructing a MATLAB data structure according to **[ft_datatype_raw](https://github.com/fieldtrip/fieldtrip/blob/release/utilities/ft_datatype_raw.m)**, as if it were produced by **[ft_preprocessing](https://github.com/fieldtrip/fieldtrip/blob/release/ft_preprocessing.m)**.
%
data_raw = [];
data_raw.label = nirs.nfo.clab(:);
data_raw.trial{1} = [
  % transpose each channel, we want the data matrix to be Nchans * Nsamples
  nirs.ch1'
  nirs.ch2'
  nirs.ch3'
  nirs.ch4'
  nirs.ch5'
  nirs.ch6'
  nirs.ch7'
  nirs.ch8'
  nirs.ch9'
  nirs.ch10'
  nirs.ch11'
  nirs.ch12'
  nirs.ch13'
  nirs.ch14'
  nirs.ch15'
  nirs.ch16'
  nirs.ch17'
  nirs.ch18'
  nirs.ch19'
  nirs.ch20'
  nirs.ch21'
  nirs.ch22'
  nirs.ch23'
  nirs.ch24'
  nirs.ch25'
  nirs.ch26'
  nirs.ch27'
  nirs.ch28'
  nirs.ch29'
  nirs.ch30'
  nirs.ch31'
  nirs.ch32'
  nirs.ch33'
  nirs.ch34'
  nirs.ch35'
  nirs.ch36'
  nirs.ch37'
  nirs.ch38'
  nirs.ch39'
  nirs.ch40'
  ];
data_raw.fsample = nirs.nfo.fs;
data_raw.time{1} = ((1:nirs.nfo.T)-1)/nirs.nfo.fs;

%% # Convert the optode montage to a layout for plotting
%
% In FieldTrip we can have a [detailled description](/faq/how_are_electrodes_magnetometers_or_gradiometers_described/#the-definition-of-nirs-sensors) of the NIRS optode placement and how optodes are combined to form channels. However, in this dataset this information is not complete and we cannot make a complete |opto| structure.
%
% This is how far we can get
%
opto = [];
opto.label = nirs.mnt.clab(:);
opto.chanpos = nirs.mnt.pos_3d'; % these are all nan

% But this is not a complete description of the channel and sensor information according to **[ft_dataype_sens](https://github.com/fieldtrip/fieldtrip/blob/release/utilities/ft_datatype_sens.m)**. However, a full sensor definitioon is also not required: the opical densities have already been converted in HbO and HbR prior to sharing, so we only care about channel positions for plotting.
%
% For the plotting of channel level data (see also [this tutorial](/tutorial/plotting/)) we need a 2D layout. That is explained in detail in [this tutorial](/tutorial/layout/). If the |opto| definition had included 2D or 3D channel positions, then we could have used **[ft_prepare_layout](https://github.com/fieldtrip/fieldtrip/blob/release/ft_prepare_layout.m)** but now we will manually construct the layout structure.
%
layout = [];
layout.label = nirs.mnt.clab(:);
layout.pos = [nirs.mnt.x(:) nirs.mnt.y(:)]; % these are all nan
layout.pos = nirs.mnt.box';
layout.width = nirs.mnt.box_sz(1,:)';
layout.height = nirs.mnt.box_sz(2,:)';
layout.outline = {};
layout.mask = {};

% I don't think that the 41st position refers to an actual channel, so let's remove that.
%
layout.pos(41, :) = [];
layout.width(41)  = [];
layout.height(41) = [];

% We can plot the layout using **[ft_plot_layout](https://github.com/fieldtrip/fieldtrip/blob/release/plotting/ft_plot_layout.m)** and inspect it in detail. That is something I have done multiple times, going back and forth in the script to ensure that my interpretation of the |mnt| field matches the data.
%
figure
ft_plot_layout(layout, 'label', true)

%
% This shows four groups, with two groups of oxy channels on the left of the figure (for the left and right hemisphere), and the corresponding deoxy channels on the right of the figure. This matches with the schepatic display in figure 1 of the PDF manuscript, and with the displayed data in figure 3 of the PDF manuscript.
%
% To improve the plotting of topographies, we can also make an outline and mask. The outline comproses extra lines that are added to the figure, often we include a circle (representing the head) with a triangle at the top (representing the nose). The mask comprises a series of polygons that are used in topographic interpolation and masking the interpolated data that is outside the head, but also by masking the interpolated data that is in between the grid on the left and right hemisphere. This will become more clear at the end of this example script.
%
pad = 0.2;
section = {1:10 11:20 21:30 31:40};
for i=1:4
  sel = section{i};
  % upper left
  corner1x = min(layout.pos(sel,1)-layout.width (sel)/2) - pad;
  corner1y = max(layout.pos(sel,2)+layout.height(sel)/2) + pad;

  % upper right
  corner2x = max(layout.pos(sel,1)+layout.width (sel)/2) + pad;
  corner2y = max(layout.pos(sel,2)+layout.height(sel)/2) + pad;

  % lower right
  corner3x = max(layout.pos(sel,1)+layout.width (sel)/2) + pad;
  corner3y = min(layout.pos(sel,2)-layout.height(sel)/2) - pad;

  % lower left
  corner4x = min(layout.pos(sel,1)-layout.width (sel)/2) - pad;
  corner4y = min(layout.pos(sel,2)-layout.height(sel)/2) - pad;

  layout.outline{i} = [
    corner1x corner1y
    corner2x corner2y
    corner3x corner3y
    corner4x corner4y
    corner1x corner1y % this closes the outline
    ];

  layout.mask{i} = [
    corner1x corner1y
    corner2x corner2y
    corner3x corner3y
    corner4x corner4y
    corner1x corner1y % this closes the outline
    ];
end

figure
ft_plot_layout(layout, 'label', true, 'outline', true, 'mask', true)

%
% The layout now also shows the outline, as thick black lines. If you disable the outline, you can also see the mask as dotted lines.
%
%% # Parsing the events and segmenting the data in trials
%
% In general in FieldTrip events have to be aligned with samples. The first sample of a recording on disk is referred to as sample 1.
%
% The |mrk| field contains times
%
% >> nirs.mrk.time
% ans =
%   Columns 1 through 9
%         1950    30975    60975    91950    ...

% These numbers are larger than the total number of samples, so they do not directly map onto samples. I started off assuming that the marker time is expressed in milliseconds, but that turned out not to be correct. Therefore I experimented with the following piece of code until I had something that made sense.
%
% This timestamp to sample ratio results in the events being nicely spaced over the whole experiment
%
ratio = 75;

% Note that `1/data.fsample = 0.075`, i.e., with the 13.333 Hz sampling rate each sample happens to be 75 ms. When working with unknown data it is not uncommon to have to deal with a puzzle like this, it reminds me of [Kabbalah](https://en.wikipedia.org/wiki/Kabbalah).
%
event = [];
for i=1:nirs.nfo.nEvents
  event(i).type = 'marker';
  event(i).value = nirs.mrk.className{nirs.mrk.event.desc(i)};
  event(i).sample = round(nirs.mrk.time(i)/ratio) + 1;
  event(i).duration = nan;
  event(i).offset = nan;
end

cfg = [];
cfg.event = event; % store the events in the configuration to have them displayed
cfg.viewmode = 'vertical';
cfg.preproc.demean = 'yes';
cfg.blocksize = nirs.nfo.length; % show the data over the whole length
ft_databrowser(cfg, data_raw);

%
%% # Apply a band-pass filter on the continuous data
%
cfg = [];
cfg.bpfilter = 'yes';
cfg.bpfreq = [0.02 0.8];
cfg.bpfilford = 2;
data_filt = ft_preprocessing(cfg, data_raw);

%% # Segment the continuous data into trials
%
% To segment the data we have to create a so-called "trial definition". This would normally be done using **[ft_definetrial](https://github.com/fieldtrip/fieldtrip/blob/release/ft_definetrials.m)** and using the events that were read from the data on disk (had it been a supported file format). The trial definition specifies the begin and end sample of each trial, and the offset, i.e., how many samples the time defined as t=0 is shifted relative to the data segment. Furthermore, it can contain additional columns with trial specific information, such as condition codes.
%
% The temporal structure of each trial is
%
%* instruction for 2s
%* fixation cross, this indicates that the task has to be executed, for 10s
%* "stop" for 2s
%* fixation cross indicating rest period, for 15-17 seconds
%
% The total duration of a trial, including the instruction, ranges from 2+10+2+15=29 to 2+10+2+17=31 seconds.
%
% We will use the standard FieldTrip |event| structure to make the trials, this is also what **[ft_read_event](https://github.com/fieldtrip/fieldtrip/blob/release/fileio/ft_read_event.m)** returns for supported data formats.
%
taskonset = [event.sample];  % let's assume that the marker is at task onset
begsample = taskonset - 2*data_raw.fsample;
endsample = taskonset + (10+2+15)*data_raw.fsample;
offset = - 2*data_raw.fsample * ones(size(begsample)); % each trial has the same 2 seconds offset

% The begin sample, end sample and offset should be integers:
%
begsample = round(begsample);
endsample = round(endsample);
offset = round(offset);

% and we map the right, left, and foot condition onto condition codes 1, 2, and 3.
%
[dummy, condition] = ismember({event.value}, {'RIGHT'  'LEFT'  'FOOT'});

trl = [begsample(:) endsample(:) offset(:) condition(:)];

cfg = [];
cfg.trl = trl;
data_segmented = ft_redefinetrial(cfg, data_filt);

%% # Visualize the segmented data
%
cfg = [];
cfg.event = event;
cfg.viewmode = 'vertical';
cfg.preproc.demean = 'no'; % it is filtered by now
ft_databrowser(cfg, data_segmented);

%
%% # Average the segmented data, conditional on each condition
%
cfg = [];
cfg.trials = data_segmented.trialinfo==1;
timelockRight = ft_timelockanalysis(cfg, data_segmented);
cfg.trials = data_segmented.trialinfo==2;
timelockLeft = ft_timelockanalysis(cfg, data_segmented);
cfg.trials = data_segmented.trialinfo==3;
timelockFoot = ft_timelockanalysis(cfg, data_segmented);

%% # Plot the averaged responses
%
%
cfg = [];
cfg.layout = layout;
cfg.showoutline = 'yes';
cfg.baseline = [-2 0];
cfg.showlabels = 'yes';
cfg.interactive = 'no'; % use MATLAB for zooming in
ft_multiplotER(cfg, timelockRight, timelockLeft, timelockFoot);

%
% Although some of the more noisy channels dominate the figure due to the automatic vertical scaling, if you zoom in and pay attention to |Ch4oxy|, |Ch5oxy|, and |Ch6oxy| over the left hemisphere, and |Ch14oxy| and |Ch16oxy| over the right hemisphere, you can see that the left hemisphere shows more activity during the right fingertapping task (blue) and the right hemisphere shows more activity following the left fingertapping task (red).
%
% This [example script](/example/nirs_layout/) explains in more detail how for a NIRS dataset the oxy and deoxy channels can either be plotted on top of each other (which is spatially consistent with how the data is recorded) or side-by-side. Plotting them side-by-side makes the results easier to interpret, especially if you have multiple responses (like we have here for left, right and foot conditions). For plotting topographic distributions you also have to ensure that your 2D channel layout does not have overlapping channels that have bvery different numbers.
%
%% # Plot the topography of the averaged responses
%
% This step benefits from the layout having an outline and especially a mask, otherwise the interpolation will cause the whole image to be color coded, including the pieces in between the hemispheres and in between the oxy (on the left) and deoxy channels (on the right).
%
cfg = [];
cfg.layout = layout;
cfg.interactive = 'yes';
cfg.xlim = [7 15];
cfg.ylim = [-0.004 0.004];
figure; ft_topoplotER(cfg, timelockRight);
figure; ft_topoplotER(cfg, timelockLeft);
figure; ft_topoplotER(cfg, timelockFoot);

%
%
% From the topographic arrangements it is clear that the left hemisphere responds with an increase in HbO to the right fingertapping task and the right hemisphere to the left fingertapping task; the HbR (on the right side of the figure) shows the opposite pattern. Also interesting is that both left and right hemisphere show a decrease in HbO and an increase in HbR during the tapping of the foot.