def initializeData(self): # Store the variables that will be used durign this data run self.startWave = self.startWaveSpinBox.value() self.endWave = self.endWaveSpinBox.value() self.scanRate = self.scanRateSpinBox.value() self.sampleRate = self.sampleRateSpinBox.value() self.inputs = self.inputsTextEdit.toPlainText() self.fileName = self.fileNameLineEdit.text() self.metadata = self.metadataTextEdit.toPlainText() # Configure the graph choices comboBoxes self.aiPorts = list() self.aiNames = list() self.graphChoices1.clear() self.graphChoices2.clear() self.graphChoices3.clear() self.inputs = self.inputsTextEdit.toPlainText() textLines = self.inputs.split('\n') for line in textLines: splitLine = line.split(':') if len(splitLine) == 2: # In case there are trailing new lines self.aiPorts.append( str.strip(str(splitLine[0])) ) # str.strip() gets rid of any leading an trailing spaces self.aiNames.append(str.strip(str(splitLine[1]))) self.graphChoices1.addItem(str.strip(str(splitLine[1]))) self.graphChoices2.addItem(str.strip(str(splitLine[1]))) self.graphChoices3.addItem(str.strip(str(splitLine[1]))) # Initialize some dictionaries to link variables to the port names self.aiNameDict = {} portString = "" for port, name in zip(self.aiPorts, self.aiNames): self.aiNameDict[port] = name portString += (dev + port + ", ") portString = portString[: -2] # Since we will have a trailing ", " at the end ## Configure the Analog Input lines self.fSampPerChan = self.sampleRate / len(self.aiPorts) self.tSamp = abs(self.endWave - self.startWave) / self.scanRate self.nSampPerChan = int( self.tSamp * self.fSampPerChan) # pydaqmx.uInt64 can only accept integer inputs self.readAI = pydaqmx.TaskHandle() pydaqshortcuts.makeAnalogIn(portString, self.readAI, self.fSampPerChan, self.nSampPerChan) pydaqmx.DAQmxCfgDigEdgeStartTrig( self.readAI, startTrig, pydaqmx.DAQmx_Val_Rising ) # Trigger to actually start collecting data
def setup_hw_di(fs, lines, callback, callback_samples, start_trigger=None, clock=None, task_name='hw_di'): ''' M series DAQ cards do not have onboard timing engines for digital IO. Therefore, we have to create one (e.g., using a counter or by using the analog input or output sample clock. ''' task = create_task(task_name) mx.DAQmxCreateDIChan(task, lines, '', mx.DAQmx_Val_ChanForAllLines) # Get the current state of the lines so that we know what happened during # the first change detection event. Do this before configuring the timing # of the lines (otherwise we have to start the master clock as well)! mx.DAQmxStartTask(task) initial_state = read_digital_lines(task, 1) mx.DAQmxStopTask(task) # M-series acquisition boards don't have a dedicated engine for digital # acquisition. Use a clock to configure the acquisition. if clock is not None: clock_task = create_task('{}_clock'.format(task_name)) mx.DAQmxCreateCOPulseChanFreq(clock_task, clock, '', mx.DAQmx_Val_Hz, mx.DAQmx_Val_Low, 0, fs, 0.5) mx.DAQmxCfgImplicitTiming(clock_task, mx.DAQmx_Val_ContSamps, int(fs)) clock += 'InternalOutput' if start_trigger: mx.DAQmxCfgDigEdgeStartTrig(clock_task, start_trigger, mx.DAQmx_Val_Rising) setup_timing(task, clock, -1, None) else: setup_timing(task, fs, -1, start_trigger) cb_helper = DigitalSamplesAcquiredCallbackHelper(callback) cb_ptr = mx.DAQmxEveryNSamplesEventCallbackPtr(cb_helper) mx.DAQmxRegisterEveryNSamplesEvent(task, mx.DAQmx_Val_Acquired_Into_Buffer, int(callback_samples), 0, cb_ptr, None) task._cb_ptr = cb_ptr task._cb_helper = cb_helper task._initial_state = initial_state rate = ctypes.c_double() mx.DAQmxGetSampClkRate(task, rate) mx.DAQmxTaskControl(task, mx.DAQmx_Val_Task_Reserve) mx.DAQmxTaskControl(clock_task, mx.DAQmx_Val_Task_Reserve) return [task, clock_task]
def setup_timing(task, channels, delay=0): ''' Configures timing for task Parameters ---------- task : niDAQmx task handle Task to configure timing for channels : list of channels List of channels to configure References ---------- http://www.ni.com/white-paper/11369/en/ http://www.ni.com/pdf/manuals/371235h.pdf ''' fs = get_channel_property(channels, 'fs') sample_clock = get_channel_property(channels, 'sample_clock') start_trigger = get_channel_property(channels, 'start_trigger') samples = get_channel_property(channels, 'samples') reference_clock = get_channel_property(channels, 'reference_clock') if reference_clock: mx.DAQmxSetRefClkSrc(task, reference_clock) if start_trigger: mx.DAQmxCfgDigEdgeStartTrig(task, start_trigger, mx.DAQmx_Val_Rising) if samples == 0: sample_mode = mx.DAQmx_Val_ContSamps samples = 2 else: sample_mode = mx.DAQmx_Val_FiniteSamps samples += delay mx.DAQmxCfgSampClkTiming(task, sample_clock, fs, mx.DAQmx_Val_Rising, sample_mode, samples) properties = get_timing_config(task) actual_fs = properties['sample clock rate'] if round(actual_fs, 4) != round(fs, 4): names = ', '.join(get_channel_property(channels, 'name', True)) m = f'Actual sample clock rate of {actual_fs} does not match ' \ f'requested sample clock rate of {fs} for {names}' raise ValueError(m) return properties
def __init__(self, osas, simulation=False): """ :param osas: dictionary containing OSA configuration info :param simulation: if True we operate in simulation mode """ self.simulation = simulation self.osas = dict(osas) self.handles = {} if self.simulation: self.f0 = {} for osa_name, osa in self.osas.items(): self.f0[osa_name] = np.random.uniform(0, 1) * osa["num_samples"] return try: # Reset all DAQ cards devices = set([osa["device"] for _, osa in self.osas.items()]) for device in devices: PyDAQmx.DAQmxResetDevice(device) for name, osa in self.osas.items(): self.handles[name] = task_handle = PyDAQmx.TaskHandle(0) PyDAQmx.DAQmxCreateTask("osa_" + name, byref(task_handle)) PyDAQmx.DAQmxCreateAIVoltageChan( task_handle, "/{}/{}".format(osa["device"], osa["input_channel"]), "Voltage", PyDAQmx.DAQmx_Val_NRSE, -osa["v_span"] / 2, osa["v_span"] / 2, PyDAQmx.DAQmx_Val_Volts, None) PyDAQmx.DAQmxCfgSampClkTiming(task_handle, None, osa["sample_rate"], PyDAQmx.DAQmx_Val_Rising, PyDAQmx.DAQmx_Val_FiniteSamps, osa["num_samples"]) PyDAQmx.DAQmxCfgDigEdgeStartTrig( task_handle, "/{}/{}".format(osa["device"], osa["trigger_channel"]), PyDAQmx.DAQmx_Val_Falling) except DAQError as err: self.clear() raise OSAException(err)
def setup_hw_ai(fs, lines, expected_range, callback, callback_samples, sync_ao): # Record AI filter delay task = create_task() lb, ub = expected_range mx.DAQmxCreateAIVoltageChan(task, lines, '', mx.DAQmx_Val_RSE, lb, ub, mx.DAQmx_Val_Volts, '') if sync_ao: mx.DAQmxCfgDigEdgeStartTrig(task, 'ao/StartTrigger', mx.DAQmx_Val_Rising) mx.DAQmxCfgSampClkTiming(task, 'ao/SampleClock', fs, mx.DAQmx_Val_Rising, mx.DAQmx_Val_ContSamps, int(fs)) else: mx.DAQmxCfgSampClkTiming(task, '', fs, mx.DAQmx_Val_Rising, mx.DAQmx_Val_ContSamps, int(fs)) result = ctypes.c_uint32() mx.DAQmxGetBufInputBufSize(task, result) buffer_size = result.value mx.DAQmxGetTaskNumChans(task, result) n_channels = result.value log.debug('Buffer size for %s automatically allocated as %d samples', lines, buffer_size) log.debug('%d channels in task', n_channels) new_buffer_size = np.ceil(buffer_size/callback_samples)*callback_samples mx.DAQmxSetBufInputBufSize(task, int(new_buffer_size)) callback_helper = SamplesAcquiredCallbackHelper(callback, n_channels) cb_ptr = mx.DAQmxEveryNSamplesEventCallbackPtr(callback_helper) mx.DAQmxRegisterEveryNSamplesEvent(task, mx.DAQmx_Val_Acquired_Into_Buffer, int(callback_samples), 0, cb_ptr, None) task._cb_ptr = cb_ptr return task
def set_up_clock(self, clock_frequency=None, clock_channel=None, scanner=False, idle=False, delay=2.0): """ Configures the hardware clock of the NiDAQ card to give the timing. @param float clock_frequency: if defined, this sets the frequency of the clock in Hz @param string clock_channel: if defined, this is the physical channel of the clock within the NI card. @param bool scanner: if set to True method will set up a clock function for the scanner, otherwise a clock function for a counter will be set. @param bool idle: set whether idle situation of the counter (where counter is doing nothing) is defined as True = 'Voltage High/Rising Edge' False = 'Voltage Low/Falling Edge' @return int: error code (0:OK, -1:error) """ if not scanner and self._clock_daq_task is not None: self.log.error( 'Another counter clock is already running, close this one first.' ) return -1 if scanner and self._scanner_clock_daq_task is not None: self.log.error( 'Another scanner clock is already running, close this one first.' ) return -1 # Create handle for task, this task will generate pulse signal for # photon counting my_clock_daq_task = daq.TaskHandle() smiq = daq.TaskHandle() switch = daq.TaskHandle() cam = daq.TaskHandle() # assign the clock frequency, if given if clock_frequency is not None: if not scanner: self._clock_frequency = float(clock_frequency) else: self._scanner_clock_frequency = float(clock_frequency) self._smiq_clock_frequency = float(clock_frequency) / 2. self._switch_clock_frequency = float(clock_frequency) / 2. self._cam_clock_frequency = float(clock_frequency) else: if not scanner: self._clock_frequency = self._default_clock_frequency else: self._scanner_clock_frequency = self._default_scanner_clock_frequency # use the correct clock in this method if scanner: my_clock_frequency = self._scanner_clock_frequency * 2 else: my_clock_frequency = self._clock_frequency * 2 # assign the clock channel, if given if clock_channel is not None: if not scanner: self._clock_channel = clock_channel else: self._scanner_clock_channel = clock_channel # use the correct clock channel in this method if scanner: my_clock_channel = self._scanner_clock_channel self._smiq_channel = self._smiq_channel self._switch_channel = self._switch_channel self._cam_channel = self._cam_channel else: my_clock_channel = self._clock_channel # check whether only one clock pair is available, since some NI cards # only one clock channel pair. if self._scanner_clock_channel == self._clock_channel: if not ((self._clock_daq_task is None) and (self._scanner_clock_daq_task is None)): self.log.error( 'Only one clock channel is available!\n' 'Another clock is already running, close this one first ' 'in order to use it for your purpose!') return -1 if self.pseudo_pulsed: self.clock_duty = self.duty_value / ( 1 / self._scanner_clock_frequency) / 2 self.switch_duty = self.duty_value / ( 1 / self._switch_clock_frequency) / 2 self.switch_delay = 0 else: self.clock_duty = 0.9998 self.switch_duty = 0.5 self.switch_delay = 0 # Adjust the idle state if necessary my_idle = daq.DAQmx_Val_High if idle else daq.DAQmx_Val_Low try: # create task for clock task_name = 'ScannerClock' if scanner else 'CounterClock' daq.DAQmxCreateTask(task_name, daq.byref(my_clock_daq_task)) # create a digital clock channel with specific clock frequency: daq.DAQmxCreateCOPulseChanFreq( # The task to which to add the channels my_clock_daq_task, # which channel is used? my_clock_channel, # Name to assign to task (NIDAQ uses by # default the physical channel name as # the virtual channel name. If name is specified, then you must use the name # when you refer to that channel in other NIDAQ functions) 'Clock Producer', # units, Hertz in our case daq.DAQmx_Val_Hz, # idle state my_idle, # initial delay delay, my_clock_frequency / 2, self.clock_duty) daq.DAQmxCfgImplicitTiming( # Define task my_clock_daq_task, daq.DAQmx_Val_ContSamps, # buffer length which stores temporarily the number of # generated samples 1000) ############################## # Configure SMIQ trigger clock ############################## duty_cycle = 0.1 d = 0 daq.DAQmxCreateTask('mySmiqTask', daq.byref(smiq)) # Create channel to generate digital pulses that freq and dutyCycle # define and adds the channel to the task daq.DAQmxCreateCOPulseChanFreq( smiq, self. _smiq_channel, # The name of the counter to use to create virtual channels "mySmiqChannel", # The name to assign to the created virtual channel daq.DAQmx_Val_Hz, # The units in which to specify freq. daq.DAQmx_Val_Low, # The resting state of the output terminal. d, # The amount of time in seconds to wait before generating the # first pulse. self._smiq_clock_frequency, # The frequency at which to generate pulses. duty_cycle, # The width of the pulse divided by the pulse period. ) j = 1000 # # Sets only the number of samples to acquire or generate without specifying timing. daq.DAQmxCfgImplicitTiming( smiq, # daq.DAQmx_Val_ContSamps, daq.DAQmx_Val_ContSamps, # Acquire or generate samples until you stop the task. j # the buffer size ) daq.DAQmxCfgDigEdgeStartTrig(smiq, my_clock_channel + 'InternalOutput', daq.DAQmx_Val_Rising) ############################## # Configure switch trigger clock ############################## duty_cycle = 0.5 d = 0 daq.DAQmxCreateTask('mySwitchTask', daq.byref(switch)) # Create channel to generate digital pulses that freq and dutyCycle # define and adds the channel to the task daq.DAQmxCreateCOPulseChanFreq( switch, self. _switch_channel, # The name of the counter to use to create virtual channels "mySwitchChannel", # The name to assign to the created virtual channel daq.DAQmx_Val_Hz, # The units in which to specify freq. daq.DAQmx_Val_High, # The resting state of the output terminal. self.switch_delay, # The amount of time in seconds to wait before generating the # first pulse. self._switch_clock_frequency, # The frequency at which to generate pulses. self.switch_duty, # The width of the pulse divided by the pulse period. ) j = 1000 # # Sets only the number of samples to acquire or generate without specifying timing. daq.DAQmxCfgImplicitTiming( switch, # daq.DAQmx_Val_ContSamps, daq.DAQmx_Val_ContSamps, # Acquire or generate samples until you stop the task. j # the buffer size ) daq.DAQmxCfgDigEdgeStartTrig(switch, my_clock_channel + 'InternalOutput', daq.DAQmx_Val_Rising) ############################## # Configure cam trigger clock ############################## duty_cycle = 0.1 d = (1. / self._cam_clock_frequency) / 4. d = 0 daq.DAQmxCreateTask('myCamTask', daq.byref(cam)) # Create channel to generate digital pulses that freq and dutyCycle # define and adds the channel to the task daq.DAQmxCreateCOPulseChanFreq( cam, self. _cam_channel, # The name of the counter to use to create virtual channels "myCamChannel", # The name to assign to the created virtual channel daq.DAQmx_Val_Hz, # The units in which to specify freq. daq.DAQmx_Val_Low, # The resting state of the output terminal. d, # The amount of time in seconds to wait before generating the # first pulse. self._cam_clock_frequency, # The frequency at which to generate pulses. duty_cycle, # The width of the pulse divided by the pulse period. ) j = 1000 # # Sets only the number of samples to acquire or generate without specifying timing. daq.DAQmxCfgImplicitTiming( cam, # daq.DAQmx_Val_ContSamps, daq.DAQmx_Val_ContSamps, # Acquire or generate samples until you stop the task. j # the buffer size ) daq.DAQmxCfgDigEdgeStartTrig(cam, my_clock_channel + 'InternalOutput', daq.DAQmx_Val_Rising) if scanner: self._scanner_clock_daq_task = my_clock_daq_task # Added the daq tasks to class variables self._smiq_clock_daq_task = smiq self._switch_clock_daq_task = switch self._cam_clock_daq_task = cam else: # actually start the preconfigured clock task daq.DAQmxStartTask(my_clock_daq_task) self._clock_daq_task = my_clock_daq_task except BaseException: self.log.exception('Error while setting up clock.') return -1 return 0
def start(self, test=False, **kwargs): """ 1. Creates a task using settings. 2. Starts the task. You need to call wait_and_clean() after you start() kwargs are sent to self() to set parameters. """ self(**kwargs) # make sure everything that should be a list is a list if not isinstance(self["ao_channels"], Iterable): self["ao_channels"] = [self["ao_channels"]] # if the first element of the waveform is not an array if len(_n.shape(self["ao_waveforms"][0])) < 1: self["ao_waveforms"] = [self["ao_waveforms"]] # create the task object. This doesn't return an object, because # National Instruments. Instead, we have this handle, and we need # to be careful about clearing the thing attached to the handle. debug("output task handle") _mx.DAQmxClearTask(self._handle) _mx.DAQmxCreateTask(self["ao_task_name"], _mx.byref(self._handle)) # create all the output channels debug("output channels") # this is an array of output data arrays, grouped by channel samples = 0 data = _n.array([]) # loop over all the channels for n in range(len(self["ao_channels"])): # get the channel-specific attributes name = self["ao_channels"][n] nickname = name.replace("/", "") debug(name) if isinstance(self["ao_min"], Iterable): ao_min = self["ao_min"][n] else: ao_min = self["ao_min"] if isinstance(self["ao_max"], Iterable): ao_max = self["ao_max"][n] else: ao_max = self["ao_max"] if isinstance(self["ao_units"], Iterable): ao_units = self["ao_units"][n] else: ao_units = self["ao_units"] waveform = self["ao_waveforms"][n] # add an output channel _mx.DAQmxCreateAOVoltageChan(self._handle, name, nickname, ao_min, ao_max, ao_units, "") # add the corresponding output wave to the master data array debug("data", data, "waveform", waveform) data = _n.concatenate([data, waveform]) # Set the samples number to the biggest output array size samples = max(len(self["ao_waveforms"][n]), samples) # Configure the clock debug("output clock") # make sure we don't exceed the max #ao_max_rate = _mx.float64() #_mx.DAQmxGetSampClkMaxRate(self._handle, _mx.byref(ao_max_rate)) #if self['ao_rate'] > ao_max_rate.value: # print "ERROR: ao_rate is too high! Current max = "+str(ao_max_rate.value) # self.clean() # return False _mx.DAQmxCfgSampClkTiming(self._handle, self["ao_clock_source"], self["ao_rate"], self["ao_clock_edge"], self["ao_mode"], samples) # if we're supposed to, export a signal if not self['ao_export_terminal'] == None: _mx.DAQmxExportSignal(self._handle, self['ao_export_signal'], self['ao_export_terminal']) # update to the actual ao_rate (may be different than what was set) ao_rate = _mx.float64() _mx.DAQmxGetSampClkRate(self._handle, _mx.byref(ao_rate)) debug("output actual ao_rate =", ao_rate.value) self(ao_rate=ao_rate.value) # Configure the trigger debug("output trigger") _mx.DAQmxCfgDigEdgeStartTrig(self._handle, self["ao_trigger_source"], self["ao_trigger_slope"]) # Set the post-trigger delay try: n = self["ao_delay"] * 10e6 if n < 2: n = 2 _mx.DAQmxSetStartTrigDelayUnits(self._handle, _mx.DAQmx_Val_Ticks) _mx.DAQmxSetStartTrigDelay(self._handle, n) except: _traceback.print_exc() # write the data to the analog outputs (arm it) debug("output write", len(data)) write_success = _mx.int32() _mx.DAQmxWriteAnalogF64( self._handle, samples, False, self["ao_timeout"], _mx. DAQmx_Val_GroupByChannel, # Type of grouping of data in the array (for interleaved use DAQmx_Val_GroupByScanNumber) data, # Array of data to output _mx.byref(write_success), # Output the number of successful write None) # Reserved input (just put in None...) debug("success:", samples, write_success) if test: self.clean() else: # Start the task!! debug("output start") try: _mx.DAQmxStartTask(self._handle) except: _traceback.print_exc() return True
def start(self, test=False, **kwargs): """ 1. Creates a task using settings. 2. Starts the task. 3. Fetches data. You need to call read_and_clean() after start(). kwargs are sent to self() to set parameters. """ # update any last-minute settings self(**kwargs) debug(self.settings) # create the task object. This doesn't return an object, because # National Instruments. Instead, we have this handle, and we need # to be careful about clearing the thing attached to the handle. debug("input task handle") _mx.DAQmxClearTask(self._handle) _mx.DAQmxCreateTask(self["ai_task_name"], _mx.byref(self._handle)) # Loop over all the input channel names and create a channel for each debug("input channels") for n in range(len(self["ai_channels"])): # get the channel-specific attributes name = self["ai_channels"][n] nickname = name.replace("/", "") debug(name) if isinstance(self["ai_terminal_config"], Iterable): ai_terminal_config = self["ai_terminal_config"][n] else: ai_terminal_config = self["ai_terminal_config"] if isinstance(self["ai_min"], Iterable): ai_min = self["ai_min"][n] else: ai_min = self["ai_min"] if isinstance(self["ai_max"], Iterable): ai_max = self["ai_max"][n] else: ai_max = self["ai_max"] if isinstance(self["ai_units"], Iterable): ai_units = self["ai_units"][n] else: ai_units = self["ai_units"] # add an input channel debug(name) _mx.DAQmxCreateAIVoltageChan(self._handle, name, nickname, ai_terminal_config, ai_min, ai_max, ai_units, "") # set the input coupling (optional) if not self["ai_input_couplings"] == None: ai_input_coupling = self["ai_input_couplings"][n] if ai_input_coupling == "AC": _mx.DAQmxSetAICoupling(self._handle, name, _mx.DAQmx_Val_AC) if ai_input_coupling == "DC": _mx.DAQmxSetAICoupling(self._handle, name, _mx.DAQmx_Val_DC) if ai_input_coupling == "GND": _mx.DAQmxSetAICoupling(self._handle, name, _mx.DAQmx_Val_GND) # Configure the clock debug("input clock") # make sure we don't exceed the max ai_max_rate = _mx.float64() _mx.DAQmxGetSampClkMaxRate(self._handle, _mx.byref(ai_max_rate)) if self['ai_rate'] > ai_max_rate.value: print("ERROR: ai_rate is too high! Current max = " + str(ai_max_rate.value)) self.clean() return False _mx.DAQmxCfgSampClkTiming(self._handle, self["ai_clock_source"], self["ai_rate"], self["ai_clock_edge"], self["ai_mode"], self["ai_samples"]) # get the actual ai_rate ai_rate = _mx.float64() _mx.DAQmxGetSampClkRate(self._handle, _mx.byref(ai_rate)) debug("input actual ai_rate =", ai_rate.value) self(ai_rate=ai_rate.value) # Configure the trigger debug("input trigger") _mx.DAQmxCfgDigEdgeStartTrig(self._handle, self.settings["ai_trigger_source"], self.settings["ai_trigger_slope"]) # Set the post-trigger delay try: n = self["ai_delay"] * 10e6 if n < 2: n = 2 _mx.DAQmxSetStartTrigDelayUnits(self._handle, _mx.DAQmx_Val_Ticks) _mx.DAQmxSetStartTrigDelay(self._handle, n) except: _traceback.print_exc() # in test mode, just check that it doesn't fail and clean up. if test: self.clean() # otherwise, start the show! else: debug("input start") try: _mx.DAQmxStartTask(self._handle) except: _traceback.print_exc() return True
def setTriggerFalling(self, c, name, chan): pydaqmx.DAQmxCfgDigEdgeStartTrig(self.handleDict[name], chan, pydaqmx.DAQmx_Val_Falling)
def takeData(self): self.takingData = True time.sleep(0.5) print "Starting scan" self.readAI = pydaqmx.TaskHandle() pydaqshortcuts.makeAnalogIn(self.portString, self.readAI, self.sampFreqPerChan, self.nSampsPerChan) pydaqmx.DAQmxCfgDigEdgeStartTrig( self.readAI, startTrig, pydaqmx.DAQmx_Val_Rising ) # Trigger to actually start collecting data pydaqmx.DAQmxStartTask(self.readAI) self.tn.write("(exec 'laser1:ctl:scan:start) \r\n") # Initialize some variables ptsPerChan = 0 stepBuffer = np.zeros( (int(self.stepBufferSize), ), dtype=np.int16 ) # Needs to be int16 to work with PyDAQmx, the DAQ units are all integers anyway fullBuffer = np.zeros( (int(self.nSampsPerChan), int(self.nChan) + 1), dtype=np.float32 ) # One extra column for the nominal wavelength. Use float32 so we can cast an int16 into it as well as put floats in fullBuffer[:, -1] = np.linspace( self.startWave, self.endWave, self.nSampsPerChan ) # Place nominal wavelengths in the last column just so we don't have "+1" floating around the loops while (self.nChan * ptsPerChan ) < self.nSamps: # While we haven't collected all the data # Make a thread to grab data from the readAI task in parallel to the rest of the code t = threading.Thread(target=pydaqshortcuts.putDataInQueue, args=(self.readAI, stepBuffer, self.nChan, self.q)) t.start() # Retrieve the data from the queue and sort it stepData = self.q.get() numTakenPerChan = self.q.get( ).value # The ctypes object stores the value in a python-friendly format using the .value attribute for j in range(self.nChan): fullBuffer[ptsPerChan:ptsPerChan + numTakenPerChan, j] = stepData[j * numTakenPerChan:(j + 1) * numTakenPerChan] ptsPerChan += numTakenPerChan # Make sure this is updated AFTER we used it in fullBuffer for axis, canvas in zip(self.axes, self.canvases): axis.cla( ) # Clear the axes so that we aren't re-drawing the old data underneath the new data (which slows down plotting ~1.5x) axis.plot(fullBuffer[0:ptsPerChan, -1], fullBuffer[0:ptsPerChan, self.axisToColDict[axis]], 'k') canvas.draw() app.processEvents( ) # If we don't have this line the graphs don't update until the end of data collection # for axis, canvas in zip(self.axes, self.canvases): # p = threading.Thread(target=self.plotData, args=(axis, canvas, fullBuffer[0:ptsPerChan, -1], fullBuffer[0:ptsPerChan, self.axisToColDict[axis]])) # p.start() # app.processEvents() # If we don't have this line the graphs don't update until the end of data collection pydaqmx.DAQmxStopTask(self.readAI) self.takingData = False return fullBuffer