2022-09-27 15:20:45 +00:00
|
|
|
/* #define DEBUGTRACE_ENABLED */
|
2022-08-01 15:26:22 +00:00
|
|
|
#include "debugtrace.hpp"
|
|
|
|
|
2022-07-20 12:58:48 +00:00
|
|
|
#include "lasp_daqconfig.h"
|
|
|
|
#include "lasp_deviceinfo.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cassert>
|
2022-08-01 15:26:22 +00:00
|
|
|
#include <stdexcept>
|
2022-07-20 12:58:48 +00:00
|
|
|
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
vector<DaqApi> DaqApi::getAvailableApis() {
|
|
|
|
|
|
|
|
vector<DaqApi> apis;
|
|
|
|
#if LASP_HAS_ULDAQ == 1
|
2022-08-01 15:26:22 +00:00
|
|
|
apis.push_back(uldaqapi);
|
2022-07-20 12:58:48 +00:00
|
|
|
#endif
|
|
|
|
#if LASP_HAS_RTAUDIO == 1
|
2022-08-01 15:26:22 +00:00
|
|
|
apis.push_back(rtaudioAlsaApi);
|
|
|
|
apis.push_back(rtaudioPulseaudioApi);
|
|
|
|
apis.push_back(rtaudioWasapiApi);
|
|
|
|
apis.push_back(rtaudioDsApi);
|
|
|
|
apis.push_back(rtaudioAsioApi);
|
2022-07-20 12:58:48 +00:00
|
|
|
#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;
|
2022-08-01 15:26:22 +00:00
|
|
|
for (auto &inch : inchannel_config) {
|
2022-07-20 12:58:48 +00:00
|
|
|
inch.name = "Unnamed input channel " + std::to_string(i);
|
2024-01-10 11:26:38 +00:00
|
|
|
inch.rangeIndex = device.prefInputRangeIndex;
|
2022-07-20 12:58:48 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
2022-08-01 15:26:22 +00:00
|
|
|
for (auto &outch : outchannel_config) {
|
2022-07-20 12:58:48 +00:00
|
|
|
outch.name = "Unnamed output channel " + std::to_string(i);
|
2024-01-10 11:26:38 +00:00
|
|
|
outch.rangeIndex = device.prefOutputRangeIndex;
|
2022-07-20 12:58:48 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-10-10 17:17:38 +00:00
|
|
|
int DaqConfiguration::getHighestEnabledInChannel() const {
|
2022-07-20 12:58:48 +00:00
|
|
|
for (int i = inchannel_config.size() - 1; i > -1; i--) {
|
|
|
|
if (inchannel_config.at(i).enabled)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-10-10 17:17:38 +00:00
|
|
|
int DaqConfiguration::getHighestEnabledOutChannel() const {
|
2022-07-20 12:58:48 +00:00
|
|
|
for (us i = outchannel_config.size() - 1; i >= 0; i--) {
|
|
|
|
if (outchannel_config.at(i).enabled)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2022-10-10 17:17:38 +00:00
|
|
|
int DaqConfiguration::getLowestEnabledInChannel() const {
|
2022-07-20 12:58:48 +00:00
|
|
|
for (us i = 0; i < inchannel_config.size(); i++) {
|
|
|
|
if (inchannel_config.at(i).enabled)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2022-10-10 17:17:38 +00:00
|
|
|
int DaqConfiguration::getLowestEnabledOutChannel() const {
|
2022-07-20 12:58:48 +00:00
|
|
|
for (us i = 0; i < outchannel_config.size(); i++) {
|
|
|
|
if (outchannel_config.at(i).enabled)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2022-10-17 17:37:31 +00:00
|
|
|
vector<DaqChannel> DaqConfiguration::enabledInChannels(const bool include_monitor) const {
|
2022-09-28 07:41:02 +00:00
|
|
|
vector<DaqChannel> res;
|
2022-10-17 17:37:31 +00:00
|
|
|
if(monitorOutput && include_monitor) {
|
2022-10-05 09:27:46 +00:00
|
|
|
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));
|
|
|
|
|
|
|
|
}
|
2022-09-28 07:41:02 +00:00
|
|
|
for(auto& ch: inchannel_config) {
|
|
|
|
if(ch.enabled) { res.push_back(ch);}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2022-07-20 12:58:48 +00:00
|
|
|
|
|
|
|
#include "toml++/toml.h"
|
|
|
|
#include <sstream>
|
|
|
|
|
2022-08-01 15:26:22 +00:00
|
|
|
toml::table daqChannelToTOML(const DaqChannel &ch) {
|
|
|
|
DEBUGTRACE_ENTER;
|
2022-07-20 12:58:48 +00:00
|
|
|
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 {
|
|
|
|
|
2022-08-01 15:26:22 +00:00
|
|
|
DEBUGTRACE_ENTER;
|
|
|
|
toml::table apitbl{
|
|
|
|
{"apiname", api.apiname}, // Api name
|
|
|
|
{"apicode", api.apicode}, // Api code
|
|
|
|
{"api_specific_subcode", api.api_specific_subcode} // Subcode
|
|
|
|
};
|
2022-07-20 12:58:48 +00:00
|
|
|
|
|
|
|
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;
|
2022-08-01 15:26:22 +00:00
|
|
|
for (const auto &ch : inchannel_config) {
|
2022-07-20 12:58:48 +00:00
|
|
|
inchannel_config_tbl.emplace_back(daqChannelToTOML(ch));
|
|
|
|
}
|
|
|
|
tbl.emplace("inchannel_config", inchannel_config_tbl);
|
|
|
|
|
|
|
|
toml::array outchannel_config_tbl;
|
2022-08-01 15:26:22 +00:00
|
|
|
for (const auto &ch : outchannel_config) {
|
2022-07-20 12:58:48 +00:00
|
|
|
outchannel_config_tbl.emplace_back(daqChannelToTOML(ch));
|
|
|
|
}
|
|
|
|
tbl.emplace("outchannel_config", outchannel_config_tbl);
|
|
|
|
|
|
|
|
std::stringstream str;
|
|
|
|
|
|
|
|
str << tbl;
|
|
|
|
|
|
|
|
return str.str();
|
|
|
|
}
|
2022-08-01 15:26:22 +00:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|