Skip to content

Commit

Permalink
[fixes psambit9791#26] Implement Leaky NLMS adaptive filter
Browse files Browse the repository at this point in the history
  • Loading branch information
SiboVG committed Nov 22, 2021
1 parent bc28235 commit 7903016
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* The NLMS adaptive filter is a filter that adapts its filter weights to get an input signal x to match a desired output
* signal (= the output of the filter). It does this by trying to minimize the squared error between the desired signal
* and the filter output signal.
* Additionally, you can use a Leaky LMS filter by setting the leakage factor in the LMS constructor.
* A leakage factor < 1 results in improved stability and tracking of the filter.
*
* It is very similar to the LMS-filter, with the difference that the learning rate gets automatically adjusted according
* to the input signal's power.
Expand All @@ -16,6 +18,7 @@
*/
public class NLMS {
private final double learningRate; // Learning rate (= step size)
private final double leakageFactor; // Leakage factor
private double[] weights; // Weights of the filter
private double[] error; // Error of the filter
private double[] output; // Filtered output
Expand All @@ -37,33 +40,55 @@ public enum WeightsFillMethod {
* be unstable. A correct learning rate is dependent on the power of the input signal
* For a stable filter, the learning rate should be:
* 0 ≤ learningRate ≤ 2
* @param leakageFactor defines how much leakage the Leaky LMS filter should have
* 0 ≤ leakageFactor ≤ 1
* leakageFactor = 1 => no leakage; leakageFactor < 1 => leakage
* @param weights initialized weights (size = number of taps of the filter)
*/
public NLMS(double learningRate, double[] weights) {
public NLMS(double learningRate, double leakageFactor, double[] weights) {
if (weights == null || weights.length == 0) {
throw new IllegalArgumentException("Weights must be non-null and with a length greater than 0");
}
if (learningRate < 0 || learningRate > 2) {
System.err.println("Keep the learning rate between 0 and 2 to avoid diverging results");
}
this.learningRate = learningRate;
this.leakageFactor = leakageFactor;
this.weights = weights;
}

/**
* This constructor initialises the prerequisites required for the NLMS adaptive filter, without leakage.
* @param learningRate also known as step size. Determines how fast the adaptive filter changes its filter weights.
* If it is too slow, the filter may have bad performance. If it is too high, the filter will
* be unstable. A correct learning rate is dependent on the power of the input signal
* For a stable filter, the learning rate should be:
* 0 ≤ learningRate ≤ 2
* @param weights initialized weights (size = number of taps of the filter)
*/
public NLMS(double learningRate, double[] weights) {
this(learningRate, 1, weights);
}

/**
* This constructor initialises the prerequisites required for the NLMS adaptive filter.
* @param learningRate also known as step size. Determines how fast the adaptive filter changes its filter weights.
* If it is too slow, the filter may have bad performance. If it is too high, the filter will
* be unstable. A correct learning rate is dependent on the power of the input signal
* For a stable filter, the learning rate should be:
* 0 ≤ learningRate ≤ 2
* @param leakageFactor defines how much leakage the Leaky LMS filter should have
* 0 ≤ leakageFactor ≤ 1
* leakageFactor = 1 => no leakage; leakageFactor < 1 => leakage
* @param length length (number of taps) of the filter
* @param fillMethod determines how the weights should be initialized
*/
public NLMS(double learningRate, int length, WeightsFillMethod fillMethod) {
public NLMS(double learningRate, double leakageFactor, int length, WeightsFillMethod fillMethod) {
if (learningRate < 0 || learningRate > 2) {
System.err.println("Keep the learning rate between 0 and 2 to avoid diverging results");
}
this.learningRate = learningRate;
this.leakageFactor = leakageFactor;
this.weights = new double[length];
switch (fillMethod) {
// Create random weights between 0 and 1
Expand All @@ -79,7 +104,20 @@ public NLMS(double learningRate, int length, WeightsFillMethod fillMethod) {
default:
throw new IllegalArgumentException("Unknown weights fill method");
}
this.learningRate = learningRate;
}

/**
* This constructor initialises the prerequisites required for the NLMS adaptive filter, without leakage.
* @param learningRate also known as step size. Determines how fast the adaptive filter changes its filter weights.
* If it is too slow, the filter may have bad performance. If it is too high, the filter will
* be unstable. A correct learning rate is dependent on the power of the input signal
* For a stable filter, the learning rate should be:
* 0 ≤ learningRate ≤ 2
* @param length length (number of taps) of the filter
* @param fillMethod determines how the weights should be initialized
*/
public NLMS(double learningRate, int length, WeightsFillMethod fillMethod) {
this(learningRate, 1, length, fillMethod);
}

/**
Expand All @@ -103,7 +141,7 @@ private double[] adaptWeights(double desired, double[] x) {

// Update filter coefficients
for (int i = 0; i < this.weights.length; i++) {
this.weights[i] = this.weights[i] + this.learningRate/(regTerm + power_x) * error * x[x.length - 1 - i];
this.weights[i] = this.leakageFactor*this.weights[i] + this.learningRate/(regTerm + power_x) * error * x[x.length - 1 - i];
}

return new double[] {y, error};
Expand Down
Loading

0 comments on commit 7903016

Please sign in to comment.