extern "C" { # include } #include #include #include #include #include #include #include #include #include "app.hh" #include "input.hh" namespace pg { namespace { class Sensor final : public App { public: static inline TypeInfo kType = TypeInfo::Create("Sensor"); Sensor() noexcept { } void Update() noexcept override { const auto em = ImGui::GetFontSize(); const auto id = "Sensor | "+ std::to_string(reinterpret_cast(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(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(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(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(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 values; double avg; double var; double cov; double correl; Data(float x, float y, int off) noexcept : pos{x, y}, offset(static_cast(off)) { } }; std::vector data_; std::string msg_; void Calc(); }; void Sensor::Calc() { auto in = Input::instance().slots(static_cast(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(dur_); const auto st = static_cast(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