Rotative array

Hi,
in a my hold post i asked "how write a code for FIFO buffer". this wasn't correct because i doesn't need of FIFO buffer but of "rotative array".

I know that it isn't correct but my problem isn't resolved....

My (hold) problem is this below:

I have two array where will put samples.
-pressure;
-time;

So, i will filter my samples with "least squares" method. For this, in the vector of time, i need that the first element of time vector is ever "0". When i shift this vector to left, the second element will be "0" and will be put in the first cell.
However i need de temporal distance between the first element (that will be "0") and other elements!

#define n_samples 50 //number of samples
float time_vect[n_samples]; //vector of time
float first_value;

void setup() {
//[...] setup

}

void loop() {
for(int c = 0; c< n_samples; c++)  time_vect[c] = time_vect[c+1];
time_vect[n_samples-1] = millis()-time_vect[0];
first_value =  time_vect[0];
for(int c = 0; c< n_samples-2; c++)  time_vect[c] -first_value;


//[...] program
}

in this mode, time_vect[0] will be ever the tempora origin and, for example, temp_vect[8] will be the temporal distance between time_vect[0] and time_vect[8]. I'm not shure that this is correct....

is it a good method for my task??

I'm again, completely lost what you want to do...

But if I read between the lines it sounds to me you want to store samples for a defined window of time? And do stuff like averaging? Correct?

I have to agree with septillion, your post makes no sense at all. A circular buffer is a very simple construction:

#define BUFFER_SIZE 10
byte buffer[BUFFER_SIZE];
int buffer_start = 0;

int getIndex(int index, bool inc_start)
{
  index += buffer_start;
  if (index < 0) index += BUFFER_SIZE;
  else if (index >= BUFFER_SIZE) index -= BUFFER_SIZE;
  if (inc_start)
  {
    buffer_start++;
    if (buffer_start >= BUFFER_SIZE) buffer_start -= BUFFER_SIZE;
  }
  return index;
}

byte getElement(int index, bool inc_start = false)
{
  return buffer[getIndex(index, inc_start)];
}

void setElement(int index, byte element, bool inc_start = false)
{
  buffer[getIndex(index, inc_start)] = element;
}

Ideally you would encapsulate it in a struct or class for convenience.

EDIT: Corrected a brain fart, keeping buffer_start in range is nice :slight_smile:

septillion:
But if I read between the lines it sounds to me you want to store samples for a defined window of time? And do stuff like averaging? Correct?

"to define window of time" is my target!

I don't speack english, i just learning now... i ask for a litle patience!
Below, i try to explane better my question:

I take samples of pressure and relative times (pressure X at time Y). I try to find the "climb rate" (it's a variometer...).
Now, if you draw a graph with these variables (X,Y), you can draw a "parametric line" that linearize (approximates) the data. The angoular coefficient:
Y= aX+C (straight line) => I need of "a"!

so, we can use the "least squares method" for this scope (there ise a formula...)

my "temp_vect[n_samples]" is my "X" array (and Pressure[n_samples) is "Y" array). n_samples is just the "number of samples".

Every cycle i take a new sample of pressure and time and will store these in the "n_samples-1" cell (respectivey pressure[ n_samples-1] and temp_vect[n_samples-1])

at this point i lost the first element of vectors (because there is a new data), but i would like that the origin of time is ever "0".

do you understand?

septillion:
I'm again, completely lost what you want to do...

But if I read between the lines it sounds to me you want to store samples for a defined window of time? And do stuff like averaging? Correct?

Danois90:
I have to agree with septillion, your post makes no sense at all. A circular buffer is a very simple construction:

#define BUFFER_SIZE 10

byte buffer[BUFFER_SIZE];
int buffer_start = 0;

int getIndex(int index, bool inc_start)
{
  index += buffer_start;
  if (index < 0) index += BUFFER_SIZE;
  else if (index >= BUFFER_SIZE) index -= BUFFER_SIZE;
  if (inc_start) buffer_start++;
  return index;
}

byte getElement(int index, bool inc_start = false)
{
  return buffer[getIndex(index, inc_start)];
}

void setElement(int index, byte element, bool inc_start = false)
{
  buffer[getIndex(index, inc_start)] = element;
}




Ideally you would encapsulate it in a struct or class for convenience.

i will try tis code..... thanks!

Wouldn't it be easier to keep a running average of the slope between the last two points?
Depending on the application, this might give better results than filling an entire buffer to use with linear regression.

You have to provide a detailed explanation of what you want to do. What's the process? What sensors are you using? What do you want to calculate? At what interval? What does your measured data look like? Can you show an example plot/spreadsheet?

Pieter

If you only need the most recent climb rate, you could do it quite simple:

#define MEASURE_TIME 5000

unsigned long lastTime = 0, loopMs;
int lastValue = 0, temp;

void loop()
{
  loopMs = millis();
  unsigned int elapsed = loopMs - lastTime;
  if (elapsed >= MEASURE_TIME)
  {
    temp = readSensor();
    float progress = temp - lastValue;
    lastValue = temp;
    lastTime = loopMs;

    //"progress" is now the change in sensor reading which has
    //occurred during the last "elapsed" milliseconds. You can
    //calculate per-second change with:
    //progress = (progress / elapsed) * 1000.0f
    //Or per-minute change with
    //progress = (progress / elapsed) * 60000.0f
    //and so on. 
  }
}

Danois90:
If you only need the most recent climb rate[...]

Your code is work, but it isn't "satisfactory". In my group of paragliders there are someone that built a variometer whit this algoritm, but, we think that it isn't a lot of "accurate".

We think that with more samples and the "least squares method" (or Kalman filter) it will be better....
the accuracy of sensor is good, we working on the firmwere for get better the device.

in the "1.0" version we took two samples:
first_samples at time 1;
second_samples at time2;

with these two samples and "time_between_samples" we tryed to "linearized" measure.... no good!

Accuracy would depend on the sensor and on the implementation of "readSensor".

Danois90:
Accuracy would depend on the sensor and on the implementation of "readSensor".

Yes but the "single" samlple is a lot of accuracyed!
If you read only barometric pressure every (for example) 100 ms, the value in very god. The Sparkfun library (i'm using GY-BME280 and this library) is work very well!

i repeat, for try to get better the device, i would try a filter.....

i can use the elapsed time from start of program ( millis()) but i don't like because the time value will grow.... it shouldn't be a big problem because the "float" variable has 10^38 bits and they will go in overflow after some day!

i would use only the elapsed time from the first samples of array, not the elepsed time from the start of program!

they will go in overflow after some day!

49 and a bit days

Matteo1991:
because the "float" variable has 10^38 bits

Uhm, no! A float is 4 bytes aka 32-bits and has 6 digits (not the same as decimal places!) of accuracy at best. If you want accurate, stay with integers!

And as for millis(), yes, it's growing, but so it the time on your clock. And do you find that a problem for accuracy?

septillion:
Uhm, no! A float is 4 bytes aka 32-bits and has 6 digits (not the same as decimal places!) of accuracy at best. If you want accurate, stay with integers!

yes! 10^38 is the bigest number (10^38-1)! i was wrong to write!

accuracy is a propriety of sensor, not of the program! i doesn't explane very clean.

so, i would try this filter because i'm shure that will be better!

Matteo1991:
yes! 10^38 is the bigest number (10^38-1)!

No, biggest number is 3.4028235E+38. And you only have 2^31 steps to get there from -3.4028235E+38. Which is A LOT of orders of magnitude less. That's why a float is only accurate up to 6 digits.

For example, try to store the value of 123456789 in a float and see what it holds :wink:

Matteo1991:
accuracy is a propriety of sensor, not of the program!

It's a combination of both. You can kill accuracy pretty easy for example by trunking integers of incrementing a float beyond 6 digits.

Matteo1991:
i would use only the elapsed time from the first samples of array, not the elepsed time from the start of program!

It seems like you understand most of the maths; your problem is just making the code do this.

First, store times in an array of unsigned long. Do the subtraction to subtract the "first" element in the array in unsigned long. This removes the "overflow problem." Then convert to float for the rest of the calculations.

So what is the "first" element in a circular buffer? The stupid way to make this kind of buffer is to insert each new value at the end of the array by first copying position 2 to 1, 3 to 2, 4 to 3... All those copies take a lot of time. So instead of moving the values, you just move the pointer which points to the last place you inserted a value.

So think about the state of the buffer after it has filled up. You have pushed your pointer all the way to the end and back to the start several times. The last place you inserted a new value was position 5. Where is the oldest (first) value in the buffer? That is at position 6.

You just have to be careful when you add 1 to the pointer that you may have been at the end of the buffer and adding 1 goes off the end. In that case, your first value is at position 0.

Why don't you just read an average from the sensor?

#define SAMPLE_COUNT 5
#define SAMPLE_DELAY 50

float avgPressure()
{
  float res = 0;
  for (byte i = 0; i < SAMPLE_COUNT; i++)
  {
    res += sensor.readFloatPressure();
    delay(SAMPLE_DELAY);
  }
  return res / SAMPLE_COUNT;
}

If you combine it with the code in #5 and replace "readSensor()" with "avgPressure()" you should get som quite stable readings.

Because the important quantity being measured is the change over time. Oversampling (taking many measurements at about the same time) helps but it is not the whole solution.

MorganS:
First, store times in an array of unsigned long. Do the subtraction to subtract the "first" element in the array in unsigned long. This removes the "overflow problem." Then convert to float for the rest of the calculations.

like this below?

#define n_samples 50
unsigned long vect_time_1[n_samples];
float vect_time_2[n_samples];

void setup()
{
....................................
}
void loop()
{
............................................

vect_time_1[n_samples-1] = millis();

for(int c = 0; c<n_samples-1; c++) {
float vect_time_2[c] = vect_time_1[c]- vect_time_1[0];
}

............................................
}

and i use vect_time_2[] for all calculations...

so, i don't understand the question of "pointers" : the "for" cycle for the shift of array, takes a long time?

i don't think that this is a problem because i measure the elapset time between two measure. this is a "real" (more or less) measure of time (i don't like "millis()", probably should be better a timer interrupt....). The time spent of the shift shouldn't be a big problem.

This could be a big problem if i use a delay: in this case, the elapsed time is fixed: but don't know the delay that the program introduced....

Tell me if my considerations are correct.....

Every time you gather a sample, you are moving around with 49*4=196 bytes for absolutely no reason. That is why you are suggested to use a circular array which will prevent this. The methodology has been explained in code earlier.

As I can understand, you are trying to make a device which can be used to measure altitude / rate of descent for paragliders. In order to create a smooth curve / straight line, you want to use the "least square" equation. If this is the case, you will need an entire data-set from lift off to touch down or else it would (IMO) not make much sense :slight_smile:

Danois90:
If this is the case, you will need an entire data-set from lift off to touch down or else it would (IMO) not make much sense :slight_smile:

How much is a byte? ;D
this will get better the efficiency against wind! ahahahah

I understand the remark.
i will try to "lighten" ( :smiley: ) the firmware.....

thanks!

float vect_time_2[c] = vect_time_1[c]- vect_time_1[0];

This is a fundamental misunderstanding of how a circular buffer works. The oldest sample stored is not in position [ 0 ]

If re-reading my description in reply #13 doesn't help you, look for other descriptions on the net. Wikipedia or something.

Danois90:
If this is the case, you will need an entire data-set from lift off to touch down or else it would (IMO) not make much sense :slight_smile:

That example has a simple solution: the climb rate is exactly zero, if you landed at the same altitude you launched from. The variometer used for flight is really looking at the last 1 second to 20 seconds.