|
1 | 1 | /* |
2 | | - MedianFilter.cpp - Median Filter for the Arduino platform. |
3 | | - Copyright (c) 2013 Phillip Schmidt. All right reserved. |
4 | | -
|
5 | | - This library is free software; you can redistribute it and/or |
6 | | - modify it under the terms of the GNU Lesser General Public |
7 | | - License as published by the Free Software Foundation; either |
8 | | - version 2.1 of the License, or (at your option) any later version. |
9 | | -
|
10 | | - This library is distributed in the hope that it will be useful, |
11 | | - but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | - Lesser General Public License for more details. |
14 | | -
|
15 | | - You should have received a copy of the GNU Lesser General Public |
16 | | - License along with this library; if not, write to the Free Software |
17 | | - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 2 | + MedianFilter.cpp - Median Filter for the Arduino platform. |
| 3 | + Copyright (c) 2013 Phillip Schmidt. All right reserved. |
| 4 | +
|
| 5 | + This library is free software; you can redistribute it and/or |
| 6 | + modify it under the terms of the GNU Lesser General Public |
| 7 | + License as published by the Free Software Foundation; either |
| 8 | + version 2.1 of the License, or (at your option) any later version. |
| 9 | +
|
| 10 | + This library is distributed in the hope that it will be useful, |
| 11 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | + Lesser General Public License for more details. |
| 14 | +
|
| 15 | + You should have received a copy of the GNU Lesser General Public |
| 16 | + License along with this library; if not, write to the Free Software |
| 17 | + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 | 18 | */ |
19 | 19 |
|
20 | | -/* |
21 | | - A median filter object is created by by passing the desired filter window size on object creation. |
22 | | - The window size should be an odd number between 3 and 255. |
23 | | - |
24 | | - New data is added to the median filter by passing the data through the in() function. The new medial value is returned. |
25 | | - The new data will over-write the oldest data point, then be shifted in the array to place it in the correct location. |
26 | | - |
27 | | - The current median value is returned by the out() function for situations where the result is desired without passing in new data. |
28 | | - |
29 | | - !!! All data must be type INT. !!! |
30 | | - |
31 | | - Window Size / avg processing time [us] |
32 | | - 5 / 22 |
33 | | - 7 / 30 |
34 | | - 9 / 40 |
35 | | - 11 / 49 |
36 | | - 21 / 99 |
37 | | - |
| 20 | +/* |
| 21 | + A median filter object is created by by passing the desired filter window size on object creation. |
| 22 | + The window size should be an odd number between 3 and 255. |
| 23 | +
|
| 24 | + New data is added to the median filter by passing the data through the in() function. The new medial value is returned. |
| 25 | + The new data will over-write the oldest data point, then be shifted in the array to place it in the correct location. |
| 26 | +
|
| 27 | + The current median value is returned by the out() function for situations where the result is desired without passing in new data. |
| 28 | +
|
| 29 | + !!! All data must be type INT. !!! |
| 30 | +
|
| 31 | + Window Size / avg processing time [us] |
| 32 | + 5 / 22 |
| 33 | + 7 / 30 |
| 34 | + 9 / 40 |
| 35 | + 11 / 49 |
| 36 | + 21 / 99 |
| 37 | +
|
38 | 38 | */ |
39 | 39 |
|
40 | 40 | #include "MedianFilter.h" |
41 | 41 |
|
42 | | - |
43 | 42 |
|
44 | | -MedianFilter::MedianFilter(const byte size, const int seed) |
| 43 | +MedianFilter::MedianFilter(int size, int seed) |
45 | 44 | { |
46 | | - |
47 | | - medFilterWin = max(size, 3); // number of samples in sliding median filter window - usually odd # |
48 | | - medDataPointer = size >> 1; // mid point of window |
49 | | - data = (int*) calloc (size, sizeof(int)); // array for data |
50 | | - sizeMap = (byte*) calloc (size, sizeof(byte)); // array for locations of data in sorted list |
51 | | - locationMap = (byte*) calloc (size, sizeof(byte)); // array for locations of history data in map list |
52 | | - oldestDataPoint = medDataPointer; // oldest data point location in data array |
53 | | - |
54 | | - for(byte i = 0; i < medFilterWin; i++) // initialize the arrays |
55 | | - { |
56 | | - sizeMap[i] = i; // start map with straight run |
57 | | - locationMap[i] = i; // start map with straight run |
58 | | - data[i] = seed; // populate with seed value |
59 | | - } |
60 | | - |
| 45 | + medFilterWin = constrain(size, 3, 255); // number of samples in sliding median filter window - usually odd # |
| 46 | + medDataPointer = size >> 1; // mid point of window |
| 47 | + data = (int*) calloc (size, sizeof(int)); // array for data |
| 48 | + sizeMap = (uint8_t*) calloc (size, sizeof(uint8_t)); // array for locations of data in sorted list |
| 49 | + locationMap = (uint8_t*) calloc (size, sizeof(uint8_t)); // array for locations of history data in map list |
| 50 | + oldestDataPoint = medDataPointer; // oldest data point location in data array |
| 51 | + totalSum = size * seed; // total of all values |
| 52 | + |
| 53 | + for(uint8_t i = 0; i < medFilterWin; i++) // initialize the arrays |
| 54 | + { |
| 55 | + sizeMap[i] = i; // start map with straight run |
| 56 | + locationMap[i] = i; // start map with straight run |
| 57 | + data[i] = seed; // populate with seed value |
| 58 | + } |
61 | 59 | } |
62 | 60 |
|
63 | | -int MedianFilter::in(int value) |
64 | | -{ |
65 | | - // sort sizeMap |
66 | | - // small vaues on the left (-) |
67 | | - // larger values on the right (+) |
68 | | - |
69 | | - boolean dataMoved = false; |
70 | | - const byte rightEdge = medFilterWin - 1; // adjusted for zero indexed array |
71 | | - |
72 | | - data[oldestDataPoint] = value; // store new data in location of oldest data in ring buffer |
73 | | - |
74 | | - // SORT LEFT (-) <======(n) (+) |
75 | | - if(locationMap[oldestDataPoint] > 0){ // don't check left neighbours if at the extreme left |
76 | | - |
77 | | - for(byte i = locationMap[oldestDataPoint]; i > 0; i--){ //index through left adjacent data |
78 | | - |
79 | | - byte n = i - 1; // neighbour location |
80 | | - |
81 | | - if(data[oldestDataPoint] < data[sizeMap[n]]){ // find insertion point, move old data into position |
82 | | - |
83 | | - sizeMap[i] = sizeMap[n]; // move existing data right so the new data can go left |
84 | | - locationMap[sizeMap[n]]++; |
85 | | - |
86 | | - sizeMap[n] = oldestDataPoint; // assign new data to neighbor position |
87 | | - locationMap[oldestDataPoint]--; |
88 | | - |
89 | | - dataMoved = true; |
90 | | - |
91 | | - } |
92 | | - else { |
93 | | - |
94 | | - break; // stop checking once a smaller value is found on the left |
95 | | - |
96 | | - } |
97 | | - } |
98 | | - } |
99 | | - |
100 | | - // SORT RIGHT (-) (n)======> (+) |
101 | | - if(!dataMoved && locationMap[oldestDataPoint] < rightEdge){ // don't check right if at right border, or the data has already moved |
102 | | - |
103 | | - for(int i = locationMap[oldestDataPoint]; i < rightEdge; i++){ //index through left adjacent data |
104 | | - |
105 | | - int n = i + 1; // neighbour location |
106 | | - |
107 | | - if(data[oldestDataPoint] > data[sizeMap[n]]){ // find insertion point, move old data into position |
108 | | - |
109 | | - sizeMap[i] = sizeMap[n]; // move existing data left so the new data can go right |
110 | | - locationMap[sizeMap[n]]--; |
111 | | - |
112 | | - sizeMap[n] = oldestDataPoint; // assign new data to neighbor position |
113 | | - locationMap[oldestDataPoint]++; |
114 | | - |
115 | | - } |
116 | | - else { |
117 | | - |
118 | | - break; // stop checking once a smaller value is found on the right |
119 | | - |
120 | | - } |
121 | | - } |
122 | | - } |
123 | | - |
124 | | - oldestDataPoint++; // increment and wrap |
125 | | - if(oldestDataPoint == medFilterWin) |
126 | | - oldestDataPoint = 0; |
127 | | - |
128 | | - return data[sizeMap[medDataPointer]]; |
| 61 | + |
| 62 | +int MedianFilter::in(const int & value) |
| 63 | +{ |
| 64 | + // sort sizeMap |
| 65 | + // small vaues on the left (-) |
| 66 | + // larger values on the right (+) |
| 67 | + |
| 68 | + boolean dataMoved = false; |
| 69 | + const uint8_t rightEdge = medFilterWin - 1; // adjusted for zero indexed array |
| 70 | + |
| 71 | + totalSum += value - data[oldestDataPoint]; // add new value and remove oldest value |
| 72 | + |
| 73 | + data[oldestDataPoint] = value; // store new data in location of oldest data in ring buffer |
| 74 | + |
| 75 | + // SORT LEFT (-) <======(n) (+) |
| 76 | + if(locationMap[oldestDataPoint] > 0) // don't check left neighbours if at the extreme left |
| 77 | + { |
| 78 | + for(uint8_t i = locationMap[oldestDataPoint]; i > 0; i--) //index through left adjacent data |
| 79 | + { |
| 80 | + uint8_t n = i - 1; // neighbour location |
| 81 | + |
| 82 | + if(data[oldestDataPoint] < data[sizeMap[n]]) // find insertion point, move old data into position |
| 83 | + { |
| 84 | + sizeMap[i] = sizeMap[n]; // move existing data right so the new data can go left |
| 85 | + locationMap[sizeMap[n]]++; |
| 86 | + |
| 87 | + sizeMap[n] = oldestDataPoint; // assign new data to neighbor position |
| 88 | + locationMap[oldestDataPoint]--; |
| 89 | + |
| 90 | + dataMoved = true; |
| 91 | + } |
| 92 | + else |
| 93 | + { |
| 94 | + break; // stop checking once a smaller value is found on the left |
| 95 | + } |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + // SORT RIGHT (-) (n)======> (+) |
| 100 | + if(!dataMoved && locationMap[oldestDataPoint] < rightEdge) // don't check right if at right border, or the data has already moved |
| 101 | + { |
| 102 | + for(int i = locationMap[oldestDataPoint]; i < rightEdge; i++) //index through left adjacent data |
| 103 | + { |
| 104 | + int n = i + 1; // neighbour location |
| 105 | + |
| 106 | + if(data[oldestDataPoint] > data[sizeMap[n]]) // find insertion point, move old data into position |
| 107 | + { |
| 108 | + sizeMap[i] = sizeMap[n]; // move existing data left so the new data can go right |
| 109 | + locationMap[sizeMap[n]]--; |
| 110 | + |
| 111 | + sizeMap[n] = oldestDataPoint; // assign new data to neighbor position |
| 112 | + locationMap[oldestDataPoint]++; |
| 113 | + } |
| 114 | + else |
| 115 | + { |
| 116 | + break; // stop checking once a smaller value is found on the right |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + oldestDataPoint++; // increment and wrap |
| 121 | + if(oldestDataPoint == medFilterWin) oldestDataPoint = 0; |
| 122 | + |
| 123 | + return data[sizeMap[medDataPointer]]; |
129 | 124 | } |
130 | 125 |
|
131 | 126 |
|
132 | 127 | int MedianFilter::out() // return the value of the median data sample |
133 | 128 | { |
134 | | - return data[sizeMap[medDataPointer]]; |
| 129 | + return data[sizeMap[medDataPointer]]; |
135 | 130 | } |
136 | 131 |
|
137 | 132 |
|
| 133 | +int MedianFilter::getMin() |
| 134 | +{ |
| 135 | + return data[sizeMap[ 0 ]]; |
| 136 | +} |
| 137 | + |
| 138 | + |
| 139 | +int MedianFilter::getMax() |
| 140 | +{ |
| 141 | + return data[sizeMap[ medFilterWin - 1 ]]; |
| 142 | +} |
| 143 | + |
| 144 | + |
| 145 | +int MedianFilter::getMean() |
| 146 | +{ |
| 147 | + return totalSum / medFilterWin; |
| 148 | +} |
| 149 | + |
| 150 | + |
| 151 | +int MedianFilter::getStDev() // Arduino run time [us]: filterSize * 2 + 131 |
| 152 | +{ |
| 153 | + int32_t diffSquareSum = 0; |
| 154 | + int mean = getMean(); |
| 155 | + |
| 156 | + for( int i = 0; i < medFilterWin; i++ ) |
| 157 | + { |
| 158 | + int diff = data[i] - mean; |
| 159 | + diffSquareSum += diff * diff; |
| 160 | + } |
| 161 | + |
| 162 | + return int( sqrtf( float(diffSquareSum) / float(medFilterWin - 1) ) + 0.5f ); |
| 163 | +} |
| 164 | + |
138 | 165 |
|
139 | 166 | // *** debug fuctions *** |
140 | 167 | /* |
141 | 168 | void MedianFilter::printData() // display sorting data for debugging |
142 | 169 | { |
143 | | - for(int i=0; i<medFilterWin; i++) |
144 | | - { |
145 | | - Serial.print(data[i]); |
146 | | - Serial.print("\t"); |
147 | | - } |
148 | | - Serial.println("Data in ring buffer"); |
| 170 | + for(int i=0; i<medFilterWin; i++) |
| 171 | + { |
| 172 | + Serial.print(data[i]); |
| 173 | + Serial.print("\t"); |
| 174 | + } |
| 175 | + Serial.println("Data in ring buffer"); |
149 | 176 | } |
150 | 177 |
|
151 | 178 | void MedianFilter::printSizeMap() |
152 | | -{ |
153 | | - for(int i=0; i<medFilterWin; i++) |
154 | | - { |
155 | | - Serial.print(sizeMap[i]); |
156 | | - Serial.print("\t"); |
157 | | - } |
158 | | - Serial.println("Size Map, data sorted by size"); |
| 179 | +{ |
| 180 | + for(int i=0; i<medFilterWin; i++) |
| 181 | + { |
| 182 | + Serial.print(sizeMap[i]); |
| 183 | + Serial.print("\t"); |
| 184 | + } |
| 185 | + Serial.println("Size Map, data sorted by size"); |
159 | 186 | } |
160 | 187 |
|
161 | 188 | void MedianFilter::printLocationMap() |
162 | | -{ |
163 | | - for(int i=0; i<medFilterWin; i++) |
164 | | - { |
165 | | - Serial.print(locationMap[i]); |
166 | | - Serial.print("\t"); |
167 | | - } |
168 | | - Serial.println("Location Map, size data sorted by age"); |
169 | | -} |
| 189 | +{ |
| 190 | + for(int i=0; i<medFilterWin; i++) |
| 191 | + { |
| 192 | + Serial.print(locationMap[i]); |
| 193 | + Serial.print("\t"); |
| 194 | + } |
| 195 | + Serial.println("Location Map, size data sorted by age"); |
| 196 | +} |
170 | 197 |
|
171 | 198 | void MedianFilter::printSortedData() // display data for debugging |
172 | 199 | { |
173 | | - for(int i=0; i<medFilterWin; i++) |
174 | | - { |
175 | | - Serial.print(data[sizeMap[i]]); |
176 | | - Serial.print("\t"); |
177 | | - } |
178 | | - Serial.println("Data sorted by size"); |
| 200 | + for(int i=0; i<medFilterWin; i++) |
| 201 | + { |
| 202 | + Serial.print(data[sizeMap[i]]); |
| 203 | + Serial.print("\t"); |
| 204 | + } |
| 205 | + Serial.println("Data sorted by size"); |
179 | 206 | } |
180 | 207 | */ |
0 commit comments