gusucode.com > 信号处理工具箱 - signal源码程序 > signal\signal\signal\cremez.m

    function [h,delta,result] = cremez(M, edges, filt_str, varargin)
%CREMEZ Complex and nonlinear phase equiripple FIR filter design.
%  CREMEZ allows arbitrary frequency-domain constraints to be
%  specified for the design of a possibly complex FIR filter.  The 
%  Chebyshev (or minimax) filter error is optimized, producing
%  equiripple FIR filter designs.
%
%  B=CREMEZ(N,F,'fresp',W) returns a length N+1 FIR filter which
%  has the best approximation to the desired frequency response
%  as returned by function 'fresp'.  The function is called
%  from within CREMEZ using the syntax:
%
%                    [DH,DW] = fresp(N,F,GF,W);
%  where:
%  N is the filter order.
%  F is the vector of frequency band edges which must appear
%    monotonically between -1 and +1, where 1 is the Nyquist
%    frequency.  The frequency bands span F(k) to F(k+1) for k odd;
%    the intervals  F(k+1) to F(k+2) for k odd are "transition bands"
%    or "don't care" regions during optimization.
%  GF is a vector of grid points which have been linearly interpolated
%    over each specified frequency band by CREMEZ, and determines the
%    frequency grid at which the response function will be evaluated.
%  W is a vector of real, positive weights, one per band, for use
%    during optimization.  W is optional; if not specified, it is set
%    to unity weighting before being passed to 'fresp'.
%  DH and DW are the desired complex frequency response and
%    optimization weight vectors, respectively, evaluated at each
%    frequency in grid GF.
%
%  Predefined frequency response functions for 'fresp' include:
%       'lowpass'  'bandpass' 'multiband'      'hilbfilt'
%       'highpass' 'bandstop' 'differentiator' 'invsinc'
%  See the help for PRIVATE/LOWPASS, etc., for more information.
%
%  B=CREMEZ(N,F,{'fresp',P1,P2,...},W) specifies optional arguments
%  P1, P2, etc., to be passed to the response function 'fresp'.
%
%  B=CREMEZ(N,F,A,W) is a synonym for B=CREMEZ(N,F,{'multiband',A},W),
%  where A is a vector of response amplitudes at each band edge in F.
%
%  B=CREMEZ(..., SYM) imposes a symmetry constraint on the impulse
%  response of the design, where SYM may be one of the following:
%          'none' - Default if any negative band edge frequencies are
%                   passed, or if 'fresp' does not supply a default.
%          'even' - Impulse response will be real and even.  This is
%                   the default for highpass, lowpass, bandpass,
%                   bandstop, and multiband designs.
%           'odd' - Impulse response will be real and odd.  This is
%                   the default for Hilbert and differentiator designs.
%          'real' - Impose conjugate symmetry on frequency response.
%  Each frequency response function 'fresp' provides a default value
%  for SYM; see help on private/lowpass, etc., for more information.
%  If any SYM option other than 'none' is specified, the band edges
%  should only be specified over positive frequencies; the negative
%  frequency region will be filled in from symmetry.
%
%  B=CREMEZ(..., 'skip_stage2') disables the second-stage optimization
%  algorithm, which executes only when CREMEZ determines that an
%  optimal solution has not been reached by the standard Remez
%  error-exchange.  Disabling this algorithm may increase the speed of
%  computation, but with a reduction in accuracy.  By default, the
%  second-stage optimization is enabled.
%
%  B=CREMEZ(..., DEBUG) enables the display of intermediate results
%  during the filter design, where DEBUG may be one of 'trace',
%  'plots', 'both', or 'off'.  By default, DEBUG is set to'off'.
%
%  B=CREMEZ(...,{LGRID}), where {LGRID} is a one-by-one cell array containing
%  an integer, controls the density of the frequency grid. The frequency grid 
%  size is roughly 2^nextpow2(LGRID*N).  LGRID defaults to 25.
%
%  Note that any combination of the SYM, DEBUG, 'skip_stage2', and {LGRID}
%  options may be specified.
%
%  [B,ERR]=CREMEZ(...) returns the maximum ripple height ERR.
%
%  [B,ERR,RES]=CREMEZ(...) returns a structure RES of optional results
%  computed by CREMEZ, and contains the following fields:
%
%     RES.fgrid: vector containing the frequency grid used in
%                the filter design optimization
%       RES.des: desired response on fgrid
%        RES.wt: weights on fgrid
%         RES.H: actual frequency response on the grid
%     RES.error: error at each point on the frequency grid
%     RES.iextr: vector of indices into fgrid of extremal frequencies
%     RES.fextr: vector of extremal frequencies
%
%  See also REMEZ, FIR1, FIRLS, FILTER,  PRIVATE/LOWPASS,
%  PRIVATE/HIGHPASS,   PRIVATE/BANDPASS, PRIVATE/BANDSTOP,
%  PRIVATE/MULTIBAND,  PRIVATE/INVSINC,  PRIVATE/HILBFILT,
%  PRIVATE/DIFFERENTIATOR.

%   Authors: L. Karam, J. McClellan
%   Revised: October 1996, D. Orofino
%
%   Copyright (c) 1988-98 by The MathWorks, Inc.
%   $Revision: 1.1 $  $Date: 1998/06/03 14:42:20 $

% NOTE: This algorithm is equivalent to Remez for real B
%       when the filter specs are exactly linear phase.

% Declare globals:
global DES_CRMZ WT_CRMZ GRID_CRMZ TGRID_CRMZ IFGRD_CRMZ

if nargin<3, error('Not enough input arguments.'); end

L         = M+1;             % convert ORDER to LENGTH
edges     = edges/2;         % compensate for [-1,1) frequency normalization
num_bands = length(edges)/2; % # of frequency bands specified

% Some quick checks on band edge vector:
if (num_bands ~= floor(num_bands)),
  error('F must contain an even number of band edge entries.');
end
if any(diff(edges) <= 0),
   error('Band edges must be monotonically increasing.')
end

% Assign default parameter values:
wgts         = ones(1, num_bands);
h_sym        = 'unspecified';
plot_flag    = 'off';
allow_stage2 = 1;
grid_density = 25;

plot_opts = {'plots','trace','both','off'};
sym_opts  = {'even','odd','real','none'};
for ii=1:length(varargin),
  if iscell(varargin{ii})
     grid_density = varargin{ii}{1};
  elseif ~isstr(varargin{ii}),
     if ii==1,
       wgts = varargin{ii};
     else
       error('Expecting a string argument.');
     end
  else
    ppv = lower(varargin{ii});
    switch ppv
    case plot_opts
      plot_flag = ppv;      % plot option
    case sym_opts
      h_sym = ppv;          % symmetry option
    case 'skip_stage2'
      allow_stage2 = 0;     % skip 2nd stage optimization
    otherwise
      error(['Invalid argument "' ppv '" specified.']);
    end
  end
end

PLOTS = any(strcmp(plot_flag,{'plots','both'}));
TRACE = any(strcmp(plot_flag,{'trace','both'}));

% Force function name into a cell-array:
if ~iscell(filt_str)
   if isstr(filt_str)
      % Function name passed:
      filt_str = {filt_str};
   else
      % User passed a non-string, non-cell -- assume it to be a
      % magnitude vector.  Pass this to multiband:
      filt_str = {'multiband', filt_str};
   end
end
filt_call    = lower(filt_str{1});  % string name of function
other_params = filt_str(2:end);     % cell_array of additional args

% Determine symmetry options:
h_sym = lower(h_sym);
if strcmp(h_sym,'unspecified'),

   % If symmetry was not specified, AND there are negative freqencies
   % passed in the edge vector, use 'none':
   if any(edges < 0),
     h_sym = 'none';
   else
     % If symmetry was not specified, call the fresp function
     % with 'defaults' string and a cell-array of the actual
     % function call arguments to query the default value.
     h_sym = feval(filt_call, 'defaults', ...
                  {M, 2*edges, [], wgts, other_params{:}} );
     if ~any(strcmp(h_sym,sym_opts)),
       error(['Invalid default symmetry option "' h_sym '" returned ' ...
              'from response function "' filt_call '".  Must be one ' ...
              'of ''none'',''real'',''even'', or ''odd''']);
     end
  end
end

if TRACE,
  disp(['   Symmetry option: ' h_sym]);
end

if any(strcmp(h_sym,{'real','none'})),
  fdomain = 'whole';     % [-0.5,0.5)
  sym = 0;               % h_sym = 'real' or 'none'
else
  fdomain = 'half';      % [0,0.5]
  if strcmp(h_sym,'even')
    sym = 1;           % h_sym = 'even'
  else
    sym = 2;           % h_sym = 'odd'
  end
end

% Check domain before generating frequency grid:
if strcmp(fdomain, 'whole'),
  % Domain is [-1,+1) for user input, [-0.5,0.5) internally
  if any(edges < -0.5 | edges > 0.5),
    error(['Frequency band edges must be in the range [-1,+1] for ' ...
           'designs with SYM=''' h_sym '''.']);
  end
else
  % Domain is [0,+1] for user input, [0,.5] internally
  if any(edges < 0 | edges > 0.5),
    error(['Frequency band edges must be in the range [0,+1] for ' ...
           'designs with SYM=''' h_sym '''.']);
  end
end

% Generate frequency grid:
edge_pairs = reshape(edges, 2, num_bands)';
[tgrid, Lfft, vec_edges, indx_edges] = ...
               crmz_grid(edge_pairs, L, fdomain, grid_density);

%  Input: indx_edges = [a1 a2 a3 a4 ...]
% Output: IFGRD_CRMZ = [a1:a2 a3:a4 ...]
IFGRD_CRMZ = [];
for jj = 1:2:length(indx_edges),
  IFGRD_CRMZ = [IFGRD_CRMZ indx_edges(jj):indx_edges(jj+1)];
end

% Get just those points from the full grid (tgrid) which correspond
% to the frequency band intervals delimited by indx_edges (i.e., by
% edge_pairs, quantized to grid indices):
TGRID_CRMZ = tgrid(:);
GRID_CRMZ  = TGRID_CRMZ( IFGRD_CRMZ );
if (max(GRID_CRMZ) > edges(end)) | (min(GRID_CRMZ) < edges(1)),
  error('Internal error: Grid frequencies out of range.')
end

% Get desired frequency characteristics at the frequency points
% in the specified frequency band intervals:
%
% NOTE! The user needs to see normalized frequencies in the range
% [-1,+1], and not [-0.5,+0.5] as we use internally.
[DES_CRMZ, WT_CRMZ] = feval(filt_call, ...
                            M, 2*edges, 2*GRID_CRMZ, wgts, other_params{:}); 
% Cleanup the results, and check sizes:
DES_CRMZ = DES_CRMZ(:);
WT_CRMZ  = WT_CRMZ(:);
if ~isequal(size(DES_CRMZ), size(GRID_CRMZ)) | ...
   ~isequal(size(WT_CRMZ),  size(GRID_CRMZ)),
  str = ['Incorrect size of results from response function "' ...
         filt_call '".  Sizes must be the same size as the ' ...
         'frequency grid GF.'];
  error(str);
end

if strcmp(h_sym,'real') & all(edges >= 0)
   % We need to make DES and WT conjugate symmetric
   %error(['Frequency band edges must be specified over the entire ' ...
   %       'interval [-1,+1) for designs with SYM=''' h_sym '''.']);

   % crmz_grid moved the band edge grid points, so do the same
   % when constructing symmetric spectrum:
   len = length(TGRID_CRMZ);
   % If the DC term is included in the band edges, remove it:
   if any(indx_edges==len/2+1),
     indx_edges(1)=[]; % Throw away DC point
   end
   q = len+2-flipud(indx_edges(:));
   TGRID_CRMZ(flipud(q)) = -TGRID_CRMZ(indx_edges);
   % Adjust other grid vectors accordingly:
   indx_edges = [q; indx_edges(:)];
   IFGRD_CRMZ = [len+2-fliplr(IFGRD_CRMZ) IFGRD_CRMZ];
   GRID_CRMZ  = TGRID_CRMZ(IFGRD_CRMZ);
   % Now, impose conjugate symmetry:
   DES_CRMZ   = [conj(flipud(DES_CRMZ)); DES_CRMZ];
   WT_CRMZ    = [conj(flipud(WT_CRMZ));  WT_CRMZ];
end

% Complex Remez Stage:
[h,a,delta,not_optimal,iext,HH,EE,M_str,HH_str,h_str,Lf,Lb,Ls,Lc,A] = ...
             crmz( L, sym, Lfft, indx_edges, PLOTS, TRACE ); 
if PLOTS,
  plot_struct.L          = L;
  plot_struct.HH         = HH;
  plot_struct.EE         = EE;
  plot_struct.iext       = iext;
  plot_struct.indx_edges = indx_edges;
  plot_struct.sym        = sym;
  plot_struct.delta      = delta;
  plot_struct.plot       = 'plot-result';
  crmz( plot_struct );   % generate final plot
end

if not_optimal & allow_stage2,
  % Ascent-descent Stage:

  if TRACE,
     disp('           ***********************************************');
     disp('           *****    Invoking Second Ascent Stage      ****');
     disp('           ***********************************************');
  end

  [h,a,delta,HH,EE] = ...
       adesc( L, Lf, Lb, Ls, Lc, sym, indx_edges, iext, HH, EE, a,...
	      M_str, HH_str, h_str, A, delta, PLOTS, TRACE );
  if PLOTS,
    plot_struct.L          = L;
    plot_struct.HH         = HH;
    plot_struct.EE         = EE;
    plot_struct.iext       = iext;
    plot_struct.indx_edges = indx_edges;
    plot_struct.sym        = sym;
    plot_struct.delta      = delta;
    plot_struct.plot       = 'plot-result';
    crmz( plot_struct );   % generate final plot
  end
end

% Return a row-vector, and remove imag part if it's small:
h = h(:).';
if ~isreal(h) & (norm(imag(h)) < 1E-12*norm(real(h))),
  h = real(h);
end

if nargout>2,
  result.fgrid = 2*GRID_CRMZ;
  result.des   = DES_CRMZ;
  result.wt    = WT_CRMZ;
  result.H     = HH(IFGRD_CRMZ);
  result.error = EE;
  result.iextr = iext;
  result.fextr = 2*GRID_CRMZ(iext);
end

clear global DES_CRMZ WT_CRMZ GRID_CRMZ TGRID_CRMZ IFGRD_CRMZ

%========================================================

function  [grid,Ngrid,edge_vec,edge_idx] = crmz_grid(edge_pairs, L, fdomain, ...
                                                grid_density)
% CRMZ_GRID Generate grid for Remez exchange.
%
%   [Grid, Ngrid, V_edges, Indx_edges] = ...
%                    CRMZ_GRID(Edge_Pairs, L, fdomain, grid_density)
%           Grid: frequencies on the fine grid
%          Ngrid: number of grid pts if whole domain used [-1,+1]
%       edge_vec: specified edges reshaped into a vector of adjacent edge-pairs
%       edge_idx: index of specified band-edges within grid array
%
%     edge_pairs: specified band-edges, [Nbands X 2] array
%              L: filter length
%        fdomain: domain for frequency approximation
%                 default is [0,0.5] called 'half'
%                 'whole' means [-0.5,0.5), 
%   grid_density: density of grid

error(nargchk(3,4,nargin));

if (edge_pairs(1) == -0.5) & (edge_pairs(end) == 0.5),
   % -pi and +pi are the same point - move one a little:
   new_freq = 0.5 - 1/(50*L);
   if new_freq <= edge_pairs(end-1),
     % Last two freq points are too close to move - try first two:
     new_freq = -new_freq;
     if (new_freq >= edge_pairs(2)),
       error(['Both -1 and 1 have been specified as frequencies ' ...
              'in F, and the frequency spacing is too close to ' ...
              'move either of them toward its neighbor.']);
     else
       edge_pairs(1) = new_freq;
     end
   else
     edge_pairs(end) = new_freq;
   end
end

Ngrid = 2.^nextpow2(L*grid_density);
if (Ngrid/2) > 20*L,
   Ngrid = Ngrid/2;
end

del_f    = 1./Ngrid;
edge_vec = edge_pairs';  % M-by-2 to 2-by-M
edge_vec = edge_vec(:);  % single column of adjacent edge-pairs

switch fdomain
case 'whole'
  grid = (0:Ngrid-1)./Ngrid - 0.5;             % uniform grid points [-.5,.5)
  edge_idx = 1 + round((edge_vec+0.5)*Ngrid);  % closest indices in grid
case 'half'
  grid = (0:Ngrid/2)./Ngrid;                   % uniform grid points [0,.5]
  edge_idx = 1 + round(edge_vec*Ngrid);        % closest indices in grid
otherwise
  error('Internal error: domain must be "whole" or "half".');
end
edge_idx(end) = min(length(grid), edge_idx(end));  % Clip last index

% Fix repeated edges:
% This determines not only if
%         i(1)==i(2) and i(3)==i(4),
% but also if
%         i(2)==i(3), etc.
m = find(edge_idx(1:end-1) == edge_idx(2:end));
if ~isempty(m),
  % Replace REPEATED band edges with the uniform grid points
  % Could be a problem if [-1 -1] (if whole) or [0 0] (if half) specified
  edge_idx(m) = edge_idx(m) - 1;   % move 1 index lower
  edge_vec(m) = grid(edge_idx(m)); % change user's band edge accordingly
  m = m + 1;
  edge_idx(m) = edge_idx(m) + 1;
  edge_vec(m) = grid(edge_idx(m));
end

% Replace closest grid points with exact band edges:
grid(edge_idx) = edge_vec;

% end of crmz_grid