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
|
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 *
|
from .lasp_cpp import *
|
||||||
|
import lasp.lasp_cpp
|
||||||
|
from .lasp_common import *
|
||||||
__version__ = lasp_cpp.__version__
|
__version__ = lasp_cpp.__version__
|
||||||
|
|
||||||
# from .lasp_imptube import * # TwoMicImpedanceTube
|
# from .lasp_imptube import * # TwoMicImpedanceTube
|
||||||
|
@ -125,7 +125,7 @@ public:
|
|||||||
return (apiname == other.apiname && apicode == other.apicode &&
|
return (apiname == other.apiname && apicode == other.apicode &&
|
||||||
api_specific_subcode == other.api_specific_subcode);
|
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();
|
static std::vector<DaqApi> getAvailableApis();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,12 +160,9 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Possible physical quantities that are recorded.
|
* @brief Possible physical quantities that are recorded.
|
||||||
*/
|
*/
|
||||||
enum class Qty {
|
enum class Qty { Number, AcousticPressure, Voltage, UserDefined };
|
||||||
Number,
|
|
||||||
AcousticPressure,
|
DaqChannel() = default;
|
||||||
Voltage,
|
|
||||||
UserDefined
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the channel is enabled.
|
* @brief Whether the channel is enabled.
|
||||||
|
@ -11,9 +11,7 @@ using std::cerr;
|
|||||||
using std::endl;
|
using std::endl;
|
||||||
using rte = std::runtime_error;
|
using rte = std::runtime_error;
|
||||||
|
|
||||||
InDataHandler::InDataHandler(StreamMgr &mgr) : _mgr(mgr) {
|
InDataHandler::InDataHandler(StreamMgr &mgr) : _mgr(mgr) { DEBUGTRACE_ENTER; }
|
||||||
DEBUGTRACE_ENTER;
|
|
||||||
}
|
|
||||||
void InDataHandler::start() {
|
void InDataHandler::start() {
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
_mgr.addInDataHandler(*this);
|
_mgr.addInDataHandler(*this);
|
||||||
@ -57,9 +55,13 @@ void StreamMgr::checkRightThread() const {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void StreamMgr::rescanDAQDevices(std::function<void()> callback,
|
void StreamMgr::rescanDAQDevices(bool background,
|
||||||
bool background) {
|
std::function<void()> callback) {
|
||||||
checkRightThread();
|
checkRightThread();
|
||||||
|
if (_inputStream || _outputStream) {
|
||||||
|
throw rte("Rescanning DAQ devices only possible when no stream is running");
|
||||||
|
}
|
||||||
|
_devices.clear();
|
||||||
auto &pool = getPool();
|
auto &pool = getPool();
|
||||||
if (background) {
|
if (background) {
|
||||||
pool.push_task(&StreamMgr::rescanDAQDevices_impl, this, callback);
|
pool.push_task(&StreamMgr::rescanDAQDevices_impl, this, callback);
|
||||||
@ -192,9 +194,9 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
|
|||||||
{
|
{
|
||||||
std::scoped_lock lck(_devices_mtx);
|
std::scoped_lock lck(_devices_mtx);
|
||||||
|
|
||||||
auto devinfo2 =
|
auto devinfo2 = std::find_if(
|
||||||
std::find_if(_devices.cbegin(), _devices.cend(),
|
_devices.cbegin(), _devices.cend(),
|
||||||
[&config](const DeviceInfo& d) { return config.match(d); });
|
[&config](const DeviceInfo &d) { return config.match(d); });
|
||||||
if (devinfo2 == std::cend(_devices)) {
|
if (devinfo2 == std::cend(_devices)) {
|
||||||
throw rte("Could not find a device with name " + config.device_name +
|
throw rte("Could not find a device with name " + config.device_name +
|
||||||
" in list of devices.");
|
" in list of devices.");
|
||||||
@ -212,7 +214,7 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
|
|||||||
|
|
||||||
if (!isInput && !isOutput) {
|
if (!isInput && !isOutput) {
|
||||||
throw rte("Neither input, nor output channels enabled for "
|
throw rte("Neither input, nor output channels enabled for "
|
||||||
"stream. Cannotr start.");
|
"stream. Cannot start.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((isDuplex || isInput) && _inputStream) {
|
if ((isDuplex || isInput) && _inputStream) {
|
||||||
|
@ -130,7 +130,7 @@ public:
|
|||||||
* @return A copy of the internal stored list of devices
|
* @return A copy of the internal stored list of devices
|
||||||
*/
|
*/
|
||||||
std::vector<DeviceInfo> getDeviceInfo() const {
|
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;
|
return _devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,13 +138,13 @@ public:
|
|||||||
* @brief Triggers a background scan of the DAQ devices, which updates the
|
* @brief Triggers a background scan of the DAQ devices, which updates the
|
||||||
* internally stored list of devices.
|
* internally stored list of devices.
|
||||||
*
|
*
|
||||||
* @param callback Function to call when complete.
|
|
||||||
* @param background Perform searching for DAQ devices in the background. If
|
* @param background Perform searching for DAQ devices in the background. If
|
||||||
* set to true, the function returns immediately.
|
* set to true, the function returns immediately.
|
||||||
|
* @param callback Function to call when complete.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
rescanDAQDevices(std::function<void()> callback = std::function<void()>(),
|
rescanDAQDevices(bool background = false,
|
||||||
bool background = false);
|
std::function<void()> callback = std::function<void()>());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start a stream based on given configuration.
|
* @brief Start a stream based on given configuration.
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#include <optional>
|
/* #define DEBUGTRACE_ENABLED */
|
||||||
#define DEBUGTRACE_ENABLED
|
|
||||||
#include "debugtrace.hpp"
|
#include "debugtrace.hpp"
|
||||||
|
#include <optional>
|
||||||
#include "lasp_avpowerspectra.h"
|
#include "lasp_avpowerspectra.h"
|
||||||
#include <cmath>
|
#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::PowerSpectra(const us nfft, const Window::WindowType w)
|
||||||
: PowerSpectra(Window::create(w, nfft)) {}
|
: 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)));
|
_scale_fac * (rfft.col(i) % arma::conj(rfft.col(j)));
|
||||||
|
|
||||||
output.slice(j).col(i)(0) /= 2;
|
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;
|
return output;
|
||||||
@ -48,13 +50,13 @@ AvPowerSpectra::AvPowerSpectra(const us nfft, const Window::WindowType w,
|
|||||||
|
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
if (overlap_percentage >= 100 || overlap_percentage < 0) {
|
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;
|
_overlap_keep = (nfft * overlap_percentage) / 100;
|
||||||
if (_overlap_keep >= nfft) {
|
if (_overlap_keep >= nfft) {
|
||||||
throw runtime_error("Overlap is too high. Results in no jump. Please "
|
throw rte("Overlap is too high. Results in no jump. Please "
|
||||||
"choose a smaller overlap percentage or a higher nfft");
|
"choose a smaller overlap percentage or a higher nfft");
|
||||||
}
|
}
|
||||||
if (time_constant < 0) {
|
if (time_constant < 0) {
|
||||||
_mode = Mode::Averaging;
|
_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) {
|
std::optional<arma::cx_cube> AvPowerSpectra::compute(const dmat &timedata) {
|
||||||
|
DEBUGTRACE_ENTER;
|
||||||
|
|
||||||
_timeBuf.push(timedata);
|
_timeBuf.push(timedata);
|
||||||
|
|
||||||
std::optional<arma::cx_cube> res;
|
std::optional<arma::cx_cube> res;
|
||||||
|
|
||||||
|
us i = 0;
|
||||||
while (auto samples = _timeBuf.pop(_ps.nfft, _overlap_keep)) {
|
while (auto samples = _timeBuf.pop(_ps.nfft, _overlap_keep)) {
|
||||||
|
/* DEBUGTRACE_PRINT((int)_mode); */
|
||||||
switch (_mode) {
|
switch (_mode) {
|
||||||
case (Mode::Spectrogram): {
|
case (Mode::Spectrogram): {
|
||||||
res.emplace(_ps.compute(samples.value()));
|
_est = _ps.compute(samples.value());
|
||||||
} break;
|
} break;
|
||||||
case (Mode::Averaging): {
|
case (Mode::Averaging): {
|
||||||
n_averages++;
|
n_averages++;
|
||||||
if (n_averages == 1) {
|
if (n_averages == 1) {
|
||||||
_est = _ps.compute(samples.value());
|
_est = _ps.compute(samples.value());
|
||||||
} else {
|
} else {
|
||||||
_est = _est * (n_averages - 1) / n_averages +
|
_est = _est * (static_cast<d>(n_averages - 1) / n_averages) +
|
||||||
_ps.compute(samples.value()) / n_averages;
|
_ps.compute(samples.value()) / n_averages;
|
||||||
}
|
}
|
||||||
res = _est;
|
|
||||||
} break;
|
} break;
|
||||||
case (Mode::Leaking): {
|
case (Mode::Leaking): {
|
||||||
if (arma::size(_est) == arma::size(0, 0, 0)) {
|
if (arma::size(_est) == arma::size(0, 0, 0)) {
|
||||||
@ -92,11 +97,14 @@ std::optional<arma::cx_cube> AvPowerSpectra::compute(const dmat &timedata) {
|
|||||||
} else {
|
} else {
|
||||||
_est = _alpha * _est + (1 - _alpha) * _ps.compute(samples.value());
|
_est = _alpha * _est + (1 - _alpha) * _ps.compute(samples.value());
|
||||||
}
|
}
|
||||||
res = _est;
|
|
||||||
} break;
|
} break;
|
||||||
} // end switch mode
|
} // end switch mode
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
return res;
|
if(i>0) {
|
||||||
|
return _est;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
std::optional<arma::cx_cube> AvPowerSpectra::get_est() {
|
std::optional<arma::cx_cube> AvPowerSpectra::get_est() {
|
||||||
if (_est.n_cols > 0)
|
if (_est.n_cols > 0)
|
||||||
|
@ -7,10 +7,13 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
/** \defgroup dsp Digital Signal Processing utilities
|
/** \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 {
|
class PowerSpectra {
|
||||||
public:
|
public:
|
||||||
|
@ -94,7 +94,7 @@ void BiquadBank::setGains(const vd &gains) {
|
|||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
const us nfilters = _filters.size();
|
const us nfilters = _filters.size();
|
||||||
if (gains.size() != nfilters) {
|
if (gains.size() != nfilters) {
|
||||||
throw runtime_error("Invalid number of gain values given.");
|
throw rte("Invalid number of gain values given.");
|
||||||
}
|
}
|
||||||
_gains = gains;
|
_gains = gains;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "lasp_filter.h"
|
#include "lasp_filter.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup dsp
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A set of Biquad filters in series.
|
* @brief A set of Biquad filters in series.
|
||||||
*/
|
*/
|
||||||
@ -74,3 +79,4 @@ public:
|
|||||||
void reset() override final;
|
void reset() override final;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
/** @} */
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#define DEBUGTRACE_ENABLED
|
/* #define DEBUGTRACE_ENABLED */
|
||||||
#include "lasp_fft.h"
|
#include "lasp_fft.h"
|
||||||
#include "debugtrace.hpp"
|
#include "debugtrace.hpp"
|
||||||
#include "lasp_config.h"
|
#include "lasp_config.h"
|
||||||
@ -152,22 +152,29 @@ dmat Fft::ifft(const cmat &freqdata) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_fft_wisdom(const char *wisdom) {
|
void Fft::load_fft_wisdom(const std::string& wisdom) {
|
||||||
#if LASP_FFT_BACKEND == Armadillo
|
#if LASP_FFT_BACKEND == Armadillo
|
||||||
#elif LASP_FFT_BACKEND == FFTW
|
#elif LASP_FFT_BACKEND == FFTW
|
||||||
if (wisdom) {
|
if (wisdom.length() > 0) {
|
||||||
int rv = fftw_import_wisdom_from_string(wisdom);
|
int rv = fftw_import_wisdom_from_string(wisdom.c_str());
|
||||||
if (rv != 1) {
|
if (rv != 1) {
|
||||||
fprintf(stderr, "Error loading FFTW wisdom");
|
throw rte("Error loading FFTW wisdom");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
char *store_fft_wisdom() {
|
std::string Fft::store_fft_wisdom() {
|
||||||
#if LASP_FFT_BACKEND == Armadillo
|
#if LASP_FFT_BACKEND == Armadillo
|
||||||
return NULL;
|
return "";
|
||||||
#elif LASP_FFT_BACKEND == FFTW
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -88,4 +88,20 @@ class Fft {
|
|||||||
*/
|
*/
|
||||||
dmat ifft(const cmat& freqdata);
|
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(const d freq_Hz);
|
||||||
~Sine() = default;
|
~Sine() = default;
|
||||||
virtual vd genSignalUnscaled(const us nframes) override;
|
virtual vd genSignalUnscaled(const us nframes) override final;
|
||||||
void setFreq(const d newFreq);
|
void setFreq(const d newFreq);
|
||||||
void resetImpl() override { phase=0; }
|
void resetImpl() override final { phase=0; }
|
||||||
};
|
};
|
||||||
class Periodic: public Siggen {
|
class Periodic: public Siggen {
|
||||||
protected:
|
protected:
|
||||||
@ -42,7 +42,7 @@ class Periodic: public Siggen {
|
|||||||
us _cur_pos = 0;
|
us _cur_pos = 0;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual vd genSignalUnscaled(const us nframes) override;
|
virtual vd genSignalUnscaled(const us nframes) override final;
|
||||||
~Periodic() = default;
|
~Periodic() = default;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@ SLM::SLM(const d fs, const d Lref, const us downsampling_fac, const d tau,
|
|||||||
// components of
|
// components of
|
||||||
// single pole low pass
|
// single pole low pass
|
||||||
// filter
|
// filter
|
||||||
Lref(Lref), // Reference level
|
Lrefsq(Lref*Lref), // Reference level
|
||||||
downsampling_fac(downsampling_fac),
|
downsampling_fac(downsampling_fac),
|
||||||
|
|
||||||
// Initalize mean square
|
// 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));
|
Pmax(i) = std::max(Pmax(i), arma::max(work));
|
||||||
|
|
||||||
// Convert to levels in dB
|
// Convert to levels in dB
|
||||||
/* work.transform([&](d val) { */
|
work = 10*arma::log10((work+arma::datum::eps)/Lrefsq);
|
||||||
/* 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));
|
|
||||||
|
|
||||||
return work;
|
return work;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup dsp
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sound Level Meter implementation that gives a result for each
|
* @brief Sound Level Meter implementation that gives a result for each
|
||||||
* channel. A channel is the result of a filtered signal
|
* channel. A channel is the result of a filtered signal
|
||||||
@ -25,7 +30,7 @@ class SLM {
|
|||||||
d _alpha = -1;
|
d _alpha = -1;
|
||||||
vd _sp_storage;
|
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 downsampling_fac; /// Every x'th sample is returned.
|
||||||
us cur_offset = 0; /// Storage for offset point in input arrays
|
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
|
* @brief Public storage for the maximum signal power, after single pole
|
||||||
* low-pass filter.
|
* 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
|
* @brief Public storage for the peak signal power, before single pole
|
||||||
* low-pass filter.
|
* low-pass filter.
|
||||||
*/
|
*/
|
||||||
vd Ppeak;
|
vd Ppeak;
|
||||||
|
|
||||||
us N = 0; /// Counter for the number of time samples counted that came
|
us N = 0; /// Counter for the number of time samples counted that came
|
||||||
/// in;
|
/// in;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize a Sound Level Meter
|
* @brief Initialize a Sound Level Meter
|
||||||
@ -118,13 +123,33 @@ public:
|
|||||||
*
|
*
|
||||||
* @param input Raw input data
|
* @param input Raw input data
|
||||||
*
|
*
|
||||||
* @return Filtered level data.
|
* @return Filtered level data for each filtered channel.
|
||||||
*/
|
*/
|
||||||
dmat run(const vd &input);
|
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:
|
private:
|
||||||
vd run_single(vd input, const us filter_no);
|
vd run_single(vd input, const us filter_no);
|
||||||
};
|
};
|
||||||
|
/** @} */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#define DEBUGTRACE_ENABLED
|
/* #define DEBUGTRACE_ENABLED */
|
||||||
#include "lasp_thread.h"
|
#include "lasp_thread.h"
|
||||||
#include "BS_thread_pool.hpp"
|
#include "BS_thread_pool.hpp"
|
||||||
#include "debugtrace.hpp"
|
#include "debugtrace.hpp"
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
using std::runtime_error;
|
using rte = std::runtime_error;
|
||||||
|
|
||||||
class TimeBufferImp {
|
class TimeBufferImp {
|
||||||
/**
|
/**
|
||||||
@ -22,10 +22,11 @@ public:
|
|||||||
_storage.clear();
|
_storage.clear();
|
||||||
}
|
}
|
||||||
void push(const dmat &mat) {
|
void push(const dmat &mat) {
|
||||||
|
DEBUGTRACE_ENTER;
|
||||||
#if LASP_DEBUG==1
|
#if LASP_DEBUG==1
|
||||||
if(!_storage.empty()) {
|
if(!_storage.empty()) {
|
||||||
if(mat.n_cols != _storage.front().n_cols) {
|
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
|
#endif
|
||||||
@ -39,7 +40,7 @@ public:
|
|||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
|
|
||||||
if (keep > nsamples)
|
if (keep > nsamples)
|
||||||
throw runtime_error("keep should be <= nsamples");
|
throw rte("keep should be <= nsamples");
|
||||||
|
|
||||||
if (nsamples <= n_frames()) {
|
if (nsamples <= n_frames()) {
|
||||||
|
|
||||||
|
@ -2,30 +2,36 @@
|
|||||||
//
|
//
|
||||||
// Author: J.A. de Jong - ASCEE
|
// Author: J.A. de Jong - ASCEE
|
||||||
//
|
//
|
||||||
|
/* #define DEBUGTRACE_ENABLED */
|
||||||
|
#include "debugtrace.hpp"
|
||||||
#include "lasp_window.h"
|
#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).
|
// Safe some typing. Linspace form 0 up to (and NOT including N).
|
||||||
#define lin0N arma::linspace(0, N - 1, N)
|
#define lin0N arma::linspace(0, N - 1, N)
|
||||||
|
|
||||||
vd Window::hann(const us 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) {
|
vd Window::hamming(const us N) {
|
||||||
d alpha = 25.0 / 46.0;
|
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) {
|
vd Window::blackman(const us N) {
|
||||||
d a0 = 7938. / 18608.;
|
d a0 = 7938. / 18608.;
|
||||||
d a1 = 9240. / 18608.;
|
d a1 = 9240. / 18608.;
|
||||||
d a2 = 1430. / 18608.;
|
d a2 = 1430. / 18608.;
|
||||||
return a0 - a1 * d_cos(2 * number_pi * lin0N / (N - 1)) +
|
return a0 - a1 * d_cos((2 * number_pi/N) * lin0N) +
|
||||||
a2 * d_cos(4 * number_pi * lin0N / (N - 1));
|
a2 * d_cos((4 * number_pi / N)* lin0N );
|
||||||
}
|
}
|
||||||
|
|
||||||
vd Window::rectangular(const us N) { return arma::ones(N); }
|
vd Window::rectangular(const us N) { return arma::ones(N); }
|
||||||
|
|
||||||
vd Window::bartlett(const us 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) {
|
vd Window::create(const WindowType w, const us N) {
|
||||||
|
|
||||||
|
@ -15,6 +15,38 @@ class Window {
|
|||||||
Blackman = 4,
|
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
|
* @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 import dataclass
|
||||||
from dataclasses_json import dataclass_json
|
from dataclasses_json import dataclass_json
|
||||||
from enum import Enum, unique, auto
|
from enum import Enum, unique, auto
|
||||||
from .lasp_cpp import Window as wWindow
|
from .lasp_cpp import DaqChannel
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Common definitions used throughout the code.
|
Common definitions used throughout the code.
|
||||||
@ -14,7 +14,7 @@ Common definitions used throughout the code.
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime', 'getFreq', 'Qty',
|
'P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime', 'getFreq', 'Qty',
|
||||||
'SIQtys', 'Window',
|
'SIQtys',
|
||||||
'lasp_shelve', 'this_lasp_shelve', 'W_REF', 'U_REF', 'I_REF', 'dBFS_REF',
|
'lasp_shelve', 'this_lasp_shelve', 'W_REF', 'U_REF', 'I_REF', 'dBFS_REF',
|
||||||
'AvType'
|
'AvType'
|
||||||
]
|
]
|
||||||
@ -39,6 +39,7 @@ U_REF = 5e-8 # 50 nano meter / s
|
|||||||
# hence this is the reference level as specified below.
|
# hence this is the reference level as specified below.
|
||||||
dBFS_REF = 0.5*2**0.5 # Which level would be -3.01 dBFS
|
dBFS_REF = 0.5*2**0.5 # Which level would be -3.01 dBFS
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class AvType(Enum):
|
class AvType(Enum):
|
||||||
"""Specificying the type of data, for adding and removing callbacks from
|
"""Specificying the type of data, for adding and removing callbacks from
|
||||||
@ -52,7 +53,6 @@ class AvType(Enum):
|
|||||||
|
|
||||||
# Both input as well as output
|
# Both input as well as output
|
||||||
audio_duplex = (2, 'duplex')
|
audio_duplex = (2, 'duplex')
|
||||||
# video = 4
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@ -67,6 +67,7 @@ class Qty:
|
|||||||
# yet able to compute `dBPa's'
|
# yet able to compute `dBPa's'
|
||||||
level_ref_name: object
|
level_ref_name: object
|
||||||
level_ref_value: object
|
level_ref_value: object
|
||||||
|
cpp_enum: DaqChannel.Qty
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.name} [{self.unit_symb}]'
|
return f'{self.name} [{self.unit_symb}]'
|
||||||
@ -91,14 +92,16 @@ class SIQtys(Enum):
|
|||||||
unit_symb='-',
|
unit_symb='-',
|
||||||
level_unit=('dBFS',),
|
level_unit=('dBFS',),
|
||||||
level_ref_name=('Relative to full scale sine wave',),
|
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',
|
AP = Qty(name='Acoustic Pressure',
|
||||||
unit_name='Pascal',
|
unit_name='Pascal',
|
||||||
unit_symb='Pa',
|
unit_symb='Pa',
|
||||||
level_unit=('dB SPL','dBPa'),
|
level_unit=('dB SPL','dBPa'),
|
||||||
level_ref_name=('2 micropascal', '1 pascal',),
|
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',
|
V = Qty(name='Voltage',
|
||||||
@ -107,6 +110,7 @@ class SIQtys(Enum):
|
|||||||
level_unit=('dBV',), # dBV
|
level_unit=('dBV',), # dBV
|
||||||
level_ref_name=('1V',),
|
level_ref_name=('1V',),
|
||||||
level_ref_value=(1.0,),
|
level_ref_value=(1.0,),
|
||||||
|
cpp_enum = DaqChannel.Qty.Voltage
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -268,33 +272,6 @@ class this_lasp_shelve(Shelve):
|
|||||||
node = platform.node()
|
node = platform.node()
|
||||||
return os.path.join(lasp_appdir, f'{node}_config.shelve')
|
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:
|
class TimeWeighting:
|
||||||
none = (-1, 'Raw (no time weighting)')
|
none = (-1, 'Raw (no time weighting)')
|
||||||
uufast = (1e-4, '0.1 ms')
|
uufast = (1e-4, '0.1 ms')
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup pybind Pybind11 wrapper code
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
*/
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
|
|
||||||
void init_dsp(py::module &m);
|
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_MAJOR") = LASP_VERSION_MAJOR;
|
||||||
m.attr("LASP_VERSION_MINOR") = LASP_VERSION_MINOR;
|
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("threadError", Daq::StreamStatus::StreamError::threadError)
|
||||||
.value("logicError", Daq::StreamStatus::StreamError::logicError)
|
.value("logicError", Daq::StreamStatus::StreamError::logicError)
|
||||||
.value("apiSpecificError",
|
.value("apiSpecificError",
|
||||||
Daq::StreamStatus::StreamError::apiSpecificError)
|
Daq::StreamStatus::StreamError::apiSpecificError);
|
||||||
.export_values();
|
|
||||||
|
|
||||||
/// Daq
|
/// Daq
|
||||||
daq.def("neninchannels", &Daq::neninchannels);
|
daq.def("neninchannels", &Daq::neninchannels);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "lasp_daqconfig.h"
|
#include "lasp_daqconfig.h"
|
||||||
#include "lasp_deviceinfo.h"
|
#include "lasp_deviceinfo.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <pybind11/numpy.h>
|
#include <pybind11/numpy.h>
|
||||||
#include <pybind11/stl.h>
|
#include <pybind11/stl.h>
|
||||||
@ -12,17 +13,31 @@ void init_daqconfiguration(py::module &m) {
|
|||||||
|
|
||||||
/// DataType
|
/// DataType
|
||||||
py::class_<DataTypeDescriptor> dtype_desc(m, "DataTypeDescriptor");
|
py::class_<DataTypeDescriptor> dtype_desc(m, "DataTypeDescriptor");
|
||||||
|
|
||||||
dtype_desc.def_readonly("name", &DataTypeDescriptor::name);
|
dtype_desc.def_readonly("name", &DataTypeDescriptor::name);
|
||||||
dtype_desc.def_readonly("sw", &DataTypeDescriptor::sw);
|
dtype_desc.def_readonly("sw", &DataTypeDescriptor::sw);
|
||||||
dtype_desc.def_readonly("is_floating", &DataTypeDescriptor::is_floating);
|
dtype_desc.def_readonly("is_floating", &DataTypeDescriptor::is_floating);
|
||||||
|
dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype);
|
||||||
|
|
||||||
py::enum_<DataTypeDescriptor::DataType>(dtype_desc, "DataType")
|
py::enum_<DataTypeDescriptor::DataType>(dtype_desc, "DataType")
|
||||||
.value("dtype_fl32", DataTypeDescriptor::DataType::dtype_fl32)
|
.value("dtype_fl32", DataTypeDescriptor::DataType::dtype_fl32)
|
||||||
.value("dtype_fl64", DataTypeDescriptor::DataType::dtype_fl64)
|
.value("dtype_fl64", DataTypeDescriptor::DataType::dtype_fl64)
|
||||||
.value("dtype_int8", DataTypeDescriptor::DataType::dtype_int8)
|
.value("dtype_int8", DataTypeDescriptor::DataType::dtype_int8)
|
||||||
.value("dtype_int16", DataTypeDescriptor::DataType::dtype_int16)
|
.value("dtype_int16", DataTypeDescriptor::DataType::dtype_int16)
|
||||||
.value("dtype_int32", DataTypeDescriptor::DataType::dtype_int32)
|
.value("dtype_int32", DataTypeDescriptor::DataType::dtype_int32);
|
||||||
.export_values();
|
|
||||||
|
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);
|
dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype);
|
||||||
|
|
||||||
@ -36,15 +51,8 @@ void init_daqconfiguration(py::module &m) {
|
|||||||
/// DaqChannel, DaqConfiguration
|
/// DaqChannel, DaqConfiguration
|
||||||
py::class_<DaqConfiguration> daqconfig(m, "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");
|
py::class_<DaqChannel> daqchannel(m, "DaqChannel");
|
||||||
|
daqchannel.def(py::init<>());
|
||||||
daqchannel.def_readwrite("enabled", &DaqChannel::enabled);
|
daqchannel.def_readwrite("enabled", &DaqChannel::enabled);
|
||||||
daqchannel.def_readwrite("name", &DaqChannel::name);
|
daqchannel.def_readwrite("name", &DaqChannel::name);
|
||||||
daqchannel.def_readwrite("sensitivity", &DaqChannel::sensitivity);
|
daqchannel.def_readwrite("sensitivity", &DaqChannel::sensitivity);
|
||||||
@ -53,12 +61,19 @@ void init_daqconfiguration(py::module &m) {
|
|||||||
daqchannel.def_readwrite("digitalHighpassCutOn",
|
daqchannel.def_readwrite("digitalHighpassCutOn",
|
||||||
&DaqChannel::digitalHighPassCutOn);
|
&DaqChannel::digitalHighPassCutOn);
|
||||||
daqchannel.def_readwrite("rangeIndex", &DaqChannel::rangeIndex);
|
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);
|
daqchannel.def_readwrite("qty", &DaqChannel::qty);
|
||||||
|
|
||||||
/// DaqConfiguration
|
/// DaqConfiguration
|
||||||
daqconfig.def(py::init<>());
|
daqconfig.def(py::init<>());
|
||||||
daqconfig.def(py::init<const DeviceInfo &>());
|
daqconfig.def(py::init<const DeviceInfo &>());
|
||||||
|
daqconfig.def_readwrite("api", &DaqConfiguration::api);
|
||||||
|
daqconfig.def_readwrite("device_name", &DaqConfiguration::device_name);
|
||||||
|
|
||||||
daqconfig.def_readwrite("sampleRateIndex",
|
daqconfig.def_readwrite("sampleRateIndex",
|
||||||
&DaqConfiguration::sampleRateIndex);
|
&DaqConfiguration::sampleRateIndex);
|
||||||
|
@ -10,6 +10,7 @@ void init_deviceinfo(py::module& m) {
|
|||||||
|
|
||||||
/// DeviceInfo
|
/// DeviceInfo
|
||||||
py::class_<DeviceInfo> devinfo(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("api", &DeviceInfo::api);
|
||||||
devinfo.def_readonly("device_name", &DeviceInfo::device_name);
|
devinfo.def_readonly("device_name", &DeviceInfo::device_name);
|
||||||
|
|
||||||
|
@ -1,27 +1,41 @@
|
|||||||
#include <carma>
|
#include <carma>
|
||||||
|
#include "lasp_avpowerspectra.h"
|
||||||
#include "lasp_biquadbank.h"
|
#include "lasp_biquadbank.h"
|
||||||
|
#include "lasp_fft.h"
|
||||||
#include "lasp_siggen.h"
|
#include "lasp_siggen.h"
|
||||||
#include "lasp_siggen_impl.h"
|
#include "lasp_siggen_impl.h"
|
||||||
#include "lasp_slm.h"
|
#include "lasp_slm.h"
|
||||||
#include "lasp_avpowerspectra.h"
|
|
||||||
#include "lasp_window.h"
|
#include "lasp_window.h"
|
||||||
#include "lasp_fft.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <pybind11/pybind11.h>
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
namespace py = pybind11;
|
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) {
|
void init_dsp(py::module &m) {
|
||||||
|
|
||||||
py::class_<Fft> fft(m, "Fft");
|
py::class_<Fft> fft(m, "Fft");
|
||||||
fft.def(py::init<us>());
|
fft.def(py::init<us>());
|
||||||
fft.def("fft", py::overload_cast<const vd&>(&Fft::fft));
|
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 dmat &>(&Fft::fft));
|
||||||
|
|
||||||
fft.def("ifft", py::overload_cast<const vc&>(&Fft::ifft));
|
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 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::class_<Window> w(m, "Window");
|
||||||
|
|
||||||
py::enum_<Window::WindowType>(w, "WindowType")
|
py::enum_<Window::WindowType>(w, "WindowType")
|
||||||
@ -29,9 +43,11 @@ void init_dsp(py::module &m) {
|
|||||||
.value("Hamming", Window::WindowType::Hamming)
|
.value("Hamming", Window::WindowType::Hamming)
|
||||||
.value("Bartlett", Window::WindowType::Bartlett)
|
.value("Bartlett", Window::WindowType::Bartlett)
|
||||||
.value("Blackman", Window::WindowType::Bartlett)
|
.value("Blackman", Window::WindowType::Bartlett)
|
||||||
.value("Rectangular", Window::WindowType::Rectangular)
|
.value("Rectangular", Window::WindowType::Rectangular);
|
||||||
.export_values();
|
|
||||||
|
|
||||||
|
w.def_static("toTxt", &Window::toText);
|
||||||
|
|
||||||
|
/// Siggen
|
||||||
py::class_<Siggen, std::shared_ptr<Siggen>> siggen(m, "Siggen");
|
py::class_<Siggen, std::shared_ptr<Siggen>> siggen(m, "Siggen");
|
||||||
siggen.def("setLevel", &Siggen::setLevel,
|
siggen.def("setLevel", &Siggen::setLevel,
|
||||||
"Set the level of the signal generator");
|
"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);
|
py::class_<Sine, std::shared_ptr<Sine>> sw(m, "Sine", siggen);
|
||||||
sw.def(py::init<const d>());
|
sw.def(py::init<const d>());
|
||||||
|
|
||||||
|
/// SeriesBiquad
|
||||||
py::class_<SeriesBiquad> sbq(m, "SeriesBiquad");
|
py::class_<SeriesBiquad> sbq(m, "SeriesBiquad");
|
||||||
sbq.def(py::init<const vd &>());
|
sbq.def(py::init<const vd &>());
|
||||||
sbq.def("filter", [](SeriesBiquad &s, const vd &input) {
|
sbq.def("filter", [](SeriesBiquad &s, const vd &input) {
|
||||||
@ -47,14 +64,27 @@ void init_dsp(py::module &m) {
|
|||||||
return res;
|
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");
|
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);
|
ps.def("compute", &PowerSpectra::compute);
|
||||||
|
|
||||||
|
/// AvPowerSpectra
|
||||||
py::class_<AvPowerSpectra> aps(m, "AvPowerSpectra");
|
py::class_<AvPowerSpectra> aps(m, "AvPowerSpectra");
|
||||||
/* ps.def(py::init<us, const Window::WindowType>()); */
|
aps.def(py::init<const us, const Window::WindowType, const d, const int>(),
|
||||||
/* ps.def("compute", &PowerSpectra::compute); */
|
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");
|
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("Pm", &SLM::Pm);
|
||||||
slm.def_readonly("Pmax", &SLM::Pmax);
|
slm.def_readonly("Pmax", &SLM::Pmax);
|
||||||
slm.def_readonly("Ppeak", &SLM::Ppeak);
|
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) {
|
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(
|
py::class_<StreamMgr, std::unique_ptr<StreamMgr, py::nodelete>> smgr(
|
||||||
m, "StreamMgr");
|
m, "StreamMgr");
|
||||||
|
|
||||||
py::enum_<StreamMgr::StreamType>(smgr, "StreamType")
|
py::enum_<StreamMgr::StreamType>(smgr, "StreamType")
|
||||||
.value("input", StreamMgr::StreamType::input)
|
.value("input", StreamMgr::StreamType::input)
|
||||||
.value("output", StreamMgr::StreamType::output)
|
.value("output", StreamMgr::StreamType::output);
|
||||||
.export_values();
|
|
||||||
|
|
||||||
smgr.def("startStream", &StreamMgr::startStream);
|
smgr.def("startStream", &StreamMgr::startStream);
|
||||||
smgr.def("stopStream", &StreamMgr::stopStream);
|
smgr.def("stopStream", &StreamMgr::stopStream);
|
||||||
@ -26,4 +27,6 @@ void init_streammgr(py::module &m) {
|
|||||||
|
|
||||||
smgr.def("setSiggen", &StreamMgr::setSiggen);
|
smgr.def("setSiggen", &StreamMgr::setSiggen);
|
||||||
smgr.def("getDeviceInfo", &StreamMgr::getDeviceInfo);
|
smgr.def("getDeviceInfo", &StreamMgr::getDeviceInfo);
|
||||||
|
smgr.def("getStreamStatus", &StreamMgr::getStreamStatus);
|
||||||
|
smgr.def("isStreamRunningOK", &StreamMgr::isStreamRunningOK);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user