From c7045a81e9dee56366c578b9cd8b9d7d4bd2629a Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - Redu-Sone B.V., ASCEE V.O.F" Date: Thu, 5 Jan 2023 10:35:47 +0100 Subject: [PATCH] Added real time signal viewer --- src/lasp/dsp/CMakeLists.txt | 1 + src/lasp/dsp/lasp_rtaps.h | 4 + src/lasp/dsp/lasp_rtsignalviewer.cpp | 104 +++++++++++++++++++++ src/lasp/dsp/lasp_rtsignalviewer.h | 93 ++++++++++++++++++ src/lasp/pybind11/lasp_pyindatahandler.cpp | 22 ++++- 5 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 src/lasp/dsp/lasp_rtsignalviewer.cpp create mode 100644 src/lasp/dsp/lasp_rtsignalviewer.h diff --git a/src/lasp/dsp/CMakeLists.txt b/src/lasp/dsp/CMakeLists.txt index 61a8f59..6c695b9 100644 --- a/src/lasp/dsp/CMakeLists.txt +++ b/src/lasp/dsp/CMakeLists.txt @@ -7,6 +7,7 @@ set(lasp_dsp_files lasp_window.cpp lasp_fft.cpp lasp_rtaps.cpp + lasp_rtsignalviewer.cpp lasp_avpowerspectra.cpp lasp_biquadbank.cpp lasp_thread.cpp diff --git a/src/lasp/dsp/lasp_rtaps.h b/src/lasp/dsp/lasp_rtaps.h index 5e95a2a..58cf047 100644 --- a/src/lasp/dsp/lasp_rtaps.h +++ b/src/lasp/dsp/lasp_rtaps.h @@ -19,6 +19,10 @@ * @{ */ +/** + * @brief Real time spectral estimator using Welch method of spectral + * estimation. + */ class RtAps : public ThreadedInDataHandler { std::unique_ptr _filterPrototype; diff --git a/src/lasp/dsp/lasp_rtsignalviewer.cpp b/src/lasp/dsp/lasp_rtsignalviewer.cpp new file mode 100644 index 0000000..fa16ca6 --- /dev/null +++ b/src/lasp/dsp/lasp_rtsignalviewer.cpp @@ -0,0 +1,104 @@ +/* #define DEBUGTRACE_ENABLED */ +#include "debugtrace.hpp" +#include "lasp_rtsignalviewer.h" +#include +#include + +using std::cerr; +using std::endl; +using Lck = std::scoped_lock; +using rte = std::runtime_error; + +RtSignalViewer::RtSignalViewer(StreamMgr &mgr, const d approx_time_hist, + const us resolution, const us channel) + : ThreadedInDataHandler(mgr), _approx_time_hist(approx_time_hist), + _resolution(resolution), _channel(channel) { + + DEBUGTRACE_ENTER; + + if (approx_time_hist <= 0) { + throw rte("Invalid time history. Should be > 0"); + } + if (resolution <= 1) { + throw rte("Invalid resolution. Should be > 1"); + } + } + +bool RtSignalViewer::inCallback_threaded(const DaqData &data) { + + DEBUGTRACE_ENTER; + + Lck lck(_sv_mtx); + + /// Push new data in time buffer, scaled by sensitivity + _tb.push(data.toFloat(_channel) / _sens(_channel)); + + _dat.col(0) += _nsamples_per_point / _fs; + /// Required number of samples for a new 'point' + while (_tb.size() > _nsamples_per_point) { + // Roll forward time column + _dat.col(0) += _nsamples_per_point / _fs; + vd newvals = _tb.pop(_nsamples_per_point); + d newmax = newvals.max(); + d newmin = newvals.min(); + + vd mincol = _dat.col(1); + vd maxcol = _dat.col(2); + _dat(arma::span(0, _resolution-2),1) = mincol(arma::span(1, _resolution-1)); + _dat(arma::span(0, _resolution-2),2) = maxcol(arma::span(1, _resolution-1)); + _dat(_resolution-1, 1) = newmin; + _dat(_resolution-1, 2) = newmax; + } + + return true; +} + +RtSignalViewer::~RtSignalViewer() { + Lck lck(_sv_mtx); + stop(); +} +void RtSignalViewer::reset(const Daq *daq) { + + DEBUGTRACE_ENTER; + Lck lck(_sv_mtx); + + // Reset time buffer + _tb.reset(); + + if (daq) { + _fs = daq->samplerate(); + + /// Update sensitivity values + _sens.resize(daq->neninchannels()); + us i = 0; + for (const auto &ch : daq->enabledInChannels()) { + _sens[i] = ch.sensitivity; + i++; + } + + /// The number of samples per point, based on which minmax are computed + _nsamples_per_point = + std::max(static_cast(_approx_time_hist / _resolution * _fs), 1); + + const d dt_points = _nsamples_per_point / _fs; + const d tend = dt_points * _resolution; + + // Initialize data array + _dat.resize(_resolution, 3); + _dat.col(0) = arma::linspace(0, tend, _resolution); + _dat.col(1).zeros(); + _dat.col(2).zeros(); + + } else { + _sens.zeros(); + _fs = 0; + _dat.reset(); + } +} + +dmat RtSignalViewer::getCurrentValue() const { + + DEBUGTRACE_ENTER; + Lck lck(_sv_mtx); + return _dat; +} diff --git a/src/lasp/dsp/lasp_rtsignalviewer.h b/src/lasp/dsp/lasp_rtsignalviewer.h new file mode 100644 index 0000000..85fefd1 --- /dev/null +++ b/src/lasp/dsp/lasp_rtsignalviewer.h @@ -0,0 +1,93 @@ +// lasp_threadedaps.h +// +// Author: J.A. de Jong - ASCEE +// +// Description: Real Time Signal Viewer. +#pragma once +#include "lasp_avpowerspectra.h" +#include "lasp_filter.h" +#include "lasp_mathtypes.h" +#include "lasp_threadedindatahandler.h" +#include "lasp_timebuffer.h" +#include +#include + +/** + * \addtogroup dsp + * @{ + * + * \addtogroup rt + * @{ + */ + +/** + * @brief Real time signal viewer. Shows envelope of the signal based on amount + * of history shown. + */ +class RtSignalViewer : public ThreadedInDataHandler { + + /** + * @brief Storage for sensitivity values + */ + vd _sens; + + /** + * @brief Storage for time samples + */ + TimeBuffer _tb; + + /** + * @brief The amount of time history to show + */ + const d _approx_time_hist; + /** + * @brief The number of points to show + */ + const us _resolution; + + /** + * @brief The channel to show + */ + const us _channel; + + us _nsamples_per_point; + d _fs; + + dmat _dat; + + /** + * @brief Mutex only for _signalviewer. + */ + mutable std::mutex _sv_mtx; + +public: + /** + * @brief + * + * @param mgr Stream manager + * @param approx_time_hist The *approximate* time history to show. The exact + * time history will be calculated such that there fits an integer number of + * samples in a single 'point'. + * @param resolution Number of time points + * @param channel The channel number + */ + RtSignalViewer(StreamMgr &mgr, const d approx_time_hist, const us resolution, + const us channel); + + ~RtSignalViewer(); + + /** + * @brief Returns a 2D array, with: + * - first column: time instances. + * - second column: minimum value of signal + * - third column: maximum value of signal + * + */ + dmat getCurrentValue() const; + + bool inCallback_threaded(const DaqData &) override final; + void reset(const Daq *) override final; +}; + +/** @} */ +/** @} */ diff --git a/src/lasp/pybind11/lasp_pyindatahandler.cpp b/src/lasp/pybind11/lasp_pyindatahandler.cpp index aa3fb02..116efc1 100644 --- a/src/lasp/pybind11/lasp_pyindatahandler.cpp +++ b/src/lasp/pybind11/lasp_pyindatahandler.cpp @@ -1,11 +1,12 @@ /* #define DEBUGTRACE_ENABLED */ -#include #include "arma_npy.h" #include "debugtrace.hpp" #include "lasp_ppm.h" #include "lasp_rtaps.h" +#include "lasp_rtsignalviewer.h" #include "lasp_streammgr.h" #include "lasp_threadedindatahandler.h" +#include #include #include #include @@ -185,11 +186,10 @@ void init_datahandler(py::module &m) { ppm.def(py::init()); ppm.def("getCurrentValue", [](const PPMHandler &ppm) { - std::tuple tp = ppm.getCurrentValue(); return py::make_tuple(ColToNpy(std::get<0>(tp)), - ColToNpy(std::get<1>(tp))); + ColToNpy(std::get<1>(tp))); }); /// Real time Aps @@ -216,6 +216,20 @@ void init_datahandler(py::module &m) { rtaps.def("getCurrentValue", [](RtAps &rt) { ccube val = rt.getCurrentValue(); - return CubeToNpy(val); + return CubeToNpy(val); + }); + + /// Real time Signal Viewer + /// + py::class_ rtsv(m, "RtSignalViewer"); + rtsv.def(py::init()); + + rtsv.def("getCurrentValue", [](RtSignalViewer &rt) { + dmat val = rt.getCurrentValue(); + return MatToNpy(val); }); }