-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvoltage_monitor.h
173 lines (149 loc) · 4.54 KB
/
voltage_monitor.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// -*- mode: C++; -*-
/**\file voltage_monitor.h
\brief Trivial co-routine to gather and filter ADC samples
*/
#pragma once
#include <cstdlib>
#include <limits>
#include "Time.h"
#include "actions.h"
/// Size of ring buffer for samples
const static size_t N = 2000;
/// Number of histogram bins
const static size_t K = 100;
/// Old Calibration data:
/// 39930 19600
/// 33957 16700
/// 10190 5330
/// 8816 5000
/// 7984 3330
///
/// Regression equation:
/// 0.4874166181 * x + 0.1581747627
///
/// Calibration data:
/// 22804 11500
/// 25251 12600
/// 28800 14400
///
/// Regression equation:
/// 0.48534 * x + 399.65
const static float gain = 0.48534;
const static float offset = 399.65;
///\class VoltageMonitor
///\brief Computes the median of 2000 samples with the work spread over
/// many small steps to maintain a responsive user interface.
class VoltageMonitor {
public:
VoltageMonitor() : ring_index(0), sample_count(0), mv_hold(999999) {};
/// Return the latest voltage reading.
long getValue() {
// Limit update rate.
//\todo Need a better way of inhibiting the long update
// calculation when the user is providing Morse code
//input. Perhaps disable this unless idle for a few seconds.
time_t currentSeconds(now());
if (updateTime != currentSeconds &&
currentSeconds % getVoltagePollInterval() == 0)
{
update();
}
return mv_hold;
}
/// Keep the most recent N samples in a ring buffer.
void getSample() {
float raw(analogRead(analogInput));
// Raw reading at 2.7V. Can't really run under this voltage.
const float rawMin(0*12352.2);
// Raw reading at 20V. Risking damage over this voltage.
const float rawMax(10*112237.0);
if (rawMin < raw && raw < rawMax) {
sample[ring_index] = raw;
++ring_index;
++sample_count;
if (N < sample_count) {
// Saturate. There are N values in the buffer.
sample_count = N;
}
if (ring_index == N) {
// Wrap to keep most recent N.
ring_index = 0;
}
} else {
// Unreasonable ADC reading
;
}
}
uint32_t getRawVoltage() {
return analogRead(analogInput);
}
/// Filter and scale raw ADC values to millivolts.
void update() {
// Simple sum over the samples
float sample_sum(0.0);
// Discarding impossible values is done while gathering samples
// in getSample() above.
float min(std::numeric_limits<int>::max());
float max(std::numeric_limits<int>::min());
for (size_t k(0); k < sample_count; ++k) {
sample_sum += sample[k];
if (sample[k] < min) min = sample[k];
if (max < sample[k]) max = sample[k];
}
float range(max - min);
float bin(range/(K - 1));
for (uint32_t k(0); k < K; ++k) {
histogram[k] = 0.0;
binsum[k] = 0.0;
}
for (uint32_t k(0); k < sample_count; ++k) {
size_t index((sample[k] - min)/bin);
if (0 <= index && index < K) {
histogram[index]++;
binsum[index] += sample[k];
}
}
// Find the bottom quartile.
size_t lower_quartile(0);
for (uint32_t k(0);
k < sample_count/4;
k += histogram[lower_quartile++])
;
// Find the top quartile.
size_t upper_quartile(K);
for (uint32_t k(0);
k < sample_count/4;
k += histogram[--upper_quartile])
;
// Sum and count the middle two quartiles.
float mid_sum(0.0);
size_t mid_count(0.0);
for (uint32_t k(lower_quartile); k <= upper_quartile; ++k) {
mid_sum += binsum[k];
mid_count += histogram[k];
}
if (0.0 < mid_count) {
mv_hold = gain * mid_sum / mid_count + offset;
} else {
mv_hold = gain * sample_sum / sample_count + offset;
}
updateTime = now();
}
private:
/// The time in seconds of the last update.
time_t updateTime;
/// The index into the sample[] ring buffer.
size_t ring_index;
/// The number of samples collected so far. This quickly maxes out
/// and remains equal to N.
size_t sample_count;
/// A ring buffer of the most recent sample_count raw ADC sample
/// values.
float sample[N];
/// The number of values in each bin of a histogram.
float histogram[K];
/// The sum of the values in each bin of a histogram.
float binsum[K];
/// The most recent voltage reading.
int32_t mv_hold;
};