def extract_measurements(self, raw_data, waits_in_use): self.logger.debug('extract_measurements') if waits_in_use: # There were waits in this shot. We need to wait until the other process has # determined their durations before we proceed: self.wait_durations_analysed.wait(self.h5_file) with h5py.File(self.h5_file, 'a') as hdf5_file: if waits_in_use: # get the wait start times and durations waits = hdf5_file['/data/waits'] wait_times = waits['time'] wait_durations = waits['duration'] try: acquisitions = hdf5_file['/devices/' + self.device_name + '/AI'] except KeyError: # No acquisitions! return try: measurements = hdf5_file['/data/traces'] except KeyError: # Group doesn't exist yet, create it: measurements = hdf5_file.create_group('/data/traces') t0 = self.AI_start_delay for connection, label, t_start, t_end, _, _, _ in acquisitions: connection = _ensure_str(connection) label = _ensure_str(label) if waits_in_use: # add durations from all waits that start prior to t_start of # acquisition t_start += wait_durations[(wait_times < t_start)].sum() # compare wait times to t_end to allow for waits during an # acquisition t_end += wait_durations[(wait_times < t_end)].sum() i_start = int(np.ceil(self.buffered_rate * (t_start - t0))) i_end = int(np.floor(self.buffered_rate * (t_end - t0))) # np.ceil does what we want above, but float errors can miss the # equality: if t0 + (i_start - 1) / self.buffered_rate - t_start > -2e-16: i_start -= 1 # We want np.floor(x) to yield the largest integer < x (not <=): if t_end - t0 - i_end / self.buffered_rate < 2e-16: i_end -= 1 # IBS: we sometimes find that t_end (with waits) gives a time # after the end of acquisition. The following line # will produce return a shorter than expected array if i_end # is larger than the length of the array. values = raw_data[connection][i_start:i_end + 1] i_end = i_start + len(values) - 1 # re-measure i_end t_i = t0 + i_start / self.buffered_rate t_f = t0 + i_end / self.buffered_rate times = np.linspace(t_i, t_f, len(values), endpoint=True) dtypes = [('t', np.float64), ('values', np.float32)] data = np.empty(len(values), dtype=dtypes) data['t'] = times data['values'] = values measurements.create_dataset(label, data=data)
def extract_measurements(self, device_name): self.logger.debug('extract_measurements') with h5py.File(self.h5_file,'a') as hdf5_file: waits_in_use = len(hdf5_file['waits']) > 0 if waits_in_use: # There were waits in this shot. We need to wait until the other process has # determined their durations before we proceed: self.wait_durations_analysed.wait(self.h5_file) with h5py.File(self.h5_file,'a') as hdf5_file: if waits_in_use: # get the wait start times and durations waits = hdf5_file['/data/waits'] wait_times = waits['time'] wait_durations = waits['duration'] try: acquisitions = hdf5_file['/devices/'+device_name+'/ACQUISITIONS'] except: # No acquisitions! return try: measurements = hdf5_file['/data/traces'] except: # Group doesn't exist yet, create it: measurements = hdf5_file.create_group('/data/traces') for connection,label,start_time,end_time,wait_label,scale_factor,units in acquisitions: connection = _ensure_str(connection) label = _ensure_str(label) wait_label = _ensure_str(wait_label) if waits_in_use: # add durations from all waits that start prior to start_time of acquisition start_time += wait_durations[(wait_times < start_time)].sum() # compare wait_times to end_time to allow for waits during an acquisition end_time += wait_durations[(wait_times < end_time)].sum() start_index = int(numpy.ceil(self.buffered_rate*(start_time-self.ai_start_delay))) end_index = int(numpy.floor(self.buffered_rate*(end_time-self.ai_start_delay))) # numpy.ceil does what we want above, but float errors can miss the equality if self.ai_start_delay + (start_index-1)/self.buffered_rate - start_time > -2e-16: start_index -= 1 # We actually want numpy.floor(x) to yield the largest integer < x (not <=) if end_time - self.ai_start_delay - end_index/self.buffered_rate < 2e-16: end_index -= 1 acquisition_start_time = self.ai_start_delay + start_index/self.buffered_rate acquisition_end_time = self.ai_start_delay + end_index/self.buffered_rate times = numpy.linspace(acquisition_start_time, acquisition_end_time, end_index-start_index+1, endpoint=True) values = self.buffered_data[connection][start_index:end_index+1] dtypes = [('t', numpy.float64),('values', numpy.float32)] data = numpy.empty(len(values),dtype=dtype_workaround(dtypes)) data['t'] = times data['values'] = values measurements.create_dataset(label, data=data)
def wait_monitor(self): try: # Read edge times from the counter input task, indiciating the times of the # pulses that occur at the start of the experiment and after every wait. If a # timeout occurs, pulse the timeout output to force a resume of the master # pseudoclock. Save the resulting self.logger.debug('Wait monitor thread starting') with self.kill_lock: self.logger.debug('Waiting for start of experiment') # Wait for the pulse indicating the start of the experiment: if self.incomplete_sample_detection: semiperiods = self.read_edges(1, timeout=None) else: semiperiods = self.read_edges(2, timeout=None) self.logger.debug('Experiment started, got edges:' + str(semiperiods)) # May have been one or two edges, depending on whether the device has # incomplete sample detection. We are only interested in the second one # anyway, it tells us how long the initial pulse was. Store the pulse width # for later, we will use it for making timeout pulses if necessary. Note # that the variable current_time is labscript time, so it will be reset # after each wait to the time of that wait plus pulse_width. current_time = pulse_width = semiperiods[-1] self.semiperiods.append(semiperiods[-1]) # Alright, we're now a short way into the experiment. for wait in self.wait_table: # How long until when the next wait should timeout? timeout = wait['time'] + wait['timeout'] - current_time timeout = max(timeout, 0) # ensure non-negative # Wait that long for the next pulse: self.logger.debug( 'Waiting for pulse indicating end of wait') semiperiods = self.read_edges(2, timeout) # Did the wait finish of its own accord, or time out? if semiperiods is None: # It timed out. Better trigger the clock to resume! msg = """Wait timed out; retriggering clock with {:.3e} s pulse ({} edge)""" msg = dedent(msg).format(pulse_width, self.timeout_trigger_type) self.logger.debug(msg) self.send_resume_trigger(pulse_width) # Wait for it to respond to that: self.logger.debug( 'Waiting for pulse indicating end of wait') semiperiods = self.read_edges(2, timeout=None) # Alright, now we're at the end of the wait. self.semiperiods.extend(semiperiods) self.logger.debug('Wait completed') current_time = wait['time'] + semiperiods[-1] # Inform any interested parties that a wait has completed: postdata = _ensure_str(wait['label']) self.wait_completed.post(self.h5_file, data=postdata) # Inform any interested parties that waits have all finished: self.logger.debug('All waits finished') self.all_waits_finished.post(self.h5_file) except Exception: self.logger.exception('Exception in wait monitor thread:') # Save the exception so it can be raised in transition_to_manual self.wait_monitor_thread_exception = sys.exc_info()
def transition_to_buffered(self, device_name, h5file, initial_values, fresh): self.logger.debug('transition_to_buffered') # read channels, acquisition rate, etc from H5 file with h5py.File(h5file, 'r') as f: group = f['/devices/' + device_name] if 'AI' not in group: # No acquisition return {} AI_table = group['AI'][:] device_properties = properties.get(f, device_name, 'device_properties') chans = [_ensure_str(c) for c in AI_table['connection']] # Remove duplicates and sort: if chans: self.buffered_chans = sorted(set(chans), key=split_conn_AI) self.h5_file = h5file self.buffered_rate = device_properties['acquisition_rate'] self.acquired_data = [] # Stop the manual mode task and start the buffered mode task: self.stop_task() self.buffered_mode = True self.start_task(self.buffered_chans, self.buffered_rate) return {}
def check_status(self): """Checks the operational status of the PrawnBlaster. This is automatically called by BLACS to update the status of the PrawnBlaster. It also reads the lengths of any accumulated waits during a shot. Returns: (int, int, bool): Tuple containing: - **run_status** (int): Possible values are: * 0 : manual-mode * 1 : transitioning to buffered execution * 2 : buffered execution * 3 : abort requested * 4 : currently aborting buffered execution * 5 : last buffered execution aborted * 6 : transitioning to manual mode - **clock_status** (int): Possible values are: * 0 : internal clock * 1 : external clock - **waits_pending** (bool): Indicates if all expected waits have not been read out yet. """ if (self.started and self.wait_table is not None and self.current_wait < len(self.wait_table)): # Try to read out wait. For now, we're only reading out waits from # pseudoclock 0 since they should all be the same (requirement imposed by labscript) self.prawnblaster.write(b"getwait %d %d\r\n" % (0, self.current_wait)) response = self.prawnblaster.readline().decode() if response != "wait not yet available\r\n": # Parse the response from the PrawnBlaster wait_remaining = int(response) # Divide by two since the clock_resolution is for clock pulses, which # have twice the clock_resolution of waits # Technically, waits also only have a resolution of `clock_resolution` # but the PrawnBlaster firmware accepts them in half of that so that # they are easily converted to seconds via the clock frequency. # Maybe this was a mistake, but it's done now. clock_resolution = self.device_properties[ "clock_resolution"] / 2 input_response_time = self.device_properties[ "input_response_time"] timeout_length = round( self.wait_table[self.current_wait]["timeout"] / clock_resolution) if wait_remaining == (2**32 - 1): # The wait hit the timeout - save the timeout duration as wait length # and flag that this wait timedout self.measured_waits[self.current_wait] = (timeout_length * clock_resolution) self.wait_timeout[self.current_wait] = True else: # Calculate wait length # This is a measurement of between the end of the last pulse and the # retrigger signal. We obtain this by subtracting off the time it takes # to detect the pulse in the ASM code once the trigger has hit the input # pin (stored in input_response_time) self.measured_waits[self.current_wait] = ( (timeout_length - wait_remaining) * clock_resolution) - input_response_time self.wait_timeout[self.current_wait] = False self.logger.info( f"Wait {self.current_wait} finished. Length={self.measured_waits[self.current_wait]:.9f}s. Timed-out={self.wait_timeout[self.current_wait]}" ) # Inform any interested parties that a wait has completed: self.wait_completed.post( self.h5_file, data=_ensure_str( self.wait_table[self.current_wait]["label"]), ) # increment the wait we are looking for! self.current_wait += 1 # post message if all waits are done if len(self.wait_table) == self.current_wait: self.logger.info("All waits finished") self.all_waits_finished.post(self.h5_file) # Determine if we are still waiting for wait information waits_pending = False if self.wait_table is not None: if self.current_wait == len(self.wait_table): waits_pending = False else: waits_pending = True run_status, clock_status = self.read_status() return run_status, clock_status, waits_pending