Moved whole threading code with UlDaq to C++

This commit is contained in:
Anne de Jong 2020-09-22 19:32:00 +02:00
parent 248fa8d8a4
commit 108e023026
8 changed files with 820 additions and 720 deletions

View File

@ -1,4 +1,7 @@
include_directories(/usr/include/rtaudio)
add_library(cpp_daq lasp_cppuldaq.cpp)
set_source_files_properties(lasp_rtaudio.pyx PROPERTIES CYTHON_IS_CXX TRUE)
set_source_files_properties(lasp_uldaq.pyx PROPERTIES CYTHON_IS_CXX TRUE)
set_source_files_properties(lasp_rtaudio.cxx PROPERTIES COMPILE_FLAGS
@ -8,8 +11,11 @@ cython_add_module(lasp_rtaudio lasp_rtaudio.pyx)
cython_add_module(lasp_uldaq lasp_uldaq.pyx)
target_link_libraries(lasp_rtaudio pthread rtaudio)
target_link_libraries(lasp_uldaq uldaq)
target_link_libraries(lasp_uldaq cpp_daq uldaq pthread)
if(win32)
target_link_libraries(lasp_rtaudio python37)
target_link_libraries(lasp_uldaq python37)
endif(win32)
add_executable(test_uldaq test_uldaq.cpp)
target_link_libraries(test_uldaq cpp_daq uldaq pthread)

18
lasp/device/lasp_cppdaq.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef LAP_CPPDAQ_H
#define LAP_CPPDAQ_H
#include "lasp_cppqueue.h"
class Daq{
public:
virtual void start(
SafeQueue<double*> *inqueue,
SafeQueue<double*> *outqueue) = 0;
virtual void stop() = 0;
virtual double samplerate() const = 0;
virtual ~Daq() {};
};
#endif // LAP_CPPDAQ_H

View File

View File

@ -0,0 +1,547 @@
#include "lasp_cppuldaq.h"
#include <stdexcept>
#include <uldaq.h>
#include <cassert>
#include <iostream>
#include <cstring>
#include <chrono>
using std::endl;
using std::cerr;
using std::runtime_error;
typedef std::lock_guard<std::mutex> mutexlock;
/* using std::this_thread; */
const us MAX_DEF_COUNT = 100;
const us UL_ERR_MSG_LEN = 512;
inline void showErr(UlError err) {
if(err != ERR_NO_ERROR) {
char errmsg[UL_ERR_MSG_LEN];
ulGetErrMsg(err, errmsg);
std::cerr << "UlError: " << errmsg << std::endl;
}
}
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");
}
stopThread = false;
high_range = boolvec({false, false, false, false});
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(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;
UlError err;
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");
}
descriptor = devdescriptors[deviceno];
// get a handle to the DAQ device associated with the first descriptor
handle = ulCreateDaqDevice(descriptor);
if (handle == 0) {
throw runtime_error("Unable to create a handle to the specified DAQ device. Is the device currently in use?");
}
err = ulConnectDaqDevice(handle);
if (err != ERR_NO_ERROR) {
showErr(err);
ulReleaseDaqDevice(handle);
handle = 0;
throw runtime_error("Unable to connect to device: {err}");
}
for(us ch=0; ch<4; ch++) {
err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0);
showErr(err);
if(err != ERR_NO_ERROR) {
throw runtime_error("Fatal: could normalize channel sensitivity");
}
}
}
DT9837A::~DT9837A() {
UlError err;
if(isRunning()) {
stop();
}
if(handle) {
err = ulDisconnectDaqDevice(handle);
showErr(err);
err = ulReleaseDaqDevice(handle);
showErr(err);
}
}
void DT9837A::setIEPEEnabled(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");
}
IepeMode iepe;
UlError err;
for(us i=0; i< config.size(); i++) {
iepe = config[i] ? IEPE_ENABLED : IEPE_DISABLED;
err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, i, iepe);
if(err != ERR_NO_ERROR) {
showErr(err);
throw runtime_error("Fatal: could not set IEPE mode");
}
}
}
void DT9837A::setACCouplingMode(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");
}
CouplingMode cm;
UlError err;
for(us i=0; i< coupling.size(); i++) {
cm = coupling[i] ? CM_AC : CM_DC;
err = ulAISetConfig(handle, AI_CFG_CHAN_COUPLING_MODE, i, cm);
if(err != ERR_NO_ERROR) {
showErr(err);
throw runtime_error("Fatal: could not set IEPE mode");
}
}
}
void DT9837A::setInputRange(boolvec& high_range) {
if(isRunning()) {
throw runtime_error("Cannot change config while sampling is running");
}
// Some sanity checks
if(high_range.size() != 4) {
throw runtime_error("Invalid length of enabled high range vector");
}
this->high_range = high_range;
}
/* static inline void copysamples_transpose(const us samplesPerBlock,const us nchannels, */
/* double* from, double* to, */
/* const us from_startidx, */
/* const us to_startidx, */
/* const bool touldaqbuffer) { */
/* // Copy from "our type of buffer" to an uldaq buffer */
/* if(touldaqbuffer) { */
/* for(us sample=0;sample< samplesPerBlock;sample++) { */
/* for(us channel=0;channel<nchannels;channel++) { */
/* to[to_startidx+sample*nchannels + channel] = from[from_startidx + channel*samplesPerBlock+ sample]; */
/* } */
/* } */
/* } */
/* // Copy from an uldaq buffer to our type of buffer */
/* else { */
/* for(us sample=0;sample< samplesPerBlock;sample++) { */
/* for(us channel=0;channel<nchannels;channel++) { */
/* to[to_startidx + channel*samplesPerBlock+ sample] = from[from_startidx+sample*nchannels + channel]; */
/* } */
/* } */
/* } */
/* } */
void threadfcn(DT9837A* td) {
std::cerr << "Starting DAQ Thread fcn" << endl;
const us nenoutchannels = td->nenoutchannels();
us neninchannels = td->neninchannels();
const us samplesPerBlock = td->samplesPerBlock;
const bool hasinput = neninchannels > 0;
const bool hasoutput = nenoutchannels > 0;
bool monitorOutput = td->monitorOutput;
double* inbuffer = td->inbuffer;
double* outbuffer = td->outbuffer;
SafeQueue<double*>* inqueue = td->inqueue;
SafeQueue<double*>* outqueue = td->outqueue;
ScanStatus inscanstat;
ScanStatus outscanstat;
TransferStatus inxstat, outxstat;
double samplerate = td->samplerate();
const us buffer_mid_idx_in = neninchannels*samplesPerBlock;
const us buffer_mid_idx_out = nenoutchannels*samplesPerBlock;
DaqDeviceHandle handle = td->handle;
assert(handle);
DaqInScanFlag inscanflags = DAQINSCAN_FF_DEFAULT;
AOutScanFlag outscanflags = AOUTSCAN_FF_DEFAULT;
ScanOption scanoptions = SO_CONTINUOUS;
UlError err = ERR_NO_ERROR;
DaqInChanDescriptor *indesc = NULL;
bool topinenqueued = false;
// Start with true here, to not copy here the first time
bool botinenqueued = true;
bool topoutenqueued = false;
bool botoutenqueued = false;
std::cerr << "SFSG" << endl;
// outitialize output, if any
if(hasoutput) {
assert(nenoutchannels == 1);
assert(outqueue);
double *firstout;
if(outqueue->empty()) {
cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL QUEUE WITH ZEROS ***********" << endl;
firstout = static_cast<double*>(malloc(sizeof(double)*samplesPerBlock*nenoutchannels));
for(us sample=0;sample< samplesPerBlock;sample++) {
firstout[sample] = 0;
}
}
else {
firstout = outqueue->dequeue();
}
for(us sample=0;sample<2*samplesPerBlock;sample++) {
outbuffer[sample] = firstout[sample];
}
memcpy(td->outbuffer, firstout, samplesPerBlock*nenoutchannels);
/* free(firstout); */
topoutenqueued = true;
err = ulAOutScan(handle,
0,
0,
BIP10VOLTS,
/* BIP60VOLTS, */
2*td->samplesPerBlock, // Watch the 2 here!
&samplerate,
scanoptions,
outscanflags,
outbuffer);
if(err != ERR_NO_ERROR) {
showErr(err);
goto exit;
}
}
// Initialize input, if any
if(hasinput) {
indesc = new DaqInChanDescriptor[neninchannels];
us j = 0;
for(us chin=0; chin < 4; chin++) {
if(td->inChannels[chin] == true) {
indesc[j].type = DAQI_ANALOG_SE;
indesc[j].channel = chin;
indesc[j].range = td->high_range[chin] ? BIP10VOLTS : BIP1VOLTS;
j++;
}
}
// Overwrite last channel
if(monitorOutput) {
indesc[j].type = DAQI_DAC;
indesc[j].channel = 0;
indesc[j].range = BIP10VOLTS;
j++;
}
assert(j==neninchannels);
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;
}
}
std::cerr << "Entering while loop" << endl;
std::cerr << "hasinput: " << hasinput << endl;
while(!td->stopThread && err == ERR_NO_ERROR) {
/* std::cerr << "While..." << endl; */
if(hasoutput) {
err = ulAOutScanStatus(handle, &outscanstat, &outxstat);
if(err != ERR_NO_ERROR) {
showErr(err);
goto exit;
}
assert(outscanstat == SS_RUNNING);
/* std::cerr << "Samples scanned: " << outxstat.currentTotalCount << endl; */
if(outxstat.currentIndex < buffer_mid_idx_out) {
topoutenqueued = false;
if(!botoutenqueued) {
cerr << "Copying output buffer to bottom" << endl;
double* bufcpy;
if(!outqueue->empty()) {
bufcpy = outqueue->dequeue();
}
else {
cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL QUEUE WITH ZEROS ***********" << endl;
bufcpy = static_cast<double*>(malloc(sizeof(double)*samplesPerBlock*nenoutchannels));
for(us sample=0;sample< samplesPerBlock;sample++) {
bufcpy[sample] = 0;
}
}
assert(nenoutchannels > 0);
for(us sample=0;sample< samplesPerBlock;sample++) {
outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample];
}
free(bufcpy);
botoutenqueued = true;
}
}
else {
botoutenqueued = false;
if(!topoutenqueued) {
topoutenqueued = true;
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<double*>(malloc(sizeof(double)*samplesPerBlock*nenoutchannels));
for(us sample=0;sample< samplesPerBlock;sample++) {
bufcpy[sample] = 0;
}
}
assert(nenoutchannels > 0);
for(us sample=0;sample<samplesPerBlock;sample++) {
outbuffer[sample] = bufcpy[sample];
}
free(bufcpy);
topoutenqueued = true;
}
}
}
if(hasinput) {
err = ulDaqInScanStatus(handle, &inscanstat, &inxstat);
if(err != ERR_NO_ERROR) {
showErr(err);
goto exit;
}
assert(inscanstat == SS_RUNNING);
if(inxstat.currentIndex < buffer_mid_idx_in) {
topinenqueued = false;
if(!botinenqueued) {
cerr << "Copying in buffer bot" << endl;
double* bufcpy = static_cast<double*>(malloc(sizeof(double)*samplesPerBlock*neninchannels));
us monitoroffset = monitorOutput ? 1: 0;
assert(neninchannels > 0);
for(us channel=0;channel<(neninchannels-monitoroffset);channel++) {
for(us sample=0;sample< samplesPerBlock;sample++) {
bufcpy[(monitoroffset+channel)*samplesPerBlock+sample] = inbuffer[buffer_mid_idx_in + sample*neninchannels+channel];
}
}
if(monitorOutput) {
// Monitor output goes to first channel, that is
// our convention
us channel = neninchannels - 1;
for(us sample=0;sample< samplesPerBlock;sample++) {
bufcpy[sample] = inbuffer[buffer_mid_idx_in + sample*neninchannels+channel];
}
}
inqueue->enqueue(bufcpy);
botinenqueued = true;
}
}
else {
botinenqueued = false;
if(!topinenqueued) {
double* bufcpy = static_cast<double*>(malloc(sizeof(double)*samplesPerBlock*neninchannels));
us monitoroffset = monitorOutput ? 1: 0;
assert(neninchannels > 0);
for(us channel=0;channel<(neninchannels-monitoroffset);channel++) {
for(us sample=0;sample< samplesPerBlock;sample++) {
bufcpy[(monitoroffset+channel)*samplesPerBlock+sample] = inbuffer[sample*neninchannels+channel];
}
}
if(monitorOutput) {
// Monitor output goes to first channel, that is
// our convention
us channel = neninchannels - 1;
for(us sample=0;sample< samplesPerBlock;sample++) {
bufcpy[sample] = inbuffer[sample*neninchannels+channel];
}
}
cerr << "Copying in buffer top" << endl;
inqueue->enqueue(bufcpy);
topinenqueued = true;
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} // End of while loop
std::cerr << "Exit of while loop" << endl;
exit:
if(hasoutput) {
ulAOutScanStop(handle);
if(err != ERR_NO_ERROR) {
showErr(err);
}
}
if(hasinput) {
ulDaqInScanStop(handle);
if(err != ERR_NO_ERROR) {
showErr(err);
}
}
if(indesc) delete indesc;
std::cerr << "Exit of thread fcn" << endl;
}
void DT9837A::start( SafeQueue<double*> *inqueue, SafeQueue<double*> *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];
}
if(hasoutput) {
assert(!outbuffer);
outbuffer = new double[nenoutchannels()*samplesPerBlock*2];
}
this->inqueue = inqueue;
this->outqueue = outqueue;
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);
}

View File

@ -0,0 +1,73 @@
#ifndef ULDAQ_THREAD_H
#define ULDAQ_THREAD_H
#include "lasp_cppqueue.h"
#include "lasp_cppdaq.h"
#include <uldaq.h>
#include <algorithm>
#include <vector>
#include <mutex>
#include <atomic>
#include <thread>
using std::vector;
using std::atomic;
typedef vector<bool> boolvec;
typedef unsigned int us;
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<double*> *inqueue = NULL;
SafeQueue<double*> *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<double*> *inqueue,
SafeQueue<double*> *outqueue) final;
virtual void stop() final;
friend void threadfcn(DT9837A*);
};
#endif // ULDAQ_THREAD_H

View File

@ -1,6 +1,5 @@
include "lasp_common_decls.pxd"
cdef extern from "uldaq.h" nogil:
ctypedef enum DaqDeviceInterface:

View File

@ -4,634 +4,37 @@ from .lasp_daqconfig import (DeviceInfo, InputMode, Range as pyRange,
DAQChannel)
from .lasp_avtype import AvType
__all__ = ['UlDT9837A', 'UlDaq']
__all__ = ['UlDaq']
DEF MAX_DEF_COUNT = 100
DEF UL_ERR_MSG_LEN = 512
cdef struct DaqThreadData:
ctypedef unsigned us
unsigned int samplesPerBlock
double samplerate
cdef extern from "lasp_cppdaq.h":
cdef cppclass Daq:
void start(SafeQueue[double*] *inQueue,
SafeQueue[double*] *outQueue)
void stop()
double samplerate()
DaqDeviceHandle handle
SafeQueue[void*] *inQueue
SafeQueue[void*] *outQueue
DaqInChanDescriptor* inChanDescriptors
unsigned ninChanDescriptors
unsigned noutChanDescriptors
atomic[bool] stopThread
atomic[bool] outThread_ready
mutex* tdmutex
CPPThread[void*, void (*)(void*)] *inThread
CPPThread[void*, void (*)(void*)] *outThread
double* inbuffer
double* outbuffer
cdef void showErr(UlError err) nogil:
cdef:
char errmsg[UL_ERR_MSG_LEN]
ulGetErrMsg(err, errmsg)
fprintf(stderr, 'UlError: %s\n', errmsg)
cdef void inThreadFunction(void* threaddata_void) nogil:
"""
Stream input thread function
"""
cdef:
DaqThreadData* td = <DaqThreadData*> threaddata_void
UlError err
ScanOption scanoptions
DaqInScanFlag inscanflags
TransferStatus xstat
ScanStatus scanstat
double samplerate
bint top_enqueued, bottom_enqueued
unsigned buffer_mid_idx, chan, sample
double sleep_time
double *inbuffer_cpy
unsigned sleep_time_mus # Thread sleep time in ms
samplerate = td.samplerate
sleep_time = td.samplesPerBlock / td.samplerate / 2
sleep_time_mus = <unsigned> (sleep_time * 1e6)
inscanflags = DAQINSCAN_FF_DEFAULT
scanoptions = <ScanOption> SO_CONTINUOUS
buffer_mid_idx = td.samplesPerBlock*td.ninChanDescriptors
top_enqueued = True
bottom_enqueued = True
fprintf(stderr, 'Starting input thread\n')
while not td.outThread_ready.load():
CPPsleep_ms(50)
td.tdmutex.lock()
err = ulDaqInScan(td.handle,
td.inChanDescriptors,
td.ninChanDescriptors,
2*td.samplesPerBlock, # Watch the 2 here!
&samplerate,
scanoptions,
inscanflags,
td.inbuffer)
td.tdmutex.unlock()
fprintf(stderr, 'Actual input sampling rate: %0.2f\n', samplerate)
if err != ERR_NO_ERROR:
fprintf(stderr, 'Error starting data in\n')
showErr(err)
return
td.tdmutex.lock()
err = ulDaqInScanStatus(td.handle, &scanstat, &xstat)
td.tdmutex.unlock()
if err != ERR_NO_ERROR:
fprintf(stderr, 'Error obtaining input scan status\n')
showErr(err)
return
while td.stopThread.load() == False and err == ERR_NO_ERROR:
td.tdmutex.lock()
err = ulDaqInScanStatus(td.handle, &scanstat, &xstat)
td.tdmutex.unlock()
if err != ERR_NO_ERROR:
showErr(err)
break
if xstat.currentIndex < buffer_mid_idx:
top_enqueued = False
if not bottom_enqueued:
# Copy the bottom of the buffer to the queue, while transposing
# it.
inbuffer_cpy = <double*> malloc(sizeof(double)*td.samplesPerBlock*td.ninChanDescriptors)
for chan in range(td.ninChanDescriptors):
for sample in range(td.samplesPerBlock):
inbuffer_cpy[chan*td.samplesPerBlock+sample] = td.inbuffer[buffer_mid_idx+sample*td.ninChanDescriptors+chan]
td.inQueue.enqueue(inbuffer_cpy)
bottom_enqueued = True
else:
# fprintf(stderr,'sleep...\n')
CPPsleep_us(sleep_time_mus)
# fprintf(stderr,'awake...\n')
else:
bottom_enqueued = False
if not top_enqueued:
inbuffer_cpy = <double*> malloc(sizeof(double)*td.samplesPerBlock*td.ninChanDescriptors)
for chan in range(td.ninChanDescriptors):
for sample in range(td.samplesPerBlock):
inbuffer_cpy[chan*td.samplesPerBlock+sample] = td.inbuffer[sample*td.ninChanDescriptors+chan]
td.inQueue.enqueue(inbuffer_cpy)
# Enqueue the top part of the queue
top_enqueued = True
else:
# fprintf(stderr,'sleep...\n')
CPPsleep_us(sleep_time_mus)
# fprintf(stderr,'awake...\n')
fprintf(stderr, 'Exit while loop input thread\n')
td.tdmutex.lock()
err = ulDaqInScanStop(td.handle)
td.tdmutex.unlock()
if err != ERR_NO_ERROR:
fprintf(stderr, "Error stopping DAQ input thread\n")
showErr(err)
return
cdef void outThreadFunction(void* threaddata_void) nogil:
"""
Stream output thread function
"""
cdef:
DaqThreadData* td = <DaqThreadData*> threaddata_void
UlError err
ScanOption scanoptions
AOutScanFlag outscanflags
TransferStatus xstat
ScanStatus scanstat
bint top_enqueued, bottom_enqueued
unsigned buffer_mid_idx, chan, sample
double sleep_time
double *outbuffer_cpy
unsigned sleep_time_mus # Thread sleep time in ms
double samplerate
samplerate = td.samplerate
sleep_time = td.samplesPerBlock / td.samplerate / 2
sleep_time_mus = <unsigned> (sleep_time * 1e6)
# CAREFULL, a MEMCPY here is only allowed for a single channel!!
if not td.noutChanDescriptors == 1:
fprintf(stderr, 'Error: multiple output channels not implemented!\n')
return
# Copy first queue blob to top of outbuffer
outbuffer_cpy = <double*> td.outQueue.dequeue()
memcpy(td.outbuffer, outbuffer_cpy, sizeof(double)*td.samplesPerBlock)
free(outbuffer_cpy)
outscanflags = AOUTSCAN_FF_DEFAULT
scanoptions = <ScanOption> SO_CONTINUOUS
buffer_mid_idx = td.samplesPerBlock
top_enqueued = True
bottom_enqueued = False
fprintf(stderr, 'Starting output thread\n')
td.tdmutex.lock()
fprintf(stderr, 'mutex locked\n')
err = ulAOutScan(td.handle,
0,
0,
BIP10VOLTS,
2*td.samplesPerBlock, # Watch the 2 here!
&samplerate,
scanoptions,
outscanflags,
td.outbuffer)
fprintf(stderr, 'returned\n')
td.tdmutex.unlock()
fprintf(stderr, 'Actual output sampling rate: %0.2f\n', samplerate)
if err != ERR_NO_ERROR:
fprintf(stderr, 'Error starting data out\n')
showErr(err)
return
td.tdmutex.lock()
err = ulAOutScanStatus(td.handle, &scanstat, &xstat)
td.tdmutex.unlock()
if err != ERR_NO_ERROR:
showErr(err)
return
td.outThread_ready.store(True)
while td.stopThread.load() == False:
# printf('Running output thread in loop\n')
td.tdmutex.lock()
err = ulAOutScanStatus(td.handle, &scanstat, &xstat)
td.tdmutex.unlock()
if err != ERR_NO_ERROR:
showErr(err)
break
if xstat.currentIndex < buffer_mid_idx:
top_enqueued = False
# fprintf('xstat.currentIndex'
if not bottom_enqueued:
# Copy the bottom of the buffer to the queue, while transposing
# it.
if not td.outQueue.empty():
outbuffer_cpy = <double*> td.outQueue.dequeue()
else:
# outbuffer_cpy = <double*> malloc(sizeof(double)*td.noutChanDescriptors*td.samplesPerBlock)
outbuffer_cpy = <double*> malloc(sizeof(double)*td.samplesPerBlock)
# for chan in range(td.noutChanDescriptors):
# for sample in range(td.samplesPerBlock):
# outbuffer_cpy[chan*td.samplesPerBlock+sample] = 0.0
for sample in range(td.samplesPerBlock):
outbuffer_cpy[sample] = 0.0
fprintf(stderr, 'Output buffer queue empty! Filling with zeros\n')
for chan in range(td.noutChanDescriptors):
for sample in range(td.samplesPerBlock):
# td.outbuffer[buffer_mid_idx+sample*td.noutChanDescriptors+chan] = outbuffer_cpy[chan*td.samplesPerBlock+sample]
# Simpler, valid for single chan
td.outbuffer[buffer_mid_idx+sample] = outbuffer_cpy[sample]
free(outbuffer_cpy)
bottom_enqueued = True
else:
CPPsleep_us(sleep_time_mus)
else:
bottom_enqueued = False
if not top_enqueued:
if not td.outQueue.empty():
outbuffer_cpy = <double*> td.outQueue.dequeue()
else:
outbuffer_cpy = <double*> malloc(sizeof(double)*td.noutChanDescriptors*td.samplesPerBlock)
# for chan in range(td.noutChanDescriptors):
# for sample in range(td.samplesPerBlock):
# outbuffer_cpy[chan*td.samplesPerBlock+sample] = 0.0
for sample in range(td.samplesPerBlock):
outbuffer_cpy[sample] = 0.0
fprintf(stderr, 'Output buffer queue empty! Filling with zeros\n')
for chan in range(td.noutChanDescriptors):
for sample in range(td.samplesPerBlock):
# td.outbuffer[sample*td.noutChanDescriptors+chan] = outbuffer_cpy[chan*td.samplesPerBlock+sample]
# Simpler, valid for single chan
td.outbuffer[sample] = outbuffer_cpy[sample]
free(outbuffer_cpy)
# Enqueue the top part of the queue
top_enqueued = True
else:
CPPsleep_us(sleep_time_mus)
fprintf(stderr, 'Exit while loop output thread\n')
td.tdmutex.lock()
err = ulAOutScanStop(td.handle)
td.tdmutex.unlock()
if err != ERR_NO_ERROR:
fprintf(stderr, "Error stopping AOut output thread\n")
showErr(err)
cdef class UlDT9837A:
cdef:
DaqDeviceHandle handle
bint handle_connected
unsigned int ninchannels
unsigned int noutchannels
object input_range
object enabled_inputs
bint out_enabled
bint monitor_gen
DaqThreadData *td
def __cinit__(self, unsigned int deviceno):
self.handle = 0
self.handle_connected = False
self.ninchannels = 4
self.noutchannels = 1
self.input_range = 4*[False]
self.enabled_inputs = 4*[False]
self.out_enabled = False
self.monitor_gen = False
self.td = NULL
cdef:
DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]
DaqDeviceDescriptor descriptor
DaqDeviceInterface interfaceType = ANY_IFC
UlError err
unsigned int numdevs = MAX_DEF_COUNT
err = ulGetDaqDeviceInventory(interfaceType,
devdescriptors,
&numdevs)
if(err != ERR_NO_ERROR):
raise RuntimeError(f'Device inventarization failed: {err}')
if deviceno >= numdevs:
raise ValueError(f'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
self.handle = ulCreateDaqDevice(descriptor);
if self.handle == 0:
raise RuntimeError ("Unable to create a handle to the specified DAQ device. Is the device currently in use?");
err = ulConnectDaqDevice(self.handle)
if err != ERR_NO_ERROR:
raise RuntimeError(f'Unable to connect to device: {err}')
cdef void startScan(self,
unsigned int samplesPerBlock,
double samplerate,
SafeQueue[void*] *inQueue,
SafeQueue[void*] *outQueue):
cdef:
int i, j, nnormalinchannels, neninchannels
# Sanity checks
if inQueue and (not any(self.enabled_inputs) and (not self.monitor_gen)):
raise ValueError('Input queue given, but no input channels enabled and monitor output disabled')
if outQueue and not self.out_enabled:
raise ValueError('Output queue given, but output channel is not enabled')
# End sanity checks
cdef:
DaqThreadData *td
td = <DaqThreadData*> malloc(sizeof(DaqThreadData))
if not td:
raise MemoryError()
with nogil:
td.samplesPerBlock = samplesPerBlock
td.samplerate = <double> samplerate
td.handle = self.handle
td.inQueue = inQueue
td.outQueue = outQueue
td.inChanDescriptors = NULL
td.ninChanDescriptors = 0
td.noutChanDescriptors = 0
td.inThread = NULL
td.outThread = NULL
td.stopThread.store(False)
td.tdmutex = new mutex()
td.inbuffer = NULL
td.outbuffer = NULL
if not outQueue:
td.outThread_ready.store(True)
else:
td.outThread_ready.store(False)
# Configure INPUTS
py_nnormalinchannels = sum([1 if en_input else 0 for en_input in
self.enabled_inputs])
nnormalinchannels = py_nnormalinchannels
j = 0
neninchannels = nnormalinchannels
if self.monitor_gen:
neninchannels += 1
fprintf(stderr, 'neninchannels: %u\n', neninchannels)
if neninchannels > 0:
td.inChanDescriptors = <DaqInChanDescriptor*> malloc(neninchannels*
sizeof(DaqInChanDescriptor))
if not td.inChanDescriptors:
self.cleanupThreadData(td)
raise MemoryError()
td.ninChanDescriptors = neninchannels
td.inbuffer = <double*> malloc(2*sizeof(double)*td.samplesPerBlock*
neninchannels)
if not td.inbuffer:
self.cleanupThreadData(td)
raise MemoryError()
j = 0
for i in range(4):
if self.enabled_inputs[i]:
td.inChanDescriptors[j].type = DAQI_ANALOG_SE
td.inChanDescriptors[j].channel = i
td.inChanDescriptors[j].range = BIP10VOLTS if self.input_range[i] else BIP1VOLTS
j+=1
td.inbuffer = <double*> malloc(2*sizeof(double)*td.samplesPerBlock*neninchannels)
if not td.inbuffer:
self.cleanupThreadData(td)
raise MemoryError()
# Create input channel descriptors
if self.monitor_gen:
td.inChanDescriptors[neninchannels-1].type = DAQI_DAC
td.inChanDescriptors[neninchannels-1].channel = 0
td.inChanDescriptors[neninchannels-1].range = BIP10VOLTS
fprintf(stderr, 'SFSG12\n')
# CONFIGURE OUTPUTS
if self.out_enabled:
fprintf(stderr, 'SFSG13\n')
td.noutChanDescriptors = 1
# WATCH THE TWO HERE!!!
td.outbuffer = <double*> malloc(2*sizeof(double)*td.samplesPerBlock)
if not td.outbuffer:
self.cleanupThreadData(td)
raise MemoryError()
# Create DAQ Threads
with nogil:
fprintf(stderr, 'SFSG14\n')
if self.out_enabled:
td.outThread = new CPPThread[void*, void (*)(void*)](
outThreadFunction, <void*> td)
if not td.outThread:
self.cleanupThreadData(td)
raise RuntimeError('Error creating thread')
if neninchannels > 0:
fprintf(stderr, 'SFSG15\n')
td.inThread = new CPPThread[void*, void (*)(void*)](
inThreadFunction, <void*> td)
if not td.inThread:
self.cleanupThreadData(td)
raise RuntimeError('Error creating thread')
self.td = td
# def start(self):
# cdef:
# SafeQueue[void*] *inqueue
# SafeQueue[void*] *outqueue
# int i, sample, samples, samplesperbuf
# double meas_seconds, samplerate
# double* inbuf
# double* outbuf
# inqueue = new SafeQueue[void*]()
# outqueue = new SafeQueue[void*]()
# samplesperbuf = 512
# samplerate = 10000
# meas_seconds = 4
# for i in range(int(meas_seconds*samplerate/samplesperbuf)):
# outbuf = <double*> malloc(sizeof(double)*samplesperbuf)
# for sample in range(samplesperbuf):
# outbuf[sample] = <double> 1
# outqueue.enqueue(<void*> outbuf)
# self.startScan(
# samplesperbuf,
# samplerate, # Sample rate
# inqueue,
# outqueue)
# CPPsleep_ms(int(0.5*1000*meas_seconds))
# self.stop()
# while not inqueue.empty():
# inbuf = <double*> inqueue.dequeue()
# for sample in range(samplesperbuf):
# print(f'Value monitor: {inbuf[sample]:1.2f}, value input: {inbuf[samplesperbuf+sample]:1.2f}')
# pass
# free(inbuf)
# while not outqueue.empty():
# outbuf = <double*> outqueue.dequeue()
# free(outbuf)
# del inqueue
# del outqueue
def stopScan(self):
print(f'UlDT9837A: stopScan()')
self.cleanupThreadData(self.td)
self.td = NULL
def setInputChannelConfig(self, unsigned chnum, channelconfig: DAQChannel):
if self.td:
raise RuntimeError('Cannot change settings while sampling')
cdef:
int i
UlError err
IepeMode iepe
CouplingMode cm
if chnum > 3:
raise RuntimeError('Invalid input channel number')
# if chnum == 0:
# fprintf(stderr, '====================== BIG WARNING ==============\n')
# fprintf(stderr, 'We override IEPE to enabled on ch 0\n')
# channelconfig.IEPE_enabled = True
# fprintf(stderr, '====================== END BIG WARNING ==============\n')
self.input_range[chnum] = True if channelconfig.range_ == pyRange.tenV else False
self.enabled_inputs[chnum] = channelconfig.channel_enabled
iepe = IEPE_ENABLED if channelconfig.IEPE_enabled else IEPE_DISABLED
cm = CM_AC if channelconfig.IEPE_enabled else CM_DC
err = ulAISetConfig(self.handle, AI_CFG_CHAN_IEPE_MODE, chnum,
iepe)
if err != ERR_NO_ERROR:
raise RuntimeError('Fatal: could not set IEPE mode')
err = ulAISetConfig(self.handle, AI_CFG_CHAN_COUPLING_MODE, chnum, cm);
if err != ERR_NO_ERROR:
raise RuntimeError('Fatal: could not set coupling mode')
# err = ulAISetConfigDbl(self.handle, AI_CFG_CHAN_SENSOR_SENSITIVITY,
# chnum, channelconfig.sensitivity)
err = ulAISetConfigDbl(self.handle, AI_CFG_CHAN_SENSOR_SENSITIVITY,
chnum, 1.0)
# TODO: Fix this problem, of setting sensitivity twice, how do we do it
# in the future?
if err != ERR_NO_ERROR:
raise RuntimeError('Fatal: could not set sensitivity')
def setOutputChannelConfig(self, unsigned chnum, channelconfig:
DAQChannel, bint monitor_gen):
if self.td:
raise RuntimeError('Cannot change settings while sampling')
if chnum > 0:
raise RuntimeError('Invalid output channel number')
if monitor_gen and not channelconfig.channel_enabled:
raise RuntimeError('Output channel should be enabled to enable channel monitoring')
self.out_enabled = channelconfig.channel_enabled
self.monitor_gen = monitor_gen
def __dealloc__(self):
fprintf(stderr, '__dealloc__\n')
self.cleanupThreadData(self.td)
self.td = NULL
if self.handle_connected:
ulDisconnectDaqDevice(self.handle)
ulReleaseDaqDevice(self.handle)
cdef void cleanupThreadData(self, DaqThreadData* td) nogil:
fprintf(stderr, 'cleanupThreadData()\n')
if td is NULL:
printf('TD is zero\n')
return
td.stopThread.store(True)
if td.inThread:
td.inThread.join()
del td.inThread
fprintf(stderr, 'SFSG0\n')
if td.outThread:
td.outThread.join()
del td.outThread
fprintf(stderr, 'SFSG1\n')
del td.tdmutex
if td.inChanDescriptors:
free(td.inChanDescriptors)
fprintf(stderr, 'SFSG1\n')
if td.inbuffer:
free(td.inbuffer)
fprintf(stderr, 'SFSG2\n')
if td.outbuffer:
free(td.outbuffer)
fprintf(stderr, 'SFSG3\n')
free(td)
fprintf(stderr, 'end cleanupThreadData()\n')
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)
void start(SafeQueue[double*] *inQueue,
SafeQueue[double*] *outQueue)
void stop()
void setACCouplingMode(vector[bool] accoupling)
void setInputRange(vector[bool] accoupling)
void setIEPEEnabled(vector[bool] iepe)
us neninchannels()
us nenoutchannels()
ctypedef struct PyStreamData:
PyObject* pyCallback
@ -650,8 +53,8 @@ ctypedef struct PyStreamData:
# If these queue pointers are NULL, it means the stream does not have an
# input, or output.
SafeQueue[void*] *inQueue
SafeQueue[void*] *outQueue
SafeQueue[double*] *inQueue
SafeQueue[double*] *outQueue
CPPThread[void*, void (*)(void*)] *thread
cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
@ -659,8 +62,8 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
PyStreamData* sd
cnp.NPY_TYPES npy_format
void* inbuffer = NULL
void* outbuffer = NULL
double* inbuffer = NULL
double* outbuffer = NULL
unsigned noutchannels
unsigned ninchannels
@ -669,7 +72,6 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
unsigned sw = sizeof(double)
sd = <PyStreamData*> voidsd
ninchannels = sd.ninchannels
noutchannels = sd.noutchannels
@ -677,7 +79,6 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
nBytesPerChan = sd.nBytesPerChan
nFramesPerBlock = sd.nFramesPerBlock
with gil:
npy_format = cnp.NPY_FLOAT64
callback = <object> sd.pyCallback
@ -687,7 +88,7 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
while not sd.stopThread.load():
if sd.outQueue:
outbuffer = malloc(nBytesPerChan*noutchannels)
outbuffer = <double*> malloc(sizeof(double)*nBytesPerChan*noutchannels)
if sd.inQueue:
if not sd.outQueue:
@ -745,7 +146,7 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
return
if sd.outQueue:
sd.outQueue.enqueue(outbuffer)
sd.outQueue.enqueue(<double*> outbuffer)
if not sd.inQueue:
while sd.outQueue.size() > 10 and not sd.stopThread.load():
# printf('Sleeping...\n')
@ -762,7 +163,7 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
cdef class UlDaq:
cdef:
PyStreamData *sd
UlDT9837A daq_device
Daq* daq_device
def __cinit__(self):
@ -770,68 +171,9 @@ cdef class UlDaq:
Acquires a daq handle, and opens the device
"""
self.daq_device = None
self.daq_device = NULL
self.sd = NULL
def getDeviceInfo(self):
"""
Returns device information objects (DeviceInfo) for all available
devices
"""
cdef:
DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]
DaqDeviceDescriptor descriptor
DaqDeviceInterface interfaceType = ANY_IFC
DaqDeviceHandle handle
UlError err
unsigned int numdevs = MAX_DEF_COUNT
unsigned deviceno
if self.sd is not NULL or self.daq_device is not None:
assert self.daq_device is not None
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}')
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 - '
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
@cython.nonecheck(True)
def start(self, avstream):
"""
@ -844,7 +186,7 @@ cdef class UlDaq:
"""
if self.sd is not NULL:
assert self.daq_device is not None
assert self.daq_device is not NULL
raise RuntimeError('Stream is already opened.')
daqconfig = avstream.daqconfig
@ -862,6 +204,8 @@ cdef class UlDaq:
bint in_stream=False
bint out_stream=False
DT9837A* daq_device
if daqconfig.nFramesPerBlock > 8192 or daqconfig.nFramesPerBlock < 512:
raise ValueError('Invalid number of nFramesPerBlock')
@ -911,47 +255,46 @@ cdef class UlDaq:
self.sd.nBytesPerChan = daqconfig.nFramesPerBlock*sizeof(double)
self.sd.nFramesPerBlock = daqconfig.nFramesPerBlock
if 'DT9837A' in device.name:
self.daq_device = UlDT9837A(device.index)
else:
raise RuntimeError(f'Device {device.name} not found or not configured')
# Create channel maps for in channels, set in stream
# parameters
inch_enabled = 4*False
if in_stream:
print('Stream is input stream')
for i, ch in enumerate(daqconfig.getInputChannels()):
if ch.channel_enabled:
self.sd.ninchannels += 1
self.daq_device.setInputChannelConfig(i, ch)
self.sd.inQueue = new SafeQueue[void*]()
inch_enabled = [True if ch.channel_enabled else False for ch in
daqconfig.getInputChannels()]
self.sd.inQueue = new SafeQueue[double*]()
# Create channel maps for output channels
outch_enabled = 4*False
if out_stream:
print('Stream is output stream')
for i, ch in enumerate(daqconfig.getOutputChannels()):
if ch.channel_enabled:
self.sd.noutchannels += 1
self.daq_device.setOutputChannelConfig(i, ch, monitorOutput)
outch_enabled = [True if ch.channel_enabled else False for ch in
daqconfig.getOutputChannels()]
self.sd.outQueue = new SafeQueue[void*]()
self.sd.outQueue = new SafeQueue[double*]()
if monitorOutput and duplex_mode:
self.sd.ninchannels += self.sd.noutchannels
if 'DT9837A' in device.name:
daq_device = new DT9837A(
daqconfig.nFramesPerBlock,
inch_enabled,
outch_enabled,
daqconfig.samplerate,
monitorOutput,
device.index)
else:
raise RuntimeError(f'Device {device.name} not found or not configured')
self.sd.pyCallback = <PyObject*> avstream._audioCallback
self.sd.ninchannels = daq_device.neninchannels()
self.sd.noutchannels = daq_device.nenoutchannels()
self.daq_device = daq_device
# Increase reference count to the callback
Py_INCREF(<object> avstream._audioCallback)
self.daq_device.startScan(
daqconfig.nFramesPerBlock,
samplerate,
self.sd.inQueue,
self.sd.outQueue)
with nogil:
self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
<void*> self.sd)
@ -959,15 +302,20 @@ cdef class UlDaq:
# Allow it to start
CPPsleep_ms(500)
self.daq_device.start(
self.sd.inQueue,
self.sd.outQueue)
return nFramesPerBlock, self.daq_device.td.samplerate
return nFramesPerBlock, self.daq_device.samplerate()
def stop(self):
if self.sd is NULL:
raise RuntimeError('Stream is not opened')
if self.daq_device is not NULL:
self.daq_device.stop()
del self.daq_device
self.daq_device = NULL
self.daq_device.stopScan()
self.daq_device = None
self.cleanupStream(self.sd)
self.sd = NULL
@ -1004,3 +352,62 @@ cdef class UlDaq:
sd.pyCallback = NULL
free(sd)
def getDeviceInfo(self):
"""
Returns device information objects (DeviceInfo) for all available
devices
"""
cdef:
DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]
DaqDeviceDescriptor descriptor
DaqDeviceInterface interfaceType = ANY_IFC
DaqDeviceHandle handle
UlError err
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.')
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 - '
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

View File

@ -0,0 +1,50 @@
#include "lasp_cppuldaq.h"
#include <chrono>
#include <iostream>
using std::cout;
using std::endl;
int main() {
boolvec inChannels = {true, false, false, false};
/* boolvec inChannels = {false, false, false, false}; */
boolvec outChannels = {true};
double samplerate = 10000;
const us samplesPerBlock = 2048;
DT9837A daq(
samplesPerBlock,
inChannels,
outChannels,
samplerate,
false // monitor Output
);
SafeQueue<double*> inqueue;
SafeQueue<double*> outqueue;
daq.start(&inqueue, &outqueue);
/* daq.start(NULL, &outqueue); */
std::this_thread::sleep_for(std::chrono::seconds(5));
/* std::string a; */
/* std::cin >> a; */
daq.stop();
while(!inqueue.empty()) {
double* buf = inqueue.dequeue();
for(us i=0;i<samplesPerBlock;i++) {
for(us ch=0;ch<daq.neninchannels();ch++) {
cout << buf[ch*samplesPerBlock+i] << " ";
}
cout << endl;
}
free(buf);
}
return 0;
}