/* #define DEBUGTRACE_ENABLED */ #include "debugtrace.hpp" #include "lasp_daqconfig.h" #include "lasp_deviceinfo.h" #include #include #include using std::vector; vector DaqApi::getAvailableApis() { vector 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); inch.rangeIndex = device.prefInputRangeIndex; i++; } i = 0; for (auto &outch : outchannel_config) { outch.name = "Unnamed output channel " + std::to_string(i); outch.rangeIndex = device.prefOutputRangeIndex; i++; } sampleRateIndex = device.prefSampleRateIndex; dataTypeIndex = device.prefDataTypeIndex; framesPerBlockIndex = device.prefFramesPerBlockIndex; monitorOutput = false; assert(match(device)); } bool DaqConfiguration::match(const DeviceInfo &dev) const { DEBUGTRACE_ENTER; return (dev.device_name == device_name && dev.api == api); } int DaqConfiguration::getHighestEnabledInChannel() const { DEBUGTRACE_ENTER; for (int i = inchannel_config.size() - 1; i > -1; i--) { if (inchannel_config.at(i).enabled) return i; } return -1; } int DaqConfiguration::getHighestEnabledOutChannel() const { DEBUGTRACE_ENTER; for (int i = outchannel_config.size() - 1; i > -1; i--) { if (outchannel_config.at(i).enabled) return i; } return -1; } int DaqConfiguration::getLowestEnabledInChannel() const { DEBUGTRACE_ENTER; 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 DaqConfiguration::enabledInChannels(const bool include_monitor) const { vector res; if(monitorOutput && include_monitor) { 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 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(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 T getValue(R &tbl, const char* key) { using std::runtime_error; DEBUGTRACE_ENTER; // Yeah, weird syntax quirck of C++ std::optional val = tbl[key].template value(); if (!val) { throw runtime_error(string("Error while parsing Daq configuration. Table " "does not contain key: ") + key); } return val.value(); } template DaqChannel TOMLToDaqChannel(T& node) { DEBUGTRACE_ENTER; DaqChannel d; toml::table& tbl = *node.as_table(); d.enabled = getValue(tbl, "enabled"); d.name = getValue(tbl, "name"); d.sensitivity = getValue(tbl, "sensitivity"); d.IEPEEnabled = getValue(tbl, "IEPEEnabled"); d.ACCouplingMode = getValue(tbl, "ACCouplingMode"); d.rangeIndex = getValue(tbl, "rangeIndex"); d.qty = static_cast(getValue(tbl, "qty")); d.digitalHighPassCutOn = getValue(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(daqapi, "apicode"); api.api_specific_subcode = getValue(daqapi, "api_specific_subcode"); api.apiname = getValue(daqapi, "apiname"); config.api = api; config.device_name = getValue(tbl, "device_name"); config.sampleRateIndex = getValue(tbl, "sampleRateIndex"); config.dataTypeIndex = getValue(tbl, "dataTypeIndex"); config.framesPerBlockIndex = getValue(tbl, "framesPerBlockIndex"); config.monitorOutput = getValue(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()); } }