From 29662c82e3719cbe6cc127a45f82cfa2ede26f4f Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - Redu-Sone B.V., ASCEE V.O.F" Date: Sat, 10 Oct 2020 18:28:43 +0200 Subject: [PATCH] Working C++ multiple API sit --- lasp/device/CMakeLists.txt | 8 + lasp/device/__init__.py | 4 +- lasp/device/lasp_common_decls.pxd | 33 +- lasp/device/lasp_cppdaq.cpp | 130 ++- lasp/device/lasp_cppdaq.h | 216 ++-- lasp/device/lasp_cppuldaq.cpp | 966 ++++++++---------- lasp/device/lasp_cppuldaq.h | 8 - lasp/device/lasp_daq.pyx | 4 +- lasp/device/lasp_daqconfig.pxd | 0 .../{lasp_daqconfig.py => lasp_daqconfig.pyx} | 0 .../{lasp_avtype.py => lasp_device_common.py} | 0 lasp/device/lasp_deviceinfo.pyx | 9 +- lasp/lasp_avstream.py | 5 +- test/test_uldaq.cpp | 136 ++- 14 files changed, 784 insertions(+), 735 deletions(-) create mode 100644 lasp/device/lasp_daqconfig.pxd rename lasp/device/{lasp_daqconfig.py => lasp_daqconfig.pyx} (100%) rename lasp/device/{lasp_avtype.py => lasp_device_common.py} (100%) diff --git a/lasp/device/CMakeLists.txt b/lasp/device/CMakeLists.txt index 78972d0..b7d7dc5 100644 --- a/lasp/device/CMakeLists.txt +++ b/lasp/device/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(cpp_daq lasp_cppdaq.cpp lasp_cppuldaq.cpp ) set_source_files_properties(lasp_daq.pyx PROPERTIES CYTHON_IS_CXX TRUE) set_source_files_properties(lasp_deviceinfo.pyx PROPERTIES CYTHON_IS_CXX TRUE) +set_source_files_properties(lasp_daqconfig.pyx PROPERTIES CYTHON_IS_CXX TRUE) set_source_files_properties(lasp_daq.cxx PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}") @@ -12,12 +13,19 @@ set_source_files_properties(lasp_daq.cxx PROPERTIES COMPILE_FLAGS set_source_files_properties(lasp_deviceinfo.cxx PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}") +set_source_files_properties(lasp_daqconfig.cxx PROPERTIES COMPILE_FLAGS + "${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}") + cython_add_module(lasp_daq lasp_daq.pyx) cython_add_module(lasp_deviceinfo lasp_deviceinfo.pyx) +cython_add_module(lasp_daqconfig lasp_daqconfig.pyx) target_link_libraries(lasp_daq cpp_daq uldaq rtaudio pthread) target_link_libraries(lasp_deviceinfo cpp_daq uldaq rtaudio pthread) +target_link_libraries(lasp_daqconfig cpp_daq uldaq rtaudio pthread) if(win32) target_link_libraries(lasp_daq python37) +target_link_libraries(lasp_deviceinfo python37) +target_link_libraries(lasp_daqconfig python37) endif(win32) diff --git a/lasp/device/__init__.py b/lasp/device/__init__.py index 8c7e9b2..f6c878b 100644 --- a/lasp/device/__init__.py +++ b/lasp/device/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -from .lasp_daqconfig import * -from .lasp_avtype import * +from .lasp_device_common import * from .lasp_daq import * from .lasp_deviceinfo import * +from .lasp_daqconfig import * diff --git a/lasp/device/lasp_common_decls.pxd b/lasp/device/lasp_common_decls.pxd index 69433c6..2c06fa2 100644 --- a/lasp/device/lasp_common_decls.pxd +++ b/lasp/device/lasp_common_decls.pxd @@ -39,16 +39,10 @@ cdef extern from "lasp_pyarray.h": ctypedef unsigned us ctypedef vector[bool] boolvec +ctypedef vector[double] dvec +ctypedef vector[us] usvec cdef extern from "lasp_cppdaq.h" nogil: - cdef cppclass cppDaq "Daq": - void start(SafeQueue[void*] *inQueue, - SafeQueue[void*] *outQueue) except + - void stop() - double samplerate() - us neninchannels() - us nenoutchannels() - DataType getDataType() cdef cppclass DaqApi: string apiname @@ -63,21 +57,27 @@ cdef extern from "lasp_cppdaq.h" nogil: cdef cppclass cppDeviceInfo "DeviceInfo": DaqApi api - string name + string device_name unsigned devindex vector[DataType] availableDataTypes vector[double] availableSampleRates + vector[us] availableFramesPerBlock + int prefSampleRateIndex + int prefInputRangeIndex + int prefFramesPerBlockIndex unsigned ninchannels unsigned noutchannels bool hasInputIEPE bool hasInputACCouplingSwitch bool hasInputTrigger - vector[double] inputRanges + vector[double] availableInputRanges cdef cppclass DaqConfiguration: boolvec eninchannels boolvec enoutchannels + vector[string] channel_names + vector[double] channel_sensitivities unsigned sampleRateIndex DataType datatype bool monitorOutput @@ -85,12 +85,19 @@ cdef extern from "lasp_cppdaq.h" nogil: boolvec inputIEPEEnabled; boolvec inputACCouplingMode; - boolvec inputHighRange; + usvec inputRangeIndices; + + cdef cppclass cppDaq "Daq": + void start(SafeQueue[void*] *inQueue, + SafeQueue[void*] *outQueue) except + + void stop() + double samplerate() + us neninchannels() + us nenoutchannels() + DataType getDataType() - cdef cppclass DaqDevices: @staticmethod cppDaq* createDaqDevice(cppDeviceInfo&, DaqConfiguration&) @staticmethod vector[cppDeviceInfo] getDeviceInfo() - diff --git a/lasp/device/lasp_cppdaq.cpp b/lasp/device/lasp_cppdaq.cpp index 5017aa1..796effd 100644 --- a/lasp/device/lasp_cppdaq.cpp +++ b/lasp/device/lasp_cppdaq.cpp @@ -1,6 +1,9 @@ #include "lasp_cppdaq.h" #include "lasp_cppuldaq.h" +#include +#include #include +#include using std::cout; using std::endl; @@ -11,7 +14,6 @@ void fillUlDaqDeviceInfo(vector &devinfolist) { UlError err; unsigned int numdevs = MAX_DEV_COUNT_PER_API; - unsigned deviceno; DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API]; DaqDeviceDescriptor descriptor; @@ -43,13 +45,11 @@ void fillUlDaqDeviceInfo(vector &devinfolist) { name += string(descriptor.productName) + " "; name += string(descriptor.uniqueId); - devinfo.name = name; + devinfo.device_name = name; - devinfo.devindex = i; + devinfo.api_specific_devindex = i; devinfo.availableDataTypes.push_back(dtype_fl64); - - devinfo.ninchannels = 4; - devinfo.noutchannels = 1; + devinfo.prefDataTypeIndex = 0; devinfo.availableSampleRates.push_back(10000); devinfo.availableSampleRates.push_back(12000); @@ -60,18 +60,32 @@ void fillUlDaqDeviceInfo(vector &devinfolist) { devinfo.availableSampleRates.push_back(51000); devinfo.prefSampleRateIndex = 5; + + devinfo.availableFramesPerBlock.push_back(512); + devinfo.availableFramesPerBlock.push_back(1024); + devinfo.availableFramesPerBlock.push_back(2048); + devinfo.availableFramesPerBlock.push_back(4096); + devinfo.availableFramesPerBlock.push_back(8192); + devinfo.prefFramesPerBlockIndex = 1; + + devinfo.availableInputRanges = {1.0, 10.0}; + devinfo.prefInputRangeIndex = 0; + + devinfo.ninchannels = 4; + devinfo.noutchannels = 1; devinfo.hasInputIEPE = true; devinfo.hasInputACCouplingSwitch = true; devinfo.hasInputTrigger = true; + // Finally, this devinfo is pushed back in list devinfolist.push_back(devinfo); } } } #endif -vector DaqDevices::getDeviceInfo() { +vector Daq::getDeviceInfo() { vector devs; #ifdef HAS_ULDAQ_LINUX_API fillUlDaqDeviceInfo(devs); @@ -89,8 +103,57 @@ vector DaqDevices::getDeviceInfo() { return devs; } -Daq *DaqDevices::createDevice(const DeviceInfo &devinfo, - const DaqConfiguration &config) { +DaqConfiguration::DaqConfiguration(const DeviceInfo &device) { + + api = device.api; + device_name = device.device_name; + + eninchannels.resize(device.ninchannels, false); + enoutchannels.resize(device.noutchannels, false); + + inchannel_sensitivities.resize(device.ninchannels, 1.0); + for (us i = 0; i < eninchannels.size(); i++) { + std::strstream chname; + chname << "Unnamed input channel " << i; + inchannel_names.push_back(chname.str()); + } + + for (us i = 0; i < enoutchannels.size(); i++) { + std::strstream chname; + chname << "Unnamed output channel " << i; + outchannel_names.push_back(chname.str()); + } + sampleRateIndex = device.prefSampleRateIndex; + dataTypeIndex = device.prefDataTypeIndex; + framesPerBlockIndex = device.prefFramesPerBlockIndex; + + monitorOutput = false; + + inputIEPEEnabled.resize(device.ninchannels, false); + inputACCouplingMode.resize(device.ninchannels, false); + inputRangeIndices.resize(device.ninchannels, device.prefInputRangeIndex); + + assert(match(device)); +} + +bool DaqConfiguration::match(const DeviceInfo& dev) const { + return (dev.device_name == device_name && dev.api == api); +} + +Daq *Daq::createDevice(const DaqConfiguration &config, + const vector &devinfos) { + + bool match; + DeviceInfo devinfo; + for (auto cur_devinfo : devinfos) { + if ((match = config.match(cur_devinfo))) { + devinfo = cur_devinfo; + break; + } + } + if (!match) { + return NULL; + } // Some basic sanity checks if ((devinfo.ninchannels != config.eninchannels.size())) { @@ -107,3 +170,52 @@ Daq *DaqDevices::createDevice(const DeviceInfo &devinfo, devinfo.api.apiname); } } + + +Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config) + : DaqConfiguration(config), DeviceInfo(devinfo) { + + if (monitorOutput && !(nenoutchannels() > 0)) { + throw runtime_error( + "Output monitoring only possible when output is enabled"); + } + // Some sanity checks + if (eninchannels.size() != 4) { + throw runtime_error("Invalid length of enabled inChannels vector"); + } + + if (enoutchannels.size() != 1) { + throw runtime_error("Invalid length of enabled outChannels vector"); + } +} + + +double Daq::samplerate() const { + assert(sampleRateIndex < availableSampleRates.size()); + return availableSampleRates[sampleRateIndex]; +} + + +DataType Daq::dataType() const { + + assert((us)dataTypeIndex < availableDataTypes.size()); + return availableDataTypes[dataTypeIndex]; +} +double Daq::inputRangeForChannel(us ch) const { + if (!(ch < ninchannels)) { + throw runtime_error("Invalid channel number"); + } + assert(inputRangeIndices.size() == eninchannels.size()); + return availableInputRanges[inputRangeIndices[ch]]; +} +us Daq::neninchannels() const { + mutexlock lock(mutex); + us inch = std::count(eninchannels.begin(), eninchannels.end(), true); + if (monitorOutput) + inch++; + return inch; +} +us Daq::nenoutchannels() const { + mutexlock lock(mutex); + return std::count(enoutchannels.begin(), enoutchannels.end(), true); +} diff --git a/lasp/device/lasp_cppdaq.h b/lasp/device/lasp_cppdaq.h index fceefa3..36349ad 100644 --- a/lasp/device/lasp_cppdaq.h +++ b/lasp/device/lasp_cppdaq.h @@ -1,6 +1,5 @@ #ifndef LASP_CPPDAQ_H #define LASP_CPPDAQ_H -#include #ifdef HAS_RTAUDIO #include @@ -13,25 +12,29 @@ #include "lasp_cppqueue.h" #include "string" #include "vector" +#include +using std::cerr; +using std::cout; +using std::endl; +using std::runtime_error; using std::string; using std::vector; -using std::runtime_error; typedef unsigned int us; typedef vector boolvec; +typedef vector dvec; +typedef vector usvec; +typedef std::lock_guard mutexlock; class DataType { - public: - string name; - unsigned sw; - bool is_floating; + public: + string name; + unsigned sw; + bool is_floating; - DataType(const char *name, unsigned sw, bool is_floating) - : name(name), sw(sw), is_floating(is_floating) {} - DataType(): - name("invalid data type"), - sw(0), - is_floating(false) {} + DataType(const char *name, unsigned sw, bool is_floating) + : name(name), sw(sw), is_floating(is_floating) {} + DataType() : name("invalid data type"), sw(0), is_floating(false) {} }; const DataType dtype_invalid; @@ -41,26 +44,26 @@ const DataType dtype_int16("16-bits integers", 2, false); const DataType dtype_int32("32-bits integers", 4, false); const std::vector dataTypes = { - dtype_int8, - dtype_int16, - dtype_int32, - dtype_fl64, + dtype_int8, + dtype_int16, + dtype_int32, + dtype_fl64, }; class DaqApi { - public: - string apiname = ""; - unsigned apicode = 0; - unsigned api_specific_subcode = 0; + public: + string apiname = ""; + unsigned apicode = 0; + 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); - } + 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); + } }; #ifdef HAS_ULDAQ_LINUX_API @@ -72,101 +75,134 @@ const DaqApi rtaudioalsaapi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA); const vector compiledApis = { #ifdef HAS_ULDAQ_LINUX_API - uldaqapi, + uldaqapi, #endif #ifdef HAS_RTAUDIO_ALSA_API - rtaudioalsaapi, + rtaudioalsaapi, #endif #ifdef HAS_RTAUDIO_PULSEAUDIO_API - DaqApi("RtAudio Linux Pulseaudio", 2, RtAudio::Api::LINUX_PULSE), + DaqApi("RtAudio Linux Pulseaudio", 2, RtAudio::Api::LINUX_PULSE), #endif #ifdef HAS_RTAUDIO_WIN_WASAPI_API - DaqApi("RtAudio Windows Wasapi", 3, RtAudio::Api::WINDOWS_WASAPI), + DaqApi("RtAudio Windows Wasapi", 3, RtAudio::Api::WINDOWS_WASAPI), #endif }; // Structure containing device info parameters class DeviceInfo { - public: - DaqApi api; - string name = ""; - unsigned devindex; + public: + DaqApi api; + string device_name = ""; - vector availableDataTypes; - vector availableSampleRates; - vector availableFramesPerBlock; + int api_specific_devindex = -1; - int prefSampleRateIndex = -1; - unsigned ninchannels = 0; - unsigned noutchannels = 0; + vector availableDataTypes; + int prefDataTypeIndex = 0; - bool hasInputIEPE = false; - bool hasInputACCouplingSwitch = false; - bool hasInputTrigger = false; - vector inputRanges; + vector availableSampleRates; + int prefSampleRateIndex = -1; - /* DeviceInfo(): */ - /* datatype(dtype_invalid) { } */ + vector availableFramesPerBlock; + unsigned prefFramesPerBlockIndex = 0; - double prefSampleRate() const { - if ((prefSampleRateIndex < availableSampleRates.size()) && - (prefSampleRateIndex >= 0)) { - return availableSampleRates[prefSampleRateIndex]; - } else { - throw std::runtime_error("No prefered sample rate available"); - } - } - operator string() const { - std::stringstream str; - str << api.apiname + " " << devindex << - " number of input channels: " << ninchannels << - " number of output channels: " << noutchannels; - return str.str(); + dvec availableInputRanges; + int prefInputRangeIndex = 0; - } + unsigned ninchannels = 0; + unsigned noutchannels = 0; + + bool hasInputIEPE = false; + bool hasInputACCouplingSwitch = false; + bool hasInputTrigger = false; + + /* DeviceInfo(): */ + /* datatype(dtype_invalid) { } */ + + double prefSampleRate() const { + if (((us) prefSampleRateIndex < availableSampleRates.size()) && + (prefSampleRateIndex >= 0)) { + return availableSampleRates[prefSampleRateIndex]; + } else { + throw std::runtime_error("No prefered sample rate available"); + } + } + + operator string() const { + std::stringstream str; + str << api.apiname + " " << api_specific_devindex + << " number of input channels: " << ninchannels + << " number of output channels: " << noutchannels; + return str.str(); + } }; // Device configuration parameters class DaqConfiguration { - public: - boolvec eninchannels; // Enabled input channels - boolvec enoutchannels; // Enabled output channels + public: + DaqApi api; + string device_name; - unsigned sampleRateIndex; // Index in list of sample rates - DataType - datatype = dtype_invalid; // Required datatype for output, should be present in the list - bool monitorOutput; // Whether the first output channel should be replicated - // to the input as the first channel + boolvec eninchannels; // Enabled input channels + boolvec enoutchannels; // Enabled output channels - unsigned nFramesPerBlock; + vector inchannel_sensitivities; + vector inchannel_names; - boolvec inputIEPEEnabled; - boolvec inputACCouplingMode; - boolvec inputHighRange; + // This is not necessary at the moment + /* vector outchannel_sensitivities; */ + vector outchannel_names; + us sampleRateIndex = 0; // Index in list of sample rates + + us dataTypeIndex = 0; // Required datatype for output, should be + // present in the list + + us framesPerBlockIndex = 0; + + bool monitorOutput = false; // Whether the first output channel should be replicated + // to the input as the first channel + + + boolvec inputIEPEEnabled; + boolvec inputACCouplingMode; + + usvec inputRangeIndices; + + // Create a default configuration, with all channels disabled on both + // input and output, and default channel names + DaqConfiguration(const DeviceInfo &device); + + bool match(const DeviceInfo &devinfo) const; }; class Daq; -class DaqDevices { - public: - static vector getDeviceInfo(); - static Daq *createDevice(const DeviceInfo &, const DaqConfiguration &config); -}; +class Daq : public DaqConfiguration,public DeviceInfo { -class Daq { + mutable std::mutex mutex; - public: - virtual void start(SafeQueue *inqueue, - SafeQueue *outqueue) = 0; + public: + static vector getDeviceInfo(); - virtual void stop() = 0; - virtual double samplerate() const = 0; - virtual DataType getDataType() const = 0; + static Daq *createDevice(const DaqConfiguration &config, + const std::vector &devinfos); - virtual ~Daq(){}; - virtual us neninchannels() const = 0; - virtual us nenoutchannels() const = 0; + Daq(const DeviceInfo &devinfo, const DaqConfiguration &config); + + virtual void start(SafeQueue *inqueue, + SafeQueue *outqueue) = 0; + + virtual void stop() = 0; + + virtual bool isRunning() const = 0; + + virtual ~Daq(){}; + us neninchannels() const; + us nenoutchannels() const; + + double samplerate() const; + double inputRangeForChannel(us ch) const; + DataType dataType() const; }; #endif // LASP_CPPDAQ_H diff --git a/lasp/device/lasp_cppuldaq.cpp b/lasp/device/lasp_cppuldaq.cpp index 97387e6..aae8240 100644 --- a/lasp/device/lasp_cppuldaq.cpp +++ b/lasp/device/lasp_cppuldaq.cpp @@ -1,567 +1,178 @@ #include "lasp_cppuldaq.h" -#include #include #include #include -#include #include -#include -#include #include #include #include -using std::cerr; -using std::cout; -using std::endl; -using std::runtime_error; -typedef std::lock_guard mutexlock; -/* using std::this_thread; */ +using std::atomic; +/* using std::this_thread; */ const us MAX_DEF_COUNT = 100; const us UL_ERR_MSG_LEN = 512; -class DT9837A : public Daq { - us samplesPerBlock; - double _samplerate; - boolvec inChannels; - boolvec high_range; - boolvec outChannels; - bool monitorOutput; - atomic stopThread; - DaqDeviceHandle handle = 0; - - std::thread *thread = NULL; - mutable std::mutex mutex; - SafeQueue *inqueue = NULL; - SafeQueue *outqueue = NULL; - - double *inbuffer = NULL; - double *outbuffer = NULL; - - public: - double samplerate() const { return this->_samplerate; } - - DT9837A(us samplesPerBlock, const boolvec &inChannels, - const boolvec &outChannels, double samplerate, bool monitorOutput, - us device_no = 0); - DT9837A(const DT9837A &) = delete; - - ~DT9837A(); - - void setIEPEEnabled(const boolvec &config); - - // Coupling_ac: true, means AC coupling, false means DC coupling - void setACCouplingMode(const boolvec &coupling_ac); - - void setInputRange(const boolvec &high_range); - - us neninchannels() const final; - us nenoutchannels() const final; - - bool isRunning() const { return bool(thread); } - - virtual void start(SafeQueue *inqueue, - SafeQueue *outqueue) final; - - virtual void stop() final; - DataType getDataType() const final { return dtype_fl64; } - - friend void threadfcn(DT9837A *); -}; - inline void showErr(UlError err) { - if (err != ERR_NO_ERROR) { - char errmsg[UL_ERR_MSG_LEN]; - ulGetErrMsg(err, errmsg); - std::cerr << "UlError: " << errmsg << std::endl; - } + if (err != ERR_NO_ERROR) { + char errmsg[UL_ERR_MSG_LEN]; + ulGetErrMsg(err, errmsg); + std::cerr << "UlError: " << errmsg << std::endl; + } } -DT9837A::DT9837A(us samplesPerBlock, const boolvec &inChannels, - const boolvec &outChannels, double samplerate, - bool monitorOutput, us deviceno) - : samplesPerBlock(samplesPerBlock), _samplerate(samplerate), - inChannels(inChannels), outChannels(outChannels), - monitorOutput(monitorOutput) { - if (monitorOutput && !(nenoutchannels() > 0)) { - throw runtime_error( - "Output monitoring only possible when output is enabled"); - } +class DT9837A; +void threadfcn(DT9837A *td); - stopThread = false; - high_range = boolvec({false, false, false, false}); +class DT9837A : public Daq { - if (samplesPerBlock < 24 || samplesPerBlock > 8192) { - throw runtime_error("Unsensible number of samples per block chosen"); - } + atomic stopThread; + DaqDeviceHandle handle = 0; - // Some sanity checks - if (inChannels.size() != 4) { - throw runtime_error("Invalid length of enabled inChannels vector"); - } + std::thread *thread = NULL; + SafeQueue *inqueue = NULL; + SafeQueue *outqueue = NULL; - // Some sanity checks - if (outChannels.size() != 1) { - throw runtime_error("Invalid length of enabled outChannels vector"); - } - if (samplerate < 10000 || samplerate > 51000) { - throw runtime_error("Invalid sample rate"); - } + double *inbuffer = NULL; + double *outbuffer = NULL; - DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]; - DaqDeviceDescriptor descriptor; - DaqDeviceInterface interfaceType = ANY_IFC; + us nFramesPerBlock; - UlError err; +public: + DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config) + : Daq(devinfo, config) { - us numdevs = MAX_DEF_COUNT; - err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, &numdevs); - if (err != ERR_NO_ERROR) { - throw runtime_error("Device inventarization failed"); - } + stopThread = false; - if (deviceno >= numdevs) { - throw runtime_error("Device number {deviceno} too high {err}. This could " - "happen when the device is currently not connected"); - } + nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex]; - descriptor = devdescriptors[deviceno]; - // get a handle to the DAQ device associated with the first descriptor - handle = ulCreateDaqDevice(descriptor); - - if (handle == 0) { - throw runtime_error("Unable to create a handle to the specified DAQ " - "device. Is the device currently in use?"); - } - - err = ulConnectDaqDevice(handle); - if (err != ERR_NO_ERROR) { - showErr(err); - ulReleaseDaqDevice(handle); - handle = 0; - throw runtime_error("Unable to connect to device: {err}"); - } - - 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 runtime_error("Fatal: could normalize channel sensitivity"); - } - } + if (nFramesPerBlock < 24 || nFramesPerBlock > 8192) { + throw runtime_error("Unsensible number of samples per block chosen"); } -DT9837A::~DT9837A() { + if (samplerate() < 10000 || samplerate() > 51000) { + throw runtime_error("Invalid sample rate"); + } + + DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]; + DaqDeviceDescriptor descriptor; + DaqDeviceInterface interfaceType = ANY_IFC; + + UlError err; + + us numdevs = MAX_DEF_COUNT; + err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, &numdevs); + if (err != ERR_NO_ERROR) { + throw runtime_error("Device inventarization failed"); + } + + if ((us) api_specific_devindex >= numdevs) { + throw runtime_error("Device number {deviceno} too high {err}. This could " + "happen when the device is currently not connected"); + } + + descriptor = devdescriptors[api_specific_devindex]; + + // get a handle to the DAQ device associated with the first descriptor + handle = ulCreateDaqDevice(descriptor); + + if (handle == 0) { + throw runtime_error("Unable to create a handle to the specified DAQ " + "device. Is the device currently in use?"); + } + + err = ulConnectDaqDevice(handle); + if (err != ERR_NO_ERROR) { + showErr(err); + ulReleaseDaqDevice(handle); + handle = 0; + throw runtime_error("Unable to connect to device: {err}"); + } + + 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 runtime_error("Fatal: could normalize channel sensitivity"); + } + + CouplingMode cm = inputACCouplingMode[ch] ? CM_AC : CM_DC; + err = ulAISetConfig(handle, AI_CFG_CHAN_COUPLING_MODE, ch, cm); + if (err != ERR_NO_ERROR) { + showErr(err); + throw runtime_error("Fatal: could not set AC/DC coupling mode"); + } + + IepeMode iepe = inputIEPEEnabled[ch] ? IEPE_ENABLED : IEPE_DISABLED; + err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe); + if (err != ERR_NO_ERROR) { + showErr(err); + throw runtime_error("Fatal: could not set IEPE mode"); + } + } + } + + DT9837A(const DT9837A &) = delete; + + ~DT9837A() { UlError err; if (isRunning()) { - stop(); + stop(); } if (handle) { - err = ulDisconnectDaqDevice(handle); - showErr(err); - err = ulReleaseDaqDevice(handle); - showErr(err); + err = ulDisconnectDaqDevice(handle); + showErr(err); + err = ulReleaseDaqDevice(handle); + showErr(err); } -} + } -void DT9837A::setIEPEEnabled(const boolvec &config) { + bool isRunning() const { return bool(thread); } + + void start(SafeQueue *inqueue, SafeQueue *outqueue) { if (isRunning()) { - throw runtime_error("Cannot change config while sampling is running"); - } - - // Some sanity checks - if (config.size() != 4) { - throw runtime_error("Invalid length of enabled IEPE config vector"); - } - - IepeMode iepe; - UlError err; - - for (us i = 0; i < config.size(); i++) { - - iepe = config[i] ? IEPE_ENABLED : IEPE_DISABLED; - err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, i, iepe); - if (err != ERR_NO_ERROR) { - showErr(err); - throw runtime_error("Fatal: could not set IEPE mode"); - } - } -} - -void DT9837A::setACCouplingMode(const boolvec &coupling) { - if (isRunning()) { - throw runtime_error("Cannot change config while sampling is running"); - } - - // Some sanity checks - if (coupling.size() != 4) { - throw runtime_error("Invalid length of enabled AC coupling mode vector"); - } - - CouplingMode cm; - UlError err; - - for (us i = 0; i < coupling.size(); i++) { - cm = coupling[i] ? CM_AC : CM_DC; - - err = ulAISetConfig(handle, AI_CFG_CHAN_COUPLING_MODE, i, cm); - if (err != ERR_NO_ERROR) { - showErr(err); - throw runtime_error("Fatal: could not set IEPE mode"); - } - } -} - -void DT9837A::setInputRange(const boolvec &high_range) { - if (isRunning()) { - throw runtime_error("Cannot change config while sampling is running"); - } - - // Some sanity checks - if (high_range.size() != 4) { - throw runtime_error("Invalid length of enabled high range vector"); - } - - this->high_range = high_range; -} - -void threadfcn(DT9837A *td) { - - std::cerr << "Starting DAQ Thread fcn" << endl; - - const us nenoutchannels = td->nenoutchannels(); - us neninchannels = td->neninchannels(); - const us samplesPerBlock = td->samplesPerBlock; - - const bool hasinput = neninchannels > 0; - const bool hasoutput = nenoutchannels > 0; - - bool monitorOutput = td->monitorOutput; - - double *inbuffer = td->inbuffer; - double *outbuffer = td->outbuffer; - - SafeQueue *inqueue = td->inqueue; - SafeQueue *outqueue = td->outqueue; - - ScanStatus inscanstat; - ScanStatus outscanstat; - TransferStatus inxstat, outxstat; - - double samplerate = td->samplerate(); - - const double sleeptime = ((double)samplesPerBlock) / (4 * samplerate); - const us sleeptime_us = (us)(sleeptime * 1e6); - /* cerr << "Sleep time in loop: " << sleeptime_us << "us." << endl; */ - if (sleeptime_us < 10) { - cerr << "ERROR: Too small buffer size (samplesPerBlock) chosen!" << endl; - return; - } - - const us buffer_mid_idx_in = neninchannels * samplesPerBlock; - const us buffer_mid_idx_out = nenoutchannels * samplesPerBlock; - - DaqDeviceHandle handle = td->handle; - assert(handle); - - DaqInScanFlag inscanflags = DAQINSCAN_FF_DEFAULT; - AOutScanFlag outscanflags = AOUTSCAN_FF_DEFAULT; - ScanOption scanoptions = SO_CONTINUOUS; - UlError err = ERR_NO_ERROR; - - DaqInChanDescriptor *indesc = NULL; - - bool topinenqueued = true; - // Start with true here, to not copy here the first time - bool botinenqueued = true; - - bool topoutenqueued = true; - bool botoutenqueued = true; - - size_t inTotalCount = 0; - size_t outTotalCount = 0; - - // initialize output, if any - if (hasoutput) { - assert(nenoutchannels == 1); - assert(outqueue); - - // Initialize the buffer with zeros, before pushing any data. - for (us sample = 0; sample < 2 * samplesPerBlock; sample++) { - outbuffer[sample] = 0; - } - - cerr << "Starting output DAC" << endl; - err = ulAOutScan(handle, 0, 0, BIP10VOLTS, - /* BIP60VOLTS, */ - 2 * td->samplesPerBlock, // Watch the 2 here! - &samplerate, scanoptions, outscanflags, outbuffer); - - if (err != ERR_NO_ERROR) { - showErr(err); - goto exit; - } - } - - // Initialize input, if any - if (hasinput) { - - indesc = new DaqInChanDescriptor[neninchannels]; - us j = 0; - for (us chin = 0; chin < 4; chin++) { - if (td->inChannels[chin] == true) { - indesc[j].type = DAQI_ANALOG_SE; - indesc[j].channel = chin; - indesc[j].range = td->high_range[chin] ? BIP10VOLTS : BIP1VOLTS; - j++; - } - } - // Overwrite last channel - if (monitorOutput) { - indesc[j].type = DAQI_DAC; - indesc[j].channel = 0; - indesc[j].range = BIP10VOLTS; - j++; - } - assert(j == neninchannels); - - cerr << "Starting input ADC" << endl; - err = ulDaqInScan(handle, indesc, neninchannels, - 2 * td->samplesPerBlock, // Watch the 2 here! - &samplerate, scanoptions, inscanflags, inbuffer); - if (err != ERR_NO_ERROR) { - showErr(err); - goto exit; - } - } - - // Runs scan status on output, to catch up with position - if (hasoutput) { - err = ulAOutScanStatus(handle, &outscanstat, &outxstat); - if (err != ERR_NO_ERROR) { - showErr(err); - goto exit; - } - outTotalCount = outxstat.currentScanCount; - assert(outscanstat == SS_RUNNING); - } - - /* std::cerr << "Entering while loop" << endl; */ - /* std::cerr << "hasinput: " << hasinput << endl; */ - while (!td->stopThread && err == ERR_NO_ERROR) { - /* std::cerr << "While..." << endl; */ - if (hasoutput) { - err = ulAOutScanStatus(handle, &outscanstat, &outxstat); - if (err != ERR_NO_ERROR) { - showErr(err); - goto exit; - } - assert(outscanstat == SS_RUNNING); - - if (outxstat.currentScanCount > outTotalCount + 2 * samplesPerBlock) { - cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count=" - << outxstat.currentScanCount - << " while loop count = " << outTotalCount - << ", probably due to too small buffer size. *****" << endl; - } - outTotalCount = outxstat.currentScanCount; - - /* std::cerr << "Samples scanned: " << outxstat.currentTotalCount << endl; - */ - if (outxstat.currentIndex < buffer_mid_idx_out) { - topoutenqueued = false; - if (!botoutenqueued) { - /* cerr << "Copying output buffer to bottom" << endl; */ - double *bufcpy; - if (!outqueue->empty()) { - bufcpy = (double *)outqueue->dequeue(); - } else { - cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " - "QUEUE WITH ZEROS ***********" - << endl; - bufcpy = static_cast( - malloc(sizeof(double) * samplesPerBlock * nenoutchannels)); - for (us sample = 0; sample < samplesPerBlock; sample++) { - bufcpy[sample] = 0; - } - } - assert(nenoutchannels > 0); - for (us sample = 0; sample < samplesPerBlock; sample++) { - outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample]; - } - free(bufcpy); - botoutenqueued = true; - } - } else { - botoutenqueued = false; - if (!topoutenqueued) { - /* cerr << "Copying output buffer to top" << endl; */ - double *bufcpy; - if (!outqueue->empty()) { - bufcpy = (double *)outqueue->dequeue(); - } else { - cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " - "QUEUE WITH ZEROS ***********" - << endl; - bufcpy = static_cast( - malloc(sizeof(double) * samplesPerBlock * nenoutchannels)); - for (us sample = 0; sample < samplesPerBlock; sample++) { - bufcpy[sample] = 0; - } - } - assert(nenoutchannels > 0); - for (us sample = 0; sample < samplesPerBlock; sample++) { - outbuffer[sample] = bufcpy[sample]; - } - free(bufcpy); - topoutenqueued = true; - } - } - } - - if (hasinput) { - err = ulDaqInScanStatus(handle, &inscanstat, &inxstat); - if (err != ERR_NO_ERROR) { - showErr(err); - goto exit; - } - assert(inscanstat == SS_RUNNING); - if (inxstat.currentScanCount > inTotalCount + 2 * samplesPerBlock) { - cerr << "***** ERROR: Missing input sample blocks, count=" - << inxstat.currentScanCount - << ", probably due to too small buffer size. Exiting thread. *****" - << endl; - break; - } - inTotalCount = inxstat.currentScanCount; - - if (inxstat.currentIndex < buffer_mid_idx_in) { - topinenqueued = false; - if (!botinenqueued) { - /* cerr << "Copying in buffer bot" << endl; */ - double *bufcpy = static_cast( - malloc(sizeof(double) * samplesPerBlock * neninchannels)); - us monitoroffset = monitorOutput ? 1 : 0; - assert(neninchannels > 0); - for (us channel = 0; channel < (neninchannels - monitoroffset); - channel++) { - for (us sample = 0; sample < samplesPerBlock; sample++) { - bufcpy[(monitoroffset + channel) * samplesPerBlock + sample] = - inbuffer[buffer_mid_idx_in + sample * neninchannels + - channel]; - } - } - if (monitorOutput) { - // Monitor output goes to first channel, that is - // our convention - us channel = neninchannels - 1; - for (us sample = 0; sample < samplesPerBlock; sample++) { - bufcpy[sample] = inbuffer[buffer_mid_idx_in + - sample * neninchannels + channel]; - } - } - inqueue->enqueue((void *)bufcpy); - botinenqueued = true; - } - } else { - botinenqueued = false; - if (!topinenqueued) { - double *bufcpy = static_cast( - malloc(sizeof(double) * samplesPerBlock * neninchannels)); - us monitoroffset = monitorOutput ? 1 : 0; - assert(neninchannels > 0); - for (us channel = 0; channel < (neninchannels - monitoroffset); - channel++) { - for (us sample = 0; sample < samplesPerBlock; sample++) { - bufcpy[(monitoroffset + channel) * samplesPerBlock + sample] = - inbuffer[sample * neninchannels + channel]; - } - } - if (monitorOutput) { - // Monitor output goes to first channel, that is - // our convention - us channel = neninchannels - 1; - for (us sample = 0; sample < samplesPerBlock; sample++) { - bufcpy[sample] = inbuffer[sample * neninchannels + channel]; - } - } - - /* cerr << "Copying in buffer top" << endl; */ - inqueue->enqueue((void *)bufcpy); - topinenqueued = true; - } - } - } - - std::this_thread::sleep_for(std::chrono::microseconds(sleeptime_us)); - - } // End of while loop - /* std::cerr << "Exit of while loop" << endl; */ - -exit: - - if (hasoutput) { - ulAOutScanStop(handle); - if (err != ERR_NO_ERROR) { - showErr(err); - } - } - - if (hasinput) { - ulDaqInScanStop(handle); - if (err != ERR_NO_ERROR) { - showErr(err); - } - } - - if (indesc) - delete indesc; - std::cerr << "Exit of DAQ thread fcn" << endl; -} - -void DT9837A::start(SafeQueue *inqueue, SafeQueue *outqueue) { - if (isRunning()) { - throw runtime_error("Thread is already running"); + throw runtime_error("Thread is already running"); } bool hasinput = neninchannels() > 0; bool hasoutput = nenoutchannels() > 0; if (neninchannels() > 0 && !inqueue) { - throw runtime_error("Inqueue not given, while input is enabled"); + throw runtime_error("Inqueue not given, while input is enabled"); } if (nenoutchannels() > 0 && !outqueue) { - throw runtime_error("outqueue not given, while output is enabled"); + throw runtime_error("outqueue not given, while output is enabled"); } if (hasinput) { - assert(!inbuffer); - inbuffer = - new double[neninchannels() * samplesPerBlock * 2]; // Watch the 2! + assert(!inbuffer); + inbuffer = + new double[neninchannels() * nFramesPerBlock * 2]; // Watch the 2! } if (hasoutput) { - assert(!outbuffer); - outbuffer = - new double[nenoutchannels() * samplesPerBlock * 2]; // Watch the 2! + assert(!outbuffer); + outbuffer = + new double[nenoutchannels() * nFramesPerBlock * 2]; // Watch the 2! } this->inqueue = inqueue; this->outqueue = outqueue; - /* std::cerr << "************************ WARNING: Forcing coupling mode to AC + /* std::cerr << "************************ WARNING: Forcing coupling mode + * to AC * **************************" << endl; */ /* boolvec couplingmode = {true, true, true, true}; */ /* setACCouplingMode(couplingmode); */ stopThread = false; thread = new std::thread(threadfcn, this); -} + } -void DT9837A::stop() { + void stop() { if (!isRunning()) { - throw runtime_error("No data acquisition running"); + throw runtime_error("No data acquisition running"); } assert(thread); @@ -573,45 +184,334 @@ void DT9837A::stop() { outqueue = NULL; inqueue = NULL; if (inbuffer) - delete inbuffer; + delete inbuffer; if (outbuffer) - delete outbuffer; + delete outbuffer; outbuffer = NULL; inbuffer = NULL; -} + } -us DT9837A::neninchannels() const { - mutexlock lock(mutex); - us inch = std::count(inChannels.begin(), inChannels.end(), true); - if (monitorOutput) - inch++; - return inch; -} + friend void threadfcn(DT9837A *); +}; -us DT9837A::nenoutchannels() const { - mutexlock lock(mutex); - return std::count(outChannels.begin(), outChannels.end(), true); -} +void threadfcn(DT9837A *td) { + std::cerr << "Starting DAQ Thread fcn" << endl; + + const us nenoutchannels = td->nenoutchannels(); + us neninchannels = td->neninchannels(); + const us nFramesPerBlock = td->nFramesPerBlock; + + const bool hasinput = neninchannels > 0; + const bool hasoutput = nenoutchannels > 0; + + bool monitorOutput = td->monitorOutput; + + double *inbuffer = td->inbuffer; + double *outbuffer = td->outbuffer; + + SafeQueue *inqueue = td->inqueue; + SafeQueue *outqueue = td->outqueue; + + ScanStatus inscanstat; + ScanStatus outscanstat; + TransferStatus inxstat, outxstat; + + double samplerate = td->samplerate(); + + const double sleeptime = ((double)nFramesPerBlock) / (4 * samplerate); + const us sleeptime_us = (us)(sleeptime * 1e6); + /* cerr << "Sleep time in loop: " << sleeptime_us << "us." << endl; */ + if (sleeptime_us < 10) { + cerr << "ERROR: Too small buffer size (nFramesPerBlock) chosen!" << endl; + return; + } + + const us buffer_mid_idx_in = neninchannels * nFramesPerBlock; + const us buffer_mid_idx_out = nenoutchannels * nFramesPerBlock; + + DaqDeviceHandle handle = td->handle; + assert(handle); + + DaqInScanFlag inscanflags = DAQINSCAN_FF_DEFAULT; + AOutScanFlag outscanflags = AOUTSCAN_FF_DEFAULT; + ScanOption scanoptions = SO_CONTINUOUS; + UlError err = ERR_NO_ERROR; + + DaqInChanDescriptor *indesc = NULL; + + bool topinenqueued = true; + // Start with true here, to not copy here the first time + bool botinenqueued = true; + + bool topoutenqueued = true; + bool botoutenqueued = true; + + size_t inTotalCount = 0; + size_t outTotalCount = 0; + + // initialize output, if any + if (hasoutput) { + assert(nenoutchannels == 1); + assert(outqueue); + + // Initialize the buffer with zeros, before pushing any data. + for (us sample = 0; sample < 2 * nFramesPerBlock; sample++) { + outbuffer[sample] = 0; + } + + cerr << "Starting output DAC" << endl; + err = ulAOutScan(handle, 0, 0, BIP10VOLTS, + /* BIP60VOLTS, */ + 2 * td->nFramesPerBlock, // Watch the 2 here! + &samplerate, scanoptions, outscanflags, outbuffer); + + if (err != ERR_NO_ERROR) { + showErr(err); + goto exit; + } + } + + // Initialize input, if any + if (hasinput) { + indesc = new DaqInChanDescriptor[neninchannels]; + us j = 0; + for (us chin = 0; chin < 4; chin++) { + if (td->eninchannels[chin] == true) { + indesc[j].type = DAQI_ANALOG_SE; + indesc[j].channel = chin; + + double rangeval = td->inputRangeForChannel(chin); + Range rangenum; + if (abs(rangeval - 1.0) < 1e-8) { + rangenum = BIP1VOLTS; + } else if (abs(rangeval - 10.0) < 1e-8) { + rangenum = BIP10VOLTS; + } else { + std::cerr << "Fatal: input range value is invalid" << endl; + goto exit; + } + indesc[j].range = rangenum; + j++; + } + } + // Overwrite last channel + if (monitorOutput) { + indesc[j].type = DAQI_DAC; + indesc[j].channel = 0; + indesc[j].range = BIP10VOLTS; + j++; + } + assert(j == neninchannels); + + cerr << "Starting input ADC" << endl; + err = ulDaqInScan(handle, indesc, neninchannels, + 2 * td->nFramesPerBlock, // Watch the 2 here! + &samplerate, scanoptions, inscanflags, inbuffer); + if (err != ERR_NO_ERROR) { + showErr(err); + goto exit; + } + } + + // Runs scan status on output, to catch up with position + if (hasoutput) { + err = ulAOutScanStatus(handle, &outscanstat, &outxstat); + if (err != ERR_NO_ERROR) { + showErr(err); + goto exit; + } + outTotalCount = outxstat.currentScanCount; + assert(outscanstat == SS_RUNNING); + } + + /* std::cerr << "Entering while loop" << endl; */ + /* std::cerr << "hasinput: " << hasinput << endl; */ + while (!td->stopThread && err == ERR_NO_ERROR) { + /* std::cerr << "While..." << endl; */ + if (hasoutput) { + err = ulAOutScanStatus(handle, &outscanstat, &outxstat); + if (err != ERR_NO_ERROR) { + showErr(err); + goto exit; + } + assert(outscanstat == SS_RUNNING); + + if (outxstat.currentScanCount > outTotalCount + 2 * nFramesPerBlock) { + cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count=" + << outxstat.currentScanCount + << " while loop count = " << outTotalCount + << ", probably due to too small buffer size. *****" << endl; + } + outTotalCount = outxstat.currentScanCount; + + /* std::cerr << "Samples scanned: " << outxstat.currentTotalCount << + * endl; + */ + if (outxstat.currentIndex < buffer_mid_idx_out) { + topoutenqueued = false; + if (!botoutenqueued) { + /* cerr << "Copying output buffer to bottom" << endl; */ + double *bufcpy; + if (!outqueue->empty()) { + bufcpy = (double *)outqueue->dequeue(); + } else { + cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " + "QUEUE WITH ZEROS ***********" + << endl; + bufcpy = static_cast( + malloc(sizeof(double) * nFramesPerBlock * nenoutchannels)); + for (us sample = 0; sample < nFramesPerBlock; sample++) { + bufcpy[sample] = 0; + } + } + assert(nenoutchannels > 0); + for (us sample = 0; sample < nFramesPerBlock; sample++) { + outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample]; + } + free(bufcpy); + botoutenqueued = true; + } + } else { + botoutenqueued = false; + if (!topoutenqueued) { + /* cerr << "Copying output buffer to top" << endl; */ + double *bufcpy; + if (!outqueue->empty()) { + bufcpy = (double *)outqueue->dequeue(); + } else { + cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " + "QUEUE WITH ZEROS ***********" + << endl; + bufcpy = static_cast( + malloc(sizeof(double) * nFramesPerBlock * nenoutchannels)); + for (us sample = 0; sample < nFramesPerBlock; sample++) { + bufcpy[sample] = 0; + } + } + assert(nenoutchannels > 0); + for (us sample = 0; sample < nFramesPerBlock; sample++) { + outbuffer[sample] = bufcpy[sample]; + } + free(bufcpy); + topoutenqueued = true; + } + } + } + + if (hasinput) { + err = ulDaqInScanStatus(handle, &inscanstat, &inxstat); + if (err != ERR_NO_ERROR) { + showErr(err); + goto exit; + } + assert(inscanstat == SS_RUNNING); + if (inxstat.currentScanCount > inTotalCount + 2 * nFramesPerBlock) { + cerr << "***** ERROR: Missing input sample blocks, count=" + << inxstat.currentScanCount + << ", probably due to too small buffer size. Exiting thread. " + "*****" + << endl; + break; + } + inTotalCount = inxstat.currentScanCount; + + if (inxstat.currentIndex < buffer_mid_idx_in) { + topinenqueued = false; + if (!botinenqueued) { + /* cerr << "Copying in buffer bot" << endl; */ + double *bufcpy = static_cast( + malloc(sizeof(double) * nFramesPerBlock * neninchannels)); + us monitoroffset = monitorOutput ? 1 : 0; + assert(neninchannels > 0); + for (us channel = 0; channel < (neninchannels - monitoroffset); + channel++) { + for (us sample = 0; sample < nFramesPerBlock; sample++) { + bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] = + inbuffer[buffer_mid_idx_in + sample * neninchannels + + channel]; + } + } + if (monitorOutput) { + // Monitor output goes to first channel, that is + // our convention + us channel = neninchannels - 1; + for (us sample = 0; sample < nFramesPerBlock; sample++) { + bufcpy[sample] = inbuffer[buffer_mid_idx_in + + sample * neninchannels + channel]; + } + } + inqueue->enqueue((void *)bufcpy); + botinenqueued = true; + } + } else { + botinenqueued = false; + if (!topinenqueued) { + double *bufcpy = static_cast( + malloc(sizeof(double) * nFramesPerBlock * neninchannels)); + us monitoroffset = monitorOutput ? 1 : 0; + assert(neninchannels > 0); + for (us channel = 0; channel < (neninchannels - monitoroffset); + channel++) { + for (us sample = 0; sample < nFramesPerBlock; sample++) { + bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] = + inbuffer[sample * neninchannels + channel]; + } + } + if (monitorOutput) { + // Monitor output goes to first channel, that is + // our convention + us channel = neninchannels - 1; + for (us sample = 0; sample < nFramesPerBlock; sample++) { + bufcpy[sample] = inbuffer[sample * neninchannels + channel]; + } + } + + /* cerr << "Copying in buffer top" << endl; */ + inqueue->enqueue((void *)bufcpy); + topinenqueued = true; + } + } + } + + std::this_thread::sleep_for(std::chrono::microseconds(sleeptime_us)); + + } // End of while loop + /* std::cerr << "Exit of while loop" << endl; */ + +exit: + + if (hasoutput) { + ulAOutScanStop(handle); + if (err != ERR_NO_ERROR) { + showErr(err); + } + } + + if (hasinput) { + ulDaqInScanStop(handle); + if (err != ERR_NO_ERROR) { + showErr(err); + } + } + + if (indesc) + delete indesc; + std::cerr << "Exit of DAQ thread fcn" << endl; +} Daq *createUlDaqDevice(const DeviceInfo &devinfo, - const DaqConfiguration &config) { + const DaqConfiguration &config) { - DT9837A *daq = NULL; - try { - daq = new DT9837A(config.nFramesPerBlock, config.eninchannels, - config.enoutchannels, - devinfo.availableSampleRates[config.sampleRateIndex], - config.monitorOutput, devinfo.devindex); + DT9837A *daq = NULL; - daq->setACCouplingMode(config.inputACCouplingMode); - daq->setIEPEEnabled(config.inputIEPEEnabled); - daq->setInputRange(config.inputHighRange); + try { + daq = new DT9837A(devinfo, config); - } catch (runtime_error &e) { - if (daq) - delete daq; - throw; - } - return daq; + } catch (runtime_error &e) { + if (daq) + delete daq; + throw; + } + return daq; } diff --git a/lasp/device/lasp_cppuldaq.h b/lasp/device/lasp_cppuldaq.h index 959d339..c82dba6 100644 --- a/lasp/device/lasp_cppuldaq.h +++ b/lasp/device/lasp_cppuldaq.h @@ -2,14 +2,6 @@ #define ULDAQ_H #include "lasp_cppqueue.h" #include "lasp_cppdaq.h" -#include -#include -#include -#include -#include -#include - -using std::atomic; Daq* createUlDaqDevice(const DeviceInfo& devinfo, const DaqConfiguration& config); diff --git a/lasp/device/lasp_daq.pyx b/lasp/device/lasp_daq.pyx index f5e34d0..8546b2b 100644 --- a/lasp/device/lasp_daq.pyx +++ b/lasp/device/lasp_daq.pyx @@ -1,8 +1,6 @@ cimport cython from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF -from .lasp_daqconfig import (Range as pyRange, - DAQChannel) -from .lasp_avtype import AvType +from .lasp_device_common import AvType __all__ = ['Daq'] diff --git a/lasp/device/lasp_daqconfig.pxd b/lasp/device/lasp_daqconfig.pxd new file mode 100644 index 0000000..e69de29 diff --git a/lasp/device/lasp_daqconfig.py b/lasp/device/lasp_daqconfig.pyx similarity index 100% rename from lasp/device/lasp_daqconfig.py rename to lasp/device/lasp_daqconfig.pyx diff --git a/lasp/device/lasp_avtype.py b/lasp/device/lasp_device_common.py similarity index 100% rename from lasp/device/lasp_avtype.py rename to lasp/device/lasp_device_common.py diff --git a/lasp/device/lasp_deviceinfo.pyx b/lasp/device/lasp_deviceinfo.pyx index 1f428d9..123d9c0 100644 --- a/lasp/device/lasp_deviceinfo.pyx +++ b/lasp/device/lasp_deviceinfo.pyx @@ -8,10 +8,7 @@ cdef class DeviceInfo: def api(self): return self.devinfo.api.apiname.decode('utf-8') @property - def name(self): return self.devinfo.name.decode('utf-8') - - @property - def devindex(self): return self.devinfo.devindex + def name(self): return self.devinfo.device_name.decode('utf-8') @property def ninchannels(self): return self.devinfo.ninchannels @@ -47,7 +44,7 @@ cdef class DeviceInfo: @property def inputRanges(self): - return self.devinfo.inputRanges + return self.devinfo.availableInputRanges @staticmethod def getDeviceInfo(): @@ -60,7 +57,7 @@ cdef class DeviceInfo: us numdevs, devno cppDeviceInfo* devinfo - devinfos = DaqDevices.getDeviceInfo() + devinfos = cppDaq.getDeviceInfo() numdevs = devinfos.size() pydevinfos = [] diff --git a/lasp/lasp_avstream.py b/lasp/lasp_avstream.py index e2e09bb..2d4dc53 100644 --- a/lasp/lasp_avstream.py +++ b/lasp/lasp_avstream.py @@ -8,8 +8,11 @@ from .lasp_atomic import Atomic from threading import Thread, Condition, Lock import numpy as np +class DAQConfiguration: + pass + import time -from .device import (Daq, DeviceInfo, DAQConfiguration, +from .device import (Daq, DeviceInfo, # get_numpy_dtype_from_format_string, # get_sampwidth_from_format_string, AvType, diff --git a/test/test_uldaq.cpp b/test/test_uldaq.cpp index e9d45f5..9668bf6 100644 --- a/test/test_uldaq.cpp +++ b/test/test_uldaq.cpp @@ -10,84 +10,80 @@ using std::endl; int main() { - /* boolvec inChannels = {true, false, false, false}; */ - boolvec inChannels = {true, true, false, false}; - boolvec outChannels = {true}; - double samplerate = 10000; - const us samplesPerBlock = 256; - - DaqConfiguration config; - config.eninchannels = inChannels; - config.enoutchannels = outChannels; - config.inputIEPEEnabled = {false, false, false, false}; - config.inputACCouplingMode = {false, false, false, false}; - config.inputHighRange = {false, false, false, false}; - config.sampleRateIndex = 0; - config.datatype = dtype_fl64; - config.monitorOutput = true; - config.inputIEPEEnabled = {false, false, false, false}; - config.nFramesPerBlock = samplesPerBlock; - cout << "Inchannnels size: " < devinfos = DaqDevices::getDeviceInfo(); - DeviceInfo devinfo; - us i; - bool found = false; - for(i=0;i inqueue; + SafeQueue outqueue; + + double totalTime = 5; + double t = 0; + double freq = 1000; + us nblocks = ((us) totalTime*samplerate/samplesPerBlock) + 10; + + for(us i=0;i(malloc(sizeof(double)*samplesPerBlock)); + for(us sample=0;samplestart(&inqueue, &outqueue); - SafeQueue inqueue; - SafeQueue outqueue; + std::this_thread::sleep_for(std::chrono::seconds((int) totalTime)); - double totalTime = 5; - double t = 0; - double freq = 1000; - us nblocks = ((us) totalTime*samplerate/samplesPerBlock) + 10; + daq->stop(); - for(us i=0;i(malloc(sizeof(double)*samplesPerBlock)); - for(us sample=0;sampleneninchannels();ch++) { + cout << buf[ch*samplesPerBlock+i] << " "; + } + cout << endl; } + free(buf); + } + while(!outqueue.empty()){ + void* dat = outqueue.dequeue(); + free(dat); + } - daq->start(&inqueue, &outqueue); - - std::this_thread::sleep_for(std::chrono::seconds((int) totalTime)); - - daq->stop(); - - while(!inqueue.empty()) { - double* buf = (double*) inqueue.dequeue(); - for(us i=0;ineninchannels();ch++) { - cout << buf[ch*samplesPerBlock+i] << " "; - } - cout << endl; - } - free(buf); - } - while(!outqueue.empty()){ - void* dat = outqueue.dequeue(); - free(dat); - } - - return 0; + return 0; }