Code cleanup. RescanDaqdevices changed API. DaqApi string returns only apiname. Added comments, added Doxygen groups, exported FFT wisdom load / store to Python. SLM stores reference level squared. Added comments on SLM Lpeak, Leq and Lmax, a lot of using rte = std::runtime_error, added Window string conversion, Pybind11 enum no longer exports values, added cpp_enum to convert Qty to an enumerated value in C++ code. Removed class and methods to fill comboboxes. Does not belong in LASP, but in ACME instead. DeviceInfo has operator string().
This commit is contained in:
parent
b629edde4c
commit
b200b465f6
@ -3,12 +3,9 @@
|
||||
LASP: Library for Acoustic Signal Processing
|
||||
|
||||
"""
|
||||
from .lasp_common import (P_REF, FreqWeighting, TimeWeighting, getTime,
|
||||
getFreq, Qty, SIQtys, Window, lasp_shelve,
|
||||
this_lasp_shelve, W_REF, U_REF, I_REF, dBFS_REF,
|
||||
AvType)
|
||||
from .lasp_cpp import *
|
||||
|
||||
import lasp.lasp_cpp
|
||||
from .lasp_common import *
|
||||
__version__ = lasp_cpp.__version__
|
||||
|
||||
# from .lasp_imptube import * # TwoMicImpedanceTube
|
||||
|
@ -125,7 +125,7 @@ public:
|
||||
return (apiname == other.apiname && apicode == other.apicode &&
|
||||
api_specific_subcode == other.api_specific_subcode);
|
||||
}
|
||||
operator string() const { return apiname + ", code: " + to_string(apicode); }
|
||||
operator string() const { return apiname; }
|
||||
static std::vector<DaqApi> getAvailableApis();
|
||||
};
|
||||
|
||||
@ -160,12 +160,9 @@ public:
|
||||
/**
|
||||
* @brief Possible physical quantities that are recorded.
|
||||
*/
|
||||
enum class Qty {
|
||||
Number,
|
||||
AcousticPressure,
|
||||
Voltage,
|
||||
UserDefined
|
||||
};
|
||||
enum class Qty { Number, AcousticPressure, Voltage, UserDefined };
|
||||
|
||||
DaqChannel() = default;
|
||||
|
||||
/**
|
||||
* @brief Whether the channel is enabled.
|
||||
|
@ -11,9 +11,7 @@ using std::cerr;
|
||||
using std::endl;
|
||||
using rte = std::runtime_error;
|
||||
|
||||
InDataHandler::InDataHandler(StreamMgr &mgr) : _mgr(mgr) {
|
||||
DEBUGTRACE_ENTER;
|
||||
}
|
||||
InDataHandler::InDataHandler(StreamMgr &mgr) : _mgr(mgr) { DEBUGTRACE_ENTER; }
|
||||
void InDataHandler::start() {
|
||||
DEBUGTRACE_ENTER;
|
||||
_mgr.addInDataHandler(*this);
|
||||
@ -57,9 +55,13 @@ void StreamMgr::checkRightThread() const {
|
||||
}
|
||||
#endif
|
||||
|
||||
void StreamMgr::rescanDAQDevices(std::function<void()> callback,
|
||||
bool background) {
|
||||
void StreamMgr::rescanDAQDevices(bool background,
|
||||
std::function<void()> callback) {
|
||||
checkRightThread();
|
||||
if (_inputStream || _outputStream) {
|
||||
throw rte("Rescanning DAQ devices only possible when no stream is running");
|
||||
}
|
||||
_devices.clear();
|
||||
auto &pool = getPool();
|
||||
if (background) {
|
||||
pool.push_task(&StreamMgr::rescanDAQDevices_impl, this, callback);
|
||||
@ -192,9 +194,9 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
|
||||
{
|
||||
std::scoped_lock lck(_devices_mtx);
|
||||
|
||||
auto devinfo2 =
|
||||
std::find_if(_devices.cbegin(), _devices.cend(),
|
||||
[&config](const DeviceInfo& d) { return config.match(d); });
|
||||
auto devinfo2 = std::find_if(
|
||||
_devices.cbegin(), _devices.cend(),
|
||||
[&config](const DeviceInfo &d) { return config.match(d); });
|
||||
if (devinfo2 == std::cend(_devices)) {
|
||||
throw rte("Could not find a device with name " + config.device_name +
|
||||
" in list of devices.");
|
||||
@ -212,7 +214,7 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
|
||||
|
||||
if (!isInput && !isOutput) {
|
||||
throw rte("Neither input, nor output channels enabled for "
|
||||
"stream. Cannotr start.");
|
||||
"stream. Cannot start.");
|
||||
}
|
||||
|
||||
if ((isDuplex || isInput) && _inputStream) {
|
||||
|
@ -130,7 +130,7 @@ public:
|
||||
* @return A copy of the internal stored list of devices
|
||||
*/
|
||||
std::vector<DeviceInfo> getDeviceInfo() const {
|
||||
std::scoped_lock lck(const_cast<std::mutex&>(_devices_mtx));
|
||||
std::scoped_lock lck(const_cast<std::mutex &>(_devices_mtx));
|
||||
return _devices;
|
||||
}
|
||||
|
||||
@ -138,13 +138,13 @@ public:
|
||||
* @brief Triggers a background scan of the DAQ devices, which updates the
|
||||
* internally stored list of devices.
|
||||
*
|
||||
* @param callback Function to call when complete.
|
||||
* @param background Perform searching for DAQ devices in the background. If
|
||||
* set to true, the function returns immediately.
|
||||
* @param callback Function to call when complete.
|
||||
*/
|
||||
void
|
||||
rescanDAQDevices(std::function<void()> callback = std::function<void()>(),
|
||||
bool background = false);
|
||||
rescanDAQDevices(bool background = false,
|
||||
std::function<void()> callback = std::function<void()>());
|
||||
|
||||
/**
|
||||
* @brief Start a stream based on given configuration.
|
||||
|
@ -1,10 +1,12 @@
|
||||
#include <optional>
|
||||
#define DEBUGTRACE_ENABLED
|
||||
/* #define DEBUGTRACE_ENABLED */
|
||||
#include "debugtrace.hpp"
|
||||
#include <optional>
|
||||
#include "lasp_avpowerspectra.h"
|
||||
#include <cmath>
|
||||
|
||||
using std::runtime_error;
|
||||
using rte = std::runtime_error;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
PowerSpectra::PowerSpectra(const us nfft, const Window::WindowType w)
|
||||
: PowerSpectra(Window::create(w, nfft)) {}
|
||||
@ -35,7 +37,7 @@ arma::Cube<c> PowerSpectra::compute(const dmat &input) {
|
||||
_scale_fac * (rfft.col(i) % arma::conj(rfft.col(j)));
|
||||
|
||||
output.slice(j).col(i)(0) /= 2;
|
||||
output.slice(j).col(i)(nfft/2) /= 2;
|
||||
output.slice(j).col(i)(nfft / 2) /= 2;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
@ -48,13 +50,13 @@ AvPowerSpectra::AvPowerSpectra(const us nfft, const Window::WindowType w,
|
||||
|
||||
DEBUGTRACE_ENTER;
|
||||
if (overlap_percentage >= 100 || overlap_percentage < 0) {
|
||||
throw runtime_error("Overlap percentage should be >= 0 and < 100");
|
||||
throw rte("Overlap percentage should be >= 0 and < 100");
|
||||
}
|
||||
|
||||
_overlap_keep = (nfft * overlap_percentage) / 100;
|
||||
if (_overlap_keep >= nfft) {
|
||||
throw runtime_error("Overlap is too high. Results in no jump. Please "
|
||||
"choose a smaller overlap percentage or a higher nfft");
|
||||
throw rte("Overlap is too high. Results in no jump. Please "
|
||||
"choose a smaller overlap percentage or a higher nfft");
|
||||
}
|
||||
if (time_constant < 0) {
|
||||
_mode = Mode::Averaging;
|
||||
@ -67,24 +69,27 @@ AvPowerSpectra::AvPowerSpectra(const us nfft, const Window::WindowType w,
|
||||
}
|
||||
|
||||
std::optional<arma::cx_cube> AvPowerSpectra::compute(const dmat &timedata) {
|
||||
DEBUGTRACE_ENTER;
|
||||
|
||||
_timeBuf.push(timedata);
|
||||
|
||||
std::optional<arma::cx_cube> res;
|
||||
|
||||
us i = 0;
|
||||
while (auto samples = _timeBuf.pop(_ps.nfft, _overlap_keep)) {
|
||||
/* DEBUGTRACE_PRINT((int)_mode); */
|
||||
switch (_mode) {
|
||||
case (Mode::Spectrogram): {
|
||||
res.emplace(_ps.compute(samples.value()));
|
||||
_est = _ps.compute(samples.value());
|
||||
} break;
|
||||
case (Mode::Averaging): {
|
||||
n_averages++;
|
||||
if (n_averages == 1) {
|
||||
_est = _ps.compute(samples.value());
|
||||
} else {
|
||||
_est = _est * (n_averages - 1) / n_averages +
|
||||
_est = _est * (static_cast<d>(n_averages - 1) / n_averages) +
|
||||
_ps.compute(samples.value()) / n_averages;
|
||||
}
|
||||
res = _est;
|
||||
} break;
|
||||
case (Mode::Leaking): {
|
||||
if (arma::size(_est) == arma::size(0, 0, 0)) {
|
||||
@ -92,11 +97,14 @@ std::optional<arma::cx_cube> AvPowerSpectra::compute(const dmat &timedata) {
|
||||
} else {
|
||||
_est = _alpha * _est + (1 - _alpha) * _ps.compute(samples.value());
|
||||
}
|
||||
res = _est;
|
||||
} break;
|
||||
} // end switch mode
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
if(i>0) {
|
||||
return _est;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<arma::cx_cube> AvPowerSpectra::get_est() {
|
||||
if (_est.n_cols > 0)
|
||||
|
@ -7,10 +7,13 @@
|
||||
#include <optional>
|
||||
|
||||
/** \defgroup dsp Digital Signal Processing utilities
|
||||
* \ingroup dsp
|
||||
* @{
|
||||
/**
|
||||
* @brief Computes single-sided cross-power spectra for a group of channels
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Computes single-sided cross-power spectra for a group of channels.
|
||||
* Only a single block of length fft, no averaging. This class should normally
|
||||
* not be used. Please refer to AvPowerSpectra instead.
|
||||
*/
|
||||
class PowerSpectra {
|
||||
public:
|
||||
|
@ -94,7 +94,7 @@ void BiquadBank::setGains(const vd &gains) {
|
||||
DEBUGTRACE_ENTER;
|
||||
const us nfilters = _filters.size();
|
||||
if (gains.size() != nfilters) {
|
||||
throw runtime_error("Invalid number of gain values given.");
|
||||
throw rte("Invalid number of gain values given.");
|
||||
}
|
||||
_gains = gains;
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
#pragma once
|
||||
#include "lasp_filter.h"
|
||||
|
||||
/**
|
||||
* \ingroup dsp
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief A set of Biquad filters in series.
|
||||
*/
|
||||
@ -74,3 +79,4 @@ public:
|
||||
void reset() override final;
|
||||
|
||||
};
|
||||
/** @} */
|
||||
|
@ -8,7 +8,7 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#define DEBUGTRACE_ENABLED
|
||||
/* #define DEBUGTRACE_ENABLED */
|
||||
#include "lasp_fft.h"
|
||||
#include "debugtrace.hpp"
|
||||
#include "lasp_config.h"
|
||||
@ -152,22 +152,29 @@ dmat Fft::ifft(const cmat &freqdata) {
|
||||
return res;
|
||||
}
|
||||
|
||||
void load_fft_wisdom(const char *wisdom) {
|
||||
void Fft::load_fft_wisdom(const std::string& wisdom) {
|
||||
#if LASP_FFT_BACKEND == Armadillo
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
if (wisdom) {
|
||||
int rv = fftw_import_wisdom_from_string(wisdom);
|
||||
if (wisdom.length() > 0) {
|
||||
int rv = fftw_import_wisdom_from_string(wisdom.c_str());
|
||||
if (rv != 1) {
|
||||
fprintf(stderr, "Error loading FFTW wisdom");
|
||||
throw rte("Error loading FFTW wisdom");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
char *store_fft_wisdom() {
|
||||
std::string Fft::store_fft_wisdom() {
|
||||
#if LASP_FFT_BACKEND == Armadillo
|
||||
return NULL;
|
||||
return "";
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
return fftw_export_wisdom_to_string();
|
||||
// It is not possible to let FFTW directly return a C++ string. We have to
|
||||
// put it in this container by copying in. Not a good solution if this has to
|
||||
// happen often.
|
||||
// Fortunately, this function is only called at the end of the program.
|
||||
char* wis = fftw_export_wisdom_to_string();
|
||||
std::string res {wis};
|
||||
free(wis);
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
@ -88,4 +88,20 @@ class Fft {
|
||||
*/
|
||||
dmat ifft(const cmat& freqdata);
|
||||
|
||||
/**
|
||||
* @brief Load FFT wisdom from a wisdom string. Function does nothing if
|
||||
* FFT backend is not FFTW
|
||||
*
|
||||
* @param wisdom Wisdom string content.
|
||||
*/
|
||||
static void load_fft_wisdom(const std::string& wisdom);
|
||||
|
||||
/**
|
||||
* @brief Return a string containing FFT wisdom storage. String is empty
|
||||
* for backend != FFTW
|
||||
*
|
||||
* @return FFT wisdom string
|
||||
*/
|
||||
static std::string store_fft_wisdom();
|
||||
|
||||
};
|
||||
|
@ -32,9 +32,9 @@ class Sine : public Siggen {
|
||||
*/
|
||||
Sine(const d freq_Hz);
|
||||
~Sine() = default;
|
||||
virtual vd genSignalUnscaled(const us nframes) override;
|
||||
virtual vd genSignalUnscaled(const us nframes) override final;
|
||||
void setFreq(const d newFreq);
|
||||
void resetImpl() override { phase=0; }
|
||||
void resetImpl() override final { phase=0; }
|
||||
};
|
||||
class Periodic: public Siggen {
|
||||
protected:
|
||||
@ -42,7 +42,7 @@ class Periodic: public Siggen {
|
||||
us _cur_pos = 0;
|
||||
public:
|
||||
|
||||
virtual vd genSignalUnscaled(const us nframes) override;
|
||||
virtual vd genSignalUnscaled(const us nframes) override final;
|
||||
~Periodic() = default;
|
||||
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ SLM::SLM(const d fs, const d Lref, const us downsampling_fac, const d tau,
|
||||
// components of
|
||||
// single pole low pass
|
||||
// filter
|
||||
Lref(Lref), // Reference level
|
||||
Lrefsq(Lref*Lref), // Reference level
|
||||
downsampling_fac(downsampling_fac),
|
||||
|
||||
// Initalize mean square
|
||||
@ -127,15 +127,7 @@ vd SLM::run_single(vd work,const us i) {
|
||||
Pmax(i) = std::max(Pmax(i), arma::max(work));
|
||||
|
||||
// Convert to levels in dB
|
||||
/* work.transform([&](d val) { */
|
||||
/* return 10 * log10((val */
|
||||
|
||||
/* + arma::datum::eps // Add a bit of machine epsilon to */
|
||||
/* // the values to not compute -inf. */
|
||||
/* ) / */
|
||||
/* (Lref * Lref)); */
|
||||
/* }); */
|
||||
work = 10*arma::log10((work+arma::datum::eps)/(Lref*Lref));
|
||||
work = 10*arma::log10((work+arma::datum::eps)/Lrefsq);
|
||||
|
||||
return work;
|
||||
}
|
||||
|
@ -4,6 +4,11 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
/**
|
||||
* \ingroup dsp
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Sound Level Meter implementation that gives a result for each
|
||||
* channel. A channel is the result of a filtered signal
|
||||
@ -25,7 +30,7 @@ class SLM {
|
||||
d _alpha = -1;
|
||||
vd _sp_storage;
|
||||
|
||||
d Lref; /// Reference value for computing decibels
|
||||
d Lrefsq; /// Square of reference value for computing decibels
|
||||
us downsampling_fac; /// Every x'th sample is returned.
|
||||
us cur_offset = 0; /// Storage for offset point in input arrays
|
||||
///
|
||||
@ -38,15 +43,15 @@ public:
|
||||
* @brief Public storage for the maximum signal power, after single pole
|
||||
* low-pass filter.
|
||||
*/
|
||||
vd Pmax; /// Storage for maximum computed signal power so far.
|
||||
vd Pmax; /// Storage for maximum computed signal power so far.
|
||||
/**
|
||||
* @brief Public storage for the peak signal power, before single pole
|
||||
* low-pass filter.
|
||||
*/
|
||||
vd Ppeak;
|
||||
|
||||
us N = 0; /// Counter for the number of time samples counted that came
|
||||
/// in;
|
||||
us N = 0; /// Counter for the number of time samples counted that came
|
||||
/// in;
|
||||
|
||||
/**
|
||||
* @brief Initialize a Sound Level Meter
|
||||
@ -118,13 +123,33 @@ public:
|
||||
*
|
||||
* @param input Raw input data
|
||||
*
|
||||
* @return Filtered level data.
|
||||
* @return Filtered level data for each filtered channel.
|
||||
*/
|
||||
dmat run(const vd &input);
|
||||
vd Lpeak() const { return 10*arma::log10(Ppeak/Lref);};
|
||||
vd Leq() const { return 10*arma::log10(Pm/Lref);};
|
||||
vd Lmax() const { return 10*arma::log10(Pmax/Lref);};
|
||||
|
||||
/**
|
||||
* @brief Calculates peak levels measured for each filter channel. The peak
|
||||
* level is just the highest instantaneous measured power value.
|
||||
*
|
||||
* @return vector of peak level values
|
||||
*/
|
||||
vd Lpeak() const { return 10 * arma::log10(Ppeak / Lrefsq); };
|
||||
/**
|
||||
* @brief Calculates equivalent (time-averaged) levels measured for each
|
||||
* filter channel
|
||||
*
|
||||
* @return vector of equivalent level values
|
||||
*/
|
||||
vd Leq() const { return 10 * arma::log10(Pm / Lrefsq); };
|
||||
/**
|
||||
* @brief Calculates max levels measured for each filter channel. The max
|
||||
* value is the maximum time-filtered (Fast / Slow) power level.
|
||||
*
|
||||
* @return vector of max level values
|
||||
*/
|
||||
vd Lmax() const { return 10 * arma::log10(Pmax / Lrefsq); };
|
||||
|
||||
private:
|
||||
vd run_single(vd input, const us filter_no);
|
||||
};
|
||||
/** @} */
|
||||
|
@ -1,4 +1,4 @@
|
||||
#define DEBUGTRACE_ENABLED
|
||||
/* #define DEBUGTRACE_ENABLED */
|
||||
#include "lasp_thread.h"
|
||||
#include "BS_thread_pool.hpp"
|
||||
#include "debugtrace.hpp"
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
using std::runtime_error;
|
||||
using rte = std::runtime_error;
|
||||
|
||||
class TimeBufferImp {
|
||||
/**
|
||||
@ -22,10 +22,11 @@ public:
|
||||
_storage.clear();
|
||||
}
|
||||
void push(const dmat &mat) {
|
||||
DEBUGTRACE_ENTER;
|
||||
#if LASP_DEBUG==1
|
||||
if(!_storage.empty()) {
|
||||
if(mat.n_cols != _storage.front().n_cols) {
|
||||
throw runtime_error("Invalid number of channels in mat");
|
||||
throw rte("Invalid number of channels in mat");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -39,7 +40,7 @@ public:
|
||||
DEBUGTRACE_ENTER;
|
||||
|
||||
if (keep > nsamples)
|
||||
throw runtime_error("keep should be <= nsamples");
|
||||
throw rte("keep should be <= nsamples");
|
||||
|
||||
if (nsamples <= n_frames()) {
|
||||
|
||||
|
@ -2,30 +2,36 @@
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
/* #define DEBUGTRACE_ENABLED */
|
||||
#include "debugtrace.hpp"
|
||||
#include "lasp_window.h"
|
||||
|
||||
using rte = std::runtime_error;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
// Safe some typing. Linspace form 0 up to (and NOT including N).
|
||||
#define lin0N arma::linspace(0, N - 1, N)
|
||||
|
||||
vd Window::hann(const us N) {
|
||||
return arma::pow(arma::sin(number_pi * lin0N), 2);
|
||||
return arma::pow(arma::sin((arma::datum::pi/N) * lin0N), 2);
|
||||
}
|
||||
vd Window::hamming(const us N) {
|
||||
d alpha = 25.0 / 46.0;
|
||||
return alpha - (1 - alpha) * arma::cos(2 * number_pi * lin0N / (N - 1));
|
||||
return alpha - (1 - alpha) * arma::cos(2 * number_pi * lin0N / N);
|
||||
}
|
||||
vd Window::blackman(const us N) {
|
||||
d a0 = 7938. / 18608.;
|
||||
d a1 = 9240. / 18608.;
|
||||
d a2 = 1430. / 18608.;
|
||||
return a0 - a1 * d_cos(2 * number_pi * lin0N / (N - 1)) +
|
||||
a2 * d_cos(4 * number_pi * lin0N / (N - 1));
|
||||
return a0 - a1 * d_cos((2 * number_pi/N) * lin0N) +
|
||||
a2 * d_cos((4 * number_pi / N)* lin0N );
|
||||
}
|
||||
|
||||
vd Window::rectangular(const us N) { return arma::ones(N); }
|
||||
|
||||
vd Window::bartlett(const us N) {
|
||||
return 1 - arma::abs(2 * (lin0N - (N - 1) / 2.) / (N - 1));
|
||||
return 1 - arma::abs(2 * (lin0N - (N - 1) / 2.) / N);
|
||||
}
|
||||
vd Window::create(const WindowType w, const us N) {
|
||||
|
||||
|
@ -15,6 +15,38 @@ class Window {
|
||||
Blackman = 4,
|
||||
|
||||
};
|
||||
/**
|
||||
* @brief Convert a window type enum to its equivalent text.
|
||||
*
|
||||
* @param wt The window type to convert
|
||||
*
|
||||
* @return Text string
|
||||
*/
|
||||
static std::string toText(const WindowType wt) {
|
||||
switch(wt) {
|
||||
case(WindowType::Hann): {
|
||||
return "Hann";
|
||||
}
|
||||
break;
|
||||
case(WindowType::Hamming): {
|
||||
return "Hamming";
|
||||
}
|
||||
break;
|
||||
case(WindowType::Rectangular): {
|
||||
return "Rectangular";
|
||||
}
|
||||
break;
|
||||
case(WindowType::Bartlett): {
|
||||
return "Bartlett";
|
||||
}
|
||||
break;
|
||||
case(WindowType::Blackman): {
|
||||
return "Blackman";
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw std::runtime_error("Not implemenented window type");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dispatcher: create a window based on enum type and len
|
||||
|
@ -6,7 +6,7 @@ from collections import namedtuple
|
||||
from dataclasses import dataclass
|
||||
from dataclasses_json import dataclass_json
|
||||
from enum import Enum, unique, auto
|
||||
from .lasp_cpp import Window as wWindow
|
||||
from .lasp_cpp import DaqChannel
|
||||
|
||||
"""
|
||||
Common definitions used throughout the code.
|
||||
@ -14,7 +14,7 @@ Common definitions used throughout the code.
|
||||
|
||||
__all__ = [
|
||||
'P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime', 'getFreq', 'Qty',
|
||||
'SIQtys', 'Window',
|
||||
'SIQtys',
|
||||
'lasp_shelve', 'this_lasp_shelve', 'W_REF', 'U_REF', 'I_REF', 'dBFS_REF',
|
||||
'AvType'
|
||||
]
|
||||
@ -39,6 +39,7 @@ U_REF = 5e-8 # 50 nano meter / s
|
||||
# hence this is the reference level as specified below.
|
||||
dBFS_REF = 0.5*2**0.5 # Which level would be -3.01 dBFS
|
||||
|
||||
|
||||
@unique
|
||||
class AvType(Enum):
|
||||
"""Specificying the type of data, for adding and removing callbacks from
|
||||
@ -52,7 +53,6 @@ class AvType(Enum):
|
||||
|
||||
# Both input as well as output
|
||||
audio_duplex = (2, 'duplex')
|
||||
# video = 4
|
||||
|
||||
|
||||
@dataclass_json
|
||||
@ -67,6 +67,7 @@ class Qty:
|
||||
# yet able to compute `dBPa's'
|
||||
level_ref_name: object
|
||||
level_ref_value: object
|
||||
cpp_enum: DaqChannel.Qty
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name} [{self.unit_symb}]'
|
||||
@ -91,14 +92,16 @@ class SIQtys(Enum):
|
||||
unit_symb='-',
|
||||
level_unit=('dBFS',),
|
||||
level_ref_name=('Relative to full scale sine wave',),
|
||||
level_ref_value=(dBFS_REF,)
|
||||
level_ref_value=(dBFS_REF,),
|
||||
cpp_enum = DaqChannel.Qty.Number
|
||||
)
|
||||
AP = Qty(name='Acoustic Pressure',
|
||||
unit_name='Pascal',
|
||||
unit_symb='Pa',
|
||||
level_unit=('dB SPL','dBPa'),
|
||||
level_ref_name=('2 micropascal', '1 pascal',),
|
||||
level_ref_value=(P_REF, 1)
|
||||
level_ref_value=(P_REF, 1),
|
||||
cpp_enum = DaqChannel.Qty.AcousticPressure
|
||||
)
|
||||
|
||||
V = Qty(name='Voltage',
|
||||
@ -107,6 +110,7 @@ class SIQtys(Enum):
|
||||
level_unit=('dBV',), # dBV
|
||||
level_ref_name=('1V',),
|
||||
level_ref_value=(1.0,),
|
||||
cpp_enum = DaqChannel.Qty.Voltage
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@ -268,33 +272,6 @@ class this_lasp_shelve(Shelve):
|
||||
node = platform.node()
|
||||
return os.path.join(lasp_appdir, f'{node}_config.shelve')
|
||||
|
||||
|
||||
@unique
|
||||
class Window(Enum):
|
||||
hann = (wWindow.Hann, 'Hann')
|
||||
hamming = (wWindow.Hamming, 'Hamming')
|
||||
rectangular = (wWindow.Rectangular, 'Rectangular')
|
||||
bartlett = (wWindow.Bartlett, 'Bartlett')
|
||||
blackman = (wWindow.Blackman, 'Blackman')
|
||||
|
||||
@staticmethod
|
||||
def fillComboBox(cb):
|
||||
"""
|
||||
Fill Windows to a combobox
|
||||
|
||||
Args:
|
||||
cb: QComboBox to fill
|
||||
"""
|
||||
cb.clear()
|
||||
for w in list(Window):
|
||||
cb.addItem(w.value[1], w)
|
||||
cb.setCurrentIndex(0)
|
||||
|
||||
@staticmethod
|
||||
def getCurrent(cb):
|
||||
return list(Window)[cb.currentIndex()]
|
||||
|
||||
|
||||
class TimeWeighting:
|
||||
none = (-1, 'Raw (no time weighting)')
|
||||
uufast = (1e-4, '0.1 ms')
|
||||
|
@ -18,6 +18,11 @@
|
||||
*
|
||||
* */
|
||||
|
||||
/**
|
||||
* \defgroup pybind Pybind11 wrapper code
|
||||
* @{
|
||||
*
|
||||
*/
|
||||
namespace py = pybind11;
|
||||
|
||||
void init_dsp(py::module &m);
|
||||
@ -45,3 +50,4 @@ PYBIND11_MODULE(lasp_cpp, m) {
|
||||
m.attr("LASP_VERSION_MAJOR") = LASP_VERSION_MAJOR;
|
||||
m.attr("LASP_VERSION_MINOR") = LASP_VERSION_MINOR;
|
||||
}
|
||||
/** @} */
|
||||
|
@ -27,8 +27,7 @@ void init_daq(py::module &m) {
|
||||
.value("threadError", Daq::StreamStatus::StreamError::threadError)
|
||||
.value("logicError", Daq::StreamStatus::StreamError::logicError)
|
||||
.value("apiSpecificError",
|
||||
Daq::StreamStatus::StreamError::apiSpecificError)
|
||||
.export_values();
|
||||
Daq::StreamStatus::StreamError::apiSpecificError);
|
||||
|
||||
/// Daq
|
||||
daq.def("neninchannels", &Daq::neninchannels);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "lasp_daqconfig.h"
|
||||
#include "lasp_deviceinfo.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/stl.h>
|
||||
@ -12,17 +13,31 @@ void init_daqconfiguration(py::module &m) {
|
||||
|
||||
/// DataType
|
||||
py::class_<DataTypeDescriptor> dtype_desc(m, "DataTypeDescriptor");
|
||||
|
||||
dtype_desc.def_readonly("name", &DataTypeDescriptor::name);
|
||||
dtype_desc.def_readonly("sw", &DataTypeDescriptor::sw);
|
||||
dtype_desc.def_readonly("is_floating", &DataTypeDescriptor::is_floating);
|
||||
dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype);
|
||||
|
||||
py::enum_<DataTypeDescriptor::DataType>(dtype_desc, "DataType")
|
||||
.value("dtype_fl32", DataTypeDescriptor::DataType::dtype_fl32)
|
||||
.value("dtype_fl64", DataTypeDescriptor::DataType::dtype_fl64)
|
||||
.value("dtype_int8", DataTypeDescriptor::DataType::dtype_int8)
|
||||
.value("dtype_int16", DataTypeDescriptor::DataType::dtype_int16)
|
||||
.value("dtype_int32", DataTypeDescriptor::DataType::dtype_int32)
|
||||
.export_values();
|
||||
.value("dtype_int32", DataTypeDescriptor::DataType::dtype_int32);
|
||||
|
||||
dtype_desc.def_static("toStr", [](const DataTypeDescriptor::DataType d) {
|
||||
return dtype_map.at(d).name;
|
||||
});
|
||||
dtype_desc.def_static("fromStr", [](const std::string &dtype_str) {
|
||||
decltype(dtype_map.cbegin()) d = std::find_if(
|
||||
dtype_map.cbegin(), dtype_map.cend(),
|
||||
[&dtype_str](const auto &d) { return d.second.name == dtype_str; });
|
||||
if (d != dtype_map.cend()) {
|
||||
return d->first;
|
||||
}
|
||||
throw std::runtime_error(dtype_str + " not found in list of data types");
|
||||
});
|
||||
|
||||
dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype);
|
||||
|
||||
@ -36,15 +51,8 @@ void init_daqconfiguration(py::module &m) {
|
||||
/// DaqChannel, DaqConfiguration
|
||||
py::class_<DaqConfiguration> daqconfig(m, "DaqConfiguration");
|
||||
|
||||
py::enum_<DaqChannel::Qty>(daqconfig, "Qty")
|
||||
.value("Number", DaqChannel::Qty::Number)
|
||||
.value("AcousticPressure", DaqChannel::Qty::AcousticPressure)
|
||||
.value("Voltage", DaqChannel::Qty::Voltage)
|
||||
.value("UserDefined", DaqChannel::Qty::UserDefined)
|
||||
.export_values();
|
||||
|
||||
dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype);
|
||||
py::class_<DaqChannel> daqchannel(m, "DaqChannel");
|
||||
daqchannel.def(py::init<>());
|
||||
daqchannel.def_readwrite("enabled", &DaqChannel::enabled);
|
||||
daqchannel.def_readwrite("name", &DaqChannel::name);
|
||||
daqchannel.def_readwrite("sensitivity", &DaqChannel::sensitivity);
|
||||
@ -53,12 +61,19 @@ void init_daqconfiguration(py::module &m) {
|
||||
daqchannel.def_readwrite("digitalHighpassCutOn",
|
||||
&DaqChannel::digitalHighPassCutOn);
|
||||
daqchannel.def_readwrite("rangeIndex", &DaqChannel::rangeIndex);
|
||||
|
||||
py::enum_<DaqChannel::Qty>(daqchannel, "Qty")
|
||||
.value("Number", DaqChannel::Qty::Number)
|
||||
.value("AcousticPressure", DaqChannel::Qty::AcousticPressure)
|
||||
.value("Voltage", DaqChannel::Qty::Voltage)
|
||||
.value("UserDefined", DaqChannel::Qty::UserDefined);
|
||||
daqchannel.def_readwrite("qty", &DaqChannel::qty);
|
||||
|
||||
/// DaqConfiguration
|
||||
daqconfig.def(py::init<>());
|
||||
daqconfig.def(py::init<const DeviceInfo &>());
|
||||
|
||||
daqconfig.def_readwrite("api", &DaqConfiguration::api);
|
||||
daqconfig.def_readwrite("device_name", &DaqConfiguration::device_name);
|
||||
|
||||
daqconfig.def_readwrite("sampleRateIndex",
|
||||
&DaqConfiguration::sampleRateIndex);
|
||||
|
@ -10,6 +10,7 @@ void init_deviceinfo(py::module& m) {
|
||||
|
||||
/// DeviceInfo
|
||||
py::class_<DeviceInfo> devinfo(m, "DeviceInfo");
|
||||
devinfo.def("__str__", [](const DeviceInfo& d) {return d.device_name;});
|
||||
devinfo.def_readonly("api", &DeviceInfo::api);
|
||||
devinfo.def_readonly("device_name", &DeviceInfo::device_name);
|
||||
|
||||
|
@ -1,27 +1,41 @@
|
||||
#include <carma>
|
||||
#include "lasp_avpowerspectra.h"
|
||||
#include "lasp_biquadbank.h"
|
||||
#include "lasp_fft.h"
|
||||
#include "lasp_siggen.h"
|
||||
#include "lasp_siggen_impl.h"
|
||||
#include "lasp_slm.h"
|
||||
#include "lasp_avpowerspectra.h"
|
||||
#include "lasp_window.h"
|
||||
#include "lasp_fft.h"
|
||||
#include <iostream>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
using std::cerr;
|
||||
namespace py = pybind11;
|
||||
|
||||
/**
|
||||
* \ingroup pybind
|
||||
* @{
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize DSP code
|
||||
*
|
||||
* @param m The Python module to add classes and methods to
|
||||
*/
|
||||
void init_dsp(py::module &m) {
|
||||
|
||||
py::class_<Fft> fft(m, "Fft");
|
||||
fft.def(py::init<us>());
|
||||
fft.def("fft", py::overload_cast<const vd&>(&Fft::fft));
|
||||
fft.def("fft", py::overload_cast<const dmat&>(&Fft::fft));
|
||||
fft.def("fft", py::overload_cast<const vd &>(&Fft::fft));
|
||||
fft.def("fft", py::overload_cast<const dmat &>(&Fft::fft));
|
||||
|
||||
fft.def("ifft", py::overload_cast<const vc&>(&Fft::ifft));
|
||||
fft.def("ifft", py::overload_cast<const cmat&>(&Fft::ifft));
|
||||
fft.def("ifft", py::overload_cast<const vc &>(&Fft::ifft));
|
||||
fft.def("ifft", py::overload_cast<const cmat &>(&Fft::ifft));
|
||||
fft.def_static("load_fft_wisdom", &Fft::load_fft_wisdom);
|
||||
fft.def_static("store_fft_wisdom", &Fft::store_fft_wisdom);
|
||||
|
||||
/// Window
|
||||
py::class_<Window> w(m, "Window");
|
||||
|
||||
py::enum_<Window::WindowType>(w, "WindowType")
|
||||
@ -29,9 +43,11 @@ void init_dsp(py::module &m) {
|
||||
.value("Hamming", Window::WindowType::Hamming)
|
||||
.value("Bartlett", Window::WindowType::Bartlett)
|
||||
.value("Blackman", Window::WindowType::Bartlett)
|
||||
.value("Rectangular", Window::WindowType::Rectangular)
|
||||
.export_values();
|
||||
.value("Rectangular", Window::WindowType::Rectangular);
|
||||
|
||||
w.def_static("toTxt", &Window::toText);
|
||||
|
||||
/// Siggen
|
||||
py::class_<Siggen, std::shared_ptr<Siggen>> siggen(m, "Siggen");
|
||||
siggen.def("setLevel", &Siggen::setLevel,
|
||||
"Set the level of the signal generator");
|
||||
@ -39,6 +55,7 @@ void init_dsp(py::module &m) {
|
||||
py::class_<Sine, std::shared_ptr<Sine>> sw(m, "Sine", siggen);
|
||||
sw.def(py::init<const d>());
|
||||
|
||||
/// SeriesBiquad
|
||||
py::class_<SeriesBiquad> sbq(m, "SeriesBiquad");
|
||||
sbq.def(py::init<const vd &>());
|
||||
sbq.def("filter", [](SeriesBiquad &s, const vd &input) {
|
||||
@ -47,14 +64,27 @@ void init_dsp(py::module &m) {
|
||||
return res;
|
||||
});
|
||||
|
||||
/// BiquadBank
|
||||
py::class_<BiquadBank> bqb(m, "BiquadBank");
|
||||
bqb.def(py::init<const dmat&,const vd*>());
|
||||
bqb.def("setGains",&BiquadBank::setGains);
|
||||
bqb.def("filter",&BiquadBank::filter);
|
||||
|
||||
/// PowerSpectra
|
||||
py::class_<PowerSpectra> ps(m, "PowerSpectra");
|
||||
ps.def(py::init<us, const Window::WindowType>());
|
||||
ps.def(py::init<const us, const Window::WindowType>());
|
||||
ps.def("compute", &PowerSpectra::compute);
|
||||
|
||||
/// AvPowerSpectra
|
||||
py::class_<AvPowerSpectra> aps(m, "AvPowerSpectra");
|
||||
/* ps.def(py::init<us, const Window::WindowType>()); */
|
||||
/* ps.def("compute", &PowerSpectra::compute); */
|
||||
|
||||
aps.def(py::init<const us, const Window::WindowType, const d, const int>(),
|
||||
py::arg("nfft"), py::arg("WindowType"), py::arg("overlap_percentage"),
|
||||
py::arg("time_constant"));
|
||||
aps.def("compute", [](AvPowerSpectra &aps, const dmat &timedata) {
|
||||
std::optional<arma::cx_cube> res = aps.compute(timedata);
|
||||
return res.value_or(arma::cx_cube(0,0,0));
|
||||
}
|
||||
);
|
||||
|
||||
py::class_<SLM> slm(m, "SLM");
|
||||
|
||||
@ -70,4 +100,8 @@ void init_dsp(py::module &m) {
|
||||
slm.def_readonly("Pm", &SLM::Pm);
|
||||
slm.def_readonly("Pmax", &SLM::Pmax);
|
||||
slm.def_readonly("Ppeak", &SLM::Ppeak);
|
||||
slm.def("Lpeak", &SLM::Lpeak);
|
||||
slm.def("Leq", &SLM::Leq);
|
||||
slm.def("Lmax", &SLM::Lmax);
|
||||
}
|
||||
/** @} */
|
||||
|
@ -9,13 +9,14 @@ namespace py = pybind11;
|
||||
|
||||
void init_streammgr(py::module &m) {
|
||||
|
||||
/// The stream manager is a singleton, and the lifetime is managed elsewhere.
|
||||
// It should not be deleted.
|
||||
py::class_<StreamMgr, std::unique_ptr<StreamMgr, py::nodelete>> smgr(
|
||||
m, "StreamMgr");
|
||||
|
||||
py::enum_<StreamMgr::StreamType>(smgr, "StreamType")
|
||||
.value("input", StreamMgr::StreamType::input)
|
||||
.value("output", StreamMgr::StreamType::output)
|
||||
.export_values();
|
||||
.value("output", StreamMgr::StreamType::output);
|
||||
|
||||
smgr.def("startStream", &StreamMgr::startStream);
|
||||
smgr.def("stopStream", &StreamMgr::stopStream);
|
||||
@ -26,4 +27,6 @@ void init_streammgr(py::module &m) {
|
||||
|
||||
smgr.def("setSiggen", &StreamMgr::setSiggen);
|
||||
smgr.def("getDeviceInfo", &StreamMgr::getDeviceInfo);
|
||||
smgr.def("getStreamStatus", &StreamMgr::getStreamStatus);
|
||||
smgr.def("isStreamRunningOK", &StreamMgr::isStreamRunningOK);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user