First work on uldaq implementatio for DT9837A

This commit is contained in:
Anne de Jong 2020-09-12 23:34:28 +02:00
parent 2192f5a7fc
commit a3a7c37548
9 changed files with 1105 additions and 58 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
*.a *.a
*.pyc *.pyc
lasp_rtaudio.cxx lasp_rtaudio.cxx
lasp_uldaq.cxx
dist dist
CMakeFiles CMakeFiles
CMakeCache.txt CMakeCache.txt

View File

@ -1,11 +1,14 @@
include_directories(/usr/include/rtaudio) include_directories(/usr/include/rtaudio)
set_source_files_properties(lasp_rtaudio.pyx PROPERTIES CYTHON_IS_CXX TRUE) 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 set_source_files_properties(lasp_rtaudio.cxx PROPERTIES COMPILE_FLAGS
"${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}") "${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}")
cython_add_module(lasp_rtaudio lasp_rtaudio.pyx) 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_rtaudio pthread rtaudio)
target_link_libraries(lasp_uldaq uldaq)
if(win32) if(win32)
target_link_libraries(lasp_rtaudio python37) target_link_libraries(lasp_rtaudio python37)
endif(win32) endif(win32)

View File

@ -1,3 +1,6 @@
#!/usr/bin/python3 #!/usr/bin/python3
from .lasp_rtaudio import *
from .lasp_daqconfig import * from .lasp_daqconfig import *
from .lasp_avtype import *
from .lasp_rtaudio import *
from .lasp_uldaq import *

View File

@ -0,0 +1,9 @@
__all__ = ['AvType']
class AvType:
"""Specificying the type of data, for adding and removing callbacks from
the stream."""
audio_input = 1
audio_output = 2
video = 4

View File

@ -0,0 +1,54 @@
import sys
include "config.pxi"
cimport cython
from .lasp_daqconfig import DeviceInfo
from .lasp_avtype import AvType
from libcpp.string cimport string
from libcpp.vector cimport vector
from libc.stdlib cimport malloc, free
from libc.stdio cimport printf, fprintf, stderr
from libc.string cimport memcpy, memset
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
__all__ = ['RtAudio', 'get_numpy_dtype_from_format_string',
'get_sampwidth_from_format_string']
cdef extern from "lasp_cppthread.h" nogil:
cdef cppclass CPPThread[T,F]:
CPPThread(F threadfunction, T data)
void join()
void CPPsleep(unsigned int ms)
cdef extern from "lasp_cppqueue.h" nogil:
cdef cppclass SafeQueue[T]:
SafeQueue()
void enqueue(T t)
T dequeue()
size_t size() const
bool empty() const
cdef extern from "atomic" namespace "std" nogil:
cdef cppclass atomic[T]:
T load()
void store(T)
cdef extern from "lasp_pyarray.h":
PyObject* data_to_ndarray(void* data,
int n_rows,int n_cols,
int typenum,
bool transfer_ownership,
bool F_contiguous)
cdef inline void copyChannel(void* to, void* from_,
unsigned bytesperchan,
unsigned toindex,
unsigned fromindex) nogil:
memcpy(<void*> &((<char*> to)[bytesperchan*toindex]),
<void*> &((<char*> from_)[bytesperchan*fromindex]),
bytesperchan)

View File

@ -14,11 +14,27 @@ from ..lasp_common import lasp_shelve, Qty, SIQtys
from typing import List from typing import List
class InputMode:
differential = 'Differential'
single_ended = 'Single-ended'
pseudo_differential = 'Pseudo-differential'
undefined = 'Undefined'
class CouplingMode:
ac = 'AC'
dc = 'DC'
undefined = 'Undefined'
class Range:
oneV = '+/- 1 V'
tenV = '+/- 10 V'
undefined = 'Undefined'
@dataclass @dataclass
class DeviceInfo: class DeviceInfo:
api: int api: int
index: int index: int
probed: bool probed: bool #
name: str name: str
outputchannels: int outputchannels: int
inputchannels: int inputchannels: int
@ -26,7 +42,7 @@ class DeviceInfo:
samplerates: list samplerates: list
sampleformats: list sampleformats: list
prefsamplerate: int prefsamplerate: int
hasInputIEPE: bool = False
@dataclass_json @dataclass_json
@ -36,6 +52,9 @@ class DAQChannel:
channel_name: str channel_name: str
sensitivity: float sensitivity: float
qty: Qty qty: Qty
range_: str = 'Undefined'
IEPE_enabled: bool = False
@dataclass_json @dataclass_json

View File

@ -1,23 +1,8 @@
import sys include "lasp_common_decls.pxd"
include "config.pxi"
cimport cython
from .lasp_daqconfig import DeviceInfo
from libcpp.string cimport string
from libcpp.vector cimport vector
from libc.stdlib cimport malloc, free
from libc.stdio cimport printf, fprintf, stderr
from libc.string cimport memcpy, memset
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
__all__ = ['AvType', 'RtAudio', 'get_numpy_dtype_from_format_string', __all__ = ['RtAudio', 'get_numpy_dtype_from_format_string',
'get_sampwidth_from_format_string'] 'get_sampwidth_from_format_string']
class AvType:
"""Specificying the type of data, for adding and removing callbacks from
the stream."""
audio_input = 1
audio_output = 2
video = 4
cdef extern from "RtAudio.h" nogil: cdef extern from "RtAudio.h" nogil:
ctypedef unsigned long RtAudioStreamStatus ctypedef unsigned long RtAudioStreamStatus
@ -141,33 +126,6 @@ cdef extern from "RtAudio.h" nogil:
unsigned int getStreamSampleRate() unsigned int getStreamSampleRate()
void showWarnings(bool value) void showWarnings(bool value)
cdef extern from "lasp_cppthread.h" nogil:
cdef cppclass CPPThread[T,F]:
CPPThread(F threadfunction, T data)
void join()
void CPPsleep(unsigned int ms)
cdef extern from "lasp_cppqueue.h" nogil:
cdef cppclass SafeQueue[T]:
SafeQueue()
void enqueue(T t)
T dequeue()
size_t size() const
bool empty() const
cdef extern from "atomic" namespace "std" nogil:
cdef cppclass atomic[T]:
T load()
void store(T)
cdef extern from "lasp_pyarray.h":
PyObject* data_to_ndarray(void* data,
int n_rows,int n_cols,
int typenum,
bool transfer_ownership,
bool F_contiguous)
_formats_strkey = { _formats_strkey = {
'8-bit integers': (RTAUDIO_SINT8, 1, np.int8), '8-bit integers': (RTAUDIO_SINT8, 1, np.int8),
@ -192,7 +150,6 @@ def get_sampwidth_from_format_string(format_string):
return _formats_strkey[format_string][-2] return _formats_strkey[format_string][-2]
# It took me quite a long time to fully understand Cython's idiosyncrasies # It took me quite a long time to fully understand Cython's idiosyncrasies
# concerning C(++) callbacks, the GIL and passing Python objects as pointers # concerning C(++) callbacks, the GIL and passing Python objects as pointers
# into C(++) functions. But finally, here it is! # into C(++) functions. But finally, here it is!
@ -205,13 +162,6 @@ def get_sampwidth_from_format_string(format_string):
# """ # """
# memcpy(buf, arr.data, arr.size*arr.itemsize) # memcpy(buf, arr.data, arr.size*arr.itemsize)
cdef void copyChannel(void* to, void* from_,
unsigned bytesperchan,
unsigned toindex,
unsigned fromindex) nogil:
memcpy(<void*> &((<char*> to)[bytesperchan*toindex]),
<void*> &((<char*> from_)[bytesperchan*fromindex]),
bytesperchan)
ctypedef struct _Stream: ctypedef struct _Stream:
@ -522,9 +472,6 @@ cdef class RtAudio:
} }
return apidict return apidict
cpdef unsigned int getDeviceCount(self):
return self._rtaudio.getDeviceCount()
cpdef unsigned int getDefaultOutputDevice(self): cpdef unsigned int getDefaultOutputDevice(self):
return self._rtaudio.getDefaultOutputDevice() return self._rtaudio.getDefaultOutputDevice()

387
lasp/device/lasp_uldaq.pxd Normal file
View File

@ -0,0 +1,387 @@
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]
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 AiInputMode:
AI_DIFFERENTIAL = 1,
AI_SINGLE_ENDED = 2,
AI_PSEUDO_DIFFERENTIAL = 3
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 DaqOutScanFlag:
DAQOUTSCAN_FF_DEFAULT
DAQOUTSCAN_FF_NOSCALEDATA
DAQOUTSCAN_FF_NOCALIBRATEDATA
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 DaqOutChanType:
DAQO_ANALOG
DAQO_DIGITAL
ctypedef struct DaqOutChanDescriptor:
int channel
DaqOutChanType 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);
UlError ulDaqOutScan(DaqDeviceHandle daqDeviceHandle, DaqOutChanDescriptor chanDescriptors[], int numChans, int samplesPerChan, double* rate, ScanOption options, DaqOutScanFlag flags, double data[]);
UlError ulDaqOutScanStatus(DaqDeviceHandle daqDeviceHandle, ScanStatus* status, TransferStatus* xferStatus);
UlError ulDaqOutScanStop(DaqDeviceHandle daqDeviceHandle);
UlError ulDaqOutSetTrigger(DaqDeviceHandle daqDeviceHandle, TriggerType type, DaqInChanDescriptor trigChanDescriptor, double level, double variance, unsigned int retriggerSampleCount);
# UlError ulAIGetConfigDbl(DaqDeviceHandle daqDeviceHandle, AiConfigItemDbl configItem, unsigned int index, double* configValue);
# UlError ulAIGetConfigStr(DaqDeviceHandle daqDeviceHandle, AiConfigItemStr configItem, unsigned int index, char* configStr, unsigned int* maxConfigLen);
# ctypedef enum AiIn
# {
# /** Returns the minimum scan rate in samples per second to the \p infoValue argument. Index is ignored. */
# AI_INFO_MIN_SCAN_RATE = 1000,
# /** Returns the maximum scan rate in samples per second to the \p infoValue argument. Index is ignored. */
# AI_INFO_MAX_SCAN_RATE = 1001,
# /** Returns the maximum throughput in samples per second to the \p infoValue argument. Index is ignored. */
# AI_INFO_MAX_THROUGHPUT = 1002,
# /** Returns the maximum scan rate in samples per second when using the ::SO_BURSTIO ScanOption to the \p infoValue argument. Index is ignored. */
# AI_INFO_MAX_BURST_RATE = 1003,
# /** Returns the maximum throughput in samples per second when using the ::SO_BURSTIO ScanOption to the \p infoValue argument. Index is ignored. */
# AI_INFO_MAX_BURST_THROUGHPUT = 1004
# }AiInfoItemDbl

624
lasp/device/lasp_uldaq.pyx Normal file
View File

@ -0,0 +1,624 @@
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
from .lasp_daqconfig import (DeviceInfo, InputMode, Range as pyRange,
DAQChannel)
from .lasp_avtype import AvType
__all__ = ['UlDT9837A', 'UlDaq']
DEF MAX_DEF_COUNT = 100
cdef struct DaqThreadData:
unsigned int samplesPerBlock
double samplerate
DaqDeviceHandle handle
SafeQueue[void*] *inputQueue
SafeQueue[void*] *outputQueue
DaqInChanDescriptor* inChanDescriptors
unsigned ninputChanDescriptors
DaqOutChanDescriptor* outChanDescriptors
unsigned noutputChanDescriptors
atomic[bool] stopThread
CPPThread[void*, void (*)(void*)] *thread
cdef void eventCallbackFunction(DaqDeviceHandle handle, DaqEventType eventType,
unsigned long long eventData, void* userData):
pass
cdef void ulThreadFunction(void* threaddata_void) nogil:
"""
Stream thread function
"""
cdef:
DaqThreadData* td = <DaqThreadData*> threaddata_void
TransferStatus instatus
TransferStatus outstatus
UlError err
ScanOption scanoptions
DaqInScanFlag inscanflags
DaqOutScanFlag outscanflags
# AScanFlag inscanflags
double* outbuffer, inbuffer
# inbuffer = NULL
# outbuffer = NULL
inscanflags = DAQINSCAN_FF_NOSCALEDATA
outscanflags = DAQOUTSCAN_FF_NOSCALEDATA
scanoptions = SO_CONTINUOUS
if td.noutputChanDescriptors > 0:
# Enable input and outputs
err = ulDaqOutScan(td.handle,
td.outChanDescriptors,
td.noutputChanDescriptors,
td.samplesPerBlock,
&td.samplerate,
scanoptions,
outscanflags,
outbuffer)
if err:
fprintf(stderr, 'Error starting data output\n')
return
if td.ninputChanDescriptors > 0:
# Enable input and outputs
# err = ulDaqOutScan(td.handle,
# td.outChanDescriptors,
# td.noutputChanDescriptors,
# td.samplesPerBlock,
# &td.samplerate,
# scanoptions,
# outscanflags,
# outbuffer)
# if err:
# fprintf(stderr, 'Error starting data output\n')
# return
pass
cdef class UlDT9837A:
cdef:
DaqDeviceHandle handle
bint handle_connected
bint stream_running
unsigned int ninputchannels
unsigned int noutputchannels
object input_range
object enabled_inputs
bint output_enabled
bint monitor_gen
DaqThreadData *threaddata
def __cinit__(self, unsigned int deviceno):
self.handle = 0
self.handle_connected = False
self.ninputchannels = 4
self.noutputchannels = 1
self.input_range = 4*[False]
self.enabled_inputs = 4*[False]
self.output_enabled = False
self.threaddata = NULL
self.monitor_gen = False
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,
SafeQueue[void*] *inputQueue,
SafeQueue[void*] *outputQueue):
cdef:
int i, j
# Sanity checks
if inputQueue 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 outputQueue and not self.output_enabled:
raise ValueError('Output queue given, but output channel is not enabled')
# End sanity checks
cdef:
DaqThreadData *threaddata
threaddata = <DaqThreadData*> malloc(sizeof(threaddata))
if not threaddata:
raise MemoryError()
threaddata.samplesPerBlock = samplesPerBlock
threaddata.handle = self.handle
threaddata.inputQueue = inputQueue
threaddata.outputQueue = outputQueue
threaddata.inChanDescriptors = NULL
threaddata.outChanDescriptors = NULL
threaddata.ninputChanDescriptors = 0
threaddata.noutputChanDescriptors = 0
neninputchannels = sum([1 if self.enabled_inputs == True else 0])
threaddata.inputQueue = inputQueue
threaddata.outputQueue = inputQueue
threaddata.thread = NULL
j = 0
if self.monitor_gen:
neninputchannels +=1
j+=1
threaddata.inChanDescriptors = <DaqInChanDescriptor*> malloc(neninputchannels*sizeof(DaqInChanDescriptor))
if not threaddata.inChanDescriptors:
self.cleanupThreadData(threaddata)
raise MemoryError()
if self.monitor_gen:
threaddata.inChanDescriptors[0].chantype = DAQI_DAC
threaddata.inChanDescriptors[0].channel = 7
threaddata.inChanDescriptors[0].range = BIP10VOLTS
for i in range(neninputchannels):
if self.enabled_inputs[i]:
threaddata.inChanDescriptors[j].chantype = DAQI_ANALOG_DIFF
threaddata.inChanDescriptors[j].channel = i
threaddata.inChanDescriptors[j].range = BIP10VOLTS if self.input_range[i] else BIP1VOLTS
j+=1
threaddata.ninputChanDescriptors = neninputchannels
if self.output_enabled:
threaddata.outChanDescriptors = <DaqOutChanDescriptor*> malloc(sizeof(DaqInChanDescriptor))
if not threaddata.outChanDescriptors:
self.cleanupThreadData(threaddata)
raise MemoryError()
self.threaddata.outChanDescriptors[0].channel = 0
self.threaddata.outChanDescriptors[0].type = DAQO_ANALOG
self.threaddata.outChanDescriptors[0].range = BIP10VOLTS
self.threaddata.thread = new CPPThread[void*, void (*)(void*)](
ulThreadFunction,
self.threaddata)
def setInputChannelConfig(self, unsigned chnum, channelconfig: DAQChannel):
if self.threaddata:
raise RuntimeError('Cannot change settings while sampling')
cdef:
int i
UlError err
IepeMode iepe
CouplingMode cm
if chnum >= self.ninputchannels:
raise RuntimeError('Invalid input channel number')
self.input_range[chnum] = True if channelconfig.range_ == pyRange.tenV else False
self.enabled_input[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)
if err != ERR_NO_ERROR:
raise RuntimeError('Fatal: could not set sensitivity')
def setOutputChannelConfig(self, unsigned chnum, channelconfig:
DAQChannel, bint monitor_gen):
if self.threaddata:
raise RuntimeError('Cannot change settings while sampling')
if chnum >= self.noutputchannels:
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.output_enabled = channelconfig.channel_enabled
self.monitor_gen = monitor_gen
def __dealloc__(self):
if self.handle_connected:
ulDisconnectDaqDevice(self.handle)
ulReleaseDaqDevice(self.handle)
cdef cleanupThreadData(self, DaqThreadData* td):
if td is NULL:
return
if td.thread:
td.stopThread.store(True)
td.thread.join()
del td.thread
td.thread = NULL
if td.inChanDescriptors:
free(td.inChanDescriptors)
td.inChanDescriptors = NULL
if td.outChanDescriptors:
free(td.outChanDescriptors)
td.outChanDescriptors = NULL
free(td)
cdef class UlDaq:
cdef:
DaqDeviceHandle handle
def __cinit__(self):
"""
Acquires a daq handle, and opens the device
"""
self.handle = 0
# # Open the device to probe the number of input and output ch.
# handle = ulCreateDaqDevice(descriptor)
# if not handle:
# raise RuntimeError('Unable to create device handle on device')
cpdef int getDeviceCount(self):
cdef:
DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]
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}')
devices = []
return numdevs
def getDeviceInfo(self, unsigned int deviceno):
"""
"""
cdef:
DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]
DaqDeviceDescriptor descriptor
DaqDeviceInterface interfaceType = ANY_IFC
DaqDeviceHandle handle
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}')
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')
return DeviceInfo(
api = -1,
index = deviceno,
probed = True,
name = name,
outputchannels = 1,
inputchannels = 4,
duplexchannels = 0,
samplerates = [100, 500, 1000, 2000, 4000, 8000, 16000, 20000, 32000, 48000, 50000] ,
sampleformats = ['64-bit floats'],
prefsamplerate = 48000,
hasInputIEPE = True)
else:
raise RuntimeError(f"No config found for device \"{descriptor.productName.decode('utf-8')}\".")
# @cython.nonecheck(True)
# def openStream(self,
# avstream
# ):
# """
# Opens a stream with specified parameters
# Args:
# avstream: AvStream instance
# Returns: None
# """
# if self._stream is not NULL:
# raise RuntimeError('Stream is already opened.')
# daqconfig = avstream.daqconfig
# avtype = avstream.avtype
# device = avstream.device
# cdef:
# bint duplex_mode = daqconfig.duplex_mode
# bint monitorOutput = daqconfig.monitor_gen
# size_t sw # Sample width in bytes
# unsigned int nFramesPerBlock = unsigned int(daqconfig.nFramesPerBlock)
# int firstinputchannel, firstoutputchannel
# int lastinputchannel, lastoutputchannel
# unsigned int ninputchannels_forwarded=0
# unsigned int ninputchannels_uldaq=0
# unsigned int noutputchannels_uldaq=0
# unsigned int noutputchannels_forwarded=0
# unsigned int samplerate
# int i
# bint input_stream=False, output_stream=False
# bool* inputChannelsEnabled
# bool* outputChannelsEnabled
# if daqconfig.nFramesPerBlock > 8192 or daqconfig.nFramesPerBlock < 512:
# raise ValueError('Invalid number of nFramesPerBlock')
# if daqconfig.outputDelayBlocks < 0 or daqconfig.outputDelayBlocks > 10:
# raise ValueError('Invalid number of outputDelayBlocks')
# try:
# # Determine sample rate and sample format, determine whether we are an
# # input or an output stream, or both
# if avtype == AvType.audio_input or duplex_mode:
# # Here, we override the sample format in case of duplex mode.
# sampleformat = daqconfig.en_input_sample_format
# samplerate = int(daqconfig.en_input_rate)
# input_stream = True
# if duplex_mode:
# output_stream = True
# else:
# sampleformat = daqconfig.en_output_sample_format
# samplerate = int(daqconfig.en_output_rate)
# output_stream = True
# sw = 64
# # All set, allocate the stream!
# self._stream = <_Stream*> malloc(sizeof(_Stream))
# if self._stream == NULL:
# raise MemoryError('Could not allocate stream: memory error.')
# self._stream.pyCallback = <PyObject*> avstream._audioCallback
# # Increase reference count to the callback
# Py_INCREF(<object> avstream._audioCallback)
# self._stream.sw = sw
# self._stream.stopThread.store(False)
# self._stream.inputQueue = NULL
# self._stream.outputQueue = NULL
# self._stream.outputDelayQueue = NULL
# self._stream.thread = NULL
# self._stream.outputDelayBlocks = outputDelayBlocks
# self._stream.ninputchannels_forwarded = 0
# self._stream.noutputchannels_forwarded = 0
# self._stream.inputChannelsEnabled = NULL
# self._stream.outputChannelsEnabled = NULL
# # Create channel maps for input channels, set input stream
# # parameters
# if input_stream:
# firstinputchannel = daqconfig.firstEnabledInputChannelNumber()
# lastinputchannel = daqconfig.lastEnabledInputChannelNumber()
# ninputchannels_uldaq = lastinputchannel-firstinputchannel+1
# if lastinputchannel < 0 or ninputchannels_uldaq < 1:
# raise ValueError('Not enough input channels selected')
# input_ch = daqconfig.input_channel_configs
# inputChannelsEnabled = <bool*> malloc(sizeof(bool)*ninputchannels_uldaq)
# self._stream.inputChannelsEnabled = inputChannelsEnabled
# for i in range(firstinputchannel, lastinputchannel+1):
# ch_en = input_ch[i].channel_enabled
# if ch_en:
# ninputchannels_forwarded += 1
# inputChannelsEnabled[i] = ch_en
# self._stream.inputQueue = new SafeQueue[void*]()
# self._stream.ninputchannels_forwarded = ninputchannels_forwarded
# # Create channel maps for output channels
# if output_stream:
# firstoutputchannel = daqconfig.firstEnabledOutputChannelNumber()
# lastoutputchannel = daqconfig.lastEnabledOutputChannelNumber()
# noutputchannels_uldaq = lastoutputchannel-firstoutputchannel+1
# if lastoutputchannel < 0 or noutputchannels_uldaq < 1:
# raise ValueError('Not enough output channels selected')
# output_ch = daqconfig.output_channel_configs
# outputChannelsEnabled = <bool*> malloc(sizeof(bool)*noutputchannels_uldaq)
# self._stream.outputChannelsEnabled = outputChannelsEnabled
# for i in range(firstoutputchannel, lastoutputchannel+1):
# ch_en = output_ch[i].channel_enabled
# if ch_en:
# noutputchannels_forwarded += 1
# outputChannelsEnabled[i] = ch_en
# rtOutputParams_ptr = &self._stream.outputParams
# rtOutputParams_ptr.deviceId = device.index
# rtOutputParams_ptr.nChannels = noutputchannels_uldaq
# rtOutputParams_ptr.firstChannel = firstoutputchannel
# self._stream.outputQueue = new SafeQueue[void*]()
# self._stream.noutputchannels_forwarded = noutputchannels_forwarded
# if monitorOutput and duplex_mode:
# self._stream.ninputchannels_forwarded += noutputchannels_forwarded
# # self._uldaq.openStream(rtOutputParams_ptr,
# # rtInputParams_ptr,
# # _formats_strkey[sampleformat][0],
# # samplerate,
# # &nFramesPerBlock,
# # audioCallback,
# # <void*> self._stream,
# # &streamoptions, # Stream options
# # errorCallback # Error callback
# # )
# # self._stream.nBytesPerChan = nFramesPerBlock*sw
# # self._stream.nFramesPerBlock = nFramesPerBlock
# except Exception as e:
# print('Exception occured in stream opening: ', e)
# self.cleanupStream(self._stream)
# self._stream = NULL
# raise e
# with nogil:
# self._stream.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
# <void*> self._stream)
# # Allow it to start
# CPPsleep(500)
# pass
# return nFramesPerBlock
# cdef cleanupStream(self, _Stream* stream):
# # printf('Entrance function cleanupStream...\n')
# cdef:
# void* ptr
# if stream == NULL:
# return
# with nogil:
# if stream.thread:
# stream.stopThread.store(True)
# if stream.inputQueue:
# # If waiting in the input queue, hereby we let it run.
# stream.inputQueue.enqueue(NULL)
# # printf('Joining thread...\n')
# # HERE WE SHOULD RELEASE THE GIL, as exiting the thread function
# # will require the GIL, which is locked by this thread!
# stream.thread.join()
# # printf('Thread joined!\n')
# del stream.thread
# stream.thread = NULL
# if stream.inputChannelsEnabled:
# free(stream.inputChannelsEnabled)
# if stream.outputChannelsEnabled:
# free(stream.outputChannelsEnabled)
# if stream.outputQueue:
# while not stream.outputQueue.empty():
# free(stream.outputQueue.dequeue())
# del stream.outputQueue
# if stream.inputQueue:
# while not stream.inputQueue.empty():
# free(stream.inputQueue.dequeue())
# del stream.inputQueue
# if stream.outputDelayQueue:
# while not stream.outputDelayQueue.empty():
# free(stream.outputDelayQueue.dequeue())
# del stream.outputDelayQueue
# fprintf(stderr, "End cleanup stream queues...\n")
# if stream.pyCallback:
# Py_DECREF(<object> stream.pyCallback)
# stream.pyCallback = NULL
# # fprintf(stderr, "End cleanup callback...\n")
# free(stream)
# def startStream(self):
# self._uldaq.startStream()
# def stopStream(self):
# if self._stream is NULL:
# raise RuntimeError('Stream is not opened')
# try:
# self._uldaq.stopStream()
# except:
# pass
# def closeStream(self):
# # print('closeStream')
# if self._stream is NULL:
# raise RuntimeError('Stream is not opened')
# # Closing stream
# self._uldaq.closeStream()
# self.cleanupStream(self._stream)
# self._stream = NULL
# def abortStream(self):
# if self._stream is NULL:
# raise RuntimeError('Stream is not opened')
# self._uldaq.abortStream()
# def isStreamOpen(self):
# return self._uldaq.isStreamOpen()
# def isStreamRunning(self):
# return self._uldaq.isStreamRunning()
# def getStreamTime(self):
# return self._uldaq.getStreamTime()
# def setStreamTime(self, double time):
# return self._uldaq.setStreamTime(time)