% WELCHPARSE   Parser for the PWELCH & SPECTROGRAM functions
%
% Outputs:
% X        - First input signal (used when esttype = MS & PSD)
% M        - An integer containing the length of the data to be segmented
% isreal_x - Boolean for input complexity
% Y        - Second input signal (used when esttype = CPSD, TFE, MSCOHERE)
% Ly       - Length of second input signal (used when esttype = CPSD, TFE, MSCOHERE)
% WIN      - A scalar or vector containing the length of the window or the
%            window respectively (Note that the length of the window determines
%            the length of the segments)
% WINNAME  - String with the window name.
% WINPARAM - Window parameter.
% NOVERLAP - An integer containing the number of samples to overlap (may
%            be empty)
% K        - Number of segments
% OPTIONS  - A structure with the following fields:
% OPTIONS.nfft  - number of freq. points at which the psd is estimated
% OPTIONS.Fs    - sampling freq. if any
% OPTIONS.range - 'onesided' or 'twosided' psd
%
% Direct copy from MATLAB
%
% M Hewitson 08-05-08
%
% $Id: welchparse.m,v 1.3 2010/07/23 14:53:40 mauro Exp $
%

%   Author(s): P. Costa
%   Copyright 1988-2006 The MathWorks, Inc.
%   $Revision: 1.3 $  $Date: 2010/07/23 14:53:40 $

function [x,M,isreal_x,y,Ly,win,winName,winParam,noverlap,k,L,options] = ...
    welchparse(x,esttype,varargin)

  % Parse input arguments.
  [x,M,isreal_x,y,Ly,win,winName,winParam,noverlap,opts,errid,errmsg] = ...
    parse_inputs(x,esttype,varargin{:});
  if ~isempty(errmsg), error(errid,errmsg); end

  % Obtain the necessary information to segment x and y.
  [L,noverlap,win,errid,errmsg] = segment_info(M,win,noverlap);
  if ~isempty(errmsg), error(errid,errmsg); end

  % Parse optional args nfft, fs, and spectrumType.
  [options,msg] = welch_options(isreal_x,L,opts{:});
  if ~isempty(msg), error(generatemsgid('InvalidParam'),msg); end

  % Compute the number of segments
  k = (M-noverlap)./(L-noverlap);

  % Uncomment the following line to produce a warning each time the data
  % segmentation does not produce an integer number of segements.
  %if fix(k) ~= k,
  %   warning(generatemsgid('MustBeInteger'),'The number of segments is not an integer, truncating data.');
  %end

  k = fix(k);
end

%-----------------------------------------------------------------------------------------------
function [x,Lx,isreal_x,y,Ly,win,winName,winParam,noverlap,opts,errid,errmsg] = ...
    parse_inputs(x,esttype,varargin)
  % Parse the inputs to the welch function.

  % Assign defaults in case of early return.
  isreal_x = 1;
  y        = [];
  Ly       = 0;
  is2sig   = false;
  win      = [];
  winName  = 'User Defined';
  winParam = '';
  noverlap = [];
  opts     = {};
  errid    = '';
  errmsg   = '';

  % Determine if one or two signal vectors was specified.
  Lx = length(x);
  if iscell(x),
    if Lx > 1, % Cell array.
      y = x{2};
      is2sig = true;
    end
    x = x{1};
    Lx = length(x);
  else
    if ~any(strcmpi(esttype,{'psd','ms'})),
      errid = generatemsgid('invalidSignalVectors');
      errmsg = 'You must specify a cell array with two signal vectors to estimate either the cross power spectral density or the transfer function.';
      return;
    end
  end

  x = x(:);
  isreal_x = isreal(x);

  % Parse 2nd input signal vector.
  if is2sig,
    y = y(:);
    isreal_x = isreal(y) && isreal_x;
    Ly = length(y);
    if Ly ~= Lx,
      errid = generatemsgid('invalidSignalVectors');
      errmsg = 'The length of the two input vectors must be equal to calculate the cross spectral density.';
      return;
    end
  end

  % Parse window and overlap, and cache remaining inputs.
  lenargin = length(varargin);
  if lenargin >= 1,
    win = varargin{1};
    if lenargin >= 2,
      noverlap = varargin{2};

      % Cache optional args nfft, fs, and spectrumType.
      if lenargin >= 3,  opts = varargin(3:end); end
    end
  end

  if isempty(win) | isscalar(win),
    winName = 'hamming';
    winParam = 'symmetric';
  end

end

%-----------------------------------------------------------------------------------------------
%SEGMENT_INFO   Determine the information necessary to segment the input data.
%
%   Inputs:
%      M        - An integer containing the length of the data to be segmented
%      WIN      - A scalar or vector containing the length of the window or the window respectively
%                 (Note that the length of the window determines the length of the segments)
%      NOVERLAP - An integer containing the number of samples to overlap (may be empty)
%
%   Outputs:
%      L        - An integer containing the length of the segments
%      NOVERLAP - An integer containing the number of samples to overlap
%      WIN      - A vector containing the window to be applied to each section
%      ERRID    - A string containing possible error identifier
%      ERRMSG   - A string containing possible error messages
%
%
%   The key to this function is the following equation:
%
%      K = (M-NOVERLAP)/(L-NOVERLAP)
%
%   where
%
%      K        - Number of segments
%      M        - Length of the input data X
%      NOVERLAP - Desired overlap
%      L        - Length of the segments
%
%   The segmentation of X is based on the fact that we always know M and two of the set
%   {K,NOVERLAP,L}, hence determining the unknown quantity is trivial from the above
%   formula.

function [L,noverlap,win,errid,errmsg] = segment_info(M,win,noverlap)

  % Initialize outputs
  L = [];
  errid = '';
  errmsg = '';

  % Check that noverlap is a scalar
  if any(size(noverlap) > 1),
    errid = generatemsgid('invalidNoverlap');
    errmsg = 'You must specify an integer number of samples to overlap.';
    return;
  end

  if isempty(win),
    % Use 8 sections, determine their length
    if isempty(noverlap),
      % Use 50% overlap
      L = fix(M./4.5);
      noverlap = fix(0.5.*L);
    else
      L = fix((M+7.*noverlap)./8);
    end
    % Use a default window
    win = hamming(L);
  else
    % Determine the window and its length (equal to the length of the segments)
    if ~any(size(win) <= 1) | ischar(win),
      errid = generatemsgid('invalidWindow');
      errmsg = 'The WINDOW argument must be a vector or a scalar.';
      return
    elseif length(win) > 1,
      % WIN is a vector
      L = length(win);
    elseif length(win) == 1,
      L = win;
      win = hamming(win);
    end
    if isempty(noverlap),
      % Use 50% overlap
      noverlap = fix(0.5.*L);
    end
  end

  % Do some argument validation
  if L > M,
    errid = generatemsgid('invalidSegmentLength');
    errmsg = 'The length of the segments cannot be greater than the length of the input signal.';
    return;
  end

  if noverlap >= L,
    errid = generatemsgid('invalidNoverlap');
    errmsg = 'The number of samples to overlap must be less than the length of the segments.';
    return;
  end
end

%------------------------------------------------------------------------------
%WELCH_OPTIONS   Parse the optional inputs to the PWELCH function.
%   WELCH_OPTIONS returns a structure, OPTIONS, with following fields:
%
%   options.nfft         - number of freq. points at which the psd is estimated
%   options.Fs           - sampling freq. if any
%   options.range        - 'onesided' or 'twosided' psd

function [options,msg] = welch_options(isreal_x,N,varargin)

  % Generate defaults
  options.nfft = max(256,2^nextpow2(N));
  options.Fs = []; % Work in rad/sample

  % Determine if frequency vector specified
  freqVecSpec = false;
  if (length(varargin) > 0 && length(varargin{1}) > 1)
    freqVecSpec = true;
  end

  if isreal_x && ~freqVecSpec,
    options.range = 'onesided';
  else
    options.range = 'twosided';
  end
  msg = '';

  if any(strcmp(varargin, 'whole'))
    warning(generatemsgid('invalidRange'), '''whole'' is not a valid range, use ''twosided'' instead.');
  elseif any(strcmp(varargin, 'half'))
    warning(generatemsgid('invalidRange'), '''half'' is not a valid range, use ''onesided'' instead.');
  end

  [options,msg] = psdoptions(isreal_x,options,varargin{:});

end

