Working C++ multiple API sit

This commit is contained in:
Anne de Jong 2020-10-10 18:28:43 +02:00
parent a43857070b
commit 29662c82e3
14 changed files with 784 additions and 735 deletions

View File

@ -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_daq.pyx PROPERTIES CYTHON_IS_CXX TRUE)
set_source_files_properties(lasp_deviceinfo.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 set_source_files_properties(lasp_daq.cxx PROPERTIES COMPILE_FLAGS
"${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_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 set_source_files_properties(lasp_deviceinfo.cxx PROPERTIES COMPILE_FLAGS
"${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_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_daq lasp_daq.pyx)
cython_add_module(lasp_deviceinfo lasp_deviceinfo.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_daq cpp_daq uldaq rtaudio pthread)
target_link_libraries(lasp_deviceinfo 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) if(win32)
target_link_libraries(lasp_daq python37) target_link_libraries(lasp_daq python37)
target_link_libraries(lasp_deviceinfo python37)
target_link_libraries(lasp_daqconfig python37)
endif(win32) endif(win32)

View File

@ -1,6 +1,6 @@
#!/usr/bin/python3 #!/usr/bin/python3
from .lasp_daqconfig import * from .lasp_device_common import *
from .lasp_avtype import *
from .lasp_daq import * from .lasp_daq import *
from .lasp_deviceinfo import * from .lasp_deviceinfo import *
from .lasp_daqconfig import *

View File

@ -39,16 +39,10 @@ cdef extern from "lasp_pyarray.h":
ctypedef unsigned us ctypedef unsigned us
ctypedef vector[bool] boolvec ctypedef vector[bool] boolvec
ctypedef vector[double] dvec
ctypedef vector[us] usvec
cdef extern from "lasp_cppdaq.h" nogil: 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: cdef cppclass DaqApi:
string apiname string apiname
@ -63,21 +57,27 @@ cdef extern from "lasp_cppdaq.h" nogil:
cdef cppclass cppDeviceInfo "DeviceInfo": cdef cppclass cppDeviceInfo "DeviceInfo":
DaqApi api DaqApi api
string name string device_name
unsigned devindex unsigned devindex
vector[DataType] availableDataTypes vector[DataType] availableDataTypes
vector[double] availableSampleRates vector[double] availableSampleRates
vector[us] availableFramesPerBlock
int prefSampleRateIndex int prefSampleRateIndex
int prefInputRangeIndex
int prefFramesPerBlockIndex
unsigned ninchannels unsigned ninchannels
unsigned noutchannels unsigned noutchannels
bool hasInputIEPE bool hasInputIEPE
bool hasInputACCouplingSwitch bool hasInputACCouplingSwitch
bool hasInputTrigger bool hasInputTrigger
vector[double] inputRanges vector[double] availableInputRanges
cdef cppclass DaqConfiguration: cdef cppclass DaqConfiguration:
boolvec eninchannels boolvec eninchannels
boolvec enoutchannels boolvec enoutchannels
vector[string] channel_names
vector[double] channel_sensitivities
unsigned sampleRateIndex unsigned sampleRateIndex
DataType datatype DataType datatype
bool monitorOutput bool monitorOutput
@ -85,12 +85,19 @@ cdef extern from "lasp_cppdaq.h" nogil:
boolvec inputIEPEEnabled; boolvec inputIEPEEnabled;
boolvec inputACCouplingMode; 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 @staticmethod
cppDaq* createDaqDevice(cppDeviceInfo&, DaqConfiguration&) cppDaq* createDaqDevice(cppDeviceInfo&, DaqConfiguration&)
@staticmethod @staticmethod
vector[cppDeviceInfo] getDeviceInfo() vector[cppDeviceInfo] getDeviceInfo()

View File

@ -1,6 +1,9 @@
#include "lasp_cppdaq.h" #include "lasp_cppdaq.h"
#include "lasp_cppuldaq.h" #include "lasp_cppuldaq.h"
#include <algorithm>
#include <cassert>
#include <iostream> #include <iostream>
#include <strstream>
using std::cout; using std::cout;
using std::endl; using std::endl;
@ -11,7 +14,6 @@ void fillUlDaqDeviceInfo(vector<DeviceInfo> &devinfolist) {
UlError err; UlError err;
unsigned int numdevs = MAX_DEV_COUNT_PER_API; unsigned int numdevs = MAX_DEV_COUNT_PER_API;
unsigned deviceno;
DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API]; DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API];
DaqDeviceDescriptor descriptor; DaqDeviceDescriptor descriptor;
@ -43,13 +45,11 @@ void fillUlDaqDeviceInfo(vector<DeviceInfo> &devinfolist) {
name += string(descriptor.productName) + " "; name += string(descriptor.productName) + " ";
name += string(descriptor.uniqueId); 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.availableDataTypes.push_back(dtype_fl64);
devinfo.prefDataTypeIndex = 0;
devinfo.ninchannels = 4;
devinfo.noutchannels = 1;
devinfo.availableSampleRates.push_back(10000); devinfo.availableSampleRates.push_back(10000);
devinfo.availableSampleRates.push_back(12000); devinfo.availableSampleRates.push_back(12000);
@ -61,17 +61,31 @@ void fillUlDaqDeviceInfo(vector<DeviceInfo> &devinfolist) {
devinfo.prefSampleRateIndex = 5; 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.hasInputIEPE = true;
devinfo.hasInputACCouplingSwitch = true; devinfo.hasInputACCouplingSwitch = true;
devinfo.hasInputTrigger = true; devinfo.hasInputTrigger = true;
// Finally, this devinfo is pushed back in list
devinfolist.push_back(devinfo); devinfolist.push_back(devinfo);
} }
} }
} }
#endif #endif
vector<DeviceInfo> DaqDevices::getDeviceInfo() { vector<DeviceInfo> Daq::getDeviceInfo() {
vector<DeviceInfo> devs; vector<DeviceInfo> devs;
#ifdef HAS_ULDAQ_LINUX_API #ifdef HAS_ULDAQ_LINUX_API
fillUlDaqDeviceInfo(devs); fillUlDaqDeviceInfo(devs);
@ -89,8 +103,57 @@ vector<DeviceInfo> DaqDevices::getDeviceInfo() {
return devs; return devs;
} }
Daq *DaqDevices::createDevice(const DeviceInfo &devinfo, DaqConfiguration::DaqConfiguration(const DeviceInfo &device) {
const DaqConfiguration &config) {
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<DeviceInfo> &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 // Some basic sanity checks
if ((devinfo.ninchannels != config.eninchannels.size())) { if ((devinfo.ninchannels != config.eninchannels.size())) {
@ -107,3 +170,52 @@ Daq *DaqDevices::createDevice(const DeviceInfo &devinfo,
devinfo.api.apiname); 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);
}

View File

@ -1,6 +1,5 @@
#ifndef LASP_CPPDAQ_H #ifndef LASP_CPPDAQ_H
#define LASP_CPPDAQ_H #define LASP_CPPDAQ_H
#include <strstream>
#ifdef HAS_RTAUDIO #ifdef HAS_RTAUDIO
#include <RtAudio.h> #include <RtAudio.h>
@ -13,12 +12,19 @@
#include "lasp_cppqueue.h" #include "lasp_cppqueue.h"
#include "string" #include "string"
#include "vector" #include "vector"
#include <mutex>
using std::cerr;
using std::cout;
using std::endl;
using std::runtime_error;
using std::string; using std::string;
using std::vector; using std::vector;
using std::runtime_error;
typedef unsigned int us; typedef unsigned int us;
typedef vector<bool> boolvec; typedef vector<bool> boolvec;
typedef vector<double> dvec;
typedef vector<us> usvec;
typedef std::lock_guard<std::mutex> mutexlock;
class DataType { class DataType {
public: public:
@ -28,10 +34,7 @@ class DataType {
DataType(const char *name, unsigned sw, bool is_floating) DataType(const char *name, unsigned sw, bool is_floating)
: name(name), sw(sw), is_floating(is_floating) {} : name(name), sw(sw), is_floating(is_floating) {}
DataType(): DataType() : name("invalid data type"), sw(0), is_floating(false) {}
name("invalid data type"),
sw(0),
is_floating(false) {}
}; };
const DataType dtype_invalid; const DataType dtype_invalid;
@ -90,83 +93,116 @@ const vector<DaqApi> compiledApis = {
class DeviceInfo { class DeviceInfo {
public: public:
DaqApi api; DaqApi api;
string name = ""; string device_name = "";
unsigned devindex;
int api_specific_devindex = -1;
vector<DataType> availableDataTypes; vector<DataType> availableDataTypes;
vector<double> availableSampleRates; int prefDataTypeIndex = 0;
vector<us> availableFramesPerBlock;
vector<double> availableSampleRates;
int prefSampleRateIndex = -1; int prefSampleRateIndex = -1;
vector<us> availableFramesPerBlock;
unsigned prefFramesPerBlockIndex = 0;
dvec availableInputRanges;
int prefInputRangeIndex = 0;
unsigned ninchannels = 0; unsigned ninchannels = 0;
unsigned noutchannels = 0; unsigned noutchannels = 0;
bool hasInputIEPE = false; bool hasInputIEPE = false;
bool hasInputACCouplingSwitch = false; bool hasInputACCouplingSwitch = false;
bool hasInputTrigger = false; bool hasInputTrigger = false;
vector<double> inputRanges;
/* DeviceInfo(): */ /* DeviceInfo(): */
/* datatype(dtype_invalid) { } */ /* datatype(dtype_invalid) { } */
double prefSampleRate() const { double prefSampleRate() const {
if ((prefSampleRateIndex < availableSampleRates.size()) && if (((us) prefSampleRateIndex < availableSampleRates.size()) &&
(prefSampleRateIndex >= 0)) { (prefSampleRateIndex >= 0)) {
return availableSampleRates[prefSampleRateIndex]; return availableSampleRates[prefSampleRateIndex];
} else { } else {
throw std::runtime_error("No prefered sample rate available"); throw std::runtime_error("No prefered sample rate available");
} }
} }
operator string() const { operator string() const {
std::stringstream str; std::stringstream str;
str << api.apiname + " " << devindex << str << api.apiname + " " << api_specific_devindex
" number of input channels: " << ninchannels << << " number of input channels: " << ninchannels
" number of output channels: " << noutchannels; << " number of output channels: " << noutchannels;
return str.str(); return str.str();
} }
}; };
// Device configuration parameters // Device configuration parameters
class DaqConfiguration { class DaqConfiguration {
public: public:
DaqApi api;
string device_name;
boolvec eninchannels; // Enabled input channels boolvec eninchannels; // Enabled input channels
boolvec enoutchannels; // Enabled output channels boolvec enoutchannels; // Enabled output channels
unsigned sampleRateIndex; // Index in list of sample rates vector<double> inchannel_sensitivities;
DataType vector<string> inchannel_names;
datatype = dtype_invalid; // Required datatype for output, should be present in the list
bool monitorOutput; // Whether the first output channel should be replicated // This is not necessary at the moment
/* vector<double> outchannel_sensitivities; */
vector<string> 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 // to the input as the first channel
unsigned nFramesPerBlock;
boolvec inputIEPEEnabled; boolvec inputIEPEEnabled;
boolvec inputACCouplingMode; boolvec inputACCouplingMode;
boolvec inputHighRange;
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 Daq;
class DaqDevices { class Daq : public DaqConfiguration,public DeviceInfo {
mutable std::mutex mutex;
public: public:
static vector<DeviceInfo> getDeviceInfo(); static vector<DeviceInfo> getDeviceInfo();
static Daq *createDevice(const DeviceInfo &, const DaqConfiguration &config);
};
class Daq { static Daq *createDevice(const DaqConfiguration &config,
const std::vector<DeviceInfo> &devinfos);
Daq(const DeviceInfo &devinfo, const DaqConfiguration &config);
public:
virtual void start(SafeQueue<void *> *inqueue, virtual void start(SafeQueue<void *> *inqueue,
SafeQueue<void *> *outqueue) = 0; SafeQueue<void *> *outqueue) = 0;
virtual void stop() = 0; virtual void stop() = 0;
virtual double samplerate() const = 0;
virtual DataType getDataType() const = 0; virtual bool isRunning() const = 0;
virtual ~Daq(){}; virtual ~Daq(){};
virtual us neninchannels() const = 0; us neninchannels() const;
virtual us nenoutchannels() const = 0; us nenoutchannels() const;
double samplerate() const;
double inputRangeForChannel(us ch) const;
DataType dataType() const;
}; };
#endif // LASP_CPPDAQ_H #endif // LASP_CPPDAQ_H

View File

@ -1,75 +1,18 @@
#include "lasp_cppuldaq.h" #include "lasp_cppuldaq.h"
#include <algorithm>
#include <atomic> #include <atomic>
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <cstring>
#include <iostream> #include <iostream>
#include <mutex>
#include <stdexcept>
#include <thread> #include <thread>
#include <uldaq.h> #include <uldaq.h>
#include <vector> #include <vector>
using std::cerr; using std::atomic;
using std::cout;
using std::endl;
using std::runtime_error;
typedef std::lock_guard<std::mutex> mutexlock;
/* using std::this_thread; */
/* using std::this_thread; */
const us MAX_DEF_COUNT = 100; const us MAX_DEF_COUNT = 100;
const us UL_ERR_MSG_LEN = 512; 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<bool> stopThread;
DaqDeviceHandle handle = 0;
std::thread *thread = NULL;
mutable std::mutex mutex;
SafeQueue<void *> *inqueue = NULL;
SafeQueue<void *> *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<void *> *inqueue,
SafeQueue<void *> *outqueue) final;
virtual void stop() final;
DataType getDataType() const final { return dtype_fl64; }
friend void threadfcn(DT9837A *);
};
inline void showErr(UlError err) { inline void showErr(UlError err) {
if (err != ERR_NO_ERROR) { if (err != ERR_NO_ERROR) {
char errmsg[UL_ERR_MSG_LEN]; char errmsg[UL_ERR_MSG_LEN];
@ -78,34 +21,36 @@ inline void showErr(UlError err) {
} }
} }
DT9837A::DT9837A(us samplesPerBlock, const boolvec &inChannels, class DT9837A;
const boolvec &outChannels, double samplerate, void threadfcn(DT9837A *td);
bool monitorOutput, us deviceno)
: samplesPerBlock(samplesPerBlock), _samplerate(samplerate), class DT9837A : public Daq {
inChannels(inChannels), outChannels(outChannels),
monitorOutput(monitorOutput) { atomic<bool> stopThread;
if (monitorOutput && !(nenoutchannels() > 0)) { DaqDeviceHandle handle = 0;
throw runtime_error(
"Output monitoring only possible when output is enabled"); std::thread *thread = NULL;
} SafeQueue<void *> *inqueue = NULL;
SafeQueue<void *> *outqueue = NULL;
double *inbuffer = NULL;
double *outbuffer = NULL;
us nFramesPerBlock;
public:
DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config)
: Daq(devinfo, config) {
stopThread = false; stopThread = false;
high_range = boolvec({false, false, false, false});
if (samplesPerBlock < 24 || samplesPerBlock > 8192) { nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex];
if (nFramesPerBlock < 24 || nFramesPerBlock > 8192) {
throw runtime_error("Unsensible number of samples per block chosen"); throw runtime_error("Unsensible number of samples per block chosen");
} }
// Some sanity checks if (samplerate() < 10000 || samplerate() > 51000) {
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"); throw runtime_error("Invalid sample rate");
} }
@ -121,12 +66,13 @@ DT9837A::DT9837A(us samplesPerBlock, const boolvec &inChannels,
throw runtime_error("Device inventarization failed"); throw runtime_error("Device inventarization failed");
} }
if (deviceno >= numdevs) { if ((us) api_specific_devindex >= numdevs) {
throw runtime_error("Device number {deviceno} too high {err}. This could " throw runtime_error("Device number {deviceno} too high {err}. This could "
"happen when the device is currently not connected"); "happen when the device is currently not connected");
} }
descriptor = devdescriptors[deviceno]; descriptor = devdescriptors[api_specific_devindex];
// get a handle to the DAQ device associated with the first descriptor // get a handle to the DAQ device associated with the first descriptor
handle = ulCreateDaqDevice(descriptor); handle = ulCreateDaqDevice(descriptor);
@ -144,15 +90,32 @@ DT9837A::DT9837A(us samplesPerBlock, const boolvec &inChannels,
} }
for (us ch = 0; ch < 4; ch++) { for (us ch = 0; ch < 4; ch++) {
err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0); err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0);
showErr(err); showErr(err);
if (err != ERR_NO_ERROR) { if (err != ERR_NO_ERROR) {
throw runtime_error("Fatal: could normalize channel sensitivity"); 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::~DT9837A() { DT9837A(const DT9837A &) = delete;
~DT9837A() {
UlError err; UlError err;
if (isRunning()) { if (isRunning()) {
stop(); stop();
@ -164,68 +127,72 @@ DT9837A::~DT9837A() {
err = ulReleaseDaqDevice(handle); err = ulReleaseDaqDevice(handle);
showErr(err); showErr(err);
} }
} }
void DT9837A::setIEPEEnabled(const boolvec &config) { bool isRunning() const { return bool(thread); }
void start(SafeQueue<void *> *inqueue, SafeQueue<void *> *outqueue) {
if (isRunning()) { if (isRunning()) {
throw runtime_error("Cannot change config while sampling is running"); throw runtime_error("Thread is already running");
} }
// Some sanity checks bool hasinput = neninchannels() > 0;
if (config.size() != 4) { bool hasoutput = nenoutchannels() > 0;
throw runtime_error("Invalid length of enabled IEPE config vector");
if (neninchannels() > 0 && !inqueue) {
throw runtime_error("Inqueue not given, while input is enabled");
} }
IepeMode iepe; if (nenoutchannels() > 0 && !outqueue) {
UlError err; throw runtime_error("outqueue not given, while output is enabled");
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 (hasinput) {
if (coupling.size() != 4) { assert(!inbuffer);
throw runtime_error("Invalid length of enabled AC coupling mode vector"); inbuffer =
new double[neninchannels() * nFramesPerBlock * 2]; // Watch the 2!
}
if (hasoutput) {
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
* **************************" << endl; */
/* boolvec couplingmode = {true, true, true, true}; */
/* setACCouplingMode(couplingmode); */
stopThread = false;
thread = new std::thread(threadfcn, this);
} }
CouplingMode cm; void stop() {
UlError err; if (!isRunning()) {
throw runtime_error("No data acquisition running");
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");
} }
} assert(thread);
}
void DT9837A::setInputRange(const boolvec &high_range) { stopThread = true;
if (isRunning()) { thread->join();
throw runtime_error("Cannot change config while sampling is running"); delete thread;
thread = NULL;
outqueue = NULL;
inqueue = NULL;
if (inbuffer)
delete inbuffer;
if (outbuffer)
delete outbuffer;
outbuffer = NULL;
inbuffer = NULL;
} }
// Some sanity checks friend void threadfcn(DT9837A *);
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) {
@ -233,7 +200,7 @@ void threadfcn(DT9837A *td) {
const us nenoutchannels = td->nenoutchannels(); const us nenoutchannels = td->nenoutchannels();
us neninchannels = td->neninchannels(); us neninchannels = td->neninchannels();
const us samplesPerBlock = td->samplesPerBlock; const us nFramesPerBlock = td->nFramesPerBlock;
const bool hasinput = neninchannels > 0; const bool hasinput = neninchannels > 0;
const bool hasoutput = nenoutchannels > 0; const bool hasoutput = nenoutchannels > 0;
@ -252,16 +219,16 @@ void threadfcn(DT9837A *td) {
double samplerate = td->samplerate(); double samplerate = td->samplerate();
const double sleeptime = ((double)samplesPerBlock) / (4 * samplerate); const double sleeptime = ((double)nFramesPerBlock) / (4 * samplerate);
const us sleeptime_us = (us)(sleeptime * 1e6); const us sleeptime_us = (us)(sleeptime * 1e6);
/* cerr << "Sleep time in loop: " << sleeptime_us << "us." << endl; */ /* 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; cerr << "ERROR: Too small buffer size (nFramesPerBlock) chosen!" << endl;
return; return;
} }
const us buffer_mid_idx_in = neninchannels * samplesPerBlock; const us buffer_mid_idx_in = neninchannels * nFramesPerBlock;
const us buffer_mid_idx_out = nenoutchannels * samplesPerBlock; const us buffer_mid_idx_out = nenoutchannels * nFramesPerBlock;
DaqDeviceHandle handle = td->handle; DaqDeviceHandle handle = td->handle;
assert(handle); assert(handle);
@ -289,14 +256,14 @@ void threadfcn(DT9837A *td) {
assert(outqueue); assert(outqueue);
// Initialize the buffer with zeros, before pushing any data. // Initialize the buffer with zeros, before pushing any data.
for (us sample = 0; sample < 2 * samplesPerBlock; sample++) { for (us sample = 0; sample < 2 * nFramesPerBlock; sample++) {
outbuffer[sample] = 0; outbuffer[sample] = 0;
} }
cerr << "Starting output DAC" << endl; cerr << "Starting output DAC" << endl;
err = ulAOutScan(handle, 0, 0, BIP10VOLTS, err = ulAOutScan(handle, 0, 0, BIP10VOLTS,
/* BIP60VOLTS, */ /* BIP60VOLTS, */
2 * td->samplesPerBlock, // Watch the 2 here! 2 * td->nFramesPerBlock, // Watch the 2 here!
&samplerate, scanoptions, outscanflags, outbuffer); &samplerate, scanoptions, outscanflags, outbuffer);
if (err != ERR_NO_ERROR) { if (err != ERR_NO_ERROR) {
@ -307,14 +274,24 @@ void threadfcn(DT9837A *td) {
// Initialize input, if any // Initialize input, if any
if (hasinput) { if (hasinput) {
indesc = new DaqInChanDescriptor[neninchannels]; indesc = new DaqInChanDescriptor[neninchannels];
us j = 0; us j = 0;
for (us chin = 0; chin < 4; chin++) { for (us chin = 0; chin < 4; chin++) {
if (td->inChannels[chin] == true) { if (td->eninchannels[chin] == true) {
indesc[j].type = DAQI_ANALOG_SE; indesc[j].type = DAQI_ANALOG_SE;
indesc[j].channel = chin; indesc[j].channel = chin;
indesc[j].range = td->high_range[chin] ? BIP10VOLTS : BIP1VOLTS;
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++; j++;
} }
} }
@ -329,7 +306,7 @@ void threadfcn(DT9837A *td) {
cerr << "Starting input ADC" << endl; cerr << "Starting input ADC" << endl;
err = ulDaqInScan(handle, indesc, neninchannels, err = ulDaqInScan(handle, indesc, neninchannels,
2 * td->samplesPerBlock, // Watch the 2 here! 2 * td->nFramesPerBlock, // Watch the 2 here!
&samplerate, scanoptions, inscanflags, inbuffer); &samplerate, scanoptions, inscanflags, inbuffer);
if (err != ERR_NO_ERROR) { if (err != ERR_NO_ERROR) {
showErr(err); showErr(err);
@ -360,7 +337,7 @@ void threadfcn(DT9837A *td) {
} }
assert(outscanstat == SS_RUNNING); assert(outscanstat == SS_RUNNING);
if (outxstat.currentScanCount > outTotalCount + 2 * samplesPerBlock) { if (outxstat.currentScanCount > outTotalCount + 2 * nFramesPerBlock) {
cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count=" cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count="
<< outxstat.currentScanCount << outxstat.currentScanCount
<< " while loop count = " << outTotalCount << " while loop count = " << outTotalCount
@ -368,7 +345,8 @@ void threadfcn(DT9837A *td) {
} }
outTotalCount = outxstat.currentScanCount; outTotalCount = outxstat.currentScanCount;
/* std::cerr << "Samples scanned: " << outxstat.currentTotalCount << endl; /* std::cerr << "Samples scanned: " << outxstat.currentTotalCount <<
* endl;
*/ */
if (outxstat.currentIndex < buffer_mid_idx_out) { if (outxstat.currentIndex < buffer_mid_idx_out) {
topoutenqueued = false; topoutenqueued = false;
@ -382,13 +360,13 @@ void threadfcn(DT9837A *td) {
"QUEUE WITH ZEROS ***********" "QUEUE WITH ZEROS ***********"
<< endl; << endl;
bufcpy = static_cast<double *>( bufcpy = static_cast<double *>(
malloc(sizeof(double) * samplesPerBlock * nenoutchannels)); malloc(sizeof(double) * nFramesPerBlock * nenoutchannels));
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[sample] = 0; bufcpy[sample] = 0;
} }
} }
assert(nenoutchannels > 0); assert(nenoutchannels > 0);
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample]; outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample];
} }
free(bufcpy); free(bufcpy);
@ -406,13 +384,13 @@ void threadfcn(DT9837A *td) {
"QUEUE WITH ZEROS ***********" "QUEUE WITH ZEROS ***********"
<< endl; << endl;
bufcpy = static_cast<double *>( bufcpy = static_cast<double *>(
malloc(sizeof(double) * samplesPerBlock * nenoutchannels)); malloc(sizeof(double) * nFramesPerBlock * nenoutchannels));
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[sample] = 0; bufcpy[sample] = 0;
} }
} }
assert(nenoutchannels > 0); assert(nenoutchannels > 0);
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
outbuffer[sample] = bufcpy[sample]; outbuffer[sample] = bufcpy[sample];
} }
free(bufcpy); free(bufcpy);
@ -428,10 +406,11 @@ void threadfcn(DT9837A *td) {
goto exit; goto exit;
} }
assert(inscanstat == SS_RUNNING); assert(inscanstat == SS_RUNNING);
if (inxstat.currentScanCount > inTotalCount + 2 * samplesPerBlock) { if (inxstat.currentScanCount > inTotalCount + 2 * nFramesPerBlock) {
cerr << "***** ERROR: Missing input sample blocks, count=" cerr << "***** ERROR: Missing input sample blocks, count="
<< inxstat.currentScanCount << inxstat.currentScanCount
<< ", probably due to too small buffer size. Exiting thread. *****" << ", probably due to too small buffer size. Exiting thread. "
"*****"
<< endl; << endl;
break; break;
} }
@ -442,13 +421,13 @@ void threadfcn(DT9837A *td) {
if (!botinenqueued) { if (!botinenqueued) {
/* cerr << "Copying in buffer bot" << endl; */ /* cerr << "Copying in buffer bot" << endl; */
double *bufcpy = static_cast<double *>( double *bufcpy = static_cast<double *>(
malloc(sizeof(double) * samplesPerBlock * neninchannels)); malloc(sizeof(double) * nFramesPerBlock * neninchannels));
us monitoroffset = monitorOutput ? 1 : 0; us monitoroffset = monitorOutput ? 1 : 0;
assert(neninchannels > 0); assert(neninchannels > 0);
for (us channel = 0; channel < (neninchannels - monitoroffset); for (us channel = 0; channel < (neninchannels - monitoroffset);
channel++) { channel++) {
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[(monitoroffset + channel) * samplesPerBlock + sample] = bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] =
inbuffer[buffer_mid_idx_in + sample * neninchannels + inbuffer[buffer_mid_idx_in + sample * neninchannels +
channel]; channel];
} }
@ -457,7 +436,7 @@ void threadfcn(DT9837A *td) {
// Monitor output goes to first channel, that is // Monitor output goes to first channel, that is
// our convention // our convention
us channel = neninchannels - 1; us channel = neninchannels - 1;
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[sample] = inbuffer[buffer_mid_idx_in + bufcpy[sample] = inbuffer[buffer_mid_idx_in +
sample * neninchannels + channel]; sample * neninchannels + channel];
} }
@ -469,13 +448,13 @@ void threadfcn(DT9837A *td) {
botinenqueued = false; botinenqueued = false;
if (!topinenqueued) { if (!topinenqueued) {
double *bufcpy = static_cast<double *>( double *bufcpy = static_cast<double *>(
malloc(sizeof(double) * samplesPerBlock * neninchannels)); malloc(sizeof(double) * nFramesPerBlock * neninchannels));
us monitoroffset = monitorOutput ? 1 : 0; us monitoroffset = monitorOutput ? 1 : 0;
assert(neninchannels > 0); assert(neninchannels > 0);
for (us channel = 0; channel < (neninchannels - monitoroffset); for (us channel = 0; channel < (neninchannels - monitoroffset);
channel++) { channel++) {
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[(monitoroffset + channel) * samplesPerBlock + sample] = bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] =
inbuffer[sample * neninchannels + channel]; inbuffer[sample * neninchannels + channel];
} }
} }
@ -483,7 +462,7 @@ void threadfcn(DT9837A *td) {
// Monitor output goes to first channel, that is // Monitor output goes to first channel, that is
// our convention // our convention
us channel = neninchannels - 1; us channel = neninchannels - 1;
for (us sample = 0; sample < samplesPerBlock; sample++) { for (us sample = 0; sample < nFramesPerBlock; sample++) {
bufcpy[sample] = inbuffer[sample * neninchannels + channel]; bufcpy[sample] = inbuffer[sample * neninchannels + channel];
} }
} }
@ -521,92 +500,13 @@ exit:
std::cerr << "Exit of DAQ thread fcn" << endl; std::cerr << "Exit of DAQ thread fcn" << endl;
} }
void DT9837A::start(SafeQueue<void *> *inqueue, SafeQueue<void *> *outqueue) {
if (isRunning()) {
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");
}
if (nenoutchannels() > 0 && !outqueue) {
throw runtime_error("outqueue not given, while output is enabled");
}
if (hasinput) {
assert(!inbuffer);
inbuffer =
new double[neninchannels() * samplesPerBlock * 2]; // Watch the 2!
}
if (hasoutput) {
assert(!outbuffer);
outbuffer =
new double[nenoutchannels() * samplesPerBlock * 2]; // Watch the 2!
}
this->inqueue = inqueue;
this->outqueue = outqueue;
/* 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()) {
throw runtime_error("No data acquisition running");
}
assert(thread);
stopThread = true;
thread->join();
delete thread;
thread = NULL;
outqueue = NULL;
inqueue = NULL;
if (inbuffer)
delete inbuffer;
if (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;
}
us DT9837A::nenoutchannels() const {
mutexlock lock(mutex);
return std::count(outChannels.begin(), outChannels.end(), true);
}
Daq *createUlDaqDevice(const DeviceInfo &devinfo, Daq *createUlDaqDevice(const DeviceInfo &devinfo,
const DaqConfiguration &config) { const DaqConfiguration &config) {
DT9837A *daq = NULL; 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); try {
daq->setIEPEEnabled(config.inputIEPEEnabled); daq = new DT9837A(devinfo, config);
daq->setInputRange(config.inputHighRange);
} catch (runtime_error &e) { } catch (runtime_error &e) {
if (daq) if (daq)

View File

@ -2,14 +2,6 @@
#define ULDAQ_H #define ULDAQ_H
#include "lasp_cppqueue.h" #include "lasp_cppqueue.h"
#include "lasp_cppdaq.h" #include "lasp_cppdaq.h"
#include <uldaq.h>
#include <algorithm>
#include <vector>
#include <mutex>
#include <atomic>
#include <thread>
using std::atomic;
Daq* createUlDaqDevice(const DeviceInfo& devinfo, Daq* createUlDaqDevice(const DeviceInfo& devinfo,
const DaqConfiguration& config); const DaqConfiguration& config);

View File

@ -1,8 +1,6 @@
cimport cython cimport cython
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
from .lasp_daqconfig import (Range as pyRange, from .lasp_device_common import AvType
DAQChannel)
from .lasp_avtype import AvType
__all__ = ['Daq'] __all__ = ['Daq']

View File

View File

@ -8,10 +8,7 @@ cdef class DeviceInfo:
def api(self): return self.devinfo.api.apiname.decode('utf-8') def api(self): return self.devinfo.api.apiname.decode('utf-8')
@property @property
def name(self): return self.devinfo.name.decode('utf-8') def name(self): return self.devinfo.device_name.decode('utf-8')
@property
def devindex(self): return self.devinfo.devindex
@property @property
def ninchannels(self): return self.devinfo.ninchannels def ninchannels(self): return self.devinfo.ninchannels
@ -47,7 +44,7 @@ cdef class DeviceInfo:
@property @property
def inputRanges(self): def inputRanges(self):
return self.devinfo.inputRanges return self.devinfo.availableInputRanges
@staticmethod @staticmethod
def getDeviceInfo(): def getDeviceInfo():
@ -60,7 +57,7 @@ cdef class DeviceInfo:
us numdevs, devno us numdevs, devno
cppDeviceInfo* devinfo cppDeviceInfo* devinfo
devinfos = DaqDevices.getDeviceInfo() devinfos = cppDaq.getDeviceInfo()
numdevs = devinfos.size() numdevs = devinfos.size()
pydevinfos = [] pydevinfos = []

View File

@ -8,8 +8,11 @@ from .lasp_atomic import Atomic
from threading import Thread, Condition, Lock from threading import Thread, Condition, Lock
import numpy as np import numpy as np
class DAQConfiguration:
pass
import time import time
from .device import (Daq, DeviceInfo, DAQConfiguration, from .device import (Daq, DeviceInfo,
# get_numpy_dtype_from_format_string, # get_numpy_dtype_from_format_string,
# get_sampwidth_from_format_string, # get_sampwidth_from_format_string,
AvType, AvType,

View File

@ -11,25 +11,7 @@ using std::endl;
int main() { int main() {
/* boolvec inChannels = {true, false, false, false}; */ /* boolvec inChannels = {true, false, false, false}; */
boolvec inChannels = {true, true, false, false}; auto devinfos = Daq::getDeviceInfo();
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: " <<config.eninchannels.size() << endl;
vector<DeviceInfo> devinfos = DaqDevices::getDeviceInfo();
DeviceInfo devinfo; DeviceInfo devinfo;
us i; us i;
bool found = false; bool found = false;
@ -44,7 +26,21 @@ int main() {
throw runtime_error("Could not find UlDaq device"); throw runtime_error("Could not find UlDaq device");
} }
Daq* daq = DaqDevices::createDevice(devinfo, config); DaqConfiguration config(devinfos[0]);
boolvec inChannels = {true, true, false, false};
boolvec outChannels = {true};
double samplerate = 10000;
const us samplesPerBlock = 256;
config.eninchannels = inChannels;
config.enoutchannels = outChannels;
config.sampleRateIndex = 0;
config.monitorOutput = true;
config.inputIEPEEnabled = {false, false, false, false};
cout << "Inchannnels size: " << config.eninchannels.size() << endl;
Daq* daq = Daq::createDevice(config, devinfos);
SafeQueue<void*> inqueue; SafeQueue<void*> inqueue;
SafeQueue<void*> outqueue; SafeQueue<void*> outqueue;