private static Trajectory secondOrderFilter(
          int f1_length,
          int f2_length,
          double dt,
          double start_vel,
          double max_vel,
          double total_impulse,
          int length,
          IntegrationMethod integration) {
    if (length <= 0) {
      return null;
    }
    Trajectory traj = new Trajectory(length);

    Trajectory.Segment last = new Trajectory.Segment();
    // First segment is easy
    last.pos = 0;
    last.vel = start_vel;
    last.acc = 0;
    last.jerk = 0;
    last.dt = dt;

    // f2 is the average of the last f2_length samples from f1, so while we
    // can recursively compute f2's sum, we need to keep a buffer for f1.
    double[] f1 = new double[length];
    f1[0] = (start_vel / max_vel) * f1_length;
    double f2;
    for (int i = 0; i < length; ++i) {
      // Apply input
      double input = Math.min(total_impulse, 1);
      if (input < 1) {
        // The impulse is over, so decelerate
        input -= 1;
        total_impulse = 0;
      } else {
        total_impulse -= input;
      }

      // Filter through F1
      double f1_last;
      if (i > 0) {
        f1_last = f1[i - 1];
      } else {
        f1_last = f1[0];
      }
      f1[i] = Math.max(0.0, Math.min(f1_length, f1_last + input));

      f2 = 0;
      // Filter through F2
      for (int j = 0; j < f2_length; ++j) {
        if (i - j < 0) {
          break;
        }

        f2 += f1[i - j];
      }
      f2 = f2 / f1_length;

      // Velocity is the normalized sum of f2 * the max velocity
      traj.segments_[i].vel = f2 / f2_length * max_vel;

      if (integration == RectangularIntegration) {
        traj.segments_[i].pos = traj.segments_[i].vel * dt + last.pos;
      } else if (integration == TrapezoidalIntegration) {
        traj.segments_[i].pos = (last.vel
                + traj.segments_[i].vel) / 2.0 * dt + last.pos;
      }
      traj.segments_[i].x = traj.segments_[i].pos;
      traj.segments_[i].y = 0;

      // Acceleration and jerk are the differences in velocity and
      // acceleration, respectively.
      traj.segments_[i].acc = (traj.segments_[i].vel - last.vel) / dt;
      traj.segments_[i].jerk = (traj.segments_[i].acc - last.acc) / dt;
      traj.segments_[i].dt = dt;

      last = traj.segments_[i];
    }

    return traj;
  }