lasp/src/lasp/device/lasp_daqconfig.cpp

238 lines
6.4 KiB
C++

/* #define DEBUGTRACE_ENABLED */
#include "debugtrace.hpp"
#include "lasp_daqconfig.h"
#include "lasp_deviceinfo.h"
#include <algorithm>
#include <cassert>
#include <stdexcept>
using std::vector;
vector<DaqApi> DaqApi::getAvailableApis() {
vector<DaqApi> apis;
#if LASP_HAS_ULDAQ == 1
apis.push_back(uldaqapi);
#endif
#if LASP_HAS_RTAUDIO == 1
apis.push_back(rtaudioAlsaApi);
apis.push_back(rtaudioPulseaudioApi);
apis.push_back(rtaudioWasapiApi);
apis.push_back(rtaudioDsApi);
apis.push_back(rtaudioAsioApi);
#endif
return apis;
}
DaqConfiguration::DaqConfiguration(const DeviceInfo &device) {
api = device.api;
device_name = device.device_name;
inchannel_config.resize(device.ninchannels);
outchannel_config.resize(device.noutchannels);
us i = 0;
for (auto &inch : inchannel_config) {
inch.name = "Unnamed input channel " + std::to_string(i);
i++;
}
i = 0;
for (auto &outch : outchannel_config) {
outch.name = "Unnamed output channel " + std::to_string(i);
i++;
}
sampleRateIndex = device.prefSampleRateIndex;
dataTypeIndex = device.prefDataTypeIndex;
framesPerBlockIndex = device.prefFramesPerBlockIndex;
monitorOutput = false;
assert(match(device));
}
bool DaqConfiguration::match(const DeviceInfo &dev) const {
return (dev.device_name == device_name && dev.api == api);
}
int DaqConfiguration::getHighestEnabledInChannel() const {
for (int i = inchannel_config.size() - 1; i > -1; i--) {
if (inchannel_config.at(i).enabled)
return i;
}
return -1;
}
int DaqConfiguration::getHighestEnabledOutChannel() const {
for (us i = outchannel_config.size() - 1; i >= 0; i--) {
if (outchannel_config.at(i).enabled)
return i;
}
return -1;
}
int DaqConfiguration::getLowestEnabledInChannel() const {
for (us i = 0; i < inchannel_config.size(); i++) {
if (inchannel_config.at(i).enabled)
return i;
}
return -1;
}
int DaqConfiguration::getLowestEnabledOutChannel() const {
for (us i = 0; i < outchannel_config.size(); i++) {
if (outchannel_config.at(i).enabled)
return i;
}
return -1;
}
vector<DaqChannel> DaqConfiguration::enabledInChannels() const {
vector<DaqChannel> res;
if(monitorOutput) {
DaqChannel ch;
ch.name = "Internal output monitor (loopback)";
ch.enabled = true;
ch.sensitivity = 1;
ch.qty = DaqChannel::Qty::Number;
res.emplace_back(std::move(ch));
}
for(auto& ch: inchannel_config) {
if(ch.enabled) { res.push_back(ch);}
}
return res;
}
#include "toml++/toml.h"
#include <sstream>
toml::table daqChannelToTOML(const DaqChannel &ch) {
DEBUGTRACE_ENTER;
toml::table tbl;
tbl.emplace("enabled", ch.enabled);
tbl.emplace("name", ch.name);
tbl.emplace("sensitivity", ch.sensitivity);
tbl.emplace("IEPEEnabled", ch.IEPEEnabled);
tbl.emplace("ACCouplingMode", ch.ACCouplingMode);
tbl.emplace("rangeIndex", ch.rangeIndex);
tbl.emplace("qty", static_cast<int>(ch.qty));
tbl.emplace("digitalHighpassCutOn", ch.digitalHighPassCutOn);
return tbl;
}
string DaqConfiguration::toTOML() const {
DEBUGTRACE_ENTER;
toml::table apitbl{
{"apiname", api.apiname}, // Api name
{"apicode", api.apicode}, // Api code
{"api_specific_subcode", api.api_specific_subcode} // Subcode
};
toml::table tbl{{"daqapi", apitbl}};
tbl.emplace("device_name", device_name);
tbl.emplace("sampleRateIndex", sampleRateIndex);
tbl.emplace("dataTypeIndex", dataTypeIndex);
tbl.emplace("framesPerBlockIndex", framesPerBlockIndex);
tbl.emplace("monitorOutput", monitorOutput);
toml::array inchannel_config_tbl;
for (const auto &ch : inchannel_config) {
inchannel_config_tbl.emplace_back(daqChannelToTOML(ch));
}
tbl.emplace("inchannel_config", inchannel_config_tbl);
toml::array outchannel_config_tbl;
for (const auto &ch : outchannel_config) {
outchannel_config_tbl.emplace_back(daqChannelToTOML(ch));
}
tbl.emplace("outchannel_config", outchannel_config_tbl);
std::stringstream str;
str << tbl;
return str.str();
}
template <typename T,typename R> T getValue(R &tbl, const char* key) {
using std::runtime_error;
DEBUGTRACE_ENTER;
// Yeah, weird syntax quirck of C++
std::optional<T> val = tbl[key].template value<T>();
if (!val) {
throw runtime_error(string("Error while parsing Daq configuration. Table "
"does not contain key: ") +
key);
}
return val.value();
}
template<typename T> DaqChannel TOMLToDaqChannel(T& node) {
DEBUGTRACE_ENTER;
DaqChannel d;
toml::table& tbl = *node.as_table();
d.enabled = getValue<bool>(tbl, "enabled");
d.name = getValue<string>(tbl, "name");
d.sensitivity = getValue<double>(tbl, "sensitivity");
d.IEPEEnabled = getValue<bool>(tbl, "IEPEEnabled");
d.ACCouplingMode = getValue<bool>(tbl, "ACCouplingMode");
d.rangeIndex = getValue<bool>(tbl, "rangeIndex");
d.qty = static_cast<DaqChannel::Qty>(getValue<int64_t>(tbl, "qty"));
d.digitalHighPassCutOn = getValue<double>(tbl, "digitalHighpassCutOn");
return d;
}
DaqConfiguration DaqConfiguration::fromTOML(const std::string &tomlstr) {
DEBUGTRACE_ENTER;
using std::runtime_error;
try {
toml::table tbl = toml::parse(tomlstr);
DaqConfiguration config;
auto daqapi = tbl["daqapi"];
DaqApi api;
api.apicode = getValue<int64_t>(daqapi, "apicode");
api.api_specific_subcode = getValue<int64_t>(daqapi, "api_specific_subcode");
api.apiname = getValue<string>(daqapi, "apiname");
config.api = api;
config.device_name = getValue<string>(tbl, "device_name");
config.sampleRateIndex = getValue<int64_t>(tbl, "sampleRateIndex");
config.dataTypeIndex = getValue<int>(tbl, "dataTypeIndex");
config.framesPerBlockIndex = getValue<int64_t>(tbl, "framesPerBlockIndex");
config.monitorOutput = getValue<bool>(tbl, "monitorOutput");
if(toml::array* in_arr = tbl["inchannel_config"].as_array()) {
for(auto& el: *in_arr) {
config.inchannel_config.push_back(TOMLToDaqChannel(el));
}
} else {
throw runtime_error("inchannel_config is not an array");
}
if(toml::array* out_arr = tbl["outchannel_config"].as_array()) {
for(auto& el: *out_arr) {
config.outchannel_config.push_back(TOMLToDaqChannel(el));
}
} else {
throw runtime_error("outchannel_config is not an array");
}
return config;
} catch (const toml::parse_error &e) {
throw std::runtime_error(string("Error parsing TOML DaqConfiguration: ") +
e.what());
}
}