1111from datetime import timezone
1212from hightime import datetime as ht_datetime
1313from hightime import timedelta as ht_timedelta
14- from typing import Callable , List , Optional , Sequence , Tuple , TYPE_CHECKING
14+ from typing import Callable , List , MutableSequence , Optional , Sequence , Tuple , TYPE_CHECKING
1515
1616from nidaqmx ._base_interpreter import BaseEventHandler , BaseInterpreter
1717from nidaqmx ._lib import lib_importer , ctypes_byte_str , c_bool32 , wrapped_ndpointer , TaskHandle
18- from nidaqmx .constants import FillMode
18+ from nidaqmx .constants import FillMode , WaveformAttributeMode
1919from nidaqmx .error_codes import DAQmxErrors , DAQmxWarnings
2020from nidaqmx .errors import DaqError , DaqFunctionNotSupportedError , DaqReadError , DaqWarning , DaqWriteError
2121from nidaqmx ._lib_time import AbsoluteTime
@@ -6376,24 +6376,35 @@ def read_analog_waveform(
63766376 task_handle : object ,
63776377 number_of_samples_per_channel : int ,
63786378 timeout : float ,
6379- waveform : AnalogWaveform [numpy .float64 ]
6379+ waveform : AnalogWaveform [numpy .float64 ],
6380+ waveform_attribute_mode : WaveformAttributeMode
63806381 ) -> None :
63816382 """Read an analog waveform with timing and attributes."""
6382- error_code , samples_read , timestamps , sample_intervals = self ._internal_read_analog_waveform_ex (
6383+ if WaveformAttributeMode .EXTENDED_PROPERTIES in waveform_attribute_mode :
6384+ properties = [waveform .extended_properties ]
6385+ else :
6386+ properties = None
6387+
6388+ if WaveformAttributeMode .TIMING in waveform_attribute_mode :
6389+ t0_array = numpy .zeros (1 , dtype = numpy .int64 )
6390+ dt_array = numpy .zeros (1 , dtype = numpy .int64 )
6391+ else :
6392+ t0_array = None
6393+ dt_array = None
6394+
6395+ error_code , samples_read = self ._internal_read_analog_waveform_ex (
63836396 task_handle ,
6384- 1 , # single channel
63856397 number_of_samples_per_channel ,
63866398 timeout ,
63876399 FillMode .GROUP_BY_CHANNEL .value ,
63886400 waveform .raw_data ,
6389- [waveform .extended_properties ]
6401+ properties ,
6402+ t0_array ,
6403+ dt_array ,
63906404 )
63916405
6392- waveform .timing = Timing (
6393- sample_interval_mode = SampleIntervalMode .REGULAR ,
6394- timestamp = timestamps [0 ],
6395- sample_interval = sample_intervals [0 ],
6396- )
6406+ if t0_array is not None and dt_array is not None :
6407+ self ._set_waveform_timings ([waveform ], t0_array , dt_array )
63976408
63986409 # TODO: AB#3228924 - if the read was short, set waveform.sample_count before throwing the exception
63996410 self .check_for_error (error_code , samps_per_chan_read = samples_read )
@@ -6403,41 +6414,51 @@ def read_analog_waveforms(
64036414 task_handle : object ,
64046415 number_of_samples_per_channel : int ,
64056416 timeout : float ,
6406- waveforms : Sequence [AnalogWaveform [numpy .float64 ]]
6417+ waveforms : Sequence [AnalogWaveform [numpy .float64 ]],
6418+ waveform_attribute_mode : WaveformAttributeMode
64076419 ) -> None :
64086420 """Read a set of analog waveforms with timing and attributes. All of the waveforms must be the same size."""
6409- error_code , samples_read , timestamps , sample_intervals = self .internal_read_analog_waveform_per_chan (
6421+ if WaveformAttributeMode .EXTENDED_PROPERTIES in waveform_attribute_mode :
6422+ properties = [waveform .extended_properties for waveform in waveforms ]
6423+ else :
6424+ properties = None
6425+
6426+ if WaveformAttributeMode .TIMING in waveform_attribute_mode :
6427+ t0_array = numpy .zeros (len (waveforms ), dtype = numpy .int64 )
6428+ dt_array = numpy .zeros (len (waveforms ), dtype = numpy .int64 )
6429+ else :
6430+ t0_array = None
6431+ dt_array = None
6432+
6433+ error_code , samples_read = self .internal_read_analog_waveform_per_chan (
64106434 task_handle ,
64116435 number_of_samples_per_channel ,
64126436 timeout ,
64136437 [waveform .raw_data for waveform in waveforms ],
6414- [waveform .extended_properties for waveform in waveforms ]
6438+ properties ,
6439+ t0_array ,
6440+ dt_array ,
64156441 )
64166442
6417- for i , waveform in enumerate (waveforms ):
6418- waveform .timing = Timing (
6419- sample_interval_mode = SampleIntervalMode .REGULAR ,
6420- timestamp = timestamps [i ],
6421- sample_interval = sample_intervals [i ],
6422- )
6443+ if t0_array is not None and dt_array is not None :
6444+ self ._set_waveform_timings (waveforms , t0_array , dt_array )
64236445
64246446 # TODO: AB#3228924 - if the read was short, set waveform.sample_count before throwing the exception
64256447 self .check_for_error (error_code , samps_per_chan_read = samples_read )
64266448
64276449 def _internal_read_analog_waveform_ex (
64286450 self ,
64296451 task_handle : object ,
6430- channel_count : int ,
64316452 number_of_samples_per_channel : int ,
64326453 timeout : float ,
64336454 fill_mode : int ,
64346455 read_array : numpy .typing .NDArray [numpy .float64 ],
6435- properties : Sequence [ExtendedPropertyDictionary ]
6456+ properties : Sequence [ExtendedPropertyDictionary ] | None ,
6457+ t0_array : numpy .typing .NDArray [numpy .int64 ] | None ,
6458+ dt_array : numpy .typing .NDArray [numpy .int64 ] | None ,
64366459 ) -> Tuple [
64376460 int , # error code
64386461 int , # The number of samples per channel that were read
6439- Sequence [ht_datetime ], # The timestamps for each sample, indexed by channel
6440- Sequence [ht_timedelta ], # The sample intervals, indexed by channel
64416462 ]:
64426463 assert isinstance (task_handle , TaskHandle )
64436464 samps_per_chan_read = ctypes .c_int ()
@@ -6462,19 +6483,6 @@ def _internal_read_analog_waveform_ex(
64626483 ctypes .POINTER (c_bool32 ),
64636484 ]
64646485
6465- t0_array = numpy .zeros (channel_count , dtype = numpy .int64 )
6466- dt_array = numpy .zeros (channel_count , dtype = numpy .int64 )
6467-
6468- def set_wfm_attr_callback (
6469- channel_index : int ,
6470- attribute_name : str ,
6471- attribute_type : WfmAttrType ,
6472- value : ExtendedPropertyValue ,
6473- callback_data : object ,
6474- ) -> int :
6475- properties [channel_index ][attribute_name ] = value
6476- return 0
6477-
64786486 error_code = cfunc (
64796487 task_handle ,
64806488 number_of_samples_per_channel ,
@@ -6483,31 +6491,28 @@ def set_wfm_attr_callback(
64836491 t0_array ,
64846492 dt_array ,
64856493 0 if t0_array is None else t0_array .size ,
6486- self ._get_wfm_attr_callback_ptr ( set_wfm_attr_callback ),
6494+ self ._get_wfm_attr_callback ( properties ),
64876495 None ,
64886496 read_array ,
64896497 read_array .size ,
64906498 ctypes .byref (samps_per_chan_read ),
64916499 None ,
64926500 )
64936501
6494- timestamps = [_T0_EPOCH + ht_timedelta (seconds = t0 * _INT64_WFM_SEC_PER_TICK ) for t0 in t0_array ]
6495- sample_intervals = [ht_timedelta (seconds = dt * _INT64_WFM_SEC_PER_TICK ) for dt in dt_array ]
6496-
6497- return error_code , samps_per_chan_read .value , timestamps , sample_intervals
6502+ return error_code , samps_per_chan_read .value
64986503
64996504 def internal_read_analog_waveform_per_chan (
65006505 self ,
65016506 task_handle : object ,
65026507 num_samps_per_chan : int ,
65036508 timeout : float ,
65046509 read_arrays : Sequence [numpy .typing .NDArray [numpy .float64 ]],
6505- properties : Sequence [ExtendedPropertyDictionary ]
6510+ properties : Sequence [ExtendedPropertyDictionary ] | None ,
6511+ t0_array : numpy .typing .NDArray [numpy .int64 ] | None ,
6512+ dt_array : numpy .typing .NDArray [numpy .int64 ] | None ,
65066513 ) -> Tuple [
65076514 int , # error code
65086515 int , # The number of samples per channel that were read
6509- Sequence [ht_datetime ], # The timestamps for each sample, indexed by channel
6510- Sequence [ht_timedelta ], # The sample intervals, indexed by channel
65116516 ]:
65126517 assert isinstance (task_handle , TaskHandle )
65136518 samps_per_chan_read = ctypes .c_int ()
@@ -6537,31 +6542,18 @@ def internal_read_analog_waveform_per_chan(
65376542 ctypes .POINTER (c_bool32 ),
65386543 ]
65396544
6540- t0_array = numpy .zeros (channel_count , dtype = numpy .int64 )
6541- dt_array = numpy .zeros (channel_count , dtype = numpy .int64 )
6542-
65436545 read_array_ptrs = (ctypes .POINTER (ctypes .c_double ) * channel_count )()
65446546 for i , read_array in enumerate (read_arrays ):
65456547 read_array_ptrs [i ] = read_array .ctypes .data_as (ctypes .POINTER (ctypes .c_double ))
65466548
6547- def set_wfm_attr_callback (
6548- channel_index : int ,
6549- attribute_name : str ,
6550- attribute_type : WfmAttrType ,
6551- value : ExtendedPropertyValue ,
6552- callback_data : object ,
6553- ) -> int :
6554- properties [channel_index ][attribute_name ] = value
6555- return 0
6556-
65576549 error_code = cfunc (
65586550 task_handle ,
65596551 num_samps_per_chan ,
65606552 timeout ,
65616553 t0_array ,
65626554 dt_array ,
65636555 0 if t0_array is None else t0_array .size ,
6564- self ._get_wfm_attr_callback_ptr ( set_wfm_attr_callback ),
6556+ self ._get_wfm_attr_callback ( properties ),
65656557 None ,
65666558 read_array_ptrs ,
65676559 channel_count ,
@@ -6571,11 +6563,22 @@ def set_wfm_attr_callback(
65716563 )
65726564 self .check_for_error (error_code , samps_per_chan_read = samps_per_chan_read .value )
65736565
6574- timestamps = [_T0_EPOCH + ht_timedelta (seconds = t0 * _INT64_WFM_SEC_PER_TICK ) for t0 in t0_array ]
6575- sample_intervals = [ht_timedelta (seconds = dt * _INT64_WFM_SEC_PER_TICK ) for dt in dt_array ]
6576-
6577- return error_code , samps_per_chan_read .value , timestamps , sample_intervals
6578-
6566+ return error_code , samps_per_chan_read .value
6567+
6568+ def _get_wfm_attr_callback (self , properties ):
6569+ if properties is not None :
6570+ def set_wfm_attr_callback (
6571+ channel_index : int ,
6572+ attribute_name : str ,
6573+ attribute_type : WfmAttrType ,
6574+ value : ExtendedPropertyValue ,
6575+ callback_data : object ,
6576+ ) -> int :
6577+ properties [channel_index ][attribute_name ] = value
6578+ return 0
6579+ return self ._get_wfm_attr_callback_ptr (set_wfm_attr_callback )
6580+ else :
6581+ return CSetWfmAttrCallbackPtr ()
65796582
65806583 def _get_wfm_attr_value (
65816584 self , attribute_type : int , value : ctypes .c_void_p , value_size_in_bytes : int
@@ -6597,11 +6600,8 @@ def _get_wfm_attr_value(
65976600 raise ValueError (f"Unsupported attribute type { attribute_type } " )
65986601
65996602 def _get_wfm_attr_callback_ptr (
6600- self , set_wfm_attr_callback : Optional [ SetWfmAttrCallback ]
6603+ self , set_wfm_attr_callback : SetWfmAttrCallback
66016604 ) -> ctypes ._FuncPointer :
6602- if set_wfm_attr_callback is None :
6603- return CSetWfmAttrCallbackPtr ()
6604-
66056605 def _invoke_callback (
66066606 channel_index : int ,
66076607 attribute_name : bytes ,
@@ -6624,6 +6624,19 @@ def _invoke_callback(
66246624
66256625 return CSetWfmAttrCallbackPtr (_invoke_callback )
66266626
6627+ def _set_waveform_timings (
6628+ self ,
6629+ waveforms : Sequence [AnalogWaveform [numpy .float64 ]],
6630+ t0_array : numpy .typing .NDArray [numpy .int64 ],
6631+ dt_array : numpy .typing .NDArray [numpy .int64 ]
6632+ ) -> None :
6633+ for i , waveform in enumerate (waveforms ):
6634+ waveform .timing = Timing (
6635+ sample_interval_mode = SampleIntervalMode .REGULAR ,
6636+ timestamp = _T0_EPOCH + ht_timedelta (seconds = t0_array [i ] * _INT64_WFM_SEC_PER_TICK ),
6637+ sample_interval = ht_timedelta (seconds = dt_array [i ] * _INT64_WFM_SEC_PER_TICK ),
6638+ )
6639+
66276640 def read_id_pin_memory (self , device_name , id_pin_name ):
66286641 data_length_read = ctypes .c_uint ()
66296642 format_code = ctypes .c_uint ()
0 commit comments