Array Segments - wrap around

I have Googled for a simple method to sum elements in a segment of a circular array. So far nothing that is better than my first attempt.

Perhaps it is best to use a real world example, one that started my quest. I have built an irrigation controller, and I record rain in mm for each hour (0-23). That was easy, I used a MRMP array.

  *(rainDuringHour210 + hourToday( *TZ300 - 1)) = mmB;  // A byte array of 24 elements 0 to 23 matching the previous hour.

In this graphic, 2 mm recorded in element 0, and 8 mm in element 6, totaling 10mm.

My irrigate controller must know the total rain during the previous x hours.

Now the tricky part...
The code I created to perform this task is flexible, in that it allows me to reuse the index calculation for other circular arrays. It handles indexes that are multiple times the size of the circular array.

In this case, I have an array, *rainDuringHour210, starting at 0, and ending at 23.

I want the indexes for each element from 05:00, back 9 hours to 21:00. Notice that crosses 0, which is not too difficult to handle.

// Constrain Index
// Returns an absoulte index to a rolling range from start + index.

int ConstrainFromIndexInt(int minimum, int maximum, int start, int index)
{
  maximum++;
  start = constrain(start,minimum,maximum); // be sure start is within the range
  if(minimum < 0)
  {
    maximum--;
    minimum--;
  }
  index = (index + start) % (maximum - minimum);  // Go round and round and land within minimum and maximum

    if(index > maximum)
  {
    index = minimum + (index - maximum);
  }

  if(index < minimum)
  {
    index = maximum + index;
  }
  return index;
}

So how do I use it?

// Return total rain in mm x10 in the previous x hours
int rainPreviousHours(int hours)
{
  hours = constrain(hours,1,24);
  int mm = 0;
  

  for(hours; hours >= 1 ; hours--)
  {
    // segments array rainDuringHour210 and wraps around at 23 -> 0

    mm = mm + *(rainDuringHour210 + ConstrainFromIndexInt(0, 23, hourToday(*TZ300), -hours)); 
  }
  return mm * 10;  // 200 series array did not save decimal.
}

Any suggestions about simplifying ConstrainFromIndexInt(

  • George

What are MRMP arrays?
http://www.mmcs.com/~gmatthews/FAV1-00012D85/S0005027B-0005027B

*(rainDuringHour210 + hourToday( *TZ300 - 1)) = mmB;

Any particular reason for this?

Why not the shorter, and more intuitive

rainDuringHour210 [hourToday( *TZ300 - 1)] = mmB;

?

(hourToday( *TZ300 - 1))[rainDuringHour210] = mmB;

No comment.

I think you're making this too difficult for yourself - what's wrong with modulo arithmetic?

In fact I do modulo ...

index = (index + start) % (maximum - minimum);  // Go round and round and land within minimum and maximum

The other parts basically prevent the calling function from ever reaching outside of the minimum to maximum.

Any particular reason for this?

Why not the shorter, and more intuitive

I use pointers to 'chunks' of memory, even single values, for MRMP array purposes.

What are MRMP arrays?
http://www.mmcs.com/~gmatthews/FAV1-00012D85/S0005027B-0005027B

I use pointers to 'chunks' of memory

But pointers still allow array syntax; even rFree's example is perfectly valid with arrays or pointers.

Still not getting the problem with arithmetic - can you explain what it is you think you want?

Sidebar:

Shouldn't this...

index = (index + start) % (maximum - minimum);  // Go round and round and land within minimum and maximum

...be this...

index = (index + start) % (maximum - minimum + 1);  // Go round and round and land within minimum and maximum
  • Brian

It may not cover all cases, but I think it boils down to:

int ConstrainFromIndexInt(int minimum, int maximum, int start, int index)
{
  const int range = abs (maximum - minimum) + 1;
  return ((constrain(start,minimum,maximum) + index) + range) % range;
}

Test setup...

void testConstrain()
{
 Serial.println("Test ");
  for(int i = -49; i <= 49; i++)
  {

    Serial.print(i);
    Serial.print(':');
    Serial.print(ConstrainFromIndexInt(0,23,0,i));
    Serial.print("   ");
  }
}

AWOL Thanks for the effort... it looks promising...
However negative indexes beyond -24 are also negative. :o

Index:returned

-49:-1 -48:0 -47:-23 -46:-22 -45:-21 -44:-20 -43:-19 -42:-18 -41:-17 -40:-16 -39:-15 -38:-14 -37:-13 -36:-12 -35:-11 -34:-10 -33:-9 -32:-8 -31:-7 -30:-6 -29:-5 -28:-4 -27:-3 -26:-2 -25:-1 -24:0 -23:1 -22:2 -21:3 -20:4 -19:5 -18:6 -17:7 -16:8 -15:9 -14:10 -13:11 -12:12 -11:13 -10:14 -9:15 -8:16 -7:17 -6:18 -5:19 -4:20 -3:21 -2:22 -1:23 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 8:8 9:9 10:10 11:11 12:12 13:13 14:14 15:15 16:16 17:17 18:18 19:19 20:20 21:21 22:22 23:23 24:0 25:1 26:2 27:3 28:4 29:5 30:6 31:7 32:8 33:9 34:10 35:11 36:12 37:13 38:14 39:15 40:16 41:17 42:18 43:19 44:20 45:21 46:22 47:23 48:0 49:1

Where-as the not-so-clean function I posted is working as desired...

-49:23 -48:0 -47:1 -46:2 -45:3 -44:4 -43:5 -42:6 -41:7 -40:8 -39:9 -38:10 -37:11 -36:12 -35:13 -34:14 -33:15 -32:16 -31:17 -30:18 -29:19 -28:20 -27:21 -26:22 -25:23 -24:0 -23:1 -22:2 -21:3 -20:4 -19:5 -18:6 -17:7 -16:8 -15:9 -14:10 -13:11 -12:12 -11:13 -10:14 -9:15 -8:16 -7:17 -6:18 -5:19 -4:20 -3:21 -2:22 -1:23 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 8:8 9:9 10:10 11:11 12:12 13:13 14:14 15:15 16:16 17:17 18:18 19:19 20:20 21:21 22:22 23:23 24:0 25:1 26:2 27:3 28:4 29:5 30:6 31:7 32:8 33:9 34:10 35:11 36:12 37:13 38:14 39:15 40:16 41:17 42:18 43:19 44:20 45:21 46:22 47:23 48:0 49:1

However negative indexes beyond -24 are also negative.

Yes, they will be.

Simple way is to add a stupidly big multiple of "range".

  const int range = abs (maximum - minimum) + 1;
  return ((constrain(start,minimum,maximum) + index) + (range * 1000)) % range;

Potentially time consuming way is to stick in a "while" loop.

const int range = abs (maximum - minimum) + 1;
int temp = constrain(start,minimum,maximum) + index;
for (; temp < 0; temp += range) {}
return temp % range;

But then you have to ask yourself what large negative numbers represent in a circular buffer, and why you would be summing ranges of more than 24 hours, when all you've stored is 24 hours' worth of data?
That would be a bit pointless. :wink:

But then you have to ask yourself what large negative numbers represent in a circular buffer, and why you would be summing ranges of more than 24 hours, when all you've stored is 24 hours' worth of data?
That would be a bit pointless

Since I wish to ruse this function, and my first attempt looked inefficient, I thought it best to seek advice.
So to say it's pointless is missing the bigger picture.

The whole point of ConstrainFromIndexInt() is to prevent any possibility of a returned index outside of the bound of the array. In that way I (we) can reuse the code (with your permission) and be sure it is safe.
There is nothing like a write beyond the bounds of your array that will cause endless grief!!!

I tested your latest function, and it woks as desired. May I use it in my code?
Thanks!

If you're keeping track of the total of a known range all the time, just add to your running total whenever you "push" a new value into the buffer, and subtract from your running total whenever you "drop" an old value from the buffer.

May I use it in my code?

Of course! It's in the public domain.

It's just a pity that we don't have 16 or 32 hour days...it would all be so much simpler. :sunglasses:

Seriously though, I'm having difficulty imagining an application that needs to go back (or forwards) around a circular buffer more than once.

But then, I'm not very imaginative :-/

@Halley

If you're keeping track of the total of a known range all the time

But that doesn't work if sometimes you want to look back over 5 hours, sometiimes over only 3.

Seriously though, I'm having difficulty imagining an application that needs to go back (or forwards) around a circular buffer more than once.

In reality... (whatever that is)... indexes are calculated. I, like most carbon based life forms create code that may occasionally overflow the index... because I can't imagine why it would ever do that!!!
Of course I never wrote any code like that ::slight_smile:

I'd rather have the index wrap the array than overwrite outside of it!!!
Then maybe it should return a flag if it does wrap? (me being cautious... again).

, like most carbon based life forms create code that may occasionally overflow the index

So, bounds-check it before you try to use it, but don't rely on the index generator to simply wrap it!

Hi All
I am new at programming and I am trying to use a bucket tip to generate interupts in order to measure the amount of rain in the last 24 hrs. I cant get the snippets above to compile. :-[

Could some kind person put a code example together please?
thanks :sunglasses:

I cant get the snippets above to compile.

The compiler is just saying "Sorry, Dave, I can't do that!"?

Could some kind person put a code example together please?

Sure. What would you like it to do?

Thanks for the reply

I'd like it to record the number of interrupts (rain bucket tips) in an hour and add that number the the previous 23 hours and delete the oldest reading. I'd like to be able to print the running total on an hourly basis.

This is what I have so far:

int pulsePin =3;
unsigned long counter = 0;
unsigned long duration = 0;
unsigned long timeout = 1000000; // in microseconds

void setup() {
pinMode(pulsePin, INPUT);
// enable the 20K pull-up resistor to steer
// the input pin to a HIGH reading.
digitalWrite(pulsePin, HIGH);
Serial.begin(9600);
Serial.println("Here we go again");
}

void loop() {
duration = pulseIn(pulsePin, HIGH, timeout);
if (duration == 0) {
Serial.print("Pulse started before the timeout.");
Serial.println("");
} else {
counter++;
//Serial.print(counter);
// Serial.print(", ");
Serial.print(duration*.000001);
Serial.println(" s");
//Serial.println("");
Serial.print (counter*.2794);
Serial.println(" mm");
}
}

You haven't got any interrupt service in that sketch.