August 29, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

An Adaptive Whitening Filter in Java

  • November 1, 2005
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Complete Program Listings

Complete listings of the programs discussed in this lesson are shown in Listing 21 and Listing 22 below.
 
/*File Adapt02.java.java
Copyright 2005, R.G.Baldwin
This program illustrates one aspect of time-adaptive signal
processing.  
This program implements a time-adaptive whitening filter 
using a predictive approach. The program input is a time 
series consisting of a wide-band signal plus up to three 
sinusoidal noise functions. The program adaptively creates 
a filter that attempts to eliminate the sinusoidal noise 
while preserving the wide-band signal.
The following time series are displayed when the program 
runs:
-err: This is the negative of the error which is actually 
the output from the whitening filter.  Ideally this time 
series contains the wide-band signal with the sinusoidal 
noise having been removed.
signal: The raw wideband signal containing samples taken 
from a random noise generator.
sineNoise: The raw noise consisting of the sum of one, two,
or three sinusoidal functions.
input: The sum of the signal plus the sinusoidal noise.
output: The output produced by applying the prediction 
filter to the input signal plus noise.
target: The target signal that is used to control the 
adaptive process. This is the next sample beyond the 
samples that are processed by the prediction filter. In
other words, the prediction filter attempts to predict this
value. Thus, the adaptive process attempts to cause  the 
output from the prediction filter to match the next sample 
in the incoming signal plus noise.  This is an attempt to 
predict a future value based solely on the current and past
values.
Although not required by the adaptive process, the 
frequency response of the whitening filter is computed and 
displayed once every 100 iterations.  Ideally the amplitude
response is flat with very narrow notches at the 
frequencies of the interfering sinusoidal noise components.
Both the amplitude and phase response are displayed. This 
makes it possible to see the notches develop in the 
frequency response of the whitening filter as it converges.
It also makes it possible to see how the phase behaves at
the notches.
The individual whitening filters on which the frequency 
response is computed are also displayed as time series.
USAGE: The user provides six command line parameters to 
control the operation of the program.  If the user doesn't 
provide any command line parameters, six default values are
used instead.
The command line parameters are:
feedbackGain: The gain factor that is used in the feedback 
loop to adjust the coefficient values in the 
prediction/whitening filter. (A whitening filter is a 
prediction filter with a -1 appended to its end.) If the
value of the feedbackGain is too high, the program will 
become unstable.  If too low, convergence will take a long 
time. Values toward the low end tend to converge to better 
solutions. It is possible for the feedbackGain value to be 
low enough to avoid instability but high enough to cause 
the adaptive process to bounce around and never find a good
solution. Typical useful values for feedbackGain in this 
program are around 0.00001.  
numberIterations: The is the number of iterations that the 
program executes before stopping and displaying all of the 
graphic results.
predictionFilterLength: This is the number of coefficients 
in the prediction filter.  This can be any integer value 
greater than zero. The program will throw an exception if 
this value is zero.  Typical values are 15 to 30.  Longer 
filters tend to produce better results in terms of the 
narrowness of the notches at the noise frequencies and the 
flatness of the filter between the notches.
signalScale: A scale factor that is applied to the wide 
band signal provided by the random noise generator. The 
random noise generator produces uniformly distributed 
values ranging from -0.5 to +0.5. Scaling values of from 10
to 20 work well in terms of producing a wide-band signal 
that is of a suitable magnitude for plotting. Set this to 0
to see how the program behaves in the presence of noise and
the absence of signal.
noiseScale: A scale factor that is applied to each of the 
sinusoidal noise functions before they are added to the 
signal. The raw sinusoids vary from -1.0 to +1.0.  Scaling 
values of from 10 to 20 work well in terms of being of a 
suitable magnitude for plotting. Set this to 0 to see how 
the program behaves in the presence of wide-band signal and
the absence of narrow-band noise. 
numberNoiseSources: This value specifies the number of
sinusoidal noise components that are added to the wide-band
signal.  Must be an integer value from 0 to 3.
Tested using J2SE 5.0 and WinXP.  J2SE 5.0 or later is 
required.
**********************************************************/
import static java.lang.Math.*;//J2SE 5.0 req
class Adapt02{
  public static void main(String[] args){
    //Default parameter values
    double feedbackGain = 0.00001;
    int numberIterations = 500;
    int predictionFilterLength = 26;
    double signalScale = 20;
    double noiseScale = 20;
    int numberNoiseSources = 1;
    
    if(args.length != 6){
      System.out.println(
                   "Usage with all parameters following " +
                               "program name:n" +
                               "java Adapt02n" +
                               "feedbackGainn" + 
                               "numberIterationsn" + 
                               "predictionFilterLengthn" +
                               "signalScalen" +
                               "noiseScalen" +
                               "numberNoiseSourcesn");
      System.out.println(
            "Using following values by default:n" +
            "feedbackGain: " + feedbackGain +
            "nnumberIterations: " + numberIterations +
            "npredictionFilterLength: " + 
                                   predictionFilterLength +
            "nsignalScale: " + signalScale +
            "nnoiseScale: " + noiseScale +
            "nnumberNoiseSources: " + numberNoiseSources);
    }else{//Command line params were provided.
      feedbackGain = Double.parseDouble(args[0]);
      numberIterations = Integer.parseInt(args[1]);
      predictionFilterLength = Integer.parseInt(args[2]);
      signalScale = Double.parseDouble(args[3]);
      noiseScale = Double.parseDouble(args[4]);
      numberNoiseSources = Integer.parseInt(args[5]);
    
      System.out.println(
            "Using following values from input:n" +
            "feedbackGain: " + feedbackGain +
            "nnumberIterations: " + numberIterations +
            "npredictionFilterLength: " + 
                                   predictionFilterLength +
            "nsignalScale: " + signalScale +
            "nnoiseScale: " + noiseScale +
            "nnumberNoiseSources: " + numberNoiseSources);
    }//end else
      
    //Instantiate a new object of the Adapt02 class and
    // invoke the method named process on that object.
    new Adapt02().process(feedbackGain,
                          numberIterations,
                          predictionFilterLength,
                          signalScale,
                          noiseScale,
                          numberNoiseSources);
  }//end main
  //-----------------------------------------------------//
  
  //This is the primary adaptive processing and plotting
  // method for the program.
  void process(double feedbackGain,
               int numberIterations,
               int predictionFilterLength,
               double signalScale,
               double noiseScale,
               int numberNoiseSources){
    //Create the initial predictionFilter containing all
    // zero values.  You could initialize this to different
    // values if you wanted to.
    double[] predictionFilter = 
                        new double[predictionFilterLength];
    //Create the initial whiteningFilter and initialize it
    // for spectrum analysis and plotting by copying the
    // initial prediction filter into the lower elements of
    // the whitening filter.
    double[] whiteningFilter = 
                   new double[predictionFilter.length + 1];
    System.arraycopy(predictionFilter,
                     0,
                     whiteningFilter,
                     0,
                     predictionFilter.length);
    //Set the final value in the whitening filter to -1.
    whiteningFilter[whiteningFilter.length - 1] = -1;
    
    //Create an array to serve as a two-sample delay line
    // for the raw data.
    double[] rawData = new double[2];
    //Create an array to serve as a processing delay line
    // for the data being processed.
    double[] chanA = new double[predictionFilter.length];
    
    //Instantiate a plotting object for six channels of
    // time-series data.
    PlotALot07 timePlotObj = 
                  new PlotALot07("Time",468,200,25,10,4,4);
            
    //Instantiate a plotting object for two channels of
    // filter frequency response data.  One channel is for
    // the amplitude and the other channel is the phase.
    PlotALot03 freqPlotObj = 
                   new PlotALot03("Freq",264,487,35,2,0,0);
    
    //Instantiate a plotting object to display the
    // whitening filter at specific time intervals during
    // the adaptive process. Note that the minimum
    // allowable width for a Java Frame object is 112
    // pixels under WinXP.  Therefore, the following
    // display doesn't synchronize properly for prediction
    // filter lengths less than 25 coefficients.  However,
    // the code that feeds data to the plotting object
    // later in the program extends the length of the
    // filter to cause it to synchronize and to plot one
    // set of filter coefficients on each axis.
    PlotALot01 filterPlotObj = new PlotALot01("Filter",
            (whiteningFilter.length * 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 signal = 0;
    double sineNoise = 0;
    
    //Display frequency response of initial whitening
    // filter computed at 128 points between zero and the
    // folding frequency.
    displayFreqResponse(whiteningFilter,freqPlotObj,128,
                               whiteningFilter.length - 1);
    
    //Display the initial whitening filter as a time series
    // on the first axis.
    for(int cnt = 0;cnt < whiteningFilter.length;cnt++){
      filterPlotObj.feedData(40*whiteningFilter[cnt]);
    }//end for loop
    //Extend the whitening filter with a value of 2.5 for
    // display purposes only if it is too short to
    // synchronize properly with the plotting software.
    // This value of 2.5 is easily recognizable in the 
    // plot as artificial extended data.  See earlier
    // comment on this topic.  
    //Note that this approach to forcing synchronization
    // will not cause the plot to synchronize properly on
    // an operating system for which the sum of the left
    // and right insets on a Frame object are different
    // from 8 pixels.  The same approach to synchronization
    // could be used but the minimum synchronizable filter
    // length would probably be different.
    if(whiteningFilter.length <= 26){
      for(int cnt = 0;cnt < (26 - whiteningFilter.length);
                                                    cnt++){
        filterPlotObj.feedData(2.5);
      }//end for loop
    }//end if
    
    //Do the iterative adaptive process
    for(int cnt = 0;cnt < numberIterations;cnt++){
      //Get the next sample of wideband signal.
      signal = signalScale*(Math.random() - 0.5);
      
      //Get the next sample of sinusoidal noise containing
      // three, two, or one sinusoid.
      if(numberNoiseSources == 3){
        sineNoise = noiseScale*(Math.sin(2*cnt*PI/8) +
                                Math.sin(2*cnt*PI/5) +
                                Math.sin(2*cnt*PI/3));
      }else if(numberNoiseSources == 2){
        sineNoise = noiseScale*(Math.sin(2*cnt*PI/8) + 
                                Math.sin(2*cnt*PI/5));
      }else if(numberNoiseSources == 1){
        sineNoise = noiseScale*(Math.sin(2*cnt*PI/8));
      }else{
        System.out.println(
            "Incorrect number noise sources, terminating");
        System.exit(0);
      }//end else
      //Insert the signal plus noise into the raw data
      // delay line.
      flowLine(rawData,signal + sineNoise);
      //Populate chanA with the next to the last value in
      // the raw data delay line.  The last sample value in
      // the delay line will be the adaptive target.
      flowLine(chanA,rawData[rawData.length - 2]);
      //Get the most recent sample that was put into the
      // chanA delay line and save for plotting.
      input = chanA[chanA.length -1];
      
      //Apply the current predictionFilter to the data
      // contained in the chanA delay line.
      output = dotProduct(predictionFilter,chanA);
      //Get the signal plus noise sample from the end of
      // the raw data delay line for an adaptive target.
      target = rawData[rawData.length - 1];
      
      //Compute the error between the current filter output
      // and the target.
      err = output - target;
      
      //Use the error to update the predictionFilter
      // coefficients.
      for(int ctr = 0;ctr < predictionFilter.length;ctr++){
        predictionFilter[ctr] -= 
                               err*chanA[ctr]*feedbackGain;
      }//end for loop. This is the end of the adaptive
      // process.  Code following this point in the program
      // is used for display only.
      //Feed the time series data to the plotting object.
      timePlotObj.feedData(
                -err,signal,sineNoise,input,output,target);
      
      //Compute and plot the frequency response and plot
      // the whitening filter every 100 iterations.
      if(cnt%100 == 0){
        //Create a whitening filter from the data in the
        // prediction filter.  Begin by copying the
        // prediction filter into the bottom elements of
        // the whitening filter.
        System.arraycopy(predictionFilter,
                         0,
                         whiteningFilter,
                         0,
                         predictionFilter.length);
        //Now set the final value in the whitening filter
        // to -1. A whitening filter is a prediction filter
        // with a -1 appended to its end.
        whiteningFilter[whiteningFilter.length - 1] = -1;
        displayFreqResponse(whiteningFilter,freqPlotObj,
                           128,whiteningFilter.length - 1);
        //Display the whitening filter coefficient values.
        for(int ctr = 0;ctr < whiteningFilter.length;
                                                    ctr++){
          filterPlotObj.feedData(40*whiteningFilter[ctr]);
        }//end for loop
        //Extend the whitening filter with a value of 2.5
        // for plotting if necessary to cause it to
        // synchronize with one filter on each axis.
        // See explanatory comment earlier.
        if(whiteningFilter.length <= 26){
          for(int count = 0;
              count < (26-whiteningFilter.length);count++){
            filterPlotObj.feedData(2.5);
          }//End for loop
        }//End if statement
      }//End display of frequency response and whitening
       // filter
    }//End for loop, End adaptive process
    
    //Cause all the data to be plotted.
    timePlotObj.plotData();
    freqPlotObj.plotData(0,201);
    filterPlotObj.plotData(265,201);
    
  }//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
  //-----------------------------------------------------//
  
  //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];
      }//end for loop
      return result;
    }else{
      for(int cnt = 0;cnt < v2.length;cnt++){
        result += v1[cnt]*v2[cnt];
      }//med for loop
      return result;
    }//end else
  }//end dotProduct
  //-----------------------------------------------------//
  
  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);
    //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
    
    //Note that from this point forward, all references to
    // magnitude are referring to log base 10 data, which
    // can be thought of as scaled decibels.
    //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 50 times the peak value and shift up the
    // page by 50 units to make the values compatible with
    // the plotting program.  Recall that adding a
    // constant to log values is equivalent to scaling the
    // original data.
    for(int cnt = 0;cnt < magnitude.length;cnt++){
      magnitude[cnt] = 50*magnitude[cnt]/peak + 50;
    }//end for loop
    //Now feed the normalized decibel data to the plotting
    // system.
    for(int cnt = 0;cnt < magnitude.length;cnt++){
      plot.feedData(magnitude[cnt],angle[cnt]/20);
    }//end for loop
    
  }//end displayFreqResponse
  //-----------------------------------------------------//
}//end class Adapt02


Listing 21

 

/*File PlotALot07.java 
Copyright 2005, R.G.Baldwin
This program is an update to the program named 
PlotALot04 for the purpose of plotting six
data channels.  See PlotALot04 for descriptive
comments.  Otherwise, the comments in this
program have not been updated to reflect this
update.
The program was tested using J2SE 5.0 and WinXP.
Requires J2SE 5.0 to support generics.
************************************************/
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class PlotALot07{
  //This main method is provided so that the
  // class can be run as an application to test
  // itself.
  public static void main(String[] args){
    //Instantiate a plotting object using the
    // version of the constructor that allows for
    // controlling the plotting parameters.
    PlotALot07 plotObjectA = 
            new PlotALot07("A",158,350,25,5,4,4);
    
    //Feed quadruplets of data values to the 
    // plotting object.
    for(int cnt = 0;cnt < 115;cnt++){
      //Plot some white random noise. Note that
      // fifteen of the values for each time
      // series are not random.  See the opening
      // comments for a discussion of the reasons
      // why.
      double valBlack = (Math.random() - 0.5)*25;
      double valRed = valBlack;
      double valBlue = valBlack;
      double valGreen = valBlack;
      double valMagenta = valBlack;
      double valCyan = valBlack;
      //Feed quadruplets of values to the plotting
      // object by invoking the feedData method
      // once for each quadruplet of data values.
      if(cnt == 57){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else if(cnt == 58){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else if(cnt == 59){
        plotObjectA.feedData(25,25,25,25,25,25);
      }else if(cnt == 60){
        plotObjectA.feedData(-25,-25,-25,-25,-25,-25);
      }else if(cnt == 61){
        plotObjectA.feedData(25,25,25,25,25,25);
      }else if(cnt == 62){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else if(cnt == 63){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else if(cnt == 26){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else if(cnt == 27){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else if(cnt == 28){
        plotObjectA.feedData(20,20,20,20,20,20);
      }else if(cnt == 29){
        plotObjectA.feedData(20,20,20,20,20,20);
      }else if(cnt == 30){
        plotObjectA.feedData(-20,-20,-20,-20,-20,-20);
      }else if(cnt == 31){
        plotObjectA.feedData(-20,-20,-20,-20,-20,-20);
      }else if(cnt == 32){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else if(cnt == 33){
        plotObjectA.feedData(0,0,0,0,0,0);
      }else{
        plotObjectA.feedData(valBlack,
                             valRed,
                             valBlue,
                             valGreen,
                             valMagenta,
                             valCyan);
      }//end else
    }//end for loop
    //Cause the data to be plotted in the default
    // screen location.
    plotObjectA.plotData();
  }//end main
  //-------------------------------------------//
  String title;
  int frameWidth;
  int frameHeight;
  int traceSpacing;//pixels between traces
  int sampSpacing;//pixels between samples
  int ovalWidth;//width of sample marking oval
  int ovalHeight;//height of sample marking oval
  
  int tracesPerPage;
  int samplesPerPage;
  int pageCounter = 0;
  int sampleCounter = 0;
  ArrayList <Page> pageLinks = 
                           new ArrayList<Page>();
  
  //There are two overloaded versions of the
  // constructor for this class.  This
  // overloaded version accepts several incoming
  // parameters allowing the user to control
  // various aspects of the plotting format. A
  // different overloaded version accepts a title
  // string only and sets all of the plotting
  // parameters to default values.
  PlotALot07(String title,//Frame title
             int frameWidth,//in pixels
             int frameHeight,//in pixels
             int traceSpacing,//in pixels
             int sampSpace,//in pixels per sample
             int ovalWidth,//sample marker width
             int ovalHeight)//sample marker hite
  {//constructor
    //Specify sampSpace as pixels per sample.
    // Should never be less than 1.  Convert to
    // pixels between samples for purposes of
    // computation.
    this.title = title;
    this.frameWidth = frameWidth;
    this.frameHeight = frameHeight;
    this.traceSpacing = traceSpacing;
    //Convert to pixels between samples.
    this.sampSpacing = sampSpace - 1;
    this.ovalWidth = ovalWidth;
    this.ovalHeight = ovalHeight;
    //The following object is instantiated solely
    // to provide information about the width and
    // height of the canvas. This information is
    // used to compute a variety of other
    // important values.
    Page tempPage = new Page(title);
    int canvasWidth = tempPage.canvas.getWidth();
    int canvasHeight = 
                     tempPage.canvas.getHeight();
    //Display information about this plotting
    // object.
    System.out.println("nTitle: " + title);
    System.out.println(
          "Frame width: " + tempPage.getWidth());
    System.out.println(
        "Frame height: " + tempPage.getHeight());
    System.out.println(
                   "Page width: " + canvasWidth);
    System.out.println(
                 "Page height: " + canvasHeight);
    System.out.println(
               "Trace spacing: " + traceSpacing);
    System.out.println(
         "Sample spacing: " + (sampSpacing + 1));
    if(sampSpacing < 0){
      System.out.println("Terminating");
      System.exit(0);
    }//end if
    //Get rid of this temporary page.
    tempPage.dispose();
    //Now compute the remaining important values.
    tracesPerPage = 
                 (canvasHeight - traceSpacing/2)/
                                    traceSpacing;
    System.out.println("Traces per page: "
                                + tracesPerPage);
    if((tracesPerPage == 0) || 
                        (tracesPerPage%6 != 0) ){
      System.out.println("Terminating program");
      System.exit(0);
    }//end if
    samplesPerPage = canvasWidth * tracesPerPage/
                             (sampSpacing + 1)/6;
    System.out.println("Samples per page: "
                               + samplesPerPage);
    //Now instantiate the first usable Page
    // object and store its reference in the
    // list.
    pageLinks.add(new Page(title));
  }//end constructor
  //-------------------------------------------//
  
  PlotALot07(String title){
    //Invoke the other overloaded constructor
    // passing default values for all but the
    // title.
    this(title,400,410,50,2,2,2);
  }//end overloaded constructor
  //-------------------------------------------//
  
  //Invoke this method once for each quadruplet of 
  // data values to be plotted.
  void feedData(double valBlack,
                double valRed,
                double valBlue,
                double valGreen,
                double valMagenta,
                double valCyan){
    if((sampleCounter) == samplesPerPage){
      //if the page is full, increment the page
      // counter, create a new empty page, and
      // reset the sample counter.
      pageCounter++;
      sampleCounter = 0;
      pageLinks.add(new Page(title));
    }//end if
    //Store the sample values in the MyCanvas
    // object to be used later to paint the
    // screen.  Then increment the sample
    // counter.  The sample values pass through
    // the page object into the current MyCanvas
    // object.
    pageLinks.get(pageCounter).putData(
                                  valBlack,
                                  valRed,
                                  valBlue,
                                  valGreen,
                                  valMagenta,
                                  valCyan,
                                  sampleCounter);
    sampleCounter++;
  }//end feedData
  //-------------------------------------------//
  
  //There are two overloaded versions of the
  // plotData method.  One version allows the
  // user to specify the location on the screen
  // where the stack of plotted pages will
  // appear.  The other version places the stack
  // in the upper left corner of the screen.
  
  //Invoke one of the overloaded versions of
  // this method once when all data has been fed
  // to the plotting object in order to rearrange
  // the order of the pages with page 0 at the
  // top of the stack on the screen.
  
  //For this overloaded version, specify xCoor
  // and yCoor to control the location of the
  // stack on the screen.  Values of 0,0 will
  // place the stack at the upper left corner of
  // the screen.  Also see the other overloaded
  // version, which places the stack at the upper
  // left corner of the screen by default.
  void plotData(int xCoor,int yCoor){
    Page lastPage = 
             pageLinks.get(pageLinks.size() - 1);
    //Delay until last page becomes visible.
    while(!lastPage.isVisible()){
      //Loop until last page becomes visible
    }//end while loop
    
    Page tempPage = null;
    //Make all pages invisible
    for(int cnt = 0;cnt < (pageLinks.size());
                                          cnt++){
      tempPage = pageLinks.get(cnt);
      tempPage.setVisible(false);
    }//end for loop
    
    //Now make all pages visible in reverse order
    // so that page 0 will be on top of the
    // stack on the screen.
    for(int cnt = pageLinks.size() - 1;cnt >= 0;
                                          cnt--){
      tempPage = pageLinks.get(cnt);
      tempPage.setLocation(xCoor,yCoor);
      tempPage.setVisible(true);
    }//end for loop
  }//end plotData(int xCoor,int yCoor)
  //-------------------------------------------//
  
  //This overloaded version of the method causes
  // the stack to be located in the upper left
  // corner of the screen by default
  void plotData(){
    plotData(0,0);//invoke overloaded version
  }//end plotData()
  //-------------------------------------------//
  //Inner class.  A PlotALot07 object may
  // have as many Page objects as are required
  // to plot all of the data values.  The 
  // reference to each Page object is stored
  // in an ArrayList object belonging to the
  // PlotALot07 object.
  class Page extends Frame{
    MyCanvas canvas;
    int sampleCounter;
    Page(String title){//constructor
      canvas = new MyCanvas();
      add(canvas);
      setSize(frameWidth,frameHeight);    
      setTitle(title + " Page: " + pageCounter);
      setVisible(true);
      
      //---------------------------------------//
      //Anonymous inner class to terminate the
      // program when the user clicks the close
      // button on the Frame.
      addWindowListener(
        new WindowAdapter(){
          public void windowClosing(
                                  WindowEvent e){
            System.exit(0);//terminate program
          }//end windowClosing()
        }//end WindowAdapter
      );//end addWindowListener
      //---------------------------------------//
    }//end constructor
    //=========================================//
  
    //This method receives a quadruplet of sample
    // values of type double and stores each of
    // them in a separate array object belonging
    // to the MyCanvas object.
    void putData(double valBlack,
                 double valRed,
                 double valBlue,
                 double valGreen,
                 double valMagenta,
                 double valCyan,
                 int sampleCounter){
      canvas.blackData[sampleCounter] = valBlack;
      canvas.redData[sampleCounter] = valRed;
      canvas.blueData[sampleCounter] = valBlue;
      canvas.greenData[sampleCounter] = valGreen;
      canvas.magentaData[sampleCounter] = valMagenta;
      canvas.cyanData[sampleCounter] = valCyan;
      //Save the sample counter in an instance
      // variable to make it available to the
      // overridden paint method. This value is
      // needed by the paint method so it will
      // know how many samples to plot on the
      // final page which probably won't be full.
      this.sampleCounter = sampleCounter;
    }//end putData
    
    //=========================================//
    //Inner class
    class MyCanvas extends Canvas{
      double [] blackData = 
                      new double[samplesPerPage];
      double [] redData = 
                      new double[samplesPerPage];
      double [] blueData = 
                      new double[samplesPerPage];
      double [] greenData = 
                      new double[samplesPerPage];
      double [] magentaData = 
                      new double[samplesPerPage];
      double [] cyanData = 
                      new double[samplesPerPage];
                      
      //Override the paint method
      public void paint(Graphics g){
        //Draw horizontal axes, one for each
        // trace.
        for(int cnt = 0;cnt < tracesPerPage;
                                          cnt++){
          g.drawLine(0,
                     (cnt+1)*traceSpacing,
                     this.getWidth(),
                     (cnt+1)*traceSpacing);
        }//end for loop
        
        //Plot the points if there are any to be
        // plotted.
        if(sampleCounter > 0){
          for(int cnt = 0;cnt <= sampleCounter;
                                          cnt++){
                                            
            //Begin by plotting the values from
            // the blackData array object.
            g.setColor(Color.BLACK);
            
            //Compute a vertical offset to locate
            // the black data on every third axis
            // on the page.
            int yOffset = 
               ((1 + cnt*(sampSpacing + 1)/
                this.getWidth())*6*traceSpacing)
                                - 5*traceSpacing;
            //Draw an oval centered on the sample
            // value to mark the sample in the
            // plot. It is best if the dimensions
            // of the oval are evenly divisable
            // by 2 for  centering purposes.
            //Reverse the sign of the sample
            // value to cause positive sample
            // values to be plotted above the
            // axis.
            g.drawOval(cnt*(sampSpacing + 1)%
                   this.getWidth() - ovalWidth/2,
              yOffset - (int)blackData[cnt] 
                                  - ovalHeight/2,
              ovalWidth,
              ovalHeight);
            
            //Connect the sample values with
            // straight lines.  Do not draw a
            // line connecting the last sample in
            // one trace to the first sample in
            // the next trace.
            if(cnt*(sampSpacing + 1)%
                               this.getWidth() >=
                                sampSpacing + 1){
              g.drawLine(
                (cnt - 1)*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)blackData[cnt-1],
                cnt*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)blackData[cnt]);
            }//end if
            //Now plot the data stored in the
            // redData array object.
            g.setColor(Color.RED);
            //Compute a vertical offset to locate
            // the red data on every third axis
            // on the page.
            yOffset = (1 + cnt*(sampSpacing + 1)/
                  this.getWidth())*6*traceSpacing
                                  - 4*traceSpacing;
            
            //Draw the ovals as described above.
            g.drawOval(cnt*(sampSpacing + 1)%
                   this.getWidth() - ovalWidth/2,
              yOffset - (int)redData[cnt] 
                                  - ovalHeight/2,
              ovalWidth,
              ovalHeight);
            
            //Connect the sample values with
            // straight lines as described above.
            if(cnt*(sampSpacing + 1)%
                               this.getWidth() >=
                                sampSpacing + 1){
              g.drawLine(
                (cnt - 1)*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)redData[cnt-1],
                cnt*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)redData[cnt]);
                
            }//end if
          
            //Now plot the data stored in the
            // blueData array object.
            g.setColor(Color.BLUE);
            //Compute a vertical offset to locate
            // the blue data on every third axis
            // on the page.
            yOffset = (1 + cnt*(sampSpacing + 1)/
                 this.getWidth())*6*traceSpacing 
                                 -3*traceSpacing;
            
            //Draw the ovals as described above.
            g.drawOval(cnt*(sampSpacing + 1)%
                   this.getWidth() - ovalWidth/2,
              yOffset - (int)blueData[cnt] 
                                  - ovalHeight/2,
              ovalWidth,
              ovalHeight);
            
            //Connect the sample values with
            // straight lines as described above.
            if(cnt*(sampSpacing + 1)%
                               this.getWidth() >=
                                sampSpacing + 1){
              g.drawLine(
                (cnt - 1)*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)blueData[cnt-1],
                cnt*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)blueData[cnt]);
            }//end if
            
            
            //Now plot the data stored in the
            // greenData array object.
            g.setColor(Color.GREEN);
            //Compute a vertical offset to locate
            // the green data on every third axis
            // on the page.
            yOffset = (1 + cnt*(sampSpacing + 1)/
                 this.getWidth())*6*traceSpacing 
                                 -2*traceSpacing;
            
            //Draw the ovals as described above.
            g.drawOval(cnt*(sampSpacing + 1)%
                   this.getWidth() - ovalWidth/2,
              yOffset - (int)greenData[cnt] 
                                  - ovalHeight/2,
              ovalWidth,
              ovalHeight);
            
            //Connect the sample values with
            // straight lines as described above.
            if(cnt*(sampSpacing + 1)%
                               this.getWidth() >=
                                sampSpacing + 1){
              g.drawLine(
                (cnt - 1)*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)greenData[cnt-1],
                cnt*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)greenData[cnt]);
            }//end if
            
            //Now plot the data stored in the
            // magentaData array object.
            g.setColor(Color.MAGENTA);
            //Compute a vertical offset to locate
            // the magenta data on every third axis
            // on the page.
            yOffset = (1 + cnt*(sampSpacing + 1)/
                 this.getWidth())*6*traceSpacing 
                                    -traceSpacing;
            
            //Draw the ovals as described above.
            g.drawOval(cnt*(sampSpacing + 1)%
                   this.getWidth() - ovalWidth/2,
              yOffset - (int)magentaData[cnt] 
                                  - ovalHeight/2,
              ovalWidth,
              ovalHeight);
            
            //Connect the sample values with
            // straight lines as described above.
            if(cnt*(sampSpacing + 1)%
                               this.getWidth() >=
                                sampSpacing + 1){
              g.drawLine(
                (cnt - 1)*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)magentaData[cnt-1],
                cnt*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)magentaData[cnt]);
            }//end if
            
            //Now plot the data stored in the
            // cyanData array object.
            g.setColor(Color.CYAN);
            //Compute a vertical offset to locate
            // the cyan data on every third axis
            // on the page.
            yOffset = (1 + cnt*(sampSpacing + 1)/
                 this.getWidth())*6*traceSpacing;
            
            //Draw the ovals as described above.
            g.drawOval(cnt*(sampSpacing + 1)%
                   this.getWidth() - ovalWidth/2,
              yOffset - (int)cyanData[cnt] 
                                  - ovalHeight/2,
              ovalWidth,
              ovalHeight);
            
            //Connect the sample values with
            // straight lines as described above.
            if(cnt*(sampSpacing + 1)%
                               this.getWidth() >=
                                sampSpacing + 1){
              g.drawLine(
                (cnt - 1)*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)cyanData[cnt-1],
                cnt*(sampSpacing + 1)%
                                 this.getWidth(),
                yOffset - (int)cyanData[cnt]);
            }//end if
            
          }//end for loop
        }//end if for sampleCounter > 0
      }//end overridden paint method
    }//end inner class MyCanvas
  }//end inner class Page
}//end class PlotALot07
//=============================================//

Listing 22


Copyright 2005, 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