Implement eeprom stored persistent histogram
This commit is contained in:
parent
cae18b98e7
commit
b8a40aed17
@ -1 +1 @@
|
||||
Subproject commit 33a4d55f03451f487ace046a6f2328351fda69ee
|
||||
Subproject commit 3bcba0a1919906d7cb358d3d8185a064bc1aa705
|
@ -8,59 +8,95 @@
|
||||
#include "clock.hpp"
|
||||
#include "controller.hpp"
|
||||
|
||||
uint64_t Statistics::m_lastTempSave = 0;
|
||||
double Statistics::m_minTemp;
|
||||
double Statistics::m_maxTemp;
|
||||
uint64_t Statistics::m_lastTemperatureWriteback = 0;
|
||||
uint64_t Statistics::m_lastTemperatureSample = 0;
|
||||
uint32_t Statistics::m_temperatureHistogram[TEMPERATURE_RANGE];
|
||||
|
||||
namespace {
|
||||
|
||||
// Must be in translation unit and cannot be forward declared
|
||||
EEVAR(double, e_minTemp);
|
||||
EEVAR(double, e_maxTemp);
|
||||
EEARRAY(uint32_t, e_temperatureHistogram, 100);
|
||||
|
||||
// Could be declared in every function that uses it, but that would be code duplication
|
||||
Eeprom<e_minTemp, true> g_eepMinTemp;
|
||||
Eeprom<e_maxTemp, true> g_eepMaxTemp;
|
||||
EepromArray<e_temperatureHistogram, EEARRAY_SIZE(e_temperatureHistogram), true> g_eepTemperatureHistogram;
|
||||
|
||||
} // namespace
|
||||
|
||||
void Statistics::init()
|
||||
{
|
||||
m_minTemp = getMinTemp();
|
||||
m_maxTemp = getMaxTemp();
|
||||
|
||||
if (isnan(m_minTemp)) {
|
||||
m_minTemp = type::numeric_limits<double>::max();
|
||||
}
|
||||
if (isnan(m_maxTemp)) {
|
||||
m_maxTemp = type::numeric_limits<double>::lowest();
|
||||
for (uint8_t i = 0; i < g_eepTemperatureHistogram.size(); ++i) {
|
||||
m_temperatureHistogram[i] = g_eepTemperatureHistogram[i];
|
||||
using temperature_t = type::decay_t<decltype(m_temperatureHistogram[0])>;
|
||||
if (m_temperatureHistogram[i] == type::numeric_limits<temperature_t>::max()) {
|
||||
m_temperatureHistogram[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Statistics::callback()
|
||||
{
|
||||
if (Controller::m_dataAvailable) {
|
||||
if (Controller::m_temperature < m_minTemp) {
|
||||
m_minTemp = Controller::m_temperature;
|
||||
}
|
||||
if (Controller::m_temperature > m_maxTemp) {
|
||||
m_maxTemp = Controller::m_temperature;
|
||||
if (clk::millis() >= m_lastTemperatureSample + TEMPERATURE_SAMPLE_DELAY) {
|
||||
const auto temperature = clampTemperature(static_cast<int8_t>(round(Controller::m_temperature)));
|
||||
++m_temperatureHistogram[temperature];
|
||||
m_lastTemperatureSample = clk::millis();
|
||||
}
|
||||
|
||||
if (clk::millis() >= m_lastTempSave + TEMP_SAVE_DELAY) {
|
||||
g_eepMinTemp = m_minTemp;
|
||||
g_eepMaxTemp = m_maxTemp;
|
||||
m_lastTempSave = clk::millis();
|
||||
if (clk::millis() >= m_lastTemperatureWriteback + TEMPERATURE_WRITEBACK_DELAY) {
|
||||
writeTemperatureHistogram();
|
||||
m_lastTemperatureWriteback = clk::millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double Statistics::getMinTemp()
|
||||
uint8_t Statistics::getMinTemp()
|
||||
{
|
||||
return g_eepMinTemp;
|
||||
for (uint8_t i = 0; i < TEMPERATURE_RANGE; ++i) {
|
||||
if (m_temperatureHistogram[i] > 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return TEMPERATURE_RANGE;
|
||||
}
|
||||
|
||||
double Statistics::getMaxTemp()
|
||||
uint8_t Statistics::getMaxTemp()
|
||||
{
|
||||
return g_eepMaxTemp;
|
||||
for (int8_t i = TEMPERATURE_RANGE - 1; i >= 0; --i) {
|
||||
if (m_temperatureHistogram[i] > 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return TEMPERATURE_RANGE;
|
||||
}
|
||||
|
||||
uint32_t Statistics::getMaximumHistogramSamples()
|
||||
{
|
||||
uint32_t max = 1; // Avoid division by zero
|
||||
for (uint8_t i = 0; i < TEMPERATURE_RANGE; ++i) {
|
||||
if (m_temperatureHistogram[i] > max) {
|
||||
max = m_temperatureHistogram[i];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
uint8_t Statistics::getHistogram(const int8_t &temperature, const uint32_t &normalizationFactor)
|
||||
{
|
||||
return static_cast<uint8_t>(m_temperatureHistogram[clampTemperature(temperature)] / normalizationFactor);
|
||||
}
|
||||
|
||||
uint8_t Statistics::clampTemperature(const int8_t &temperature)
|
||||
{
|
||||
if (temperature < 0)
|
||||
return 0;
|
||||
if (temperature >= TEMPERATURE_RANGE)
|
||||
return TEMPERATURE_RANGE - 1;
|
||||
return static_cast<uint8_t>(temperature);
|
||||
}
|
||||
|
||||
void Statistics::writeTemperatureHistogram()
|
||||
{
|
||||
for (uint8_t i = 0; i < g_eepTemperatureHistogram.size(); ++i) {
|
||||
g_eepTemperatureHistogram[i] = m_temperatureHistogram[i];
|
||||
}
|
||||
}
|
||||
|
@ -7,14 +7,21 @@ class Statistics {
|
||||
static void init();
|
||||
static void callback();
|
||||
|
||||
static double getMinTemp();
|
||||
static double getMaxTemp();
|
||||
static uint8_t getMinTemp();
|
||||
static uint8_t getMaxTemp();
|
||||
|
||||
static uint32_t getMaximumHistogramSamples();
|
||||
static uint8_t getHistogram(const int8_t &temperature, const uint32_t &normalizationFactor);
|
||||
|
||||
private:
|
||||
static constexpr auto TEMP_SAVE_DELAY = 60000;
|
||||
static constexpr auto TEMPERATURE_WRITEBACK_DELAY = 1'800'000;
|
||||
static constexpr auto TEMPERATURE_SAMPLE_DELAY = 1'000;
|
||||
static constexpr auto TEMPERATURE_RANGE = 100;
|
||||
|
||||
static uint64_t m_lastTempSave;
|
||||
static uint64_t m_lastTemperatureWriteback;
|
||||
static uint64_t m_lastTemperatureSample;
|
||||
static uint32_t m_temperatureHistogram[TEMPERATURE_RANGE];
|
||||
|
||||
static double m_minTemp;
|
||||
static double m_maxTemp;
|
||||
static uint8_t clampTemperature(const int8_t &temperature);
|
||||
static void writeTemperatureHistogram();
|
||||
};
|
||||
|
@ -22,9 +22,10 @@ GF(MONITOR_CMD, "monitor");
|
||||
GF(BOOTLOADER_CMD, "bootloader");
|
||||
GF(UPTIME_CMD, "uptime");
|
||||
GF(STATISTICS_CMD, "statistics");
|
||||
GF(HISTOGRAM_CMD, "histogram");
|
||||
|
||||
GF(VERSION_CMD, "version");
|
||||
GF(VERSION, "1.4");
|
||||
GF(VERSION, "1.5");
|
||||
|
||||
static inline bool substringEquals(const char *str, const ::detail::FlashString *flashStr, const size_t &size)
|
||||
{
|
||||
@ -146,6 +147,8 @@ class Terminal {
|
||||
printUptime();
|
||||
} else if (substringEquals(m_inputBuffer, detail::STATISTICS_CMD, m_inputSize)) {
|
||||
printStatistics();
|
||||
} else if (substringEquals(m_inputBuffer, detail::HISTOGRAM_CMD, m_inputSize)) {
|
||||
printHistogram();
|
||||
} else if (substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) {
|
||||
printVersion();
|
||||
} else {
|
||||
@ -175,6 +178,7 @@ class Terminal {
|
||||
m_serial << detail::BOOTLOADER_CMD << F(" .: enters the bootloader after 3 seconds") << detail::ENDL;
|
||||
m_serial << detail::UPTIME_CMD << F(" .....: show system uptime") << detail::ENDL;
|
||||
m_serial << detail::STATISTICS_CMD << F(" .: Print overall statistics like min and max temp") << detail::ENDL;
|
||||
m_serial << detail::HISTOGRAM_CMD << F(" ..: Prints a histogram of the temperature") << detail::ENDL;
|
||||
m_serial << detail::VERSION_CMD << F(" ....: displays firmware version") << detail::ENDL;
|
||||
}
|
||||
|
||||
@ -202,7 +206,7 @@ class Terminal {
|
||||
m_serial.template txNumber<uint8_t, 10, 3, ' '>(Controller::mapTemperature(i));
|
||||
m_serial << F("%\t");
|
||||
for (uint8_t s = 0; s < Controller::mapTemperature(i); ++s) {
|
||||
m_serial << "#";
|
||||
m_serial << '#';
|
||||
}
|
||||
m_serial << detail::ENDL;
|
||||
}
|
||||
@ -239,12 +243,23 @@ class Terminal {
|
||||
|
||||
static void printStatistics()
|
||||
{
|
||||
char floatBuffer[16];
|
||||
m_serial << F("Minimum temperature .: ") << Statistics::getMinTemp() << F(" C") << detail::ENDL;
|
||||
m_serial << F("Maximum temperature .: ") << Statistics::getMaxTemp() << F(" C") << detail::ENDL;
|
||||
}
|
||||
|
||||
sprintf(floatBuffer, "%.2f", Statistics::getMinTemp());
|
||||
m_serial << F("Minimum temperature .: ") << floatBuffer << F(" C") << detail::ENDL;
|
||||
sprintf(floatBuffer, "%.2f", Statistics::getMaxTemp());
|
||||
m_serial << F("Maximum temperature .: ") << floatBuffer << F(" C") << detail::ENDL;
|
||||
static void printHistogram()
|
||||
{
|
||||
auto normalizationFactor = Statistics::getMaximumHistogramSamples();
|
||||
normalizationFactor = (normalizationFactor / 100 > 1) ? (normalizationFactor / 100) : 1;
|
||||
|
||||
for (uint8_t t = 10; t <= 60; ++t) {
|
||||
m_serial.template txNumber<uint8_t, 10, 2>(t);
|
||||
m_serial << F(" C\t");
|
||||
for (uint8_t i = 0; i < Statistics::getHistogram(t, normalizationFactor); ++i) {
|
||||
m_serial << '#';
|
||||
}
|
||||
m_serial << detail::ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
static void printVersion()
|
||||
|
Loading…
Reference in New Issue
Block a user