2022-05-23 15:26:29 +00:00
|
|
|
#pragma once
|
2022-06-29 10:25:32 +00:00
|
|
|
#include "lasp_config.h"
|
|
|
|
#include "lasp_types.h"
|
2022-07-20 12:58:48 +00:00
|
|
|
#include <map>
|
|
|
|
#include <vector>
|
2022-05-23 15:26:29 +00:00
|
|
|
|
2022-09-03 18:59:14 +00:00
|
|
|
/** \addtogroup device
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
using std::string;
|
|
|
|
using std::to_string;
|
|
|
|
|
|
|
|
using boolvec = std::vector<bool>;
|
|
|
|
using dvec = std::vector<double>;
|
|
|
|
using usvec = std::vector<us>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Descriptor for data types containing more detailed information.
|
|
|
|
*/
|
|
|
|
class DataTypeDescriptor {
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Basic data types coming from a DAQ that we can deal with. The naming
|
|
|
|
* will be self-explainging.
|
|
|
|
*/
|
|
|
|
enum class DataType {
|
|
|
|
dtype_fl32 = 0,
|
|
|
|
dtype_fl64 = 1,
|
|
|
|
dtype_int8 = 2,
|
|
|
|
dtype_int16 = 3,
|
|
|
|
dtype_int32 = 4
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Name of the datatype
|
|
|
|
*/
|
|
|
|
string name;
|
|
|
|
/**
|
|
|
|
* @brief Sample width of a single sample, in bytes
|
|
|
|
*/
|
|
|
|
unsigned sw;
|
|
|
|
/**
|
|
|
|
* @brief Whether the datatype is a floating point Y/N
|
|
|
|
*/
|
|
|
|
bool is_floating;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief The number from the enumeration
|
|
|
|
*/
|
|
|
|
DataType dtype;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Down-cast a DataTypeDescriptor to a datatype
|
|
|
|
*
|
|
|
|
* @return The descriptor as an enum
|
|
|
|
*/
|
|
|
|
operator DataType() { return dtype; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Compare two data type descriptors. Returns true if the DataType
|
|
|
|
* enumerator is the same.
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
bool operator==(const DataTypeDescriptor &o) noexcept {
|
|
|
|
return dtype == o.dtype;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const DataTypeDescriptor dtype_desc_fl32 = {
|
2022-09-03 18:59:14 +00:00
|
|
|
.name = "32-bits floating point",
|
|
|
|
.sw = 4,
|
|
|
|
.is_floating = true,
|
|
|
|
.dtype = DataTypeDescriptor::DataType::dtype_fl32};
|
2022-05-23 15:26:29 +00:00
|
|
|
const DataTypeDescriptor dtype_desc_fl64 = {
|
2022-09-03 18:59:14 +00:00
|
|
|
.name = "64-bits floating point",
|
|
|
|
.sw = 8,
|
|
|
|
.is_floating = true,
|
|
|
|
.dtype = DataTypeDescriptor::DataType::dtype_fl64};
|
2022-05-23 15:26:29 +00:00
|
|
|
const DataTypeDescriptor dtype_desc_int8 = {
|
2022-09-03 18:59:14 +00:00
|
|
|
.name = "8-bits integer",
|
|
|
|
.sw = 1,
|
|
|
|
.is_floating = false,
|
|
|
|
.dtype = DataTypeDescriptor::DataType::dtype_int8};
|
2022-05-23 15:26:29 +00:00
|
|
|
const DataTypeDescriptor dtype_desc_int16 = {
|
2022-09-03 18:59:14 +00:00
|
|
|
.name = "16-bits integer",
|
|
|
|
.sw = 2,
|
|
|
|
.is_floating = false,
|
|
|
|
.dtype = DataTypeDescriptor::DataType::dtype_int16};
|
2022-05-23 15:26:29 +00:00
|
|
|
const DataTypeDescriptor dtype_desc_int32 = {
|
2022-09-03 18:59:14 +00:00
|
|
|
.name = "32-bits integer",
|
|
|
|
.sw = 4,
|
|
|
|
.is_floating = false,
|
|
|
|
.dtype = DataTypeDescriptor::DataType::dtype_int32};
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
const std::map<DataTypeDescriptor::DataType, const DataTypeDescriptor>
|
|
|
|
dtype_map = {
|
|
|
|
{DataTypeDescriptor::DataType::dtype_fl32, dtype_desc_fl32},
|
|
|
|
{DataTypeDescriptor::DataType::dtype_fl64, dtype_desc_fl64},
|
|
|
|
{DataTypeDescriptor::DataType::dtype_int8, dtype_desc_int8},
|
|
|
|
{DataTypeDescriptor::DataType::dtype_int16, dtype_desc_int16},
|
|
|
|
{DataTypeDescriptor::DataType::dtype_int32, dtype_desc_int32},
|
|
|
|
};
|
|
|
|
|
2022-09-03 18:59:14 +00:00
|
|
|
/**
|
|
|
|
* @brief Class that specifies API related information. An API configuration is
|
|
|
|
* part of the DAQConfiguration and used to address a certain physical device.
|
|
|
|
* For that, a specific backend needs to be compiled in. Examples of API's are
|
|
|
|
* RtAudio and UlDaq.
|
|
|
|
*/
|
2022-05-23 15:26:29 +00:00
|
|
|
class DaqApi {
|
|
|
|
public:
|
|
|
|
string apiname = "Invalid API";
|
|
|
|
int apicode = -1;
|
|
|
|
unsigned api_specific_subcode = 0;
|
|
|
|
|
|
|
|
DaqApi(string apiname, unsigned apicode, unsigned api_specific_subcode = 0)
|
|
|
|
: apiname(apiname), apicode(apicode),
|
|
|
|
api_specific_subcode(api_specific_subcode) {}
|
|
|
|
DaqApi() {}
|
|
|
|
bool operator==(const DaqApi &other) const {
|
|
|
|
return (apiname == other.apiname && apicode == other.apicode &&
|
|
|
|
api_specific_subcode == other.api_specific_subcode);
|
|
|
|
}
|
2022-09-22 08:18:38 +00:00
|
|
|
operator string() const { return apiname; }
|
2022-05-23 15:26:29 +00:00
|
|
|
static std::vector<DaqApi> getAvailableApis();
|
|
|
|
};
|
|
|
|
|
|
|
|
#if LASP_HAS_ULDAQ == 1
|
2022-07-29 07:32:26 +00:00
|
|
|
const us LASP_ULDAQ_APICODE = 0;
|
2022-05-23 15:26:29 +00:00
|
|
|
const DaqApi uldaqapi("UlDaq", 0);
|
|
|
|
#endif
|
|
|
|
#if LASP_HAS_RTAUDIO == 1
|
2022-10-05 09:27:46 +00:00
|
|
|
#include "RtAudio.h"
|
2022-07-29 07:32:26 +00:00
|
|
|
const us LASP_RTAUDIO_APICODE = 1;
|
2022-05-23 15:26:29 +00:00
|
|
|
const DaqApi rtaudioAlsaApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA);
|
2023-06-14 19:23:53 +00:00
|
|
|
const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", LASP_RTAUDIO_APICODE,
|
2022-05-23 15:26:29 +00:00
|
|
|
RtAudio::Api::LINUX_PULSE);
|
2023-06-14 19:23:53 +00:00
|
|
|
const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", LASP_RTAUDIO_APICODE,
|
2022-05-23 15:26:29 +00:00
|
|
|
RtAudio::Api::WINDOWS_WASAPI);
|
2023-06-14 19:23:53 +00:00
|
|
|
const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", LASP_RTAUDIO_APICODE,
|
2022-05-23 15:26:29 +00:00
|
|
|
RtAudio::Api::WINDOWS_DS);
|
2023-06-14 19:23:53 +00:00
|
|
|
const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", LASP_RTAUDIO_APICODE,
|
2022-05-23 15:26:29 +00:00
|
|
|
RtAudio::Api::WINDOWS_ASIO);
|
|
|
|
#endif
|
2023-06-14 19:23:53 +00:00
|
|
|
#if LASP_HAS_PORTAUDIO == 1
|
|
|
|
const us LASP_PORTAUDIO_APICODE = 2;
|
2024-01-25 14:31:53 +00:00
|
|
|
const DaqApi portaudioALSAApi("PortAudio Linux ALSA", LASP_PORTAUDIO_APICODE, 0);
|
|
|
|
const DaqApi portaudioPulseApi("PortAudio Linux PulseAudio", LASP_PORTAUDIO_APICODE, 1);
|
|
|
|
const DaqApi portaudioASIOApi("PortAudio Windows ASIO", LASP_PORTAUDIO_APICODE, 2);
|
|
|
|
const DaqApi portaudioDSApi("PortAudio Windows DirectSound", LASP_PORTAUDIO_APICODE, 3);
|
|
|
|
const DaqApi portaudioWMMEApi("PortAudio Windows WMME", LASP_PORTAUDIO_APICODE, 4);
|
|
|
|
const DaqApi portaudioWASAPIApi("PortAudio Windows WASAPI", LASP_PORTAUDIO_APICODE, 5);
|
|
|
|
const DaqApi portaudioWDMKS("PortAudio Windows WDMKS", LASP_PORTAUDIO_APICODE, 6);
|
|
|
|
const DaqApi portaudioDirectSoundApi("PortAudio Windows DirectSound", LASP_PORTAUDIO_APICODE, 7);
|
2023-06-14 19:23:53 +00:00
|
|
|
#endif
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
class DeviceInfo;
|
2022-07-20 12:58:48 +00:00
|
|
|
|
|
|
|
/**
|
2022-09-03 18:59:14 +00:00
|
|
|
* @brief Stores channel configuration data for each channel. I.e. the physical
|
|
|
|
* quantity measured, sensitivity, device-specific channel settings, a channel
|
|
|
|
* name and others.
|
2022-07-20 12:58:48 +00:00
|
|
|
*/
|
|
|
|
class DaqChannel {
|
|
|
|
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Possible physical quantities that are recorded.
|
|
|
|
*/
|
2022-09-22 08:18:38 +00:00
|
|
|
enum class Qty { Number, AcousticPressure, Voltage, UserDefined };
|
|
|
|
|
2022-10-05 09:27:46 +00:00
|
|
|
DaqChannel() {}
|
2022-07-20 12:58:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Whether the channel is enabled.
|
|
|
|
*/
|
|
|
|
bool enabled = false;
|
|
|
|
|
|
|
|
string name = "";
|
|
|
|
/**
|
|
|
|
* @brief The conversion between recorded physical unit and stored /
|
|
|
|
* outputed number, i.e. Number/Volt or Number/Pa. Converting stored numbers
|
|
|
|
* to physical qty, *divide* by the sensitivity.
|
|
|
|
*/
|
2022-10-05 09:27:46 +00:00
|
|
|
double sensitivity = 1;
|
2022-07-20 12:58:48 +00:00
|
|
|
/**
|
|
|
|
* @brief For input-only: enable IEPE constant current power supply for
|
|
|
|
* this channel. Only for hardware that is capable of doing so.
|
|
|
|
*/
|
|
|
|
bool IEPEEnabled = false;
|
|
|
|
/**
|
|
|
|
* @brief Whether to enable HW AC-coupling for this channel.
|
|
|
|
*/
|
|
|
|
bool ACCouplingMode = false;
|
|
|
|
/**
|
|
|
|
* @brief Index in possible ranges for input / output
|
|
|
|
*/
|
|
|
|
int rangeIndex = 0;
|
|
|
|
/**
|
|
|
|
* @brief The physical quantity that is inputed / outputed
|
|
|
|
*/
|
|
|
|
Qty qty = Qty::Number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Whether to enable a digital high pass on the signal before
|
|
|
|
* passing the result in case of input, or outputing the result in case of
|
|
|
|
* output.
|
|
|
|
*/
|
|
|
|
double digitalHighPassCutOn = -1;
|
2022-10-01 17:59:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Compare two channels to eachother. They are equal when the name,
|
|
|
|
* sensitivity and quantity are the same.
|
|
|
|
*
|
|
|
|
* @param other The DaqChannel to compare with
|
|
|
|
*
|
|
|
|
* @return true if equal
|
|
|
|
*/
|
2022-10-17 17:37:31 +00:00
|
|
|
bool operator==(const DaqChannel &other) const {
|
|
|
|
return other.name == name && other.sensitivity == sensitivity &&
|
|
|
|
other.qty == qty;
|
2022-10-01 17:59:35 +00:00
|
|
|
}
|
2022-07-20 12:58:48 +00:00
|
|
|
};
|
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Configuration of a DAQ device
|
|
|
|
*/
|
|
|
|
class DaqConfiguration {
|
|
|
|
public:
|
|
|
|
/**
|
2022-07-20 12:58:48 +00:00
|
|
|
* @brief Export the class to TOML markup language.
|
|
|
|
*
|
|
|
|
* @return String with TOML exported data.
|
|
|
|
*/
|
|
|
|
std::string toTOML() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Load in a DAQConfiguration from TOML.
|
|
|
|
*
|
|
|
|
* @param toml String containing TOML data
|
|
|
|
*
|
|
|
|
* @return DaqConfiguration object
|
2022-05-23 15:26:29 +00:00
|
|
|
*/
|
2022-07-20 12:58:48 +00:00
|
|
|
static DaqConfiguration fromTOML(const std::string &toml);
|
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
DaqApi api;
|
|
|
|
/**
|
|
|
|
* @brief The internal device name this DAQ configuration applies to.
|
|
|
|
*/
|
|
|
|
string device_name;
|
|
|
|
|
|
|
|
/**
|
2022-07-20 12:58:48 +00:00
|
|
|
* @brief Channel configuration for input channels
|
2022-05-23 15:26:29 +00:00
|
|
|
*/
|
2022-07-20 12:58:48 +00:00
|
|
|
std::vector<DaqChannel> inchannel_config;
|
|
|
|
|
2022-09-28 07:41:02 +00:00
|
|
|
/**
|
|
|
|
* @brief Return list of enabled input channels
|
|
|
|
*
|
2022-10-17 17:37:31 +00:00
|
|
|
* @param include_monitor If set to true and a monitor channel is available,
|
|
|
|
* the first indices in the array will correspond to the monitor channel(s).
|
|
|
|
*
|
2022-09-28 07:41:02 +00:00
|
|
|
* @return That.
|
|
|
|
*/
|
2022-10-17 17:37:31 +00:00
|
|
|
std::vector<DaqChannel>
|
|
|
|
enabledInChannels(const bool include_monitor = true) const;
|
2022-09-28 07:41:02 +00:00
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
2022-07-20 12:58:48 +00:00
|
|
|
* @brief Channel configuration for output channels
|
2022-05-23 15:26:29 +00:00
|
|
|
*/
|
2022-07-20 12:58:48 +00:00
|
|
|
std::vector<DaqChannel> outchannel_config;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Index in list of sample rates that are available for the device.
|
|
|
|
*/
|
2022-07-20 12:58:48 +00:00
|
|
|
int sampleRateIndex = 0; //
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Required datatype for output, should be present in the list
|
|
|
|
*/
|
2022-07-20 12:58:48 +00:00
|
|
|
int dataTypeIndex = 0;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief The index in the array of frames per block that can be used for the
|
|
|
|
* device.
|
|
|
|
*/
|
2022-07-20 12:58:48 +00:00
|
|
|
int framesPerBlockIndex = 0;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief If set to true and if the device has this capability, the output
|
|
|
|
* channels are added as input channels as well.
|
|
|
|
*/
|
|
|
|
bool monitorOutput = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Create a default configuration, with all channels disabled on both
|
|
|
|
* input and output, and default channel names
|
|
|
|
*
|
|
|
|
* @param deviceinfo DeviceInfo structure
|
|
|
|
*/
|
2022-09-03 18:59:14 +00:00
|
|
|
DaqConfiguration(const DeviceInfo &deviceinfo);
|
2022-07-20 12:58:48 +00:00
|
|
|
|
|
|
|
DaqConfiguration() {}
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Check to see whether the DAQ configuration matches with the device.
|
|
|
|
* This means, some basic checks are done on channels, sampling rate, etc,
|
|
|
|
* and that the name corresponds with the device name.
|
|
|
|
*
|
|
|
|
* @param devinfo The DeviceInfo to check
|
|
|
|
*
|
|
|
|
* @return true if it matches. Otherwise false.
|
|
|
|
*/
|
|
|
|
bool match(const DeviceInfo &devinfo) const;
|
|
|
|
|
|
|
|
/**
|
2022-10-17 17:37:31 +00:00
|
|
|
* @brief Get the enabled highest channel number from the list of enabled
|
|
|
|
* input channels.
|
2022-05-23 15:26:29 +00:00
|
|
|
*
|
|
|
|
* @return Index to the highest input channel. -1 if no input channels are
|
|
|
|
* enabled.
|
|
|
|
*/
|
2022-10-10 17:17:38 +00:00
|
|
|
int getHighestEnabledInChannel() const;
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Get the highest channel number from the list of enabled output
|
|
|
|
* channels.
|
|
|
|
*
|
|
|
|
* @return Index to the highest input channel. -1 if no output channels are
|
|
|
|
* enabled.
|
|
|
|
*/
|
2022-10-10 17:17:38 +00:00
|
|
|
int getHighestEnabledOutChannel() const;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the lowest channel number from the list of enabled input
|
|
|
|
* channels.
|
|
|
|
*
|
|
|
|
* @return Index to the lowest input channel. -1 if no input channels are
|
|
|
|
* enabled.
|
|
|
|
*/
|
2022-10-10 17:17:38 +00:00
|
|
|
int getLowestEnabledInChannel() const;
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Get the lowest channel number from the list of enabled output
|
|
|
|
* channels.
|
|
|
|
*
|
2022-10-10 17:17:38 +00:00
|
|
|
* @return Index to the lowest output channel. -1 if no output channels are
|
2022-05-23 15:26:29 +00:00
|
|
|
* enabled.
|
|
|
|
*/
|
2022-10-10 17:17:38 +00:00
|
|
|
int getLowestEnabledOutChannel() const;
|
2022-09-27 15:20:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set all input channels to enabled / disabled state.
|
|
|
|
*
|
|
|
|
* @param val true if enabled, false if disabled.
|
|
|
|
*/
|
|
|
|
void setAllInputEnabled(bool val) {
|
2022-10-17 17:37:31 +00:00
|
|
|
for (auto &ch : inchannel_config) {
|
2022-09-27 15:20:45 +00:00
|
|
|
ch.enabled = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set all output channels to enabled / disabled state.
|
|
|
|
*
|
|
|
|
* @param val true if enabled, false if disabled.
|
|
|
|
*/
|
|
|
|
void setAllOutputEnabled(bool val) {
|
2022-10-17 17:37:31 +00:00
|
|
|
for (auto &ch : outchannel_config) {
|
2022-09-27 15:20:45 +00:00
|
|
|
ch.enabled = val;
|
|
|
|
}
|
|
|
|
}
|
2022-07-20 12:58:48 +00:00
|
|
|
};
|
2022-09-03 18:59:14 +00:00
|
|
|
/**
|
|
|
|
* @}
|
|
|
|
*/
|