def write_ao_raw_sequence(self, c, raw_sequence_channels_list_json): """ NOT all AO channels are written for now, we select channels by the length of sequence, in principle it should be >2. This function works for analog stream out where sequence input are in 2D, number_of_ports rows, float element array, like [[x, x,..., x], ...], where -10.0 <= x <= 10.0. Each row corresponds to a channel in the task. Each column corresponds to a sample to write to each channel. """ raw_sequence_channels_list = json.loads( raw_sequence_channels_list_json) sequence_bytes = make_sequence_bytes_ao(raw_sequence_channels_list) num_port = len(sequence_bytes) num_samp = len(sequence_bytes[0]) if self.seq_task_ao: # Make sure task is close before you add new stuffs. self.seq_task_ao.close() self.seq_task_ao = nidaqmx.Task() self.seq_task_ao.ao_channels.add_ao_voltage_chan( 'Dev2/ao0:{}'.format(num_port - 1)) self.seq_task_ao.timing.cfg_samp_clk_timing( rate=self.rate_ao, source=self.clk_channel_ao, samps_per_chan=num_samp) #rate, samps_per_chan self.writer_ao = AnalogMultiChannelWriter( self.seq_task_ao.out_stream, auto_start=False ) # use multi-channel writer, so input is 2D array. self.writer_ao.write_many_sample(sequence_bytes, timeout=-1)
def run(self): """ Starts writing a waveform continuously while reading the buffer periodically """ #DAQ with nidaqmx.Task() as slave_Task3, nidaqmx.Task() as master_Task: #slave_Task3 = nidaqmx.Task() slave_Task3.ao_channels.add_ao_voltage_chan("/Dev1/ao0:1") master_Task.ai_channels.add_ai_voltage_chan("/Dev1/ai0") slave_Task3.timing.cfg_samp_clk_timing( rate=self.sampleRate, source='ai/SampleClock', sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS) # Analoginput master_Task.timing.cfg_samp_clk_timing( rate=self.sampleRate, sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS, samps_per_chan=self.readNumber) reader = AnalogSingleChannelReader(master_Task.in_stream) writer = AnalogMultiChannelWriter(slave_Task3.out_stream) reader.auto_start = False writer.auto_start = False writer.write_many_sample(self.wave) """Reading data from the buffer in a loop. The idea is to let the task read more than could be loaded in the buffer for each iteration. This way the task will have to wait slightly longer for incoming samples. And leaves the buffer entirely clean. This way we always know the correct numpy size and are always left with an empty buffer (and the buffer will not slowly fill up).""" output = np.zeros(self.readNumber) slave_Task3.start( ) #Will wait for the readtask to start so it can use its clock master_Task.start() while not self.isInterruptionRequested(): reader.read_many_sample( data=output, number_of_samples_per_channel=self.readNumber) #Emiting the data just received as a signal Dataholder_average = np.mean(output.reshape( self.averagenumber, -1), axis=0) self.data_PMT = np.reshape( Dataholder_average, (self.ypixelnumber, self.ScanArrayXnum)) if self.ypixelnumber == 500: self.data_PMT = self.data_PMT[:, 50:550] * -1 elif self.ypixelnumber == 256: self.data_PMT = self.data_PMT[:, 70:326] * -1 self.measurement.emit(self.data_PMT)
def __init__(self, *args, read_task, write_task_z, write_task_xy): super().__init__(*args) self.read_task = read_task self.write_task_xy = write_task_xy self.write_task_z = write_task_z self.z_writer = AnalogMultiChannelWriter(write_task_z.out_stream) self.xy_writer = AnalogMultiChannelWriter(write_task_xy.out_stream) self.z_reader = AnalogSingleChannelReader(read_task.in_stream) self.xy_array = np.zeros((2, self.n_samples)) self.z_array = np.zeros((4, self.n_samples)) self.read_array = np.zeros(self.n_samples) self.setup_tasks()
def _init_hardware_interface(self): # Initialize DAQmx interface self._scan_task = nidaqmx.Task() self._scan_timing = nidaqmx.task.Timing(self._scan_task) dac_name = self.niconfig.dac_device dac_ao_channel_ids = [ dac_name + '/' + self.niconfig.line_trig_ch, dac_name + '/' + self.niconfig.frame_trig_ch, dac_name + '/' + self.niconfig.x_ch, dac_name + '/' + self.niconfig.y_ch ] for ch_name in dac_ao_channel_ids: self._scan_task.ao_channels.add_ao_voltage_chan(ch_name) self._scan_task.timing.cfg_samp_clk_timing( self.niconfig.dac_rate, # TODO move? source="", active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS) self._scan_task.regen_mode = nidaqmx.constants.RegenerationMode.DONT_ALLOW_REGENERATION # TODO test self._scan_writer = AnalogMultiChannelWriter( self._scan_task.out_stream) print("NIOCTController: initialized DAQmx interface...") # Initialize IMAQ interface PyIMAQ.imgOpen(self.niconfig.cam_device) PyIMAQ.imgConfigTrigBufferWithTTL1(timeout=1000) PyIMAQ.imgSetAttributeROI(0, 0, self.oct.alines_per_buffer, self.oct.aline_size) PyIMAQ.imgInitBuffer(self.niconfig.number_of_imaq_buffers) print("NIOCTController: initialized IMAQ interface...")
def scan_loop(self, read_task, write_task): writer = AnalogMultiChannelWriter(write_task.out_stream) reader = AnalogMultiChannelReader(read_task.in_stream) first_write = True i_acquired = 0 while not self.stop_event.is_set() and ( not self.scanning_parameters.scanning_state == ScanningState.EXPERIMENT_RUNNING or i_acquired < self.scanning_parameters.n_frames): # The first write has to be defined before the task starts try: writer.write_many_sample(self.write_signals) if i_acquired == 0: self.check_start_plane() if first_write: read_task.start() write_task.start() first_write = False reader.read_many_sample( self.read_buffer, number_of_samples_per_channel=self.n_samples_in, timeout=1, ) i_acquired += 1 except nidaqmx.DaqError as e: print(e) break self.data_queue.put(self.read_buffer[0, :]) # if new parameters have been received and changed, update # them, breaking out of the loop if the experiment is not running try: self.new_parameters = self.parameter_queue.get(timeout=0.0001) if self.new_parameters != self.scanning_parameters and ( self.scanning_parameters.scanning_state != ScanningState.EXPERIMENT_RUNNING or self.new_parameters.scanning_state == ScanningState.PREVIEW): break except Empty: pass # calculate duration self.calculate_duration()
def makeSinglePic(self, writeTask, readTask): #Check if the measurement is possible and execute if self.checkMeasurement(): self.writeTask.timing.cfg_samp_clk_timing( rate=self.sampleRate, sample_mode=nidaqmx.constants.AcquisitionType.FINITE, samps_per_chan=self.outputData.size) self.readTask.timing.cfg_samp_clk_timing( rate=self.sampleRate, sample_mode=nidaqmx.constants.AcquisitionType.FINITE, samps_per_chan=self.inputData.size) reader = AnalogSingleChannelReader(self.readTask.in_stream) writer = AnalogMultiChannelWriter(self.writeTask.out_stream) estT = self.outputData.size / self.sampleRate writer.write_many_sample(self.outputData) self.writeTask.start() reader.read_many_sample(self.inputData, timeout=estT + 5) return self.inputData
def test_one_sample(self, x_series_device, seed): # Reset the pseudorandom number generator with seed. random.seed(seed) # Select a random loopback channel pair on the device. loopback_channel_pairs = self._get_analog_loopback_channels( x_series_device) number_of_channels = random.randint(2, len(loopback_channel_pairs)) channels_to_test = random.sample( loopback_channel_pairs, number_of_channels) with nidaqmx.Task() as write_task, nidaqmx.Task() as read_task: write_task.ao_channels.add_ao_voltage_chan( flatten_channel_string( [c.output_channel for c in channels_to_test]), max_val=10, min_val=-10) read_task.ai_channels.add_ai_voltage_chan( flatten_channel_string( [c.input_channel for c in channels_to_test]), max_val=10, min_val=-10) writer = AnalogMultiChannelWriter(write_task.out_stream) reader = AnalogMultiChannelReader(read_task.in_stream) values_to_test = numpy.array( [random.uniform(-10, 10) for _ in range(number_of_channels)], dtype=numpy.float64) writer.write_one_sample(values_to_test) time.sleep(0.001) values_read = numpy.zeros(number_of_channels, dtype=numpy.float64) reader.read_one_sample(values_read) numpy.testing.assert_allclose( values_read, values_to_test, rtol=0.05, atol=0.005)
def sawtooth(ai_chan, ao_chan_x, ao_chan_y, sRate, imAngle, outputX, outputY, xPixels, yPixels, exPixels): """ Notes: Variables: ai_chan = string specifying the analog input channel ao_chan_x, ao_chan_y = string, specifying the analog output channel for x and y """ print("Measurement started") writingRate = sRate readingRate = writingRate #radAngle = math.pi/180*imAngle #exPixelsX = int(math.cos(radAngle)*exPixels) #Amount of excess pixels in X #exPixelsY = int(math.sin(radAngle)*exPixels) #Amount of excess pixels in Y totalxPixels = exPixels + xPixels totalyPixels = yPixels print("total x pixels", totalxPixels, "xpixels", xPixels, "exPixels", exPixels) inputData = np.zeros( outputX.size + 1) #Need the extra one because of the sample shift due to the trigger outputData = np.array([outputX, outputY]) outputChannels = ao_chan_x + ", " + ao_chan_y with nidaqmx.Task() as writeTask, nidaqmx.Task() as readTask: writeTask.ao_channels.add_ao_voltage_chan(outputChannels) readTask.ai_channels.add_ai_voltage_chan(ai_chan) #In the USB6001 DAQ there seems to be no SampleClock, therefore we cannot sync the signals without an external trigger/clock #sample_clock = '/Dev2/ai/SampleClock' writeTask.timing.cfg_samp_clk_timing( rate=writingRate, sample_mode=nidaqmx.constants.AcquisitionType.FINITE, samps_per_chan=outputX.size) readTask.timing.cfg_samp_clk_timing( rate=readingRate, sample_mode=nidaqmx.constants.AcquisitionType.FINITE, samps_per_chan=inputData.size) writeTask.triggers.start_trigger.cfg_dig_edge_start_trig( trigger_source='/Dev2/ai/StartTrigger' ) #Setting the trigger on the analog input reader = AnalogSingleChannelReader(readTask.in_stream) writer = AnalogMultiChannelWriter(writeTask.out_stream) estT = outputX.size / writingRate writer.write_many_sample(outputData) writeTask.start() reader.read_many_sample(inputData, timeout=estT + 5) writeTask.wait_until_done(timeout=estT + 5) readTask.wait_until_done(timeout=estT + 5) print("Done with data") #----------------------------Creating the Image---------------------------- print("Creating the image") #Creating the array for the image imArray = inputData[1::].reshape( (totalyPixels, totalxPixels)) #Not taking into account the first sample #Plotting the image plt.imshow(imArray, cmap=plt.cm.gray) plt.show()
def triangle(ai_chan, ao_chan_x, ao_chan_y, sRate, imAngle, outputX, outputY, xPixels, yPixels, exPixels): print("Measurement started") writingRate = sRate readingRate = writingRate radAngle = math.pi / 180 * imAngle exPixelsX = int(math.cos(radAngle) * exPixels) #Amount of excess pixels in X exPixelsY = int(math.sin(radAngle) * exPixels) #Amount of excess pixels in Y totalxPixels = 2 * exPixelsX + xPixels #2 times excess pixels because we overshoot on two sides totalyPixels = 2 * exPixelsY + yPixels inputData = np.zeros( outputX.size + 1) #Need the extra one because of the sample shift due to the trigger outputData = np.array([outputX, outputY]) outputChannels = ao_chan_x + ", " + ao_chan_y with nidaqmx.Task() as writeTask, nidaqmx.Task() as readTask: writeTask.ao_channels.add_ao_voltage_chan(outputChannels) readTask.ai_channels.add_ai_voltage_chan(ai_chan) #In the USB6001 DAQ there seems to be no SampleClock, therefore we cannot sync the signals without an external trigger/clock #sample_clock = '/Dev2/ai/SampleClock' writeTask.timing.cfg_samp_clk_timing( rate=writingRate, sample_mode=nidaqmx.constants.AcquisitionType.FINITE, samps_per_chan=outputX.size) readTask.timing.cfg_samp_clk_timing( rate=readingRate, sample_mode=nidaqmx.constants.AcquisitionType.FINITE, samps_per_chan=inputData.size) writeTask.triggers.start_trigger.cfg_dig_edge_start_trig( trigger_source='/Dev2/ai/StartTrigger' ) #Setting the trigger on the analog input reader = AnalogSingleChannelReader(readTask.in_stream) writer = AnalogMultiChannelWriter(writeTask.out_stream) estT = outputX.size / writingRate writer.write_many_sample(outputData) writeTask.start() reader.read_many_sample(inputData, timeout=estT + 5) writeTask.wait_until_done(timeout=estT + 5) readTask.wait_until_done(timeout=estT + 5) #----------------------------Creating the Image---------------------------- #Creating the array for the image imArray = np.zeros( (totalyPixels, totalxPixels)) #Not taking into account the first sample #Resolving the sample shift inputData = inputData[1::] #Now we have to loop over the positions because the reshape function does not work forward = True xPix = exPixelsX #Defines the x position yPix = 0 #Defines the y position #Doing the first iteration in case we start at x=0 imArray[yPix, xPix] = inputData[0] xPix += 1 #Going through all the other values i = 1 while i < inputData.size: imArray[yPix, xPix] = inputData[i] #Moving one y position up when reaching one end of the image if xPix == (totalxPixels - 1): xPix = totalxPixels - exPixelsX - 1 yPix += 1 #Going to the next value manually i += 1 if i == inputData.size: break #Making sure i does not exceed the input data array imArray[yPix, xPix] = inputData[i] forward = not forward elif xPix == 0: xPix = exPixelsX yPix += 1 #Going to the next value manually i += 1 if i == inputData.size: break #Making sure i does not exceed the input data array imArray[yPix, xPix] = inputData[i] forward = not forward if forward == True: xPix += 1 #Moving one up if moving forward over the x pixels else: xPix -= 1 #Moving one down if moving backward over the x pixels i += 1 #Going to the next value of the input data #Plotting the image plt.imshow(imArray, cmap=plt.cm.gray) plt.show()
def load(self, stim, digstim=None, use_ao_trigger=False): # check if the modes are up to date assert type(stim) is list, 'Stim needs to be a list of numpy arrays' self._check_modes() self.nsamples = int( np.max([np.max(s.shape) for s in stim if not s is None])) aoconversion = self._get_conversion(False) if not self.task_ai is None and self.task_ao is None: self.task_ai.timing.cfg_samp_clk_timing( rate=self.srate, samps_per_chan=self.nsamples) if not self.task_ao is None: self.task_ao.timing.cfg_samp_clk_timing( rate=self.srate, samps_per_chan=self.nsamples) self.task_ao.export_signals.export_signal( nidaqmx.constants.Signal.SAMPLE_CLOCK, 'PFI0') if not self.task_ai is None: if use_ao_trigger: self.task_ai.triggers.start_trigger.cfg_dig_edge_start_trig( 'ao/StartTrigger') self.task_ai.timing.cfg_samp_clk_timing( rate=self.srate, source='PFI0', samps_per_chan=self.nsamples) # Take care of the conversions stims = np.zeros((self.n_output_chan, self.nsamples), dtype=np.float64) # Fix the analog output conversions for i in range(self.n_output_chan): if i < len(stim): if stim[i] is None: continue if not len(stim[i]): continue stims[i] = stim[i] * aoconversion[i] if hasattr(self, 'task_clock'): self.task_clock.timing.cfg_implicit_timing( samps_per_chan=self.nsamples) if not self.task_ao is None: self.task_ao.timing.cfg_samp_clk_timing( self.srate, source=self.samp_clk_terminal, active_edge=nidaqmx.constants.Edge.RISING, samps_per_chan=self.nsamples) if not self.task_ai is None: self.task_ai.timing.cfg_samp_clk_timing( self.srate, source=self.samp_clk_terminal, active_edge=nidaqmx.constants.Edge.FALLING, samps_per_chan=self.nsamples) self.writer = AnalogMultiChannelWriter(self.task_ao.out_stream) self.reader = AnalogMultiChannelReader(self.task_ai.in_stream) self.writer.write_many_sample(stims) self.run_do = False if not digstim is None: if not self.task_do is None: self.task_do.timing.cfg_samp_clk_timing( rate=self.srate, source=self.samp_clk_terminal, active_edge=nidaqmx.constants.Edge.RISING, samps_per_chan=self.nsamples) #this works for 32 bit ports only, all iputs must be on the same port for now. digstims = np.zeros((self.nsamples, 32), dtype=np.uint32) for i in range(len(digstim)): if digstim[i] is None: continue if not len(digstim[i]): continue digstims[:, i] = (digstim[i] > 0) self.di_writer = DigitalMultiChannelWriter( self.task_do.out_stream) self.di_writer.write_many_sample_port_uint32( np.packbits(digstims, bitorder='little').reshape( (1, -1)).view(np.uint32)) self.run_do = True self.acquired = False
def launch(self): if self.outputs is not None: self.write_task = nidaqmx.Task() if self.Nchannel_analog_in > 0: self.read_analog_task = nidaqmx.Task() if self.Nchannel_digital_in > 0: self.read_digital_task = nidaqmx.Task() self.sample_clk_task = nidaqmx.Task() # Use a counter output pulse train task as the sample clock source # for both the AI and AO tasks. self.sample_clk_task.co_channels.add_co_pulse_chan_freq( '{0}/ctr0'.format(self.device.name), freq=self.sampling_rate) self.sample_clk_task.timing.cfg_implicit_timing( samps_per_chan=int(self.max_time / self.dt)) self.samp_clk_terminal = '/{0}/Ctr0InternalOutput'.format( self.device.name) ### ---- OUTPUTS ---- ## if self.outputs is not None: self.write_task.ao_channels.add_ao_voltage_chan( flatten_channel_string(self.output_channels), max_val=10, min_val=-10) self.write_task.timing.cfg_samp_clk_timing( self.sampling_rate, source=self.samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=int(self.max_time / self.dt)) ### ---- INPUTS ---- ## if self.Nchannel_analog_in > 0: self.read_analog_task.ai_channels.add_ai_voltage_chan( flatten_channel_string(self.analog_input_channels), max_val=10, min_val=-10) if self.Nchannel_digital_in > 0: self.read_digital_task.di_channels.add_di_chan( flatten_channel_string(self.digital_input_channels)) if self.Nchannel_analog_in > 0: self.read_analog_task.timing.cfg_samp_clk_timing( self.sampling_rate, source=self.samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=int(self.max_time / self.dt)) if self.Nchannel_digital_in > 0: self.read_digital_task.timing.cfg_samp_clk_timing( self.sampling_rate, source=self.samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=int(self.max_time / self.dt)) if self.Nchannel_analog_in > 0: self.analog_reader = AnalogMultiChannelReader( self.read_analog_task.in_stream) self.read_analog_task.register_every_n_samples_acquired_into_buffer_event( self.buffer_size, self.reading_task_callback) if self.Nchannel_digital_in > 0: self.digital_reader = DigitalMultiChannelReader( self.read_digital_task.in_stream) self.read_digital_task.register_every_n_samples_acquired_into_buffer_event( self.buffer_size, self.reading_task_callback) if self.outputs is not None: self.writer = AnalogMultiChannelWriter(self.write_task.out_stream) self.writer.write_many_sample(self.outputs) # Start the read task before starting the sample clock source task. if self.Nchannel_analog_in > 0: self.read_analog_task.start() if self.Nchannel_digital_in > 0: self.read_digital_task.start() if self.outputs is not None: self.write_task.start() self.sample_clk_task.start() if self.filename is not None: np.save(self.filename.replace('.npy', '.start.npy'), np.ones(1) * time.time()) # saving the time stamp of the start ! self.running, self.data_saved = True, False
class Acquisition: def __init__( self, dt=1e-3, Nchannel_analog_in=2, Nchannel_digital_in=1, max_time=10, buffer_time=0.5, filename=None, device=None, outputs=None, output_steps=[], # should be a set of dictionaries, output_steps=[{'channel':0, 'onset': 2.3, 'duration': 1., 'value':5}] verbose=False): self.running, self.data_saved = False, False self.dt = dt self.max_time = max_time self.sampling_rate = 1. / self.dt self.buffer_size = int(buffer_time / self.dt) self.Nchannel_analog_in = Nchannel_analog_in self.Nchannel_digital_in = Nchannel_digital_in self.filename = filename self.select_device() # preparing input channels # - analog: self.analog_data = np.zeros((Nchannel_analog_in, 1), dtype=np.float64) if self.Nchannel_analog_in > 0: self.analog_input_channels = get_analog_input_channels( self.device)[:Nchannel_analog_in] # - digital: self.digital_data = np.zeros((1, 1), dtype=np.uint32) if self.Nchannel_digital_in > 0: self.digital_input_channels = get_digital_input_channels( self.device)[:Nchannel_digital_in] # preparing output channels if outputs is not None: # used as a flag for output or not self.output_channels = get_analog_output_channels( self.device)[:outputs.shape[0]] elif len(output_steps) > 0: Nchannel = max([d['channel'] for d in output_steps]) + 1 # have to be elements t = np.arange(int(self.max_time / self.dt)) * self.dt outputs = np.zeros((Nchannel, len(t))) # add as many channels as necessary for step in output_steps: if step['channel'] > outputs.shape[0]: outputs = np.append(outputs, np.zeros((1, len(t))), axis=0) for step in output_steps: cond = (t > step['onset']) & (t <= step['onset'] + step['duration']) outputs[step['channel']][cond] = step['value'] self.output_channels = get_analog_output_channels( self.device)[:outputs.shape[0]] self.outputs = outputs def launch(self): if self.outputs is not None: self.write_task = nidaqmx.Task() if self.Nchannel_analog_in > 0: self.read_analog_task = nidaqmx.Task() if self.Nchannel_digital_in > 0: self.read_digital_task = nidaqmx.Task() self.sample_clk_task = nidaqmx.Task() # Use a counter output pulse train task as the sample clock source # for both the AI and AO tasks. self.sample_clk_task.co_channels.add_co_pulse_chan_freq( '{0}/ctr0'.format(self.device.name), freq=self.sampling_rate) self.sample_clk_task.timing.cfg_implicit_timing( samps_per_chan=int(self.max_time / self.dt)) self.samp_clk_terminal = '/{0}/Ctr0InternalOutput'.format( self.device.name) ### ---- OUTPUTS ---- ## if self.outputs is not None: self.write_task.ao_channels.add_ao_voltage_chan( flatten_channel_string(self.output_channels), max_val=10, min_val=-10) self.write_task.timing.cfg_samp_clk_timing( self.sampling_rate, source=self.samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=int(self.max_time / self.dt)) ### ---- INPUTS ---- ## if self.Nchannel_analog_in > 0: self.read_analog_task.ai_channels.add_ai_voltage_chan( flatten_channel_string(self.analog_input_channels), max_val=10, min_val=-10) if self.Nchannel_digital_in > 0: self.read_digital_task.di_channels.add_di_chan( flatten_channel_string(self.digital_input_channels)) if self.Nchannel_analog_in > 0: self.read_analog_task.timing.cfg_samp_clk_timing( self.sampling_rate, source=self.samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=int(self.max_time / self.dt)) if self.Nchannel_digital_in > 0: self.read_digital_task.timing.cfg_samp_clk_timing( self.sampling_rate, source=self.samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=int(self.max_time / self.dt)) if self.Nchannel_analog_in > 0: self.analog_reader = AnalogMultiChannelReader( self.read_analog_task.in_stream) self.read_analog_task.register_every_n_samples_acquired_into_buffer_event( self.buffer_size, self.reading_task_callback) if self.Nchannel_digital_in > 0: self.digital_reader = DigitalMultiChannelReader( self.read_digital_task.in_stream) self.read_digital_task.register_every_n_samples_acquired_into_buffer_event( self.buffer_size, self.reading_task_callback) if self.outputs is not None: self.writer = AnalogMultiChannelWriter(self.write_task.out_stream) self.writer.write_many_sample(self.outputs) # Start the read task before starting the sample clock source task. if self.Nchannel_analog_in > 0: self.read_analog_task.start() if self.Nchannel_digital_in > 0: self.read_digital_task.start() if self.outputs is not None: self.write_task.start() self.sample_clk_task.start() if self.filename is not None: np.save(self.filename.replace('.npy', '.start.npy'), np.ones(1) * time.time()) # saving the time stamp of the start ! self.running, self.data_saved = True, False def close(self): """ not optimal... nidaqmx has weird behaviors sometimes... :( """ if self.running: if self.Nchannel_digital_in > 0: self.read_digital_task.close() if self.Nchannel_analog_in > 0: self.read_analog_task.close() if self.outputs is not None: self.write_task.close() self.sample_clk_task.close() if (self.filename is not None): if self.data_saved: print('[ok] NIdaq data already saved as: %s ' % self.filename) else: np.save( self.filename, { 'analog': self.analog_data[:, 1:], 'digital': self.digital_data[:, 1:] }) print('[ok] NIdaq data saved as: %s ' % self.filename) self.data_saved = True self.running = False def reading_task_callback(self, task_idx, event_type, num_samples, callback_data=None): if self.running: if self.Nchannel_analog_in > 0: analog_buffer = np.zeros( (self.Nchannel_analog_in, num_samples), dtype=np.float64) self.analog_reader.read_many_sample(analog_buffer, num_samples, timeout=WAIT_INFINITELY) self.analog_data = np.append(self.analog_data, analog_buffer, axis=1) if self.Nchannel_digital_in > 0: digital_buffer = np.zeros((1, num_samples), dtype=np.uint32) self.digital_reader.read_many_sample_port_uint32( digital_buffer, num_samples, timeout=WAIT_INFINITELY) self.digital_data = np.append(self.digital_data, digital_buffer, axis=1) else: self.close() return 0 # needed for this callback to be well defined (see nidaqmx doc). def select_device(self): success = False try: self.device = find_x_series_devices()[0] print('X-series card found:', self.device) success = True except BaseException: pass try: self.device = find_m_series_devices()[0] print('M-series card found:', self.device) success = True except BaseException: pass if not success: print('Neither M-series nor X-series NI DAQ card found')
def run(self): """ Starts writing a waveform continuously while reading the buffer periodically """ with nidaqmx.Task() as slave_Task, nidaqmx.Task() as master_Task: slave_Task.ao_channels.add_ao_voltage_chan("/Dev1/ao0:1") master_Task.ai_channels.add_ai_voltage_chan("/Dev1/ai0") if self.flag_continuous == False: # Timing of analog output channels slave_Task.timing.cfg_samp_clk_timing( rate=self.Daq_sample_rate, source='ai/SampleClock', sample_mode=AcquisitionType.FINITE, samps_per_chan=self.Totalscansamples) # Timing of recording channels master_Task.timing.cfg_samp_clk_timing( rate=self.Daq_sample_rate, sample_mode=AcquisitionType.FINITE, samps_per_chan=self.Totalscansamples) else: # Timing of analog output channels slave_Task.timing.cfg_samp_clk_timing( rate=self.Daq_sample_rate, source='ai/SampleClock', sample_mode=AcquisitionType.CONTINUOUS) # Timing of recording channels master_Task.timing.cfg_samp_clk_timing( rate=self.Daq_sample_rate, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=self.Totalscansamples) reader = AnalogSingleChannelReader(master_Task.in_stream) writer = AnalogMultiChannelWriter(slave_Task.out_stream) reader.auto_start = False writer.auto_start = False writer.write_many_sample(self.Galvo_samples) """Reading data from the buffer in a loop. The idea is to let the task read more than could be loaded in the buffer for each iteration. This way the task will have to wait slightly longer for incoming samples. And leaves the buffer entirely clean. This way we always know the correct numpy size and are always left with an empty buffer (and the buffer will not slowly fill up).""" output = np.zeros(self.Totalscansamples) slave_Task.start( ) #Will wait for the readtask to start so it can use its clock master_Task.start() # while not self.isInterruptionRequested(): reader.read_many_sample( data=output, number_of_samples_per_channel=self.Totalscansamples) Dataholder_average = np.mean(output.reshape(self.averagenum, -1), axis=0) if self.flag_return_image == True: # Calculate the mean of average frames. self.data_PMT = np.reshape( Dataholder_average, (self.pixel_number, self.total_X_sample_number)) # Cut off the flying back part. if self.pixel_number == 500: self.image_PMT = self.data_PMT[:, 50:550] * -1 elif self.pixel_number == 256: self.image_PMT = self.data_PMT[:, 70:326] * -1 return self.image_PMT
def stim_and_rec(device, t_array, analog_inputs, analog_outputs, N_digital_inputs=0): dt = t_array[1] - t_array[0] sampling_rate = 1. / dt # if analog_outputs.shape[0]>0: output_analog_channels = get_analog_output_channels( device)[:analog_outputs.shape[0]] input_analog_channels = get_analog_input_channels( device)[:analog_inputs.shape[0]] if N_digital_inputs > 0: input_digital_channels = get_digital_input_channels( device)[:N_digital_inputs] with nidaqmx.Task() as write_analog_task, nidaqmx.Task() as read_analog_task,\ nidaqmx.Task() as read_digital_task, nidaqmx.Task() as sample_clk_task: # Use a counter output pulse train task as the sample clock source # for both the AI and AO tasks. sample_clk_task.co_channels.add_co_pulse_chan_freq('{0}/ctr0'.format( device.name), freq=sampling_rate) sample_clk_task.timing.cfg_implicit_timing(samps_per_chan=len(t_array)) samp_clk_terminal = '/{0}/Ctr0InternalOutput'.format(device.name) ### ---- OUTPUTS ---- ## write_analog_task.ao_channels.add_ao_voltage_chan( flatten_channel_string(output_analog_channels), max_val=10, min_val=-10) write_analog_task.timing.cfg_samp_clk_timing( sampling_rate, source=samp_clk_terminal, active_edge=Edge.RISING, samps_per_chan=len(t_array)) ### ---- INPUTS ---- ## read_analog_task.ai_channels.add_ai_voltage_chan( flatten_channel_string(input_analog_channels), max_val=10, min_val=-10) if N_digital_inputs > 0: read_digital_task.di_channels.add_di_chan( flatten_channel_string(input_digital_channels)) read_analog_task.timing.cfg_samp_clk_timing( sampling_rate, source=samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=len(t_array)) if N_digital_inputs > 0: read_digital_task.timing.cfg_samp_clk_timing( sampling_rate, source=samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=len(t_array)) analog_writer = AnalogMultiChannelWriter(write_analog_task.out_stream) analog_reader = AnalogMultiChannelReader(read_analog_task.in_stream) if N_digital_inputs > 0: digital_reader = DigitalMultiChannelReader( read_digital_task.in_stream) analog_writer.write_many_sample(analog_outputs) # Start the read and write tasks before starting the sample clock # source task. read_analog_task.start() if N_digital_inputs > 0: read_digital_task.start() write_analog_task.start() sample_clk_task.start() analog_reader.read_many_sample( analog_inputs, number_of_samples_per_channel=len(t_array), timeout=t_array[-1] + 2 * dt) if N_digital_inputs > 0: digital_inputs = np.zeros((1, len(t_array)), dtype=np.uint32) digital_reader.read_many_sample_port_uint32( digital_inputs, number_of_samples_per_channel=len(t_array), timeout=t_array[-1] + 2 * dt) else: digital_inputs = None return analog_inputs, digital_inputs
class SignalWriter(QtCore.QThread): """A QThread that periodically reads the voltages output by the data acquisition card. This thread outputs signals to the function generator according to parameters. These parameters can be modified by other threads as this thread simply reads them. Also, keep in mind that the __init__ method only runs once to establish properties and default values. Attributes: funcg_name (str): name given to the NI card by the drivers writechannel_list (list): list containing all of the connected channels funcg_rate (int): rate at which the NI DAQ card generate data writechunksize (int): The QThread adds this amount of data to the buffer at once. Hard coded to be the rate divided by 10. zcoeff (float): used to calculate the signals, defined in detail other places running (bool): used to control the state of the run loop from outside this thread """ errorMessage = QtCore.pyqtSignal(object) def __init__(self, funcg_name, writechannel_list, funcg_rate, writechunksize, zcoeff, vmulti, freq, camber, zphase, calib_xamp, calib_yamp, calib_zamp): super().__init__() # Inherit properties of a QThread # Static variables self.funcg_name = funcg_name self.writechannel_list = writechannel_list self.funcg_rate = funcg_rate self.writechunksize = self.funcg_rate // 10 self.zcoeff = zcoeff # Changing variables self.vmulti = vmulti self.freq = freq # single integer self.camber = camber self.zphase = zphase # single integer # Calibration variables self.calib_mode = False # variable to store whether in calibration mode self.calib_xamp = calib_xamp self.calib_yamp = calib_yamp self.calib_zamp = calib_zamp # pre-allocate an empty output array self.output = np.zeros([len(self.writechannel_list), self.writechunksize]) self.running = False # Variable to keep track of whether the thread is running. # Instantiate the WaveGenerator self.WaveGen = WaveGenerator() def run(self): """Runs when the start method is called on the thread. First, the output channels are initialized. If the thread can't, it emits an error signal. Next, it sets the generation rate and mode. Other properties are set for continuous modulation of the signal. The buffer size is set to be 2 times the *writechunksize* to allow for some wiggle room if a data point is a microsecond late. Next, an event is registered along with the continuous generation mode that signals the **add_more_data** method after every *writechunksize* values are transferred from the buffer. This is what allows this run method to loop, as the writeTask never truly finishes as data points are constantly being added to the buffer. For the first write to the buffer, two *writechunksize* chunks are written to fill up the buffer completely. """ self.running = True self.writeTask = nidaqmx.Task() # Start the task # Add input channels for index, i in enumerate(self.writechannel_list): channel_string = self.funcg_name + '/' + f'ao{i}' try: self.writeTask.ao_channels.add_ao_voltage_chan(channel_string) except Exception as e: if index == 0: self.errorMessage.emit('Could not open write channels. Are device names correct?' f" Devices connected: {find_ni_devices()}") return # Set the generation rate, and buffer size. self.writeTask.timing.cfg_samp_clk_timing( rate=self.funcg_rate, sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS) # Set more properties for continuous signal modulation self.writeTask.out_stream.regen_mode = nidaqmx.constants.RegenerationMode.DONT_ALLOW_REGENERATION self.writeTask.out_stream.output_buf_size = 2 * self.writechunksize # Register the listening method to add more data self.writeTask.register_every_n_samples_transferred_from_buffer_event( sample_interval=self.writechunksize, callback_method=self.add_more_data) # Initialize the writer self.writer = AnalogMultiChannelWriter(self.writeTask.out_stream) if not self.calib_mode: self.output = self.WaveGen.generate_waves( funcg_rate=self.funcg_rate, writechunksize=self.writechunksize, vmulti=self.vmulti, freq=self.freq, camber=self.camber, zphase=self.zphase, zcoeff=self.zcoeff) elif self.calib_mode: self.output = self.WaveGen.generate_calib_waves( funcg_rate=self.funcg_rate, writechunksize=self.writechunksize, calib_xamp=self.calib_xamp, calib_yamp=self.calib_yamp, calib_zamp=self.calib_zamp ) # Write the first set of data into the output buffer try: self.writer.write_many_sample(data=self.output) # Write two chunks of beginning data to avoid interruption except: self.errorMessage.emit('Could not write data to the output. Is the output device name correct?' f" Devices connected: {find_ni_devices()}") return self.writer.write_many_sample(data=self.output) # write a second chunk to the buffer # Start the task, which will hold the thread at this location, continually calling add_more_data self.writeTask.start() def add_more_data(self, task_handle, every_n_samples_event_type, number_of_samples, callback_data): """This method adds data to the buffer when it is called. If running is false, it instead closes the NI writeTask. """ if self.running is True: if not self.calib_mode: self.output = self.WaveGen.generate_waves( funcg_rate=self.funcg_rate, writechunksize=self.writechunksize, vmulti=self.vmulti, freq=self.freq, camber=self.camber, zphase=self.zphase, zcoeff=self.zcoeff) elif self.calib_mode: self.output = self.WaveGen.generate_calib_waves( funcg_rate=self.funcg_rate, writechunksize=self.writechunksize, calib_xamp=self.calib_xamp, calib_yamp=self.calib_yamp, calib_zamp=self.calib_zamp ) self.writer.write_many_sample(data=self.output) else: self.writeTask.close() return 0
def run(self): """Runs when the start method is called on the thread. First, the output channels are initialized. If the thread can't, it emits an error signal. Next, it sets the generation rate and mode. Other properties are set for continuous modulation of the signal. The buffer size is set to be 2 times the *writechunksize* to allow for some wiggle room if a data point is a microsecond late. Next, an event is registered along with the continuous generation mode that signals the **add_more_data** method after every *writechunksize* values are transferred from the buffer. This is what allows this run method to loop, as the writeTask never truly finishes as data points are constantly being added to the buffer. For the first write to the buffer, two *writechunksize* chunks are written to fill up the buffer completely. """ self.running = True self.writeTask = nidaqmx.Task() # Start the task # Add input channels for index, i in enumerate(self.writechannel_list): channel_string = self.funcg_name + '/' + f'ao{i}' try: self.writeTask.ao_channels.add_ao_voltage_chan(channel_string) except Exception as e: if index == 0: self.errorMessage.emit('Could not open write channels. Are device names correct?' f" Devices connected: {find_ni_devices()}") return # Set the generation rate, and buffer size. self.writeTask.timing.cfg_samp_clk_timing( rate=self.funcg_rate, sample_mode=nidaqmx.constants.AcquisitionType.CONTINUOUS) # Set more properties for continuous signal modulation self.writeTask.out_stream.regen_mode = nidaqmx.constants.RegenerationMode.DONT_ALLOW_REGENERATION self.writeTask.out_stream.output_buf_size = 2 * self.writechunksize # Register the listening method to add more data self.writeTask.register_every_n_samples_transferred_from_buffer_event( sample_interval=self.writechunksize, callback_method=self.add_more_data) # Initialize the writer self.writer = AnalogMultiChannelWriter(self.writeTask.out_stream) if not self.calib_mode: self.output = self.WaveGen.generate_waves( funcg_rate=self.funcg_rate, writechunksize=self.writechunksize, vmulti=self.vmulti, freq=self.freq, camber=self.camber, zphase=self.zphase, zcoeff=self.zcoeff) elif self.calib_mode: self.output = self.WaveGen.generate_calib_waves( funcg_rate=self.funcg_rate, writechunksize=self.writechunksize, calib_xamp=self.calib_xamp, calib_yamp=self.calib_yamp, calib_zamp=self.calib_zamp ) # Write the first set of data into the output buffer try: self.writer.write_many_sample(data=self.output) # Write two chunks of beginning data to avoid interruption except: self.errorMessage.emit('Could not write data to the output. Is the output device name correct?' f" Devices connected: {find_ni_devices()}") return self.writer.write_many_sample(data=self.output) # write a second chunk to the buffer # Start the task, which will hold the thread at this location, continually calling add_more_data self.writeTask.start()
def test_many_sample(self, x_series_device, seed): # Reset the pseudorandom number generator with seed. random.seed(seed) number_of_samples = random.randint(20, 100) sample_rate = random.uniform(1000, 5000) # Select a random loopback channel pair on the device. loopback_channel_pairs = self._get_analog_loopback_channels( x_series_device) number_of_channels = random.randint(2, len(loopback_channel_pairs)) channels_to_test = random.sample( loopback_channel_pairs, number_of_channels) with nidaqmx.Task() as write_task, nidaqmx.Task() as read_task, \ nidaqmx.Task() as sample_clk_task: # Use a counter output pulse train task as the sample clock source # for both the AI and AO tasks. sample_clk_task.co_channels.add_co_pulse_chan_freq( '{0}/ctr0'.format(x_series_device.name), freq=sample_rate) sample_clk_task.timing.cfg_implicit_timing( samps_per_chan=number_of_samples) samp_clk_terminal = '/{0}/Ctr0InternalOutput'.format( x_series_device.name) write_task.ao_channels.add_ao_voltage_chan( flatten_channel_string( [c.output_channel for c in channels_to_test]), max_val=10, min_val=-10) write_task.timing.cfg_samp_clk_timing( sample_rate, source=samp_clk_terminal, active_edge=Edge.RISING, samps_per_chan=number_of_samples) read_task.ai_channels.add_ai_voltage_chan( flatten_channel_string( [c.input_channel for c in channels_to_test]), max_val=10, min_val=-10) read_task.timing.cfg_samp_clk_timing( sample_rate, source=samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=number_of_samples) writer = AnalogMultiChannelWriter(write_task.out_stream) reader = AnalogMultiChannelReader(read_task.in_stream) values_to_test = numpy.array( [[random.uniform(-10, 10) for _ in range(number_of_samples)] for _ in range(number_of_channels)], dtype=numpy.float64) writer.write_many_sample(values_to_test) # Start the read and write tasks before starting the sample clock # source task. read_task.start() write_task.start() sample_clk_task.start() values_read = numpy.zeros( (number_of_channels, number_of_samples), dtype=numpy.float64) reader.read_many_sample( values_read, number_of_samples_per_channel=number_of_samples, timeout=2) numpy.testing.assert_allclose( values_read, values_to_test, rtol=0.05, atol=0.005)
class NIBoards(AbstractScanInterface): def __init__(self, *args, read_task, write_task_z, write_task_xy): super().__init__(*args) self.read_task = read_task self.write_task_xy = write_task_xy self.write_task_z = write_task_z self.z_writer = AnalogMultiChannelWriter(write_task_z.out_stream) self.xy_writer = AnalogMultiChannelWriter(write_task_xy.out_stream) self.z_reader = AnalogSingleChannelReader(read_task.in_stream) self.xy_array = np.zeros((2, self.n_samples)) self.z_array = np.zeros((4, self.n_samples)) self.read_array = np.zeros(self.n_samples) self.setup_tasks() def setup_tasks(self): # Configure the channels # read channel is only the piezo position on board 1 self.read_task.ai_channels.add_ai_voltage_chan( self.conf["z_board"]["read"]["channel"], min_val=self.conf["z_board"]["read"]["min_val"], max_val=self.conf["z_board"]["read"]["max_val"], ) # write channels are on board 1: piezo and z galvos self.write_task_z.ao_channels.add_ao_voltage_chan( self.conf["z_board"]["write"]["channel"], min_val=self.conf["z_board"]["write"]["min_val"], max_val=self.conf["z_board"]["write"]["max_val"], ) # on board 2: lateral galvos self.write_task_xy.ao_channels.add_ao_voltage_chan( self.conf["xy_board"]["write"]["channel"], min_val=self.conf["xy_board"]["write"]["min_val"], max_val=self.conf["xy_board"]["write"]["max_val"], ) # Set the timing of both to the onboard clock so that they are synchronised self.read_task.timing.cfg_samp_clk_timing( rate=self.sample_rate, source="OnboardClock", active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=self.n_samples, ) self.write_task_z.timing.cfg_samp_clk_timing( rate=self.sample_rate, source="OnboardClock", active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=self.n_samples, ) self.write_task_xy.timing.cfg_samp_clk_timing( rate=self.sample_rate, source="OnboardClock", active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=self.n_samples, ) # This is necessary to synchronise reading and writing self.read_task.triggers.start_trigger.cfg_dig_edge_start_trig( self.conf["z_board"]["sync"]["channel"], Edge.RISING) def start(self): self.read_task.start() self.write_task_xy.start() self.write_task_z.start() def write(self): self.z_writer.write_many_sample(self.z_array) self.xy_writer.write_many_sample(self.xy_array) def read(self): self.z_reader.read_many_sample( self.read_array, number_of_samples_per_channel=self.n_samples, timeout=1, ) self.read_array[:] = self.read_array @property def z_piezo(self): return self.read_array / self.conf["piezo"]["scale"] @z_piezo.setter def z_piezo(self, waveform): self.z_array[0, :] = waveform * self.conf["piezo"]["scale"] @property def z_lateral(self): return self.z_array[1, :] @property def z_frontal(self): return self.z_array[2, :] @z_lateral.setter def z_lateral(self, waveform): self.z_array[1, :] = waveform @z_frontal.setter def z_frontal(self, waveform): self.z_array[2, :] = waveform @property def camera_trigger(self): return self.z_array[3, :] @camera_trigger.setter def camera_trigger(self, waveform): self.z_array[3, :] = waveform @property def xy_frontal(self): return self.xy_array[1, :] @xy_frontal.setter def xy_frontal(self, waveform): self.xy_array[1, :] = waveform @property def xy_lateral(self): return self.xy_array[0, :] @xy_lateral.setter def xy_lateral(self, waveform): self.xy_array[0, :] = waveform
class NIServer(ThreadedServer): """ Provide access to NI DAQ via nidaqmx.""" name = 'ni' def initServer(self): self.rate_do = config().set_do_rate() self.timeout_do = 150 self.clk_channel_do = 'PFI4' self.rate_ao = config().set_ao_rate() self.timeout_ao = 150 self.trigger_channel = 'PFI0' self.clk_channel_ao = 'PFI1' self.rate_clk = config().set_clk() # self.source = '100kHzTimebase' # self.source = 'PXI_Clk10' # Internal clock source, 10 MHz self.source = 'PFI0' #External CLK source input, 10 MHz self.timeout_clk = 150 # (Variable clock output is = p0.0) self.seq_task_ao = None self.seq_task_do = None self.seq_task_clk = None self.ai_trigger = 'PFI1' # Should be PFI1 self.pd_task_ai = None self.pmt_task_ai = None self.timeout_ai = 180 super(NIServer, self).initServer() @setting(1) def write_ao_manual(self, c, voltage, port): """ Writes Voltage for ONE channel that we are interested in. """ if self.seq_task_ao: # Make sure task is close before you add new stuffs. self.seq_task_ao.close() self.seq_task_ao = None with nidaqmx.Task() as task: task.ao_channels.add_ao_voltage_chan('Dev2/ao{}'.format(port)) task.write(voltage, auto_start=True) task.stop() @setting(2) def write_do_manual(self, c, boolean): """ Writes Boolean for channels set to "manual" """ if self.seq_task_do: # Make sure task is close before you add new stuffs. self.seq_task_do.close() self.seq_task_do = None with nidaqmx.Task() as task: task.do_channels.add_do_chan( 'Dev1/port0/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) task.do_channels.add_do_chan( 'Dev1/port1/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) task.do_channels.add_do_chan( 'Dev1/port2/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) task.do_channels.add_do_chan( 'Dev1/port3/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) task.write(boolean, auto_start=True) task.stop() @setting(3) def write_clk_manual(self, c, boolean): """ CLK Manual value is always False. """ if self.seq_task_clk: # Make sure task is close before you add new stuffs. self.seq_task_clk.close() self.seq_task_clk = None with nidaqmx.Task() as task: task.do_channels.add_do_chan('Dev0/port0/line0') task.write(boolean, auto_start=True) task.stop() @setting(4, sequence='s') def write_ao_sequence(self, c, sequence): """ NOT all AO channels are written, we selecte channels by the length of sequence, in principle it should be >2. """ sequence_json = json.loads(sequence) num_port = len(sequence_json) num_samp = len(sequence_json[0]) if self.seq_task_ao: # Make sure task is close before you add new stuffs. self.seq_task_ao.close() self.seq_task_ao = nidaqmx.Task() self.seq_task_ao.ao_channels.add_ao_voltage_chan( 'Dev2/ao0:{}'.format(num_port - 1)) self.seq_task_ao.timing.cfg_samp_clk_timing( rate=self.rate_ao, source=self.clk_channel_ao, samps_per_chan=num_samp) #rate, samps_per_chan # self.seq_task_ao.triggers.start_trigger.cfg_dig_edge_start_trig(self.trigger_channel) # Default with rising edge # If share the same clock, there is no need to use DO trigger self.seq_task_ao.write(sequence_json, auto_start=False, timeout=-1) @setting(5, sequence='s') def write_do_sequence(self, c, sequence): """ We write all 32 DO channels at a time. Default time-out is 120 s, which sets the limit of total sequence length. """ sequence_json = json.loads(sequence) num_samp = len(sequence_json[0]) if self.seq_task_do: # Make sure task is close before you add new stuffs. self.seq_task_do.close() self.seq_task_do = nidaqmx.Task() self.seq_task_do.do_channels.add_do_chan( 'Dev1/port0/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) self.seq_task_do.do_channels.add_do_chan( 'Dev1/port1/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) self.seq_task_do.do_channels.add_do_chan( 'Dev1/port2/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) self.seq_task_do.do_channels.add_do_chan( 'Dev1/port3/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) self.seq_task_do.timing.cfg_samp_clk_timing( rate=self.rate_do, source=self.clk_channel_do, samps_per_chan=num_samp) #rate, samps_per_chan self.seq_task_do.write(sequence_json, auto_start=False, timeout=-1) @setting(6, sequence_path='s') def write_clk_sequence(self, c, sequence_path): """ We use port0/line0 on AI card as the "Variable Clock Reference" to sample the DO and AO card. """ sequence = np.load(sequence_path + '.npy').tolist() num_samp = len(sequence) if self.seq_task_clk: # Make sure task is close before you add new stuffs. self.seq_task_clk.close() self.seq_task_clk = nidaqmx.Task() self.seq_task_clk.do_channels.add_do_chan('Dev0/port0/line0') self.seq_task_clk.timing.cfg_samp_clk_timing( rate=self.rate_clk, source=self.source, samps_per_chan=num_samp) #rate, samps_per_chan self.seq_task_clk.write(sequence, auto_start=False, timeout=-1) @setting(11) def start_ao_sequence(self, c): if self.seq_task_ao.is_task_done(): self.seq_task_ao.start() else: print('Sequencer AO tasks do not exist!') @setting(12) def start_do_sequence(self, c): if self.seq_task_do.is_task_done(): self.seq_task_do.start() else: print('Sequencer DO tasks do not exist!') @setting(13) def start_clk_sequence(self, c): if self.seq_task_clk.is_task_done(): self.seq_task_clk.start() # conductor_server = self.client.conductor # shot = conductor_server.get_shotnumber() # print('NI DAQ tasks shot#{} started'.format(shot)) self.seq_task_ao.wait_until_done(self.timeout_ao) # print('ao done') self.seq_task_do.wait_until_done(self.timeout_do) # print('do done') self.seq_task_clk.wait_until_done(self.timeout_clk) # print('clk done') self.seq_task_ao.stop() self.seq_task_do.stop() self.seq_task_clk.stop() # print('NI DAQ tasks shot#{} done'.format(shot)) else: print('Sequencer CLK tasks do not exist!') # @setting(11) # def stop_sequence(self, c): # if self.seq_task_clk != None and self.seq_task_do != None and self.seq_task_ao != None: # self.seq_task_clk.close() # self.seq_task_do.close() # self.seq_task_ao.close() # else: # pass @setting(15) def read_ai_manual(self, c, port): with nidaqmx.Task() as task: task.ai_channels.add_ai_voltage_chan("Dev0/ai{}".format(port)) data = task.read() return data @setting(16, returns='s') def pd_ai_trigger(self, c, port, samp_rate, n_samp): """ Read Analog-In voltage for PD in Trigger Mode """ if self.pd_task_ai: # Make sure task is close before you add new stuffs. self.pd_task_ai.close() self.pd_task_ai = nidaqmx.Task() self.pd_task_ai.ai_channels.add_ai_voltage_chan( "Dev0/ai{}".format(port)) self.pd_task_ai.timing.cfg_samp_clk_timing( rate=int(samp_rate), samps_per_chan=int(n_samp)) #rate, samps_per_chan self.pd_task_ai.triggers.start_trigger.cfg_dig_edge_start_trig( self.ai_trigger) # Default with rising edge print('AI configured.') data = self.pd_task_ai.read( number_of_samples_per_channel=-1, timeout=self.timeout_ai ) # -1 means will read all available samples self.pd_task_ai.wait_until_done(self.timeout_ao) self.pd_task_ai.stop() data_json = json.dumps(data) return data_json @setting(19) def reset_devices(self, c): available_devices = list(nidaqmx.system.System.local().devices) if available_devices: for device in available_devices: device.reset_device() else: pass # @setting(20, raw_sequence_channels_list_json ='s') # def write_ao_raw_sequence(self, c, raw_sequence_channels_list_json): # """ # NOT all AO channels are written for now, we select channels by the length of sequence, in principle it should be >2. # """ # raw_sequence_channels_list = json.loads(raw_sequence_channels_list_json) # sequence_bytes = make_sequence_bytes_ao(raw_sequence_channels_list) # num_port = len(sequence_bytes) # num_samp = len(sequence_bytes[0]) # if self.seq_task_ao: # Make sure task is close before you add new stuffs. # self.seq_task_ao.close() # self.seq_task_ao = nidaqmx.Task() # self.seq_task_ao.ao_channels.add_ao_voltage_chan('Dev2/ao0:{}'.format(num_port-1)) # self.seq_task_ao.timing.cfg_samp_clk_timing(rate = self.rate_ao, source = self.clk_channel_ao, samps_per_chan = num_samp) #rate, samps_per_chan # self.seq_task_ao.write(sequence_bytes, auto_start = False, timeout = -1) # @setting(21, raw_sequence_json ='s', channels_list_json = 's') # def write_do_raw_sequence(self, c, raw_sequence_json, channels_list_json): # """ # Write all 32 DO channels at a time. # Default time-out is 120 s, which sets the limit of total sequence length. # Input at this point is json.dump(raw_sequence). # """ # raw_sequence = json.loads(raw_sequence_json) # channels_list = json.loads(channels_list_json) # sequence_bytes = make_sequence_bytes_do(raw_sequence, channels_list) # num_samp = len(sequence_bytes[0]) # if self.seq_task_do: # Make sure task is close before you add new stuffs. # self.seq_task_do.close() # self.seq_task_do = nidaqmx.Task() # self.seq_task_do.do_channels.add_do_chan('Dev1/port0/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) # self.seq_task_do.do_channels.add_do_chan('Dev1/port1/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) # self.seq_task_do.do_channels.add_do_chan('Dev1/port2/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) # self.seq_task_do.do_channels.add_do_chan('Dev1/port3/line0:7', line_grouping=LineGrouping.CHAN_PER_LINE) # self.seq_task_do.timing.cfg_samp_clk_timing(rate = self.rate_do, source = self.clk_channel_do, samps_per_chan = num_samp) #rate, samps_per_chan # self.seq_task_do.write(sequence_bytes, auto_start = False, timeout = -1) # @setting(22, raw_sequence_json = 's') # def write_clk_raw_sequence(self, c, raw_sequence_json): # """ # We use port0/line0 on AI card as the "Variable Clock Reference" to sample the DO and AO card. # Input at this point is json.dump(raw_sequence). # """ # raw_sequence = json.loads(raw_sequence_json) # sequence_bytes = make_sequence_bytes_clk(raw_sequence) # t1 = time.time() # num_samp = len(sequence_bytes) # if self.seq_task_clk: # Make sure task is close before you add new stuffs. # self.seq_task_clk.close() # self.seq_task_clk = nidaqmx.Task() # self.seq_task_clk.do_channels.add_do_chan('Dev0/port0/line0') # self.seq_task_clk.timing.cfg_samp_clk_timing(rate = self.rate_clk, source = self.source, samps_per_chan = num_samp) #rate, samps_per_chan # self.seq_task_clk.write(sequence_bytes, auto_start = False, timeout = -1) # print('DAQ ', time.time() - t1) @setting(20, raw_sequence_channels_list_json='s') def write_ao_raw_sequence(self, c, raw_sequence_channels_list_json): """ NOT all AO channels are written for now, we select channels by the length of sequence, in principle it should be >2. This function works for analog stream out where sequence input are in 2D, number_of_ports rows, float element array, like [[x, x,..., x], ...], where -10.0 <= x <= 10.0. Each row corresponds to a channel in the task. Each column corresponds to a sample to write to each channel. """ raw_sequence_channels_list = json.loads( raw_sequence_channels_list_json) sequence_bytes = make_sequence_bytes_ao(raw_sequence_channels_list) num_port = len(sequence_bytes) num_samp = len(sequence_bytes[0]) if self.seq_task_ao: # Make sure task is close before you add new stuffs. self.seq_task_ao.close() self.seq_task_ao = nidaqmx.Task() self.seq_task_ao.ao_channels.add_ao_voltage_chan( 'Dev2/ao0:{}'.format(num_port - 1)) self.seq_task_ao.timing.cfg_samp_clk_timing( rate=self.rate_ao, source=self.clk_channel_ao, samps_per_chan=num_samp) #rate, samps_per_chan self.writer_ao = AnalogMultiChannelWriter( self.seq_task_ao.out_stream, auto_start=False ) # use multi-channel writer, so input is 2D array. self.writer_ao.write_many_sample(sequence_bytes, timeout=-1) @setting(21, raw_sequence_json='s', channels_list_json='s') def write_do_raw_sequence(self, c, raw_sequence_json, channels_list_json): """ We write all 32 DO channels together at the time where one of the channel output changes. We group 8 lines from each port as a channel, so the input should have 4 rows. This function works for digital stream out where sequence input are in 2D, 32 rows, 8 bit NumPy array, like [[x, x,..., x], ...], where 0 <= x < 2^8. Each row corresponds to a channel in the task. Each column corresponds to a sample to write to each channel. Here 0 stands for all off. 1 stands for the first channel on, with 100..0, and so on. The elements can be calculated using "element += out_list[i]*2**j for j in range(len(channel))". Each element should be in "dtype = numpy.uint8". """ raw_sequence = json.loads(raw_sequence_json) channels_list = json.loads(channels_list_json) sequence_bytes = make_sequence_bytes_do(raw_sequence, channels_list) num_samp = len(sequence_bytes[0]) if self.seq_task_do: # Make sure task is close before you add new stuffs. self.seq_task_do.close() self.seq_task_do = nidaqmx.Task() self.seq_task_do.do_channels.add_do_chan( 'Dev1/port0/line0:7', line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) self.seq_task_do.do_channels.add_do_chan( 'Dev1/port1/line0:7', line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) self.seq_task_do.do_channels.add_do_chan( 'Dev1/port2/line0:7', line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) self.seq_task_do.do_channels.add_do_chan( 'Dev1/port3/line0:7', line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) self.seq_task_do.timing.cfg_samp_clk_timing( rate=self.rate_do, source=self.clk_channel_do, samps_per_chan=num_samp) #rate, samps_per_chan self.writer_do = DigitalMultiChannelWriter( self.seq_task_do.out_stream, auto_start=False ) # use multi-channel writer, so input is 2D array. self.writer_do.write_many_sample_port_byte(sequence_bytes, timeout=-1) @setting(22, raw_sequence_json='s') def write_clk_raw_sequence(self, c, raw_sequence_json): """ We use port0/line0 on AI card as the "Variable Clock Reference" to sample the DO and AO card. Input at this point is json.dump(raw_sequence). This function works for digital stream out where sequence input are in 1D, 8 bit NumPy array, like [1010...]. Each element represents on (1) and off (0), and should be in "dtype = numpy.uint8". """ raw_sequence = json.loads(raw_sequence_json) sequence_bytes = make_sequence_bytes_clk(raw_sequence) # t1 = time.time() num_samp = len(sequence_bytes) if self.seq_task_clk: # Make sure task is close before you add new stuffs. self.seq_task_clk.close() self.seq_task_clk = nidaqmx.Task() self.seq_task_clk.do_channels.add_do_chan( 'Dev0/port0/line0', line_grouping=LineGrouping.CHAN_PER_LINE) self.seq_task_clk.timing.cfg_samp_clk_timing( rate=self.rate_clk, source=self.source, samps_per_chan=num_samp) #rate, samps_per_chan self.writer_clk = DigitalSingleChannelWriter( self.seq_task_clk.out_stream, auto_start=False) self.writer_clk.write_many_sample_port_byte(sequence_bytes, timeout=-1)
class IOTaskNidaq(BaseTask): # This is only nidaq for now. def __init__(self, channels, modes): ''' IOTask for using nidaqmx cards on Windows. ''' super(IOTaskNidaq, self).__init__(channels, modes) self.buff_dtype = np.float64 def _create_tasks(self): if self.n_output_chan: for ichan in self.output_chan_index: dev = self.chaninfo[ichan]['device'] if 'ni:' in dev: dev = dev.strip('ni:') chanstr = dev + '/' + self.chaninfo[ichan]['channel'] if self.task_ao is None: self.task_ao = nidaqmx.Task() if not 'range' in self.chaninfo[ichan].keys(): self.chaninfo[ichan]['range'] = [-10, 10] self.task_ao.ao_channels.add_ao_voltage_chan( chanstr, min_val=self.chaninfo[ichan]['range'][0], max_val=self.chaninfo[ichan]['range'][1]) self.task_ao_modes.append(self.chaninfo[ichan]['modes'][0]) if 'acq_rate' in self.chaninfo[ichan].keys(): self.srate = self.chaninfo[ichan]['acq_rate'] if len(self.digioutput_chan_index): for ichan in self.digioutput_chan_index: dev = self.chaninfo[ichan]['device'] if 'ni:' in dev: dev = dev.strip('ni:') chanstr = dev + '/' + self.chaninfo[ichan]['channel'] if self.task_do is None: self.task_do = nidaqmx.Task() self.task_do.do_channels.add_do_chan( chanstr, line_grouping=nidaqmx.constants.LineGrouping. CHAN_FOR_ALL_LINES) self.task_do_modes.append(self.chaninfo[ichan]['modes'][0]) if self.n_input_chan: for ichan in self.input_chan_index: dev = self.chaninfo[ichan]['device'] if 'ni:' in dev: dev = dev.strip('ni:') chanstr = dev + '/' + self.chaninfo[ichan]['channel'] if self.task_ai is None: self.task_ai = nidaqmx.Task() if not 'range' in self.chaninfo[ichan].keys(): self.chaninfo[ichan]['range'] = [-10, 10] self.task_ai.ai_channels.add_ai_voltage_chan( chanstr, min_val=self.chaninfo[ichan]['range'][0], max_val=self.chaninfo[ichan]['range'][1]) self.task_ai_modes.append(self.chaninfo[ichan]['modes'][0]) if 'acq_rate' in self.chaninfo[ichan].keys(): self.srate = self.chaninfo[ichan]['acq_rate'] # uses the device of the first channel. # this part needs to be better specified to make it more general. dev = self.chaninfo[0]['device'] if 'ni:' in dev: dev = dev.strip('ni:') self.task_clock = nidaqmx.Task() self.task_clock.co_channels.add_co_pulse_chan_freq(dev + '/ctr0', freq=self.srate) self.samp_clk_terminal = '/{0}/Ctr0InternalOutput'.format(dev) # This should probably be on an independent function. if len(self.axon200B_mode): ichan = self.axon200B_mode[0] dev = self.chaninfo[ichan]['device'] if 'ni:' in dev: dev = dev.strip('ni:') chanstr = dev + '/' + self.chaninfo[ichan]['channel'] if not hasattr(self, 'task_axon200B_mode'): self.task_axon200B_mode = nidaqmx.Task() if not 'range' in self.chaninfo[ichan].keys(): self.chaninfo[ichan]['range'] = [-10, 10] self.task_axon200B_mode.ai_channels.add_ai_voltage_chan( chanstr, min_val=self.chaninfo[ichan]['range'][0], max_val=self.chaninfo[ichan]['range'][1]) def load(self, stim, digstim=None, use_ao_trigger=False): # check if the modes are up to date assert type(stim) is list, 'Stim needs to be a list of numpy arrays' self._check_modes() self.nsamples = int( np.max([np.max(s.shape) for s in stim if not s is None])) aoconversion = self._get_conversion(False) if not self.task_ai is None and self.task_ao is None: self.task_ai.timing.cfg_samp_clk_timing( rate=self.srate, samps_per_chan=self.nsamples) if not self.task_ao is None: self.task_ao.timing.cfg_samp_clk_timing( rate=self.srate, samps_per_chan=self.nsamples) self.task_ao.export_signals.export_signal( nidaqmx.constants.Signal.SAMPLE_CLOCK, 'PFI0') if not self.task_ai is None: if use_ao_trigger: self.task_ai.triggers.start_trigger.cfg_dig_edge_start_trig( 'ao/StartTrigger') self.task_ai.timing.cfg_samp_clk_timing( rate=self.srate, source='PFI0', samps_per_chan=self.nsamples) # Take care of the conversions stims = np.zeros((self.n_output_chan, self.nsamples), dtype=np.float64) # Fix the analog output conversions for i in range(self.n_output_chan): if i < len(stim): if stim[i] is None: continue if not len(stim[i]): continue stims[i] = stim[i] * aoconversion[i] if hasattr(self, 'task_clock'): self.task_clock.timing.cfg_implicit_timing( samps_per_chan=self.nsamples) if not self.task_ao is None: self.task_ao.timing.cfg_samp_clk_timing( self.srate, source=self.samp_clk_terminal, active_edge=nidaqmx.constants.Edge.RISING, samps_per_chan=self.nsamples) if not self.task_ai is None: self.task_ai.timing.cfg_samp_clk_timing( self.srate, source=self.samp_clk_terminal, active_edge=nidaqmx.constants.Edge.FALLING, samps_per_chan=self.nsamples) self.writer = AnalogMultiChannelWriter(self.task_ao.out_stream) self.reader = AnalogMultiChannelReader(self.task_ai.in_stream) self.writer.write_many_sample(stims) self.run_do = False if not digstim is None: if not self.task_do is None: self.task_do.timing.cfg_samp_clk_timing( rate=self.srate, source=self.samp_clk_terminal, active_edge=nidaqmx.constants.Edge.RISING, samps_per_chan=self.nsamples) #this works for 32 bit ports only, all iputs must be on the same port for now. digstims = np.zeros((self.nsamples, 32), dtype=np.uint32) for i in range(len(digstim)): if digstim[i] is None: continue if not len(digstim[i]): continue digstims[:, i] = (digstim[i] > 0) self.di_writer = DigitalMultiChannelWriter( self.task_do.out_stream) self.di_writer.write_many_sample_port_uint32( np.packbits(digstims, bitorder='little').reshape( (1, -1)).view(np.uint32)) self.run_do = True self.acquired = False def _run(self, blocking=True): #self.task_ao.ao_channels.all.ao_dac_ref_allow_conn_to_gnd = True if not self.task_ai is None: self.task_ai.start() if self.run_do: self.task_do.start() if not self.task_ao is None: self.task_ao.start() if not self.task_clock is None: self.task_clock.start() #self.task_ao.ao_channels.all.ao_dac_ref_conn_to_gnd = False if blocking: self.reader.read_many_sample( self.data, number_of_samples_per_channel=self.nsamples, timeout=self.nsamples / self.srate + 1) self.data = self.data * aiconversion self.ibuff = self.nsamples self._clean_run() return self.data else: def run_thread(): self.reader.read_all_avail_samp = True buffer = np.zeros((self.n_input_chan, 1000), dtype=np.float64) while not self.ibuff == self.nsamples: nsamples = self.reader.read_many_sample( buffer, number_of_samples_per_channel=1000, timeout=1) self.data[:, self.ibuff:self.ibuff + nsamples] = buffer[:, :nsamples] * aiconversion self.ibuff += int(nsamples) self._clean_run() self.thread_task = threading.Thread(target=run_thread) self.thread_task.start() return None def _clean_run(self): self.task_clock.wait_until_done() self.task_clock.stop() if not self.task_ai is None: self.task_ai.wait_until_done() self.task_ai.stop() if not self.task_ao is None: self.task_ao.wait_until_done() self.task_ao.stop() if self.run_do: self.task_do.wait_until_done() self.task_do.stop() self.acquired = True def _get_axon200B_mode(self): mm = int(np.round(self.task_axon200B_mode.read())) if mm in [2, 1]: self.mode = 'cc' elif mm == 3: self.mode = 'cc=0' elif mm in [4, 6]: self.mode = 'vc' def close(self): self.task_ai.close() self.task_ao.close() self.task_do.close() self.task_clock.close()
min_val=-10) write_task.timing.cfg_samp_clk_timing(sample_rate, source=samp_clk_terminal, active_edge=Edge.RISING, samps_per_chan=number_of_samples) read_task.ai_channels.add_ai_voltage_chan(flatten_channel_string( [c.input_channel for c in channels_to_test]), max_val=10, min_val=-10) read_task.timing.cfg_samp_clk_timing(sample_rate, source=samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=number_of_samples) writer = AnalogMultiChannelWriter(write_task.out_stream) reader = AnalogMultiChannelReader(read_task.in_stream) values_to_test = np.array([waveform for _ in range(number_of_channels)], dtype=np.float64) writer.write_many_sample(values_to_test) # Start the read and write tasks before starting the sample clock # source task. read_task.start() write_task.start() sample_clk_task.start() values_read = np.zeros((number_of_channels, number_of_samples), dtype=np.float64)
def momi(signal, fs, aolist, ailist, rangeval, numsamprec, savedirname=None): """Perform multiple output voltage transmission and multiple input voltage reception. The recorded data is a 2-D array which each row consists of a channel recording. The implementation is referred to test_read_write.py from nidaqmx. :params signal: transmitted signals :params fs: sampling rate :params aolist: list of analog output channel name :params ailist: list of analog input channel name :params rangeval: list contains minimum and maximum amplitude values of the transmitted and received signals :params numsamprec: number of recording samples :params savedirname: directory and filename to save the recording (None means do not save) :params returns: 2-D array with the last column as the reference signal and others as the recorded data """ system = nidaqmx.system.System.local() devicename = system.devices[0].name outputchannel = [devicename + '/' + ao for ao in aolist] inputchannel = [devicename + '/' + ai for ai in ailist] numoutputchannel = len(outputchannel) numinputchannel = len(inputchannel) numsignal, numsamptrans = signal.shape if numsignal != numoutputchannel: raise ValueError('dimension mismatch') if numsamptrans > numsamprec: print( "Number of samples of the transmitted signals is greater than the recordings." ) print("Device name: {}".format(devicename)) print("Output channel: {}".format(outputchannel)) print("Input channel: {}".format(inputchannel)) minval, maxval = rangeval numsampclk = max(numsamptrans, numsamprec) timeout = _np.ceil(numsamprec / fs) with nidaqmx.Task() as write_task, nidaqmx.Task( ) as read_task, nidaqmx.Task() as sample_clk_task: sample_clk_task.co_channels.add_co_pulse_chan_freq( '{0}/ctr0'.format(devicename), freq=fs) sample_clk_task.timing.cfg_implicit_timing(samps_per_chan=numsampclk) samp_clk_terminal = '/{0}/Ctr0InternalOutput'.format(devicename) write_task.ao_channels.add_ao_voltage_chan( flatten_channel_string(outputchannel), max_val=maxval, min_val=minval) write_task.timing.cfg_samp_clk_timing(fs, source=samp_clk_terminal, active_edge=Edge.RISING, samps_per_chan=numsamptrans) read_task.ai_channels.add_ai_voltage_chan( flatten_channel_string(inputchannel), max_val=maxval, min_val=minval, terminal_config=TerminalConfiguration.RSE) read_task.timing.cfg_samp_clk_timing(fs, source=samp_clk_terminal, active_edge=Edge.FALLING, samps_per_chan=numsamprec) writer = AnalogMultiChannelWriter(write_task.out_stream) reader = AnalogMultiChannelReader(read_task.in_stream) writer.write_many_sample(signal) read_task.start() write_task.start() sample_clk_task.start() data = _np.zeros((numinputchannel, numsamprec), dtype=_np.float64) reader.read_many_sample(data, number_of_samples_per_channel=numsamprec, timeout=timeout) if savedirname is not None: if _os.path.isfile('.'.join([savedirname, 'npy'])): print('The file exists. The current data has not been saved.') else: _np.save(savedirname, data) return data