Add python wrapper for trajectory.

Reformatted several files and added some useful comments on how to use
the python wrappers.

Change-Id: I9fd89977e0d4f16fa24d0e3b3b7c6657df865b32
diff --git a/frc971/control_loops/drivetrain/BUILD b/frc971/control_loops/drivetrain/BUILD
index 8f98953..aee3a37 100644
--- a/frc971/control_loops/drivetrain/BUILD
+++ b/frc971/control_loops/drivetrain/BUILD
@@ -274,7 +274,11 @@
     deps = [
         ":distance_spline",
         ":spline",
+        ":trajectory",
+        "//aos/logging:implementations",
+        "//aos/network:team_number",
         "//third_party/eigen",
+        "//y2019/control_loops/drivetrain:drivetrain_base",
     ],
     linkshared=True,
 )
diff --git a/frc971/control_loops/drivetrain/libspline.cc b/frc971/control_loops/drivetrain/libspline.cc
index 117290d..0d14f66 100644
--- a/frc971/control_loops/drivetrain/libspline.cc
+++ b/frc971/control_loops/drivetrain/libspline.cc
@@ -1,62 +1,66 @@
 #include <vector>
+#include <string>
 
 #include "Eigen/Dense"
 
+#include "aos/logging/implementations.h"
+#include "aos/network/team_number.h"
 #include "frc971/control_loops/drivetrain/distance_spline.h"
 #include "frc971/control_loops/drivetrain/spline.h"
+#include "frc971/control_loops/drivetrain/trajectory.h"
+#include "y2019/control_loops/drivetrain/drivetrain_base.h"
 
 namespace frc971 {
 namespace control_loops {
 namespace drivetrain {
 
 extern "C" {
-  NSpline<6>* NewSpline(double x[6], double y[6]) {
+  // Based on spline.h
+  NSpline<6> *NewSpline(double x[6], double y[6]) {
     return new NSpline<6>((::Eigen::Matrix<double, 2, 6>() << x[0], x[1], x[2],
                            x[3], x[4], x[5], y[0], y[1], y[2], y[3], y[4],
                            y[5]).finished());
   }
 
-  void deleteSpline(NSpline<6>* spline) {
-    delete spline;
-  }
+  void deleteSpline(NSpline<6> *spline) { delete spline; }
 
-  void SplinePoint(NSpline<6>* spline, double alpha, double* res) {
-    double* val = spline->Point(alpha).data();
+  void SplinePoint(NSpline<6> *spline, double alpha, double *res) {
+    double *val = spline->Point(alpha).data();
     res[0] = val[0];
     res[1] = val[1];
   }
 
-  void SplineDPoint(NSpline<6>* spline, double alpha, double* res) {
-    double* val = spline->DPoint(alpha).data();
+  void SplineDPoint(NSpline<6> *spline, double alpha, double *res) {
+    double *val = spline->DPoint(alpha).data();
     res[0] = val[0];
     res[1] = val[1];
   }
 
-  void SplineDDPoint(NSpline<6>* spline, double alpha, double* res) {
-    double* val = spline->DDPoint(alpha).data();
+  void SplineDDPoint(NSpline<6> *spline, double alpha, double *res) {
+    double *val = spline->DDPoint(alpha).data();
     res[0] = val[0];
     res[1] = val[1];
   }
 
-  void SplineDDDPoint(NSpline<6>* spline, double alpha, double* res) {
-    double* val = spline->DDDPoint(alpha).data();
+  void SplineDDDPoint(NSpline<6> *spline, double alpha, double *res) {
+    double *val = spline->DDDPoint(alpha).data();
     res[0] = val[0];
     res[1] = val[1];
   }
 
-  double SplineTheta(NSpline<6>* spline, double alpha) {
+  double SplineTheta(NSpline<6> *spline, double alpha) {
     return spline->Theta(alpha);
   }
 
-  double SplineDTheta(NSpline<6>* spline, double alpha) {
+  double SplineDTheta(NSpline<6> *spline, double alpha) {
     return spline->DTheta(alpha);
   }
 
-  double SplineDDTheta(NSpline<6>* spline, double alpha) {
+  double SplineDDTheta(NSpline<6> *spline, double alpha) {
     return spline->DDTheta(alpha);
   }
 
-  void SplineControlPoints(NSpline<6>* spline, double* x, double* y) {
+  void SplineControlPoints(NSpline<6> *spline, double *x, double *y) {
     auto points = spline->control_points();
     // Deal with incorrectly strided matrix.
     for (int i = 0; i < 6; ++i) {
@@ -65,7 +69,8 @@
     }
   }
 
-  DistanceSpline* NewDistanceSpline(Spline** splines, int count) {
+  // Based on distance_spline.h
+  DistanceSpline *NewDistanceSpline(Spline **splines, int count) {
     ::std::vector<Spline> splines_;
     for (int i = 0; i < count; ++i) {
       splines_.push_back(*splines[i]);
@@ -73,9 +78,7 @@
     return new DistanceSpline(::std::vector<Spline>(splines_));
   }
 
-  void deleteDistanceSpline(DistanceSpline* spline) {
-    delete spline;
-  }
+  void deleteDistanceSpline(DistanceSpline *spline) { delete spline; }
 
   void DistanceSplineXY(DistanceSpline *spline, double distance, double *res) {
     double *val = spline->XY(distance).data();
@@ -116,6 +119,89 @@
   double DistanceSplineLength(DistanceSpline *spline) {
     return spline->length();
   }
+
+  // Based on trajectory.h
+  Trajectory *NewTrajectory(DistanceSpline *spline, double vmax,
+                            int num_distance) {
+    return new Trajectory(
+        spline, ::y2019::control_loops::drivetrain::GetDrivetrainConfig(), vmax,
+        num_distance);
+  }
+
+  void deleteTrajectory(Trajectory *t) { delete t; }
+
+  void TrajectorySetLongitudalAcceleration(Trajectory *t, double accel) {
+    t->set_longitudal_acceleration(accel);
+  }
+
+  void TrajectorySetLateralAcceleration(Trajectory *t, double accel) {
+    t->set_lateral_acceleration(accel);
+  }
+
+  void TrajectorySetVoltageLimit(Trajectory *t, double limit) {
+    t->set_voltage_limit(limit);
+  }
+
+  void TrajectoryLimitVelocity(Trajectory *t, double start, double end,
+                               double max) {
+    t->LimitVelocity(start, end, max);
+  }
+
+  void TrajectoryPlan(Trajectory *t) { t->Plan(); }
+
+  double TrajectoryLength(Trajectory *t) { return t->length(); }
+
+  int TrajectoryGetPathLength(Trajectory *t) { return t->plan().size(); }
+
+  // This assumes that res is created in python to be getPathLength() long.
+  // Likely to SEGFAULT otherwise.
+  void Distances(Trajectory *t, double *res) {
+    const ::std::vector<double> &distances = t->Distances();
+    ::std::memcpy(res, distances.data(), sizeof(double) * distances.size());
+  }
+
+  double TrajectoryDistance(Trajectory *t, int index) {
+    return t->Distance(index);
+  }
+
+  // This assumes that res is created in python to be getPathLength() long.
+  // Likely to SEGFAULT otherwise.
+  void TrajectoryGetPlan(Trajectory *t, double *res) {
+    const ::std::vector<double> &velocities = t->plan();
+    ::std::memcpy(res, velocities.data(), sizeof(double) * velocities.size());
+  }
+
+  // Time in in nanoseconds.
+  ::std::vector<::Eigen::Matrix<double, 3, 1>> *TrajectoryGetPlanXVAPtr(
+      Trajectory *t, int dt) {
+    return new ::std::vector<::Eigen::Matrix<double, 3, 1>>(
+        t->PlanXVA(::std::chrono::nanoseconds(dt)));
+  }
+
+  void TrajectoryDeleteVector(
+      ::std::vector<::Eigen::Matrix<double, 3, 1>> *vec) {
+    delete vec;
+  }
+
+  int TrajectoryGetVectorLength(
+      ::std::vector<::Eigen::Matrix<double, 3, 1>> *vec) {
+    return vec->size();
+  }
+
+  void TrajectoryGetPlanXVA(::std::vector<::Eigen::Matrix<double, 3, 1>> *vec,
+                            double *X, double *V, double *A) {
+    for (size_t i = 0; i < vec->size(); ++i) {
+      X[i] = (*vec)[0][0];
+      V[i] = (*vec)[0][1];
+      A[i] = (*vec)[0][2];
+    }
+  }
+
+  // Util
+  void SetUpLogging() {
+    ::aos::logging::Init();
+    ::aos::network::OverrideTeamNumber(971);
+  }
 }
 
 }  // namespace drivetrain
diff --git a/frc971/control_loops/python/lib_spline_test.py b/frc971/control_loops/python/lib_spline_test.py
index 2c77d62..4e725a0 100644
--- a/frc971/control_loops/python/lib_spline_test.py
+++ b/frc971/control_loops/python/lib_spline_test.py
@@ -5,7 +5,7 @@
 from numpy.testing import *
 import unittest
 
-from libspline import Spline, DistanceSpline
+from libspline import Spline, DistanceSpline, Trajectory
 
 class TestSpline(unittest.TestCase):
     def testSimpleSpline(self):
@@ -23,5 +23,29 @@
         dSpline = DistanceSpline([spline])
         assert_almost_equal(dSpline.Length(), 5 * math.sqrt(2))
 
+class TestTrajectory(unittest.TestCase):
+    def testTrajectory(self):
+        points = np.array([[1.0, 2.0, 3.0, 4.0, 5.0, 5.0],
+                           [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]])
+        spline = Spline(points)
+        dSpline = DistanceSpline([spline])
+        trajectory = Trajectory(dSpline)
+        trajectory.Plan()
+        plan = trajectory.GetPlanXVA(5.05*1e-3)
+        assert(plan.shape == (3, 611))
+
+    def testLimits(self):
+        """ A plan with a lower limit should take longer. """
+        points = np.array([[1.0, 2.0, 3.0, 4.0, 5.0, 5.0],
+                           [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]])
+        spline = Spline(points)
+        dSpline = DistanceSpline([spline])
+        trajectory = Trajectory(dSpline)
+        trajectory.LimitVelocity(0, trajectory.Length(), 3)
+        trajectory.Plan()
+        plan = trajectory.GetPlanXVA(5.05*1e-3)
+        self.assertEqual(plan.shape, (3, 644))
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/frc971/control_loops/python/libspline.py b/frc971/control_loops/python/libspline.py
index ca20f61..fd44cb4 100644
--- a/frc971/control_loops/python/libspline.py
+++ b/frc971/control_loops/python/libspline.py
@@ -24,10 +24,18 @@
 libSpline.DistanceSplineDThetaDt.restype = ct.c_double
 libSpline.DistanceSplineDDTheta.restype = ct.c_double
 libSpline.DistanceSplineLength.restype = ct.c_double
+libSpline.TrajectoryLength.restype = ct.c_double
+libSpline.TrajectoryDistance.resType = ct.c_double
 
+# Required for trajectory
+libSpline.SetUpLogging()
 
 class Spline:
-    """A wrapper around spline.h/cc through libspline.cc."""
+    """
+    A wrapper around spline.h/cc through libspline.cc.
+    The functions return values parameterized by alpha, a number that varies
+    between 0 and 1 along the length of the spline.
+    """
 
     def __init__(self, points):
         assert points.shape == (2, 6)
@@ -83,18 +91,22 @@
     def ControlPoints(self):
         return self.__points
 
-    def Spline(self):
+    def GetSplinePtr(self):
         return self.__spline
 
 
 class DistanceSpline:
-    """A wrapper around distance_spline.h/cc through libdistancespline.cc."""
+    """
+    A wrapper around distance_spline.h/cc through libspline.cc.
+    The functions return values parameterized by the distance along the spline,
+    starting at 0 and and ending at the value returned by Length().
+    """
 
     def __init__(self, splines):
         self.__spline = None
         spline_ptrs = []
         for spline in splines:
-            spline_ptrs.append(spline.Spline())
+            spline_ptrs.append(spline.GetSplinePtr())
         spline_ptrs = np.array(spline_ptrs)
 
         spline_array = np.ctypeslib.as_ctypes(spline_ptrs)
@@ -141,3 +153,89 @@
 
     def Length(self):
         return libSpline.DistanceSplineLength(self.__spline)
+
+    def GetSplinePtr(self):
+        return self.__spline
+
+
+class Trajectory:
+    """A wrapper around trajectory.h/cc through libspline.cc."""
+
+    def __init__(self, distance_spline, vmax=10, num_distance=0):
+        self.__trajectory = libSpline.NewTrajectory(
+            distance_spline.GetSplinePtr(), ct.c_double(vmax), num_distance)
+
+    def __del__(self):
+        libSpline.deleteTrajectory(self.__trajectory)
+
+    def SetLongitudalAcceleration(self, accel):
+        libSpline.TrajectorySetLongitualAcceleration(self.__trajectory,
+                                                     ct.c_double(accel))
+
+    def SetLateralAcceleration(self, accel):
+        libSpline.TrajectorySetLateralAcceleration(self.__trajectory,
+                                                   ct.c_double(accel))
+
+    def SetVoltageLimit(self, limit):
+        libSpline.TrajectorySetVoltageLimit(self.__trajectory,
+                                            ct.c_double(limit))
+
+    def LimitVelocity(self, start_distance, end_distance, max_vel):
+        libSpline.TrajectoryLimitVelocity(self.__trajectory,
+                                          ct.c_double(start_distance),
+                                          ct.c_double(end_distance),
+                                          ct.c_double(max_vel))
+
+    def Plan(self):
+        """
+        Call this to compute the plan, if any of the limits change, a new plan
+        must be generated.
+        """
+        libSpline.TrajectoryPlan(self.__trajectory)
+
+    def Length(self):
+        return libSpline.TrajectoryLength(self.__trajectory)
+
+    def Distance(self, index):
+        return libSpline.TrajectoryDistance(self.__trajectory, index)
+
+    def Distances(self):
+        """
+        Returns an array of distances used to compute the plan. The linear
+        distance between each distance is equal.
+        """
+        path_length = libSpline.TrajectoryGetPathLength(self.__trajectory)
+        distances = numpy.zeros(path_length)
+        libSpline.TrajectoryDistances(self.__trajectory,
+                                      np.ctypeslib.as_ctypes(distances))
+        return distances
+
+    def GetPlan(self):
+        """
+        Returns the plan as an array of velocities matched to each distance
+        from Distances.
+        """
+        path_length = libSpline.TrajectoryGetPathLength(self.__trajectory)
+        velocities = numpy.zeros(path_length)
+        libSpline.TrajectoryGetPlan(self.__trajectory,
+                                    np.ctypeslib.as_ctypes(velocities))
+        return velocities
+
+    def GetPlanXVA(self, dt):
+        """
+        dt is in seconds
+        Returns the position, velocity, and acceleration as a function of time.
+        This is returned as a 3xN numpy array where N is proportional to how
+        long it takes to run the path.
+        This is slow so don't call more than once with the same data.
+        """
+        XVAPtr = libSpline.TrajectoryGetPlanXVAPtr(self.__trajectory, int(dt*1e9))
+        XVALength = libSpline.TrajectoryGetVectorLength(XVAPtr)
+        X = np.zeros(XVALength)
+        V = np.zeros(XVALength)
+        A = np.zeros(XVALength)
+        libSpline.TrajectoryGetPlanXVA(XVAPtr, np.ctypeslib.as_ctypes(X),
+                                       np.ctypeslib.as_ctypes(V),
+                                       np.ctypeslib.as_ctypes(A))
+        libSpline.TrajectoryDeleteVector(XVAPtr)
+        return np.vstack([X, V, A])