213 lines
5.7 KiB
C++
213 lines
5.7 KiB
C++
/* #define DEBUGTRACE_ENABLED */
|
|
#include "debugtrace.hpp"
|
|
#include "lasp_config.h"
|
|
|
|
#if LASP_HAS_ULDAQ == 1
|
|
#include "lasp_daqconfig.h"
|
|
#include "lasp_uldaq.h"
|
|
#include "lasp_uldaq_bufhandler.h"
|
|
#include "lasp_uldaq_impl.h"
|
|
|
|
using namespace std::literals::chrono_literals;
|
|
|
|
DT9837A::~DT9837A() {
|
|
DEBUGTRACE_ENTER;
|
|
UlError err;
|
|
if (isRunning()) {
|
|
DEBUGTRACE_PRINT("Stop UlDAQ from destructor");
|
|
stop();
|
|
}
|
|
|
|
if (_handle) {
|
|
DEBUGTRACE_PRINT("Disconnecting and releasing DaqDevice");
|
|
/* err = ulDisconnectDaqDevice(_handle); */
|
|
/* showErr(err); */
|
|
err = ulReleaseDaqDevice(_handle);
|
|
showErr(err);
|
|
}
|
|
}
|
|
DT9837A::DT9837A(const UlDaqDeviceInfo &devinfo, const DaqConfiguration &config)
|
|
: Daq(devinfo, config),
|
|
_nFramesPerBlock(availableFramesPerBlock.at(framesPerBlockIndex)) {
|
|
|
|
const DaqDeviceDescriptor &descriptor = devinfo._uldaqDescriptor;
|
|
DEBUGTRACE_PRINT(string("Device: ") + descriptor.productName);
|
|
DEBUGTRACE_PRINT(string("Product id: ") + to_string(descriptor.productId));
|
|
DEBUGTRACE_PRINT(string("Dev string: ") + descriptor.devString);
|
|
DEBUGTRACE_PRINT(string("Unique id: ") + descriptor.uniqueId);
|
|
|
|
// get a handle to the DAQ device associated with the first descriptor
|
|
_handle = ulCreateDaqDevice(descriptor);
|
|
|
|
if (_handle == 0) {
|
|
throw rte("Unable to create a handle to the specified DAQ "
|
|
"device. Is the device currently in use? Please make sure to set "
|
|
"the DAQ configuration in duplex mode if simultaneous input and "
|
|
"output is required.");
|
|
}
|
|
|
|
UlError err = ulConnectDaqDevice(_handle);
|
|
|
|
if (err != ERR_NO_ERROR) {
|
|
ulReleaseDaqDevice(_handle);
|
|
_handle = 0;
|
|
throw rte("Unable to connect to device: " + getErrMsg(err));
|
|
}
|
|
|
|
/// Loop over input channels, set parameters
|
|
for (us ch = 0; ch < 4; ch++) {
|
|
|
|
err = ulAISetConfigDbl(_handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0);
|
|
showErr(err);
|
|
if (err != ERR_NO_ERROR) {
|
|
throw rte("Fatal: could normalize channel sensitivity");
|
|
}
|
|
|
|
CouplingMode cm = inchannel_config.at(ch).ACCouplingMode ? CM_AC : CM_DC;
|
|
err = ulAISetConfig(_handle, AI_CFG_CHAN_COUPLING_MODE, ch, cm);
|
|
if (err != ERR_NO_ERROR) {
|
|
showErr(err);
|
|
throw rte("Fatal: could not set AC/DC coupling mode");
|
|
}
|
|
|
|
IepeMode iepe =
|
|
inchannel_config.at(ch).IEPEEnabled ? IEPE_ENABLED : IEPE_DISABLED;
|
|
err = ulAISetConfig(_handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe);
|
|
if (err != ERR_NO_ERROR) {
|
|
showErr(err);
|
|
throw rte("Fatal: could not set IEPE mode");
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DT9837A::isRunning() const {
|
|
DEBUGTRACE_ENTER;
|
|
/* return _thread.joinable(); */
|
|
StreamStatus status = _streamStatus;
|
|
return status.isRunning;
|
|
}
|
|
void DT9837A::stop() {
|
|
DEBUGTRACE_ENTER;
|
|
StreamStatus status = _streamStatus;
|
|
status.isRunning = true;
|
|
_streamStatus = status;
|
|
if (!isRunning()) {
|
|
throw rte("No data acquisition running");
|
|
}
|
|
|
|
// Stop the thread and join it
|
|
_stopThread = true;
|
|
assert(_thread.joinable());
|
|
_thread.join();
|
|
_stopThread = false;
|
|
|
|
// Update stream status
|
|
status.isRunning = false;
|
|
_streamStatus = status;
|
|
}
|
|
|
|
void DT9837A::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
|
|
DEBUGTRACE_ENTER;
|
|
if (isRunning()) {
|
|
throw rte("DAQ is already running");
|
|
}
|
|
if (neninchannels() > 0) {
|
|
if (!inCallback)
|
|
throw rte("DAQ requires a callback for input data");
|
|
}
|
|
if (nenoutchannels() > 0) {
|
|
if (!outCallback)
|
|
throw rte("DAQ requires a callback for output data");
|
|
}
|
|
assert(neninchannels() + nenoutchannels() > 0);
|
|
_thread = std::thread(&DT9837A::threadFcn, this, inCallback, outCallback);
|
|
|
|
}
|
|
|
|
void DT9837A::threadFcn(InDaqCallback inCallback, OutDaqCallback outCallback) {
|
|
|
|
DEBUGTRACE_ENTER;
|
|
|
|
try {
|
|
|
|
std::unique_ptr<OutBufHandler> obh;
|
|
std::unique_ptr<InBufHandler> ibh;
|
|
|
|
StreamStatus status = _streamStatus;
|
|
status.isRunning = true;
|
|
_streamStatus = status;
|
|
|
|
if (nenoutchannels() > 0) {
|
|
assert(outCallback);
|
|
obh = std::make_unique<OutBufHandler>(*this, outCallback);
|
|
}
|
|
if (neninchannels() > 0) {
|
|
assert(inCallback);
|
|
ibh = std::make_unique<InBufHandler>(*this, inCallback);
|
|
}
|
|
if (obh)
|
|
obh->start();
|
|
if (ibh)
|
|
ibh->start();
|
|
|
|
const double sleeptime_s =
|
|
static_cast<double>(_nFramesPerBlock) / (16 * samplerate());
|
|
const us sleeptime_us = static_cast<us>(sleeptime_s * 1e6);
|
|
|
|
while (!_stopThread) {
|
|
if (ibh) {
|
|
if (!(*ibh)()) {
|
|
_stopThread = true;
|
|
break;
|
|
}
|
|
}
|
|
if (obh) {
|
|
if (!(*obh)()) {
|
|
_stopThread = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(sleeptime_us));
|
|
}
|
|
|
|
/// Update stream status that we are not running anymore
|
|
status.isRunning = false;
|
|
_streamStatus = status;
|
|
_stopThread = false;
|
|
|
|
} catch (StreamException &e) {
|
|
|
|
StreamStatus status = _streamStatus;
|
|
// Copy over error type
|
|
status.errorType = e.e;
|
|
_streamStatus = status;
|
|
|
|
cerr << "\n******************\n";
|
|
cerr << "Catched error in UlDAQ thread: " << e.what() << endl;
|
|
cerr << "\n******************\n";
|
|
}
|
|
}
|
|
|
|
void DT9837A::sanityChecks() const {
|
|
// Some sanity checks
|
|
if (inchannel_config.size() != 4) {
|
|
throw rte("Invalid length of enabled inChannels vector");
|
|
}
|
|
|
|
if (outchannel_config.size() != 1) {
|
|
throw rte("Invalid length of enabled outChannels vector");
|
|
}
|
|
|
|
if (_nFramesPerBlock < 24 || _nFramesPerBlock > 8192) {
|
|
throw rte("Unsensible number of samples per block chosen");
|
|
}
|
|
|
|
if (samplerate() < ULDAQ_SAMPLERATES.at(0) ||
|
|
samplerate() > ULDAQ_SAMPLERATES.at(ULDAQ_SAMPLERATES.size() - 1)) {
|
|
throw rte("Invalid sample rate");
|
|
}
|
|
}
|
|
|
|
#endif // LASP_HAS_ULDAQ
|