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:
Anne de Jong 2022-09-22 10:18:38 +02:00
parent b629edde4c
commit b200b465f6
24 changed files with 266 additions and 139 deletions

View File

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

View File

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

View File

@ -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,8 +194,8 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
{
std::scoped_lock lck(_devices_mtx);
auto devinfo2 =
std::find_if(_devices.cbegin(), _devices.cend(),
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 +
@ -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) {

View File

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

View File

@ -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)) {}
@ -48,12 +50,12 @@ 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 "
throw rte("Overlap is too high. Results in no jump. Please "
"choose a smaller overlap percentage or a higher nfft");
}
if (time_constant < 0) {
@ -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)

View File

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

View File

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

View File

@ -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;
};
/** @} */

View File

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

View File

@ -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();
};

View File

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

View File

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

View File

@ -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
///
@ -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);
};
/** @} */

View File

@ -1,4 +1,4 @@
#define DEBUGTRACE_ENABLED
/* #define DEBUGTRACE_ENABLED */
#include "lasp_thread.h"
#include "BS_thread_pool.hpp"
#include "debugtrace.hpp"

View File

@ -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()) {

View File

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

View File

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

View File

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

View File

@ -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;
}
/** @} */

View File

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

View File

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

View File

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

View File

@ -1,17 +1,28 @@
#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");
@ -21,7 +32,10 @@ void init_dsp(py::module &m) {
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);
}
/** @} */

View File

@ -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);
}