001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.math.estimator;
006
007import edu.wpi.first.math.MathUtil;
008import edu.wpi.first.math.Matrix;
009import edu.wpi.first.math.Num;
010import edu.wpi.first.math.numbers.N1;
011import java.util.function.BiFunction;
012import org.ejml.simple.SimpleMatrix;
013
014/** Angle statistics functions. */
015public final class AngleStatistics {
016  /** Utility class. */
017  private AngleStatistics() {}
018
019  /**
020   * Subtracts a and b while normalizing the resulting value in the selected row as if it were an
021   * angle.
022   *
023   * @param <S> Number of rows in vector.
024   * @param a A vector to subtract from.
025   * @param b A vector to subtract with.
026   * @param angleStateIdx The row containing angles to be normalized.
027   * @return Difference of two vectors with angle at the given index normalized.
028   */
029  public static <S extends Num> Matrix<S, N1> angleResidual(
030      Matrix<S, N1> a, Matrix<S, N1> b, int angleStateIdx) {
031    Matrix<S, N1> ret = a.minus(b);
032    ret.set(angleStateIdx, 0, MathUtil.angleModulus(ret.get(angleStateIdx, 0)));
033
034    return ret;
035  }
036
037  /**
038   * Returns a function that subtracts two vectors while normalizing the resulting value in the
039   * selected row as if it were an angle.
040   *
041   * @param <S> Number of rows in vector.
042   * @param angleStateIdx The row containing angles to be normalized.
043   * @return Function returning difference of two vectors with angle at the given index normalized.
044   */
045  public static <S extends Num>
046      BiFunction<Matrix<S, N1>, Matrix<S, N1>, Matrix<S, N1>> angleResidual(int angleStateIdx) {
047    return (a, b) -> angleResidual(a, b, angleStateIdx);
048  }
049
050  /**
051   * Adds a and b while normalizing the resulting value in the selected row as an angle.
052   *
053   * @param <S> Number of rows in vector.
054   * @param a A vector to add with.
055   * @param b A vector to add with.
056   * @param angleStateIdx The row containing angles to be normalized.
057   * @return Sum of two vectors with angle at the given index normalized.
058   */
059  public static <S extends Num> Matrix<S, N1> angleAdd(
060      Matrix<S, N1> a, Matrix<S, N1> b, int angleStateIdx) {
061    Matrix<S, N1> ret = a.plus(b);
062    ret.set(angleStateIdx, 0, MathUtil.angleModulus(ret.get(angleStateIdx, 0)));
063
064    return ret;
065  }
066
067  /**
068   * Returns a function that adds two vectors while normalizing the resulting value in the selected
069   * row as an angle.
070   *
071   * @param <S> Number of rows in vector.
072   * @param angleStateIdx The row containing angles to be normalized.
073   * @return Function returning of two vectors with angle at the given index normalized.
074   */
075  public static <S extends Num> BiFunction<Matrix<S, N1>, Matrix<S, N1>, Matrix<S, N1>> angleAdd(
076      int angleStateIdx) {
077    return (a, b) -> angleAdd(a, b, angleStateIdx);
078  }
079
080  /**
081   * Computes the mean of sigmas with the weights Wm while computing a special angle mean for a
082   * select row.
083   *
084   * @param <S> Number of rows in sigma point matrix.
085   * @param sigmas Sigma points.
086   * @param Wm Weights for the mean.
087   * @param angleStateIdx The row containing the angles.
088   * @return Mean of sigma points.
089   */
090  public static <S extends Num> Matrix<S, N1> angleMean(
091      Matrix<S, ?> sigmas, Matrix<?, N1> Wm, int angleStateIdx) {
092    double[] angleSigmas = sigmas.extractRowVector(angleStateIdx).getData();
093    Matrix<N1, ?> sinAngleSigmas = new Matrix<>(new SimpleMatrix(1, sigmas.getNumCols()));
094    Matrix<N1, ?> cosAngleSigmas = new Matrix<>(new SimpleMatrix(1, sigmas.getNumCols()));
095    for (int i = 0; i < angleSigmas.length; i++) {
096      sinAngleSigmas.set(0, i, Math.sin(angleSigmas[i]));
097      cosAngleSigmas.set(0, i, Math.cos(angleSigmas[i]));
098    }
099
100    double sumSin = sinAngleSigmas.times(Matrix.changeBoundsUnchecked(Wm)).elementSum();
101    double sumCos = cosAngleSigmas.times(Matrix.changeBoundsUnchecked(Wm)).elementSum();
102
103    Matrix<S, N1> ret = sigmas.times(Matrix.changeBoundsUnchecked(Wm));
104    ret.set(angleStateIdx, 0, Math.atan2(sumSin, sumCos));
105
106    return ret;
107  }
108
109  /**
110   * Returns a function that computes the mean of sigmas with the weights Wm while computing a
111   * special angle mean for a select row.
112   *
113   * @param <S> Number of rows in sigma point matrix.
114   * @param angleStateIdx The row containing the angles.
115   * @return Function returning mean of sigma points.
116   */
117  public static <S extends Num> BiFunction<Matrix<S, ?>, Matrix<?, N1>, Matrix<S, N1>> angleMean(
118      int angleStateIdx) {
119    return (sigmas, Wm) -> angleMean(sigmas, Wm, angleStateIdx);
120  }
121}