Esempio n. 1
0
def AI_start_delay(device_name):
    if 'PFI0' not in DAQmxGetDevTerminals(device_name):
        return None
    task = Task()
    clock_terminal = '/' + device_name + '/PFI0'
    rate = DAQmxGetDevAIMaxSingleChanRate(device_name)
    Vmin, Vmax = DAQmxGetDevAIVoltageRngs(device_name)[0:2]
    num_samples = 1000
    chan = device_name + '/ai0'
    task.CreateAIVoltageChan(chan, "", c.DAQmx_Val_RSE, Vmin, Vmax,
                             c.DAQmx_Val_Volts, None)
    task.CfgSampClkTiming("", rate, c.DAQmx_Val_Rising, c.DAQmx_Val_ContSamps,
                          num_samples)
    task.CfgDigEdgeStartTrig(clock_terminal, c.DAQmx_Val_Rising)

    start_trig_delay = float64()
    delay_from_sample_clock = float64()
    sample_timebase_rate = float64()

    task.GetStartTrigDelay(start_trig_delay)
    task.GetDelayFromSampClkDelay(delay_from_sample_clock)
    task.GetSampClkTimebaseRate(sample_timebase_rate)

    task.ClearTask()

    total_delay_in_ticks = start_trig_delay.value + delay_from_sample_clock.value
    total_delay_in_seconds = total_delay_in_ticks / sample_timebase_rate.value
    return total_delay_in_seconds
Esempio n. 2
0
def AI_start_delay(device_name):
    """Empirically determines the analog inputs' start delay.

    Args:
        device_name (str): NI-MAX device name

    Returns:
        float: Analog input start delay in seconds. `None` if
        analog inputs not supported.
    """
    if 'PFI0' not in DAQmxGetDevTerminals(device_name):
        return None
    task = Task()
    clock_terminal = '/' + device_name + '/PFI0'
    rate = DAQmxGetDevAIMaxSingleChanRate(device_name)
    Vmin, Vmax = DAQmxGetDevAIVoltageRngs(device_name)[0:2]
    num_samples = 1000
    chan = device_name + '/ai0'
    supp_types = DAQmxGetPhysicalChanAITermCfgs(chan)
    if supp_types & c.DAQmx_Val_Bit_TermCfg_RSE:
        input_type = c.DAQmx_Val_RSE
    elif supp_types & c.DAQmx_Val_Bit_TermCfg_Diff:
        input_type = c.DAQmx_Val_Diff
    elif supp_types & c.DAQmx_Val_Bit_TermCfg_PseudoDIFF:
        input_type = c.DAQmx_Val_PseudoDiff
    task.CreateAIVoltageChan(
        chan, "", input_type, Vmin, Vmax, c.DAQmx_Val_Volts, None
    )
    task.CfgSampClkTiming(
        "", rate, c.DAQmx_Val_Rising, c.DAQmx_Val_ContSamps, num_samples
    )
    task.CfgDigEdgeStartTrig(clock_terminal, c.DAQmx_Val_Rising)

    start_trig_delay = float64()
    delay_from_sample_clock = float64()
    sample_timebase_rate = float64()

    try:
        task.GetStartTrigDelay(start_trig_delay)
    except PyDAQmx.DAQmxFunctions.AttributeNotSupportedInTaskContextError:
        # device does not have a Start Trigger Delay property
        # is likely a dynamic signal acquisition device with filter
        # delays instead. 
        start_trig_delay.value = 0
    try:
        task.GetDelayFromSampClkDelay(delay_from_sample_clock)
    except PyDAQmx.DAQmxFunctions.AttributeNotSupportedInTaskContextError:
        # seems simultaneous sampling devices do not have this property, 
        # so assume it is zero
        delay_from_sample_clock.value = 0
    task.GetSampClkTimebaseRate(sample_timebase_rate)

    task.ClearTask()

    total_delay_in_ticks = start_trig_delay.value + delay_from_sample_clock.value
    total_delay_in_seconds = total_delay_in_ticks / sample_timebase_rate.value
    return total_delay_in_seconds
Esempio n. 3
0
def port_supports_buffered(device_name, port, clock_terminal=None):
    """Empirically determines if the digital port supports buffered output.

    Args:
        device_name (str): NI-MAX device name
        port (int): Which port to intro-spect
        clock_terminal (str, optional): String that specifies the clock terminal.

    Returns:
        bool: True if `port` supports buffered output.
    """
    all_terminals = DAQmxGetDevTerminals(device_name)
    if clock_terminal is None:
        clock_terminal = all_terminals[0]
    npts = 16
    task = Task()
    clock_terminal_full = '/' + device_name + '/' + clock_terminal
    data = np.zeros(npts, dtype=np.uint32)
    task.CreateDOChan(device_name + "/" + port, "", c.DAQmx_Val_ChanForAllLines)
    task.CfgSampClkTiming(
        clock_terminal_full, 100, c.DAQmx_Val_Rising, c.DAQmx_Val_FiniteSamps, npts
    )
    written = int32()
    try:
        task.WriteDigitalU32(
            npts, False, 10.0, c.DAQmx_Val_GroupByScanNumber, data, byref(written), None
        )
    except (
        PyDAQmx.DAQmxFunctions.BufferedOperationsNotSupportedOnSelectedLinesError,
        PyDAQmx.DAQmxFunctions.PhysicalChanNotSupportedGivenSampTimingType653xError,
    ):
        return False
    except (
        PyDAQmx.DAQmxFunctions.CantUsePort3AloneGivenSampTimingTypeOn653xError,
        PyDAQmx.DAQmxFunctions.CantUsePort1AloneGivenSampTimingTypeOn653xError,
    ):
        # Ports that throw this error on 653x devices do support buffered output, though
        # there are requirements that multiple ports be used together.
        return True
    except PyDAQmx.DAQmxFunctions.RouteNotSupportedByHW_RoutingError:
        # Try again with a different terminal
        current_terminal_index = all_terminals.index(clock_terminal)
        if current_terminal_index == len(all_terminals) - 1:
            # There are no more terminals. No terminals can be used as clocks,
            # therefore we cannot do externally clocked buffered output.
            return False
        next_terminal_to_try = all_terminals[current_terminal_index + 1]
        return port_supports_buffered(device_name, port, next_terminal_to_try)
    else:
        return True
    finally:
        task.ClearTask()
Esempio n. 4
0
 def __init__(self):
     freq1 = 10
     freq2 = 20
     freq3 = 30
     sampRate = 1000
     modAmp = 5
     N = 1000
     self.data = zeros(3, dtype='f8')
     th = Task()  #Task.__init__(self)
     th.CreateAOVoltageChan("Dev12/ao0:2", "", -10, 10, DAQmx_Val_Volts,
                            None)
     th.CfgSampClkTiming("", sampRate, DAQmx_Val_Rising,
                         DAQmx_Val_ContSamps, N)
     self.th = th
Esempio n. 5
0
def connect_analog_io_port_RSE(devport, samples, rate):
    '''
    Initialize task for reading from analog input port for Voltage measurement
    in    devport = Device/port e.g. Dev1/ai2
    out   task = Task handle
    '''
    logger.info("temp: {}".format(devport))
    max_num_samples = samples
    task = Task()
    task.CreateAIVoltageChan(devport, '', DAQmx_Val_RSE, -10.0, 10.0,
                             DAQmx_Val_Volts, None)
    task.CfgSampClkTiming('', float64(rate), DAQmx_Val_Rising,
                          DAQmx_Val_FiniteSamps, uInt64(max_num_samples))
    task.StartTask()
    return task
def init_task():

    amplifier = Task()
    amplifier.CreateAOVoltageChan("Dev1/ao0", "", -10.0, 10.0, DAQmx_Val_Volts,
                                  None)
    amplifier.StartTask()

    laser_sensor = Task()
    laser_sensor.CreateAIVoltageChan("Dev1/ai0", "", DAQmx_Val_Cfg_Default,
                                     -10.0, 10.0, DAQmx_Val_Volts, None)
    laser_sensor.CfgSampClkTiming("", 10000.0, DAQmx_Val_Rising,
                                  DAQmx_Val_ContSamps, 1000)
    laser_sensor.StartTask()

    return amplifier, laser_sensor
Esempio n. 7
0
 def __init__(self,
              Nsamps=1000,
              sampRate=1000,
              modAmpL=[5e-3, 5e-3, 5e-3],
              modFreqL=[5, 3, 1]):
     self.data = zeros(3, dtype='f8')
     self.Vx, self.Vy, self.Vz = self.data
     th = Task()  #Task.__init__(self)
     th.CreateAOVoltageChan("Dev12/ao0:2", "", -10, 10, DAQmx_Val_Volts,
                            None)
     th.CfgSampClkTiming("", sampRate, DAQmx_Val_Rising,
                         DAQmx_Val_ContSamps, Nsamps)
     self.th = th
     self.Nsamps = Nsamps
     self.sampRate = sampRate
     self.modFreqL = modFreqL
     self.modAmpL = modAmpL
     self.wvfm = zeros((3, Nsamps), dtype='f8')
     self.t = np.linspace(0, Nsamps / sampRate, Nsamps)
Esempio n. 8
0
def trigger_analog_output(devport, edge_selection):
    '''
    Using PFI to Trigger an Analog Output Generation
    in    devport = Device/port e.g. Dev2/PFI0
    out   task = Task handle
    '''
    logger.info("testlog, Device: {}".format(devport))
    max_num_samples = 2
    task = Task()
    task.CreateAOVoltageChan(devport, '', -10.0, 10.0, DAQmx_Val_Volts, None)
    task.CfgSampClkTiming('', float64(100), DAQmx_Val_Rising,
                          DAQmx_Val_FiniteSamps, uInt64(max_num_samples))

    if edge_selection == 'R':
        task.CfgDigEdgeStartTrig("/Dev2/PFI1", DAQmx_Val_Rising)
        #logger.info("testlog, trigger analog output -function, Rising: {}")
    else:
        task.CfgDigEdgeStartTrig("/Dev2/PFI1", DAQmx_Val_Falling)
        #logger.info("testlog, trigger analog output -function, Falling: {}")
    return task
Esempio n. 9
0
def AI_filter_delay(device_name):
    """Determine the filter delay for dynamic signal acquistion devices.

    Returns the delay in clock cycles. Absolute delay will vary with sample rate.
    
    Args:
        device_name (str): NI-MAX device name

    Returns:
        int: Number of analog input delays ticks between task start and acquisition start.
    """
    if 'PFI0' not in DAQmxGetDevTerminals(device_name):
        return None
    task = Task()
    clock_terminal = '/' + device_name + '/PFI0'
    rate = DAQmxGetDevAIMaxSingleChanRate(device_name)
    Vmin, Vmax = DAQmxGetDevAIVoltageRngs(device_name)[0:2]
    num_samples = 1000
    chan = device_name + '/ai0'
    task.CreateAIVoltageChan(
        chan, "", c.DAQmx_Val_PseudoDiff, Vmin, Vmax, c.DAQmx_Val_Volts, None
    )
    task.CfgSampClkTiming(
        "", rate, c.DAQmx_Val_Rising, c.DAQmx_Val_ContSamps, num_samples
    )
    task.CfgDigEdgeStartTrig(clock_terminal, c.DAQmx_Val_Rising)

    start_filter_delay = float64()
    sample_timebase_rate = float64()

    # get delay in number of clock samples
    task.SetAIFilterDelayUnits("", c.DAQmx_Val_SampleClkPeriods)

    task.GetAIFilterDelay("", start_filter_delay)
    task.GetSampClkTimebaseRate(sample_timebase_rate)

    task.ClearTask()

    return int(start_filter_delay.value)
Esempio n. 10
0
def trigger_analog_input(devport, samples, rate, edge_selection):
    '''
    Using PFI to Trigger an Analog Input Acquisition
    in    devport = Device/port e.g. Dev2/PFI0
    out   task = Task handle
    '''
    logger.info("testlog, Device: {}".format(devport))
    max_num_samples = samples
    task = Task()
    task.CreateAIVoltageChan(devport, '', DAQmx_Val_RSE, -10.0, 10.0,
                             DAQmx_Val_Volts, None)
    task.CfgSampClkTiming('', float64(rate), DAQmx_Val_Rising,
                          DAQmx_Val_FiniteSamps, uInt64(max_num_samples))
    #logger.info("trigger analog input -function, 1: {}")
    if edge_selection == 'R':
        task.CfgDigEdgeStartTrig("/Dev2/PFI0", DAQmx_Val_Rising)
        logger.info("testlog, trigger analog input -function, Rising: {}")
    else:
        task.CfgDigEdgeStartTrig("/Dev2/PFI0", DAQmx_Val_Falling)
        logger.info("testlog, trigger analog input -function, Falling: {}")
    task.StartTask()
    return task
Esempio n. 11
0
class DigitalOutput(object):
    #-- Sample Rate Property
    ## This function returns the sample rate configured in the DAQmx Task.
    #  @param self The object reference.
    def getSampleRate(self):
        if self.initialized:
            sampleRate = float64()
            self.status = self.taskRef.GetSampClkRate(ctypes.byref(sampleRate))
            self._sampleRate = sampleRate.value
        return self._sampleRate

    ## This function sets the sample rate in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the sample rate.
    def setSampleRate(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkRate(float64(value))
        self._sampleRate = value

    ## This function deletes the sample rate variable inside the
    #  DigitalOutput object.
    #  @param self The object reference.
    def _delSampleRate(self):
        del self._sampleRate

    sampleRate = property(getSampleRate,
                          setSampleRate,
                          _delSampleRate,
                          doc="""The sample rate of the digital output.""")

    #-- Samples Per Channel Property

    ## This function returns the samples per channel configured in the
    #  DAQmx Task.
    #  @param self The object reference.
    def getSamplesPerChannel(self):
        if self.initialized:
            samplesPerChannel = uInt64()
            self.status = self.taskRef.GetSampQuantSampPerChan(
                ctypes.byref(samplesPerChannel))
            self._samplesPerChannel = samplesPerChannel.value
        return self._samplesPerChannel

    ## This function sets the samples per channel in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the samples per channel.
    def setSamplesPerChannel(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampQuantSampPerChan(uInt64(value))
        self._samplesPerChannel = value

    ## This function deletes the samplesPerChannel variable from the
    #  DigitalOutput object.
    #  @param self The object reference.
    def _delSamplesPerChannel(self):
        del self._samplesPerChannel

    samplesPerChannel = property(
        getSamplesPerChannel, setSamplesPerChannel, _delSamplesPerChannel,
        """The samples per channel of the digital output.""")

    #-- Clock Source Property

    ## This function returns the sample clock source configured in the
    #  DAQmx Task.
    #  @param self The object reference.
    def getClkSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetSampClkSrc(buff, buffSize)
            self._clkSource = buff.value
        return self._clkSource

    ## This function sets the sample clock source in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the clock source.
    def setClkSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkSrc(value)
            value = self.getClkSource()
        self._clkSource = value

    ## This function deletes the clkSource variable within the
    #  DigitalOutput
    #  object.
    #  @param self The object reference.
    def _delClkSource(self):
        del self._clkSource

    clkSource = property(
        getClkSource, setClkSource, _delClkSource,
        """The clock source for the digital outputsample clock.""")

    #-- Start Trigger Property
    ## This function returns the start trigger source configured in the
    #  DAQmx Task.
    #  @param self The object reference.
    def _getStartTriggerSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetDigEdgeStartTrigSrc(buff, buffSize)
            self._startTriggerSource = buff.value
        return self._startTriggerSource

    ## This function sets the start trigger source in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the start trigger.
    def _setStartTriggerSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetDigEdgeStartTrigSrc(value)
            value = self.getStartTriggerSource()
        self._startTriggerSource = value

    startTriggerSource = property(_getStartTriggerSource,
                                  _setStartTriggerSource)

    #-- Done Property
    def _getDone(self):
        done = bool32()
        if self.initialized:
            self.status = self.taskRef.GetTaskComplete(ctypes.byref(done))
        else:
            done.value = 1
        return bool(done.value)

    ## @var done
    #  Returns the task done status.
    #
    #  This mode works differently depending on the mode. <br />
    #  <ul>
    #  <li><B>Static and Continuous</B>: done is false after a start
    #  method and true</li>
    #  only after a stop method.
    #  <li><B>Finite</B>: done is false until all samples are
    #  generated.</li></ul>
    done = property(_getDone)

    #-- Pause Trigger Source
    def _getPauseTriggerSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetDigLvlPauseTrigSrc(buff, buffSize)
            self._pauseTriggerSource = buff.value
        return self._pauseTriggerSource

    def _setPauseTriggerSource(self, value):
        if self.initialized:
            if value == '':
                self.status = self.taskRef.SetPauseTrigType(DAQmx_Val_None)
                self.status = self.taskRef.ResetDigLvlPauseTrigSrc()
            else:
                self.status = self.taskRef.SetDigLvlPauseTrigWhen(
                    DAQmx_Val_High)
                self.status = self.taskRef.SetPauseTrigType(DAQmx_Val_DigLvl)
                self.status = self.taskRef.SetDigLvlPauseTrigSrc(value)
        self._pauseTriggerSource = value

    pauseTriggerSource = property(_getPauseTriggerSource,
                                  _setPauseTriggerSource)

    #-------------------- Functions --------------------

    ## This function is a constructor for the DigitalOutput class.
    #
    #  It creates the internal variables required to perform functions
    #  within the class. This function does not initialize any hardware.
    #  @param self This object reference
    def __init__(self):
        ## The DAQmx task reference.
        self.taskRef = Task()

        ## This is the status of the DAQmx task.
        #
        #  A value greater than 0 means that an error has occurred. When
        #  the status is greater than 0 an error should be reported by
        #  the class.
        self.status = int32()

        ## This is a boolean that is true when the DAQmx task has been
        #  initialized.
        self.initialized = False

        ## @var sampleRate
        #  This is the sample rate of the digital output.
        self._sampleRate = 100e3

        ## @var samplesPerChannel
        #  This is the number of samples per channel that will be generated
        #  in Finite mode.
        self._samplesPerChannel = 100

        ## @var clkSource
        #  This is the sample clock source terminal.  It can be set to an
        #  internal clock or external clock such as a PFI line i.e.
        #  "/PXI1Slot3/PFI15."
        self._clkSource = ''

        ## @var startTriggerSource
        #  This is the start trigger source terminal.  It can be set to
        #  a PFI line such as "/PXISlot3/PFI0"
        self._startTriggerSource = ''

        ## @var pauseTriggerSource
        #  The source terminal of the pause trigger.  This can be
        #  any PFI or backplane trigger such as 'PFI5' and 'PXI_TRIG5'
        self._pauseTriggerSource = ''

        ## This is the mode of operation for the digital outputs.
        #
        #  There are currently three modes available.  Static mode is
        #  where one static digital sample is set with no need for a
        #  sample clock.  Finite mode is where a finite number of digital
        #  samples will be set at a sample clock rate. Continuous mode is
        #  where a sequence of voltages are generated at a sample rate and
        #  then repeated until the stop() method is called.
        self.mode = dutil.Mode.Finite

        self.triggerType = dutil.TriggerType.Software

        ## The number of time to iterate over a Finite number of samples.
        #
        #  This value is only useful in the "Finite" mode.  It is the
        #  number of times that a sequence of digital samples will be
        #  looped.  The default is allways 1.
        self.loops = 1

        ##  The time in seconds to wait for a trigger or for a digital
        #   state change.
        self.timeout = 1

        self.timeoutPad = 10

        self._pCh = ''

    ## Initialize the digital outputs based on the object's configuration.
    #  @param self The object reference.
    #  @param physicalChannel A string representing the device and digital
    #  output channels. Example value: "PXI1Slot3/ao0:7"
    def init(self, physicalChannel):
        self._pCh = physicalChannel
        self.__createTask(physicalChannel)
        self.initialized = True

        #Finite Mode
        if self.mode == dutil.Mode.Finite:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_FiniteSamps)

        #Continuous Mode
        if self.mode == dutil.Mode.Continuous:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_ContSamps)

        #Static Mode
        if self.mode == dutil.Mode.Static:
            pass

    ## This function returns a random 1D numpy array of samples for
    #  writing the buffer of digital output channels.
    #  @param self The objet reference.
    def createTestBuffer(self):
        import numpy
        data = numpy.random.rand(self._samplesPerChannel)
        data = numpy.ubyte(data * 255)
        return data

    ## This function returns the number of digital lines configured in
    #  the DAQmx Task.
    #  @param self The object reference.
    def getNumLines(self):
        numLines = uInt32()
        #bufferSize = 255
        #channel = ctypes.create_string_buffer(bufferSize)
        #self.taskRef.GetTaskChannels(channel, bufferSize)
        #print channel.value
        self.taskRef.GetDONumLines('', ctypes.byref(numLines))
        return numLines.value

    ## This function returns the number of digital channels configured in
    #  the DAQmx Task.
    #  @param self The object reference.
    def getNumChannels(self):
        numChannels = uInt32()
        self.taskRef.GetTaskNumChans(ctypes.byref(numChannels))
        return numChannels.value

    ## This function writes the specified values into the buffer.
    #  @param self The object reference.
    #  @param data This is a 1D 8-bit unsigned integer array that
    #  contians samples for each digital channel. Channels are
    #  non-interleaved (channel1 n-samples then channel2 n-samples).
    def writeToBuffer(self, data):
        autostart = self.mode == dutil.Mode.Static
        samplesWritten = int32()
        self.buff = data
        self.status = self.taskRef.WriteDigitalU8(self._samplesPerChannel,
                                                  autostart, 10,
                                                  DAQmx_Val_GroupByChannel,
                                                  data,
                                                  ctypes.byref(samplesWritten),
                                                  None)
        #print 'Samples Written: ' + str(samplesWritten.value)
        return samplesWritten.value

    ## This function writes a static value to the digital line(s)
    #  configured in the init() method.
    #  @param self The object reference.
    #  @param data The static value to send to the digital line(s).
    def writeStatic(self, data):
        if isinstance(data, bool) and data == True:
            digLineNum = int(self._pCh[len(self._pCh) - 1])
            data = 2**digLineNum
        autostart = True
        self.status = self.taskRef.WriteDigitalScalarU32(
            autostart, float64(self.timeout), uInt32(data), None)

    ## This function starts the digital output generation.
    #  @param self The object reference.
    def start(self):
        self.status = self.taskRef.StartTask()

    ## This functions waits for the digital output generation to complete.
    #  @param self The object reference.
    def waitUntilDone(self):
        sampPerChan = uInt64()
        self.status = self.taskRef.GetSampQuantSampPerChan(
            ctypes.byref(sampPerChan))
        #print 'DO Samples Per Channel: ' + str(sampPerChan.value)
        estAcqTime = (self.loops * sampPerChan.value) / self._sampleRate

        #print "Estimated Acquisition Time: " + str(estAcqTime)

        if self.mode != dutil.Mode.Static:
            self.status = self.taskRef.WaitUntilTaskDone(
                float64(estAcqTime + self.timeoutPad))

    ## This function stops the digital output generation.
    #  @param self The object reference.
    def stop(self):
        self.status = self.taskRef.StopTask()

    ## This is a private method that creates the Task object for use
    #  inside the DigitalOutput class.
    def __createTask(self, physicalChannel):
        self.status = self.taskRef.CreateDOChan(physicalChannel, '',
                                                DAQmx_Val_ChanForAllLines)

    ## This is a private method that configures the timing for the
    #  DigitalOutput class.
    #  @param self The object reference.
    def __configTiming(self, sampleMode):
        totalSamples = self._samplesPerChannel * self.loops
        if self.triggerType == dutil.TriggerType.Software:
            self.status = self.taskRef.CfgSampClkTiming(
                self._clkSource, float64(self._sampleRate), DAQmx_Val_Falling,
                sampleMode, uInt64(totalSamples))
        elif self.triggerType == dutil.TriggerType.Hardware:
            self.status = self.taskRef.CfgSampClkTiming(
                self._clkSource, float64(self._sampleRate), DAQmx_Val_Falling,
                sampleMode, uInt64(totalSamples))
            self.status = self.taskRef.CfgDigEdgeStartTrig(
                self._startTriggerSource, DAQmx_Val_Falling)

    ## This function will close connection to the digital ouput device and
    #  channels.
    #  @param self The object reference.
    def close(self):
        self.initialized = False
        self.status = self.taskRef.ClearTask()
        self.taskRef = Task()

    ## This is the destructor for the DigitalOutput Class.
    #  @param self The object reference.
    def __del__(self):
        if self.initialized:
            self.close()

        del self.taskRef
        del self.status
        del self.initialized
        del self.sampleRate
        del self.samplesPerChannel
        del self.clkSource
        del self.mode
        del self.loops
        del self.timeout
        del self.timeoutPad
        del self._pCh
Esempio n. 12
0
class daq(object):
    """This class prepares and configures a DAQ system for an analog adquisition. The required
    arguments are the name of the device as a string (p.e. 'Dev3'), the adquisition time in seconds
    (adqTime), the sampling frequency in Hz (adqFreq), the channels to read as a list of integer values
    with the available channels, the voltage range of the channels as a list with the maximum positive
    voltage value and with the same length as the channel list.
    
    Optional arguments:
        - mode = 'FiniteSamps': sets the adquisiton mode to finite sampling (default) or continuous 'ContSamps'
        - trigger = None: sets the trigger mode. If None it is taken as the clock edge. Otherwise,
                    a 2-element list can be passed, the first value being the channel number and the second
                    the voltage value to configure the trigger to an analog channel in rising mode.
    
    This class only contemplates reading volts signal of analog inputs in differential configuration.
    Future versions might implement other possibilities."""
    def __init__(self,
                 device,
                 adqTime,
                 adqFreq,
                 list_channels,
                 rang_channels,
                 mode='FiniteSamps',
                 trigger=None,
                 terminalConfig='diff'):
        """This will configure the DAQ and Task for the specified configuration"""
        self.device = device
        self.adqTime = adqTime  # In seconds
        self.adqFreq = adqFreq
        self.list_channels = list_channels
        self.rang_channels = rang_channels
        self.mode = mode
        self.N_samples = int(adqTime * adqFreq)
        self.N_channels = len(list_channels)
        if terminalConfig is 'diff':
            self.terminalConfig = DAQmx_Val_Diff
        elif terminalConfig is 'SE':
            self.terminalConfig = DAQmx_Val_RSE
        else:
            raise Error(
                'Terminal configuration "{}" not known'.format(terminalConfig))
        print('I will adquire {} samples for each {} channels'.format(
            self.N_samples, self.N_channels))
        # DAQmx Configure Code
        # We create a Task instance
        self.ai = Task()
        # Prepare the type of variable that it returns using the library ctypes that work with C-functions
        self.read = int32()
        # The vector data will contain the output of the DAQ card in a single vector with the data from different channels
        # in a single 1-D vector with the data concatenated
        self.data = np.zeros((self.N_samples * self.N_channels, ),
                             dtype=np.float64)
        #self.ai = Task()
        # This prepares the string that is needed for the ``CreateAIVoltageChan`` depending on the number of channels to
        # read
        for channel, Vrange in zip(self.list_channels, self.rang_channels):
            str_channels = r"{}/ai{:d}".format(self.device, channel)
            #print(r"{}/ai{:d}".format(self.device,channel) )
            self.ai.CreateAIVoltageChan(str_channels, "", self.terminalConfig,
                                        -1.0 * Vrange, Vrange, DAQmx_Val_Volts,
                                        None)

        if mode is 'FiniteSamps':
            self.ai.CfgSampClkTiming("", self.adqFreq, DAQmx_Val_Rising,
                                     DAQmx_Val_FiniteSamps, self.N_samples)
        elif mode is 'ContSamps':
            self.ai.CfgSampClkTiming("", self.adqFreq, DAQmx_Val_Rising,
                                     DAQmx_Val_ContSamps, self.N_samples)
        else:
            raise Error('Mode not known')
        if trigger is None:
            pass
        else:
            self.ai.CfgAnlgEdgeStartTrig(
                r"{}/ai{:d}".format(self.device, trigger[0]),
                DAQmx_Val_RisingSlope, trigger[1])
        # In the case the DAQ accept a trigger from one of the analog channels we could also setup it as
        #
    def start(self):
        self.ai.StartTask()

    def stop(self):
        self.ai.StopTask()
        self.data = np.zeros((self.N_samples * self.N_channels, ),
                             dtype=np.float64)

    def read_analog(self, timeout=10.0):
        """Calls the ReadAnalogF64 with the configured parameters.
        
        Optional arguments:
            - timeout = 10.0: timeout in number of seconds.
        """
        self.ai.ReadAnalogF64(self.N_samples, timeout,
                              DAQmx_Val_GroupByChannel,
                              self.data, self.N_samples * self.N_channels,
                              byref(self.read), None)
        return self.data.reshape((self.N_samples, self.N_channels), order="F")

    def clear(self):
        self.ai.ClearTask()
Esempio n. 13
0
class DigitalOutput(object):

    ## This function returns the sample rate configured in the DAQmx Task.
    #  @param self The object pointer.
    def getSampleRate(self):
        if self.initialized:
            sampleRate = float64()
            self.status = self.taskRef.GetSampClkRate(ctypes.byref(sampleRate))
            self._sampleRate = sampleRate.value
        return self._sampleRate

    ## This function sets the sample rate in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value to set the sample rate.
    def setSampleRate(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkRate(float64(value))
        self._sampleRate = value

    ## This function deletes the sample rate variable inside the DigitalOutput
    #  object.
    #  @param self The object pointer.
    def _delSampleRate(self):
        del self._sampleRate

    sampleRate = property(getSampleRate, setSampleRate, _delSampleRate, doc=
                             """The sample rate of the digital output.""")

#-- Samples Per Channel Property

    ## This function returns the samples per channel configured in the DAQmx Task.
    #  @param self The object pointer.
    def getSamplesPerChannel(self):
        if self.initialized:
            samplesPerChannel = uInt64()
            self.status = self.taskRef.GetSampQuantSampPerChan(
                    ctypes.byref(samplesPerChannel))
            self._samplesPerChannel = samplesPerChannel.value
        return self._samplesPerChannel

    ## This function sets the samples per channel in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value to set the samples per channel.
    def setSamplesPerChannel(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampQuantSampPerChan(uInt64(value))
        self._samplesPerChannel = value

    ## This function deletes the samplesPerChannel variable from the DigitalOutput
    #  object.
    #  @param self The object pointer.
    def _delSamplesPerChannel(self):
        del self._samplesPerChannel

    samplesPerChannel = property(getSamplesPerChannel, setSamplesPerChannel,
            _delSamplesPerChannel,
            """The samples per channel of the digital output.""")

#-- Clock Source Property

    ## This function returns the sample clock source configured in the DAQmx Task.
    #  @param self The object pointer.
    def getClkSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetSampClkSrc(buff, buffSize)
            self._clkSource = buff.value
        return self._clkSource

    ## This function sets the sample clock source in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value to set the clock source.
    def setClkSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkSrc(value)
            value = self.getClkSource()
        self._clkSource = value

    ## This function deletes the clkSource variable within the DigitalOutput
    #  object.
    #  @param self The object pointer.
    def _delClkSource(self):
        del self._clkSource

    clkSource = property(getClkSource, setClkSource, _delClkSource,
    """The clock source for the digital outputsample clock.""")

#-------------------- Functions --------------------

    ## This function is a constructor for the DigitalOutput class.
    #
    #  It creates the internal variables required to perform functions within
    #  the class. This function does not initialize any hardware.
    #  @param self This object pointer
    def __init__(self):
        ## The DAQmx task reference.
        self.taskRef = Task()

        ## This is the status of the DAQmx task.
        #
        #  A value greater than 0 means that an error has occurred. When the
        #  status is greater than 0 an error should be reported by the class.
        self.status = int32()

        ## This is a boolean that is true when the DAQmx task has been
        #  initialized.
        self.initialized = False

        ## @var sampleRate
        #  This is the sample rate of the digital output.
        self._sampleRate = 100e3

        ## @var samplesPerChannel
        #  This is the number of samples per channel that will be generated in
        #  Finite mode.
        self._samplesPerChannel = 100

        ## @var clkSource
        #  This is the sample clock source terminal.  It can be set to an
        #  internal clock or external clock such as a PFI line i.e. "/PXI1Slot3/PFI15."
        self._clkSource = ''

        ## This is the mode of operation for the digital outputs.
        #
        #  There are currently three modes available.  Static mode is where one
        #  static digital sample is set with no need for a sample clock.
        #  Finite mode is where a finite number of digital samples will be set
        #  at a sample clock rate. Continuous mode is where a sequence of
        #  voltages are generated at a sample rate and then repeated until the
        #  stop() method is called.
        self.mode = dutil.Mode.Finite

        ## The number of time to iterate over a Finite number of samples.
        #
        #  This value is only useful in the "Finite" mode.  It is the number of
        #  times that a sequence of digital samples will be looped.  The default
        #  is allways 1.
        self.loops = 1
    
    ## Initialize the digital outputs based on the object's configuration.
    #  @param self The object pointer.
    #  @param physicalChannel A string representing the device and digital
    #  output channels. Example value: "PXI1Slot3/ao0:7"
    def init(self, physicalChannel):
        self.__createTask(physicalChannel)
        self.initialized = True
        
        #Finite Mode
        if self.mode == dutil.Mode.Finite:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_FiniteSamps)
            
        #Continuous Mode
        if self.mode == dutil.Mode.Continuous:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_ContSamps)
            
        #Static Mode
        if self.mode == dutil.Mode.Static:
            pass
        
    ## This function returns a random 1D numpy array of samples for writing the
    #  buffer of digital output channels.
    #  @param self The objet pointer.
    def createTestBuffer(self):
        data = numpy.random.rand(self._samplesPerChannel)
        data = numpy.ubyte(data * 255)
        return data
        
    ## This function returns the number of digital lines configured in the DAQmx
    #  Task.
    #  @param self The object pointer.
    def getNumLines(self):
        numLines = uInt32()
        #bufferSize = 255
        #channel = ctypes.create_string_buffer(bufferSize)
        #self.taskRef.GetTaskChannels(channel, bufferSize)
        #print channel.value
        self.taskRef.GetDONumLines('', ctypes.byref(numLines))
        return numLines.value
    
    ## This function returns the number of digital channels configured in the 
    #  DAQmx Task.
    #  @param self The object pointer.
    def getNumChannels(self):
        numChannels = uInt32()
        self.taskRef.GetTaskNumChans(ctypes.byref(numChannels))
        return numChannels.value
        
    ## This function writes the specified values into the buffer.
    #  @param self The object pointer.
    #  @param data This is a 1D 8-bit unsigned integer array that contians samples for
    #  each digital channel. Channels are non-interleaved (channel1 n-samples
    #  then channel2 n-samples).
    def writeToBuffer(self, data):
        autostart = self.mode == dutil.Mode.Static
        samplesWritten = int32()
        self.status = self.taskRef.WriteDigitalU8(self._samplesPerChannel,
                autostart, 10, DAQmx_Val_GroupByChannel, data,
                ctypes.byref(samplesWritten), None)
        #print 'Samples Written: ' + str(samplesWritten.value)
        return samplesWritten.value
    
    ## This function starts the digital output generation.
    #  @param self The object pointer.
    def start(self):
        self.status = self.taskRef.StartTask()
        
    ## This functions waits for the digital output generation to complete.
    #  @param self The object pointer.
    def waitUntilDone(self):
        sampPerChan = uInt64()
        self.status = self.taskRef.GetSampQuantSampPerChan(
                ctypes.byref(sampPerChan))
        #print 'DO Samples Per Channel: ' + str(sampPerChan.value)
        estAcqTime = (self.loops * sampPerChan.value) / self._sampleRate
        
        #print "Estimated Acquisition Time: " + str(estAcqTime)
        
        if self.mode != dutil.Mode.Static:
            self.status = self.taskRef.WaitUntilTaskDone(float64(estAcqTime + 0.1))
    
    ## This function stops the digital output generation.
    #  @param self The object pointer.
    def stop(self):
        self.status = self.taskRef.StopTask()
    
    ## This is a private method that creates the Task object for use inside the
    #  DigitalOutput class.
    def __createTask(self, physicalChannel):
        self.status = self.taskRef.CreateDOChan(physicalChannel, '',
                DAQmx_Val_ChanForAllLines)
        
    ## This is a private method that configures the timing for the DigitalOutput
    #  class.
    #  @param self The object pointer.
    def __configTiming(self, sampleMode):
        totalSamples = self._samplesPerChannel * self.loops
        self.taskRef.CfgSampClkTiming(self._clkSource, float64(self._sampleRate),
                                          DAQmx_Val_Falling, sampleMode,
                                          uInt64(totalSamples))
        
    ## This function will close connection to the digital ouput device and
    #  channels.
    #  @param self The object pointer.
    def close(self):
        """"""
        self.initialized = False
        self.status = self.taskRef.ClearTask()
        self.taskRef = Task()
        
    ## This is the destructor for the DigitalOutput Class.
    #  @param self The object pointer.
    def __del__(self):
        if self.initialized:
            self.close()
        
        del self.taskRef
        del self.status
        del self.initialized
        del self.sampleRate
        del self.samplesPerChannel
        del self.clkSource
        del self.mode
        del self.loops
Esempio n. 14
0
class Recorder(RecorderParent):
    """
     Sets up the recording stream through a National Instrument

     Attributes
     ----------
    device_name: str
        Name of the device to be used for recording
    max_value: float
        Maximum value of recorded data
    """
#---------------- INITIALISATION METHODS -----------------------------------
    def __init__(self,channels = 1,rate = 30000.0, chunk_size = 1000,
                 num_chunk = 4,device_name = None):
        """
        Re-implemented from RecorderParent
        """

        super().__init__(channels = channels,rate = rate,
             chunk_size = chunk_size,num_chunk = num_chunk)
        print('You are using National Instrument for recording')

        self.device_name = None
        self.set_device_by_name(device_name);

        self.open_recorder()
        self.trigger_init()

        self.max_value = 10;

#---------------- DEVICE SETTING METHODS -----------------------------------
    def set_device_by_name(self, name):
        """
         Set the recording audio device by name.
         Uses the first device found if no such device found.
        """
        devices = self.available_devices()[0]
        selected_device = None
        if not devices:
            print('No NI devices found')
            return

        if not name in devices:
            print('Input device name not found, using the first device')
            selected_device = devices[0]
        else:
            selected_device = name

        print('Selected devices: %s' % selected_device)
        self.device_name = selected_device

     # Get audio device names
    def available_devices(self):
        """
        Get all the available input National Instrument devices.

        Returns
        ----------
        devices_name: List of str
            Name of the device, e.g. Dev0
        device_type: List of str
            Type of device, e.g. USB-6003
        """
        numBytesneeded = pdaq.DAQmxGetSysDevNames(None,0)
        databuffer = pdaq.create_string_buffer(numBytesneeded)
        pdaq.DAQmxGetSysDevNames(databuffer,numBytesneeded)

        #device_list = []
        devices_name = pdaq.string_at(databuffer).decode('utf-8').split(',')

        device_type = []
        for dev in devices_name:
            numBytesneeded = pdaq.DAQmxGetDevProductType(dev,None,0)
            databuffer = pdaq.create_string_buffer(numBytesneeded)
            pdaq.DAQmxGetDevProductType(dev,databuffer,numBytesneeded)
            device_type.append(pdaq.string_at(databuffer).decode('utf-8'))

        #device_list.append(devices_name)
        #device_list.append(device_type)

        return(devices_name,device_type)

    # Display the current selected device info
    def current_device_info(self):
        """
        Prints information about the current device set
        """
        device_info = {}
        info = ('Category', 'Type','Product', 'Number',
                'Analog Trigger Support','Analog Input Trigger Types','Analog Input Channels (ai)', 'Analog Output Channels (ao)',
                'ai Minimum Rate(Hz)', 'ai Maximum Rate(Single)(Hz)', 'ai Maximum Rate(Multi)(Hz)',
                'Digital Trigger Support','Digital Input Trigger Types','Digital Ports', 'Digital Lines', 'Terminals')
        funcs = (pdaq.DAQmxGetDevProductCategory, pdaq.DAQmxGetDevProductType,
                 pdaq.DAQmxGetDevProductNum, pdaq.DAQmxGetDevSerialNum,
                 pdaq.DAQmxGetDevAnlgTrigSupported,  pdaq.DAQmxGetDevAITrigUsage,
                 pdaq.DAQmxGetDevAIPhysicalChans,pdaq.DAQmxGetDevAOPhysicalChans,
                 pdaq.DAQmxGetDevAIMinRate, pdaq.DAQmxGetDevAIMaxSingleChanRate, pdaq.DAQmxGetDevAIMaxMultiChanRate,
                 pdaq.DAQmxGetDevDigTrigSupported,pdaq.DAQmxGetDevDITrigUsage,
                 pdaq.DAQmxGetDevDIPorts,pdaq.DAQmxGetDevDILines,
                 pdaq.DAQmxGetDevTerminals)
        var_types = (pdaq.int32, str, pdaq.uint32, pdaq.uint32,
                     pdaq.bool32,pdaq.int32,str,str,
                     pdaq.float64, pdaq.float64, pdaq.float64,
                     pdaq.bool32,pdaq.int32,str,str,str)

        for i,f,v in zip(info,funcs,var_types):
            try:
                if v == str:
                    nBytes = f(self.device_name,None,0)
                    string_ptr = pdaq.create_string_buffer(nBytes)
                    f(self.device_name,string_ptr,nBytes)
                    if any( x in i for x in ('Channels','Ports')):
                        device_info[i] = len(string_ptr.value.decode().split(','))
                    else:
                        device_info[i] = string_ptr.value.decode()
                else:
                    data = v()
                    f(self.device_name,data)
                    if 'Types' in i:
                        device_info[i] = bin(data.value)[2:].zfill(6)
                    else:
                        device_info[i] = data.value
            except Exception as e:
                print(e)
                device_info[i] = '-'

        pp.pprint(device_info)

    def set_channels(self):
        """
        Create the string to initiate the channels when assigning a Task

        Returns
        ----------
        channelname: str
            The channel names to be used when assigning Task
            e.g. Dev0/ai0:Dev0/ai1
        """
        if self.channels >1:
            channelname =  '%s/ai0:%s/ai%i' % (self.device_name, self.device_name,self.channels-1)
        elif self.channels == 1:
            channelname = '%s/ai0' % self.device_name

        print('Channels Name: %s' % channelname)
        return channelname

#---------------- STREAMING METHODS -----------------------------------
    # Convert data obtained into a proper array
    def audiodata_to_array(self,data):
        """
        Re-implemented from RecorderParent
        """
        return data.reshape((-1,self.channels))/(2**15) *10.0

    # Callback function for audio streaming
    def stream_audio_callback(self):
        """
        Callback function for audio streaming.
        First, it writes data to the circular buffer,
        then record data if it is recording,
        finally check for any trigger.

        Returns 0 as part of the callback format.
        More info can be found in PyDAQmx documentation on Task class
        """
        in_data = np.zeros(self.chunk_size*self.channels,dtype = np.int16)
        read = pdaq.int32()
        self.audio_stream.ReadBinaryI16(self.chunk_size,10.0,pdaq.DAQmx_Val_GroupByScanNumber,
                           in_data,self.chunk_size*self.channels,pdaq.byref(read),None)

        data_array = self.audiodata_to_array(in_data)
        self.write_buffer(data_array)
        #self.rEmitter.newdata.emit()

        if self.recording:
            self.record_data(data_array)
         # Trigger check
        if self.trigger:
            self._trigger_check_threshold(data_array)

        return 0

    def stream_init(self, playback = False):
        """
        Re-implemented from RecorderParent.
        """
        if self.audio_stream == None:
            try:
                self.audio_stream = Task()
                self.audio_stream.stream_audio_callback = self.stream_audio_callback
                self.audio_stream.CreateAIVoltageChan(self.set_channels(),"",
                                         pdaq.DAQmx_Val_RSE,-10.0,10.0,
                                         pdaq.DAQmx_Val_Volts,None)
                self.audio_stream.CfgSampClkTiming("",self.rate,
                                      pdaq.DAQmx_Val_Rising,pdaq.DAQmx_Val_ContSamps,
                                      self.chunk_size)
                self.audio_stream.AutoRegisterEveryNSamplesEvent(pdaq.DAQmx_Val_Acquired_Into_Buffer,
                                                    1000,0,name = 'stream_audio_callback')

                self.stream_start()
                return True
            except:
                t,v,tb = sys.exc_info()
                print(t)
                print(v)
                print(traceback.format_tb(tb))
                self.audio_stream = None

                return False

    # Start the streaming
    def stream_start(self):
        """
        Re-implemented from RecorderParent.
        """
        if self.audio_stream:
            task_done = pdaq.bool32()
            self.audio_stream.GetTaskComplete(task_done)
            if task_done.value:
                self.audio_stream.StartTask()
            else:
                print('stream already started')
        else:
            print('No audio stream is set up')
    # Stop the streaming
    def stream_stop(self):
        """
        Re-implemented from RecorderParent.
        """
        if self.audio_stream:
            task_done = pdaq.bool32()
            self.audio_stream.GetTaskComplete(task_done)
            if not task_done.value:
                self.audio_stream.StopTask()
            else:
                print('stream already stopped')
        else:
            print('No audio stream is set up')

    # Close the stream, probably needed if any parameter of the stream is changed
    def stream_close(self):
        """
        Re-implemented from RecorderParent.
        """
        if self.audio_stream:
            self.audio_stream.StopTask()
            self.audio_stream.ClearTask()
            self.audio_stream = None
Esempio n. 15
0
# samp_frequency = 44100 # sampling rate
# mod_frequency = 1100
# mod_amplitude = 2
# mod_offset = 2
# samples = 1000
# x = np.arange(samples)
# sinewave = mod_offset + mod_amplitude*(np.sin(2 * np.pi * mod_frequency * x / samp_frequency)).astype(np.float64)
# laser.modulate(sinewave)
# laser.StopTask()
# laser.ClearTask()

samp_frequency = 1000  # sampling rate
mod_frequency = 1100
mod_amplitude = 2
mod_offset = 2
samples = 1000
# create the sine sinewave
x = np.arange(samples)
sinewave = mod_offset + mod_amplitude * (np.sin(
    2 * np.pi * mod_frequency * x / samp_frequency)).astype(np.float64)

laser = Task()
laser.CreateAOVoltageChan("/Dev2/ao0", "Laser", 0, 5, PyDAQmx.DAQmx_Val_Volts,
                          None)
laser.CfgSampClkTiming(None, 1000.0, PyDAQmx.DAQmx_Val_Rising,
                       PyDAQmx.DAQmx_Val_ContSamps, 1000)
laser.SetWriteRegenMode(PyDAQmx.DAQmx_Val_DoNotAllowRegen)
laser.WriteAnalogF64(1000, 0, 10.0, PyDAQmx.DAQmx_Val_GroupByChannel, sinewave,
                     None, None)
laser.StartTask()
Esempio n. 16
0
class NI_6713Device():
    """
    This class is the interface to the NI driver for a NI PCI-6713 analog output card
    """
    def __init__(self, MAX_name, message_queue):
        """
        Initialise the driver and tasks using the given MAX name and message queue to communicate with this class

        Parameters
        ----------
        MAX_name : str
            the National Instrument MAX name used to identify the hardware card
        message_queue : JoinableQueue
            a message queue used to send instructions to this class
        """
        print("initialize device")
        self.NUM_AO = 8
        self.NUM_DO = 8
        self.MAX_name = MAX_name
        self.limits = [-10, 10]

        #Create AO Task
        self.ao_task = Task()
        self.ao_read = int32()
        self.ao_data = np.zeros((self.NUM_AO, ), dtype=np.float64)

        #Create DO Task
        self.do_task = Task()
        self.do_read = int32()
        self.do_data = np.zeros((self.NUM_DO, ), dtype=np.uint8)

        self.setup_static_channels()

        #DAQmx Start Code
        self.ao_task.StartTask()
        self.do_task.StartTask()

        self.wait_for_rerun = False

        self.running = True
        self.read_Thread = Thread(target=self.read_fun, args=(message_queue, ))

    def start(self):
        """
        Starts the message queue thread to read incoming instructions
        """
        self.read_Thread.start()

    def read_fun(self, message_queue):
        """
        Main method to read incoming instructions from the message queue
        """
        while self.running:
            try:
                #read an instruction from the message queue
                typ, msg = message_queue.get(timeout=0.5)
            except Queue.Empty:
                continue  #if there is no instruction in the queue, just read again until there is an instruction, or until the the the thread stops (running==False)

            # handle incoming instructions
            if typ == 'manual':
                # the msg argument contains the dict front_panel_values to send to the device
                self.program_manual(msg)
                message_queue.task_done(
                )  #signalise the sender, that the instruction is complete
            elif typ == 'trans to buff':
                #Transition to Buffered
                # msg is a dict containing all relevant arguments
                # If fresh is true, the hardware should be programmed with new commands, which were permitted
                # if fresh is false, use the last programmed harware commands again, so no hardware programming is needed at all
                if msg['fresh']:
                    self.transition_to_buffered(True, msg['clock_terminal'],
                                                msg['ao_channels'],
                                                msg['ao_data'])
                else:
                    self.transition_to_buffered(False, None, None, None)
                message_queue.task_done()  #signalize that the task is done
            elif typ == 'trans to man':
                #Transition to Manual
                self.transition_to_manual(msg['more_reps'], msg['abort'])
                message_queue.task_done()  # signalise that the task is done
            else:
                # an unknown/unimplemented instruction is requestet
                print("unkown message: " + msg)
                message_queue.task_done()
                continue

    def setup_static_channels(self):
        self.wait_for_rerun = False
        #setup AO channels
        for i in range(self.NUM_AO):
            self.ao_task.CreateAOVoltageChan(self.MAX_name + "/ao%d" % i, "",
                                             self.limits[0], self.limits[1],
                                             DAQmx_Val_Volts, None)
        #setup DO port(s)
        self.do_task.CreateDOChan(self.MAX_name + "/port0/line0:7", "",
                                  DAQmx_Val_ChanForAllLines)

    def shutdown(self):
        """
        Shutdown the device (stop & clear all tasks). Also stop the message queue thread
        """
        print("shutdown device")
        self.running = False
        self.ao_task.StopTask()
        self.ao_task.ClearTask()
        self.do_task.StopTask()
        self.do_task.ClearTask()

    def program_manual(self, front_panel_values):
        """
        Update the static output chanels with new values.

        This method transitions the device into manual mode (if it is still in rerun mode) and
        updates the output state of all channels

        Parameters
        ----------
        front_panel_values : dict {connection name : new state, ...}
            Containing the connection name and corresponding new output state
        """
        if self.wait_for_rerun:
            print("dont wait for rerun any more. setup static")
            self.ao_task.StopTask()
            self.ao_task.ClearTask()
            self.do_task.StopTask()
            self.do_task.ClearTask()
            self.ao_task = Task()
            self.do_task = Task()
            self.setup_static_channels()
            self.wait_for_rerun = False

        for i in range(self.NUM_AO):
            self.ao_data[i] = front_panel_values['ao%d' % i]
        self.ao_task.WriteAnalogF64(1, True, 1, DAQmx_Val_GroupByChannel,
                                    self.ao_data, byref(self.ao_read), None)

        for i in range(self.NUM_DO):
            self.do_data[i] = front_panel_values['do_%d' % i]
        self.do_task.WriteDigitalLines(1, True, 1, DAQmx_Val_GroupByChannel,
                                       self.do_data, byref(self.do_read), None)

    def transition_to_buffered(self, fresh, clock_terminal, ao_channels,
                               ao_data):
        """
        Transition the device to buffered mode

        This method does the hardware programming if needed

        Parameters
        ----------
        fresh : bool
            True if the device should be programmed with new instructions
            False if the old instructions should be executed again, so no programming is needed (just rerun last instructions)
        clock_terminal : str
            The device connection on which the clock signal is connected (e.g. 'PFI0')
        ao_channels : list str
            A list of all analog output channels that should be used 
        ao_data : 2d-numpy array, float64
            A 2d-array containing the instructions for each ao_channel for every clock tick
        """
        self.ao_task.StopTask(
        )  #Stop the last task (static mode or last buffered shot)
        if not fresh:
            if not self.wait_for_rerun:
                raise Exception("Cannot rerun Task.")
            self.ao_task.StartTask()  #just run old task again
            return
        elif not clock_terminal or not ao_channels or ao_data is None:
            raise Exception(
                "Cannot progam device. Some arguments are missing.")

        self.ao_task.ClearTask(
        )  #clear the last task and create a new one with new parameters & instructions
        self.ao_task = Task()

        self.ao_task.CreateAOVoltageChan(ao_channels, "", -10.0, 10.0,
                                         DAQmx_Val_Volts, None)
        self.ao_task.CfgSampClkTiming(clock_terminal, 1000000,
                                      DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,
                                      ao_data.shape[0])
        self.ao_task.WriteAnalogF64(ao_data.shape[0], False, 10.0,
                                    DAQmx_Val_GroupByScanNumber, ao_data,
                                    self.ao_read, None)

        self.ao_task.StartTask()  #finally start the task

    def transition_to_manual(self, more_reps, abort):
        """
        Stop buffered mode
        """
        if abort:
            self.wait_for_rerun = False
            self.ao_task.ClearTask()
            self.do_task.StopTask()
            self.do_task.ClearTask()

            self.ao_task = Task()
            self.do_task = Task()

            self.setup_static_channels()
            self.ao_task.StartTask()
            self.do_task.StartTask()
        else:
            self.wait_for_rerun = True
AO_sample_rate = 20000.0
amplitude = 3  # This defines the range over which we sample
x_freq = 2.0
y_freq = 200.0
written = ni.int32()

waveform, t = gen_waveform(x_freq, y_freq, num_AO_samples, AO_sample_rate,
                           amplitude)

AO_task = Task()
AO_task.CreateAOVoltageChan("/Dev1/ao0:1", "", -10.0, 10.0, ni.DAQmx_Val_Volts,
                            None)
#AO_task.StartTask()

# Specify Sample Clock Timing
AO_task.CfgSampClkTiming("OnboardClock", AO_sample_rate, ni.DAQmx_Val_Rising,
                         ni.DAQmx_Val_ContSamps, num_AO_samples)
#Trigger on the counter. PFI12 is the output of counter 0
AO_task.CfgDigEdgeStartTrig("/Dev1/PFI12", ni.DAQmx_Val_Rising)

# ## Analog input
# ---------------------------------------------------------------------------------------------------------------------------------------------

# In[36]:

# Define analog input task
AI_task = Task()
AI_task.CreateAIVoltageChan("/Dev1/ai1", "", ni.DAQmx_Val_RSE, -10.0, 10.0,
                            ni.DAQmx_Val_Volts, None)

# In[37]:
Esempio n. 18
0
class pCounter(object):
    ## This function is the constructor for the pCounter class.
    #
    #  It creates internal variables required to perform functions within
    #  the class. This function does not initialize any hardware.
    #  @param self The object pointer.
    def __init__(self, **kwargs):
        #super(pCounter, self).__init__()
        ## The string that identifies the DAQmx device and counter
        #  for the counter that is used to count edges.
        #
        #  Example: PXISlot5/ctr0
        self.edgeCounter = ''

        ## The string that identifies the DAQmx device and counter
        #  for the counter that is used to create the sample clock.
        #
        #  Example: /PXI1Slot5/ctr0
        self.clockCounter = ''

        ##  A boolean that enables the start trigger.
        #
        #  The default is false, which disables the start trigger.
        #  The measurement will immediately start when the start()
        #  method is called.
        #  A true value will make the measurement start when a digital
        #  trigger is received on the line specified by the triggerSource
        #  variable.
        self.enableStartTrigger = False

        ## A string that identifies the DAQmx digital line that will
        #  be used as an input to the edge counter.
        #
        #  Default: PFI0
        self.edgeCntrTerm = kwargs.get('edgeCntrTerm', 'PFI0')

        self._triggerClkSource = 'Ctr0InternalOutput'

        ## A string that identifies the DAQmx digital line that will
        #  be used as the start trigger.
        #
        #  Default: PFI1
        self.triggerSource = 'PFI1'

        ## A string that identifies the DAQmx digital line that will
        #  output the sample clock.
        #
        #  Default: PFI12
        self.clockSourceTerm = kwargs.get('clockSourceterm', 'PFI12')

        ## The task reference for the edge counter.
        self.edgeCntrTask = Task()

        ## The task reference for the sample clock counter.
        self.clockCntrTask = Task()

        ## @var samples
        #  This is the number of samples to take.  It is the size
        #  of the data array returned by the read() method.
        self._samples = kwargs.get('samples', None)

        ## @var sampleRate
        #  This is the sample rate to use when counting edges.
        self._sampleRate = kwargs.get('sampleRate', None)

        ## @var acqTime
        #  This is the time in milliseconds for a full acquisition
        #  period.
        self._acqTime = None

        ## @var binTime
        #  This is the time in millisenconds to take a single sample.
        self._binTime = None

        self._status = int32()

        ## This is the time to wait for a start trigger.
        #
        #  If the timeout passes, then an error is generated. Ignore
        #  this variable if the start trigger is disabled.
        self.timeout = kwargs.get('timeout', 1)
        self._samplesRead = None

    def _calcBinAndAcq(self):
        self._binTime = 1000. / self._sampleRate
        self._acqTime = (self._samples - 1.) * self._binTime

    def _calcSampsAndSampRate(self):
        self._sampleRate = 1000 / self._binTime
        self._samples = int((self._acqTime / self._binTime) + 1)

    def _getSamples(self):
        return self._samples

    def _setSamples(self, value):
        self._samples = value
        if self._sampleRate:
            self._calcBinAndAcq()

    samples = property(_getSamples, _setSamples)

    def _getSampleRate(self):
        return self._sampleRate

    def _setSampleRate(self, value):
        self._sampleRate = value
        if self._samples:
            self._calcBinAndAcq()

    sampleRate = property(_getSampleRate, _setSampleRate)

    def _getBinTime(self):
        return self._binTime

    def _setBinTime(self, value):
        self._binTime = value
        if self._acqTime:
            self._calcSampsAndSampRate()

    binTime = property(_getBinTime, _setBinTime)

    def _getAcqTime(self):
        return self._acqTime

    def _setAcqTime(self, value):
        self._acqTime = value
        if self._binTime:
            self._calcSampsAndSampRate()

    acqTime = property(_getAcqTime, _setAcqTime)

    ## This function initializes the pCounter class and opens a
    #  reference to the DAQmx device(s).
    #
    #  If specifiying a acqTime and binTime or samples and sampleRate,
    #  only one pair of parameters need to be provided.  When specifying
    #  acqTime and binTime, the samples and sampleRate are calculated.
    #  When specifying the samples and sampleRate, the acqTime and
    #  binTime are calculated.
    #
    #  @param self The object pointer.
    #  @param clockCounter The string that identifies the DAQmx
    #  device and counter for the counter that is used to create
    #  the sample clock.
    #  @param edgeCounter The string that identifies the DAQmx
    #  device and counter for the counter that is used to count edges.
    #  @param acqTime This is the time in milliseconds for a full
    #  acquisition period.
    #  @param binTime This is the time in millisenconds to take a
    #  single sample.
    #  @param samples The number of samples for the pCounter to take.
    #  @param sampleRate The frequency of the samples taken by the
    #  pCounter.
    def init(self,
             clockCounter=None,
             edgeCounter=None,
             acqTime=None,
             binTime=None,
             samples=None,
             sampleRate=None):
        if edgeCounter:
            self.edgeCounter = edgeCounter
        if clockCounter:
            self.clockCounter = clockCounter

        if samples and sampleRate:
            self._samples = samples
            self._sampleRate = sampleRate
            self._calcBinandAcq()

        if acqTime and binTime:
            self._acqTime = acqTime
            self._binTime = binTime
            self._calcSampsAndSampRate()

        # Setup the Edge Counter
        self._status = self.edgeCntrTask.CreateCICountEdgesChan(
            self.edgeCounter, '', DAQmx_Val_Rising, 0, DAQmx_Val_CountUp)
        self._status = self.edgeCntrTask.SetCICountEdgesTerm(
            self.edgeCounter, self.edgeCntrTerm)
        self._status = self.edgeCntrTask.CfgSampClkTiming(
            self._triggerClkSource, float64(self._sampleRate),
            DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, uInt64(self._samples + 1))

        # Setup the Clock Source Counter
        self._status = self.clockCntrTask.CreateCOPulseChanFreq(
            self.clockCounter, '', DAQmx_Val_Hz, DAQmx_Val_Low, 0,
            float64(self._sampleRate), float64(0.5))
        self._status = self.clockCntrTask.SetCOPulseTerm(
            self.clockCounter, self.clockSourceTerm)
        self._status = self.clockCntrTask.CfgImplicitTiming(
            DAQmx_Val_ContSamps, uInt64(self._samples + 1))
        if self.enableStartTrigger:
            self._status = self.clockCntrTask.CfgDigEdgeStartTrig(
                self.triggerSource, DAQmx_Val_Rising)

    ## This function initializes the pCounter class using the
    #  chassis config file and opens a reference to the DAQmx device(s).
    #
    #  @param self The object reference.
    #  @param filepath The path to the chassis config file.
    def initFromFile(self, filepath):
        from .chassisConfigParser import chassisConfigParser
        config = chassisConfigParser()
        edgeCounter, clockCounter = config.readCntrSection(filepath)
        self.init(clockCounter, edgeCounter)

    ## This function starts the measurement.
    #
    #  If the start trigger is enabled, then a the pCounter waits
    #  for that digital trigger.  Otherwise the measurement takes
    #  place immediately.
    #  @param self The object pointer.
    def start(self):
        self._status = self.edgeCntrTask.StartTask()
        self._status = self.clockCntrTask.StartTask()

    ## This function stops the measurement.
    #
    #  It needs to be called everytime the start() method is called.
    #  @param self The object pointer.
    def stop(self):
        self._status = self.edgeCntrTask.StopTask()
        self._status = self.clockCntrTask.StopTask()

    ## This function returns an array of the edge counts with an
    #  array size equal to the number of samples.
    #
    #  @param self The object pointer.
    def read(self):
        samplesRead = int32()
        data = np.zeros(self._samples + 1, dtype=np.uint32)

        self._status = self.edgeCntrTask.ReadCounterU32(
            int32(self._samples + 1), float64(self.timeout), data,
            uInt32(self._samples + 1), ctypes.byref(samplesRead), None)
        self._samplesRead = samplesRead.value

        dataDelta = []
        for i, item in enumerate(data):
            if i > 0:
                dataDelta.append(item - preValue)
            preValue = item

        length = len(dataDelta)
        dataSum = 0
        for item in dataDelta:
            dataSum += item
        mean = float(dataSum / length)

        sqSum = 0
        for item in dataDelta:
            sq = np.square(item - mean)
            sqSum += sq
        stdDev = sqrt(sqSum / length)

        return dataDelta, mean, stdDev

    ## This function performs the start(), read(), and stop() methods
    #  in one function call.
    #
    #  This is useful for when the results of the read() method can be
    #  retrieved immediately after a start()
    #  @param self The object pointer.
    def measure(self):
        # Start the Tasks
        self.start()

        # Read the data
        data, mean, stdDev = self.read()

        # Stop the Tasks
        self.stop()

        return data, mean, stdDev

    ## This function closes the refences to the DAQmx devices.
    #
    #  @param self The object pointer.
    def close(self):
        self._status = self.edgeCntrTask.ClearTask()
        self.edgeCntrTask = Task()

        self._status = self.clockCntrTask.ClearTask()
        self.clockCntrTask = Task()

    ## This function is the destructor for the pCounter class.
    #
    #  It deletes internal variables and closes the references to
    #  the DAQmx devices if they are not already closed.
    #  @param self The object pointer.
    def __del__(self):
        self.close()

        del self.edgeCounter
        del self.clockCounter
        del self.enableStartTrigger
        del self.edgeCntrTerm
        del self._triggerClkSource
        del self.triggerSource
        del self.clockSourceTerm
        del self.edgeCntrTask
        del self.clockCntrTask
        del self._samples
        del self._sampleRate
        del self._acqTime
        del self._binTime
        del self._status
        del self.timeout
        del self._samplesRead
Esempio n. 19
0
class NI_DIODevice():
    """
    This class is the interface to the NI driver for a NI PCI-DIO-32HS digital output card
    """
    def __init__(self, MAX_name, message_queue):
        """
        Initialise the driver and tasks using the given MAX name and message queue to communicate with this class

        Parameters
        ----------
        MAX_name : str
            the National Instrument MAX name used to identify the hardware card
        message_queue : JoinableQueue
            a message queue used to send instructions to this class
        """
        print("initialize device")
        self.NUM_DO = 32
        self.MAX_name = MAX_name

        #Create DO Task
        self.do_task = Task()
        self.do_read = int32()
        self.do_data = np.zeros((self.NUM_DO, ), dtype=np.uint8)

        self.setup_static_channels()

        #DAQmx Start Code
        self.do_task.StartTask()

        self.wait_for_rerun = False

        self.running = True
        self.read_Thread = Thread(target=self.read_fun, args=(message_queue, ))

    def start(self):
        """
        Starts the message queue thread to read incoming instructions
        """
        self.read_Thread.start()

    def read_fun(self, message_queue):
        """
        Main method to read incoming instructions from the message queue
        """
        while self.running:
            try:
                typ, msg = message_queue.get(timeout=0.5)
            except Queue.Empty:
                continue

            if typ == 'manual':
                self.program_manual(msg)
                message_queue.task_done()
            elif typ == 'trans to buff':
                #Transition to Buffered
                if msg['fresh']:
                    self.transition_to_buffered(True, msg['clock_terminal'],
                                                msg['do_channels'],
                                                msg['do_data'])
                else:
                    self.transition_to_buffered(False, None, None, None)
                message_queue.task_done()  #signalize that the task is done
            elif typ == 'trans to man':
                #Transition to Manual
                self.transition_to_manual(msg['more_reps'], msg['abort'])
                message_queue.task_done()
            else:
                print("unkown message: " + msg)
                message_queue.task_done()
                continue

    def setup_static_channels(self):
        #setup DO port(s)
        self.do_task.CreateDOChan(
            self.MAX_name + "/port0/line0:7," + self.MAX_name +
            "/port1/line0:7," + self.MAX_name + "/port2/line0:7," +
            self.MAX_name + "/port3/line0:7", "", DAQmx_Val_ChanForAllLines)

    def shutdown(self):
        """
        Shutdown the device (stop & clear all tasks). Also stop the message queue thread
        """
        print("shutdown device")
        self.running = False
        self.do_task.StopTask()
        self.do_task.ClearTask()

    def program_manual(self, front_panel_values):
        """
        Update the static output chanels with new values.

        This method transitions the device into manual mode (if it is still in rerun mode) and
        updates the output state of all channels

        Parameters
        ----------
        front_panel_values : dict {connection name : new state, ...}
            Containing the connection name and corresponding new output state
        """
        if self.wait_for_rerun:
            print("dont wait for rerun any more. setup static")
            self.do_task.StopTask()
            self.do_task.ClearTask()
            self.do_task = Task()
            self.setup_static_channels()
            self.wait_for_rerun = False

        for port in range(4):
            for line in range(8):
                self.do_data[port * 8 +
                             line] = front_panel_values['port%d/line%d' %
                                                        (port, line)]

        self.do_task.WriteDigitalLines(1, True, 1, DAQmx_Val_GroupByChannel,
                                       self.do_data, byref(self.do_read), None)

    def transition_to_buffered(self, fresh, clock_terminal, do_channels,
                               do_data):
        """
        Transition the device to buffered mode

        This method does the hardware programming if needed

        Parameters
        ----------
        fresh : bool
            True if the device should be programmed with new instructions
            False if the old instructions should be executed again, so no programming is needed (just rerun last instructions)
        clock_terminal : str
            The device connection on which the clock signal is connected (e.g. 'PFI2')
        ao_channels : list str
            A list of all analog output channels that should be used 
        ao_data : 2d-numpy array, uint8
            A 2d-array containing the instructions for each ao_channel for every clock tick
        """
        self.do_task.StopTask()
        if not fresh:
            if not self.wait_for_rerun:
                raise Exception("Cannot rerun Task.")
            self.do_task.StartTask()  #just run old task again
            return
        elif not clock_terminal or not do_channels or do_data is None:
            raise Exception(
                "Cannot progam device. Some arguments are missing.")

        self.do_task.ClearTask()
        self.do_task = Task()

        self.do_task.CreateDOChan(do_channels, "", DAQmx_Val_ChanPerLine)
        self.do_task.CfgSampClkTiming(clock_terminal, 10000000,
                                      DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,
                                      do_data.shape[0])
        self.do_task.WriteDigitalLines(do_data.shape[0], False, 10.0,
                                       DAQmx_Val_GroupByScanNumber, do_data,
                                       self.do_read, None)

        #print("Wrote "+str(self.do_read)+" samples to the buffer")

        self.do_task.StartTask()

    def transition_to_manual(self, more_reps, abort):
        """
        Stop buffered mode
        """
        if abort:
            self.wait_for_rerun = False
            self.do_task.ClearTask()
            self.do_task = Task()

            self.setup_static_channels()
            self.do_task.StartTask()
        else:
            self.wait_for_rerun = True
Esempio n. 20
0
class TriggerOutputDig(object):
    t = None
    wvfms = None
    Nsamps = None
    sampleRate = None
    pulseLength = None

    def __init__(self, port="Dev12/port0", startTrigChan="PFI0"):
        self.th = Task()  #Task.__init__(self)
        self.th.CreateDOChan(port, "", DAQmx_Val_ChanForAllLines)
        #self.setTiming(sampleRate, pulseLength)
        self.th.CfgDigEdgeStartTrig(startTrigChan, DAQmx_Val_Rising)
        self.th.SetStartTrigRetriggerable(True)
        #DAQmxLoadTask("WvfmOutputTask", byref(self.taskHandle))

    def start(self):
        self.th.StartTask()

    def stop(self):
        try:
            self.th.StopTask()
        except DAQError as e:
            if e.error == 200010:
                self.th.StopTask()
            else:
                raise (e)

    def setTiming(self, sampleRate, waveformLength):
        Nsamps = int(waveformLength * sampleRate)
        #self.th.CfgSampClkTiming("",sampleRate,DAQmx_Val_Rising,DAQmx_Val_ContSamps, 5*Nsamps);
        self.th.CfgSampClkTiming("", sampleRate, DAQmx_Val_Rising,
                                 DAQmx_Val_FiniteSamps, Nsamps)
        self.sampleRate = sampleRate
        self.Nsamps = Nsamps

    def reset(self):
        try:
            self.stop()
            sleep(0.2)
        except DAQError:
            pass
        finally:
            self.start()

    def setWaveforms(self, t, *wvfms):
        Npts = t.size
        sampleRate = 1. / (t[1] - t[0])
        totalLength = Npts / sampleRate
        self.setTiming(sampleRate, totalLength)

        wvfmPort = np.zeros(t.size, dtype='u4')
        for k, wv in enumerate(wvfms):
            wv[:] = np.where(wv, 1, 0)
            wv = wv.astype('u4')
            wvfmPort += (k + 1) * wv

        written = int32()
        self.th.WriteDigitalU32(1 * wvfmPort.size, 1, 10,
                                DAQmx_Val_GroupByChannel,
                                np.hstack(1 * [wvfmPort]).astype('u4'),
                                byref(written), None)
        self.t = t
        self.wvfms = wvfms
        return self.t, wvfmPort
Esempio n. 21
0
class AnalogOutput(object):

    ## This function is a constructor for the AnalogOutput class.
    #
    # It creates the internal variables required to perform functions within the
    # class. This function does not initialize any hardware.
    def __init__(self):

        ## The DAQmx task reference.
        self.taskRef = Task()

        ## This is a boolean that is true when the DAQmx task has been initialized.
        self.initialized = False

        ## This is the status of the DAQmx task.
        #
        #  A value greater than 0 means that an error has occurred. When the status
        #  is greater than 0 an error should be reported by the class.
        self.status = int32()

        ## @var sampleRate
        #  This is the sample rate of the analog output.
        self._sampleRate = 100e3

        ## @var numChannels
        #  This is the number of channels configured in the task.
        self._numChannels = 0

        ## @var samplesPerChannel
        #  This is the number of samples per channel that will be
        #  generated in Finite mode.
        self._samplesPerChannel = 100

        ## @var clkSource
        #  This is the sample clock source terminal.  It can be set to an
        #  internal clock or external clock such as a PFI line i.e. "/PXI1Slot3/PFI15."
        self._clkSource = ''

        ## @var startTriggerSource
        #  This is the start trigger source terminal.  The software
        #  ignores this value when the triggerType is set to "Software". Otherwise when
        #  the triggerType is "Hardware," this terminal is used to start analog
        #  generation.  Example Value: "/PXI1Slot3/PFI0"
        self._startTriggerSource = ''

        ## @var pauseTriggerSource
        #  The source terminal of the pause trigger.  This can be
        #  any PFI or backplane trigger such as 'PFI5' and 'PXI_TRIG5'
        self._pauseTriggerSource = ''

        ## This is the start trigger terminal of the NI-Sync card.
        #
        #  Setting this value will make sure that the start trigger will be
        #  propogated through the PXI backplane. If there is no sync card needed
        #  leave the value default.
        self.startTriggerSyncCard = ''

        ## This is the mode of operation for the analog outputs.
        #
        #  There are currently three modes available.  Static mode is where one
        #  static voltage is set with no need for a sample clock.  Finite mode is
        #  where a finite number of voltages will be set at a sample clock rate.
        #  Continuous mode is where a sequence of voltages are generated at a sample
        #  rate and then repeated until the stop() method is called.
        self.mode = dutil.Mode.Finite

        ## The trigger type for the analog outputs.
        #
        #  There are currently two trigger types - "Software" and
        #  "Hardware."  The "Software" mode means that analog output channels are not
        #  syncronized. While "Hardware" means that analog output channels are
        #  syncronized to a start trigger.  The startTriggerSouce attribute must be
        #  configured appropriately.
        self.triggerType = dutil.TriggerType.Software

        ## The number of times to iterate over a Finite number of samples.
        #
        #  This value is only useful in the "Finite" mode.  It is the number of
        #  times that a sequence of voltages will be looped.  The default is allways 1.
        self.loops = 1

        ## The estimated time to generate the samples for a Finite generation.
        #
        #  Once the input buffer of the analog input is configured, the
        #  amount of time it takes to generate the voltages in the buffer can be
        #  estimated.  This is a function of the sample rate and the number of samples
        #  per channel. (This attribute is for internal use only.  This attribute may
        #  not return an accurate value.)
        self.estAcqTime = 0

        ## The analog output buffer.
        #
        #  This is the data that is stored in the buffer of the Analog Output card.
        self.buff = None

        self._timeoutPad = 0.01

    def _getDone(self):
        done = bool32()
        if self.initialized:
            self.status = self.taskRef.GetTaskComplete(ctypes.byref(done))
        else:
            done.value = 1
        return bool(done.value)

    ## @var done
    #  Returns the task done status.
    #
    #  This mode works differently depending on the mode. <br />
    #  <ul>
    #  <li><B>Static and Continuous</B>: done is false after a start
    #  method and true</li>
    #  only after a stop method.
    #  <li><B>Finite</B>: done is false until all samples are
    #  generated.</li></ul>
    done = property(_getDone)

    def _getPauseTriggerSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetDigLvlPauseTrigSrc(buff, buffSize)
            self._pauseTriggerSource = buff.value
        return self._pauseTriggerSource

    def _setPauseTriggerSource(self, value):
        if self.initialized:
            if value == '':
                self.status = self.taskRef.SetPauseTrigType(DAQmx_Val_None)
                self.status = self.taskRef.ResetDigLvlPauseTrigSrc()
            else:
                self.status = self.taskRef.SetDigLvlPauseTrigWhen(
                    DAQmx_Val_High)
                self.status = self.taskRef.SetPauseTrigType(DAQmx_Val_DigLvl)
                self.status = self.taskRef.SetDigLvlPauseTrigSrc(value)
        self._pauseTriggerSource = value

    pauseTriggerSource = property(_getPauseTriggerSource,
                                  _setPauseTriggerSource)

    ## Initializes the analog outputs based on the object's configuration.
    #  @param self The object pointer.
    #  @param physicalChannel A string representing the device and analog
    #  output channels. Example Value: "PXI1Slot3/ao0:7"
    def init(self, physicalChannel):
        self.__createTask(physicalChannel)
        self.initialized = True

        #Finite Mode
        if self.mode == dutil.Mode.Finite:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_FiniteSamps)

        #Continuous Mode
        elif self.mode == dutil.Mode.Continuous:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_ContSamps)

        #Static Mode
        elif self.mode == dutil.Mode.Static:
            self.setSampleRate(self._sampleRate)
            self.setSamplesPerChannel(1)

        self.pauseTriggerSource = self._pauseTriggerSource
        #print self.samplesPerChannel
        #print self._sampleRate
        #print self.clkSource
        #print self.startTriggerSource

    ## This function returns the samples per channel configured in the DAQmx Task.
    #  @param self The object pointer.
    def getSamplesPerChannel(self):
        if self.initialized:
            samplesPerChannel = uInt64()
            self.status = self.taskRef.GetSampQuantSampPerChan(
                ctypes.byref(samplesPerChannel))
            self._samplesPerChannel = samplesPerChannel.value
        return self._samplesPerChannel

    ## This function sets the samples per channel in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value to set the samples per channel.
    def setSamplesPerChannel(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampQuantSampPerChan(uInt64(value))
        self._samplesPerChannel = value

    ## This funciton deletes the samplesPerChannel variable inside the AnalogOutput
    #  object.
    #
    #  It is an internal function that is called in the class destructor. It should
    #  not be called.
    def _delSamplesPerChannel(self):
        """
        This funciton deletes the samplesPerChannel variable inside the AnalogOutput
        object.  It is an internal function that is called in the class destructor.
        It should not be called.
        """
        del self._samplesPerChannel

    samplesPerChannel = property(getSamplesPerChannel, setSamplesPerChannel,
                                 _delSamplesPerChannel)

    ## This function returns the sample clock source configured in the DAQmx Task.
    #  @param self The object pointer.
    def getClkSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetSampClkSrc(buff, buffSize)
            self._clkSource = buff.value
        return self._clkSource

    ## This function sets the sample clock source in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The string value for the clock source terminal.
    def setClkSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkSrc(value)
            value = self.getClkSource()
        self._clkSource = value

    ## This function deletes the clkSource variable within the AnalogOutput object.
    #
    #   It is an internal function that is called in the class destructor.  It should
    #   not be called.
    def _delClkSource(self):
        del self._clkSource

    clkSource = property(getClkSource, setClkSource, _delClkSource)

    ## This function return the start trigger source configured in the DAQmx Task.
    #  @param self The object pointer.
    def getStartTriggerSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetDigEdgeStartTrigSrc(buff, buffSize)
            self._startTriggerSource = buff.value
        return self._startTriggerSource

    ## This function sets the start trigger source in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The string vaue of the start trigger source.
    #  Example value: "\PXI1Slot3\PFI0"
    def setStartTriggerSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetDigEdgeStartTrigSrc(value)
            value = self.getStartTriggerSource()
        self._startTriggerSource = value

    ## This function deletes the startTriggerSource variable within the AnalogOutput object.
    #
    #   It is an internal function that is called in the class destructor.  It should
    #   not be called.
    def _delStartTriggerSource(self):
        del self._startTriggerSource

    startTriggerSource = property(getStartTriggerSource, setStartTriggerSource,
                                  _delStartTriggerSource)

    ## This function returns the number of channels configured in the DAQmx Task.
    #  @param self The object pointer.
    def getNumChannels(self):
        if self.initialized:
            numChannels = uInt32()
            self.status = self.taskRef.GetTaskNumChans(
                ctypes.byref(numChannels))
            self._numChannels = numChannels.value
        return self._numChannels

    numChannels = property(getNumChannels)

    ## This function returns the sample rate configured in the DAQmx Task.
    #  @param self The object pointer.
    def getSampleRate(self):
        if self.initialized:
            sampleRate = float64()
            self.status = self.taskRef.GetSampClkRate(ctypes.byref(sampleRate))
            self._sampleRate = sampleRate.value
        return self._sampleRate

    ## This funciton sets the sample rate in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value of the sample rate.
    def setSampleRate(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkRate(float64(value))
        self._sampleRate = value

    ## This function deletes the sample rate variable inside the AnalogOutput object.
    #  @param self The object pointer.
    def _delSampleRate(self):
        del self._sampleRate

    sampleRate = property(getSampleRate, setSampleRate, _delSampleRate)

    ## This function returns a 1D numpy array of samples with random voltages.
    #  The returned value is intended to be used to write samples to the buffer with
    #  the writeToBuffer() method.
    #  @param self The object pointer.
    #  @param numChannels The number of channels to generate. If this parameter is
    #  not provided, Then the function will generate the number of channels configured
    #  in the analog output task.
    def createTestBuffer(self, numChannels=0):
        numChannels = numChannels if numChannels > 0 else self.getNumChannels()
        return numpy.float64(
            numpy.random.rand(self._samplesPerChannel * numChannels))

    ## This function returns a 1D numpy array of sine waves.  The returned
    #  value is intended to be used to write samples to the buffer with the
    #  writeToBuffer() method.
    #  @param self The object pointer.
    def createSineTestBuffer(self):
        from .createSineWave import createSineWave

        numChannels = self.getNumChannels()
        for i in range(numChannels):
            data = createSineWave(10, 100e3, self._sampleRate,
                                  self._samplesPerChannel,
                                  ((2 * numpy.pi) / numChannels) * i)
            if i == 0:
                sineData = data['amplitude']
            else:
                sineData = numpy.append(sineData, data['amplitude'])

        return sineData

    ## This function writes the specified values into the buffer.
    #  @param self The object pointer.
    #  @param data This is a 1D 64-bit floating point numpy array that contians data
    #  for each channel.  Channels are non-interleaved (channel1 n-samples then
    #  channel2 n-samples).
    def writeToBuffer(self, data):
        autostart = self.mode == dutil.Mode.Static
        self.buff = data

        samplesWritten = int32()
        self.status = self.taskRef.WriteAnalogF64(self._samplesPerChannel,
                                                  autostart, 10,
                                                  DAQmx_Val_GroupByChannel,
                                                  data,
                                                  ctypes.byref(samplesWritten),
                                                  None)
        return samplesWritten.value

    ## This function starts the analog output generation.
    #  @param self The object pointer.
    def start(self):
        self.status = self.taskRef.StartTask()

    ## This functions waits for the analog output generation to complete.
    #  @param self The object pointer.
    def waitUntilDone(self):
        sampPerChan = uInt64()
        self.status = self.taskRef.GetSampQuantSampPerChan(
            ctypes.byref(sampPerChan))
        self.estAcqTime = (self.loops * sampPerChan.value) / self._sampleRate
        #print 'SamplesPerChannel: ' + str(sampPerChan.value)
        #print 'Estimated Acquisition Time: ' + str(self.estAcqTime)
        #if (self.estAcqTime >= 0.01 and self.mode != dutil.Mode.Static):
        if self.mode != dutil.Mode.Static:
            self.status = self.taskRef.WaitUntilTaskDone(
                float64(self.estAcqTime + self._timeoutPad))

    ## This function stops the analog output generation.
    #  @param self The object pointer.
    def stop(self):
        self.status = self.taskRef.StopTask()

    def __createTask(self, physicalChannel):
        """
        This is a private method that creates the Task object for use inside the
        AnalogOutput class."""
        self.status = self.taskRef.CreateAOVoltageChan(physicalChannel, "",
                                                       -10, 10,
                                                       DAQmx_Val_Volts, None)

    def __configTiming(self, sampleMode):
        """
        This is a private method that configures the timing for the Analog Output
        class.
        """
        totalSamples = self._samplesPerChannel * self.loops
        onDemand = bool32()
        self.status = self.taskRef.GetOnDemandSimultaneousAOEnable(
            ctypes.byref(onDemand))
        #print 'On Demand: ' + str(onDemand.value)
        #print 'Trigger Type: ' + str(self.triggerType)
        #print 'Software Trigger Type: ' + str(dutil.TriggerType.Software)
        if self.triggerType == dutil.TriggerType.Software:
            #print 'Software Timing'
            self.status = self.taskRef.CfgSampClkTiming(
                'OnboardClock', float64(self._sampleRate), DAQmx_Val_Rising,
                sampleMode, uInt64(totalSamples))

        elif self.triggerType == dutil.TriggerType.Hardware:
            #print 'Hardware Timing'
            self.status = self.taskRef.CfgSampClkTiming(
                self._clkSource, float64(self._sampleRate), DAQmx_Val_Falling,
                sampleMode, uInt64(totalSamples))
            self.status = self.taskRef.CfgDigEdgeStartTrig(
                self._startTriggerSource, DAQmx_Val_Rising)
            if self.startTriggerSyncCard != '':
                DAQmxConnectTerms(self.startTriggerSyncCard,
                                  self._startTriggerSource,
                                  DAQmx_Val_DoNotInvertPolarity)

    ## This function will close connection to the analog output device and channels.
    #  @param self The object pointer.
    def close(self):
        self.initialized = False
        if self.startTriggerSyncCard != '':
            DAQmxDisconnectTerms(self._startTriggerSource,
                                 self.startTriggerSyncCard)

        self.status = self.taskRef.ClearTask()
        self.taskRef = Task()

    ## This is the destructor for the AnalogOutput Class.
    #  @param self The object pointer.
    def __del__(self):
        if self.initialized:
            self.close()

        del self.taskRef
        del self.initialized
        del self.status
        del self.sampleRate
        del self._numChannels
        del self.samplesPerChannel
        del self.clkSource
        del self.startTriggerSource
        del self.startTriggerSyncCard
        del self.mode
        del self.triggerType
        del self.loops
        del self.estAcqTime