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.system;
006
007import edu.wpi.first.math.Matrix;
008import edu.wpi.first.math.Num;
009import edu.wpi.first.math.numbers.N1;
010
011@SuppressWarnings("ClassTypeParameterName")
012public class LinearSystem<States extends Num, Inputs extends Num, Outputs extends Num> {
013  /** Continuous system matrix. */
014  @SuppressWarnings("MemberName")
015  private final Matrix<States, States> m_A;
016
017  /** Continuous input matrix. */
018  @SuppressWarnings("MemberName")
019  private final Matrix<States, Inputs> m_B;
020
021  /** Output matrix. */
022  @SuppressWarnings("MemberName")
023  private final Matrix<Outputs, States> m_C;
024
025  /** Feedthrough matrix. */
026  @SuppressWarnings("MemberName")
027  private final Matrix<Outputs, Inputs> m_D;
028
029  /**
030   * Construct a new LinearSystem from the four system matrices.
031   *
032   * @param a The system matrix A.
033   * @param b The input matrix B.
034   * @param c The output matrix C.
035   * @param d The feedthrough matrix D.
036   * @throws IllegalArgumentException if any matrix element isn't finite.
037   */
038  @SuppressWarnings("ParameterName")
039  public LinearSystem(
040      Matrix<States, States> a,
041      Matrix<States, Inputs> b,
042      Matrix<Outputs, States> c,
043      Matrix<Outputs, Inputs> d) {
044    for (int row = 0; row < a.getNumRows(); ++row) {
045      for (int col = 0; col < a.getNumCols(); ++col) {
046        if (!Double.isFinite(a.get(row, col))) {
047          throw new IllegalArgumentException(
048              "Elements of A aren't finite. This is usually due to model implementation errors.");
049        }
050      }
051    }
052    for (int row = 0; row < b.getNumRows(); ++row) {
053      for (int col = 0; col < b.getNumCols(); ++col) {
054        if (!Double.isFinite(b.get(row, col))) {
055          throw new IllegalArgumentException(
056              "Elements of B aren't finite. This is usually due to model implementation errors.");
057        }
058      }
059    }
060    for (int row = 0; row < c.getNumRows(); ++row) {
061      for (int col = 0; col < c.getNumCols(); ++col) {
062        if (!Double.isFinite(c.get(row, col))) {
063          throw new IllegalArgumentException(
064              "Elements of C aren't finite. This is usually due to model implementation errors.");
065        }
066      }
067    }
068    for (int row = 0; row < d.getNumRows(); ++row) {
069      for (int col = 0; col < d.getNumCols(); ++col) {
070        if (!Double.isFinite(d.get(row, col))) {
071          throw new IllegalArgumentException(
072              "Elements of D aren't finite. This is usually due to model implementation errors.");
073        }
074      }
075    }
076
077    this.m_A = a;
078    this.m_B = b;
079    this.m_C = c;
080    this.m_D = d;
081  }
082
083  /**
084   * Returns the system matrix A.
085   *
086   * @return the system matrix A.
087   */
088  public Matrix<States, States> getA() {
089    return m_A;
090  }
091
092  /**
093   * Returns an element of the system matrix A.
094   *
095   * @param row Row of A.
096   * @param col Column of A.
097   * @return the system matrix A at (i, j).
098   */
099  public double getA(int row, int col) {
100    return m_A.get(row, col);
101  }
102
103  /**
104   * Returns the input matrix B.
105   *
106   * @return the input matrix B.
107   */
108  public Matrix<States, Inputs> getB() {
109    return m_B;
110  }
111
112  /**
113   * Returns an element of the input matrix B.
114   *
115   * @param row Row of B.
116   * @param col Column of B.
117   * @return The value of the input matrix B at (i, j).
118   */
119  public double getB(int row, int col) {
120    return m_B.get(row, col);
121  }
122
123  /**
124   * Returns the output matrix C.
125   *
126   * @return Output matrix C.
127   */
128  public Matrix<Outputs, States> getC() {
129    return m_C;
130  }
131
132  /**
133   * Returns an element of the output matrix C.
134   *
135   * @param row Row of C.
136   * @param col Column of C.
137   * @return the double value of C at the given position.
138   */
139  public double getC(int row, int col) {
140    return m_C.get(row, col);
141  }
142
143  /**
144   * Returns the feedthrough matrix D.
145   *
146   * @return the feedthrough matrix D.
147   */
148  public Matrix<Outputs, Inputs> getD() {
149    return m_D;
150  }
151
152  /**
153   * Returns an element of the feedthrough matrix D.
154   *
155   * @param row Row of D.
156   * @param col Column of D.
157   * @return The feedthrough matrix D at (i, j).
158   */
159  public double getD(int row, int col) {
160    return m_D.get(row, col);
161  }
162
163  /**
164   * Computes the new x given the old x and the control input.
165   *
166   * <p>This is used by state observers directly to run updates based on state estimate.
167   *
168   * @param x The current state.
169   * @param clampedU The control input.
170   * @param dtSeconds Timestep for model update.
171   * @return the updated x.
172   */
173  @SuppressWarnings("ParameterName")
174  public Matrix<States, N1> calculateX(
175      Matrix<States, N1> x, Matrix<Inputs, N1> clampedU, double dtSeconds) {
176    var discABpair = Discretization.discretizeAB(m_A, m_B, dtSeconds);
177
178    return (discABpair.getFirst().times(x)).plus(discABpair.getSecond().times(clampedU));
179  }
180
181  /**
182   * Computes the new y given the control input.
183   *
184   * <p>This is used by state observers directly to run updates based on state estimate.
185   *
186   * @param x The current state.
187   * @param clampedU The control input.
188   * @return the updated output matrix Y.
189   */
190  @SuppressWarnings("ParameterName")
191  public Matrix<Outputs, N1> calculateY(Matrix<States, N1> x, Matrix<Inputs, N1> clampedU) {
192    return m_C.times(x).plus(m_D.times(clampedU));
193  }
194
195  @Override
196  public String toString() {
197    return String.format(
198        "Linear System: A\n%s\n\nB:\n%s\n\nC:\n%s\n\nD:\n%s\n",
199        m_A.toString(), m_B.toString(), m_C.toString(), m_D.toString());
200  }
201}