Example: Util ComputedValues
This examples dmonstrates how to use the computedvalue utility module for creating average or mojority votes.
When measuring physical “stuff”, things are not always precise. Binary signals - e.g. valve feedbacks
or switches - tend to oscillate before they settle. Pressure values from non centrifugal pumps have distinct phases. To handle these sort of values, we need to average the signals out… which is such a common operation that CENA provides you with classes that behave just like the native counterparts (adding, comparing, etc.), but auto-average on assignments.
#include <iostream>
Overview
Here are two computed value classes.
#include "computedvalue/MovingWindowAverage.hpp"
#include "computedvalue/MajorityVote.hpp"
namespace cena = semodia::controlengine::native;
namespace computedvalue = cena::util::computedvalue;
Each computed value is essentially a fixed size list of values. To improve performance, both of the above classes preallocate a vector to minimize lookup times and allocations at runtime. The vector is preinitialized with a default value.
What differs is how the window is interpreted in getValue().
Examples
Let’s take a look at two examples
Numeric averages
In our fictitious example we are measuring the pressure of a membrane pump. We want to create a pressure average and use that as a “normal” floating point value.
First, we will create a new pressure value, like so
void numericAverageExample()
{
computedvalue::MovingWindowAverage<float> pressure(
4, // Window size: averages 4 values
2 // Initial value
);
Since we have not sampled anything yet, our pressure would report its initial value.
This initial value is not part of the average; the “2” will disappear as soon as we enter a proper value. So… how do we add numbers to the average? Simply assign them, like this:
pressure = 10;
pressure = 12;
pressure = 9;
Our window holds the value 10,12 & 9. The average should be 31/3 ~ 10.333.
std::cout << "The current average pressure is " << pressure << std::endl;
If we add new values, the window will of course move…
pressure = 12;
pressure = 14;
pressure = 16;
Remember that the window size we chose was 4; so our window holds the value 9, 12, 14, 16.
std::cout << "The current average pressure is " << pressure << std::endl;
You can use the averaged value in normal calculations and assignments as you would with any other numeric type:
// float currentPressure = pressure;
// float pressureLargerThanCurrent = pressure + 10;
pressure += 10; // This would add avg(pressure) + 10 to our window
return;
}
Time bounded averages
You want an average value over a specific time, e.g. 1s?
Simply wrap the sampling - i.e. the “pressure = …” - into a FrequencyLimitedTaskLoopTask; take a look at the task tutorial for more info on that.
By running the sampling tasks once every 100ms and creating an average value with a windowsize of 10, you get an averaged value over the last second.
Logic averages
A classic: You want to debounce a binary input signal, like the open/closed-feedback of a valve? This works pretty much like the numeric example above… simply add the sampled values to a MajorityVote
void booleanAverageExample()
{
computedvalue::MajorityVote valveOpen(
4, // Window size: averages 4 values
false // Initial value
);
In a MajorityVote
, the initial values is used both for initial reporting, but it also
settles ties in windows of even size.
std::cout << "The valve is open? " << valveOpen << std::endl; // initial value: false
valveOpen = false;
valveOpen = true;
std::cout << "The valve is open? " << valveOpen << std::endl; // initial value used to settle tie
The rest works as above, including logical operations like & or |
valveOpen = !valveOpen; // adds true, as valveOpen currently results in false
std::cout << "The valve is open? " << valveOpen << std::endl; // initial value used to settle tie
valveOpen &= true; // true & true --> adds true
valveOpen |= false; // true | false --> adds true
// etc.
return;
}
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{
numericAverageExample();
booleanAverageExample();
return 0;
}
Source Code
Here’s the complete source code from this example:
1#include <iostream>
2
3#include "computedvalue/MovingWindowAverage.hpp"
4#include "computedvalue/MajorityVote.hpp"
5
6namespace cena = semodia::controlengine::native;
7namespace computedvalue = cena::util::computedvalue;
8
9void numericAverageExample()
10{
11 computedvalue::MovingWindowAverage<float> pressure(
12 4, // Window size: averages 4 values
13 2 // Initial value
14 );
15
16 pressure = 10;
17 pressure = 12;
18 pressure = 9;
19
20 std::cout << "The current average pressure is " << pressure << std::endl;
21
22 pressure = 12;
23 pressure = 14;
24 pressure = 16;
25
26 std::cout << "The current average pressure is " << pressure << std::endl;
27
28 // float currentPressure = pressure;
29 // float pressureLargerThanCurrent = pressure + 10;
30
31 pressure += 10; // This would add avg(pressure) + 10 to our window
32 return;
33}
34
35void booleanAverageExample()
36{
37 computedvalue::MajorityVote valveOpen(
38 4, // Window size: averages 4 values
39 false // Initial value
40 );
41
42 std::cout << "The valve is open? " << valveOpen << std::endl; // initial value: false
43
44 valveOpen = false;
45 valveOpen = true;
46
47 std::cout << "The valve is open? " << valveOpen << std::endl; // initial value used to settle tie
48
49 valveOpen = !valveOpen; // adds true, as valveOpen currently results in false
50 std::cout << "The valve is open? " << valveOpen << std::endl; // initial value used to settle tie
51
52 valveOpen &= true; // true & true --> adds true
53 valveOpen |= false; // true | false --> adds true
54 // etc.
55
56 return;
57}
58
59int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
60{
61 numericAverageExample();
62 booleanAverageExample();
63 return 0;
64}