#pragma once #include "lasp_config.h" #include "lasp_types.h" #include #include /** \addtogroup device * @{ */ using std::string; using std::to_string; using boolvec = std::vector; using dvec = std::vector; using usvec = std::vector; /** * @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 = { .name = "32-bits floating point", .sw = 4, .is_floating = true, .dtype = DataTypeDescriptor::DataType::dtype_fl32}; const DataTypeDescriptor dtype_desc_fl64 = { .name = "64-bits floating point", .sw = 8, .is_floating = true, .dtype = DataTypeDescriptor::DataType::dtype_fl64}; const DataTypeDescriptor dtype_desc_int8 = { .name = "8-bits integer", .sw = 1, .is_floating = false, .dtype = DataTypeDescriptor::DataType::dtype_int8}; const DataTypeDescriptor dtype_desc_int16 = { .name = "16-bits integer", .sw = 2, .is_floating = false, .dtype = DataTypeDescriptor::DataType::dtype_int16}; const DataTypeDescriptor dtype_desc_int32 = { .name = "32-bits integer", .sw = 4, .is_floating = false, .dtype = DataTypeDescriptor::DataType::dtype_int32}; const std::map 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}, }; /** * @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. */ 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); } operator string() const { return apiname; } static std::vector getAvailableApis(); }; #if LASP_HAS_ULDAQ == 1 const us LASP_ULDAQ_APICODE = 0; const DaqApi uldaqapi("UlDaq", 0); #endif #if LASP_HAS_RTAUDIO == 1 #include "RtAudio.h" const us LASP_RTAUDIO_APICODE = 1; const DaqApi rtaudioAlsaApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA); const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", LASP_RTAUDIO_APICODE, RtAudio::Api::LINUX_PULSE); const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", LASP_RTAUDIO_APICODE, RtAudio::Api::WINDOWS_WASAPI); const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", LASP_RTAUDIO_APICODE, RtAudio::Api::WINDOWS_DS); const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", LASP_RTAUDIO_APICODE, RtAudio::Api::WINDOWS_ASIO); #endif #if LASP_HAS_PORTAUDIO == 1 const us LASP_PORTAUDIO_APICODE = 2; 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); #endif class DeviceInfo; /** * @brief Stores channel configuration data for each channel. I.e. the physical * quantity measured, sensitivity, device-specific channel settings, a channel * name and others. */ class DaqChannel { public: /** * @brief Possible physical quantities that are recorded. */ enum class Qty { Number, AcousticPressure, Voltage, UserDefined }; DaqChannel() {} /** * @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. */ double sensitivity = 1; /** * @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; /** * @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 */ bool operator==(const DaqChannel &other) const { return other.name == name && other.sensitivity == sensitivity && other.qty == qty; } }; /** * @brief Configuration of a DAQ device */ class DaqConfiguration { public: /** * @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 */ static DaqConfiguration fromTOML(const std::string &toml); DaqApi api; /** * @brief The internal device name this DAQ configuration applies to. */ string device_name; /** * @brief Channel configuration for input channels */ std::vector inchannel_config; /** * @brief Return list of enabled input channels * * @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). * * @return That. */ std::vector enabledInChannels(const bool include_monitor = true) const; /** * @brief Channel configuration for output channels */ std::vector outchannel_config; /** * @brief Index in list of sample rates that are available for the device. */ int sampleRateIndex = 0; // /** * @brief Required datatype for output, should be present in the list */ int dataTypeIndex = 0; /** * @brief The index in the array of frames per block that can be used for the * device. */ int framesPerBlockIndex = 0; /** * @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 */ DaqConfiguration(const DeviceInfo &deviceinfo); DaqConfiguration() {} /** * @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; /** * @brief Get the enabled highest channel number from the list of enabled * input channels. * * @return Index to the highest input channel. -1 if no input channels are * enabled. */ int getHighestEnabledInChannel() const; /** * @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. */ int getHighestEnabledOutChannel() const; /** * @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. */ int getLowestEnabledInChannel() const; /** * @brief Get the lowest channel number from the list of enabled output * channels. * * @return Index to the lowest output channel. -1 if no output channels are * enabled. */ int getLowestEnabledOutChannel() const; /** * @brief Set all input channels to enabled / disabled state. * * @param val true if enabled, false if disabled. */ void setAllInputEnabled(bool val) { for (auto &ch : inchannel_config) { ch.enabled = val; } } /** * @brief Set all output channels to enabled / disabled state. * * @param val true if enabled, false if disabled. */ void setAllOutputEnabled(bool val) { for (auto &ch : outchannel_config) { ch.enabled = val; } } }; /** * @} */