Pages: 1 [2] 3 4   Go Down
Author Topic: runningMedian class on playground  (Read 8494 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 205
Posts: 12840
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


- heap

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

Belgium
Offline Offline
Edison Member
*
Karma: 68
Posts: 1916
Arduino rocks; but with my plugin it can fly rocking the world ;-)
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

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 -

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13663
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The playground article points to this thread, I will add a note (+link) that there is a substantial faster version.

update: done
« Last Edit: August 13, 2013, 03:46:06 pm by robtillaart » Logged

Rob Tillaart

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

Belgium
Offline Offline
Edison Member
*
Karma: 68
Posts: 1916
Arduino rocks; but with my plugin it can fly rocking the world ;-)
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I must have missed the link to this thread :-)
Thanks for the update
Jantje
Logged

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 -

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
/* 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.
« Last Edit: August 20, 2013, 10:39:53 am by WethaGuy » Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13663
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
« Last Edit: August 20, 2013, 01:11:04 pm by robtillaart » Logged

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Mid-Atlantic, USA
Offline Offline
God Member
*****
Karma: 30
Posts: 515
"Remember kids, the only difference between Science and screwing around is writing it down." - Adam Savage
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
#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);
}
Logged


Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13663
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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:
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?



Logged

Rob Tillaart

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

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 205
Posts: 12840
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
for better labeling when importing into a spreadsheet for charting

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

Mid-Atlantic, USA
Offline Offline
God Member
*****
Karma: 30
Posts: 515
"Remember kids, the only difference between Science and screwing around is writing it down." - Adam Savage
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged


Mid-Atlantic, USA
Offline Offline
God Member
*****
Karma: 30
Posts: 515
"Remember kids, the only difference between Science and screwing around is writing it down." - Adam Savage
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
[snipped, see thread above]
think even
   if (_cnt >=  nMedians)
is valid

so it could become
Code:
[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:
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.
Logged


Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13663
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The code can  get a 0/0 ==> ??? when _cnt == 0;
so:
Code:
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;
}
Logged

Rob Tillaart

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

Mid-Atlantic, USA
Offline Offline
God Member
*****
Karma: 30
Posts: 515
"Remember kids, the only difference between Science and screwing around is writing it down." - Adam Savage
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The code can  get a 0/0 ==> ??? when _cnt == 0;
so:
Code:
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. ;-)
Logged


Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13663
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I started some testing..
added getSize()  and getCount() too
« Last Edit: October 17, 2013, 01:09:10 pm by robtillaart » Logged

Rob Tillaart

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

Pages: 1 [2] 3 4   Go Up
Jump to: