//-------------------------------------------------------------------//

// Synopsis:	Adaptive least-mean square (LMS) line enhancer.

// Syntax:	</ w, y, e /> = lmsale (X, M, STEP, DELAY, WSAVE)

// Description:

//      Lmsale implements an adaptive line enhancer using the
//      least-mean squares approach where X is the input signal, M is
//      the number of filter weights, STEP is the percentage of the
//      maximum step size to use in computing the next set of filter
//      weights and DELAY is the number samples to delay X in
//      computing the new weights.  If WSAVE is not equal to 0, then
//      the filter weights are accumulated, and returned in w. The
//      final filter weights are returned in W, the estimated signal
//      in Y and the error signal in E.

//      The maximum step size is computed as

//		maxstep = 2/(M * std(x)^2);

//      LT Dennis W. Brown 3-10-93
//      Naval Postgraduate School, Monterey, CA
//      May be freely distributed.
//      Not for use in commercial products.

//      Small modifications by Ian Searle, 5/95

// Dependencies

require flipud
require std

//-------------------------------------------------------------------//

lmsale = function ( x, M, mup, delay, wsave )
{
  local ( x, M, mup, delay )

  if (!exist (wsave)) { wsave = 0; }

  // default returns
  y = []; w = []; e = [];

  // figure out if we have a vector
  if (min (size (x)) != 1) {
    error("lmsale: Input arg \"x\" must be a 1xN or Nx1 vector.");
  }

  // work with Nx1 vectors
  x = x[:];

  // initial weights
  w0 = zeros (M, 1);

  // some parameters

  Ns = length (x);
  runs = std (x)^2;

  // compute maximum step size
  mumax = 2/(M * runs);

  // make mu less than 2/lamdamax using percentage
  mu = mup/100 * mumax;

  // start with initial guess for weights equal to the null vector
  w = w0;

  if (wsave) {
    wsv = zeros (Ns, M);
    wsv[1;] = w0';
  }

  // recursively compute the weights to three significant figures
  y = zeros (Ns,1);		// space for filtered signal
  e = zeros (Ns,1);		// space for error signal
  xi = [zeros (M-1,1) ; x[1:M+delay;1]];

  // initial conditions set to zero

  for (k in delay+1:M+delay-1)
  {
    b = flipud (xi[(k-M+1:k)-delay+M-1]);

    // compute filter output
    y[k] = w' * b;
    
    // compute error
    e[k] = x[k] - y[k];
    
    // compute new weights
    w = w + mu * b * e[k];

    if (wsave) {
      wsv[k;] = w';
    }
  }

  // rest of data
  for (k in M+delay:Ns)
  {
    b = flipud (x[(k-M+1:k)-delay]);

    // compute filter output
    y[k] = w' * b;
    
    // compute error
    e[k] = x[k] - y[k];
    
    // compute new weights
    w = w + mu * b * e[k];

    if (wsave) {
      wsv[k;] = w';
    }
  }

  if (wsave) 
  {
    return << w=wsv; y=y; e=e >>;
  else
    return << w=w; y=y; e=e >>;
  }
};
