% PZM2AB convert pzmodel to IIR filter coefficients using bilinear transform.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: PZM2AB convert pzmodel to IIR filter coefficients using bilinear
%              transform.
%
% CALL:        [a,b] = pzm2ab(pzm, fs)
%
% VERSION:     $Id: pzm2ab.m,v 1.10 2009/08/31 17:11:41 ingo Exp $
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function varargout = pzm2ab(varargin)

  import utils.const.*
  
  %%% Collect input variable names
  in_names = cell(size(varargin));
  for ii = 1:nargin,in_names{ii} = inputname(ii);end

  %%% Collect all AOs and plists
  [pzm, pzm_invars, fs] = utils.helper.collect_objects(varargin(:), 'pzmodel', in_names);

  %%% Check inputs
  if numel(pzm) ~= 1
    error('### Please use only one PZ-Model.');
  end
  if numel(fs) ~= 1 && ~isnumeric(fs)
    error('### Please define ''fs''.');
  else
    fs = fs{1};
  end

  gain  = pzm.gain;
  poles = pzm.poles;
  zeros = pzm.zeros;
  np = numel(poles);
  nz = numel(zeros);

  ao = [];
  bo = [];

  utils.helper.msg(utils.const.msg.OPROC1, 'converting %s', pzm.name)

  % First we should do complex pole/zero pairs
  cpoles = [];
  for j=1:np
    if poles(j).q > 0.5
      cpoles = [cpoles poles(j)];
    end
  end
  czeros = [];
  for j=1:nz
    if zeros(j).q > 0.5
      czeros = [czeros zeros(j)];
    end
  end

  czi = 1;
  for j=1:length(cpoles)
    if czi <= length(czeros)
      % we have a pair
      p = cpoles(j);
      z = czeros(czi);

      [ai,bi] = cpolezero(p.f, p.q, z.f, z.q, fs);
      if ~isempty(ao)
        [ao,bo] = pzmodel.abcascade(ao,bo,ai,bi);
      else
        ao = ai;
        bo = bi;
      end

      % increment zero counter
      czi = czi + 1;
    end
  end

  if length(cpoles) > length(czeros)
    % do remaining cpoles
    for j=length(czeros)+1:length(cpoles)
      utils.helper.msg(msg.OPROC2, 'computing complex pole');
      [ai,bi] = cp2iir(cpoles(j), fs);
      if ~isempty(ao)
        [ao,bo] = pzmodel.abcascade(ao,bo,ai,bi);
      else
        ao = ai;
        bo = bi;
      end
    end
  else
    % do remaining czeros
    for j=length(cpoles)+1:length(czeros)
      utils.helper.msg(msg.OPROC2, 'computing complex zero');
      [ai,bi] = cz2iir(czeros(j), fs);
      if ~isempty(ao)
        [ao,bo] = pzmodel.abcascade(ao,bo,ai,bi);
      else
        ao = ai;
        bo = bi;
      end
    end
  end

  % Now do the real poles and zeros
  for j=1:np
    pole = poles(j);
    if isnan(pole.q) || pole.q < 0.5
      utils.helper.msg(msg.OPROC2, 'computing real pole');
      [ai,bi] = rp2iir(pole, fs);
      if ~isempty(ao)
        [ao,bo] = pzmodel.abcascade(ao,bo,ai,bi);
      else
        ao = ai;
        bo = bi;
      end
    end
  end

  for j=1:nz
    zero = zeros(j);
    if isnan(zero.q) || zero.q < 0.5
      utils.helper.msg(msg.OPROC2, 'computing real zero');
      [ai,bi] = rz2iir(zero, fs);
      if ~isempty(ao)
        [ao,bo] = pzmodel.abcascade(ao,bo,ai,bi);
      else
        ao = ai;
        bo = bi;
      end
    end
  end

  ao = ao.*gain;

  varargout{1} = ao;
  varargout{2} = bo;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                               Local Functions                               %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION:    cpolezero
%
% DESCRIPTION: Return IIR filter coefficients for a complex pole and
%              complex zero designed using the bilinear transform.
%
% CALL:        [a,b] = cpolezero(pf, pq, zf, zq, fs)
%
% HISTORY:     18-02-2007 M Hewitson
%                Creation.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [a,b] = cpolezero(pf, pq, zf, zq, fs)

  import utils.const.*
  utils.helper.msg(msg.OPROC1, 'computing complex pole/zero pair');

  wp = pf*2*pi;
  wp2 = wp^2;
  wz = zf*2*pi;
  wz2 = wz^2;

  k = 4*fs*fs + 2*wp*fs/pq + wp2;

  a(1) = (4*fs*fs + 2*wz*fs/zq + wz2)/k;
  a(2) = (2*wz2 - 8*fs*fs)/k;
  a(3) = (4*fs*fs - 2*wz*fs/zq + wz2)/k;
  b(1) = 1;
  b(2) = (2*wp2 - 8*fs*fs)/k;
  b(3) = (wp2 + 4*fs*fs - 2*wp*fs/pq)/k;

  % normalise dc gain to 1
  g = iirdcgain(a,b);
  a = a / g;
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION:    iirdcgain
%
% DESCRIPTION: Work out the DC gain of an IIR filter given the coefficients.
%
% CALL:        g = iirdcgain(a,b)
%
% INPUTS:      a - numerator coefficients
%              b - denominator coefficients
%
% OUTPUTS      g - gain
%
% HISTORY:     03-07-2002 M Hewitson
%                Creation.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function g = iirdcgain(a,b)
  suma = sum(a);
  if(length(b)>1)
    sumb = sum(b);
    g = suma / sumb;
  else
    g = suma;
  end
end
