function test_snirf

% WALLTIME 00:10:00
% MEM 2GB
% DEPENDENCY homer2fieldtrip fieldtrip2homer ft_write_data

p = tempdir;
f1 = fullfile(p, 'data1.snirf');
f2 = fullfile(p, 'data2.snirf');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% test the conversion of a Homer file to snirf

% the Homer nirs file has the 's' channel, which is not present in the snirf representation

% read the original data
cfg = [];
cfg.dataset = dccnpath('/home/common/matlab/fieldtrip/data/test/original/nirs/homer/S1001_run01.nirs');
cfg.channel = {'all', '-s*'};
data1 = ft_preprocessing(cfg);
event1 = ft_read_event(cfg.dataset);

% write the data to disk and convert to snirf
chanindx = find(ismember(data1.hdr.label, data1.label));
ft_write_data(f1, data1.trial{1}, 'header', data1.hdr, 'chanindx', chanindx, 'event', event1)

% read the converted data
cfg = [];
cfg.dataset = f1;
data1a = ft_preprocessing(cfg);
event1a = ft_read_event(cfg.dataset);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% test the conversion of an Artinis file to snirf

% read the original data
cfg = [];
cfg.dataset = dccnpath('/home/common/matlab/fieldtrip/data/test/original/nirs/artinis/Helena/190528_fingertap_L.oxy3');
cd(fileparts(cfg.dataset)); % we should be located in the directory with the optodetemplates.xml
data2 = ft_preprocessing(cfg);
event2 = ft_read_event(cfg.dataset, 'chanindx', -1); % do not parse the ADC channels

% replace the original data of the first sample by channel numbers. This helps to assert
% whether data2a is still the same as the original data (data2), even if the
% channel order is switched.
data2.trial{1}(:,1) = [1:length(data2.label)]';

% write the data to disk and convert to snirf
ft_write_data(f2, data2.trial{1}, 'header', data2.hdr, 'event', event2)

% read the converted data
cfg = [];
cfg.dataset = f2;
data2a = ft_preprocessing(cfg);
event2a = ft_read_event(cfg.dataset);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% clean up

delete(f1)
delete(f2)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% compare all fields

assert(isalmostequal(data1.time,        data1a.time, 'abstol', 1e-8));
assert(isalmostequal(data1.fsample,     data1a.fsample, 'abstol', 1e-8));
assert(isalmostequal(data1.sampleinfo,  data1a.sampleinfo, 'abstol', 1e-8));
assert(isequal(data1.label, data1a.label));
assert(isalmostequal(data1.trial,       data1a.trial, 'abstol', 1e-8));

% the duration of the Homer and snirf events is not the same
assert(isequal({event1.type},   {event1a.type}));
assert(isequal([event1.sample], [event1a.sample]));
assert(isequal([event1.value],  [event1a.value]));

%%

% do not compare the label field for Artinis, it differs in Tx/Rx versus S/D
assert(isalmostequal(data2.time,        data2a.time, 'abstol', 1e-8));
assert(isalmostequal(data2.fsample,     data2a.fsample, 'abstol', 1e-8));
assert(isalmostequal(data2.sampleinfo,  data2a.sampleinfo, 'abstol', 1e-8));
assert(length(intersect(data2.trial{1}(:,1), data2a.trial{1}(:,1)))==length(data2.trial{1}(:,1))); % the first sample should contain 1:nchan (see here above)

% the channel names are not the same, neither for the nirs channels, not for the ADC/AUX channels
% assert(isequal(data2.label, data2a.label));

% the type and value of the Artinis and snirf events are not the same
assert(isequal([event2.sample], [event2a.sample]));


function r = ge(p,q)
% this function compares the greater than or equality condition of x, y, and z values of the structures that I use in
% the FVtool.
%
% SYNOPSIS:
%
%
% PARAMETERS:
%
%
% RETURNS:
%
%
% EXAMPLE:
%
% SEE ALSO:
%

% Copyright (c) 2012-2016 Ali Akbar Eftekhari
% See the license file

if (isa(p, 'CellVariable')&&isa(q, 'CellVariable'))
    r=p;
    r.value = p.value>=q.value;
elseif isa(p, 'CellVariable')
    r=p;
    r.value = p.value>=q;
else
    r=q;
    r.value = p>=q.value;
end


function SaveNet(itr, useHourCost, sigGraph, learner, LOG, para)

if isfield(para, 'saveModelEveryXIter') && mod(itr,para.saveModelEveryXIter)~=0
    return;
end

modelfile = [para.output sprintf('.itr%d.hour%d.LR%s', itr, round(LOG.nHourSeen), FormatFloat4Name(learner.learningRate))];
if useHourCost
    cost_cv = LOG.cost_cv_hour.taskCost(end);
else
    cost_cv = LOG.cost_cv.taskCost(end);
end
if cost_cv>0.1
    modelfile = sprintf('%s.CV%2.3f.mat', modelfile, cost_cv);
else
    modelfile = sprintf('%s.CV%s.mat', modelfile, FormatFloat4Name(cost_cv));
end

sigGraph.useGPU = 0;
sigGraph = sigGraph.setPrecision();
sigGraph = sigGraph.cleanUp();
save(modelfile, 'sigGraph', 'para', 'learner', 'LOG');

end


%% Real Time Data Stream Plotting Example
function real_time_data_stream_plotting
%%
% This example demonstrates how to automatically read a set number of data bytes as and
% when they are available. This MATLAB(R) script also generates a real time plot of streaming
% data collected from the TCPIP server.
%
% The script may be updated to use any instrument/device/TCPIP server
% to collect real time data. You may need to update the IP address and
% port.
%
% To generate a report of this entire script, you may use the PUBLISH
% command at the MATLAB(R) command line as follows:
%
% publish(real_time_data_plot);

% Author: Ankit Desai
% Copyright 2010 - The MathWorks, Inc.

%% Create the interface object
% Create a TCPIP object listening to port 19 (Character Generator).
%
% *Note* : To enable character generator service at port 19 on a Windows platform, enable:
%
%  Control Panel > Add Remove Programs > Add/Remove Windows Component > Networking Services
%
interfaceObject = tcpip('localhost',19);

%%
% Setup a figure window and define a callback function for close operation
figureHandle = figure('NumberTitle','off',...
    'Name','Live Data Stream Plot',...
    'Color',[0 0 0],...
    'CloseRequestFcn',{@localCloseFigure,interfaceObject});

%%
% Setup the axes
axesHandle = axes('Parent',figureHandle,...
    'YGrid','on',...
    'YColor',[0.9725 0.9725 0.9725],...
    'XGrid','on',...
    'XColor',[0.9725 0.9725 0.9725],...
    'Color',[0 0 0]);

xlabel(axesHandle,'Number of Samples');
ylabel(axesHandle,'Value');

%%
% Initialize the plot and hold the settings on
hold on;
plotHandle = plot(axesHandle,0,'-y','LineWidth',1);

%% Setup interface object to read chunks of data
% Set the number of bytes to read at a time
bytesToRead = 500;

%%
% Define a callback function to be executed when desired number of bytes
% are available in the input buffer
interfaceObject.BytesAvailableFcn = {@localReadAndPlot,plotHandle,bytesToRead};
interfaceObject.BytesAvailableFcnMode = 'byte';
interfaceObject.BytesAvailableFcnCount = bytesToRead;

%%
% Open the interface object
fopen(interfaceObject);
pause(3);
snapnow;
%% Implement the bytes available callback
function localReadAndPlot(interfaceObject,~,figureHandle,bytesToRead)

%%
% Read the desired number of data bytes
data = fread(interfaceObject,bytesToRead);

%%
% Update the plot
set(figureHandle,'Ydata',data);

%% Implement the close figure callback
function localCloseFigure(figureHandle,~,interfaceObject)

%%
% Clean up the interface object
fclose(interfaceObject);
delete(interfaceObject);
clear interfaceObject;

%%
% Close the figure window
delete(figureHandle);


%testJacoComm - Example script that shows how to use the JACO2
% USB communication class for MATLAB and Simulink
% BEFORE USING THIS CLASS, CHECK README.txt
% Copyright 2017 The MathWorks, Inc.

%% Be sure that you are on the directory where the SDK files are. That is
%  <YOUR_JACOCOM_FILES_PATH>/JACO2Communicator/JACO2SDK
%  If you want to run the class from other locations,
%  you will need to add this directory to your Windows path variable.
% addpath(genpath('C:\Users\shovington\Documents\GitHub\matlab_Kinovaapi_wrapper'))
% cd 'C:\Users\shovington\Documents\GitHub\matlab_Kinovaapi_wrapper\JACO2Communicator\JACO2SDK'

%% Create an instance of the class (you can only create ONE instance)
jc = JacoComm

%% Connect to the robot
connect(jc);
% Verify the object has all the sensor data
jc

%% When you are done using the class, close the library by executing
% >> disconnect(jc)

%% Calibrate fingers
calibrateFingers(jc);

%% Query individual object properties
jc.JointPos
%%
jc.JointVel
%%
jc.JointTorque
%%
jc.JointTemp
%%
jc.FingerPos
%%
jc.FingerVel
%%
jc.FingerTorque
%%
jc.FingerTemp
%%
jc.EndEffectorPose
%%
jc.EndEffectorWrench
%%
jc.ProtectionZone
%%
jc.EndEffectorOffset
%%
jc.DOF
%%
jc.TrajectoryInfo

%% Methods to query joint and finger values all at once
pos = getJointAndFingerPos(jc)
%%
vel = getJointAndFingerVel(jc)
%%
torque = getJointAndFingerTorque(jc)

%%
temp = getJointAndFingerTemp(jc)

%% Set position control mode (default mode after powering on the robot)
setPositionControlMode(jc);

%% Go to home position (function returns when it reaches the home position);
goToHomePosition(jc);

%% Set torque control mode
% You can move the robot and test the gravity compensation. When you are
% finished, execute again setPositionControlMode(jc);
setTorqueControlMode(jc);

%% Start admittance mode
% You can move the robot in admitance mode. When you are
% finished, execute jc.StopForceControl;
jc.StartForceControl;

%% Send joint positions
%q_home = [4.806;2.924;1.004;4.204;1.445;1.323]; %6DOF
q_home = [4.9409;2.8396;0.0016;0.7581;4.6342;4.4963;5.0252]; %7DOF
jntCmd = q_home;
sendJointPositionCommand(jc,jntCmd);

%% Send finger positions
fCmd = 6000*ones(3,1);
sendFingerPositionCommand(jc,fCmd);
%%
fCmd = 0*ones(3,1);
sendFingerPositionCommand(jc,fCmd);

%% Send joint and finger position
%q_home = [4.806;2.924;1.004;4.204;1.445;1.323]; %6DOF
q_home = [4.9409;2.8396;0.0016;0.7581;4.6342;4.4963;5.0252]; %7DOF
jntCmd = q_home;
fCmd = 0*ones(3,1);
sendJointAndFingerPositionCommand(jc,jntCmd,fCmd);

%% Send joint velocities
%jntVelCmd = [0;0;0;0;0;0.8]; %6DOF
jntVelCmd = [0;0;0;0;0;0;0.8]; %7DOF
for i=1:200
    sendJointVelocityCommand(jc,jntVelCmd);
end

%% send joint torques
setTorqueControlMode(jc);
%jntTorqueCmd = [1.5;0.0;0;0;0;0.8]; %6DOF
jntTorqueCmd = [0;0;0;0;0;0;-0.8]; %7DOF
for i=1:100
   sendJointTorqueCommand(jc,jntTorqueCmd);
   pause(0.01)
end

%% Send cartesian positions
setPositionControlMode(jc);
CartPosCmd = [0.05;0;0;0;0;0];
sendCartesianPositionCommand(jc,CartPosCmd)

%% Send cartesian velocity
for i=1:100
CartVelCmd = [0;0;-0.1;0;0;0];
sendCartesianVelocityCommand(jc,CartVelCmd);
end

%% Set End Effector Offset
offsetCmd = [0;0;0;0];
jc.setEndEffectorOffset(offsetCmd);

%% Run gravity calibration (will block until it finishes, not interruptible!)
% runGravityCalibration(jc);

%% Set a protection zone with a speed limit
% See API documentation to know what to send in Zone Cmd
% Search for setProtectionZone
ZoneCmd = [0;-0.52;0;0;0;0;...
           0;0;0;...
           0.5;0;0;...
           0.5;-0.52;0;...
           0.63;...
           0.01;0.01];
jc.setProtectionZone(ZoneCmd)

%% Delete all protection zones
jc.EraseAllProtectionZones

%% Disconnect from robot and unload library
setPositionControlMode(jc);
disconnect(jc);


function cellInfo = GCS_Cmd_Bus(varargin)
% GCS_CMD_BUS returns a cell array containing bus object information
%
% Optional Input: 'false' will suppress a call to Simulink.Bus.cellToObject
%                 when the MATLAB file is executed.
% The order of bus element attributes is as follows:
%   ElementName, Dimensions, DataType, SampleTime, Complexity, SamplingMode, DimensionsMode, Min, Max, DocUnits, Description

suppressObject = false;
if nargin == 1 && islogical(varargin{1}) && varargin{1} == false
    suppressObject = true;
elseif nargin > 1
    error('Invalid input argument(s) encountered');
end

cellInfo = { ...
  { ...
    'GCS_Cmd_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'mode', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'cmd_1', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Operation channel 1')}; ...
{'cmd_2', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Operation channel 2')}; ...
    } ...
  } ...
}';

if ~suppressObject
    % Create bus objects in the MATLAB base workspace
    Simulink.Bus.cellToObject(cellInfo)
end


function boolout = iscphd( filename )
%ISCPHD Checks to see whether input file is in the CPHD format
%
% Written by: Wade Schwartzkopf, NGA/Research
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

fid = fopen(filename,'r','b','UTF-8');
[cphd_str, version_str] = strtok(fgets( fid, 20 ),'/'); % First line is just CPHD/version
vers_parts = str2double(regexp(version_str(2:end),'\.','split'));
boolout = strcmp(cphd_str,'CPHD') && ...  % CPHD"X"
    ((vers_parts(1)==0&&vers_parts(2)>=3)||vers_parts(1)>0);  % 0.3 and above
fclose(fid);

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function [dat] = read_edf(filename, hdr, begsample, endsample, chanindx)

% READ_EDF reads specified samples from an EDF continous datafile
% It neglects all trial boundaries as if the data was acquired in
% non-continous mode.
%
% Use as
%   [hdr] = read_edf(filename);
% where
%    filename        name of the datafile, including the .bdf extension
% This returns a header structure with the following elements
%   hdr.Fs           sampling frequency
%   hdr.nChans       number of channels
%   hdr.nSamples     number of samples per trial
%   hdr.nSamplesPre  number of pre-trigger samples in each trial
%   hdr.nTrials      number of trials
%   hdr.label        cell-array with labels of each channel
%   hdr.orig         detailled EDF header information
%
% Or use as
%   [dat] = read_edf(filename, hdr, begsample, endsample, chanindx);
% where
%    filename        name of the datafile, including the .bdf extension
%    hdr             header structure, see above
%    begsample       index of the first sample to read
%    endsample       index of the last sample to read
%    chanindx        index of channels to read (optional, default is all)
% This returns a Nchans X Nsamples data matrix

% Copyright (C) 2006, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id: read_edf.m 2076 2010-11-05 12:05:19Z roboos $

needhdr = (nargin==1);
needevt = (nargin==2);
needdat = (nargin>3);

if needhdr
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % read the header, this code is from EEGLAB's openbdf
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  FILENAME = filename;

  % defines Seperator for Subdirectories
  SLASH='/';
  BSLASH=char(92);

  cname=computer;
  if cname(1:2)=='PC' SLASH=BSLASH; end;

  fid=fopen(FILENAME,'r','ieee-le');
  if fid<0
    fprintf(2,['Error LOADEDF: File ' FILENAME ' not found\n']);
    return;
  end;

  EDF.FILE.FID=fid;
  EDF.FILE.OPEN = 1;
  EDF.FileName = FILENAME;

  PPos=min([max(find(FILENAME=='.')) length(FILENAME)+1]);
  SPos=max([0 find((FILENAME=='/') | (FILENAME==BSLASH))]);
  EDF.FILE.Ext = FILENAME(PPos+1:length(FILENAME));
  EDF.FILE.Name = FILENAME(SPos+1:PPos-1);
  if SPos==0
    EDF.FILE.Path = pwd;
  else
    EDF.FILE.Path = FILENAME(1:SPos-1);
  end;
  EDF.FileName = [EDF.FILE.Path SLASH EDF.FILE.Name '.' EDF.FILE.Ext];

  H1=char(fread(EDF.FILE.FID,256,'char')');     %
  EDF.VERSION=H1(1:8);                          % 8 Byte  Versionsnummer
  %if 0 fprintf(2,'LOADEDF: WARNING  Version EDF Format %i',ver); end;
  EDF.PID = deblank(H1(9:88));                  % 80 Byte local patient identification
  EDF.RID = deblank(H1(89:168));                % 80 Byte local recording identification
  %EDF.H.StartDate = H1(169:176);               % 8 Byte
  %EDF.H.StartTime = H1(177:184);               % 8 Byte
  EDF.T0=[str2num(H1(168+[7 8])) str2num(H1(168+[4 5])) str2num(H1(168+[1 2])) str2num(H1(168+[9 10])) str2num(H1(168+[12 13])) str2num(H1(168+[15 16])) ];

  % Y2K compatibility until year 2090
  if EDF.VERSION(1)=='0'
    if EDF.T0(1) < 91
      EDF.T0(1)=2000+EDF.T0(1);
    else
      EDF.T0(1)=1900+EDF.T0(1);
    end;
  else ;
    % in a future version, this is hopefully not needed
  end;

  EDF.HeadLen = str2num(H1(185:192));  % 8 Byte  Length of Header
  % reserved = H1(193:236);            % 44 Byte
  EDF.NRec = str2num(H1(237:244));     % 8 Byte  # of data records
  EDF.Dur = str2num(H1(245:252));      % 8 Byte  # duration of data record in sec
  EDF.NS = str2num(H1(253:256));       % 8 Byte  # of signals

  EDF.Label = char(fread(EDF.FILE.FID,[16,EDF.NS],'char')');
  EDF.Transducer = char(fread(EDF.FILE.FID,[80,EDF.NS],'char')');
  EDF.PhysDim = char(fread(EDF.FILE.FID,[8,EDF.NS],'char')');

  EDF.PhysMin= str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'char')'));
  EDF.PhysMax= str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'char')'));
  EDF.DigMin = str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'char')'));
  EDF.DigMax = str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'char')'));

  % check validity of DigMin and DigMax
  if (length(EDF.DigMin) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Digital Minimum\n');
    EDF.DigMin = -(2^15)*ones(EDF.NS,1);
  end
  if (length(EDF.DigMax) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Digital Maximum\n');
    EDF.DigMax = (2^15-1)*ones(EDF.NS,1);
  end
  if (any(EDF.DigMin >= EDF.DigMax))
    fprintf(2,'Warning OPENEDF: Digital Minimum larger than Maximum\n');
  end
  % check validity of PhysMin and PhysMax
  if (length(EDF.PhysMin) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Physical Minimum\n');
    EDF.PhysMin = EDF.DigMin;
  end
  if (length(EDF.PhysMax) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Physical Maximum\n');
    EDF.PhysMax = EDF.DigMax;
  end
  if (any(EDF.PhysMin >= EDF.PhysMax))
    fprintf(2,'Warning OPENEDF: Physical Minimum larger than Maximum\n');
    EDF.PhysMin = EDF.DigMin;
    EDF.PhysMax = EDF.DigMax;
  end
  EDF.PreFilt= char(fread(EDF.FILE.FID,[80,EDF.NS],'char')');   %
  tmp = fread(EDF.FILE.FID,[8,EDF.NS],'char')'; %   samples per data record
  EDF.SPR = str2num(char(tmp));               % samples per data record

  fseek(EDF.FILE.FID,32*EDF.NS,0);

  EDF.Cal = (EDF.PhysMax-EDF.PhysMin)./(EDF.DigMax-EDF.DigMin);
  EDF.Off = EDF.PhysMin - EDF.Cal .* EDF.DigMin;
  tmp = find(EDF.Cal < 0);
  EDF.Cal(tmp) = ones(size(tmp));
  EDF.Off(tmp) = zeros(size(tmp));

  EDF.Calib=[EDF.Off';(diag(EDF.Cal))];
  %EDF.Calib=sparse(diag([1; EDF.Cal]));
  %EDF.Calib(1,2:EDF.NS+1)=EDF.Off';

  EDF.SampleRate = EDF.SPR / EDF.Dur;

  EDF.FILE.POS = ftell(EDF.FILE.FID);
  if EDF.NRec == -1                            % unknown record size, determine correct NRec
    fseek(EDF.FILE.FID, 0, 'eof');
    endpos = ftell(EDF.FILE.FID);
    EDF.NRec = floor((endpos - EDF.FILE.POS) / (sum(EDF.SPR) * 2));
    fseek(EDF.FILE.FID, EDF.FILE.POS, 'bof');
    H1(237:244)=sprintf('%-8i',EDF.NRec);      % write number of records
  end;

  EDF.Chan_Select=(EDF.SPR==max(EDF.SPR));
  for k=1:EDF.NS
    if EDF.Chan_Select(k)
      EDF.ChanTyp(k)='N';
    else
      EDF.ChanTyp(k)=' ';
    end;
    if findstr(upper(EDF.Label(k,:)),'ECG')
      EDF.ChanTyp(k)='C';
    elseif findstr(upper(EDF.Label(k,:)),'EKG')
      EDF.ChanTyp(k)='C';
    elseif findstr(upper(EDF.Label(k,:)),'EEG')
      EDF.ChanTyp(k)='E';
    elseif findstr(upper(EDF.Label(k,:)),'EOG')
      EDF.ChanTyp(k)='O';
    elseif findstr(upper(EDF.Label(k,:)),'EMG')
      EDF.ChanTyp(k)='M';
    end;
  end;

  EDF.AS.spb = sum(EDF.SPR);    % Samples per Block
  bi=[0;cumsum(EDF.SPR)];

  idx=[];idx2=[];
  for k=1:EDF.NS,
    idx2=[idx2, (k-1)*max(EDF.SPR)+(1:EDF.SPR(k))];
  end;
  maxspr=max(EDF.SPR);
  idx3=zeros(EDF.NS*maxspr,1);
  for k=1:EDF.NS, idx3(maxspr*(k-1)+(1:maxspr))=bi(k)+ceil((1:maxspr)'/maxspr*EDF.SPR(k));end;

  %EDF.AS.bi=bi;
  EDF.AS.IDX2=idx2;
  %EDF.AS.IDX3=idx3;

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % convert the header to Fieldtrip-style
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  if all(EDF.SampleRate==EDF.SampleRate(1))
    hdr.Fs           = EDF.SampleRate(1);
    hdr.nChans       = EDF.NS;
    hdr.label        = cellstr(EDF.Label);
    % it is continuous data, therefore append all records in one trial
    hdr.nSamples     = EDF.Dur * EDF.SampleRate(1);
    hdr.nSamplesPre  = 0;
    hdr.nTrials      = EDF.NRec;
    hdr.orig         = EDF;

  elseif all(EDF.SampleRate(1:end-1)==EDF.SampleRate(1))
    % only the last channel has a deviant sampling frequency
    % this is the case for EGI recorded datasets that have been converted
    % to EDF+, in which case the annotation channel is the last
    chansel = find(EDF.SampleRate==EDF.SampleRate(1));
    % continue with the subset of channels that has a consistent sampling frequency
    hdr.Fs           = EDF.SampleRate(chansel(1));
    hdr.nChans       = length(chansel);
    warning('Skipping "%s" as continuous data channel because of inconsistent sampling frequency (%g Hz)', deblank(EDF.Label(end,:)), EDF.SampleRate(end));
    hdr.label        = cellstr(EDF.Label);
    hdr.label        = hdr.label(chansel);
    % it is continuous data, therefore append all records in one trial
    hdr.nSamples     = EDF.Dur * EDF.SampleRate(chansel(1));
    hdr.nSamplesPre  = 0;
    hdr.nTrials      = EDF.NRec;
    hdr.orig         = EDF;
    % this will be used on subsequent reading of data
    hdr.orig.chansel    = chansel;
    hdr.orig.annotation = find(strcmp(cellstr(hdr.orig.Label), 'EDF Annotations'));

  else
    % select the sampling rate that results in the most channels
    [a, b, c] = unique(EDF.SampleRate);
    for i=1:length(a)
      chancount(i) = sum(c==i);
    end
    [dum, indx] = max(chancount);
    chansel = find(EDF.SampleRate == a(indx));

    % continue with the subset of channels that has a consistent sampling frequency
    hdr.Fs           = EDF.SampleRate(chansel(1));
    hdr.nChans       = length(chansel);
    hdr.label        = cellstr(EDF.Label);
    hdr.label        = hdr.label(chansel);
    % it is continuous data, therefore append all records in one trial
    hdr.nSamples     = EDF.Dur * EDF.SampleRate(chansel(1));
    hdr.nSamplesPre  = 0;
    hdr.nTrials      = EDF.NRec;
    hdr.orig         = EDF;
    % this will be used on subsequent reading of data
    hdr.orig.chansel    = chansel;
    hdr.orig.annotation = find(strcmp(cellstr(hdr.orig.Label), 'EDF Annotations'));

    warning('channels with different sampling rate not supported, using a subselection of %d channels at %f Hz', length(hdr.label), hdr.Fs);
  end

  % return the header
  dat = hdr;

elseif needdat || needevt
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % read the data
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % retrieve the original header
  EDF = hdr.orig;

  % determine whether a subset of channels should be used
  % which is the case if channels have a variable sampling frequency
  variableFs = isfield(EDF, 'chansel');

  if variableFs && needevt
    % read the annotation channel, not the data channels
    EDF.chansel = EDF.annotation;
    begsample = 1;
    endsample = EDF.SampleRate(end)*EDF.NRec*EDF.Dur;
  end

  if variableFs
    epochlength = EDF.Dur * EDF.SampleRate(EDF.chansel(1));   % in samples for the selected channel
    blocksize   = sum(EDF.Dur * EDF.SampleRate);              % in samples for all channels
    chanoffset  = EDF.Dur * EDF.SampleRate;
    chanoffset  = cumsum([0; chanoffset(1:end-1)]);
    % use a subset of channels
    nchans = length(EDF.chansel);
  else
    epochlength = EDF.Dur * EDF.SampleRate(1);                % in samples for a single channel
    blocksize   = sum(EDF.Dur * EDF.SampleRate);              % in samples for all channels
    % use all channels
    nchans = EDF.NS;
  end

  % determine the trial containing the begin and end sample
  begepoch    = floor((begsample-1)/epochlength) + 1;
  endepoch    = floor((endsample-1)/epochlength) + 1;
  nepochs     = endepoch - begepoch + 1;

  if nargin<5
    chanindx = 1:nchans;
  end

  % allocate memory to hold the data
  dat = zeros(length(chanindx),nepochs*epochlength);

  % read and concatenate all required data epochs
  for i=begepoch:endepoch
    if variableFs
      % only a subset of channels with consistent sampling frequency is read
      offset = EDF.HeadLen + (i-1)*blocksize*2; % in bytes
      % read the complete data block
      buf = readLowLevel(filename, offset, blocksize); % see below in subfunction
      for j=1:length(chanindx)
        % cut out the part that corresponds with a single channel
        dat(j,((i-begepoch)*epochlength+1):((i-begepoch+1)*epochlength)) = buf((1:epochlength) + chanoffset(EDF.chansel(chanindx(j))));
      end

    elseif length(chanindx)==1
      % this is more efficient if only one channel has to be read, e.g. the status channel
      offset = EDF.HeadLen + (i-1)*blocksize*2; % in bytes
      offset = offset + (chanindx-1)*epochlength*2;
      % read the data for a single channel
      buf = readLowLevel(filename, offset, epochlength); % see below in subfunction
      dat(:,((i-begepoch)*epochlength+1):((i-begepoch+1)*epochlength)) = buf;

    else
      % read the data from all channels, subsequently select the desired channels
      offset = EDF.HeadLen + (i-1)*blocksize*2; % in bytes
      % read the complete data block
      buf = readLowLevel(filename, offset, blocksize); % see below in subfunction
      buf = reshape(buf, epochlength, nchans);
      dat(:,((i-begepoch)*epochlength+1):((i-begepoch+1)*epochlength)) = buf(:,chanindx)';
    end
  end

  % select the desired samples
  begsample = begsample - (begepoch-1)*epochlength;  % correct for the number of bytes that were skipped
  endsample = endsample - (begepoch-1)*epochlength;  % correct for the number of bytes that were skipped
  dat = dat(:, begsample:endsample);

  % Calibrate the data
  if variableFs
    % using a sparse matrix speeds up the multiplication
    calib = sparse(diag(EDF.Cal(EDF.chansel(chanindx))));
    dat   = full(calib * dat);
  elseif length(chanindx)==1
    % in case of one channel the calibration would result in a sparse array
    calib = EDF.Cal(chanindx);
    dat   = calib * dat;
  else
    % using a sparse matrix speeds up the multiplication
    calib = sparse(diag(EDF.Cal(chanindx)));
    dat   = calib * dat;
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for reading the 16 bit values
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function buf = readLowLevel(filename, offset, numwords)
if offset < 2*1024^2
  % use the external mex file, only works for <2GB
  buf = read_16bit(filename, offset, numwords);
else
  % use plain matlab, thanks to Philip van der Broek
  fp = fopen(filename,'r','ieee-le');
  status = fseek(fp, offset, 'bof');
  if status
    error(['failed seeking ' filename]);
  end
  [buf,num] = fread(fp,numwords,'bit16=>double');
  fclose(fp);
  if (num<numwords)
    error(['failed reading ' filename]);
    return
  end
end


function helpstring = hlp_HelpToString(func,args,maxwrapcols)
% Automatically create Help Text from the arg specification for a given
% function. This will only generate help for arguments that are reportable
% (i.e., any  arg_norep()  arguments will be ignored). This program
% can only be applied to functions which use Christian Kothe's argument
% input/output specification (http://sccn.ucsd.edu/wiki/BCILAB).
%
% Inputs:
%
%   func:   the handle to the function for which to generate helptext
%   args:   cell array of mandatory argument inputs for the function
%   maxwrapcols: the max number of columns before wrapping. Use Inf for
%                no wrapping
% Output:
%
%   helpstring: formatted help text which can be pasted as a function
%               header.
%
% Example:
%
% helpstring = hlp_HelpToString(@pre_prepData,struct([]))
%
% helpstring =
%
% Optional                 Information
% -------------------------------------------------------------------------------------------------------------------------------
% VerbosityLevel:          Verbosity level. 0 = no output, 1 = text, 2 = graphical
%                          Possible values: 0,1,2
%                          Default value  : 0
%                          Input Data Type: real number (double)
%
% backupOriginalData:      Keep a nonnormalized copy of the data
%                          Input Data Type: boolean
%
% SelectComponents:        Select components to analyze
%                          Input Data Type: string
%
%     | VerbosityLevel:    Verbosity level. 0 = no output, 1 = text, 2 = graphical
%                          Possible values: 0,1,2
%                          Default value  : 0
%                          Input Data Type: real number (double)
%
%     | ComponentsToKeep:  Select components to analyze
%                          This should be a cell array of strings containing the IDs of components you wish to keep
%                          Input Data Type: boolean
%
% EpochTimeRange:          [Min Max] Epoch time range (sec)
%                          If blank, use all original epoch length
%                          Input Data Type: real number (double)
%
% NewSamplingRate:         New sampling rate
%                          Data will be down/upsampled using a zero-phase filter (see 'help resample')
%                          Input Data Type: real number (double)
% ...
%
%
% See Also: arg_report()
%
% Author: Tim Mullen 2011, SCCN/INC, UCSD.
% Email:  tim@sccn.ucsd.edu

% This function is part of the Source Information Flow Toolbox (SIFT)
%
% 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 3 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

if nargin<3
    maxwrapcols = 100;
end

% get the full argument spec for this function
rep = arg_report('rich',func,args);

helpstring = [{'Input','Information'}; parseHelpText(rep,0,maxwrapcols)];

helpstring = cprintf([helpstring cell(size(helpstring,1), 1)],'-la',true,'-it',true,'-E','','-Lcs','-');
helpstring = [repmat('-',[1 size(helpstring,2)]); helpstring];

function output = parseHelpText(rep,indentlevel,maxwrapcols)
% This functon generates formatted help text for an argument (and its children).
%
% Inputs:
%         rep:              The rich report for an argument
%         indentlevel:      The depth level for this argument
%         maxwrapcols:      The max number of columns before wrapping. Use Inf for
%                           no wrapping
%
% Outputs:
%         output:           Formatted help text

% indent delimiter
delim = '    ';

if nargin < 3
    maxwrapcols = 100;
end
if nargin < 2
    indentlevel = 0;
end

output = {};

% iterate through all arguments and generate help text
for i=1:length(rep)

    if strcmpi(rep(i).names{1},'arg_selection')
        continue;
    end

    % extract the name of the entry
    name = rep(i).names{fastif(length(rep(i).names)==1,1,2)};

    % extract the help text for this entry
    % apply textwrapping if necessary
    helptext = {};
    rep(i).help = strtrim(rep(i).help);
    for j=1:length(rep(i).help)
        helptext = [helptext; textwrap([],rep(i).help(j),maxwrapcols)];
    end

    % add the data range and default value to helptext
    range = rep(i).range;
    def   = rep(i).value;
    if isempty(def), def='n/a'; end

%     if ~strcmpi(def,'__arg_mandatory__') %~isempty(range)

    def = fastif(strcmpi(def,'__arg_mandatory__'),'MANDATORY INPUT',def);

    if isempty(range), range = 'Unrestricted'; end

    if iscell(rep(i).range)
        if ~isempty(rep(i).range), range = cell2str(rep(i).range); end %['''' strrep(cell2str(rep(i).range),',',''',''') ''''];
        def = cell2str(def);
        helptext = [helptext; ...
                    sprintf('Possible values: %s', range); ...
                    sprintf('Default value  : %s', def)];
    elseif isnumeric(rep(i).range)
        if ~isempty(rep(i).range), range = ['[' num2str(rep(i).range) ']']; end
        helptext = [helptext; ...
                    sprintf('Input Range  : %s', range); ...
                    sprintf('Default value: %s', fastif(ischar(def),def,num2str(def)))];
    else
        if ~isempty(rep(i).range), range = char(rep(i).range); end
        helptext = [helptext; ...
                    sprintf('Possible values: %s', range); ...
                    sprintf('Default value  : ''%s''', def)];
    end

%     else
%         helptext = [helptext; ...
%                         'MANDATORY INPUT'];
%     end

    % add the input type to help text
    type  = simplifyDataType(rep(i).type);
    helptext = [helptext; sprintf('Input Data Type: %s',type)];

    % construct help text for this entry
%     output = cprintf({[name ':'], arg.(name)},'-d','\t','-cla',true,'-O',@(x) cprintf(x,'-E','','-d','\t','-la',true), ...
%                          '-L',repmat('\t',[1 indentlevel]));
%     output = [output; {name,cprintf(char(helptext),'-cla',true,'-O',@(x) cprintf(x,'-E','','-d','\t','-la',true),'-L',repmat('\t',[1 indentlevel])) } ];

    % create blank leading "row"
    output = [output; {'',''}];

    % fill row with [Name:   Help Text]
    output = [output; {[name ':'],char(helptext)} ];

%     % check if this has children and if so, create underline
%     % TM: omitted
%     if ismember_bc(char(rep(i).head),{'arg_sub','arg_subswitch','arg_subtoggle'})
%         output = [output; {repmat('-',1,length(name)+1), ''}];
%     end

    % now parse children and insert helptext as needed
    if ~isempty(rep(i).alternatives)
        for k=2:length(rep(i).alternatives)
            for child=1:length(rep(i).alternatives{k})
                ret    =  parseHelpText(rep(i).alternatives{k}(child),indentlevel,maxwrapcols);
                if ~isempty(ret)
                    for q=1:size(ret,1)
                        % insert indented child argument block with pipe
                        % character prepended
                        output = [output; {[repmat(delim,[1 indentlevel+1]) fastif(isempty(ret{q,1}),'','| ') ret{q,1}] ret{q,2}}];
                    end
                end
            end
        end
    end

    if ~isempty(rep(i).children)
        % parse children help text
        for child=1:length(rep(i).children)
            ret    =  parseHelpText(rep(i).children(child),indentlevel,maxwrapcols);
            if ~isempty(ret)
                for q=1:size(ret,1)
                    % insert indented child argument block with pipe
                    % character prepended
                    output = [output; {[repmat(delim,[1 indentlevel+1]) fastif(isempty(ret{q,1}),'','| ') ret{q,1}] ret{q,2}}];
                end
            end
        end
    end

end

% convert cell array of numbers or other data to a comma-delimited string
% numeric contents are converted to form a,b,c
% char contents are wrapped with quotes in form 'a','b','c'
% all other datatypes are converted to form a,b,c
function c = cell2str(C)

c = '';

if ~iscell(C)
    C = {C};
end

for k=1:length(C)
    if isnumeric(C{k})
        c = [c num2str(C{k}) ','];
    elseif ischar(C{k})
        c = [c '''' C{k} ''','];
    else
        c = [c char(C{k}) ','];
    end
end

c(end) = []; % delete trailing comma

% convert coded datatypes into more human-readable expressions
function plaintext = simplifyDataType(datatype)

    switch datatype
        case 'logical'
            plaintext = 'boolean';
        case 'char'
            plaintext = 'string';
        case 'int8'
            plaintext = 'integer (int8)';
        case 'uint8'
            plaintext = 'integer (uint8)';
        case 'int16'
            plaintext = 'integer (int16)';
        case 'uint16'
            plaintext = 'integer (uint16)';
        case 'int32'
            plaintext = 'integer (int32)';
        case 'int64'
            plaintext = 'integer (int64)';
        case 'uint64'
            plaintext = 'integer (uint64)';
        case 'denserealsingle'
            plaintext = 'real number (single)';
        case 'denserealdouble'
            plaintext = 'real number (double)';
        case 'densecomplexsingle'
            plaintext = 'complex number (single)';
        case 'densecomplexdouble'
            plaintext = 'complex number (double)';
        case 'sparserealsingle'
            plaintext = 'real number (sparse single)';
        case 'sparserealdouble'
            plaintext = 'real number (sparse double)';
        case 'sparsecomplexsingle'
            plaintext = 'complex number (sparse single)';
        case 'sparsecomplexdouble'
            plaintext = 'complex number (double)';
        case 'cellstr'
            plaintext = 'cell array of strings (cellstr)';
        case 'object'
            plaintext = 'any matlab object';
        case 'expression'
            plaintext = 'any evaluable Matlab expression.';
        otherwise
            plaintext = datatype;
    end


function b = uniquestripped(a)
%UNIQUESTRIPPED  Internal function (version without checkings etc.)

b = sort(a(:)');
i = diff([b NaN])~=0;
if ~all(i)
    b = b(i);
end


function lung_roi = PTKGetLeftLungROIFromLeftAndRightLungs(lung_image, left_and_right_lung_mask, reporting)
    % PTKGetLeftLungROIFromLeftAndRightLungs. Extracts a region of interest for the left lung given the original image and the left and right lung mask.
    %
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2012.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    mask = left_and_right_lung_mask.Copy;
    mask.ChangeRawImage(uint8(mask.RawImage == 2));
    mask.CropToFitWithBorder(5);
    mask.AddBorder(5);
    lung_roi = lung_image.Copy;
    lung_roi.ResizeToMatch(mask);
end


function sliderHandle = superSlider(varargin)

% SUPERSLIDER creates a more flexible slider, that allows for
% multiple slides, in place of the uicontrol('Style', 'Slider')
%
%   Developed to imitate a consistant, system independent version
%   of matlab's slider object, with the added capability of creating
%   multiple slides along a single body.
%
%   NOTE: This program was executed using functions rather than the more
%   intuitive use of classes, due to known slow matlab object oriented
%   processing time/ high system overhead. This means that there are few
%   applicable getters and setters, so properties should be handled upon
%   initiation of the function.
%
%   FORMAT: superSlider(handle, 'Property', Value)
%           Calling superSlider alone creates a basic slider with default
%           settings
%
%   PROPERTIES:
%   > handle: figure that the slider is placed in, otherwise it defaults to
%     gcf (use superSlider(figure) to generate a new window + slider)
%   > position: [left bottom width height], uses normalized units
%   > value: an array of length numSliders, containing the starting
%   position of each slide.
%   > tagName: Reference name of the slider object (return val alternative)
%   > min: starting value of the slider
%   > max: ending value of the slider
%   > stepSize: how much space between each arrow/slider click
%   > numSlides: the number of slides to create
%   > discrete: true/false - whether the slide is limitted to certain steps
%   > controlColor: color of the selectable controls [r g b]
%   > backgroundColor: color of the body of the slider [r g b]
%   > callback: a function that gets executed when the slider is used. It
%   is included in single quotations: superSlider('callback',
%   'functionName(Variables)');
%
%   DATA RETRIEVAL:
%   The return value of the function sliderHandle, is the parent to all the
%   slides and contains the values of each one in an array.
%   The value of the slides and the active slide can be found respectively
%   in the rows of the matrix stored in userData, accessed as follows:
%   > infoMatrix = get(sliderHandle, 'UserData')     which returns:
%                       [loc1, loc2, ...]            absolute location
%                       [ 0/1   0/1  ...]            a 1 indicates active
%   To get the handles to the slides (in ascending order):
%   > allSlides = get(sliderHandle, 'Children'); (empty if there was no
%   room for a visible slide in the given slider area)
%
%   Use of this code is encouraged for any interested party, but please
%   keep my name or cite me!
%   Created by: Danielle Ripsman
%               August 27, 2013
%               danielle.ripsman@mail.utoronto.ca

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

props = []; % Object to contain the slider preferences
parts = []; %Object to contain slider features

% Sort out the handle/parent situation
if nargin > 0
    if ishandle(varargin{1})
        props.parent = varargin{1};
    else
        props.parent = gcf;
    end
end

% Match up inputs to their respective variable names
for ii = 1:nargin - 1
    if ischar(varargin{ii})
        switch lower(varargin{ii}) % Keeping things case insensitive
            case 'position'
                props.position = varargin{ii+1};
            case 'value'
                props.value = varargin{ii+1};
            case 'tagname'
                props.tagName = varargin{ii+1};
            case 'min'
                props.min = varargin{ii+1};
            case 'max'
                props.max = varargin{ii+1};
            case 'stepsize'
                props.stepSize = varargin{ii+1};
            case 'numslides'
                props.numSlides = varargin{ii+1};
            case 'discrete'
                props.discrete = varargin{ii+1};
            case 'controlcolor'
                props.controlColor = varargin{ii+1};
            case 'backgroundcolor'
                props.backgroundColor = varargin{ii+1};
            case 'callback'
                props.callback = varargin{ii+1};
        end
        ii = ii + 1;
    end
end

% Check + complete inputs
props = getDefaults(props);

%Find the size of the figure, in terms of pixels
[parts.pixWidth, parts.pixHeight] = getPixSize(props.parent);

% Determine the location and alignment of the slider components
[props, parts] = sliderLayout(props, parts);

% Body component
propsBod = makeBodySpecs(props, parts);
[sliderHandle, props.largeEnough] = makeSliderComponent(propsBod, false);
clear('propsBod');

% Arrow components
[propsDec, propsInc] = makeArrowSpecs(sliderHandle, props, parts);
decreaseHandle = makeSliderComponent(propsDec, true); % Left arrow button
increaseHandle = makeSliderComponent(propsInc, true);% Right arrow button
clear('propsDec'); clear('propsInc');

% Tagname for the uicontrol, acting as the slider handle
if isfield(props, 'tagName'); set(sliderHandle, 'tag', props.tagName); end

% Make sure the whole slider is deleted when the handle's deleted
set(sliderHandle, 'deleteFcn', {@grabArrows, decreaseHandle, increaseHandle});

% Determining a permissible number of slides
if ~isfield(props, 'numSlides') || ~props.largeEnough || props.numSlides < 1
    props.numSlides = 1;
end
props.numSlides = floor(props.numSlides); % In case a non-integer gets by

% Creating all requested/granted slides dynamically
sliderHandle = makeSlides(sliderHandle, props);

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function props = getDefaults(props)
if ~isfield(props, 'parent')
    props.parent = gcf;
end
if ~isfield(props, 'position')
    props.position = [.1 .1 .2 .04];
end
if ~isfield(props, 'controlColor')
    props.controlColor = [.85 .85 .85];
end
if ~isfield(props, 'backgroundColor')
    props.backgroundColor = [1 1 1];
end
if ~isfield(props, 'value');
    props.value = [];
else
    props.value = sort(props.value);
end
if ~isfield(props, 'min')
    props.min = 0;
end
if ~isfield(props, 'max') || props.max <= props.min
    props.max = props.min + 1;
end
if ~isfield(props, 'stepSize') || props.stepSize <= 0
    props.stepSize = props.max - props.min;
end
if ~isfield(props, 'discrete')
    props.discrete = false;
end
if ~isfield(props, 'callback')
    props.callback = '';
end

% Keep the arrows visible, by matching light to dark text/background
if sum(props.controlColor) < 1.5
    props.textColor = [1 1 1];
else
    props.textColor = [0 0 0];
end

% Other auto illustration details
props.borderwidth = 1; %how many pixels wide to make the borders
props.shadow = [.1 .1 .1];
props.highlight = [.65 .65 .65];
props.secondHighlight = [.4 .4 .4];

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function [pixWidth, pixHeight] = getPixSize(fig)
% Pixel size, since some built-in features are measured in pixels
preserveUnits = get(fig, 'units');
set(fig, 'units', 'pixels');
figSize = get(fig, 'position');
pixWidth = figSize(3);
pixHeight = figSize(4);
set(fig, 'units', preserveUnits); %return object to initial units

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function [props, parts] = sliderLayout(props, parts)
position = props.position;

% Configuration details of the slider, based on the input position
if position(3) >= position(4)
    % Make a HORIZONTAL slider:
    parts.dSymbol = '<';
    parts.iSymbol = '>';
    props.hzn = true;
    constDir = 4; % Height of the component = the slider height
    constPix = parts.pixWidth;
    varDir = 3; % Width is adjustable
    varPix = parts.pixHeight;
else
    % Make a VERTICAL slider:
    parts.dSymbol = 'v';
    parts.iSymbol = '^';
    props.hzn = false;
    constDir = 3; % Width of the component = the slider width
    constPix = parts.pixHeight;
    varDir = 4; % Height is adjustable
    varPix = parts.pixWidth;
end

% Try to create square buttons
if 2*position(constDir)*varPix < position(varDir)*constPix
    size = position(constDir)*varPix/constPix;
else
    size = position(varDir)/2;
end

% Allocate the remaining area properly between the components
posDecreaseArrow = position;
posDecreaseArrow(varDir) = size;
posBody = position;
posBody(varDir-2) = posBody(varDir-2) + size - props.borderwidth/constPix;
posBody(varDir) = posBody(varDir) - 2*size + 2*props.borderwidth/constPix;
posIncreaseArrow = position;
posIncreaseArrow(varDir-2) = posIncreaseArrow(varDir-2)+posIncreaseArrow(varDir)-size;
posIncreaseArrow(varDir) = size;

% Asign to structure for easier passing
parts.posDArrow = posDecreaseArrow;
parts.posBody = posBody;
parts.posIArrow = posIncreaseArrow;

props.constDir = constDir;
props.varDir = varDir;

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function [propsBod] = makeBodySpecs(props, parts)
propsBod = props;
propsBod.position = parts.posBody;
propsBod.sym = '';
propsBod.bcolor = props.backgroundColor;
propsBod.color = props.backgroundColor;
propsBod.shadowcolor = props.highlight;
propsBod.callback = '';

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function [propsDec, propsInc] = makeArrowSpecs(sliderHandle, props, parts)
% For the decrease arrow:
propsDec = props;
propsDec.position = parts.posDArrow;
propsDec.sym = parts.dSymbol;
propsDec.bcolor = props.secondHighlight;
propsDec.color = props.controlColor;
propsDec.shadowcolor = props.shadow;
propsDec.callback = {@arrowCallback, sliderHandle, props, -1};

% For the increase arrow:
propsInc = props;
propsInc.position = parts.posIArrow;
propsInc.sym = parts.iSymbol;
propsInc.bcolor = props.secondHighlight;
propsInc.color = props.controlColor;
propsInc.shadowcolor = props.shadow;
propsInc.callback = {@arrowCallback, sliderHandle, props, 1};

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function [panel, largeEnough] = makeSliderComponent(props, uiOverlay)
% If the middle of the slide is unused, it shouldn't cause errors.
largeEnough = false;
width = props.borderwidth;

% Panel background for better visual
panel = uipanel('parent', props.parent,...
            'Units','normalized',...
            'backgroundColor', props.bcolor,...
            'bordertype', 'beveledout',...
            'shadowcolor', props.shadowcolor,...
            'highlightcolor', props.highlight,...
            'borderWidth', width,...
            'Position',props.position,...
            'Visible', 'on');

[pixWidth, pixHeight] = getPixSize(panel);

% Set inner region, so the button region doesn't interfere with the frame
innerPos = [0+width/pixWidth, 0+width/pixHeight,...
            1-2*width/pixWidth, 1-2*width/pixHeight];

if innerPos(3) > 0 && innerPos(4)>0
   largeEnough = true;
   if uiOverlay
   % Clickable+text component of the button
   uicontrol('Parent', panel,...
             'Units','normalized',...
             'Style','text',...
             'Enable', 'inactive',...
             'FontUnits', 'Normalized',...
             'FontName', 'Webdings',...
             'FontSize', .6,...
             'HorizontalAlignment', 'center',...
             'Position', innerPos,...
             'ForegroundColor', props.textColor,...
             'background', props.color,...
             'String', props.sym,...
             'FontWeight', 'bold',...
             'Hittest', 'on',...
             'buttonDownFcn', props.callback);
   end
end

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function grabArrows(hObject, eventData, ar1, ar2)
% Ensures the entire slider is erased as a single object
if ishandle(ar1)
    delete(ar1);
end
if ishandle(ar2)
    delete(ar2);
end

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function sliderHandle = makeSlides(sliderHandle, props)
%if there's enough room for slides
if props.largeEnough
    propsSlide = makeSlideSpecs(sliderHandle, props);
    propsSlide.position = zeros(1, 4);
    numStepsAbs = (props.max - props.min)/props.stepSize + 1;
    numSlots = numStepsAbs + props.numSlides - 1;
    size = 1/numSlots;

    varPos = props.varDir - 2; %width/height component of position array (3/4)
    propsSlide.position(props.varDir) = size;
    propsSlide.position(props.constDir) = 1;

    sliderRef = zeros(2, props.numSlides);
    % sliderRef stores slide info - [loc1, loc2, ...]
    %                               [ 0/1   0/1  ...]

    for ii = 1:props.numSlides
        if isempty(props.value)
            % Fit all sliders on in a row, if unspecified
            propsSlide.position(varPos) = (ii - 1)/numSlots;
        else
            dist = (props.value(ii) - props.min)*size/props.stepSize;
            propsSlide.position(varPos) = dist + (ii - 1)*size;
        end
        slideHandle = makeSliderComponent(propsSlide, true);
        loc = get(slideHandle, 'position');
        sliderRef(1, ii) = (loc(varPos) - size*(ii - 1))/size*props.stepSize + props.min;
    end

    % Reference the children in ascending order:
    slideRef = get(sliderHandle, 'Children');
    set(sliderHandle, 'Children', fliplr(slideRef')');
    set(sliderHandle, 'userData', sliderRef);

    slideSelect(get(slideRef(props.numSlides), 'Children'), sliderHandle, props);

    clear('propsSlide');
else
    if isempty(props.value); props.value = props.min; end
    set(sliderHandle, 'userData', [props.value; 1]);
end

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function [propsSlide] = makeSlideSpecs(sliderHandle, props)
propsSlide = props;
propsSlide.parent = sliderHandle;
propsSlide.sym = '';
propsSlide.bcolor = props.secondHighlight;
propsSlide.color = props.controlColor;
propsSlide.shadowcolor = props.shadow;
propsSlide.callback = {@slideDrag, sliderHandle, props};

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function arrowCallback(hObject, eventData, sliderHandle, props, direction)
set(hObject, 'backgroundcolor', props.textColor);

if props.largeEnough
    slides = get(sliderHandle, 'Children');
    activityMatrix = get(sliderHandle, 'userData');
    activeSlide = slides(activityMatrix(2,:)>0);
    index = find(activityMatrix(2,:));

    location = get(activeSlide, 'Position');

    size = location(props.varDir);
    varPos = props.varDir - 2;

    location(varPos) = location(varPos) + direction*size;

    % Keep the slide in bounds
    location = inBounds(location, varPos, index, size, slides);

    set(activeSlide, 'Position', location);
    sliderRef = get(sliderHandle, 'userData');
    sliderRef(1, index) = (location(varPos) - size*(index - 1))/size*props.stepSize + props.min;
    set(sliderHandle, 'userData', sliderRef);
else
    location = get(sliderHandle, 'userData');
    varPos = 1;
    location(varPos) = location(varPos) + direction*props.stepSize;
    postSlide = props.max;
    preSlide = props.min;
    if location(varPos) < preSlide; location(varPos) = preSlide; end
    if location(varPos) > postSlide; location(varPos) = postSlide; end
    set(sliderHandle, 'userData', location);
end

try % Optional user callback function
   eval(props.callback);
end

pause(.02); %pause for button color effect
set(hObject, 'backgroundcolor', props.controlColor);

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function location = inBounds(location, varPos, index, size, slides)
% makes sure the slide doesn't exceed its bounds, or surrounding slides
if index == 1
    preSlide = 0;
else
    presliderPos = get(slides(index - 1), 'Position');
    preSlide = presliderPos(varPos) + size;
end

if index == length(slides)
    postSlide = 1 - size;
else
    postsliderPos = get(slides(index + 1), 'Position');
    postSlide = postsliderPos(varPos) - size;
end

% Keep slider in bounds
if location(varPos) < preSlide; location(varPos) = preSlide; end
if location(varPos) > postSlide; location(varPos) = postSlide; end

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function slideDrag(hObject, eventData, sliderHandle, props)
% Tracks where the slide was selected, for consistent dragging
oldUnits = get(gcf, 'units');
set(gcf, 'units', 'pixels');
grabpoint = get(gcf,'CurrentPoint');
set(get(hObject, 'parent'), 'userData', grabpoint);
set(gcf, 'units', oldUnits);

% Handles the slide highlighting/selection update
slideSelect(hObject, sliderHandle, props);

% Uses the figure callback mechanisms to control the interactive slide placement
oldMotionFunction = get(gcf, 'WindowButtonMotionFcn');
set(gcf, 'WindowButtonMotionFcn', {@motionUpdate, sliderHandle, get(hObject, 'parent'), props});
set(gcf, 'WindowButtonUpFcn', {@stopDragging, oldMotionFunction, oldUnits});

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function slideSelect(slideHandle, sliderHandle, props)
set(slideHandle, 'backgroundcolor', props.controlColor);
allSlides = get(get(get(slideHandle, 'parent'), 'parent'), 'Children');
sliderRef = zeros(1, length(allSlides));
for ii = 1:length(allSlides) %Goes through all slides, order: largest to smallest
    if get(allSlides(ii), 'Children') ~= slideHandle
        set(get(allSlides(ii), 'Children'), 'backgroundcolor', props.textColor);
    else
        sliderRef(ii) = 1;
    end
    sliderInfo = get(sliderHandle, 'userData');
    sliderInfo(2,:) = sliderRef;
    set(sliderHandle, 'userData', sliderInfo);
end

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function motionUpdate(hObject, eventData, sliderHandle, slideHandle, props)
[pixWidth, pixHeight] = getPixSize(sliderHandle);
oldUnits = get(gcf, 'units');
set(gcf, 'units', 'pixels');
newPoint = get(gcf, 'currentPoint');
position = get(slideHandle, 'position');
grabPoint = get(slideHandle, 'userData');

size = position(props.varDir);
varPos = props.varDir - 2;
if props.hzn; convertPix = pixWidth; else convertPix = pixHeight; end

step = (newPoint(varPos) - grabPoint(varPos))/convertPix;
original = position(varPos);

if ~props.discrete %if intermediate points are allowed:
    position(varPos) = position(varPos) + step;
else
    round(step/size);
    position(varPos) = position(varPos) + round(step/size)*size;
end

sliderRef = get(sliderHandle, 'userData');
index = find(sliderRef(2,:));
slides = get(sliderHandle, 'Children');

position = inBounds(position, varPos, index, size, slides);

shift = (position(varPos) - original)*convertPix;
newPoint(varPos) = grabPoint(varPos) + shift;

newPosAbs = (position(varPos) - size*(index - 1))/size*props.stepSize;
sliderRef(1, index) = newPosAbs + props.min;

set(sliderHandle, 'userData', sliderRef);
set(slideHandle, 'position', position);
set(slideHandle, 'userData', newPoint);

set(gcf, 'units', oldUnits);

try % Optional user callback function
    eval(callback);
end

% -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

function stopDragging(hObject, eventData, oldMotionFunction, oldUnits)
set(gcf, 'WindowButtonMotionFcn', oldMotionFunction, 'units', oldUnits);


function idx = GetParameterIndex(modelname, parametername)
%
% function idx = GetParameterIndex(modelname, parametername)
%
% Given an input modelname and parametername, this function returns the index
% of the parameter within the model.
%
% author: Gary Hui Zhang (gary.zhang@ucl.ac.uk)
%

strings = GetParameterStrings(modelname);

for i=1:length(strings)
    if (strcmp(strings(i), parametername))
        idx = i;
        return;
    end
end

idx = -1;


function [events] = ma_updateEventFcnWithNewBodyInfo(events, newBodyInfo, celBodyData)
%ma_updateEventFcnWithNewBodyInfo Summary of this function goes here
%   Detailed explanation goes here

    for(i=1:length(events)) %#ok<*NO4LP>
        event = events{i};

        if(~isempty(strfind(func2str(event), 'ma_SoITransitionEvents')))
            event = @(T,Y) ma_SoITransitionEvents(T,Y, newBodyInfo, celBodyData);

        elseif(~isempty(strfind(func2str(event), 'ma_TrueAnomalyEvent')))
            [truTarget,~,refBody,~] = event('getInfo',[]);
            event = @(T,Y) ma_TrueAnomalyEvent(T,Y, truTarget, newBodyInfo, refBody);

        elseif(~isempty(strfind(func2str(event), 'ma_NodeEvents')))
            [node,~,refBody,~] = event('getInfo',[]);
            event = @(T,Y) ma_NodeEvents(T,Y, node, newBodyInfo, refBody);

        else
            error(['Unknown event function handle: ', func2str(event)]);
        end

        events{i} = event;
    end
end


function datetickzoom(varargin)
%DATETICKZOOM Date formatted tick labels, automatically updated when zoomed or panned.
%   Arguments are completely identical to does of DATETICK. The argument
%   DATEFORM is reset once zoomed or panned.
%
%   See also datetick, datestr, datenum

if nargin==2 && isstruct(varargin{2}) && isfield(varargin{2},'Axes') && isscalar(varargin{2}.Axes)
    datetickdata = getappdata(varargin{2}.Axes,'datetickdata');
    if isstruct(datetickdata) && isfield(datetickdata,'axh') && datetickdata.axh==varargin{2}.Axes
        axh = datetickdata.axh;
        ax = datetickdata.ax;
        dateform = datetickdata.dateform;
        keep_ticks = datetickdata.keep_ticks;
        if keep_ticks
            set(axh,[ax,'TickMode'],'auto')
            if ~isempty(dateform)
                datetick(axh,ax,dateform,'keeplimits','keepticks')
            else
                datetick(axh,ax,'keeplimits','keepticks')
            end
        else
            if ~isempty(dateform)
                datetick(axh,ax,dateform,'keeplimits')
            else
                datetick(axh,ax,'keeplimits')
            end
        end
    end
else
    [axh,ax,dateform,keep_ticks] = parseinputs(varargin);
    datetickdata = [];
    datetickdata.axh = axh;
    datetickdata.ax = ax;
    datetickdata.dateform = dateform;
    datetickdata.keep_ticks = keep_ticks;

    setappdata(axh,'datetickdata',datetickdata);
    set(zoom(axh),'ActionPostCallback',@datetickzoom)
    set(pan(get(axh,'parent')),'ActionPostCallback',@datetickzoom)
    datetick(varargin{:})
end

function [axh,ax,dateform,keep_ticks] = parseinputs(v)
%Parse Inputs

% Defaults;
nin = length(v);
dateform = [];
keep_ticks = 0;

% check to see if an axes was specified
if nin > 0 & ishandle(v{1}) & isequal(get(v{1},'type'),'axes') %#ok ishandle return is not scalar
    % use the axes passed in
    axh = v{1};
    v(1)=[];
    nin=nin-1;
else
    % use gca
    axh = gca;
end
% Look for 'keepticks'
for i=nin:-1:max(1,nin-1),
   if strcmpi(v{i},'keepticks'),
      keep_ticks = 1;
      v(i) = [];
      nin = nin-1;
   end
end

if nin==0,
   ax = 'x';
else
   ax = v{1};
end
if nin > 1
     % The dateform (Date Format) value should be a scalar or string constant
     % check this out
     dateform = v{2};
     if (isnumeric(dateform) && length(dateform) ~= 1) && ~ischar(dateform)
         error('The Date Format value should be a scalar or string');
     end
end


%mrLoadAlignParams
%
%Loads in the file 'AlignParams.mat' from the current directory.
%If the file is not found, 'VolParams.mat' is loaded, and a new
%file 'AlignParams.mat' is created.  This way, mrLoadAlignParams
%can be used to seamlessly update 'VolParams' to 'AlignParams' to
%incorporate the switch from mrLoadVol to mrAlign.

%7/28/97 gmb   Wrote it.

ipSkip = 0;
curInplane = 0;
aTheta = 0;
cTheta = 0;

qt = '''';
if check4File('AlignParams')
  load AlignParams
else
  disp(['File ',qt,'AlignParams.mat',qt,' not found.']);
  if check4File('VolParams')
    disp(['Loading file ',qt,'VolParams.mat',qt]);
    disp(['and creating file ',qt,'AlignParams.mat',qt,'.']);
    load VolParams		    %holds curSag obXM obYM subject inplane_pix_size
    ipThickness = 1/inplane_pix_size(3);
    mrSaveAlignParams(obXM,obYM,subject,inplane_pix_size,...
	ipThickness,ipSkip,curSag,curInplane,aTheta,cTheta);
  else
    disp(['File ',qt,'VolParams.mat',qt,' not found.']);
    disp(['No files were loaded.']);
    return
  end
end


%% PTspecUIcontrol - ui controls for spectral analyses plots

% ----------------------------------------------------------------------------------
% "THE BEER-WARE LICENSE" (Revision 42):
% <brian.white@queensu.ca> wrote this file. As long as you retain this notice you
% can do whatever you want with this stuff. If we meet some day, and you think
% this stuff is worth it, you can buy me a beer in return. -Brian White
% ----------------------------------------------------------------------------------

if ~isempty(fnameMaster)

%%% tooltips
TooltipString_specRun=['Run current spectral configuration',...
    newline, 'Warning: Set subsampling dropdown @ or < medium for faster processing.'];
TooltipString_presets=['Choose from a selection of PRESET configurations'];
TooltipString_cmap=['Choose from a selection of colormaps'];
TooltipString_smooth=['Choose amount of smoothing'];
TooltipString_2d=['Show 2 dimensional plots'];
TooltipString_user=['Choose the variable you wish to plot (consider PRESETs dropdown menu above for quick configurations)'];
TooltipString_sub100=['Zoom data to show sub 100Hz details',...
    newline, 'Typically used to see propwash or mid-throttle vibration in e.g. Gyro/Pterm/PIDerror'];
TooltipString_phase=['Estimated phase delay based on cross-correlation technique.',...
    newline, 'Note: estimate is most reliable with sufficient stick input so as to modulate the gyro and dterm.',...
    newline, 'Also requires that betaflight debug_mode is set to ''GYRO_SCALED'' '];
TooltipString_scale=['Colormap scaling. Note, the default is set such that an optimally filtered gyro ',...
    newline, 'should show little to no activity with the exception of a sub 100Hz band across throttle.',...
    newline, 'Dterm and motor outputs will typically be noisier, so sometimes scale adjustments ',...
    newline, 'are useful to see details. Otherwise, scaling should be the same when making comparisons'];
TooltipString_controlFreqCutoff=['Hz = Freq cutoff bounds for sub100Hz mean/peak analysis window.',...
    newline  'Changing this will move the yellow dashed lines representing this range (only in sub100Hz view).'];

%%%

% define
smat=[];%string
ampmat=[];%spec matrix
amp2d=[];%spec 2d
freq=[];% freq

% only need to call once to compute extra colormaps
PTcolormap;
SpecLineCols=[];
SpecLineCols(:,:,1) = [colorA; colorA; colorA; colorA];
SpecLineCols(:,:,2) = [colorA; colorA; colorB; colorB];
SpecLineCols(:,:,3) = [colorA; colorB; colorC; colorD];

clear posInfo.SpecPos
cols=[0.04 0.261 0.482 0.703];
rows=[0.64 0.35 0.06];
k=0;
for c=1:4
    for r=1:3
        k=k+1;
        posInfo.SpecPos(k,:)=[cols(c) rows(r) 0.18 0.24];
    end
end

posInfo.computeSpec=            [.896 .87 .0455 .026];
posInfo.resetSpec=              [.942 .87 .0455 .026];
posInfo.saveFig1=               [.8965 .845 .0455 .026];
posInfo.saveSettings1=          [.942 .845 .0455 .026];

posInfo.specPresets=            [.8945 .802 .096 .04];
posInfo.ColormapSelect=         [.8945 .778 .096 .04];
posInfo.smooth_select =         [.8945 .754 .096 .04];
posInfo.controlFreq1Cutoff =    [.91 .715 .023 .025];
posInfo.controlFreq2Cutoff =    [.945 .715 .023 .025];
posInfo.controlFreqCutoff_text =[.9 .74 .07 .025];
posInfo.checkbox2d=             [.91 .685 .04 .02];
posInfo.checkboxPSD=            [.94 .685 .04 .02];

posInfo.AphasedelayText1=[.06 .984 .14 .02];
posInfo.AphasedelayText2=[.282 .984 .14 .02];
posInfo.AphasedelayText3=[.504 .984 .14 .02];
posInfo.AphasedelayText4=[.726 .984 .14 .02];

posInfo.hCbar1pos=[0.04 0.89 0.18  0.02];
posInfo.hCbar2pos=[0.262 0.89 0.18  0.02];
posInfo.hCbar3pos=[0.484 0.89 0.18  0.02];
posInfo.hCbar4pos=[0.706 0.89 0.18  0.02];

posInfo.hDropdn1pos=[0.08 0.97 0.095   0.01];
posInfo.hDropdn2pos=[0.302 0.97 0.095   0.01];
posInfo.hDropdn3pos=[0.524 0.97 0.095   0.01];
posInfo.hDropdn4pos=[0.746 0.97 0.095   0.01];

posInfo.fDropdn1pos=[0.08 0.946 0.095  0.01];
posInfo.fDropdn2pos=[0.302 0.946 0.095  0.01];
posInfo.fDropdn3pos=[0.524 0.946 0.095  0.01];
posInfo.fDropdn4pos=[0.746 0.946 0.095  0.01];

posInfo.Sub100HzCheck1=[0.175 0.945 .06 .025];
posInfo.Sub100HzCheck2=[.4 .945 .06 .025];
posInfo.Sub100HzCheck3=[0.62 0.945 .06 .025];
posInfo.Sub100HzCheck4=[.84 .945 .06 .025];

posInfo.climMax_text = [.01 .913 .025 .024];
posInfo.climMax_input = [.01 .888 .025 .024];
posInfo.climMax_text2 = [.232 .913 .025 .024];
posInfo.climMax_input2 = [.232 .888 .025 .024];
posInfo.climMax_text3 = [.455 .913 .025 .024];
posInfo.climMax_input3 = [.455 .888 .025 .024];
posInfo.climMax_text4 = [.677 .913 .025 .024];
posInfo.climMax_input4 = [.677 .888 .025 .024];
climScale=[0.5 0.5 0.5 0.5; 10 10 10 10];
Flim1=20; % 3.3333Hz steps
Flim2=60;

PTspecfig=figure(2);
set(PTspecfig, 'units','normalized','outerposition',[.1 .1 .75 .8])
PTspecfig.NumberTitle='off';
PTspecfig.Name= ['PIDtoolbox (' PtbVersion ') - Frequency x Throttle Spectrogram'];
PTspecfig.InvertHardcopy='off';
set(PTspecfig,'color',bgcolor);

dcm_obj2 = datacursormode(PTspecfig);
set(dcm_obj2,'UpdateFcn',@PTdatatip);

specCrtlpanel = uipanel('Title','Params','FontSize',fontsz,...
              'BackgroundColor',[.95 .95 .95],...
              'Position',[.89 .68 .105 .24]);

%%% PRESET CONFIGURATIONS

% guiHandles.FileNum = uicontrol(PTspecfig,'Style','popupmenu','string',[fnameMaster],'TooltipString', [TooltipString_FileNum],...
%     'fontsize',fontsz, 'units','normalized','outerposition', [posInfo.fnameASpec],'callback','PTplotSpec;');

guiHandlesSpec.specPresets = uicontrol(PTspecfig,'Style','popupmenu','string',{'Presets:'; '1. Gyro prefilt | Gyro | Dterm prefilt | Dterm' ;  '2. Gyro prefilt | Gyro | Pterm | Dterm' ; '3. Gyro | Dterm | Set point | PID error' ; '4. A|A|B|B Gyro prefilt | Gyro' ; '5. A|A|B|B Dterm prefilt | Dterm' ; '6. A|B|C|D Gyro prefilt ' ;'7. A|B|C|D Gyro '; '8. A|B|C|D Dterm '; '9. A|B|C|D PID error'},...
    'fontsize',fontsz,'TooltipString', [TooltipString_presets], 'units','normalized','outerposition', [posInfo.specPresets],'callback',...
    ['if guiHandlesSpec.specPresets.Value == 1, guiHandlesSpec.SpecSelect{1}.Value=1; guiHandlesSpec.SpecSelect{2}.Value=1; guiHandlesSpec.SpecSelect{3}.Value=1; guiHandlesSpec.SpecSelect{4}.Value=1; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 2, guiHandlesSpec.SpecSelect{1}.Value=3; guiHandlesSpec.SpecSelect{2}.Value=2; guiHandlesSpec.SpecSelect{3}.Value=8; guiHandlesSpec.SpecSelect{4}.Value=7; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 3, guiHandlesSpec.SpecSelect{1}.Value=3; guiHandlesSpec.SpecSelect{2}.Value=2; guiHandlesSpec.SpecSelect{3}.Value=6; guiHandlesSpec.SpecSelect{4}.Value=7; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 4, guiHandlesSpec.SpecSelect{1}.Value=2; guiHandlesSpec.SpecSelect{2}.Value=7; guiHandlesSpec.SpecSelect{3}.Value=5; guiHandlesSpec.SpecSelect{4}.Value=4; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=1; guiHandlesSpec.Sub100HzCheck{4}.Value=1; end;',...
     'if guiHandlesSpec.specPresets.Value == 5, guiHandlesSpec.SpecSelect{1}.Value=3; guiHandlesSpec.SpecSelect{2}.Value=2; guiHandlesSpec.SpecSelect{3}.Value=3; guiHandlesSpec.SpecSelect{4}.Value=2; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 6, guiHandlesSpec.SpecSelect{1}.Value=8; guiHandlesSpec.SpecSelect{2}.Value=7; guiHandlesSpec.SpecSelect{3}.Value=8; guiHandlesSpec.SpecSelect{4}.Value=7; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 7, guiHandlesSpec.SpecSelect{1}.Value=3; guiHandlesSpec.SpecSelect{2}.Value=3; guiHandlesSpec.SpecSelect{3}.Value=3; guiHandlesSpec.SpecSelect{4}.Value=3; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 8, guiHandlesSpec.SpecSelect{1}.Value=2; guiHandlesSpec.SpecSelect{2}.Value=2; guiHandlesSpec.SpecSelect{3}.Value=2; guiHandlesSpec.SpecSelect{4}.Value=2; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 9, guiHandlesSpec.SpecSelect{1}.Value=7; guiHandlesSpec.SpecSelect{2}.Value=7; guiHandlesSpec.SpecSelect{3}.Value=7; guiHandlesSpec.SpecSelect{4}.Value=7; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;',...
     'if guiHandlesSpec.specPresets.Value == 10, guiHandlesSpec.SpecSelect{1}.Value=4; guiHandlesSpec.SpecSelect{2}.Value=4; guiHandlesSpec.SpecSelect{3}.Value=4; guiHandlesSpec.SpecSelect{4}.Value=4; guiHandlesSpec.Sub100HzCheck{1}.Value=0; guiHandlesSpec.Sub100HzCheck{2}.Value=0; guiHandlesSpec.Sub100HzCheck{3}.Value=0; guiHandlesSpec.Sub100HzCheck{4}.Value=0; end;']);

guiHandlesSpec.computeSpec = uicontrol(PTspecfig,'string','Run','fontsize',fontsz,'TooltipString', [TooltipString_specRun],'units','normalized','outerposition',[posInfo.computeSpec],...
    'callback','PTplotSpec;');
guiHandlesSpec.computeSpec.ForegroundColor=[colRun];

guiHandlesSpec.resetSpec = uicontrol(PTspecfig,'string','Reset','fontsize',fontsz,'TooltipString', ['Reset Spectral Tool'],'units','normalized','outerposition',[posInfo.resetSpec],...
    'callback',' for k = 1 :12, delete(subplot(''position'',posInfo.SpecPos(k,:))), end; guiHandlesSpec.specPresets.Value = 1; PTspecUIcontrol; set(PTspecfig, ''pointer'', ''arrow'');');
guiHandlesSpec.resetSpec.ForegroundColor=[cautionCol];

guiHandlesSpec.checkbox2d =uicontrol(PTspecfig,'Style','checkbox','String','2D','fontsize',fontsz,'TooltipString', [TooltipString_2d],...
    'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.checkbox2d],'callback','if ~isempty(fnameMaster), end;updateSpec=1;PTplotSpec;');

guiHandlesSpec.checkboxPSD =uicontrol(PTspecfig,'Style','checkbox','String','PSD','fontsize',fontsz,'TooltipString', ['Power Spectral Density'],...
    'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.checkboxPSD],'callback', 'PTplotSpec;');
guiHandlesSpec.checkboxPSD.Value = 0;

guiHandlesSpec.controlFreqCutoff_text = uicontrol(PTspecfig,'style','text','string','freq lims Hz','fontsize',fontsz,'TooltipString',[TooltipString_controlFreqCutoff],'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.controlFreqCutoff_text]);
guiHandlesSpec.controlFreq1Cutoff = uicontrol(PTspecfig,'style','edit','string',[num2str(round(Flim1))],'fontsize',fontsz,'TooltipString',[TooltipString_controlFreqCutoff],'units','normalized','outerposition',[posInfo.controlFreq1Cutoff],...
     'callback','@textinput_call2; Flim1=round(str2num(guiHandlesSpec.controlFreq1Cutoff.String));updateSpec=1;PTplotSpec;');
guiHandlesSpec.controlFreq2Cutoff = uicontrol(PTspecfig,'style','edit','string',[num2str(round(Flim2))],'fontsize',fontsz,'TooltipString',[TooltipString_controlFreqCutoff],'units','normalized','outerposition',[posInfo.controlFreq2Cutoff],...
     'callback','@textinput_call2; Flim2=round(str2num(guiHandlesSpec.controlFreq2Cutoff.String));updateSpec=1;PTplotSpec;');

guiHandlesSpec.saveFig1 = uicontrol(PTspecfig,'string','Save Fig','fontsize',fontsz,'TooltipString',[TooltipString_saveFig],'units','normalized','ForegroundColor',[saveCol],'outerposition',[posInfo.saveFig1],...
    'callback','guiHandlesSpec.saveFig1.FontWeight=''bold'';PTsaveFig;guiHandlesSpec.saveFig1.FontWeight=''normal'';');

guiHandlesSpec.saveSettings1 = uicontrol(PTspecfig,'string','Save Settings','fontsize',fontsz, 'TooltipString',['Save current settings to PTB defaults' ], 'units','normalized','outerposition',[posInfo.saveSettings1],...
    'callback','guiHandlesSpec.saveSettings1.FontWeight=''bold'';PTsaveSettings; guiHandlesSpec.saveSettings1.FontWeight=''normal'';');
guiHandlesSpec.saveSettings1.ForegroundColor=[saveCol];

guiHandlesSpec.Sub100HzCheck{1} =uicontrol(PTspecfig,'Style','checkbox','String','<100Hz','fontsize',fontsz,'TooltipString', [TooltipString_sub100], 'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.Sub100HzCheck1],'callback','if ~isempty(fnameMaster), end;updateSpec=1;PTplotSpec;');
guiHandlesSpec.Sub100HzCheck{2} =uicontrol(PTspecfig,'Style','checkbox','String','<100Hz','fontsize',fontsz,'TooltipString', [TooltipString_sub100], 'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.Sub100HzCheck2],'callback','if ~isempty(fnameMaster), end;updateSpec=1;PTplotSpec;');
guiHandlesSpec.Sub100HzCheck{3} =uicontrol(PTspecfig,'Style','checkbox','String','<100Hz','fontsize',fontsz,'TooltipString', [TooltipString_sub100], 'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.Sub100HzCheck3],'callback','if ~isempty(fnameMaster), end;updateSpec=1;PTplotSpec;');
guiHandlesSpec.Sub100HzCheck{4} =uicontrol(PTspecfig,'Style','checkbox','String','<100Hz','fontsize',fontsz,'TooltipString', [TooltipString_sub100], 'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.Sub100HzCheck4],'callback','if ~isempty(fnameMaster), end;updateSpec=1;PTplotSpec;');

% create string list for SpecSelect
sA={'NONE','Gyro','Gyro prefilt','PID error','Set point','Pterm','Dterm','Dterm prefilt','PIDsum'};

guiHandlesSpec.SpecSelect{1} = uicontrol(PTspecfig,'Style','popupmenu','string',sA, 'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.hDropdn1pos]);
guiHandlesSpec.SpecSelect{2} = uicontrol(PTspecfig,'Style','popupmenu','string',sA, 'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.hDropdn2pos]);
guiHandlesSpec.SpecSelect{3} = uicontrol(PTspecfig,'Style','popupmenu','string',sA,  'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.hDropdn3pos]);
guiHandlesSpec.SpecSelect{4} = uicontrol(PTspecfig,'Style','popupmenu','string',sA, 'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.hDropdn4pos]);

guiHandlesSpec.FileSelect{1} = uicontrol(PTspecfig,'Style','popupmenu','string',[fnameMaster], 'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.fDropdn1pos]);
guiHandlesSpec.FileSelect{2} = uicontrol(PTspecfig,'Style','popupmenu','string',[fnameMaster], 'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.fDropdn2pos]);
guiHandlesSpec.FileSelect{3} = uicontrol(PTspecfig,'Style','popupmenu','string',[fnameMaster], 'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.fDropdn3pos]);
guiHandlesSpec.FileSelect{4} = uicontrol(PTspecfig,'Style','popupmenu','string',[fnameMaster], 'fontsize',fontsz,'TooltipString',[TooltipString_user],'units','normalized','outerposition', [posInfo.fDropdn4pos]);

guiHandlesSpec.smoothFactor_select = uicontrol(PTspecfig,'style','popupmenu','string',{'smoothing low' 'smoothing low-med' 'smoothing medium' 'smoothing med-high' 'smoothing high'},'fontsize',fontsz,'TooltipString', [TooltipString_smooth], 'units','normalized','outerposition',[posInfo.smooth_select],...
     'callback','@selection2;updateSpec=1;PTplotSpec;');

guiHandlesSpec.climMax_text = uicontrol(PTspecfig,'style','text','string','scale','fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.climMax_text]);
guiHandlesSpec.climMax_input = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 1))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 1)=str2num(guiHandlesSpec.climMax_input.String);updateSpec=1;PTplotSpec;');

 guiHandlesSpec.climMax_text2 = uicontrol(PTspecfig,'style','text','string','scale','fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.climMax_text2]);
guiHandlesSpec.climMax_input2 = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 2))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input2],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 2)=str2num(guiHandlesSpec.climMax_input2.String);updateSpec=1;PTplotSpec;');

 guiHandlesSpec.climMax_text3 = uicontrol(PTspecfig,'style','text','string','scale','fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.climMax_text3]);
guiHandlesSpec.climMax_input3 = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 3))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input3],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 3)=str2num(guiHandlesSpec.climMax_input3.String);updateSpec=1;PTplotSpec;');

 guiHandlesSpec.climMax_text4 = uicontrol(PTspecfig,'style','text','string','scale','fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','BackgroundColor',bgcolor,'outerposition',[posInfo.climMax_text4]);
guiHandlesSpec.climMax_input4 = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 4))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input4],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 4)=str2num(guiHandlesSpec.climMax_input4.String);updateSpec=1;PTplotSpec;');

 guiHandlesSpec.ColormapSelect = uicontrol(PTspecfig,'Style','popupmenu','string',{'parula','jet','hot','cool','gray','bone','copper','viridis','linear-RED','linear-GREY'},...
    'fontsize',fontsz,'TooltipString', [TooltipString_cmap], 'units','normalized','outerposition',[posInfo.ColormapSelect],'callback','@selection2;updateSpec=1; PTplotSpec;');
guiHandlesSpec.ColormapSelect.Value=3;% jet 2 hot 3 viridis 8

try guiHandlesSpec.SpecSelect{1}.Value = defaults.Values(find(strcmp(defaults.Parameters, 'FreqXthr-Column1'))), catch, guiHandlesSpec.SpecSelect{1}.Value=3; end
try guiHandlesSpec.SpecSelect{2}.Value = defaults.Values(find(strcmp(defaults.Parameters, 'FreqXthr-Column2'))), catch, guiHandlesSpec.SpecSelect{2}.Value=2; end
try guiHandlesSpec.SpecSelect{3}.Value = defaults.Values(find(strcmp(defaults.Parameters, 'FreqXthr-Column3'))), catch, guiHandlesSpec.SpecSelect{3}.Value=8; end
try guiHandlesSpec.SpecSelect{4}.Value = defaults.Values(find(strcmp(defaults.Parameters, 'FreqXthr-Column4'))), catch, guiHandlesSpec.SpecSelect{4}.Value=7; end
try guiHandlesSpec.specPresets.Value = defaults.Values(find(strcmp(defaults.Parameters, 'FreqXthr-Preset'))), catch, guiHandlesSpec.specPresets.Value = 1; end
try guiHandlesSpec.ColormapSelect.Value = defaults.Values(find(strcmp(defaults.Parameters, 'FreqXthr-Colormap'))), catch, guiHandlesSpec.ColormapSelect.Value = 3; end
try guiHandlesSpec.smoothFactor_select.Value = defaults.Values(find(strcmp(defaults.Parameters, 'FreqXthr-Smoothing'))), catch, guiHandlesSpec.smoothFactor_select.Value = 3; end

else
     warndlg('Please select file(s)');
end

% functions
function selection2(src,event)
    val = c.Value;
    str = c.String;
    str{val};
end

function getList2(hObj,event)
v=get(hObj,'value')
end

function textinput_call2(src,eventdata)
str=get(src,'String');
    if isempty(str2num(str))
        set(src,'string','0');
        warndlg('Input must be numerical');
    end
end


% SAP2MATLAB parses Siemens ASCII protocol data and generates a
% corresponding MATLAB data structure.
%
% This function is used for de-serialising the header information
% from a FieldTrip buffer containing fMRI data from a Siemens scanner.

% Copyright (C) 2010, Stefan Klanke,
% 	Modified by Tim van Mourik, 2014
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

warning('Trying to compile MEX file')
oldDir = pwd;
try
    %This is where the header files are located
    options = '-I../include';
    %These two C-files are copiled
    fileNames = '../src/sap2matlab.c ../src/siemensap.c ';
    %the compilation does not depend on external libraries
    libraries = [];
    eval(['mex ' fileNames, libraries, options]);
catch
    rethrow(lasterror)
end

%% Test
%  This file contains an example string that will be parsed by sap2matlab
load('mrprotString.mat', 'apstr');
S = sap2matlab(apstr);

%%
file = '../Projects/TestReadMrprot/mrprot_triotim.txt';
f = fopen(file, 'r');
textData = fread(f);
fclose(f);

S = sap2matlab(sprintf('%s', textData));


function LP = setCplexParam(LP, solverParams,verbFlag)
% Sets the parameters of the IBM ILOG CPLEX object according to the structure `solverParams`
% The `solverParams` structure has to contain the same structure as the
% Cplex.Param structue in a Cplex object. But values can be set by directly
% setting the respective parameter instead of the `Cur` value which would
% be necessary if directly modifying the Cplex.Param structure.
% USAGE:
%
%    LP = setCplexParam(LP, solverParams, verbFlag);
%
% INPUTS:
%    LP:              IBM-ILOG Cplex object
%    solverParams:    parameter structure for Cplex. Check `LP.Param`. For example,
%                     `[solverParams.simplex.display, solverParams.tune.display, solverParams.barrier.display, solverParams.sifting.display, solverParams.conflict.display] = deal(0);`
%                     `[solverParams.simplex.tolerances.optimality, solverParams.simplex.tolerances.feasibility] = deal(1e-9,1e-8);`
%
%                     The full set of parameters can be obtained by calling 'Cplex().Param'
%    verbFlag:        true to show which parameter input is problematic if any (optional, default true)

if nargin < 3
    verbFlag = true;
end
% if isempty(solverParams)
%     solverParams = struct();
% end
if isempty(fieldnames(solverParams))
    return
end

% get all the end parameters and their paths from LP.Param
%(e.g., LP.Param.simplex.display will have 'display' in paramList and 'LP.Param.simplex' in paramPath)
[paramList, paramPath] = getParamList(LP.Param, 0);

% similarly get all parameters and paths from the user-supplied parameter structure
% (but not going to the bottom level. User-supplied parameter structure no
% need to have *.Cur field after every parameter. But still supported if there is .Cur)
[paramUserList, paramUserPath, addCur] = getParamList(solverParams, 1);

% whether an user-supplied parameter can be matched to LP.Param
paramIden = false(numel(paramUserList), 1);
for p = 1:numel(paramUserList)
    f = strcmpi(paramList,paramUserList{p});
    if sum(f) == 1
        % uniquely matched
        paramIden(p) = true;
        str = ['LP.Param.' paramPath{f} '.Cur = solverParams.' paramUserPath{p}];
        if addCur(p)  % add back .Cur if in the user-supplied parameters
            str = [str '.Cur'];
        end
        str = [str ';'];
        eval(str);
    elseif sum(f) > 1
        % not uniquely matched (for parameters end with 'display' etc.)
        % compare the path then
        if ismember(lower(paramUserPath{p}), paramPath)
            paramIden(p) = true;
            str = ['LP.Param.' lower(paramUserPath{p}) '.Cur = solverParams.' paramUserPath{p}];
            if addCur(p)  % add back .Cur if in the user-supplied parameters
                str = [str '.Cur'];
            end
            str = [str ';'];
            eval(str);
        else
            if verbFlag
                warning('*.%s cannot be uniquely identified as a valid cplex parameter. Ignore.', paramUserPath{p});
            end
        end
    else
        % not matched at all
        if verbFlag
            warning('*.%s cannot be identified as a valid cplex parameter. Ignore.', paramUserPath{p});
        end
    end
end
end


function output = fetch_output(handle)
    if isa(handle, 'caffe.Net')
        output = fetch_net_output(handle);
    elseif isa(handle, 'caffe.Solver')
        cnt = zeros(20,1);
        for i = 1 : length(handle.nets)
            output_t = fetch_net_output(handle.nets{i});
            if i == 1
                output = output_t;
                for j = 1 : length(output_t)
                    if output_t(j).value > 0
                        cnt(j)=cnt(j)+1;
                    end
                end
            else
                for j = 1 : length(output_t)
                    output(j).value = output(j).value + output_t(j).value;
                    if output_t(j).value > 0
                        cnt(j)=cnt(j)+1;
                    end
                end
            end
        end
        for j = 1 : length(output)
            output(j).value = output(j).value / cnt(j);
        end
    else
        error('only accept net or solver handle');
    end
end

function output = fetch_net_output(handle)
    outputs = handle.outputs;
    for i = 1 : length(outputs)
        output(i).name = outputs{i};
        output(i).value = handle.blob_vec(handle.name2blob_index(output(i).name)).get_data();
    end
end


% cell2file   writes a cell array of strings, or number into a file
% ==This function has been obtained modifyng the file str2file.m I found in the FEX.==
% Last Modified: 2008/01/09 (yyyy/mm/dd).
%
% Syntax: cell2file(fid,cell,....) where ... is PARAMETER, VALUE (in pair).
%
% Possible Parameter are: 'EndOfLine' and 'Delimiter'
%
% cell2str can write to a file both sting and numeric format.
%
% You just have to pass the handle to the file
%(so that you can choice your opening attribute) and the cellarray.
%
% Ex:
% A={'prova',[1],'prova';
% [3],'txt',[4]};
%
% fid=fopen('prova.log','a+');
% cell2file(fid,A,'EndOfLine','\r\n');
% fclose(fid);

function cell2file(fid,str,varargin)

id=find(strcmpi('Delimiter',varargin)==1);
if isempty(id)
    delimiter=';';
else
    delimiter=varargin{id+1};
end

id=find(strcmpi('EndOfLine',varargin)==1);
if isempty(id)
    EndOfLine='\n';
else
    EndOfLine=varargin{id+1};
end

for k=1:size(str,1)
    for j=1:size(str,2)
        app=[str{k,j}];
        if isnumeric(app)
            app=num2str(app);
        end
        if not(j==size(str,2))
        fprintf(fid,['%s' delimiter],app);
        else
            fprintf(fid,'%s',app);
        end
    end

    if not(k==size(str,1))
    fprintf(fid,EndOfLine);
    end
end


function y = str2double(x)
% Linearizes char input to a row, like Matlab does
if ischar(x)
    x = x(:).';
end
y = builtin('str2double', x);
end


function out=hasimag(in)

out=false;
if any(in~='0')
 out=true;
end


function physio = tapas_physio_new_philips(physio)
% Initializes physio-properties for Philips 3T Achieva system with good
% ECG-data
%
%   physio = tapas_physio_new_philips(physio)
%
% IN
%
% OUT
%
% EXAMPLE
%   tapas_physio_new_philips
%
%   See also

% Author: Lars Kasper
% Created: 2014-10-09
% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich.
%
% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public
% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version). For further details, see the file
% COPYING or <http://www.gnu.org/licenses/>.

log_files   = physio.log_files;
scan_timing = physio.scan_timing;
model       = physio.model;
preproc     = physio.preproc;
verbose     = physio.verbose;

sqpar       = scan_timing.sqpar;
sync        = scan_timing.sync;

model.type = 'RETROICOR';
model.order = struct('c',3,'r',4,'cr',1, 'orthogonalise', 'none');
log_files.sampling_interval = 1/496; % for WiFi 1/496, cable: 1/500

sqpar.Ndummies = 0; % PARAM
sqpar.Nscans = 160; % PARAM
sqpar.Nprep = [];
sqpar.time_slice_to_slice = [];

model.order.c = 3;
model.order.r = 4;
model.order.cr = 1;
model.type = 'RETROICOR';

verbose.level = 2; % PARAM
verbose.fig_output_file ='physio_output.png';
verbose.use_tabs = 0;

sync.method = 'gradient_log';
sync.grad_direction = 'z';
sync.slice = 1500;
sync.zero = 1400;
sync.vol_spacing = [];

preproc.cardiac.modality = 'ECG';
preproc.cardiac.initial_cpulse_select.method = 'load_from_logfile'; 'auto_matched';
preproc.cardiac.posthoc_cpulse_select.method = 'off';

scan_timing.sqpar = sqpar;
scan_timing.sync = sync;

physio.log_files = log_files;
physio.scan_timing = scan_timing;
physio.preproc = preproc;
physio.model = model;
physio.verbose = verbose;


function varargout = fracInt(varargin)
%FRACINT  Fractional integral of an TRIGTECH.
%   Fractional integrals of TRIGTECH objects are not yet supported.

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

error('CHEBFUN:TRIGTECH:fracInt:noSupport', ...
    'Fractional integrals of TRIGTECH objects are not yet supported.')

% TODO: Implement this.

end


function convertLeksellFileToHex(fileName)
%"convertLeksellFileToHex"
%   A simple file reader that reads a binary file and writes it out in
%   hexidecimal for easy decription.
%
%KRK 05/31/07
%
%Usage:
%   convertLeksellFileToHex(fileName)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

% Open the given file and read the binary data in bytes
fid = fopen(fileName, 'r', 'b');
binfile = fread(fid, inf, 'uint8');
fclose(fid);

hexfile = dec2hex(binfile, 2); % Convert the bin array to 2 digit hex

wfid = fopen(strcat(fileName,'_HEX.txt'), 'w');
num = size(hexfile); % Gets the size of the hex array
% Write it to a file
for i = 1:1:num(1)
    fwrite(wfid, hexfile(i,:));

    % Space the file out
    if mod(i,10)
        fprintf(wfid, ' ');
    else
        fprintf(wfid, '\n');
    end
end


classdef TestTimelapser
    %TestTimelapser

    methods (Static)
        function test_1
            ttypes = {'AsIs', 'Crop'};
            for i=1:numel(ttypes)
                obj = cv.Timelapser(ttypes{i});
                typename = obj.typeid();
                validateattributes(typename, {'char'}, {'row', 'nonempty'});

                %{
                %TODO
                obj.initialize(corners, sizes);
                obj.process(img, mask, tl);
                dst = obj.getDst();
                %}
            end
        end
    end

end