Added real time signal viewer

This commit is contained in:
Anne de Jong 2023-01-05 10:35:47 +01:00
parent 24b9a24b04
commit c7045a81e9
5 changed files with 220 additions and 4 deletions

View File

@ -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

View File

@ -19,6 +19,10 @@
* @{
*/
/**
* @brief Real time spectral estimator using Welch method of spectral
* estimation.
*/
class RtAps : public ThreadedInDataHandler {
std::unique_ptr<Filter> _filterPrototype;

View File

@ -0,0 +1,104 @@
/* #define DEBUGTRACE_ENABLED */
#include "debugtrace.hpp"
#include "lasp_rtsignalviewer.h"
#include <algorithm>
#include <mutex>
using std::cerr;
using std::endl;
using Lck = std::scoped_lock<std::mutex>;
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<us>(static_cast<us>(_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;
}

View File

@ -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 <memory>
#include <mutex>
/**
* \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;
};
/** @} */
/** @} */

View File

@ -1,11 +1,12 @@
/* #define DEBUGTRACE_ENABLED */
#include <armadillo>
#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 <armadillo>
#include <atomic>
#include <chrono>
#include <pybind11/pybind11.h>
@ -185,11 +186,10 @@ void init_datahandler(py::module &m) {
ppm.def(py::init<StreamMgr &>());
ppm.def("getCurrentValue", [](const PPMHandler &ppm) {
std::tuple<vd, arma::uvec> tp = ppm.getCurrentValue();
return py::make_tuple(ColToNpy<d>(std::get<0>(tp)),
ColToNpy<arma::uword>(std::get<1>(tp)));
ColToNpy<arma::uword>(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<c>(val);
return CubeToNpy<c>(val);
});
/// Real time Signal Viewer
///
py::class_<RtSignalViewer, ThreadedInDataHandler> rtsv(m, "RtSignalViewer");
rtsv.def(py::init<StreamMgr &, // StreamMgr
const d, // Time history
const us, // Resolution
const us // Channel number
>());
rtsv.def("getCurrentValue", [](RtSignalViewer &rt) {
dmat val = rt.getCurrentValue();
return MatToNpy<d>(val);
});
}