forked from M-Labs/artiq
pxi6733: refactor, allow multiple channels in one task, cancel any previous task
This commit is contained in:
parent
398940f5ac
commit
c251601204
|
@ -14,22 +14,38 @@ class DAQmxSim:
|
||||||
def ping(self):
|
def ping(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def string_to_bytes(string, name):
|
||||||
|
if isinstance(string, str):
|
||||||
|
string = bytes(string, encoding="ascii")
|
||||||
|
elif not isinstance(string, bytes):
|
||||||
|
raise ValueError("{} must be of either str or bytes type".format(name))
|
||||||
|
return string
|
||||||
|
|
||||||
class DAQmx:
|
class DAQmx:
|
||||||
"""NI PXI6733 DAQ interface."""
|
"""NI PXI6733 DAQ interface."""
|
||||||
|
|
||||||
def __init__(self, device, analog_output, clock):
|
def __init__(self, channels, clock):
|
||||||
|
"""
|
||||||
|
:param channels: List of channels as a string or bytes(), following
|
||||||
|
the physical channels lists and ranges NI-DAQmx syntax.
|
||||||
|
|
||||||
|
Example: Dev1/ao0, Dev1/ao1:ao3
|
||||||
|
:param clock: Clock source terminal as a string or bytes(), following
|
||||||
|
NI-DAQmx terminal names syntax.
|
||||||
|
|
||||||
|
Example: PFI5
|
||||||
|
"""
|
||||||
|
|
||||||
import PyDAQmx as daq
|
import PyDAQmx as daq
|
||||||
|
|
||||||
self.device = device
|
self.channels = string_to_bytes(channels, "channels")
|
||||||
self.analog_output = analog_output
|
self.clock = string_to_bytes(clock, "clock")
|
||||||
self.clock = clock
|
self.task = None
|
||||||
self.tasks = []
|
|
||||||
self.daq = daq
|
self.daq = daq
|
||||||
|
|
||||||
def done_callback_py(self, taskhandle, status, callback_data):
|
def done_callback_py(self, taskhandle, status, callback_data):
|
||||||
self.daq.DAQmxClearTask(taskhandle)
|
if taskhandle == self.task:
|
||||||
self.tasks.remove(taskhandle)
|
self.clear_pending_task()
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
try:
|
try:
|
||||||
|
@ -42,43 +58,75 @@ class DAQmx:
|
||||||
def load_sample_values(self, sampling_freq, values):
|
def load_sample_values(self, sampling_freq, values):
|
||||||
"""Load sample values into PXI 6733 device.
|
"""Load sample values into PXI 6733 device.
|
||||||
|
|
||||||
This loads sample values into the PXI 6733 device and then
|
This loads sample values into the PXI 6733 device.
|
||||||
configures a task to output those samples at each clock rising
|
The device will output samples at each clock rising edge.
|
||||||
edge.
|
The first sample is output at the first clock rising edge.
|
||||||
|
|
||||||
A callback is registered to clear the task (deallocate resources)
|
When using several channels simultaneously, you must concatenate the
|
||||||
when the task has completed.
|
values for the different channels in the ``values`` array.
|
||||||
|
The sample values for the same channel must be grouped together.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> values = np.array([ch0_samp0, ch0_samp1, ch1_samp0, ch1_samp1],
|
||||||
|
dtype=float)
|
||||||
|
|
||||||
|
In this example the first two samples will be output via the first
|
||||||
|
channel and the two following samples will be output via the second
|
||||||
|
channel.
|
||||||
|
|
||||||
|
Any call to this method will cancel any previous task even if it has
|
||||||
|
not yet completed.
|
||||||
|
|
||||||
:param sampling_freq: The sampling frequency in samples per second.
|
:param sampling_freq: The sampling frequency in samples per second.
|
||||||
:param values: A numpy array of sample values to load in the device.
|
:param values: A numpy array of sample values to load in the device.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.clear_pending_task()
|
||||||
|
|
||||||
t = self.daq.Task()
|
t = self.daq.Task()
|
||||||
t.CreateAOVoltageChan(self.device+b"/"+self.analog_output, b"",
|
t.CreateAOVoltageChan(self.channels, b"",
|
||||||
min(values), max(values),
|
min(values), max(values),
|
||||||
self.daq.DAQmx_Val_Volts, None)
|
self.daq.DAQmx_Val_Volts, None)
|
||||||
|
|
||||||
|
channel_number = self.daq.int32()
|
||||||
|
t.GetTaskNumChans(byref(channel_number))
|
||||||
|
nb_values = len(values)
|
||||||
|
if nb_values % channel_number.value > 0:
|
||||||
|
self.daq.DAQmxClearTask(t.taskHandle)
|
||||||
|
raise ValueError("The size of the values array must be a multiple "
|
||||||
|
"of the number of channels ({})"
|
||||||
|
.format(channel_number.value))
|
||||||
|
samps_per_channel = nb_values // channel_number
|
||||||
|
|
||||||
t.CfgSampClkTiming(self.clock, sampling_freq,
|
t.CfgSampClkTiming(self.clock, sampling_freq,
|
||||||
self.daq.DAQmx_Val_Rising,
|
self.daq.DAQmx_Val_Rising,
|
||||||
self.daq.DAQmx_Val_FiniteSamps, len(values))
|
self.daq.DAQmx_Val_FiniteSamps, samps_per_channel)
|
||||||
num_samps_written = self.daq.int32()
|
num_samps_written = self.daq.int32()
|
||||||
values = np.require(values, dtype=float,
|
values = np.require(values, dtype=float,
|
||||||
requirements=["C_CONTIGUOUS", "WRITEABLE"])
|
requirements=["C_CONTIGUOUS", "WRITEABLE"])
|
||||||
ret = t.WriteAnalogF64(len(values), False, 0,
|
ret = t.WriteAnalogF64(samps_per_channel, False, 0,
|
||||||
self.daq.DAQmx_Val_GroupByChannel, values,
|
self.daq.DAQmx_Val_GroupByChannel, values,
|
||||||
byref(num_samps_written), None)
|
byref(num_samps_written), None)
|
||||||
if num_samps_written.value != len(values):
|
if num_samps_written.value != nb_values:
|
||||||
raise IOError("Error: only {} sample values were written"
|
raise IOError("Error: only {} sample values were written"
|
||||||
.format(num_samps_written.value))
|
.format(num_samps_written.value))
|
||||||
if ret:
|
if ret:
|
||||||
raise IOError("Error while writing samples to the channel buffer")
|
raise IOError("Error while writing samples to the channel buffer")
|
||||||
|
|
||||||
done_cb = self.daq.DAQmxDoneEventCallbackPtr(self.done_callback_py)
|
done_cb = self.daq.DAQmxDoneEventCallbackPtr(self.done_callback_py)
|
||||||
self.tasks.append(t.taskHandle)
|
self.task = t.taskHandle
|
||||||
self.daq.DAQmxRegisterDoneEvent(t.taskHandle, 0, done_cb, None)
|
self.daq.DAQmxRegisterDoneEvent(t.taskHandle, 0, done_cb, None)
|
||||||
t.StartTask()
|
t.StartTask()
|
||||||
|
|
||||||
def close(self):
|
def clear_pending_task(self):
|
||||||
"""Clear all pending tasks."""
|
"""Clear any pending task."""
|
||||||
|
|
||||||
for t in self.tasks:
|
if self.task is not None:
|
||||||
self.daq.DAQmxClearTask(t)
|
self.daq.DAQmxClearTask(self.task)
|
||||||
|
self.task = None
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Free any allocated resources."""
|
||||||
|
|
||||||
|
self.clear_pending_task()
|
||||||
|
|
|
@ -11,13 +11,11 @@ from artiq.tools import verbosity_args, init_logger, simple_network_args
|
||||||
def get_argparser():
|
def get_argparser():
|
||||||
parser = argparse.ArgumentParser(description="NI PXI 6733 controller")
|
parser = argparse.ArgumentParser(description="NI PXI 6733 controller")
|
||||||
simple_network_args(parser, 3256)
|
simple_network_args(parser, 3256)
|
||||||
parser.add_argument("-d", "--device", default=None,
|
parser.add_argument("-C", "--channels", default=None,
|
||||||
help="Device name (e.g. Dev1)."
|
help="List of channels (e.g. Dev1/ao0, Dev1/ao1:3)."
|
||||||
" Omit for simulation mode.")
|
" Omit for simulation mode.")
|
||||||
parser.add_argument("-c", "--clock", default="PFI5",
|
parser.add_argument("-c", "--clock", default="PFI5",
|
||||||
help="Input clock pin name (default: PFI5)")
|
help="Input clock pin name (default: PFI5)")
|
||||||
parser.add_argument("-a", "--analog-output", default="ao0",
|
|
||||||
help="Analog output pin name (default: ao0)")
|
|
||||||
verbosity_args(parser)
|
verbosity_args(parser)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
@ -26,12 +24,11 @@ def main():
|
||||||
args = get_argparser().parse_args()
|
args = get_argparser().parse_args()
|
||||||
init_logger(args)
|
init_logger(args)
|
||||||
|
|
||||||
if args.device is None:
|
if args.channels is None:
|
||||||
daq = DAQmxSim()
|
daq = DAQmxSim()
|
||||||
else:
|
else:
|
||||||
daq = DAQmx(bytes(args.device, "ascii"),
|
daq = DAQmx(args.channels,
|
||||||
bytes(args.analog_output, "ascii"),
|
args.clock)
|
||||||
bytes(args.clock, "ascii"))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
simple_server_loop({"pxi6733": daq},
|
simple_server_loop({"pxi6733": daq},
|
||||||
|
|
Loading…
Reference in New Issue