Rewrite on libavr
Same controller: thermistor on ADC0 averaged over 1000 free-running conversions, 50 kHz fan PWM on OC0B, 115200 Bd console with the full command set, EEPROM temperature histogram, watchdog-reset path into the boot section. The Steinhart-Hart math and the libm log are gone — the Beta equation and the cubic fan curve are consteval-evaluated into flash tables; the firmware never does floating point. Byte-identical .text in both libavr modes. Legacy stays on master. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
105
src/statistics.hpp
Normal file
105
src/statistics.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include <libavr/libavr.hpp>
|
||||
|
||||
#include "board.hpp"
|
||||
|
||||
// Temperature histogram: one uint32 bucket per °C 0..99, sampled once a
|
||||
// second, written back to EEPROM every 30 minutes (update() only touches
|
||||
// changed bytes). Erased EEPROM reads back as 0xffffffff — treated as 0.
|
||||
namespace app {
|
||||
|
||||
class statistics {
|
||||
static constexpr std::uint8_t range = 100;
|
||||
static constexpr std::uint32_t sample_delay_ms = 1'000;
|
||||
static constexpr std::uint32_t writeback_delay_ms = 1'800'000;
|
||||
|
||||
using stored = avr::eeprom::var<std::array<std::uint32_t, range>, 0>;
|
||||
|
||||
static inline std::array<std::uint32_t, range> histogram{};
|
||||
static inline std::uint64_t last_sample = 0;
|
||||
static inline std::uint64_t last_writeback = 0;
|
||||
|
||||
static constexpr std::uint8_t clamp(std::int8_t t)
|
||||
{
|
||||
return t < 0 ? 0 : (t >= range ? range - 1 : static_cast<std::uint8_t>(t));
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr auto claims = stored::claims;
|
||||
|
||||
static void init()
|
||||
{
|
||||
histogram = stored::read();
|
||||
for (auto &bucket : histogram)
|
||||
if (bucket == 0xffffffff)
|
||||
bucket = 0;
|
||||
}
|
||||
|
||||
static void record(std::int8_t celsius)
|
||||
{
|
||||
auto now = uptime::millis();
|
||||
if (now >= last_sample + sample_delay_ms) {
|
||||
++histogram[clamp(celsius)];
|
||||
last_sample = now;
|
||||
}
|
||||
if (now >= last_writeback + writeback_delay_ms) {
|
||||
save();
|
||||
last_writeback = now;
|
||||
}
|
||||
}
|
||||
|
||||
static void save()
|
||||
{
|
||||
stored::update(histogram);
|
||||
}
|
||||
|
||||
static void reset()
|
||||
{
|
||||
histogram = {};
|
||||
stored::update(histogram);
|
||||
}
|
||||
|
||||
static std::uint8_t min_temperature()
|
||||
{
|
||||
for (std::uint8_t i = 0; i < range; ++i)
|
||||
if (histogram[i])
|
||||
return i;
|
||||
return range;
|
||||
}
|
||||
|
||||
static std::uint8_t max_temperature()
|
||||
{
|
||||
for (std::uint8_t i = range; i > 0; --i)
|
||||
if (histogram[i - 1])
|
||||
return i - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::uint64_t total_samples()
|
||||
{
|
||||
std::uint64_t total = 0;
|
||||
for (auto bucket : histogram)
|
||||
total += bucket;
|
||||
return total;
|
||||
}
|
||||
|
||||
static std::uint32_t highest_bucket()
|
||||
{
|
||||
std::uint32_t highest = 0;
|
||||
for (auto bucket : histogram)
|
||||
if (bucket > highest)
|
||||
highest = bucket;
|
||||
return highest;
|
||||
}
|
||||
|
||||
static std::uint32_t bucket(std::uint8_t celsius)
|
||||
{
|
||||
return histogram[clamp(static_cast<std::int8_t>(celsius))];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
Reference in New Issue
Block a user