4

I receive a buffer of data periodically that contains a number of values that are a fixed distance in time apart. I need to differentiate them. It is soo long since I did calculus at school ....

What I have come up with is this:

function DifferentiateBuffer(
  ABuffer: TDoubleDynArray; AVPS: integer): TDoubleDynArray;
var
  i: integer;
  dt, dy: double;
begin
  if (AVPS = 0) then exit;

  // calc the delta time
  dt := 1 / AVPS;

  // run through the whole buffer
  for i := 0 to high(ABuffer) do begin
    if (i = 0) then
      if (IsNan(FLastDiffValue) = false) then
        dy := ABuffer[0] - FLastDiffValue
      else
        dy := ABuffer[0]
    else
      dy := Abuffer[i] - ABuffer[i -1];

    result[i] := dy / dt
  end;

  // remember the last value for next time
  FLastDiffValue := ABuffer[high(ABuffer)];
end;

AVPS is values per second. A typical value for this would be between 10 and 100. The length of the buffers would typically be 500 to 1000 values.

I call the buffer time after time with new data which is continuous with the previous block of data, hence keeping the last value of the block for next time. That last value is set to NAN in a constructor.

Is what I have done correct? ie, will it differentiate the values properly?

I also need to integrate similar data ... what is that likely to look like?

user745323
  • 153
  • 1
  • 1
  • 8
  • 1
    If you initialize `FLastDiffValue` to zero, then you wouldn't have to check whether it's NaN. You could unconditionally set `dy := ABuffer[0] - FLastDiffValue`. – Rob Kennedy Sep 27 '13 at 16:21
  • Thanks, Rob. The problem with that is that zero maybe way off. I did consider getting the average of the first 3 or 5 values and using that as the first value, it would be a better guess than zero, but would still need a test. I may still do that, but skipping the first value seems a good solution, too. – user745323 Sep 30 '13 at 08:24

1 Answers1

4

I cannot find any faults in that.

The first value returned, on the first call will be (ABuffer[0] - 0.0) / dt which is based on the assumption that the signal starts with a zero. I presume that is what you intend.

Now, rather than asking the Stack Overflow community to check your code, you can do much better yourself. You should test the code to prove it is accurate. Test it using a unit testing framework, for example DUnitX. Feed the function values for which you can predict the output. For example, feed it values from y = x2, or y = sin(x).

The other great benefit of writing tests is that they can be executed again and again. As you develop your code you run the risk of introducing faults. The code might be correct today, but who knows whether it will still be correct after the modification you make tomorrow. If you have a strong test in place, you can defend against faults introduced during maintenance.

One comment on style is that you should not ever test = false or = true. The if statement operates on boolean expressions and so comparing against a boolean value is always rather pointless. I would write your test like this:

if not IsNan(FLastDiffValue) then
  dy := ABuffer[0] - FLastDiffValue
else
  dy := ABuffer[0]

or like this:

if IsNan(FLastDiffValue) then
  dy := ABuffer[0]
else
  dy := ABuffer[0] - FLastDiffValue
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thank you David, and thanks for the suggestion on how to test it. Regarding my boolean test style, I often use 'condition = false' rather than 'not condition' as it reads better - ie I understand it easier when I'm reading through code. I rarely, if ever, use '= true'. Would you care to show (in pseudo code) what integration might look like? – user745323 Sep 27 '13 at 15:55
  • What are you doing here, some PID? Anyway, it doesn't look like you need me to show you how to do integration. You managed differentiation, you can manage integration. Write some good tests and you'll be fine. Exactly how you do integration depends on algo choice. Trapezium, Simpson, higher order. Trapezium is simplest. You can use almost identical function design as in your diff method but sum trapezium area instead of doing finite difference. Once you get into the routine of testing, you'll find it easy to write and verify the code. – David Heffernan Sep 27 '13 at 16:00
  • I'm converting between velocity and acceleration data. Thanks for the encouragement and the suggestions. I can imagine what the trapezium method is, so I will have a go at that first. – user745323 Sep 27 '13 at 16:08
  • Take a look at these wiki pages: http://en.wikipedia.org/wiki/Trapezium_Rule http://en.wikipedia.org/wiki/Simpson's_rule I also have to repeat what I said about testing. Writing tests is what lets you make progress when coding. I've spent all week writing tests. It's wonderful to do. And at the end of it I'm in a position to write loads and loads to code!! – David Heffernan Sep 27 '13 at 16:09