193 lines
5.2 KiB
C++
193 lines
5.2 KiB
C++
extern "C" {
|
|
# include <liblocky.h>
|
|
}
|
|
|
|
#include <algorithm>
|
|
#include <cinttypes>
|
|
#include <cmath>
|
|
#include <numeric>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <imgui.h>
|
|
#include <implot.h>
|
|
|
|
#include "app.hh"
|
|
#include "input.hh"
|
|
|
|
|
|
namespace pg {
|
|
namespace {
|
|
|
|
class Sensor final : public App {
|
|
public:
|
|
static inline TypeInfo kType = TypeInfo::Create<Sensor>("Sensor");
|
|
|
|
Sensor() noexcept {
|
|
}
|
|
|
|
void Update() noexcept override {
|
|
const auto em = ImGui::GetFontSize();
|
|
const auto id = "Sensor | "+
|
|
std::to_string(reinterpret_cast<uintptr_t>(this));
|
|
|
|
ImGui::SetNextWindowSize({64*em, 24*em}, ImGuiCond_Once);
|
|
if (ImGui::Begin(id.c_str())) {
|
|
bool mod = false;
|
|
ImGui::BeginGroup();
|
|
{
|
|
ImGui::PushItemWidth(6*em);
|
|
mod |= ImGui::DragInt("input slot", &src_, 1, 0, 1024);
|
|
mod |= ImGui::DragInt("start", &start_, 1, 0, 1024);
|
|
mod |= ImGui::DragInt("dur", &dur_, 1, 1, BLKY_SENSOR_MAX_DUR);
|
|
|
|
ImGui::Spacing();
|
|
ImGui::DragFloat2("pos", pos_, .001f);
|
|
ImGui::DragInt("offset", &offset_, 1, 0, 3);
|
|
if (ImGui::Button("add")) {
|
|
mod = true;
|
|
data_.emplace_back(pos_[0], pos_[1], offset_);
|
|
}
|
|
if (ImGui::BeginListBox("##points")) {
|
|
for (size_t i = 0; i < data_.size(); ++i) {
|
|
const auto& data = data_[i];
|
|
const auto name =
|
|
std::to_string(i)+". "+
|
|
std::to_string(data.pos[0])+","+
|
|
std::to_string(data.pos[1]);
|
|
ImGui::Selectable(name.c_str());
|
|
if (ImGui::BeginPopupContextItem()) {
|
|
if (ImGui::MenuItem("clear all")) {
|
|
mod = true;
|
|
data_.clear();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::SetTooltip(
|
|
"pos : %f,%f + %" PRIiMAX "\n"
|
|
"avg : %f\n"
|
|
"var : %f\n"
|
|
"correl: %f",
|
|
data.pos[0], data.pos[1], data.offset,
|
|
data.avg, data.var, data.correl);
|
|
}
|
|
}
|
|
ImGui::EndListBox();
|
|
}
|
|
|
|
double correl_abs_avg = 0;
|
|
for (auto& data : data_) {
|
|
correl_abs_avg += fabs(data.correl);
|
|
}
|
|
correl_abs_avg /= static_cast<float>(data_.size());
|
|
ImGui::Text("avg(abs(r)) = %f", correl_abs_avg);
|
|
|
|
double correl_abs_var = 0;
|
|
for (auto& data : data_) {
|
|
const auto diff = fabs(data.correl)-correl_abs_avg;
|
|
correl_abs_var += diff*diff;
|
|
}
|
|
correl_abs_var /= static_cast<float>(data_.size());
|
|
ImGui::Text("var(abs(r)) = %f", correl_abs_var);
|
|
|
|
ImGui::PopItemWidth();
|
|
if (mod) {
|
|
try {
|
|
Calc();
|
|
msg_ = "";
|
|
} catch (const char* msg) {
|
|
msg_ = msg;
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndGroup();
|
|
ImGui::SameLine();
|
|
ImGui::BeginGroup();
|
|
auto avail = ImGui::GetContentRegionAvail();
|
|
avail.x -= em;
|
|
avail.y -= em;
|
|
|
|
const ImVec2 size = {avail.x/2, avail.y};
|
|
|
|
if (ImPlot::BeginPlot("input value", size)) {
|
|
ImPlot::SetupAxis(ImAxis_X1, nullptr, ImPlotAxisFlags_AutoFit);
|
|
ImPlot::SetupAxisLimits(ImAxis_Y1, -0.1, 1.1);
|
|
for (size_t i = 0; i < data_.size(); ++i) {
|
|
const auto& d = data_[i];
|
|
ImPlot::PlotLine(
|
|
std::to_string(i).c_str(),
|
|
d.values.data(), static_cast<int>(d.values.size()), 1, start_);
|
|
}
|
|
ImPlot::EndPlot();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImPlot::BeginPlot("input histogram", size)) {
|
|
ImPlot::SetupAxisLimits(ImAxis_X1, 0, 1);
|
|
ImPlot::SetupAxis(ImAxis_Y1, nullptr, ImPlotAxisFlags_AutoFit);
|
|
for (size_t i = 0; i < data_.size(); ++i) {
|
|
const auto& d = data_[i];
|
|
ImPlot::PlotHistogram(
|
|
std::to_string(i).c_str(),
|
|
d.values.data(), static_cast<int>(d.values.size()));
|
|
}
|
|
ImPlot::EndPlot();
|
|
}
|
|
ImGui::EndGroup();
|
|
ImGui::TextUnformatted(msg_.c_str());
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
private:
|
|
int src_ = 0, start_ = 0, dur_ = 30;
|
|
float pos_[2];
|
|
int offset_;
|
|
|
|
struct Data final {
|
|
public:
|
|
float pos[2];
|
|
size_t offset;
|
|
std::vector<float> values;
|
|
|
|
double avg;
|
|
double var;
|
|
double cov;
|
|
double correl;
|
|
|
|
Data(float x, float y, int off) noexcept :
|
|
pos{x, y}, offset(static_cast<size_t>(off)) {
|
|
}
|
|
};
|
|
std::vector<Data> data_;
|
|
std::string msg_;
|
|
|
|
void Calc();
|
|
};
|
|
|
|
void Sensor::Calc() {
|
|
auto in = Input::instance().slots(static_cast<size_t>(src_));
|
|
if (!in) throw "missing slot";
|
|
|
|
if (dur_ == 0) throw "invalid time range";
|
|
|
|
for (auto& data : data_) {
|
|
const auto xf = std::clamp(data.pos[0], 0.f, 1.f);
|
|
const auto yf = std::clamp(data.pos[1], 0.f, 1.f);
|
|
|
|
const auto dur = static_cast<size_t>(dur_);
|
|
const auto st = static_cast<size_t>(start_);
|
|
data.values = in->FetchSamples(st, dur, xf, yf, data.offset);
|
|
|
|
blky_sensor_t sensor = {};
|
|
blky_sensor_feed(&sensor, data.values.data(), data.values.size());
|
|
data.avg = sensor.avg;
|
|
data.var = sensor.var;
|
|
data.cov = sensor.cov;
|
|
data.correl = sensor.correl;
|
|
}
|
|
}
|
|
|
|
}
|
|
} // namespace pg
|