Entropy Terminal

class RingBuffer {
public:
    explicit RingBuffer(size_t cap) : cap_(cap) {}
    void push(T v) {
        std::lock_guard lk(mx_);
        if (q_.size() == cap_) q_.pop_front();
        q_.push_back(std::move(v));
        cv_.notify_all();
    }
    std::vector snapshot() const {
        std::lock_guard lk(mx_);
        return std::vector(q_.begin(), q_.end());
    }
    std::optional wait_pop_for(Ms d) {
        std::unique_lock lk(mx_);
        if (!cv_.wait_for(lk, d, [&]{ return !q_.empty() || stop_; })) return {};
        if (q_.empty()) return {};
        T v = std::move(q_.front()); q_.pop_front(); return v;
    }
    void stop() {
        { std::lock_guard lk(mx_); stop_ = true; }
        cv_.notify_all();
    }
private:
    size_t cap_;
    mutable std::mutex mx_;
    std::condition_variable cv_;
    std::deque q_;
    bool stop_{false};
};
struct Ema {
    double alpha{0.2};
    std::optional s;
    double operator()(double x) {
        if (!s) s = x;
        else    *s = alpha * x + (1.0 - alpha) * (*s);
        return *s;
    }
};
struct PeakHold {
    double value{0.0};
    double decay{0.95}; // 1.0 = no decay, 0.9 = faster fall
    double update(double x) {
        value = std::max(value * decay, x);
        return value;
    }
};
static inline std::string sparkline(const std::vector& xs, size_t width=40) {
    if (xs.empty()) return std::string(width, ' ');
    double lo = *std::min_element(xs.begin(), xs.end());
    double hi = *std::max_element(xs.begin(), xs.end());
    if (hi - lo < 1e-12) hi = lo + 1e-12;
    std::string out; out.reserve(width);
    for (size_t i=0;i(i * (xs.size()-1) / std::max(1,width-1));
        double n = (xs[idx]-lo)/(hi-lo);
        size_t b = std::min(bars.size()-1, static_cast(n*(bars.size()-1)));
        out.push_back(bars[b]);
    }
    return out;
}
class Sensor {
public:
    virtual ~Sensor() = default;
    virtual std::string name() const = 0;
    virtual double read() = 0;
protected:
    std::mt19937 rng{std::random_device{}()};
};
class TempSensor : public Sensor {
public:
    std::string name() const override { return "temp"; }
    double read() override {
        std::normal_distribution base(23.5, 0.15);
        double drift = 0.6 * std::sin(now_ms()/1000.0 * 0.3);
        return base(rng) + drift;
    }
};
class LuxSensor : public Sensor {
public:
    std::string name() const override { return "lux"; }
    double read() override {
        std::uniform_real_distribution u(450.0, 620.0);
        double blink = (std::sin(now_ms()/1000.0 * 2.1) + 1.0) * 40.0;
        return u(rng) + blink;
    }
};
class ImuAx : public Sensor {
public:
    std::string name() const override { return "ax"; }
    double read() override {
        std::normal_distribution n(0.0, 0.03);
        return 0.2*std::sin(now_ms()/1000.0*1.7) + n(rng);
    }
};
class ImuAy : public Sensor {
public:
    std::string name() const override { return "ay"; }
    double read() override {
        std::normal_distribution n(0.0, 0.03);
        return 0.2*std::cos(now_ms()/1000.0*1.1) + n(rng);
    }
};
class ImuAz : public Sensor {
public:
    std::string name() const override { return "az"; }
    double read() override {
        std::normal_distribution n(0.0, 0.02);
        return 0.98 + 0.02*std::sin(now_ms()/1000.0*0.7) + n(rng);
    }
};
class Acquisition {
public:
    explicit Acquisition(int hz=25)
        : period_(Ms(1000/hz)),
          out_(std::make_shared>(1024)) {}
    void start() {
        if (running_) return;
        running_ = true;
        thr_ = std::thread(&Acquisition::loop, this);
    }
    void stop() {
        running_ = false;
        if (thr_.joinable()) thr_.join();
        out_->stop();
    }
    std::shared_ptr> bus() const { return out_; }
private:
    void loop() {
        TempSensor ts; LuxSensor ls; ImuAx ax; ImuAy ay; ImuAz az;
        Ema eTemp{0.15}, eLux{0.12}, eAx{0.2}, eAy{0.2}, eAz{0.12};
        auto next = Clock::now();
        while (running_) {
            next += period_;
            Sample s {
                now_ms(),
                eTemp(ts.read()),
                eLux(ls.read()),
                eAx(ax.read()), eAy(ay.read()), eAz(az.read())
            };
            out_->push(s);
            std::this_thread::sleep_until(next);
        }
    }
    Ms period_;
    std::shared_ptr> out_;
    std::atomic running_{false};
    std::thread thr_;
};
class CsvLogger {
public:
    explicit CsvLogger(const std::string& path) : f_(path, std::ios::out) {
        if (f_) f_ << "t_ms,temp,lux,ax,ay,az\n";
    }
    void write(const Sample& s) {
        if (!f_) return;
        f_ << s.t << ',' << s.temp << ',' << s.lux << ','
           << s.ax << ',' << s.ay << ',' << s.az << '\n';
    }
private:
    std::ofstream f_;
};
class Dashboard {
public:
    explicit Dashboard(std::shared_ptr> bus)
      : bus_(std::move(bus)) {}
    void run(int seconds=10) {
        CsvLogger log("telemetry.csv");
        PeakHold pkTemp{0.0, 0.97}, pkLux{0.0, 0.97}, pkAcc{0.0, 0.96};
        std::vector hTemp, hLux, hAcc; hTemp.reserve(512); hLux.reserve(512); hAcc.reserve(512);

        auto t0 = Clock::now();
        while (std::chrono::duration_cast(Clock::now()-t0).count() < seconds*1000) {
            auto opt = bus_->wait_pop_for(Ms(200));
            if (!opt) continue;
            auto s = *opt; log.write(s);
            double accMag = std::sqrt(s.ax*s.ax + s.ay*s.ay + s.az*s.az);
            hTemp.push_back(s.temp); if (hTemp.size()>240) hTemp.erase(hTemp.begin());
            hLux.push_back(s.lux);   if (hLux.size()>240)  hLux.erase(hLux.begin());
            hAcc.push_back(accMag);  if (hAcc.size()>240)  hAcc.erase(hAcc.begin());
            
            pkTemp.update(s.temp);
            pkLux.update(s.lux);
            pkAcc.update(accMag);
            render(hTemp, hLux, hAcc, s, pkTemp.value, pkLux.value, pkAcc.value);
        }
    }
  
INITIALIZING WATER ENTROPY
Loading vectors...
Anomaly Equalizer