thanks for the help! Another thing I was stumbling upon was the question if there were any possible chances of creating an RA-array:
RunningAverage diff_p_RA(50);
in a way that the size (int) would be user configurable during runtime? Now, as I understand it, there is quite a fuz about growing or reducing an array during runtime - meaning that it is not that simple. However, maybe you have an idea for an approach one could try implementing. Maybe, using a fixed large array of the maximum expected size (200 would be a reasonable value -I guess) and then only using parts of that array - as the user defines the averaging to be done between 0 and 200 values.
I did some more measurements and used RunningMedian instead of RunningAverage. The Code I used can be found here:
/*
*************************************
* Thread: THREAD_DifferentialPressure
* Get`s the differential pressure.
*************************************
*/
/* Global variables for this function */
float diff_Pressure_median = 0;
float diff_Pressure = 0;
float offset_diff_pressure; // offset which can be set by the user -> FUNC_OffsetDiffPressure (X_E_LCDML_MenuFunctions)
int n_averaging; // variable can be set by the user to define ow many measurements shall be averaged
RunningMedian diff_p_RA(200); // use a uer defined size for the running averaging of the differential pressure
/* --------- INIT ---------
* Initialization of this function
* is only being done once at the start of the function
*/
void simpleThread_setup(THREAD_DifferentialPressure)
{
/* Init Function */
//diff_p_RA.clear; // Running Average for diff. pressure; explicitly start clean
}
/* --------- LOOP ----------
* This is the place for code which is going to be repeated constantly
* e.g. a time-diplay or the like.
*/
boolean simpleThread_loop(THREAD_DifferentialPressure)
{
/* Loop Function */
diff_p_RA.add(getDifferentialPressure()); // read out the sensor value and add to averaging variable
// set averaged differential pressure - pressure offset set by the user
diff_Pressure = diff_p_RA.getAverage() - offset_diff_pressure;
diff_Pressure_median = diff_p_RA.getMedian() - offset_diff_pressure;
Serial.print(diff_Pressure);
Serial.print(";");
Serial.println(diff_Pressure_median);
if (isnan(diff_Pressure) || diff_Pressure < 0.00)
{
diff_Pressure = 0.00; // set diff_Pressure to zero if the reading is NaN - Not a Number, or lower than zero
}
return true;
}
The results of these measurements can be found in the attached pdf file "Diff_Pressure_averaged_median.pdf". Now there is something strange about the averaging value. The buffer size seems not to change. even the 200 value buffer size ("RunningMedian diff_p_RA(200); ") looks more like the 25 or even 10. The RunningMedian Code I used is from your (Rob Tillaart) github repository. Furthermore, I expected something else from the RunningMedian. Let me know what you think.
Best regards,
Jan
P.S.: I now added the raw data of these measurements.
just to complete the statements above, I made another series of measurements (see "Diff_Pressure_RunningAverage.pdf") using the same buffer sizes. But now I used RunningAverage again. As can be clearly seen, the output is as expected. I did not clear the variable as this is not necessary with these measurements.
so one may draw the conclusion that there is something wrong with the code of the RunningMedian Library.
Here is the code sniplet where I calculate my RunningAverage for the differential pressure:
/*
*************************************
* Thread: THREAD_DifferentialPressure
* Get`s the differential pressure.
*************************************
*/
/* Global variables for this function */
float diff_Pressure_median = 0;
float diff_Pressure = 0;
float offset_diff_pressure; // offset which can be set by the user -> FUNC_OffsetDiffPressure (X_E_LCDML_MenuFunctions)
int n_averaging; // variable can be set by the user to define ow many measurements shall be averaged
RunningAverage diff_p_RA(200); // use a uer defined size for the running averaging of the differential pressure
/* --------- INIT ---------
* Initialization of this function
* is only being done once at the start of the function
*/
void simpleThread_setup(THREAD_DifferentialPressure)
{
/* Init Function */
diff_p_RA.clear(); // Running Average for diff. pressure; explicitly start clean
}
/* --------- LOOP ----------
* This is the place for code which is going to be repeated constantly
* e.g. a time-diplay or the like.
*/
boolean simpleThread_loop(THREAD_DifferentialPressure)
{
/* Loop Function */
diff_p_RA.addValue(getDifferentialPressure()); // read out the sensor value and add to averaging variable
// set averaged differential pressure - pressure offset set by the user
diff_Pressure = diff_p_RA.getAverage() - offset_diff_pressure;
Serial.println(diff_Pressure);
if (isnan(diff_Pressure) || diff_Pressure < 0.00)
{
diff_Pressure = 0.00; // set diff_Pressure to zero if the reading is NaN - Not a Number, or lower than zero
}
return true;
}
Best regards,
Jan
P.S.: I now added the raw data of these measurements.
jabami:
[... snipped ...]
The results of these measurements can be found in the attached pdf file "Diff_Pressure_averaged_median.pdf". Now there is something strange about the averaging value. The buffer size seems not to change. even the 200 value buffer size ("RunningMedian diff_p_RA(200); ") looks more like the 25 or even 10. The RunningMedian Code I used is from your (Rob Tillaart) github repository. Furthermore, I expected something else from the RunningMedian. Let me know what you think.
Best regards,
Jan
P.S.: I now added the raw data of these measurements.
If you look in RunningMedian.h you will see
#define MEDIAN_MAX_SIZE 19 // adjust if needed
If you don't change that, requesting a median array size of anything larger than 19 will only give you a 19 element array to run your medians and averages against. Did you change that line before compiling your sketch that requests a 200 element array?
thank you Sembazuru for pointing that out. I guess that means I should have set "MEDIAN_MAX_SIZE" to somewhat around 200 and then define a RunningMedian samples = RunningMedian(50); Well, as I learned yesterday the RunningMedian takes more processing time as it needs to sort the array. As I´m taking my measurements each 50ms I guess that would not be an option - especially not with a large array size (which seem absolutley logical now to limit the array size). I´m now using the RunningAverage-library as I found my optimum array size for my setup via the tests I did. So everything is fine now. But I still wonder if the array size can be made user configurable during runtime.
@ivanseidel: Thank you for pointing me to your library. But I allready implemented the RunningAverage-library by Rob Tillaart and only needed some fine tuning (and understanding on whats going on behind the scenes). The last results I got were suficiently constant. I just needed to adjust the array size to get the timing right (because a large array size means smoother data but more timeleg on steep gradients ).
I have tried to reproduce the getMedian() error but I could not. Using the [partial] data from your zip file I got no errors.
A possible cause I cannot verify is how much free RAM you have as it might be a problem during allocating the internal arrays as the lib uses dynamic memory. That could explain the behaviour seen.
But I still wonder if the array size can be made user configurable during runtime.
The lib could be rewritten to use dynamic memory and you could reallocate the memory. However as an Arduino has very limited RAM this gives fragmented (useless) memory quite fast.
that said, you might try with largest allocation first and gradually decrease the size to an optimum. Then the reallocation should less fragment the memory in theory ...
But I still wonder if the array size can be made user configurable during runtime.
The lib could be rewritten to use dynamic memory and you could reallocate the memory. However as an Arduino has very limited RAM this gives fragmented (useless) memory quite fast.
that said, you might try with largest allocation first and gradually decrease the size to an optimum. Then the reallocation should less fragment the memory in theory ...
That's true, but it's already implemented with my library (I use a self made LinkedList). Worth a try =)
You can change the size whenever you want, and it will not crash the code.
Also, you can use it with basic numbers 10 = Gaussian(10); or with numbers with variance: Gaussian(10,variance);, witch gives you much more flexibility on the average...
GaussianAverage avg1(NUM_AVGS);
GaussianAverage avg2(NUM_AVGS);
GaussianAverage avg3(NUM_AVGS);
GaussianAverage avg4(NUM_AVGS);
//In code to add values to the moving average
avg1 += 10;
avg2 += xx...;
// Then process and get the result
float reault1 = avg1.process().mean;
you have 10 RA objects of size 60 and 5 RA objects of size 24, that is a lot of memory (at least for an UNO)
Every object has 10 bytes local vars and 4 bytes per element.
15 objects = 150 bytes
10 x 60 x 4 = 2400 bytes
5 x 24 x 4 = 480 bytes
roughly 3000+ bytes of SRAM
What board are you using?
If it is an UNO, it does not have that much memory, ==> Get a MEGA.
Learning point for me: The RA class has no error handling or state to see its "health".
Analysis:
The class does not check if the allocation of the needed "arrays" succeed, but the code behaves as if it does so.
That means that only the first RA object are healthy and the others have internal pointers pointing to (probably) NULL.
To make it more robust the interface of the Class could change to have a method
bool begin(uint8_t size) ;
that returns true is size elements could be allocated.
The allocation should be removed from the constructor.
// this is a breaking interface change
Another alternative is to have a method uint8_t internalSize();
That returns the size of the internal size. This returns zero if allocation had failed and the internal size otherwise.
// this would not break the existing interface and would increase footprint not much.
Updated the library to version 0.2.04 ( 0.2.03 was skipped - dev only version)
To solve the problem of Chrismolloy above I added code to check if the internal array could be allocated. The previous 0.2.02 version was really opportunistic
If the array cannot be allocated the size of the array is set to 0, the new method getSize() can be used to check if the allocation worked. Having an internal size of zero the Class cannot accept new values and the average will be NAN as it cannot be calculated.
The size of the lib increased about 20 bytes, which is imho acceptable for this "safety net".
I've been using your library, in conjunction with TinyGPS++ library, in a GPS routine to average the GPS position and help reduce the position wander, and had some interesting results. Whilst the longitude seems to function correctly e.g. 1.01834380, the latitude seems to stop or stall and not continue example: 51.34103012.
Note that I'm going to 8 decimal points for the LAT/ LON position. I wondered if there is a limitation in the RA library that limits the length or size of the number?
Also, would it be capable of taking both + and - numbers?
The RA lib uses floats which are on an AVR based Arduino 32bits IEEE754 floats. This means a 23 bit mantissa which equals about 7 significant digits.
If you are using an ARM based Arduino, which supports 64 bit double you could patch the library by replacing all float types with double. Should be a good change anyway. On my todo list.