Waterfall chart to capture history of detections

This commit is contained in:
Sassa NF
2024-09-30 07:36:36 +01:00
parent 30883386c4
commit ad7ad3d8af
8 changed files with 524 additions and 19 deletions
+57
View File
@@ -0,0 +1,57 @@
#include "charts.h"
#include <cstdint>
void WaterfallChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
Chart::reset(x, y, w, h);
model->reset(model->times[0], w);
update_to = model->buckets;
}
void WaterfallChart::updatePoint(uint64_t t, float x, float y)
{
if (x < min_x || x >= max_x)
{
return;
}
update_to = max(update_to, model->updateModel(t, x2pos(x), y >= level_y));
}
void WaterfallChart::draw()
{
size_t h = min(update_to, (size_t)height);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < width; x++)
{
bool b = model->counts[y][x] > 0 &&
(model->events[y][x] >= model->counts[y][x] * threshold);
if (b)
{
display.setColor(WHITE);
}
else
{
display.setColor(BLACK);
}
display.setPixel(pos_x + x, pos_y + y);
}
}
update_to = 0;
}
int WaterfallChart::x2pos(float x)
{
if (x < min_x)
x = min_x;
if (x > max_x)
x = max_x;
return width * (x - min_x) / (max_x - min_x);
}
+25
View File
@@ -3,6 +3,7 @@
#include <OLEDDisplay.h>
#include <cstdint>
#include <models.h>
#include <stdlib.h>
struct Chart
@@ -128,4 +129,28 @@ struct StackedChart : Chart
void draw() override;
};
struct WaterfallChart : Chart
{
float min_x, max_x;
float level_y, threshold;
size_t update_to;
WaterfallModel *model;
WaterfallChart(OLEDDisplay &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
float min_x, float max_x, float level_y, float threshold,
WaterfallModel *m)
: Chart(d, x, y, w, h), model(m), min_x(min_x), max_x(max_x), level_y(level_y),
threshold(threshold), update_to(m->buckets) {};
void updatePoint(uint64_t t, float x, float y);
void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override;
void draw() override;
int x2pos(float x);
};
#endif
+153
View File
@@ -0,0 +1,153 @@
#include "models.h"
#include <cstring>
WaterfallModel::WaterfallModel(size_t w, uint64_t base_dt, size_t m_sz,
const size_t *multiples)
{
width = w;
size_t dt_sz = 0;
for (int i = 0; i < m_sz; i++)
dt_sz += multiples[i];
buckets = dt_sz;
dt = new uint64_t[buckets];
events = new uint32_t *[buckets];
counts = new uint32_t *[buckets];
times = new uint64_t[buckets];
uint64_t m = base_dt;
for (int i = 0, j = 0; i < m_sz; i++)
{
for (int k = 0; k < multiples[i]; k++, j++)
{
dt[j] = m;
events[j] = new uint32_t[width];
counts[j] = new uint32_t[width];
}
m *= multiples[i];
}
}
void WaterfallModel::reset(uint64_t t0, size_t w)
{
if (w != width)
{
width = w;
for (int i = 0; i < buckets; i++)
{
delete[] counts[i];
delete[] events[i];
counts[i] = new uint32_t[w];
events[i] = new uint32_t[w];
}
}
for (int i = 0; i < buckets; i++)
{
memset(counts[i], 0, width * sizeof(uint32_t));
memset(events[i], 0, width * sizeof(uint32_t));
times[i] = t0 + dt[i];
}
}
/*
* The model is literally a stack of counters:
* - incomplete second
* - n complete seconds
* - incomplete minute
* - n complete minutes
* - ...
*
* updateModel updates incomplete second. When the second becomes complete, it
* gets pushed to complete seconds, and the last complete second is rotated out
* and it gets added to incomplete minute. This gets repeated for incomplete
* minutes, etc.
*/
size_t WaterfallModel::updateModel(uint16_t t, size_t x, uint16_t y)
{
size_t changed = 1;
while (t > times[0])
{
changed = push();
}
counts[0][x]++;
events[0][x] += y;
return changed;
}
size_t WaterfallModel::push()
{
size_t i = 1;
for (; i < buckets; i++)
{
if (dt[i - 1] == dt[i])
continue;
if (times[i - 1] <= times[i])
break;
times[i - 1] = times[i] + dt[i];
}
uint64_t t0 = times[0];
uint32_t *cc = counts[i - 1];
uint32_t *ee = events[i - 1];
memmove(times + 1, times, (i - 1) * sizeof(uint64_t));
memmove(counts + 1, counts, (i - 1) * sizeof(uint32_t *));
memmove(events + 1, events, (i - 1) * sizeof(uint32_t *));
if (i < buckets)
{
for (int j = 0; j < width; j++)
{
counts[i][j] += cc[j];
events[i][j] += ee[j];
}
i++;
}
memset(cc, 0, width * sizeof(uint32_t));
memset(ee, 0, width * sizeof(uint32_t));
counts[0] = cc;
events[0] = ee;
times[0] = t0 + dt[0];
return i;
}
#ifdef TO_STRING
#include <sstream>
#include <string>
#endif
char *WaterfallModel::toString()
{
#ifdef TO_STRING
std::stringstream r;
r << "w:" << width << " b:" << buckets << " [";
for (int i = 0; i < buckets; i++)
{
r << "dt:" << dt[i] << " t:" << times[i] << " [";
for (int j = 0; j < width; j++)
r << " c:" << counts[i][j] << " e:" << events[i][j];
r << " ]";
}
r << " ]";
char *ret = new char[r.str().length() + 1];
strncpy(ret, r.str().c_str(), r.str().length());
#else
char *ret = NULL;
#endif
return ret;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef CHARTS_MODELS_H
#define CHARTS_MODELS_H
#include <cstdint>
#include <cstring>
#include <stdlib.h>
struct WaterfallModel
{
uint32_t **events;
uint32_t **counts;
uint64_t *times;
uint64_t *dt;
size_t buckets;
size_t width;
WaterfallModel(size_t w, uint64_t base_dt, size_t m_sz, const size_t *multiples);
void reset(uint64_t t0, size_t width);
size_t updateModel(uint16_t t, size_t x, uint16_t y);
size_t push();
char *toString();
};
#endif