diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fa387a..07cc4a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,9 +167,8 @@ set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/*.py" "${CMAKE_CURRENT_SOURCE_DIR}/lasp/*.py" "wrappers" - "lasp_rtaudio") + "lasp_daq") -# ) set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") # configure_file(${SETUP_PY_IN} ${SETUP_PY}) diff --git a/lasp/device/CMakeLists.txt b/lasp/device/CMakeLists.txt index f430ea7..bc29a28 100644 --- a/lasp/device/CMakeLists.txt +++ b/lasp/device/CMakeLists.txt @@ -1,6 +1,7 @@ include_directories(/usr/include/rtaudio) -add_library(cpp_daq lasp_cppdaq.cpp lasp_cppuldaq.cpp lasp_cpprtaudio.cpp) +# add_library(cpp_daq lasp_cppdaq.cpp lasp_cppuldaq.cpp lasp_cpprtaudio.cpp) +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_daq.cxx PROPERTIES COMPILE_FLAGS diff --git a/lasp/device/lasp_cppdaq.cpp b/lasp/device/lasp_cppdaq.cpp index bce6f1f..5017aa1 100644 --- a/lasp/device/lasp_cppdaq.cpp +++ b/lasp/device/lasp_cppdaq.cpp @@ -1,80 +1,84 @@ #include "lasp_cppdaq.h" +#include "lasp_cppuldaq.h" +#include +using std::cout; +using std::endl; + #define MAX_DEV_COUNT_PER_API 20 #ifdef HAS_ULDAQ_LINUX_API -void fillUlDaqDeviceInfo(vector& devinfolist) { +void fillUlDaqDeviceInfo(vector &devinfolist) { - UlError err; - unsigned int numdevs = MAX_DEV_COUNT_PER_API; - unsigned deviceno; + UlError err; + unsigned int numdevs = MAX_DEV_COUNT_PER_API; + unsigned deviceno; - DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API]; - DaqDeviceDescriptor descriptor; - DaqDeviceInterface interfaceType = ANY_IFC; + DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API]; + DaqDeviceDescriptor descriptor; + DaqDeviceInterface interfaceType = ANY_IFC; - err = ulGetDaqDeviceInventory(interfaceType, - devdescriptors, - &numdevs); + err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, &numdevs); - if(err != ERR_NO_ERROR) - throw std::runtime_error("Device inventarization failed"); + if (err != ERR_NO_ERROR) + throw std::runtime_error("UlDaq device inventarization failed"); - for(unsigned i =0; i DaqDevices::getDeviceInfo() { - vector devs; + vector devs; #ifdef HAS_ULDAQ_LINUX_API - fillUlDaqDeviceInfo(devs); + fillUlDaqDeviceInfo(devs); #endif #ifdef HAS_RTAUDIO_ALSA_API + #endif #ifdef HAS_RTAUDIO_PULSEAUDIO_API @@ -82,11 +86,24 @@ vector DaqDevices::getDeviceInfo() { #ifdef HAS_RTAUDIO_WIN_WASAPI_API #endif - return devs; + return devs; } -Daq* DaqDevices::createDevice(const DeviceInfo&) { +Daq *DaqDevices::createDevice(const DeviceInfo &devinfo, + const DaqConfiguration &config) { + // Some basic sanity checks + if ((devinfo.ninchannels != config.eninchannels.size())) { + throw runtime_error("Invalid length of enabled input channels specified"); + } + if ((devinfo.noutchannels != config.enoutchannels.size())) { + throw runtime_error("outvalid length of enabled output channels specified"); + } - return NULL; + if (devinfo.api == uldaqapi) { + return createUlDaqDevice(devinfo, config); + } else { + throw std::runtime_error(string("Unable to match API: ") + + devinfo.api.apiname); + } } diff --git a/lasp/device/lasp_cppdaq.h b/lasp/device/lasp_cppdaq.h index 2b16e95..8678acd 100644 --- a/lasp/device/lasp_cppdaq.h +++ b/lasp/device/lasp_cppdaq.h @@ -1,5 +1,6 @@ #ifndef LASP_CPPDAQ_H #define LASP_CPPDAQ_H +#include #ifdef HAS_RTAUDIO #include @@ -10,27 +11,30 @@ #endif #include "lasp_cppqueue.h" -#include "vector" #include "string" -using std::vector; +#include "vector" using std::string; +using std::vector; +using std::runtime_error; + +typedef unsigned int us; +typedef vector boolvec; class DataType { public: - const string name; + 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(const char *name, unsigned sw, bool is_floating) + : name(name), sw(sw), is_floating(is_floating) {} }; -const DataType dtype_fl64("64-bits floating point",4,true); -const DataType dtype_int8("8-bits integers",1,false); -const DataType dtype_int16("16-bits integers",2,false); -const DataType dtype_int32("32-bits integers",4,false); +const DataType dtype_invalid("invalid data type", 0, false); +const DataType dtype_fl64("64-bits floating point", 4, true); +const DataType dtype_int8("8-bits integers", 1, false); +const DataType dtype_int16("16-bits integers", 2, false); +const DataType dtype_int32("32-bits integers", 4, false); const std::vector dataTypes = { dtype_int8, @@ -45,27 +49,32 @@ class DaqApi { unsigned apicode = 0; unsigned api_specific_subcode = 0; - DaqApi(string apiname, - unsigned apicode, - unsigned api_specific_subcode=0): - apiname(apiname), - apicode(apicode), + DaqApi(string apiname, unsigned apicode, unsigned api_specific_subcode = 0) + : apiname(apiname), apicode(apicode), api_specific_subcode(api_specific_subcode) {} - DaqApi(){} + 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 -const DaqApi uldaqapi = DaqApi("UlDaq", 0); +const DaqApi uldaqapi("UlDaq", 0); #endif +#ifdef HAS_RTAUDIO_ALSA_API +const DaqApi rtaudioalsaapi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA); +#endif + const vector compiledApis = { #ifdef HAS_ULDAQ_LINUX_API uldaqapi, #endif #ifdef HAS_RTAUDIO_ALSA_API - DaqApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA), + 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), @@ -82,41 +91,74 @@ class DeviceInfo { vector availableDataTypes; vector availableSampleRates; - unsigned prefSampleRateIndex = 0; - double prefSampleRate = 0; + vector availableFramesPerBlock; - unsigned ninchannels =0; - unsigned noutchannels =0; + int prefSampleRateIndex = -1; + unsigned ninchannels = 0; + unsigned noutchannels = 0; bool hasInputIEPE = false; bool hasInputACCouplingSwitch = false; bool hasInputTrigger = false; + 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(); + + } +}; + +// Device configuration parameters +class DaqConfiguration { + public: + boolvec eninchannels; // Enabled input channels + boolvec enoutchannels; // Enabled output channels + + 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 + + unsigned nFramesPerBlock; + + boolvec inputIEPEEnabled; + boolvec inputACCouplingMode; + boolvec inputHighRange; + }; class Daq; class DaqDevices { public: static vector getDeviceInfo(); - static Daq* createDevice(const DeviceInfo&); - + static Daq *createDevice(const DeviceInfo &, const DaqConfiguration &config); }; - -class Daq{ +class Daq { public: - - virtual void start( - SafeQueue *inqueue, - SafeQueue *outqueue) = 0; + virtual void start(SafeQueue *inqueue, + SafeQueue *outqueue) = 0; virtual void stop() = 0; virtual double samplerate() const = 0; virtual DataType getDataType() const = 0; - virtual ~Daq() {}; - + virtual ~Daq(){}; + virtual us neninchannels() const = 0; + virtual us nenoutchannels() const = 0; }; #endif // LASP_CPPDAQ_H diff --git a/lasp/device/lasp_cppuldaq.cpp b/lasp/device/lasp_cppuldaq.cpp index dd3fa07..97387e6 100644 --- a/lasp/device/lasp_cppuldaq.cpp +++ b/lasp/device/lasp_cppuldaq.cpp @@ -1,13 +1,19 @@ #include "lasp_cppuldaq.h" -#include -#include +#include +#include #include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include -using std::endl; using std::cerr; +using std::cout; +using std::endl; using std::runtime_error; typedef std::lock_guard mutexlock; /* using std::this_thread; */ @@ -15,178 +21,213 @@ typedef std::lock_guard mutexlock; 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) { + if (err != ERR_NO_ERROR) { char errmsg[UL_ERR_MSG_LEN]; ulGetErrMsg(err, errmsg); std::cerr << "UlError: " << errmsg << std::endl; } } -DT9837A::DT9837A( - us samplesPerBlock, - boolvec& inChannels, - 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"); - } +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"); + } - stopThread = false; - high_range = boolvec({false, false, false, false}); + stopThread = false; + high_range = boolvec({false, false, false, false}); - if(samplesPerBlock < 24 || samplesPerBlock > 8192) { - throw runtime_error("Unsensible number of samples per block chosen"); - } + if (samplesPerBlock < 24 || samplesPerBlock > 8192) { + throw runtime_error("Unsensible number of samples per block chosen"); + } + // Some sanity checks + if (inChannels.size() != 4) { + throw runtime_error("Invalid length of enabled inChannels vector"); + } - // Some sanity checks - if(inChannels.size() != 4) { - throw runtime_error("Invalid length of enabled inChannels vector"); - } + // 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"); + } - // 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"); - } + DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]; + DaqDeviceDescriptor descriptor; + DaqDeviceInterface interfaceType = ANY_IFC; - DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]; - DaqDeviceDescriptor descriptor; - DaqDeviceInterface interfaceType = ANY_IFC; + UlError err; - UlError err; + us numdevs = MAX_DEF_COUNT; + err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, &numdevs); + if (err != ERR_NO_ERROR) { + throw runtime_error("Device inventarization failed"); + } - us numdevs = MAX_DEF_COUNT; - err = ulGetDaqDeviceInventory(interfaceType, - devdescriptors, - &numdevs); - if(err != ERR_NO_ERROR){ - throw runtime_error("Device inventarization failed"); - } + if (deviceno >= numdevs) { + throw runtime_error("Device number {deviceno} too high {err}. This could " + "happen when the device is currently not connected"); + } - if (deviceno >= numdevs) { - throw runtime_error("Device number {deviceno} too high {err}. This could happen when the device is currently not connected"); - } + descriptor = devdescriptors[deviceno]; + // get a handle to the DAQ device associated with the first descriptor + handle = ulCreateDaqDevice(descriptor); - 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?"); + } - 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}"); + } - 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"); + 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"); + } } } - -} - DT9837A::~DT9837A() { UlError err; - if(isRunning()) { + if (isRunning()) { stop(); } - if(handle) { + if (handle) { err = ulDisconnectDaqDevice(handle); showErr(err); err = ulReleaseDaqDevice(handle); showErr(err); } - } -void DT9837A::setIEPEEnabled(boolvec& config) { - if(isRunning()) { +void DT9837A::setIEPEEnabled(const boolvec &config) { + 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 inChannels vector"); + 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++) { + 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) { + if (err != ERR_NO_ERROR) { showErr(err); throw runtime_error("Fatal: could not set IEPE mode"); } - } } -void DT9837A::setACCouplingMode(boolvec& coupling) { - if(isRunning()) { +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 inChannels vector"); + 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++) { + 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) { + if (err != ERR_NO_ERROR) { showErr(err); throw runtime_error("Fatal: could not set IEPE mode"); } - } } -void DT9837A::setInputRange(boolvec& high_range) { - if(isRunning()) { +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) { + if (high_range.size() != 4) { throw runtime_error("Invalid length of enabled high range vector"); } this->high_range = high_range; } - -void threadfcn(DT9837A* td) { +void threadfcn(DT9837A *td) { std::cerr << "Starting DAQ Thread fcn" << endl; @@ -199,11 +240,11 @@ void threadfcn(DT9837A* td) { bool monitorOutput = td->monitorOutput; - double* inbuffer = td->inbuffer; - double* outbuffer = td->outbuffer; + double *inbuffer = td->inbuffer; + double *outbuffer = td->outbuffer; - SafeQueue* inqueue = td->inqueue; - SafeQueue* outqueue = td->outqueue; + SafeQueue *inqueue = td->inqueue; + SafeQueue *outqueue = td->outqueue; ScanStatus inscanstat; ScanStatus outscanstat; @@ -211,16 +252,16 @@ void threadfcn(DT9837A* td) { double samplerate = td->samplerate(); - const double sleeptime = ((double) samplesPerBlock)/(4*samplerate); - const us sleeptime_us = (us) (sleeptime*1e6); + 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) { + 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; + const us buffer_mid_idx_in = neninchannels * samplesPerBlock; + const us buffer_mid_idx_out = nenoutchannels * samplesPerBlock; DaqDeviceHandle handle = td->handle; assert(handle); @@ -243,142 +284,135 @@ void threadfcn(DT9837A* td) { size_t outTotalCount = 0; // initialize output, if any - if(hasoutput) { + if (hasoutput) { assert(nenoutchannels == 1); assert(outqueue); // Initialize the buffer with zeros, before pushing any data. - for(us sample=0;sample<2*samplesPerBlock;sample++) { + for (us sample = 0; sample < 2 * samplesPerBlock; sample++) { outbuffer[sample] = 0; } cerr << "Starting output DAC" << endl; - err = ulAOutScan(handle, - 0, - 0, - BIP10VOLTS, + err = ulAOutScan(handle, 0, 0, BIP10VOLTS, /* BIP60VOLTS, */ - 2*td->samplesPerBlock, // Watch the 2 here! - &samplerate, - scanoptions, - outscanflags, - outbuffer); + 2 * td->samplesPerBlock, // Watch the 2 here! + &samplerate, scanoptions, outscanflags, outbuffer); - if(err != ERR_NO_ERROR) { + if (err != ERR_NO_ERROR) { showErr(err); goto exit; } - } // Initialize input, if any - if(hasinput) { + if (hasinput) { indesc = new DaqInChanDescriptor[neninchannels]; us j = 0; - for(us chin=0; chin < 4; chin++) { - if(td->inChannels[chin] == true) { + 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) { + if (monitorOutput) { indesc[j].type = DAQI_DAC; indesc[j].channel = 0; indesc[j].range = BIP10VOLTS; j++; } - assert(j==neninchannels); + 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) { + 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) { + if (hasoutput) { err = ulAOutScanStatus(handle, &outscanstat, &outxstat); - if(err != ERR_NO_ERROR) { + 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) { + while (!td->stopThread && err == ERR_NO_ERROR) { /* std::cerr << "While..." << endl; */ - if(hasoutput) { + if (hasoutput) { err = ulAOutScanStatus(handle, &outscanstat, &outxstat); - if(err != ERR_NO_ERROR) { + 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; + 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) { + /* std::cerr << "Samples scanned: " << outxstat.currentTotalCount << endl; + */ + if (outxstat.currentIndex < buffer_mid_idx_out) { topoutenqueued = false; - if(!botoutenqueued) { + if (!botoutenqueued) { /* cerr << "Copying output buffer to bottom" << endl; */ - double* bufcpy; - if(!outqueue->empty()) { - bufcpy = 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++) { + 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++) { + for (us sample = 0; sample < samplesPerBlock; sample++) { outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample]; } free(bufcpy); botoutenqueued = true; } - } - else { + } else { botoutenqueued = false; - if(!topoutenqueued) { + if (!topoutenqueued) { /* cerr << "Copying output buffer to top" << endl; */ - double* bufcpy; - if(!outqueue->empty()) { - bufcpy = 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++) { + 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 inTotalCount+2*samplesPerBlock) { - cerr << "***** ERROR: Missing input sample blocks, count=" << inxstat.currentScanCount << ", probably due to too small buffer size. Exiting thread. *****" << endl; + 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) { + if (inxstat.currentIndex < buffer_mid_idx_in) { topinenqueued = false; - if(!botinenqueued) { + if (!botinenqueued) { /* cerr << "Copying in buffer bot" << endl; */ - double* bufcpy = static_cast(malloc(sizeof(double)*samplesPerBlock*neninchannels)); - us monitoroffset = monitorOutput ? 1: 0; + 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]; + 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) { + 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]; + for (us sample = 0; sample < samplesPerBlock; sample++) { + bufcpy[sample] = inbuffer[buffer_mid_idx_in + + sample * neninchannels + channel]; } } - inqueue->enqueue(bufcpy); + inqueue->enqueue((void *)bufcpy); botinenqueued = true; } - } - else { + } else { botinenqueued = false; - if(!topinenqueued) { - double* bufcpy = static_cast(malloc(sizeof(double)*samplesPerBlock*neninchannels)); - us monitoroffset = monitorOutput ? 1: 0; + 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]; + 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) { + 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]; + for (us sample = 0; sample < samplesPerBlock; sample++) { + bufcpy[sample] = inbuffer[sample * neninchannels + channel]; } } /* cerr << "Copying in buffer top" << endl; */ - inqueue->enqueue(bufcpy); + inqueue->enqueue((void *)bufcpy); topinenqueued = true; } } - } std::this_thread::sleep_for(std::chrono::microseconds(sleeptime_us)); @@ -457,66 +500,67 @@ void threadfcn(DT9837A* td) { } // End of while loop /* std::cerr << "Exit of while loop" << endl; */ - exit: - if(hasoutput) { + if (hasoutput) { ulAOutScanStop(handle); - if(err != ERR_NO_ERROR) { + if (err != ERR_NO_ERROR) { showErr(err); } } - if(hasinput) { + if (hasinput) { ulDaqInScanStop(handle); - if(err != ERR_NO_ERROR) { + if (err != ERR_NO_ERROR) { showErr(err); } } - if(indesc) delete indesc; + if (indesc) + delete indesc; std::cerr << "Exit of DAQ thread fcn" << endl; - } -void DT9837A::start( SafeQueue *inqueue, SafeQueue *outqueue) { - if(isRunning()) { +void DT9837A::start(SafeQueue *inqueue, SafeQueue *outqueue) { + if (isRunning()) { throw runtime_error("Thread is already running"); } bool hasinput = neninchannels() > 0; bool hasoutput = nenoutchannels() > 0; - if(neninchannels() > 0 && !inqueue) { + if (neninchannels() > 0 && !inqueue) { throw runtime_error("Inqueue not given, while input is enabled"); } - if(nenoutchannels() > 0 && !outqueue) { + if (nenoutchannels() > 0 && !outqueue) { throw runtime_error("outqueue not given, while output is enabled"); } - if(hasinput) { + if (hasinput) { assert(!inbuffer); - inbuffer = new double[neninchannels()*samplesPerBlock*2]; // Watch the 2! + inbuffer = + new double[neninchannels() * samplesPerBlock * 2]; // Watch the 2! } - if(hasoutput) { + if (hasoutput) { assert(!outbuffer); - outbuffer = new double[nenoutchannels()*samplesPerBlock*2]; // Watch the 2! + outbuffer = + new double[nenoutchannels() * samplesPerBlock * 2]; // Watch the 2! } this->inqueue = inqueue; this->outqueue = outqueue; - /* std::cerr << "************************ WARNING: Forcing coupling mode to AC **************************" << endl; */ + /* 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() { - if(!isRunning()) { + if (!isRunning()) { throw runtime_error("No data acquisition running"); } assert(thread); @@ -528,8 +572,10 @@ void DT9837A::stop() { outqueue = NULL; inqueue = NULL; - if(inbuffer) delete inbuffer; - if(outbuffer) delete outbuffer; + if (inbuffer) + delete inbuffer; + if (outbuffer) + delete outbuffer; outbuffer = NULL; inbuffer = NULL; } @@ -537,7 +583,8 @@ void DT9837A::stop() { us DT9837A::neninchannels() const { mutexlock lock(mutex); us inch = std::count(inChannels.begin(), inChannels.end(), true); - if(monitorOutput) inch++; + if (monitorOutput) + inch++; return inch; } @@ -545,3 +592,26 @@ us DT9837A::nenoutchannels() const { mutexlock lock(mutex); return std::count(outChannels.begin(), outChannels.end(), true); } + + +Daq *createUlDaqDevice(const DeviceInfo &devinfo, + const DaqConfiguration &config) { + + DT9837A *daq = NULL; + try { + daq = new DT9837A(config.nFramesPerBlock, config.eninchannels, + config.enoutchannels, + devinfo.availableSampleRates[config.sampleRateIndex], + config.monitorOutput, devinfo.devindex); + + daq->setACCouplingMode(config.inputACCouplingMode); + daq->setIEPEEnabled(config.inputIEPEEnabled); + daq->setInputRange(config.inputHighRange); + + } 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 d621ae3..959d339 100644 --- a/lasp/device/lasp_cppuldaq.h +++ b/lasp/device/lasp_cppuldaq.h @@ -9,64 +9,10 @@ #include #include -using std::vector; using std::atomic; -typedef vector boolvec; -typedef unsigned int us; -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, - boolvec& inChannels, - boolvec& outChannels, - double samplerate, - bool monitorOutput, - us device_no = 0 - ); - DT9837A(const DT9837A&) = delete; - - ~DT9837A(); - - void setIEPEEnabled(boolvec& config); - - // Coupling_ac: true, means AC coupling, false means DC coupling - void setACCouplingMode(boolvec& coupling_ac); - - void setInputRange(boolvec& high_range); - - us neninchannels() const; - us nenoutchannels() const; - - bool isRunning() const { return bool(thread); } - - virtual void start( - SafeQueue *inqueue, - SafeQueue *outqueue) final; - - virtual void stop() final; - - - friend void threadfcn(DT9837A*); -}; +Daq* createUlDaqDevice(const DeviceInfo& devinfo, + const DaqConfiguration& config); #endif // ULDAQ_H diff --git a/lasp/device/lasp_daq.pxd b/lasp/device/lasp_daq.pxd index 1a0bb2b..deb6cdc 100644 --- a/lasp/device/lasp_daq.pxd +++ b/lasp/device/lasp_daq.pxd @@ -1,360 +1,2 @@ include "lasp_common_decls.pxd" -cdef extern from "uldaq.h" nogil: - - ctypedef enum DaqDeviceInterface: - USB_IFC - BLUETOOTH_IFC - ETHERNET_IFC - ANY_IFC - - ctypedef struct DaqDeviceDescriptor: - char productName[64] - unsigned int productId - DaqDeviceInterface devInterface - char devString[64] - char uniqueId[64] - char reserved[512] - - ctypedef long long DaqDeviceHandle - - ctypedef struct TransferStatus: - unsigned long long currentScanCount - unsigned long long currentTotalCount - long long currentIndex - char reserved[64] - - UlError ulGetErrMsg(UlError, char* errMsg) - - ctypedef enum UlError: - ERR_NO_ERROR - ERR_UNHANDLED_EXCEPTION - ERR_BAD_DEV_HANDLE - ERR_BAD_DEV_TYPE - ERR_USB_DEV_NO_PERMISSION - ERR_USB_INTERFACE_CLAIMED - ERR_DEV_NOT_FOUND - ERR_DEV_NOT_CONNECTED - ERR_DEAD_DEV - ERR_BAD_BUFFER_SIZE - ERR_BAD_BUFFER - ERR_BAD_MEM_TYPE - ERR_BAD_MEM_REGION - ERR_BAD_RANGE - ERR_BAD_AI_CHAN - ERR_BAD_INPUT_MODE - ERR_ALREADY_ACTIVE - ERR_BAD_TRIG_TYPE - ERR_OVERRUN - ERR_UNDERRUN - ERR_TIMEDOUT - ERR_BAD_OPTION - ERR_BAD_RATE - ERR_BAD_BURSTIO_COUNT - ERR_CONFIG_NOT_SUPPORTED - ERR_BAD_CONFIG_VAL - ERR_BAD_AI_CHAN_TYPE - ERR_ADC_OVERRUN - ERR_BAD_TC_TYPE - ERR_BAD_UNIT - ERR_BAD_QUEUE_SIZE - ERR_BAD_CONFIG_ITEM - ERR_BAD_INFO_ITEM - ERR_BAD_FLAG - ERR_BAD_SAMPLE_COUNT - ERR_INTERNAL - ERR_BAD_COUPLING_MODE - ERR_BAD_SENSOR_SENSITIVITY - ERR_BAD_IEPE_MODE - ERR_BAD_AI_CHAN_QUEUE - ERR_BAD_AI_GAIN_QUEUE - ERR_BAD_AI_MODE_QUEUE - ERR_FPGA_FILE_NOT_FOUND - ERR_UNABLE_TO_READ_FPGA_FILE - ERR_NO_FPGA - ERR_BAD_ARG - ERR_MIN_SLOPE_VAL_REACHED - ERR_MAX_SLOPE_VAL_REACHED - ERR_MIN_OFFSET_VAL_REACHED - ERR_MAX_OFFSET_VAL_REACHED - ERR_BAD_PORT_TYPE - ERR_WRONG_DIG_CONFIG - ERR_BAD_BIT_NUM - ERR_BAD_PORT_VAL - ERR_BAD_RETRIG_COUNT - ERR_BAD_AO_CHAN - ERR_BAD_DA_VAL - ERR_BAD_TMR - ERR_BAD_FREQUENCY - ERR_BAD_DUTY_CYCLE - ERR_BAD_INITIAL_DELAY - ERR_BAD_CTR - ERR_BAD_CTR_VAL - ERR_BAD_DAQI_CHAN_TYPE - ERR_BAD_NUM_CHANS - ERR_BAD_CTR_REG - ERR_BAD_CTR_MEASURE_TYPE - ERR_BAD_CTR_MEASURE_MODE - ERR_BAD_DEBOUNCE_TIME - ERR_BAD_DEBOUNCE_MODE - ERR_BAD_EDGE_DETECTION - ERR_BAD_TICK_SIZE - ERR_BAD_DAQO_CHAN_TYPE - ERR_NO_CONNECTION_ESTABLISHED - ERR_BAD_EVENT_TYPE - ERR_EVENT_ALREADY_ENABLED - ERR_BAD_EVENT_PARAMETER - ERR_BAD_CALLBACK_FUCNTION - ERR_BAD_MEM_ADDRESS - ERR_MEM_ACCESS_DENIED - ERR_DEV_UNAVAILABLE - ERR_BAD_RETRIG_TRIG_TYPE - ERR_BAD_DEV_VER - ERR_BAD_DIG_OPERATION - ERR_BAD_PORT_INDEX - ERR_OPEN_CONNECTION - ERR_DEV_NOT_READY - ERR_PACER_OVERRUN - ERR_BAD_TRIG_CHANNEL - ERR_BAD_TRIG_LEVEL - ERR_BAD_CHAN_ORDER - ERR_TEMP_OUT_OF_RANGE - ERR_TRIG_THRESHOLD_OUT_OF_RANGE - ERR_INCOMPATIBLE_FIRMWARE - ERR_BAD_NET_IFC - ERR_BAD_NET_HOST - ERR_BAD_NET_PORT - ERR_NET_IFC_UNAVAILABLE - ERR_NET_CONNECTION_FAILED - ERR_BAD_CONNECTION_CODE - ERR_CONNECTION_CODE_IGNORED - ERR_NET_DEV_IN_USE - ERR_BAD_NET_FRAME - ERR_NET_TIMEOUT - ERR_DATA_SOCKET_CONNECTION_FAILED - ERR_PORT_USED_FOR_ALARM - ERR_BIT_USED_FOR_ALARM - ERR_CMR_EXCEEDED - ERR_NET_BUFFER_OVERRUN - ERR_BAD_NET_BUFFER - - ctypedef enum Range: - BIP10VOLTS - BIP5VOLTS - BIP4VOLTS - BIP2PT5VOLTS - BIP2VOLTS - BIP1PT25VOLTS - BIP1VOLTS - BIPPT625VOLTS - BIPPT5VOLTS - BIPPT25VOLTS - BIPPT125VOLTS - BIPPT2VOLTS - BIPPT1VOLTS - BIPPT078VOLTS - BIPPT05VOLTS - BIPPT01VOLTS - BIPPT005VOLTS - BIP3VOLTS - BIPPT312VOLTS - BIPPT156VOLTS - UNI15VOLTS - UNI20VOLTS - UNI10VOLTS - UNI5VOLTS - UNI4VOLTS - UNI2PT5VOLTS - UNI2VOLTS - UNI1PT25VOLTS - UNI1VOLTS - UNIPT625VOLTS - UNIPT5VOLTS - UNIPT25VOLTS - UNIPT125VOLTS - UNIPT2VOLTS - UNIPT1VOLTS - UNIPT078VOLTS - UNIPT05VOLTS - UNIPT01VOLTS - UNIPT005VOLTS - MA0TO20 - - ctypedef enum AdcTimingMode: - ADC_TM_AUTO - ADC_TM_HIGH_RES - ADC_TM_HIGH_SPEED - - ctypedef enum IepeMode: - IEPE_ENABLED - IEPE_DISABLED - - ctypedef enum CouplingMode: - CM_DC - CM_AC - - ctypedef enum TriggerType: - TRIG_NONE - TRIG_POS_EDGE - TRIG_NEG_EDGE - TRIG_HIGH - TRIG_LOW - GATE_HIGH - GATE_LOW - TRIG_RISING - TRIG_FALLING - TRIG_ABOVE - TRIG_BELOW - GATE_ABOVE - GATE_BELOW - GATE_IN_WINDOW - GATE_OUT_WINDOW - TRIG_PATTERN_EQ - TRIG_PATTERN_NE - TRIG_PATTERN_ABOVE - TRIG_PATTERN_BELOW - - ctypedef enum ScanStatus: - SS_IDLE - SS_RUNNING - - ctypedef enum ScanOption: - SO_DEFAULTIO - SO_SINGLEIO - SO_BLOCKIO - SO_BURSTIO - SO_CONTINUOUS - SO_EXTCLOCK - SO_EXTTRIGGER - SO_RETRIGGER - SO_BURSTMODE - SO_PACEROUT - SO_EXTTIMEBASE - SO_TIMEBASEOUT - - ctypedef enum DaqInScanFlag: - DAQINSCAN_FF_DEFAULT - DAQINSCAN_FF_NOSCALEDATA - DAQINSCAN_FF_NOCALIBRATEDATA - DAQINSCAN_FF_NOCLEAR - - ctypedef enum AOutScanFlag: - AOUTSCAN_FF_DEFAULT - AOUTSCAN_FF_NOSCALEDATA - AOUTSCAN_FF_NOCALIBRATEDATA - AOUTSCAN_FF_NOCLEAR - - ctypedef enum DaqInChanType: - DAQI_ANALOG_DIFF - DAQI_ANALOG_SE - DAQI_DIGITAL - DAQI_CTR16 - DAQI_CTR32 - DAQI_CTR48 - DAQI_DAC - - ctypedef struct DaqInChanDescriptor: - int channel - DaqInChanType type - Range range - - ctypedef enum DaqEventType: - DE_NONE - DE_ON_DATA_AVAILABLE - DE_ON_INPUT_SCAN_ERROR - DE_ON_END_OF_INPUT_SCAN - DE_ON_OUTPUT_SCAN_ERROR - DE_ON_END_OF_OUTPUT_SCAN - - ctypedef enum WaitType: - WAIT_UNTIL_DONE - - ctypedef enum DevInfoItem: - DEV_INFO_HAS_AI_DEV - DEV_INFO_HAS_AO_DEV - DEV_INFO_HAS_DIO_DEV - DEV_INFO_HAS_CTR_DEV - DEV_INFO_HAS_TMR_DEV - DEV_INFO_HAS_DAQI_DEV - DEV_INFO_HAS_DAQO_DEV - DEV_INFO_DAQ_EVENT_TYPES - DEV_INFO_MEM_REGIONS - - ctypedef enum AiInfoItem: - AI_INFO_RESOLUTION - AI_INFO_NUM_CHANS - AI_INFO_NUM_CHANS_BY_MODE - AI_INFO_NUM_CHANS_BY_TYPE - AI_INFO_CHAN_TYPES - AI_INFO_SCAN_OPTIONS - AI_INFO_HAS_PACER - AI_INFO_NUM_DIFF_RANGES - AI_INFO_NUM_SE_RANGES - AI_INFO_DIFF_RANGE - AI_INFO_SE_RANGE - AI_INFO_TRIG_TYPES - AI_INFO_MAX_QUEUE_LENGTH_BY_MODE - AI_INFO_QUEUE_TYPES - AI_INFO_QUEUE_LIMITS - AI_INFO_FIFO_SIZE - AI_INFO_IEPE_SUPPORTED - - ctypedef enum AiConfigItem: - AI_CFG_CHAN_TYPE - AI_CFG_CHAN_TC_TYPE - AI_CFG_SCAN_CHAN_TEMP_UNIT - AI_CFG_SCAN_TEMP_UNIT - AI_CFG_ADC_TIMING_MODE - AI_CFG_AUTO_ZERO_MODE - AI_CFG_CAL_DATE - AI_CFG_CHAN_IEPE_MODE - AI_CFG_CHAN_COUPLING_MODE - AI_CFG_CHAN_SENSOR_CONNECTION_TYPE - AI_CFG_CHAN_OTD_MODE - AI_CFG_OTD_MODE - AI_CFG_CAL_TABLE_TYPE - AI_CFG_REJECT_FREQ_TYPE - AI_CFG_EXP_CAL_DATE - - ctypedef enum AoConfigItem: - AO_CFG_SYNC_MODE - AO_CFG_CHAN_SENSE_MODE - - ctypedef enum AiConfigItemDbl: - AI_CFG_CHAN_SLOPE - AI_CFG_CHAN_OFFSET - AI_CFG_CHAN_SENSOR_SENSITIVITY - AI_CFG_CHAN_DATA_RATE - - ctypedef enum AInScanFlag: - AINSCAN_FF_DEFAULT - AINSCAN_FF_NOSCALEDATA - AINSCAN_FF_NOCALIBRATEDATA - - UlError ulGetDaqDeviceInventory(DaqDeviceInterface interfaceTypes, DaqDeviceDescriptor daqDevDescriptors[], unsigned int* numDescriptors ) - - DaqDeviceHandle ulCreateDaqDevice(DaqDeviceDescriptor daqDevDescriptor) - UlError ulConnectDaqDevice(DaqDeviceHandle daqDeviceHandle); - - UlError ulDisconnectDaqDevice(DaqDeviceHandle daqDeviceHandle); - UlError ulReleaseDaqDevice(DaqDeviceHandle daqDeviceHandle); - - UlError ulAISetConfig(DaqDeviceHandle daqDeviceHandle, AiConfigItem configItem, unsigned int index, long long configValue) - UlError ulAISetConfigDbl(DaqDeviceHandle daqDeviceHandle, AiConfigItemDbl configItem, unsigned int index, double configValue) - UlError ulAIGetConfig(DaqDeviceHandle daqDeviceHandle, AiConfigItem configItem, unsigned int index, long long* configValue); - - UlError ulDaqInScan(DaqDeviceHandle daqDeviceHandle, DaqInChanDescriptor chanDescriptors[], int numChans, int samplesPerChan, double* rate, ScanOption options, DaqInScanFlag flags, double data[]); - UlError ulDaqInScanStatus(DaqDeviceHandle daqDeviceHandle, ScanStatus* status, TransferStatus* xferStatus); - UlError ulDaqInScanStop(DaqDeviceHandle daqDeviceHandle); - UlError ulDaqInScanWait(DaqDeviceHandle daqDeviceHandle, WaitType waitType, long long waitParam, double timeout); - - ctypedef void (*DaqEventCallback)(DaqDeviceHandle, DaqEventType, unsigned long long, void*) - - UlError ulEnableEvent(DaqDeviceHandle daqDeviceHandle, DaqEventType eventTypes, unsigned long long eventParameter, DaqEventCallback eventCallbackFunction, void* userData); - UlError ulDisableEvent(DaqDeviceHandle daqDeviceHandle, DaqEventType eventTypes) - - UlError ulAOutScan(DaqDeviceHandle daqDeviceHandle,int lowChan,int highChan,Range range,int samplesPerChan,double * rate,ScanOption options,AOutScanFlag flags,double data[]) - UlError ulAOutScanStatus(DaqDeviceHandle daqDeviceHandle,ScanStatus * status,TransferStatus * xferStatus) - UlError ulAOutScanStop(DaqDeviceHandle daqDeviceHandle) - UlError ulAOutScanWait(DaqDeviceHandle daqDeviceHandle,WaitType waitType,long long waitParam,double timeout) - UlError ulAOutSetTrigger(DaqDeviceHandle daqDeviceHandle,TriggerType type,int trigChan,double level,double variance,unsigned int retriggerSampleCount) diff --git a/lasp/device/lasp_daq.pyx b/lasp/device/lasp_daq.pyx index 250e801..29ef4cb 100644 --- a/lasp/device/lasp_daq.pyx +++ b/lasp/device/lasp_daq.pyx @@ -10,31 +10,58 @@ DEF MAX_DEF_COUNT = 100 DEF UL_ERR_MSG_LEN = 512 ctypedef unsigned us +ctypedef vector[bool] boolvec cdef extern from "lasp_cppdaq.h" nogil: - cdef cppclass Daq: - void start(SafeQueue[double*] *inQueue, - SafeQueue[double*] *outQueue) + cdef cppclass cppDaq "Daq": + void start(SafeQueue[void*] *inQueue, + SafeQueue[void*] *outQueue) except + void stop() double samplerate() - - -cdef extern from "lasp_cppuldaq.h": - cdef cppclass DT9837A(Daq): - DT9837A(us samplesPerBlock, - vector[bool] inChannels, - vector[bool] outChannels, - double samplerate, - bint monitorOutput, - us deviceno) except + - void start(SafeQueue[double*] *inQueue, - SafeQueue[double*] *outQueue) except + - void stop() except + - void setACCouplingMode(vector[bool] accoupling) except + - void setInputRange(vector[bool] accoupling) except + - void setIEPEEnabled(vector[bool] iepe) except + us neninchannels() us nenoutchannels() + DataType getDataType() + + cdef cppclass DaqApi: + string apiname + unsigned apicode + unsigned api_specific_subcode + + cdef cppclass DataType: + string name + unsigned sw + bool is_floating + DataType dtype_fl64 + + cdef cppclass cppDeviceInfo "DeviceInfo": + DaqApi api + string name + unsigned devindex + vector[DataType] availableDataTypes + vector[double] availableSampleRates + int prefSampleRateIndex + unsigned ninchannels + unsigned noutchannels + bool hasInputIEPE + bool hasInputACCouplingSwitch + bool hasInputTrigger + + cdef cppclass DaqConfiguration: + boolvec eninchannels + boolvec enoutchannels + unsigned sampleRateIndex + DataType datatype + bool monitorOutput + unsigned nFramesPerBlock; + + boolvec inputIEPEEnabled; + boolvec inputACCouplingMode; + boolvec inputHighRange; + + cdef cppclass DaqDevices: + @staticmethod + cppDaq* createDaqDevice(cppDeviceInfo&, DaqConfiguration&) + ctypedef struct PyStreamData: PyObject* pyCallback @@ -55,34 +82,27 @@ ctypedef struct PyStreamData: # If these queue pointers are NULL, it means the stream does not have an # input, or output. - SafeQueue[double*] *inQueue - SafeQueue[double*] *outQueue + SafeQueue[void*] *inQueue + SafeQueue[void*] *outQueue CPPThread[void*, void (*)(void*)] *thread cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil: cdef: - PyStreamData* sd + PyStreamData* sd = voidsd cnp.NPY_TYPES npy_format double* inbuffer = NULL double* outbuffer = NULL - unsigned noutchannels - unsigned ninchannels - unsigned nBytesPerChan - unsigned nFramesPerBlock + unsigned noutchannels= sd.noutchannels + unsigned ninchannels= sd.ninchannels + unsigned nBytesPerChan= sd.nBytesPerChan + unsigned nFramesPerBlock= sd.nFramesPerBlock unsigned sw = sizeof(double) - sd = voidsd - cdef: - double sleeptime = ( nFramesPerBlock)/(4*sd.samplerate); + double sleeptime = ( sd.nFramesPerBlock)/(4*sd.samplerate); us sleeptime_us = (sleeptime*1e6); - ninchannels = sd.ninchannels - noutchannels = sd.noutchannels - - nBytesPerChan = sd.nBytesPerChan - nFramesPerBlock = sd.nFramesPerBlock with gil: npy_format = cnp.NPY_FLOAT64 @@ -97,18 +117,18 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil: outbuffer = malloc(sizeof(double)*nBytesPerChan*noutchannels) npy_output = data_to_ndarray( - outbuffer, - nFramesPerBlock, - noutchannels, - npy_format, - False, # Do not transfer ownership - True) # F-contiguous + outbuffer, + nFramesPerBlock, + noutchannels, + npy_format, + False, # Do not transfer ownership + True) # F-contiguous try: rval = callback(None, - npy_output, - nFramesPerBlock, - ) + npy_output, + nFramesPerBlock, + ) except Exception as e: print('exception in Cython callback for audio output: ', str(e)) @@ -119,24 +139,24 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil: if sd.inQueue and not sd.inQueue.empty(): # Waiting indefinitely on the queue... - inbuffer = sd.inQueue.dequeue() + inbuffer = sd.inQueue.dequeue() if inbuffer == NULL: printf('Stopping thread...\n') return try: npy_input = data_to_ndarray( - inbuffer, - nFramesPerBlock, - ninchannels, - npy_format, - True, # Do transfer ownership - True) # F-contiguous is True: data is Fortran-cont. + inbuffer, + nFramesPerBlock, + ninchannels, + npy_format, + True, # Do transfer ownership + True) # F-contiguous is True: data is Fortran-cont. rval = callback(npy_input, - None, - nFramesPerBlock, - ) + None, + nFramesPerBlock, + ) except Exception as e: print('exception in cython callback for audio input: ', str(e)) @@ -153,10 +173,10 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil: fprintf(stderr, 'Exiting python thread...\n') -cdef class UlDaq: +cdef class Daq: cdef: PyStreamData *sd - Daq* daq_device + cppDaq* daq_device def __cinit__(self): @@ -201,12 +221,12 @@ cdef class UlDaq: unsigned int nFramesPerBlock = daqconfig.nFramesPerBlock unsigned int samplerate - + int i bint in_stream=False bint out_stream=False - DT9837A* daq_device + cppDaq* daq_device if daqconfig.nFramesPerBlock > 8192 or daqconfig.nFramesPerBlock < 512: raise ValueError('Invalid number of nFramesPerBlock') @@ -226,12 +246,12 @@ cdef class UlDaq: if duplex_mode: fprintf(stderr, 'Duplex mode enabled\n') out_stream = True - elif avtype == AvType.audio_output: - sampleformat = daqconfig.en_output_sample_format - samplerate = int(daqconfig.en_output_rate) - out_stream = True - else: - raise ValueError(f'Invalid stream type {avtype}') + elif avtype == AvType.audio_output: + sampleformat = daqconfig.en_output_sample_format + samplerate = int(daqconfig.en_output_rate) + out_stream = True + else: + raise ValueError(f'Invalid stream type {avtype}') if out_stream and daqconfig.firstEnabledOutputChannelNumber() == -1: raise RuntimeError('No output channels enabled') @@ -264,29 +284,29 @@ cdef class UlDaq: inch_enabled = 4*[False] if in_stream: inch_enabled = [True if ch.channel_enabled else False for ch in - daqconfig.getInputChannels()] + daqconfig.getInputChannels()] + + self.sd.inQueue = new SafeQueue[void*]() - self.sd.inQueue = new SafeQueue[double*]() - # Create channel maps for output channels outch_enabled = 1*[False] if out_stream: print('Stream is output stream') outch_enabled = [True if ch.channel_enabled else False for ch in - daqconfig.getOutputChannels()] + daqconfig.getOutputChannels()] - self.sd.outQueue = new SafeQueue[double*]() + self.sd.outQueue = new SafeQueue[void*]() - if 'DT9837A' in device.name: - daq_device = new DT9837A( - daqconfig.nFramesPerBlock, - inch_enabled, - outch_enabled, - samplerate, - monitorOutput, - device.index) - else: - raise RuntimeError(f'Device {device.name} not found or not configured') + # if 'DT9837A' in device.name: + # daq_device = new DT9837A( + # daqconfig.nFramesPerBlock, + # inch_enabled, + # outch_enabled, + # samplerate, + # monitorOutput, + # device.index) + # else: + # raise RuntimeError(f'Device {device.name} not found or not configured') self.sd.pyCallback = avstream._audioCallback self.sd.ninchannels = daq_device.neninchannels() @@ -299,14 +319,14 @@ cdef class UlDaq: with nogil: self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction, - self.sd) + self.sd) # Allow it to start CPPsleep_ms(500) self.daq_device.start( - self.sd.inQueue, - self.sd.outQueue) + self.sd.inQueue, + self.sd.outQueue) return nFramesPerBlock, self.daq_device.samplerate() @@ -342,12 +362,12 @@ cdef class UlDaq: if sd.outQueue: while not sd.outQueue.empty(): free(sd.outQueue.dequeue()) - del sd.outQueue - if sd.inQueue: - while not sd.inQueue.empty(): - free(sd.inQueue.dequeue()) - del sd.inQueue - fprintf(stderr, "End cleanup stream queues...\n") + del sd.outQueue + if sd.inQueue: + while not sd.inQueue.empty(): + free(sd.inQueue.dequeue()) + del sd.inQueue + fprintf(stderr, "End cleanup stream queues...\n") if sd.pyCallback: Py_DECREF( sd.pyCallback) @@ -361,55 +381,56 @@ cdef class UlDaq: devices """ cdef: - DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT] - DaqDeviceDescriptor descriptor - DaqDeviceInterface interfaceType = ANY_IFC - DaqDeviceHandle handle + pass + # DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT] + # DaqDeviceDescriptor descriptor + # DaqDeviceInterface interfaceType = ANY_IFC + # DaqDeviceHandle handle - UlError err + # UlError err - unsigned int numdevs = MAX_DEF_COUNT - unsigned deviceno + # unsigned int numdevs = MAX_DEF_COUNT + # unsigned deviceno - if self.sd is not NULL: - assert self.daq_device is not NULL - raise RuntimeError('Cannot acquire device info: stream is already opened.') + # if self.sd is not NULL: + # assert self.daq_device is not NULL + # raise RuntimeError('Cannot acquire device info: stream is already opened.') - err = ulGetDaqDeviceInventory(interfaceType, - devdescriptors, - &numdevs) - if(err != ERR_NO_ERROR): - raise RuntimeError(f'Device inventarization failed: {err}') + # err = ulGetDaqDeviceInventory(interfaceType, + # devdescriptors, + # &numdevs) + # if(err != ERR_NO_ERROR): + # raise RuntimeError(f'Device inventarization failed: {err}') - - py_devinfo = [] - for deviceno in range(numdevs): - descriptor = devdescriptors[deviceno] - if descriptor.productName == b'DT9837A': - # Create proper interface name - if descriptor.devInterface == DaqDeviceInterface.USB_IFC: - name = 'USB - ' - elif descriptor.devInterface == DaqDeviceInterface.BLUETOOTH_IFC: - name = 'Bluetooth - ' - elif descriptor.devInterface == DaqDeviceInterface.ETHERNET_IFC: - name = 'Ethernet - ' + # py_devinfo = [] + # for deviceno in range(numdevs): + # descriptor = devdescriptors[deviceno] - name += descriptor.productName.decode('utf-8') + ', id ' + \ - descriptor.uniqueId.decode('utf-8') + # if descriptor.productName == b'DT9837A': + # # Create proper interface name + # if descriptor.devInterface == DaqDeviceInterface.USB_IFC: + # name = 'USB - ' + # elif descriptor.devInterface == DaqDeviceInterface.BLUETOOTH_IFC: + # name = 'Bluetooth - ' + # elif descriptor.devInterface == DaqDeviceInterface.ETHERNET_IFC: + # name = 'Ethernet - ' - d = DeviceInfo( - api = -1, - index = deviceno, - probed = True, - name = name, - outputchannels = 1, - inputchannels = 4, - duplexchannels = 0, - samplerates = [10000, 16000, 20000, 32000, 48000, 50000] , - sampleformats = ['64-bit floats'], - prefsamplerate = 48000, - hasInputIEPE = True) - py_devinfo.append(d) - return py_devinfo + # name += descriptor.productName.decode('utf-8') + ', id ' + \ + # descriptor.uniqueId.decode('utf-8') + + # d = DeviceInfo( + # api = -1, + # index = deviceno, + # probed = True, + # name = name, + # outputchannels = 1, + # inputchannels = 4, + # duplexchannels = 0, + # samplerates = [10000, 16000, 20000, 32000, 48000, 50000] , + # sampleformats = ['64-bit floats'], + # prefsamplerate = 48000, + # hasInputIEPE = True) + # py_devinfo.append(d) + # return py_devinfo diff --git a/test/test_uldaq.cpp b/test/test_uldaq.cpp index b2c03ca..e9d45f5 100644 --- a/test/test_uldaq.cpp +++ b/test/test_uldaq.cpp @@ -1,8 +1,10 @@ -#include "lasp_cppuldaq.h" +#include "lasp_cppdaq.h" #include #include #include +#include using std::cout; +using std::cerr; using std::endl; @@ -14,13 +16,35 @@ int main() { double samplerate = 10000; const us samplesPerBlock = 256; - DT9837A daq( - samplesPerBlock, - inChannels, - outChannels, - samplerate, - true // monitor Output - ); + 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; @@ -42,19 +66,16 @@ int main() { outqueue.enqueue(data); } - daq.start(&inqueue, &outqueue); - /* daq.start(NULL, &outqueue); */ + daq->start(&inqueue, &outqueue); std::this_thread::sleep_for(std::chrono::seconds((int) totalTime)); - /* std::string a; */ - /* std::cin >> a; */ - daq.stop(); + daq->stop(); while(!inqueue.empty()) { double* buf = (double*) inqueue.dequeue(); for(us i=0;ineninchannels();ch++) { cout << buf[ch*samplesPerBlock+i] << " "; } cout << endl;