Normalizing or Smoothing GPS Speed controller

I have a GPS that I am using to get the speed of an implement and then control come solenoids based on the speed and a switch case programmed into the ardunio. I am trying to smooth the GPS responses by averaging the numbers. I understand that I could simply make them and int or take less readings but .25 MPH matters to a point (660 mistakes per hour which is within tolerance).

I have looked at endless sketches and read as many things that DEV has had to say about GPS.

I found this code in one of him examples:
static uint16_t averageKnots = 0;
averageKnots = (averageKnots * 3) / 4 + knots;
knots = averageKnots / 4;

It isnt making mathematical sense to me. Can someone explain this.

Are you sure you copied it correctly?
It looks to be in the wrong order.

I will track it down again. I feel like I have it copied correctly.

Normally, you'd add a quarter of the current sample to three quarters of the rolling average. (Or an eighth to seven eighths...etc)

The code is wrong. It should be

averageKnots = (averageKnots * 3 + knots) / 4

The coder may have had in mind something like this:

      rollingSumKnots = rollingSumKnots * 3 / 4 + knots
     averageKnots = rollingSumKnots / 4

The 4 comes in because 1 + 3/4 + (3/4)^2 + (3/4)^3 + ... = 4

I havent found his exact reference but I di find another person using it.

void displayspeed() { if (fix.valid.speed) { uint16_t knots = fix.spd.whole;
static uint16_t averageKnots = 0;
averageKnots = (averageKnots * 3) / 4  + knots;
knots        = averageKnots / 4;

// Convert to other units
uint16_t mph = (knots * 1151L) / 1000L;
Serial.println(knots);
displaySpeed(mph); 

}
}

I feel like something isn't quite write with the code. It seems backwards a bit but I am the newbie and you all have tons of experience so I'm asking before being told Im a know it all......

Learning here. Is there a simple example of rollingsum in an example sketch somewhere that I can review. Thanks in advance!

The snippet may be out of context, but it makes no sense.

Here is the general approach for a moving average (digital low pass or exponential filter) with fixed fraction alpha = 1/4 applied to the incoming data. You can choose any fraction between 0 and 1.0. Filter response is slower, with lower values of alpha.

static float filtered_data = 0.0;  //arbitrary starting value
float new_data;
float alpha = 0.25;

// in loop()
new_data = get_sample();
filtered_data = (1.0 - alpha)*filtered_data + alpha*new_data;
Serial.println(filtered_data);

The code can be rewritten to reduce the number of calculations, but then it becomes less obvious how it works.

The code is all in my message (except, of course, "rollingSumKnots" has to be initialized to 0, as in the sketch you gave.

My two versions do the same thing. What I am calling rollingSumKnots is the sum of all the speeds up to the present speed, with past speeds discounted by 3/4 at each step. That is

rollingSumKnots = 1 * S(0) + 3/4 * S(-1) + (3/4)^2 * S(-2) + (3/4)^3 * S(-3) + ...

The sum of the weights 1, 3/4, (3/4)^2, ... is 4, so the average is rollingSumKnots/4.

rolling average code.

That's a pretty wasteful and limited implementation:

It would be much more efficient to keep a running sum, rather than adding all elements on each iteration. The modulo operation to wrap around is unnecessarily expensive as well.

I prefer this one: Arduino Filters: SimpleMovingAverage.ino

It makes more sense if you change the variable name:

  static uint16_t averageKnotsTimesFour = 0;
  averageKnotsTimesFour = (averageKnotsTimesFour * 3) / 4 + knots;
  knots = averageKnotsTimesFour / 4;

It may help understanding to make it a little more verbose:

  static uint16_t averageKnotsTimesFour = 0;
  uint16_t previousAverageKnots = averageKnotsTimesFour / 4;
  averageKnotsTimesFour = (previousAverageKnots * 3) + knots;
  knots = averageKnotsTimesFour / 4;

This verbose version is a little less precise because it does the "/ 4" before the "* 3" and thus loses some fractional bits.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.