Go Down

Topic: runningMedian class on playground (Read 10346 times) previous topic - next topic

Coding Badly


- heap

...which is fairly easy to implement with an array.

Jantje

I have used the code of rkail and it works great :-)
Is there any reason why it is not available on the playground?
Best regards
Jantje
Do not PM me a question unless you are prepared to pay for consultancy.
Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -

robtillaart

#17
Aug 13, 2013, 10:36 pm Last Edit: Aug 13, 2013, 10:46 pm by robtillaart Reason: 1
The playground article points to this thread, I will add a note (+link) that there is a substantial faster version.

update: done
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Jantje

I must have missed the link to this thread :-)
Thanks for the update
Jantje
Do not PM me a question unless you are prepared to pay for consultancy.
Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -

WethaGuy

#19
Aug 20, 2013, 12:50 am Last Edit: Aug 20, 2013, 05:39 pm by WethaGuy Reason: 1
I was looking for a way to average degrees for a project I'm working on and didn't want to use vector addition.  So, I've implemented a median function in the RunningMedian library for compass readings to compensate for hassles of degree wraps at 359-0.  This is based a previous version, not the current version of the library.  Feel free to incorporate it into your library for others to use, if you wish.

Code: [Select]
/* function by WethaGuy to allow determination of median of a set of degrees, accounting for the 359-0 wrap */
int RunningMedian::getMedianDegrees()
{
if (_cnt > 0){
       int index = 0;
sort();

/*check for valid compass value 0-359*/
       if (_as[_cnt-1] > 359 || _as[0] < 0){return NAN;}

/*check 359-0 wrap*/
if (_as[_cnt-1]-_as[0] > 180){

            /*find the index value of wrap*/
           while(_as[index+1]-_as[index] < 180){
               index++;
           }

           /* index value is less than the center of the list, the median is the sum of the index and the center of the list plus one (to account for odd number total list size) */
           if (index < (_cnt/2)){
            return _as[(_cnt/2) + index +1];

           /*index value is not less than center of list, the median is the difference between center of the list and the index */
           }else{
            return _as[index -(_cnt/2)];
           }
        }
    /* no 360-0 wrapping needed */
    return _as[_cnt/2];
}
return NAN;
}


edited code to correct error, 8/20/13.

robtillaart

#20
Aug 20, 2013, 08:08 pm Last Edit: Aug 20, 2013, 08:11 pm by robtillaart Reason: 1
It is a good use of the running median class but quite specific. People searching for the running Median and degrees will probably find this thread.

That said, I will make a note on the playground with a link to your post.
update - link added
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

sigi

Hi,

WethaGuy's Code remembers me to the Yamartino function that i used for a wind vane. It's written by Cristopher Baker.
https://github.com/SAIC-ATS/Algorithms

Sembazuru

I'm working with a sharp distance sensor that has a quite noisy output. I started to use the runningMedian class, but decided that I really want to get an average but exclude the spike outliers instead of just the simple Median. I came up with this extended method to getAverage that takes an unsigned 8bit value as an argument as the number of middle median values. I implemented it in addition to the no argument getAverage() method as an overloaded method, and it seems to work for me. (IDE v 1.0.5 ERW on Win7.)

Lets say you set up the RunningMedian class with 15 elements, getAverage(5) would average sorted[5] through sorted[9] and getAverage(10) would average sorted[2] through sorted[11]. If the current size of the array isn't larger than the requested amount to average it just returns NAN.

Please consider merging this addition to your version and/or to the template version (so I can go back to using your published version and won't have to back-port any additions/bug fixes you make in the future). Thanx.

Code: [Select]

float RunningMedian::getAverage(uint8_t nMedians)
{
if (_cnt > 0)
{
if (_cnt > nMedians)
{
uint8_t start = ((_cnt - nMedians)/2);
uint8_t stop = start + nMedians;
sort();
float sum = 0;
for (uint8_t i = start; i < stop; i++) sum += _as[i];
return sum / nMedians;
}
}
return NAN;
}


I also modified the example code to include a header line (for better labeling when importing into a spreadsheet for charting) and the new average. I also used the F() macro, so my new version of example code would break on pre 1.0 IDE versions... Here is my modified example code:

Code: [Select]

#include <RunningMedianWithAverageParameter.h> // my modified library name
// #include <RunningMedian.h> // original library name

// RunningMedian samples = RunningMedian(9);
RunningMedian samples = RunningMedian();

void setup()
{
  Serial.begin(115200);
  Serial.print(F("Running Median Version: "));
  Serial.println(RUNNINGMEDIANVERSION);
  Serial.println(F("milliseconds AnalogRead getLowest getAverage getAverage(3) getMedian getHighest"));
}

void loop()
{
  test1();
}

void test1()
{
  long x = analogRead(A0);
  samples.add(x);
  float l = samples.getLowest();
  float m = samples.getMedian();
  float a = samples.getAverage();
  float a3 = samples.getAverage(3);
  float h = samples.getHighest();
  Serial.print(millis());
  Serial.print(" ");
  Serial.print(x);
  Serial.print(" ");
  Serial.print(l);
  Serial.print(" ");
  Serial.print(a);
  Serial.print(" ");
  Serial.print(a3);
  Serial.print(" ");
  Serial.print(m);
  Serial.print(" ");
  Serial.println(h);
  delay(100);
}
http://www.catb.org/jargon/html/I/I-didn-t-change-anything-.html

robtillaart

very nice addition, code can be simpler
- if (_cnt > nMedians)     implies _cnt > 0 for all _nMedians
- there is no check for nMedians == 0;

Code: [Select]

float RunningMedian::getAverage(uint8_t nMedians)
{
if ( nMedians == 0) return NAN;

if (_cnt > nMedians)
{
uint8_t start = ((_cnt - nMedians)/2);
uint8_t stop = start + nMedians;
sort();
float sum = 0;
for (uint8_t i = start; i < stop; i++) sum += _as[i];
return sum / nMedians;
}
return NAN;
}

think even
   if (_cnt >=  nMedians)
is valid

so it could become
Code: [Select]

float RunningMedian::getAverage(uint8_t nMedians)
{
if (nMedians == 0) return NAN;

if (_cnt < nMedians) nMedians = _cnt;     // when filling the array for first time

uint8_t start = ((_cnt - nMedians)/2);
uint8_t stop = start + nMedians;
sort();
float sum = 0;
for (uint8_t i = start; i < stop; i++) sum += _as[i];
return sum / nMedians;
}


what you think?



Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Coding Badly

Quote
for better labeling when importing into a spreadsheet for charting


Tabs (instead of spaces) work really well for that.

Sembazuru


Quote
for better labeling when importing into a spreadsheet for charting


Tabs (instead of spaces) work really well for that.



True, if one wants actual TSV files, or use a comma for CSV files. I use LibreOffice (and occasionally Origin) for my charting, both of which have configurable value separators at inport. I was trying not to change the example too much. But I suppose creating a single char variable that contains ASCII tab and then using it wouldn't detract from what the example is doing.
http://www.catb.org/jargon/html/I/I-didn-t-change-anything-.html

Sembazuru


very nice addition, code can be simpler
- if (_cnt > nMedians)     implies _cnt > 0 for all _nMedians
- there is no check for nMedians == 0;

Code: [Select]

[snipped, see thread above]

think even
   if (_cnt >=  nMedians)
is valid

so it could become
Code: [Select]

[snipped, see thread above]


what you think?



Good thoughts. Though I'd probably make the following (minor) change for both consistent style and to trap accidentally trying to pass a negative value for nMedians: (on second thought, only for consistent style as uint8_t can't be negative...)
Code: [Select]

float RunningMedian::getAverage(uint8_t nMedians)
{
if (nMedians > 0)
{
if (_cnt < nMedians) nMedians = _cnt;     // when filling the array for first time
uint8_t start = ((_cnt - nMedians)/2);
uint8_t stop = start + nMedians;
sort();
float sum = 0;
for (uint8_t i = start; i < stop; i++) sum += _as[i];
return sum / nMedians;
}
return NAN;
}


BTW, when converting my program back from the template version to the regular cpp and h version for my new method, I noticed that the template version added some new methods (one of which I was using which is why I noticed it):

getSize
getCount
getStatus

I don't think the getStatus method is useful outside the template version, but both getSize (returns _size) and getCount (returns _cnt) are useful for troubleshooting. They could also be used by a programmer to make sure they have enough statistics to get proper results, especially if they clear the array often.
http://www.catb.org/jargon/html/I/I-didn-t-change-anything-.html

robtillaart

The code can  get a 0/0 ==> ??? when _cnt == 0;
so:
Code: [Select]

float RunningMedian::getAverage(uint8_t nMedians)
{
if ( (_cnt > 0) && (nMedians > 0))
{
if (_cnt < nMedians) nMedians = _cnt;     // when filling the array for first time
uint8_t start = ((_cnt - nMedians)/2);
uint8_t stop = start + nMedians;
sort();
float sum = 0;
for (uint8_t i = start; i < stop; i++) sum += _as[i];
return sum / nMedians;
}
return NAN;
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Sembazuru


The code can  get a 0/0 ==> ??? when _cnt == 0;
so:
Code: [Select]

float RunningMedian::getAverage(uint8_t nMedians)
{
if ( (_cnt > 0) && (nMedians > 0))
{
if (_cnt < nMedians) nMedians = _cnt;     // when filling the array for first time
uint8_t start = ((_cnt - nMedians)/2);
uint8_t stop = start + nMedians;
sort();
float sum = 0;
for (uint8_t i = start; i < stop; i++) sum += _as[i];
return sum / nMedians;
}
return NAN;
}



Ah... Good catch. While I learned how to divide by zero in high school AP calculus, I don't think the Arduino took any AP courses in high school. ;-)
http://www.catb.org/jargon/html/I/I-didn-t-change-anything-.html

robtillaart

#29
Oct 17, 2013, 08:01 pm Last Edit: Oct 17, 2013, 08:09 pm by robtillaart Reason: 1
I started some testing..
added getSize()  and getCount() too
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up