@Override
    public double[] smoothDerivative(double[] input) {

        if (input.length < weights.length) {
            return averageDerivativeForVeryShortTrack(input);
        }

        double[] smoothed = new double[input.length];
        int halfWindowFloored = weights.length / 2; // we want to exclude the center point

        for (int i = halfWindowFloored; i < input.length - halfWindowFloored; i++) {
            for (int windowIndex = 0; windowIndex < smoothCoeff.length; windowIndex++) {
                smoothed[i] += derivCoeff[windowIndex] * input[i + windowIndex + offsetFromWindowCenter];
            }
            smoothed[i] = smoothed[i] / timeStep;
        }

        fillSmoothDerivativeLeftSide(smoothed, halfWindowFloored);
        fillSmoothDerivativeRightSide(smoothed, halfWindowFloored);

        return smoothed;
    }
@Test
    public void Derivative_FromFakeTrackWithOutlier_RemoveBumps() {
        SavitzkyGolayFilter test = new SavitzkyGolayFilter(1.0);

        double[] input = new double[]{
            10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 13.0, // <-- outlier
            10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0
        };
        double[] actual = test.smoothDerivative(input);

        assertThat(actual.length, equalTo(input.length));

        for (int i = 0; i < input.length; i++) {
            assertThat(actual[i], closeTo(0.0, 0.5));
        }
    }