# Adaptive Noise Cancellation using Java

## Run the Program

I encourage you to copy the code for the class named **Adapt08** from the section entitled
Complete Program Listings.
Compile and execute the program. Experiment with the code. Make changes to the code, recompile, execute,
and observe the results of your changes.

Modify the program parameters and the path operator and see if you can explain the results of those modifications.

Experiment with different values for **feedback Gain**. What happens if you use a large value for **feedbackGain**?
What happens if you use a very small value for **feedbackGain**? What
happens if you use a negative value for **feedbackGain**? Can you
explain the results that you experience?

While experimenting with the **feedbackGain**, you might also want to
experiment with the program parameter named **numberIterations**.
See if you can explain the results for small, medium, and large values for this
parameter.

**Other classes required**

In addition to the classes named **Adapt08**, you will need access to the
following classes. The source code for these classes can be found in the
lessons indicated.

- AdaptEngine02: Adaptive Identification and Inverse Filtering using Java
- AdaptiveResult: Adaptive Identification and Inverse Filtering using Java
- ForwardRealToComplex01: Spectrum Analysis using Java, Sampling Frequency, Folding Frequency, and the FFT Algorithm
- PlotALot01: Plotting Large Quantities of Data using Java
- PlotALot03: Plotting Large Quantities of Data using Java
- PlotALot05: Adaptive Filtering in Java, Getting Started

## Summary

In this lesson, I showed you how to use a general-purpose
LMS
adaptive engine to write a Java program that illustrates the use of adaptive
filtering for *Noise Cancellation*.

## What's Next?

Adaptive filtering is commonly used for the following four scenarios:

- System Identification
- Inverse System Identification
- Noise Cancellation
- Prediction

A previous lesson entitled
Adaptive Identification and
Inverse Filtering using Java explained how to write Java programs to illustrate the first two
scenarios in the above list. This lesson explains how to write a Java
program that illustrates the use of adaptive filtering for the third scenario:
*Noise Cancellation*.

I plan to
publish a lesson in the near future that explains and provides examples of *
Prediction* using adaptive filtering.

## References

In preparation for understanding the material in this lesson, I recommend that you study the material in the following previously-published lessons:

- 100 Periodic Motion and Sinusoids
- 104 Sampled Time Series
- 108 Averaging Time Series
- 1478 Fun with Java, How and Why Spectral Analysis Works
- 1482 Spectrum Analysis using Java, Sampling Frequency, Folding Frequency, and the FFT Algorithm
- 1483 Spectrum Analysis using Java, Frequency Resolution versus Data Length
- 1484 Spectrum Analysis using Java, Complex Spectrum and Phase Angle
- 1485 Spectrum Analysis using Java, Forward and Inverse Transforms, Filtering in the Frequency Domain
- 1487 Convolution and Frequency Filtering in Java
- 1488 Convolution and Matched Filtering in Java
- 1492 Plotting Large Quantities of Data using Java
- 2350 Adaptive Filtering in Java, Getting Started
- 2352 An Adaptive Whitening Filter in Java
- 2354 A General-Purpose LMS Adaptive Engine in Java
- 2356 An Adaptive Line Tracker in Java
- 2358 Adaptive Identification and Inverse Filtering using Java

## Complete Program Listings

A complete listings of the class discussed in this lesson is shown in Listing 14 below.

/*File Adapt08.java Copyright 2005, R.G.Baldwin The purpose of this program is to illustrate an adaptive noise cancellation system. See the following URL for a description and a block diagram of an adaptive noise cancellation system. http://www.owlnet.rice.edu/~ryanking/elec431/intro.html This program requires the following classes: Adapt08.class AdaptEngine02.class AdaptiveResult.class ForwardRealToComplex01.class PlotALot01.class PlotALot03.class PlotALot07.class This program uses the adaptive engine named AdaptEngine02 to adaptively develop a filter. One of the inputs to the adaptive engine is the sum of signal plus noise. The other input to the adaptive engine is noise that is different from, but which is correlated to the noise that is added to the signal. The adaptive engine develops a filter that removes the noise from the signal plus noise data producing an output consisting of almost pure signal. The signal and the original noise are derived from a random number generator. Therefore, they are both essentially white. The signal is not correlated with the noise. Before scaling, the values obtained from the random number generator are uniformly distributed from -1.0 to +1.0. The white noise is processed through a convolution filter before adding it to the signal. The convolution filter is designed to simulate the effect of acoustic energy being emitted at one point in space and being modified through the addition of several time-delayed and attenuated echoes before being received at another point in space. This results in constructive and destructive interference such that the noise that is added to the signal is no longer white. However, it is still correlated with the original white noise. The program produces five graphs with three graphs in a row across the top of the screen and two graphs in a row below the top row. The following is a brief description of each of the five graphs, working from left to right, top to bottom. 1. The impulse response of the convolution filter that is applied to the white noise before it is added to the white signal. 2. The amplitude and phase response of the convolution filter that is applied to the white noise before it is added to the white signal. 3. Six time series that illustrate the time behavior of the adaptive process. 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. Graph 3 consists 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. If you increase the number of iterations described later, graphs 4 and 5 will also consist of multiple pages stacked on top of one another. The six time series that are plotted are, from top to bottom in the colors indicated: 1. (Black) Input to the adaptive filter. This is the white noise. 2. (Red) Target for the adaptive process. This is the signal plus noise. 3. (Blue) Output from the adaptive filter. 4. (Green) Error computed within the adaptive process. In this case, the error trace is actually the trace that contains the signal with the noise removed. In other words, the blue trace is an estimate of the noise and the green trace is the difference between the red trace and the blue trace. 5. (Violet) The original pure signal. This trace is provided for visual comparison with the green signal trace. 6. (Turquoise) The arithmetic difference between the pure signal trace (violet) and the green trace containing the adaptive process's estimate of the signal. This trace is provided for an arithmetic comparison of the pure signal trace and the adaptive estimate of the signal. Ideally this trace will go to zero if the adaptive process is successful in totally eliminating the noise from the green trace. 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 one occasion, an impulse is inserted into the white noise data. This makes it possible to see: 1. The impulse response of the convolution filter that is applied to the white noise before it is added to the white signal. 2. The impulse response of the final adaptive filter. There is no user input to this program. All parameters are hard coded into the main method. To run the program with different parameters, modify the parameters and recompile the program. The program 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. If you change it to a value that is less than 26, the plot of the impulse responses of the adaptive filter will not be properly aligned. noiseScale: The scale factor that is applied to the values that are extracted from the random number generator and used as white noise. signalScale: The scale factor that is applied to the values that are extracted from the random number generator and used as white signal. pathOperator: Areference to an array of type double[] containing the coefficients of the convolution filter that is applied to the white noise before it is added to the white signal. Tested using J2SE 5.0 and WinXP. J2SE 5.0 or later is required. **********************************************************/ import static java.lang.Math.*;//J2SE 5.0 req class Adapt08{ public static void main(String[] args){ //Default parameter values double feedbackGain = 0.0001; int numberIterations = 2001; int filterLength = 26;//Must be >= 26 for plotting. double noiseScale = 10; double signalScale = 10; //Define the path impulse response as a simulation of // an acoustic signal with time-delayed echoes. double[] pathOperator = {0.0, 0.0, 0.0, 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}; //Instantiate a new object of the Adapt08 class // and invoke the method named process on that object. new Adapt08().process(feedbackGain, numberIterations, filterLength, noiseScale, signalScale, pathOperator); }//end main //-----------------------------------------------------// //This is the primary adaptive processing and plotting // method for the program. void process(double feedbackGain, int numberIterations, int filterLength, double noiseScale, double signalScale, double[] pathOperator){ //The following array will be populated with the // adaptive filter for display purposes. double[] filter = null; //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 // 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. AdaptEngine02 adapter = new AdaptEngine02( filterLength,feedbackGain); //Instantiate an array object that will be used as a // delay line for the white noise data. double[] rawData = new double[pathOperator.length]; //Instantiate a plotting object for six channels of // time-series data. PlotALot07 timePlotObj = new PlotALot07("Time",468,198,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 whiteNoise = 0; double whiteSignal = 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 white noise and // white signal data from a random number generator. // Before scaling by noiseScale and signalScale, the // values are uniformly distributed from -1.0 to 1.0. whiteNoise = noiseScale*(2*(Math.random() - 0.5)); whiteSignal = signalScale*(2*(Math.random() - 0.5)); //Set white noise and white signal values near the // end of the run to zero and turn adaptation off. // Insert an impulse near the end of the whiteNoise // data that will produce impulse responses for the // path and for the adaptive filter. //Set values to zero. if(cnt > (numberIterations - 5*filterLength)){ whiteNoise = 0; whiteSignal = 0; adaptOn = false; }//end if //Now insert an impulse at one specific location in // the whiteNoise data. if(cnt == numberIterations - 3*filterLength){ whiteNoise = 2 * noiseScale; }//end if //Insert the white noise data into the delay line // that will be used to convolve the white noise // with the path operator. flowLine(rawData,whiteNoise); //Apply the path operator to the white noise data. double pathNoise = 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; //Establish the appropriate input values for an // adaptive noise cancellation filter and perform the // adaptive update. input = whiteNoise; target = pathNoise + whiteSignal; result = adapter.adapt(input,target,adaptOn); //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, whiteSignal,err + whiteSignal); //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); }//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 //-----------------------------------------------------// }//end class Adapt08 |

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.*

Page 2 of 2