April 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Adaptive Identification and Inverse Filtering using Java, Page 3

  • February 7, 2006
  • By Richard G. Baldwin, Richard G. Baldwin
  • Send Email »
  • More Articles »

Complete Program Listings

Complete listings of the classes discussed in this lesson are shown in the listings below.

/*File Adapt07.java.java
Copyright 2005, R.G.Baldwin

The purpose of this program is to illustrate adaptive 
identification and inverse identification.

Further information on these two topics can be found at the
following three web sites:

www.eee.strath.ac.uk/r.w.stewart/adaptivejava/begin.htm

www.mathworks.com/access/helpdesk/help/toolbox/
filterdesign/adaptiv5.html#5547

www.mathworks.com/access/helpdesk/help/toolbox/
filterdesign/adaptiv6.html#5557

This program requires the following classes:
Adapt07.class
AdaptEngine02.class (new to this lesson)
AdaptiveResult.class
ForwardRealToComplex01.class
PlotALot01.class
PlotALot03.class
PlotALot05.class

This program uses the adaptive engine named AdaptEngine02 
to adaptively develop a filter.  Depending on user input, 
the filter is either an identification filter or an inverse
filter.

The class named AdaptEngine02 is an upgrade to the class 
named AdaptEngine01 that was used in earlier programs.  
This upgrade makes it possible for the user to pass a 
boolean parameter to the adapt method of the engine to 
either enable or disable the adaptive update of the filter 
coefficients.

When the filter is an identification filter, the adaptive 
process attempts to replicate the impulse response of a 
path through which wideband test data are flowing.

When the filter is an inverse filter, the adaptive process 
attempts to develop a filter that is the inverse of the 
impulse response of a path through which wideband test
data are flowing.

User-selectable test cases are provided for six different 
path scenarios. The user may develop an identification 
filter or an inverse filter for any of the six cases.

If the user opts for an identification filter, five 
separate graphs are produced. If the user opts for an
inverse filter, six separate graphs are produced.  The 
graphs appear on the screen in two rows with three graphs 
in each row.

The following is a brief description of each of the six 
graphs, working from left to right across the top row and 
then working from left to right across the second row.

1.  The impulse response of the path through which the wide
band data are flowing.
2.  The amplitude and phase response of the path through 
which the wideband data are flowing.
3.  Four time series that illustrate the time behavior of 
the adaptive process.  The adaptive behavior is disabled
and impulses are appended onto the ends of the input time 
series to cause the impulse response of various filters to 
be displayed at the ends of the time series in this graph.
4.  The amplitude and phase response of the adaptive filter
at the end of every 400th iteration.
5.  The impulse response of the adaptive filter at the end 
of every 400th adaptive iteration.
6.  The amplitude and phase spectrum of the time series 
produced by convolving the final adaptive filter with the
impulse response of the path.  This graph is produced only 
when the filter being developed is an inverse filter.  This
amplitude and phase spectrum illustrates the extent to 
which the inverse filter is able to compensate for the path
characteristics.  Ideally the amplitude spectrum is flat 
and the phase spectrum is either flat or linear 
(indicating a time delay).

Graphs 3, 4, and 5 consist of multiple pages stacked on top
of one another.  Move the pages on the top of the stack to 
view the pages further down.  The pages on the top of the 
stack represent the results produced early in the adaptive 
process while those further down represent the results 
produced later in the adaptive process.

The four time series that are plotted are, from top to
bottom in the colors indicated:
1.  Input to the adaptive filter (black).
2.  Target for the adaptive process (red).
3.  Output from the adaptive filter (blue).
4.  Error computed within the adaptive process (green).

Near the end of the run, the adaptive update process is 
disabled.  The input data is set to zero for the remainder 
of the run except that on two subsequent occasions, an 
impulse is inserted into the data.  By running the same 
path twice, once in identification mode and once in inverse
filter mode, this makes it possible to see:

1. The impulse response of the path.
2. The impulse response of the final adaptive 
identification filter.
3. The extent to which the impulse response of the 
identification filter matches the impulse response of the 
path.
4. The impulse response of the final inverse filter.
5  The extent to which the convolution of the inverse 
filter with the impulse response of the path compensates 
for the characteristics of the path and produces the ideal 
output consisting of a single impulse.

In operation, the program generates wideband test data 
produced by a random number generator and convolves it with
a specified path impulse response to simulate the impact of
the path on the wideband test data.  The original 
wideband test data and the path output data are both 
presented to the adaptive engine.

When the original wideband test data is presented as the 
data to be filtered within the adaptive engine and the 
path output is presented as the target, the adaptive 
process attempts to develop an adaptive filter that 
replicates the impulse response of the path.

When the two are reversed, the adaptive process attempts to
develop an adaptive filter that is the inverse of the 
impulse response of the path.

User input is provided by five command-line parameters.  If
no command-line parameters are provided, default parameters
are used.

The command-line parameters are:

feedbackGain: The gain factor that is used in the feedback 
loop to adjust the coefficient values in the adaptive 
filter.

numberIterations: This is the number of iterations that the
program executes before stopping and displaying all of the 
graphic results.

filterLength: This is the number of coefficients in the 
adaptive filter.  Must be at least 26.

testCase:  An integer from 1 to 9 that specifies the 
simulated path scenario.

identification:  Input is T or F.  T specifies that the 
adaptive process is to develop an identification filter.  
F specifies that the adaptive process is to develop an 
inverse filter.

The minimum filter length of 26 has to do with plotting
alignment issues and has nothing to do with the adaptive
process.  See a description of the alignment issues in
earlier lessons.

The six path scenarios can be generally described as 
follows:

1.  A low-pass filter of the sort that might be realized 
with a passive RC network.
2,  Another low-pass filter that might be realized with a 
passive RC network but with a much longer time constant 
than scenario 1.
3.  A high-pass filter of the sort that might be realized 
with a passive RLC network having the same long time 
constant as scenario 2.
4,  A mid-band filter of the sort that might be realized 
with a passive RLC network having the same long time 
constant as scenarios 2 and 3.
5.  A filter that might represent an acoustic signal in 
the presence of echoes.
6.  A boxcar digital filter with a peak at half the 
Nyquist folding frequency.

Tested using J2SE 5.0 and WinXP.  J2SE 5.0 or later is 
required.
**********************************************************/
import static java.lang.Math.*;//J2SE 5.0 req

class Adapt07{
  public static void main(String[] args){
    //Default parameter values
    double feedbackGain = 0.0001;
    int numberIterations = 2001;
    int filterLength = 26;//Must be >= 26 for plotting.
    //Six test cases, numbered 1 through 6 are defined
    // later.
    int testCase = 1;
    //A value of true for the following variable causes the
    // adaptive process to attempt to develop an
    // identification filter. A value of false causes the
    // adaptive process to attempt to develop an inverse
    // filter for the path impulse response.
    boolean identification = true;
    
    //The following scale factor is applied to the
    // wideband test data.  This is not an input
    // parameter.
    double wbTestDataScale = 10;
    
    //Process command-line arguments.  Note that because of
    // plotting alignment issues discussed in earlier
    // lessons, the filter length must be at least 26.
    if(args.length != 5){
      System.out.println(
               "Usage with all parameters following the " +
               "program name:n" +
               "java Adapt07n" +
               "feedbackGainn" + 
               "numberIterationsn" + 
               "filterLength >= 26n" +
               "testCase, 1 to 9n" +
               "identification, T or Fn");
               
      System.out.println(
          "Input values were not provided.n"+
          "Using following default values:n" +
          "feedbackGain: " + feedbackGain +
          "nnumberIterations: " + numberIterations +
          "nfilterLength: " + filterLength +
          "ntestCase: " + testCase +
          "nidentification: " + identification);
    }else{//Command line params were provided.
      feedbackGain = Double.parseDouble(args[0]);
      numberIterations = Integer.parseInt(args[1]);
      filterLength = Integer.parseInt(args[2]);
      //FilterLength must be 26 or greater to avoid
      // plotting alignment problems.
      if(filterLength < 26){
        System.out.println(
                   "nfilterLength must be 26 or greater");
        System.out.println("Termnating program");
        System.exit(0);
      }//end if
      testCase = Integer.parseInt(args[3]);
      if(args[4].toUpperCase().equals("T")){
        identification = true;
      }else{
        identification = false;
      }//end else
    
      System.out.println(
          "Input values were provided.n"+
          "Using following values:n" +
          "feedbackGain: " + feedbackGain +
          "nnumberIterations: " + numberIterations +
          "nfilterLength: " + filterLength +
          "ntestCase: " + testCase +
          "nidentification: " + identification);
    }//end else

    //Instantiate a new object of the Adapt07 class
    // and invoke the method named process on that object.
    new Adapt07().process(feedbackGain,
                          numberIterations,
                          filterLength,
                          wbTestDataScale,
                          testCase,
                          identification);
  }//end main
  //-----------------------------------------------------//
  
  //This is the primary adaptive processing and plotting
  // method for the program.
  void process(double feedbackGain,
               int numberIterations,
               int filterLength,
               double wbTestDataScale,
               int testCase,
               boolean identification){

    //The following array will be populated with the
    // adaptive filter for display purposes.
    double[] filter = null;
    
    //Define several test cases for the path impulse
    // response.

    //Low-pass filter with short time constant.
    double[] pathA = {1.0,
                      0.5,
                      0.25,
                      0.125,
                      0.0625,
                      0.03125,
                      0.015625,
                      0.0};

    //Low-pass filter with long time constant.
    double[] pathB = {1.0,
                      0.8,
                      0.64,
                      0.512,
                      0.4096,
                      0.32768,
                      0.262144,
                      0.2097152,
                      0.1677721,
                      0.1342176,
                      0.1073740,
                      0.0858992,
                      0.0687193,
                      0.0549754,
                      0.0439803,
                      0.0351842,
                      0.0};

    //High-pass filter with long time constant.
    double[] pathC = {1.0,
                     -0.8,
                      0.64,
                     -0.512,
                      0.4096,
                     -0.32768,
                      0.262144,
                     -0.2097152,
                      0.1677721,
                     -0.1342176,
                      0.1073740,
                     -0.0858992,
                      0.0687193,
                     -0.0549754,
                      0.0439803,
                     -0.0351842,
                      0.0};
                        
    //Mid-pass filter with long time constant.
    double[] pathD = {1.0,
                      0.0,
                     -0.64,
                      0.0,
                      0.4096,
                      0.0,
                     -0.262144,
                      0.0,
                      0.1677721,
                      0.0,
                     -0.1073740,
                      0.0,
                      0.0687193,
                      0.0,
                     -0.0439803,
                      0.0};
                      
    //Simulation of an acoustic signal with echoes.
    double[] pathE = {1.0,
                      0.0,
                      0.64,
                      0.0,
                      0.0,
                      0.32768,
                      0.0,
                      0.0,
                      0.0,
                      0.1342176,
                      0.0,
                      0.0,
                      0.0,
                      0.0,
                      0.0439803,
                      0.0};
    
    //Digital boxcar filter with peak at half the folding
    // frequency.
    double[] pathF = {1.0,
                      0.0,
                     -1.0,
                      0.0,
                      1.0,
                      0.0,
                     -1.0};

    //A reference to the selected path operator will be
    // stored here.
    double[] pathOperator = null;
    
    if(testCase == 1){
      pathOperator = pathA;
    }else if(testCase == 2){
      pathOperator = pathB;
    }else if(testCase == 3){
      pathOperator = pathC;
    }else if(testCase == 4){
      pathOperator = pathD;
    }else if(testCase == 5){
      pathOperator = pathE;
    }else if(testCase == 6){
      pathOperator = pathF;
    }else{
      System.out.println("Invalid testCase");
      System.out.println("Terminating program");
      System.exit(0);
    }//end else
    
    //Display the pathOperator
    //First instantiate a plotting object.
    PlotALot01 pathOperatorObj = new PlotALot01("Path",
               (pathOperator.length * 4) + 8,148,70,4,0,0);

    //Feed the data to the plotting object.
    for(int cnt = 0;cnt < pathOperator.length;cnt++){
      pathOperatorObj.feedData(40*pathOperator[cnt]);
    }//end for loop
    
    //Cause the graph to be displayed on the computer
    // screen in the upper left corner.
    pathOperatorObj.plotData(0,0);
    
    //Now compute and plot the frequency response of the
    // selected path
    
    //Instantiate a plotting object for two channels of
    // frequency response data.  One channel is for
    // the amplitude and the other channel is the phase.
    PlotALot03 pathFreqPlotObj = 
                   new PlotALot03("Path",264,148,35,2,0,0);
                   
    //Compute the frequency response and feed the results
    // to the plotting object.
    displayFreqResponse(pathOperator,pathFreqPlotObj,
                                                    128,0);
                       
    //Cause the frequency response data stored in the
    // plotting object to be displayed on the screen in
    // the top row of images.
    pathFreqPlotObj.plotData(112,0);
    
    //Instantiate an object to handle the adaptive behavior
    // of the program.  The use of the class named 
    // AdaptEngine02 is new to this lesson.
    AdaptEngine02 adapter = new AdaptEngine02(
                                filterLength,feedbackGain);

    //Instantiate an array object that will be used as a
    // delay line for the wideband test data.
    double[] rawData = new double[pathOperator.length];
    
    //Instantiate a plotting object for four channels of
    // time-serie data.
    PlotALot05 timePlotObj = 
                   new PlotALot05("Time",468,148,25,2,0,0);

    //Instantiate a plotting object for two channels of
    // filter frequency response data.  One channel is for
    // the amplitude and the other channel is for the
    // phase.
    PlotALot03 freqPlotObj = 
                   new PlotALot03("Freq",264,487,35,2,0,0);

    //Instantiate a plotting object to display the filter
    // impulse response at specific time intervals during
    // the adaptive process.
    PlotALot01 filterPlotObj = new PlotALot01("Filter",
                     (filterLength * 4) + 8,487,70,4,0,0);

    //Declare and initialize working variables.
    double output = 0;
    double err = 0;
    double target = 0;
    double input = 0;
    double wbTestData = 0;
    boolean adaptOn;

    //Perform the specified number of iterations.
    for(int cnt = 0;cnt < numberIterations;cnt++){
      //The following variable is used to control whether
      // or not the adapt method of the adaptive engine
      // updates the filter coefficients when it is called.
      // The filters are updated when this variable is
      // true and are not updated when this variable is
      // false.
      adaptOn = true;
      
      //Get and scale the next sample of wideband test
      // data from a random number generator.  Before
      // scaling by wbTestDataScale, the values are
      // uniformly distributed from -1.0 to 1.0.
      wbTestData = 
                 wbTestDataScale*(2*(Math.random() - 0.5));
      
      //Set wideband input values near the end of the run
      // to zero and turn adaptation off.  Insert an
      // impulse near the end that will produce several
      // interesting impulse responses.  Among other
      // things, this will cause the impulse response of
      // the path to be visible in the graphs.
      //Set values to zero.
      if(cnt > (numberIterations - 5*filterLength)){
        wbTestData = 0;
        adaptOn = false;
      }//end if
      //Now insert an impulse at one specific location in
      // the time series.
      if(cnt == numberIterations - 3*filterLength){
        wbTestData = 2 * wbTestDataScale;
      }//end if

      //Insert the wideband test data into the raw data
      // delay line.
      flowLine(rawData,wbTestData);
    
      //Apply the path operator to the wideband test data.
      // Note the use of the method named
      // reverseDotProduct, which is new to this lesson.
      // This method provides the time reversal that is
      // necessary for true convolution.
      double pathData = 
                   reverseDotProduct(rawData,pathOperator);

      //Declare a variable that will be populated with the
      // results returned by the adapt method of the
      // adaptive engine.
      AdaptiveResult result = null;
      
      //Decide which type of filter to develop and do the
      // adaptive work.
      if(identification){//Develop an identification filter
        //Establish input values for the adaptive engine
        // that are appropriate for the development of
        // an identification filter.
        input = wbTestData;
        target = pathData;
        //Do the adaptive processing.
        result = adapter.adapt(input,target,adaptOn);
      }else{//Develop an inverse filter.
        //Establish input values for the adaptive engine
        // that are appropriate for the development of
        // an inverse filter.  Note that the input values
        // are reversed relative to an identification
        // filter.
        input = pathData;
        target = wbTestData;

        //When the run is nearly over and the wideband
        // input data values are zero, insert an impulse
        // into the pathData.  This happens later than the
        // point in time where the impulse was inserted
        // into the wideband test data. Note that
        // adaptation is still turned off at this point in
        // time.  Among other things, this makes it
        // possible to view the impulse response of the
        // inverse filter in the time series that are
        // displayed.
        if(cnt == numberIterations - filterLength){
          input = 2 * wbTestDataScale;
        }//end if

        //Do the adaptive processing
        result = adapter.adapt(input,target,adaptOn);
      }//end else
    
      //Get and save adaptive results for plotting and
      // spectral analysis
      output = result.output;
      err = result.err;
      filter = result.filterArray;
    
      //Feed the time series data to the plotting object.
      timePlotObj.feedData(input,target,output,-err);
    
      //Compute and plot summary results at the end of
      // every 400th iteration.
      if(cnt%400 == 0){
        displayFreqResponse(filter,freqPlotObj,
                                    128,filter.length - 1);

        //Display the filter impulse response coefficient
        // values.  The adaptive engine returns the filter
        // with the time axis reversed relative to the
        // conventional display of an impulse response.
        // Therefore, it is necessary to display it in
        // reverse order.
        for(int ctr = 0;ctr < filter.length;ctr++){
          filterPlotObj.feedData(
                       40*filter[filter.length - 1 - ctr]);
        }//end for loop
      }//End display of frequency response and filter
    }//End for loop,
    
    //Cause the data stored in the plotting objects to be
    // plotted.
    timePlotObj.plotData(376,0);//Top of screen
    freqPlotObj.plotData(0,148);//Left side of screen
    filterPlotObj.plotData(265,148);
    
    //For the case of an inverse filter, convolve the
    // filter with the impulse response of the path to 
    // determine the extent to which the filter is able to
    // compensate for the characteristics of the path.
    if(!identification){
      //Copy the filter into another array with zeros on
      // both ends to account for end effects in the
      // convolution process.
      double[] convInput = new double[
                    filter.length + 2*pathOperator.length];
      for(int cnt = 0;cnt < filter.length;cnt++){
        convInput[cnt + pathOperator.length] = filter[cnt];
      }//end for loop
      
      //Create an array to receive the convolution output.
      double[] convOutput = 
           new double[filter.length + pathOperator.length];

      //Call the method named convolve01 to perform the
      // convolution.
      convolve01(convInput,pathOperator,convOutput);

      //Now compute and plot the frequency spectrum of the
      // time series that results from convolving the final
      // adaptive filter with the path impulse response.
      // Ideally, the amplitude response will be flat from
      // zero to the folding frequency and the phase
      // response will be flat, or possibly linear
      // (indicating a time delay).
      
      //Instantiate a plotting object for two channels of
      // frequency response data.  One channel is for
      // the amplitude and the other channel is the phase.
      PlotALot03 finalFreqPlotObj = 
                  new PlotALot03("Final",264,137,35,2,0,0);
                     
      //Compute the frequency response and feed the results
      // to the plotting object.
      displayFreqResponse(convOutput,finalFreqPlotObj,
                                128,convOutput.length - 1);
                         
      //Cause the frequency response data stored in the
      // plotting object to be displayed on the screen.
      finalFreqPlotObj.plotData(
                           265 + filterLength * 4 + 8,148);
    }//end if

  }//end process method
  //-----------------------------------------------------//
  
  //This method simulates a tapped delay line. It receives
  // a reference to an array and a value.  It discards the
  // value at index 0 of the array, moves all the other
  // values by one element toward 0, and inserts the new
  // value at the top of the array.
  void flowLine(double[] line,double val){
    for(int cnt = 0;cnt < (line.length - 1);cnt++){
      line[cnt] = line[cnt+1];
    }//end for loop
    line[line.length - 1] = val;
  }//end flowLine
  //-----------------------------------------------------//
 
  void displayFreqResponse(
     double[] filter,PlotALot03 plot,int len,int zeroTime){

    //Create the arrays required by the Fourier Transform.
    double[] timeDataIn = new double[len];
    double[] realSpect = new double[len];
    double[] imagSpect = new double[len];
    double[] angle = new double[len];
    double[] magnitude = new double[len];
    
    //Copy the filter into the timeDataIn array
    System.arraycopy(filter,0,timeDataIn,0,filter.length);

    //Compute DFT of the filter from zero to the folding
    // frequency and save it in the output arrays.
    ForwardRealToComplex01.transform(timeDataIn,
                                     realSpect,
                                     imagSpect,
                                     angle,
                                     magnitude,
                                     zeroTime,
                                     0.0,
                                     0.5);

    //Note that the conversion to decibels has been
    // disabled.  You can re-enable the conversion by
    // removing the comment indicators.
/*    
    //Display the magnitude data. Convert to normalized
    // decibels first.
    //Eliminate or change any values that are incompatible
    // with log10 method.
    for(int cnt = 0;cnt < magnitude.length;cnt++){
      if((magnitude[cnt] == Double.NaN) || 
                                    (magnitude[cnt] <= 0)){
        //Replace the magnitude by a very small positive
        // value.
        magnitude[cnt] = 0.0000001;
      }else if(magnitude[cnt] == Double.POSITIVE_INFINITY){
        //Replace the magnitude by a very large positive
        // value.
        magnitude[cnt] = 9999999999.0;
      }//end else if
    }//end for loop
    
    //Now convert magnitude data to log base 10
    for(int cnt = 0;cnt < magnitude.length;cnt++){
      magnitude[cnt] = log10(magnitude[cnt]);
    }//end for loop
*/
    //Find the absolute peak value.  Begin with a negative
    // peak value with a large magnitude and replace it
    // with the largest magnitude value.
    double peak = -9999999999.0;
    for(int cnt = 0;cnt < magnitude.length;cnt++){
      if(peak < abs(magnitude[cnt])){
        peak = abs(magnitude[cnt]);
      }//end if
    }//end for loop

    //Normalize to 20 times the peak value
    for(int cnt = 0;cnt < magnitude.length;cnt++){
      magnitude[cnt] = 20*magnitude[cnt]/peak;
    }//end for loop

    //Now feed the normalized data to the plotting
    // object.
    for(int cnt = 0;cnt < magnitude.length;cnt++){
      plot.feedData(magnitude[cnt],angle[cnt]/20);
    }//end for loop
    
  }//end displayFreqResponse
  //-----------------------------------------------------//
  
  //This method receives two arrays and treats each array
  // as a vector. The two arrays must have the same length.
  // The program reverses the order of one of the vectors
  // and returns the vector dot product of the two vectors.
  double reverseDotProduct(double[] v1,double[] v2){
    if(v1.length != v2.length){
      System.out.println("reverseDotProduct");
      System.out.println("Vectors must be same length.");
      System.out.println("Terminating program");
      System.exit(0);
    }//end if
    
    double result = 0;
    
    for(int cnt = 0;cnt < v1.length;cnt++){
      result += v1[cnt] * v2[v1.length - cnt - 1];
    }//end for loop

    return result;
  }//end reverseDotProduct
  //-----------------------------------------------------//
  
  //This method applies an incoming convolution operator to
  // an incoming set of data and deposits the filtered data
  // in an output array whose reference is received as an
  // incoming parameter.
  public void convolve01(double[] data,//input data array
                         double[] operator,//operator array
                         double[] output//output data array
                         ){
    //Apply the operator to the data, dealing with the
    // index reversal required by convolution.
    int dataLen = data.length;
    int operatorLen = operator.length;
    for(int i=0;i < dataLen-operatorLen;i++){
      output[i] = 0;
      for(int j=operatorLen-1;j>=0;j--){
        output[i] += data[i+j]*operator[j];
      }//end inner loop
    }//end outer loop
  }//end convolve01 method
  //-----------------------------------------------------//
}//end class Adapt07

Listing 17

 

/*File AdaptEngine02.java
Copyright 2005, R.G.Baldwin

General purpose LMS adaptive algorithm.

This is an upgrade of the class named AdaptEngine01.  This
version allows the user to pass a boolean parameter to
the adapt method to turn the adaptive process on or off.
Otherwise, the behavior of the class is the same as the
behavior of the class named AdaptEngine01.

An object of this class is a general purpose adaptive 
engine that implements the classical LMS adaptive 
algorithm.

The adaptive algorithm is implemented by the instance 
method belonging to the object named adapt.

Each time the adapt method is called, it receives one 
sample from each of two different time series.  One time 
series is considered to be the data that is to be filtered.
The other time series is considered to be a target.

The purpose of the adapt method is to adaptively create a 
convolution filter which, when applied to the data time 
series, will transform it into the target time series.

Each time the method is called, it performs a dot product 
between the current version of the filter and the contents 
of a delay line in which historical data samples have been 
saved. The result of that dot product is compared with the 
target sample to produce an error value. The error value is
produced by subtracting the value of the target sample from
the result of the dot product. The error value is then used
in a classical LMS adaptive algorithm to adjust the filter 
coefficients.

The objective is to produce a set of filter coefficients 
that will drive the error to zero over time.

This adaptive engine can be used as the solution to a 
variety of different signal processing problems, depending 
on the selection of time series that are provided as data 
and target.

The constructor for the class receives two parameters:
filterLength
feedbackGain

The filter length is used to construct two arrays.  One 
array is used later to contain the filter coefficients.

The other array is used later as a tapped delay line to 
contain the historical data samples and to precess them by
one element each time the method is called.

The feedback gain is used in the LMS adaptive algorithm to
compute the new filter coefficients.

Tested using J2SE 5.0 and WinXP.
**********************************************************/

class AdaptEngine02{
  
  double[] filterArray;//filter coefficients stored here
  double[] dataArray;//historical data is stored here
  double feedbackGain;//used in LMS adaptive algorithm
  
  //Constructor
  public AdaptEngine02(int filterLength,
                       double feedbackGain){
    //Construct the two arrays and save the feedback gain.
    filterArray = new double[filterLength];
    dataArray = new double[filterLength];
    this.feedbackGain = feedbackGain;
  }//end constructor
  //-----------------------------------------------------//
  
  //This method implements a classical LMS adaptive
  // algorithm to create and to apply a convolution filter.
  // The filter output, the error, and a reference to the
  // array containing the filter coefficients are 
  // encapsulated in an object of type AdaptiveResult and
  // returned to the calling method.  The adaptive update
  // process is disabled when the parameter named adaptOn
  // is false.
  AdaptiveResult adapt(double rawData,
                       double target,
                       boolean adaptOn){
                        
    //Insert the incoming data value into the data delay
    // line.
    flowLine(dataArray,rawData);
  
    //Apply the current filter coefficients to the data.
    double output = dotProduct(filterArray,dataArray);
    //Compute the error.
    double err = output - target;

    //Only update the coefficients when adaptOn is true.
    if(adaptOn){
      //Use the error to update the filter coefficients.
      for(int ctr = 0;ctr < filterArray.length;ctr++){
        filterArray[ctr] -= 
                           err*dataArray[ctr]*feedbackGain;
      }//end for loop.
    }//end if

    //Construct and return an object containing the filter
    // output, the error, and a reference to the array
    // object containing the current filter coefficients.
    return new AdaptiveResult(filterArray,output,err);
  }//end adapt
  //-----------------------------------------------------//
  
  //This method simulates a tapped delay line. It receives
  // a reference to an array and a value.  It discards the
  // value at index 0 of the array, moves all the other
  // values by one element toward 0, and inserts the new
  // value at the top of the array.
  void flowLine(double[] line,double val){
    for(int cnt = 0;cnt < (line.length - 1);cnt++){
      line[cnt] = line[cnt+1];
    }//end for loop
    line[line.length - 1] = val;
  }//end flowLine
  //-----------------------------------------------------//
  
  //This method receives two arrays and treats the first N 
  // elements in each of the two arrays as a pair of
  // vectors.  It computes and returns the vector dot
  // product of the two vectors.  If the length of one
  // array is greater than the length of the other array,
  // it considers the number of dimensions of the vectors
  // to be equal to the length of the smaller array.
  double dotProduct(double[] v1,double[] v2){
    double result = 0;
    if((v1.length) <= (v2.length)){
      for(int cnt = 0;cnt < v1.length;cnt++){
        result += v1[cnt]*v2[cnt];
      }//emd for loop
      return result;
    }else{
      for(int cnt = 0;cnt < v2.length;cnt++){
        result += v1[cnt]*v2[cnt];
      }//emd for loop
      return result;
    }//end else
  }//end dotProduct
  //-----------------------------------------------------//
}//end class AdaptEngine02
//=======================================================//

//This class is used to encapsulate the adaptive results
// into an object for return to the calling method.
class AdaptiveResult{
  public double[] filterArray;
  public double output;
  public double err;
  
  //Constructor
  public AdaptiveResult(double[] filterArray,
                        double output,
                        double err){
    this.filterArray = filterArray;
    this.output = output;
    this.err = err;
  }//end constructor
}//end class AdaptiveResult
//=======================================================//

Listing 18
 


Copyright 2006, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com



Page 3 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel