% HETERODYNE heterodynes time-series.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: HETERODYNE heterodynes time-series.
%
% The input signal is mixed down at the specified frequency. The mixed
% signal is then (optionally) low-pass filtered and (optionally) downsampled 
% to have the specified bandwidth.
%
% CALL:     b = heterodyne(a,pl)
%
% INPUTS:   pl   - a parameter list
%           a    - input analysis object
%
% OUTPUTS:  b    - output analysis object containing the filtered data.
%
% <a href="matlab:utils.helper.displayMethodInfo('ao', 'heterodyne')">Parameters Description</a>
%
% VERSION:     $Id: heterodyne.m,v 1.22 2011/04/08 08:56:16 hewitson Exp $
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function varargout = heterodyne(varargin)
  
  % Check if this is a call for parameters
  if utils.helper.isinfocall(varargin{:})
    varargout{1} = getInfo(varargin{3});
    return
  end
  
  import utils.const.*
  utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
  
  % Collect input variable names
  in_names = cell(size(varargin));
  for ii = 1:nargin,in_names{ii} = inputname(ii);end
  
  % Collect all AOs and plists
  [as, ao_invars] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
  [pl, pl_invars] = utils.helper.collect_objects(varargin(:), 'plist', in_names);
  
  % Make copies or handles to inputs
  bs   = copy(as, nargout);
  
  % Combine plists
  pl = parse(pl, getDefaultPlist());
  
  
  % Get parameters
  t0 = find(pl, 't0');
  f0 = find(pl, 'f0');
  if isempty(f0)
    error('### Please specify the heterodyne frequency with the paramter ''f0''');
  end
  mix = find(pl, 'quad');
  
  for jj = 1:numel(bs)
    if isa(bs(jj).data, 'tsdata')
      
      % store input history
      inhist = bs(jj).hist;
      
      % check bandwidth
      bw = find(pl, 'bw');
      if isempty(bw)
        bw = bs(jj).data.fs;
      end
      if bw > bs(jj).data.fs
        error('### The output bandwidth can not exceed the input bandwidth');
      end
      
      % mix at f0
      switch lower(mix)
        case 'cos'
          bs(jj).data.setY(bs(jj).data.getY .* 2.0 .* cos(2*pi * f0 .* (bs(jj).data.getX - t0)));
        case 'sin'
          bs(jj).data.setY(bs(jj).data.getY .* 2.0 .* sin(2*pi * f0 .* (bs(jj).data.getX - t0)));
        otherwise
          error('### Unknown quadrature specified');
      end
      
      
      % user-input filter for low pass
      filt = find(pl, 'filter');
      
      % lowpass filter at bw/w
      if ~isempty(filt) || utils.prog.yes2true(find(pl, 'lp'))
        if isempty(filt)
          % standard filter for low-pass
          filt = miir(plist('type', 'lowpass', 'order', 4, 'fc', 0.4*bw, 'fs', bs(jj).data.fs));
        end
        bs.filtfilt(filt);
      end
      
      % downsample
      if utils.prog.yes2true(find(pl, 'ds'))
        factor = ceil(bs(jj).data.fs / bw);
        bs.downsample(plist('factor', factor));
      end
      
      % set name and history
      bs(jj).name = sprintf('heterodyne(%s, %s@%.01f Hz)', ao_invars{jj}, mix, f0);
      bs(jj).addHistory(getInfo('None'), pl, ao_invars(jj), inhist);
      % clear errors
      bs(jj).clearErrors;
      
    else
      error('### heterodyne only works on time-series AOs currently.');
    end
  end
  
  % Set output
  if nargout == numel(bs)
    % List of outputs
    for ii = 1:numel(bs)
      varargout{ii} = bs(ii);
    end
  else
    % Single output
    varargout{1} = bs;
  end
end

%--------------------------------------------------------------------------
% Get Info Object
%--------------------------------------------------------------------------
function ii = getInfo(varargin)
  if nargin == 1 && strcmpi(varargin{1}, 'None')
    sets = {};
    pls  = [];
  else
    sets = {'Default'};
    pls  = getDefaultPlist;
  end
  % Build info object
  ii = minfo(mfilename, 'ao', 'ltpda', utils.const.categories.sigproc, '$Id: heterodyne.m,v 1.22 2011/04/08 08:56:16 hewitson Exp $', sets, pls);
  ii.setModifier(false);
end

%--------------------------------------------------------------------------
% Get Default Plist
%--------------------------------------------------------------------------
function plout = getDefaultPlist()
  persistent pl;  
  if exist('pl', 'var')==0 || isempty(pl)
    pl = buildplist();
  end
  plout = pl;  
end

function pl = buildplist()
  
  pl = plist();
  
  % f0
  p = param({'f0', 'The heterodyne frequency in Hz.'}, paramValue.EMPTY_DOUBLE);
  pl.append(p);
  
  % t0
  p = param({'t0', 'Modulation start time offset in s.'}, paramValue.DOUBLE_VALUE(0));
  pl.append(p);
  
  % quad
  p = param({'quad', 'The quadrature to output. ''sin'' or ''cos''.'},{2, {'sin', 'cos'}, paramValue.SINGLE});
  pl.append(p);
  
  % bw
  p = param({'bw', 'The bandwidth at the output in Hz.'}, paramValue.EMPTY_DOUBLE);
  pl.append(p);
  
  % lp
  p = param({'lp', 'Low pass filter the output data at ''bw''.'}, paramValue.YES_NO);
  pl.append(p);
  
  % filter
  p = param({'filter', ['Filter to be used to low pass the output data.<br>' ...
    'If this parameter is set, the low pass is applied regardless to the value of the ''lp'' parameter']}, paramValue.EMPTY_DOUBLE);
  pl.append(p);
  
  % ds
  p = param({'ds','Downsample the output data.'}, paramValue.YES_NO);
  pl.append(p);
end
