2022-05-23 15:26:29 +00:00
|
|
|
#pragma once
|
|
|
|
#include "lasp_config.h"
|
2022-06-13 19:30:02 +00:00
|
|
|
#include "lasp_daqdata.h"
|
2022-05-23 15:26:29 +00:00
|
|
|
#include "lasp_deviceinfo.h"
|
|
|
|
#include "lasp_types.h"
|
|
|
|
#include <functional>
|
2022-06-29 10:25:32 +00:00
|
|
|
#include <memory>
|
2023-06-06 14:05:24 +00:00
|
|
|
#include <mutex>
|
|
|
|
#include <atomic>
|
2022-06-29 10:25:32 +00:00
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
2022-09-03 18:59:14 +00:00
|
|
|
* \defgroup device Device interfacing
|
|
|
|
* \addtogroup device
|
|
|
|
* @{
|
2022-06-13 19:30:02 +00:00
|
|
|
* @brief Callback of DAQ for input data. Callback should return
|
|
|
|
* false for a stop request.
|
2022-05-23 15:26:29 +00:00
|
|
|
*/
|
2023-06-09 08:43:04 +00:00
|
|
|
using InDaqCallback = std::function<void(const DaqData &)>;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
2022-06-29 10:25:32 +00:00
|
|
|
* @brief
|
2022-05-23 15:26:29 +00:00
|
|
|
*/
|
2023-06-09 08:43:04 +00:00
|
|
|
using OutDaqCallback = std::function<void(DaqData &)>;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
2022-09-03 18:59:14 +00:00
|
|
|
* @brief Base cass for all DAQ (Data Acquisition) interfaces. A DAQ can be a
|
|
|
|
* custom device, or for example a sound card.
|
2022-05-23 15:26:29 +00:00
|
|
|
*/
|
|
|
|
class Daq : public DaqConfiguration, public DeviceInfo {
|
|
|
|
|
|
|
|
protected:
|
|
|
|
Daq(const DeviceInfo &devinfo, const DaqConfiguration &config);
|
|
|
|
Daq(const Daq &) = delete;
|
|
|
|
|
|
|
|
public:
|
2022-07-20 12:58:48 +00:00
|
|
|
/**
|
|
|
|
* @brief Information regarding a stream.
|
|
|
|
*/
|
|
|
|
class StreamStatus {
|
|
|
|
public:
|
|
|
|
enum class StreamError {
|
|
|
|
noError,
|
|
|
|
inputXRun,
|
|
|
|
outputXRun,
|
|
|
|
driverError,
|
|
|
|
systemError,
|
|
|
|
threadError,
|
|
|
|
logicError,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Map between error types and messages
|
|
|
|
*/
|
|
|
|
inline static const std::map<StreamError, std::string> errorMessages{
|
|
|
|
{StreamError::noError, "No error"},
|
|
|
|
{StreamError::inputXRun, "Input buffer overrun"},
|
|
|
|
{StreamError::outputXRun, "Output buffer underrun"},
|
|
|
|
{StreamError::driverError, "Driver error"},
|
|
|
|
{StreamError::systemError, "System error"},
|
|
|
|
{StreamError::threadError, "Thread error"},
|
|
|
|
{StreamError::logicError, "Logic error (probably a bug)"},
|
|
|
|
};
|
2023-01-04 13:21:39 +00:00
|
|
|
|
2022-07-20 12:58:48 +00:00
|
|
|
bool isRunning = false;
|
|
|
|
/**
|
|
|
|
* @brief Check if stream has error
|
|
|
|
*
|
|
|
|
* @return true if there is an error.
|
|
|
|
*/
|
|
|
|
bool error() const { return errorType != StreamError::noError; };
|
|
|
|
|
|
|
|
StreamError errorType{StreamError::noError};
|
|
|
|
|
|
|
|
std::string errorMsg() const { return errorMessages.at(errorType); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Returns true if everything is OK with a certain stream and the
|
|
|
|
* stream is running.
|
|
|
|
*
|
|
|
|
* @return as described above.
|
|
|
|
*/
|
|
|
|
bool runningOK() const { return isRunning && !error(); }
|
2023-01-04 13:21:39 +00:00
|
|
|
|
|
|
|
}; // End of class StreamStatus
|
|
|
|
|
|
|
|
using rte = std::runtime_error;
|
|
|
|
/**
|
|
|
|
* @brief Used for internal throwing of exceptions.
|
|
|
|
*/
|
|
|
|
class StreamException : public rte {
|
|
|
|
using StreamError = StreamStatus::StreamError;
|
|
|
|
|
|
|
|
public:
|
|
|
|
StreamStatus::StreamError e;
|
|
|
|
StreamException(const StreamStatus::StreamError e)
|
|
|
|
: rte(StreamStatus::errorMessages.at(e)), e(e) {}
|
|
|
|
StreamException(const StreamStatus::StreamError e,
|
|
|
|
const std::string &additional_info)
|
|
|
|
: rte(StreamStatus::errorMessages.at(e) + ": " + additional_info),
|
|
|
|
e(e) {}
|
|
|
|
operator StreamError() { return e; }
|
2022-07-20 12:58:48 +00:00
|
|
|
};
|
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Create a Daq based on given device info and configuration
|
|
|
|
*
|
|
|
|
* @param devinfo Device information of device to be used.
|
|
|
|
* @param config Configuation to apply to the deviec
|
|
|
|
*
|
|
|
|
* @return Pointer to Daq device created.
|
|
|
|
*/
|
|
|
|
static std::unique_ptr<Daq> createDaq(const DeviceInfo &devinfo,
|
|
|
|
const DaqConfiguration &config);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Start the Daq.
|
|
|
|
*
|
2022-09-03 18:59:14 +00:00
|
|
|
* @param inCallback Function that is called with input data as argument,
|
|
|
|
* and which expects output data as a return value. If no input data is
|
|
|
|
* required, this callback should be unset.
|
|
|
|
* @param outCallback Function that is called when output data is required.
|
|
|
|
* If the stream does not require output data, this callback should be unset.
|
2022-05-23 15:26:29 +00:00
|
|
|
*/
|
2022-09-03 18:59:14 +00:00
|
|
|
virtual void start(InDaqCallback inCallback, OutDaqCallback outCallback) = 0;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Stop the Daq device. Throws an exception if the device is not
|
|
|
|
* running at the time this method is called.
|
|
|
|
*/
|
|
|
|
virtual void stop() = 0;
|
|
|
|
|
2022-07-20 12:58:48 +00:00
|
|
|
virtual ~Daq() = 0;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Returns the number of enabled input channels
|
|
|
|
*
|
2022-10-05 09:27:46 +00:00
|
|
|
* @param include_monitorchannels if true, all channels that are internally
|
2022-05-23 15:26:29 +00:00
|
|
|
* monitored are added to the list.
|
|
|
|
*
|
|
|
|
* @return number of enabled input channels
|
|
|
|
*/
|
2022-10-05 09:27:46 +00:00
|
|
|
us neninchannels(bool include_monitorchannels = true) const;
|
2022-07-20 12:58:48 +00:00
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Returns the number of enabled output channels
|
|
|
|
*
|
|
|
|
* @return Number of enabled output channels
|
|
|
|
*/
|
|
|
|
us nenoutchannels() const;
|
|
|
|
|
2022-07-20 12:58:48 +00:00
|
|
|
/**
|
|
|
|
* @brief Create a vector of boolean values of the enabled input channels.
|
|
|
|
*
|
|
|
|
* @param include_monitor If set to true and a monitor channel is available,
|
|
|
|
* the first index in the array will correspond to the monitor channel, and
|
|
|
|
* whether it is enabled
|
|
|
|
*
|
|
|
|
* @return Boolean vector
|
|
|
|
*/
|
2022-10-17 17:37:31 +00:00
|
|
|
boolvec eninchannels(const bool include_monitor = true) const;
|
2022-10-05 09:27:46 +00:00
|
|
|
|
2022-07-20 12:58:48 +00:00
|
|
|
/**
|
|
|
|
* @brief Create an array of booleans for each enabled output channel.
|
|
|
|
*
|
|
|
|
* @return Boolean vector
|
|
|
|
*/
|
|
|
|
boolvec enoutchannels() const;
|
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Returns current sample rate
|
|
|
|
*
|
|
|
|
* @return Sample rate in [Hz]
|
|
|
|
*/
|
|
|
|
double samplerate() const;
|
2022-07-20 12:58:48 +00:00
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
2022-10-17 17:37:31 +00:00
|
|
|
* @brief Returns the input range for each channel. Maximum allowed absolute
|
|
|
|
* value of the signal that can pass unclipped.
|
2022-05-23 15:26:29 +00:00
|
|
|
*
|
2022-10-17 17:37:31 +00:00
|
|
|
* @param include_monitor If set to true and a monitor channel is available,
|
|
|
|
* the first index in the array will correspond to the monitor channel.
|
|
|
|
* *
|
2022-05-23 15:26:29 +00:00
|
|
|
* @return Maximum offset from 0 before clipping.
|
|
|
|
*/
|
2023-01-04 13:21:39 +00:00
|
|
|
dvec inputRangeForEnabledChannels(const bool include_monitor = true) const;
|
2022-07-20 12:58:48 +00:00
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Returns datatype (enum) corresponding to the datatype of the
|
|
|
|
* samples.
|
|
|
|
*
|
|
|
|
* @return That as stated aboce
|
|
|
|
*/
|
|
|
|
DataTypeDescriptor::DataType dataType() const;
|
2022-07-20 12:58:48 +00:00
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief More elaborate description of the datatypes.
|
|
|
|
*
|
|
|
|
* @return A DataTypeDescriptor
|
|
|
|
*/
|
2023-01-04 13:21:39 +00:00
|
|
|
const DataTypeDescriptor &dtypeDescr() const;
|
2022-05-23 15:26:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief The number of frames that is send in a block of DaqData.
|
|
|
|
*
|
|
|
|
* @return The number of frames per block
|
|
|
|
*/
|
|
|
|
us framesPerBlock() const {
|
|
|
|
return availableFramesPerBlock.at(framesPerBlockIndex);
|
|
|
|
}
|
2022-06-29 10:25:32 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get stream status corresponding to current DAQ.
|
|
|
|
*
|
|
|
|
* @return StreamStatus object.
|
|
|
|
*/
|
|
|
|
virtual StreamStatus getStreamStatus() const = 0;
|
|
|
|
|
2022-05-23 15:26:29 +00:00
|
|
|
/**
|
|
|
|
* @brief Whether the device runs in duplex mode (both input and output), or
|
|
|
|
* false if only input / only output.
|
|
|
|
*
|
|
|
|
* @return true if duplex
|
|
|
|
*/
|
|
|
|
bool duplexMode() const {
|
2022-07-20 12:58:48 +00:00
|
|
|
return (neninchannels() > 0 && nenoutchannels() > 0);
|
2022-05-23 15:26:29 +00:00
|
|
|
}
|
|
|
|
};
|
2022-09-03 18:59:14 +00:00
|
|
|
/** @} */
|