@@ -14,22 +14,38 @@ def close(self):
14
14
def ping (self ):
15
15
return True
16
16
17
+ def string_to_bytes (string , name ):
Has conversations. Original line has conversations.
18
+ if isinstance (string , str ):
19
+ string = bytes (string , encoding = "ascii" )
20
+ elif not isinstance (string , bytes ):
21
+ raise ValueError ("{} must be of either str or bytes type" .format (name ))
22
+ return string
17
23
18
24
class DAQmx :
19
25
"""NI PXI6733 DAQ interface."""
20
26
21
- def __init__ (self , device , analog_output , clock ):
27
+ def __init__ (self , channels , clock ):
28
+ """
29
+ :param channels: List of channels as a string or bytes(), following
30
+ the physical channels lists and ranges NI-DAQmx syntax.
31
+
32
+ Example: Dev1/ao0, Dev1/ao1:ao3
33
+ :param clock: Clock source terminal as a string or bytes(), following
34
+ NI-DAQmx terminal names syntax.
35
+
36
+ Example: PFI5
37
+ """
38
+
22
39
import PyDAQmx as daq
23
40
24
- self .device = device
25
- self .analog_output = analog_output
26
- self .clock = clock
27
- self .tasks = []
41
+ self .channels = string_to_bytes (channels , "channels" )
Has conversations. Original line has conversations.
42
+ self .clock = string_to_bytes (clock , "clock" )
43
+ self .task = None
28
44
self .daq = daq
29
45
30
46
def done_callback_py (self , taskhandle , status , callback_data ):
31
- self .daq . DAQmxClearTask ( taskhandle )
32
- self .tasks . remove ( taskhandle )
47
+ if taskhandle == self .task :
Has conversations. Original line has conversations.
48
+ self .clear_pending_task ( )
33
49
34
50
def ping (self ):
35
51
try :
@@ -42,43 +58,75 @@ def ping(self):
42
58
def load_sample_values (self , sampling_freq , values ):
43
59
"""Load sample values into PXI 6733 device.
44
60
45
- This loads sample values into the PXI 6733 device and then
46
- configures a task to output those samples at each clock rising
47
- edge.
61
+ This loads sample values into the PXI 6733 device.
62
+ The device will output samples at each clock rising edge.
63
+ The first sample is output at the first clock rising edge.
Has conversations. Original line has conversations.
64
+
65
+ When using several channels simultaneously, you must concatenate the
66
+ values for the different channels in the ``values`` array.
67
+ The sample values for the same channel must be grouped together.
68
+
69
+ Example:
48
70
49
- A callback is registered to clear the task (deallocate resources)
50
- when the task has completed.
71
+ >>> values = np.array([ch0_samp0, ch0_samp1, ch1_samp0, ch1_samp1],
72
+ dtype=float)
73
+
74
+ In this example the first two samples will be output via the first
75
+ channel and the two following samples will be output via the second
76
+ channel.
77
+
78
+ Any call to this method will cancel any previous task even if it has
79
+ not yet completed.
51
80
52
81
:param sampling_freq: The sampling frequency in samples per second.
53
82
:param values: A numpy array of sample values to load in the device.
54
83
"""
55
84
85
+ self .clear_pending_task ()
86
+
56
87
t = self .daq .Task ()
57
- t .CreateAOVoltageChan (self .device + b"/" + self . analog_output , b"" ,
88
+ t .CreateAOVoltageChan (self .channels , b"" ,
58
89
min (values ), max (values ),
59
90
self .daq .DAQmx_Val_Volts , None )
91
+
92
+ channel_number = self .daq .int32 ()
93
+ t .GetTaskNumChans (byref (channel_number ))
94
+ nb_values = len (values )
95
+ if nb_values % channel_number .value > 0 :
Has conversations. Original line has conversations.
96
+ self .daq .DAQmxClearTask (t .taskHandle )
97
+ raise ValueError ("The size of the values array must be a multiple "
98
+ "of the number of channels ({})"
99
+ .format (channel_number .value ))
100
+ samps_per_channel = nb_values // channel_number
101
+
60
102
t .CfgSampClkTiming (self .clock , sampling_freq ,
61
103
self .daq .DAQmx_Val_Rising ,
62
- self .daq .DAQmx_Val_FiniteSamps , len ( values ) )
104
+ self .daq .DAQmx_Val_FiniteSamps , samps_per_channel )
63
105
num_samps_written = self .daq .int32 ()
64
106
values = np .require (values , dtype = float ,
65
107
requirements = ["C_CONTIGUOUS" , "WRITEABLE" ])
66
- ret = t .WriteAnalogF64 (len ( values ) , False , 0 ,
108
+ ret = t .WriteAnalogF64 (samps_per_channel , False , 0 ,
67
109
self .daq .DAQmx_Val_GroupByChannel , values ,
68
110
byref (num_samps_written ), None )
69
- if num_samps_written .value != len ( values ) :
111
+ if num_samps_written .value != nb_values :
70
112
raise IOError ("Error: only {} sample values were written"
71
113
.format (num_samps_written .value ))
72
114
if ret :
73
115
raise IOError ("Error while writing samples to the channel buffer" )
74
116
75
117
done_cb = self .daq .DAQmxDoneEventCallbackPtr (self .done_callback_py )
76
- self .tasks . append ( t .taskHandle )
118
+ self .task = t .taskHandle
77
119
self .daq .DAQmxRegisterDoneEvent (t .taskHandle , 0 , done_cb , None )
78
120
t .StartTask ()
79
121
122
+ def clear_pending_task (self ):
123
+ """Clear any pending task."""
124
+
125
+ if self .task is not None :
126
+ self .daq .DAQmxClearTask (self .task )
127
+ self .task = None
128
+
80
129
def close (self ):
81
- """Clear all pending tasks ."""
130
+ """Free any allocated resources ."""
82
131
83
- for t in self .tasks :
84
- self .daq .DAQmxClearTask (t )
132
+ self .clear_pending_task ()