def generate_code(self, hdf5_file): group = self.init_device_group(hdf5_file) clockline = self.parent_device pseudoclock = clockline.parent_device times = pseudoclock.times[clockline] # out_table = np.empty((len(times),len(self.child_devices)), dtype=np.float32) # determine dtypes dtypes = [] for device in self.child_devices: if isinstance(device, DigitalOut): device_dtype = np.int8 elif isinstance(device, AnalogOut): device_dtype = np.float64 dtypes.append((device.name, device_dtype)) # create dataset out_table = np.zeros(len(times), dtype=dtype_workaround(dtypes)) for device in self.child_devices: out_table[device.name][:] = device.raw_output group.create_dataset('OUTPUTS', compression=config.compression, data=out_table)
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 transition_to_manual(self, abort=False): self.logger.debug('transition_to_static') # Stop acquisition (this should really be done on a digital edge, but that is for later! Maybe use a Counter) # Set the abort flag so that the acquisition thread knows to expect an exception in the case of an abort: # # TODO: This is probably bad because it shortly get's overwritten to False # However whether it has an effect depends on whether daqmx_read thread holds the daqlock # when self.stop_task() is called self.abort = abort self.stop_task() # Reset the abort flag so that unexpected exceptions are still raised: self.abort = False self.logger.info('transitioning to static, task stopped') # save the data acquired to the h5 file if not abort: with h5py.File(self.h5_file, 'a') as hdf5_file: data_group = hdf5_file['data'] data_group.create_group(self.device_name) dtypes = [(chan.split('/')[-1], numpy.float32) for chan in sorted(self.buffered_channels)] start_time = time.time() if self.buffered_data_list: self.buffered_data = numpy.zeros( len(self.buffered_data_list) * 1000, dtype=dtype_workaround(dtypes)) for i, data in enumerate(self.buffered_data_list): data.shape = (len(self.buffered_channels), self.ai_read.value) for j, (chan, dtype) in enumerate(dtypes): self.buffered_data[chan][i * 1000:(i * 1000) + 1000] = data[j, :] if i % 100 == 0: self.logger.debug( str(i / 100) + " time: " + str(time.time() - start_time)) self.extract_measurements(self.device_name) self.logger.info('data written, time taken: %ss' % str(time.time() - start_time)) self.buffered_data = None self.buffered_data_list = [] # Send data to callback functions as requested (in one big chunk!) #self.result_queue.put([self.t0,self.rate,self.ai_read,len(self.channels),self.ai_data]) # return to previous acquisition mode self.buffered = False self.setup_task() return True
def find_clock_and_r(f, clocks): # Given a frequency f, find clock frequency fc from restricted set clocks # and decimator r from natural numbers to give the smallest sample rate # that exceeds the requested frequency f. divisors, remainders = divmod(clocks, f) opts_dtypes = [('rem', 'i4'), ('div', 'i4'), ('clock', 'i4')] opts = np.array(list(zip(remainders, divisors, clocks)), dtype=dtype_workaround(opts_dtypes)) opts.sort(order=b'rem' if PY2 else 'rem') minrem = opts['rem'][0] # This gets the option with minimum remainder and maximum divisor bestopt = np.sort(opts[opts['rem'] == minrem], order=b'div' if PY2 else 'div')[-1] return bestopt['clock'], bestopt['div']
def transition_to_manual(self, abort=False): self.logger.debug('transition_to_static') self.abort = abort self.stop_task() # Reset the abort flag so that unexpected exceptions are still raised: self.abort = False self.logger.info('transitioning to static, task stopped') # save the data acquired to the h5 file if not abort: if self.waits_in_use: # Let's work out how long the waits were. The absolute times of each edge on the wait # monitor were: edge_times = numpy.cumsum(self.half_periods) # Now there was also a rising edge at t=0 that we didn't measure: edge_times = numpy.insert(edge_times, 0, 0) # Ok, and the even-indexed ones of these were rising edges. rising_edge_times = edge_times[::2] # Now what were the times between rising edges? periods = numpy.diff(rising_edge_times) # How does this compare to how long we expected there to be between the start # of the experiment and the first wait, and then between each pair of waits? # The difference will give us the waits' durations. resume_times = self.wait_table['time'] # Again, include the start of the experiment, t=0: resume_times = numpy.insert(resume_times, 0, 0) run_periods = numpy.diff(resume_times) wait_durations = periods - run_periods waits_timed_out = wait_durations > self.wait_table['timeout'] with h5py.File(self.h5_file, 'a') as hdf5_file: # Work out how long the waits were, save em, post an event saying so dtypes = [('label', 'a256'), ('time', float), ('timeout', float), ('duration', float), ('timed_out', bool)] data = numpy.empty(len(self.wait_table), dtype=dtype_workaround(dtypes)) if self.waits_in_use: data['label'] = self.wait_table['label'] data['time'] = self.wait_table['time'] data['timeout'] = self.wait_table['timeout'] data['duration'] = wait_durations data['timed_out'] = waits_timed_out if self.is_wait_monitor_device: hdf5_file.create_dataset('/data/waits', data=data) if self.is_wait_monitor_device: self.wait_durations_analysed.post(self.h5_file) return True
def write_pb_inst_to_h5(self, pb_inst, hdf5_file): # OK now we squeeze the instructions into a numpy array ready for writing to hdf5: pb_dtype = dtype_workaround([('flags', np.int32), ('inst', np.int32), ('inst_data', np.int32), ('length', np.float64)]) pb_inst_table = np.empty(len(pb_inst), dtype=pb_dtype) for i, inst in enumerate(pb_inst): flagint = int(inst['flags'][::-1], 2) instructionint = self.pb_instructions[inst['instruction']] dataint = inst['data'] delaydouble = inst['delay'] pb_inst_table[i] = (flagint, instructionint, dataint, delaydouble) # Okay now write it to the file: group = hdf5_file['/devices/' + self.name] group.create_dataset('PULSE_PROGRAM', compression=config.compression, data=pb_inst_table) self.set_property('stop_time', self.stop_time, location='device_properties')
def generate_code(self, hdf5_file): Device.generate_code(self, hdf5_file) inputs = {} for device in self.child_devices: if isinstance(device, AnalogIn): inputs[device.connection] = device else: raise Exception('Got unexpected device.') input_connections = sorted(inputs) input_attrs = [] acquisitions = [] for connection in input_connections: input_attrs.append(self.name + '/' + connection) for acq in inputs[connection].acquisitions: acquisitions.append( (connection, acq['label'], acq['start_time'], acq['end_time'], acq['wait_label'], acq['scale_factor'], acq['units'])) acquisitions_table_dtypes = [('connection', 'a256'), ('label', 'a256'), ('start', float), ('stop', float), ('wait label', 'a256'), ('scale factor', float), ('units', 'a256')] acquisition_table = np.empty( len(acquisitions), dtype=dtype_workaround(acquisitions_table_dtypes)) for i, acq in enumerate(acquisitions): acquisition_table[i] = acq grp = self.init_device_group(hdf5_file) if len(acquisition_table): # Table must be non empty grp.create_dataset('ACQUISITIONS', compression=config.compression, data=acquisition_table) self.set_property('analog_in_channels', ', '.join(input_attrs), location='device_properties')
def store_front_panel_in_h5(self, hdf5_file,tab_data,notebook_data,window_data,plugin_data,save_conn_table=False,save_queue_data=True): if save_conn_table: if 'connection table' in hdf5_file: del hdf5_file['connection table'] hdf5_file.create_dataset('connection table', data=self.connection_table.raw_table) data_group = hdf5_file['/'].create_group('front_panel') front_panel_list = [] other_data_list = [] front_panel_dtype = dtype_workaround([('name','a256'),('device_name','a256'),('channel','a256'),('base_value',float),('locked',bool),('base_step_size',float),('current_units','a256')]) max_od_length = 2 # empty dictionary # Iterate over each device within a class for device_name, device_state in tab_data.items(): logger.debug("saving front panel for device:" + device_name) # Insert front panel data into dataset for hardware_name, data in device_state["front_panel"].items(): if data != {}: if isinstance(data['base_value'], (str, bytes)): logger.warning('Could not save data for channel %s on device %s because support for output values that are strings is not yet supported.'%(hardware_name, device_name)) # TODO: Implement saving of Image output type elif float(data['base_value']) == data['base_value']: front_panel_list.append((data['name'], device_name, hardware_name, data['base_value'], data['locked'], data['base_step_size'] if 'base_step_size' in data else 0, data['current_units'] if 'current_units' in data else '' ) ) else: logger.warning('Could not save data for channel %s on device %s because the output value (in base units) was not a string or could not be coerced to a float without loss of precision'%(hardware_name, device_name)) # Save "other data" od = repr(device_state["save_data"]) other_data_list.append(od) max_od_length = len(od) if len(od) > max_od_length else max_od_length # Create datasets if front_panel_list: front_panel_array = numpy.empty(len(front_panel_list),dtype=front_panel_dtype) for i, row in enumerate(front_panel_list): front_panel_array[i] = row data_group.create_dataset('front_panel',data=front_panel_array) # Save tab data i = 0 tab_data = numpy.empty(len(notebook_data),dtype=dtype_workaround([('tab_name','a256'),('notebook','a2'),('page',int),('visible',bool),('data','a'+str(max_od_length))])) for device_name,data in notebook_data.items(): tab_data[i] = (device_name,data["notebook"],data["page"],data["visible"],other_data_list[i]) i += 1 # Save BLACS Main GUI Info dataset = data_group.create_dataset("_notebook_data",data=tab_data) dataset.attrs["window_width"] = window_data["_main_window"]["width"] dataset.attrs["window_height"] = window_data["_main_window"]["height"] dataset.attrs["window_xpos"] = window_data["_main_window"]["xpos"] dataset.attrs["window_ypos"] = window_data["_main_window"]["ypos"] dataset.attrs["window_maximized"] = window_data["_main_window"]["maximized"] dataset.attrs["window_frame_height"] = window_data["_main_window"]["frame_height"] dataset.attrs["window_frame_width"] = window_data["_main_window"]["frame_width"] dataset.attrs['plugin_data'] = repr(plugin_data) dataset.attrs['analysis_data'] = repr(window_data["_main_window"]["_analysis"]) if save_queue_data: dataset.attrs['queue_data'] = repr(window_data["_main_window"]["_queue"]) for pane_name,pane_position in window_data.items(): if pane_name != "_main_window": dataset.attrs[pane_name] = pane_position
def generate_code(self, hdf5_file): from rfblaster import caspr import rfblaster.rfjuice rfjuice_folder = os.path.dirname(rfblaster.rfjuice.__file__) import rfblaster.rfjuice.const as c from rfblaster.rfjuice.cython.make_diff_table import make_diff_table from rfblaster.rfjuice.cython.compile import compileD # from rfblaster.rfjuice.compile import compileD import tempfile from subprocess import Popen, PIPE # Generate clock and save raw instructions to the h5 file: PseudoclockDevice.generate_code(self, hdf5_file) dtypes = dtype_workaround([('time', float), ('amp0', float), ('freq0', float), ('phase0', float), ('amp1', float), ('freq1', float), ('phase1', float)]) times = self.pseudoclock.times[self._clock_line] data = np.zeros(len(times), dtype=dtypes) data['time'] = times for dds in self.direct_outputs.child_devices: prefix, connection = dds.connection.split() data['freq%s' % connection] = dds.frequency.raw_output data['amp%s' % connection] = dds.amplitude.raw_output data['phase%s' % connection] = dds.phase.raw_output group = hdf5_file['devices'].create_group(self.name) group.create_dataset('TABLE_DATA', compression=config.compression, data=data) # Quantise the data and save it to the h5 file: quantised_dtypes = dtype_workaround([('time', np.int64), ('amp0', np.int32), ('freq0', np.int32), ('phase0', np.int32), ('amp1', np.int32), ('freq1', np.int32), ('phase1', np.int32)]) quantised_data = np.zeros(len(times), dtype=quantised_dtypes) quantised_data['time'] = np.array(c.tT * 1e6 * data['time'] + 0.5) for dds in range(2): # TODO: bounds checking # Adding 0.5 to each so that casting to integer rounds: quantised_data['freq%d' % dds] = np.array(c.fF * 1e-6 * data['freq%d' % dds] + 0.5) quantised_data['amp%d' % dds] = np.array((2**c.bitsA - 1) * data['amp%d' % dds] + 0.5) quantised_data['phase%d' % dds] = np.array(c.pP * data['phase%d' % dds] + 0.5) group.create_dataset('QUANTISED_DATA', compression=config.compression, data=quantised_data) # Generate some assembly code and compile it to machine code: assembly_group = group.create_group('ASSEMBLY_CODE') binary_group = group.create_group('BINARY_CODE') diff_group = group.create_group('DIFF_TABLES') # When should the RFBlaster wait for a trigger? quantised_trigger_times = np.array( [c.tT * 1e6 * t + 0.5 for t in self.trigger_times], dtype=np.int64) for dds in range(2): abs_table = np.zeros((len(times), 4), dtype=np.int64) abs_table[:, 0] = quantised_data['time'] abs_table[:, 1] = quantised_data['amp%d' % dds] abs_table[:, 2] = quantised_data['freq%d' % dds] abs_table[:, 3] = quantised_data['phase%d' % dds] # split up the table into chunks delimited by trigger times: abs_tables = [] for i, t in enumerate(quantised_trigger_times): subtable = abs_table[abs_table[:, 0] >= t] try: next_trigger_time = quantised_trigger_times[i + 1] except IndexError: # No next trigger time pass else: subtable = subtable[subtable[:, 0] < next_trigger_time] subtable[:, 0] -= t abs_tables.append(subtable) # convert to diff tables: diff_tables = [make_diff_table(tab) for tab in abs_tables] # Create temporary files, get their paths, and close them: with tempfile.NamedTemporaryFile(delete=False) as f: temp_assembly_filepath = f.name with tempfile.NamedTemporaryFile(delete=False) as f: temp_binary_filepath = f.name try: # Compile to assembly: with open(temp_assembly_filepath, 'w') as assembly_file: for i, dtab in enumerate(diff_tables): compileD(dtab, assembly_file, init=(i == 0), jump_to_start=(i == 0), jump_from_end=False, close_end=(i == len(diff_tables) - 1), local_loop_pre=bytes(i) if PY2 else str(i), set_defaults=(i == 0)) # Save the assembly to the h5 file: with open(temp_assembly_filepath, ) as assembly_file: assembly_code = assembly_file.read() assembly_group.create_dataset('DDS%d' % dds, data=assembly_code) for i, diff_table in enumerate(diff_tables): diff_group.create_dataset( 'DDS%d_difftable%d' % (dds, i), compression=config.compression, data=diff_table) # compile to binary: compilation = Popen( [caspr, temp_assembly_filepath, temp_binary_filepath], stdout=PIPE, stderr=PIPE, cwd=rfjuice_folder, startupinfo=startupinfo) stdout, stderr = compilation.communicate() if compilation.returncode: print(stdout) raise LabscriptError( 'RFBlaster compilation exited with code %d\n\n' % compilation.returncode + 'Stdout was:\n %s\n' % stdout + 'Stderr was:\n%s\n' % stderr) # Save the binary to the h5 file: with open(temp_binary_filepath, 'rb') as binary_file: binary_data = binary_file.read() # has to be numpy.string_ (string_ in this namespace, # imported from pylab) as python strings get stored # as h5py as 'variable length' strings, which 'cannot # contain embedded nulls'. Presumably our binary data # must contain nulls sometimes. So this crashes if we # don't convert to a numpy 'fixes length' string: binary_group.create_dataset('DDS%d' % dds, data=np.string_(binary_data)) finally: # Delete the temporary files: os.remove(temp_assembly_filepath) os.remove(temp_binary_filepath)
def generate_code(self, hdf5_file): IntermediateDevice.generate_code(self, hdf5_file) analogs = {} digitals = {} inputs = {} for device in self.child_devices: if isinstance(device,AnalogOut): analogs[device.connection] = device elif isinstance(device,DigitalOut): digitals[device.connection] = device elif isinstance(device,AnalogIn): inputs[device.connection] = device else: raise Exception('Got unexpected device.') clockline = self.parent_device pseudoclock = clockline.parent_device times = pseudoclock.times[clockline] analog_out_table = np.empty((len(times),len(analogs)), dtype=np.float32) analog_connections = list(analogs.keys()) analog_connections.sort() analog_out_attrs = [] for i, connection in enumerate(analog_connections): output = analogs[connection] if any(output.raw_output > 10 ) or any(output.raw_output < -10 ): # Bounds checking: raise LabscriptError('%s %s '%(output.description, output.name) + 'can only have values between -10 and 10 Volts, ' + 'the limit imposed by %s.'%self.name) analog_out_table[:,i] = output.raw_output analog_out_attrs.append(self.MAX_name +'/'+connection) input_connections = list(inputs.keys()) input_connections.sort() input_attrs = [] acquisitions = [] for connection in input_connections: input_attrs.append(self.MAX_name+'/'+connection) for acq in inputs[connection].acquisitions: acquisitions.append((connection,acq['label'],acq['start_time'],acq['end_time'],acq['wait_label'],acq['scale_factor'],acq['units'])) # The 'a256' dtype below limits the string fields to 256 # characters. Can't imagine this would be an issue, but to not # specify the string length (using dtype=str) causes the strings # to all come out empty. acquisitions_table_dtypes = dtype_workaround([('connection','a256'), ('label','a256'), ('start',float), ('stop',float), ('wait label','a256'),('scale factor',float), ('units','a256')]) acquisition_table= np.empty(len(acquisitions), dtype=acquisitions_table_dtypes) for i, acq in enumerate(acquisitions): acquisition_table[i] = acq digital_out_table = [] if digitals: digital_out_table = self.convert_bools_to_bytes(list(digitals.values())) grp = self.init_device_group(hdf5_file) if all(analog_out_table.shape): # Both dimensions must be nonzero grp.create_dataset('ANALOG_OUTS',compression=config.compression,data=analog_out_table) self.set_property('analog_out_channels', ', '.join(analog_out_attrs), location='device_properties') if len(digital_out_table): # Table must be non empty grp.create_dataset('DIGITAL_OUTS',compression=config.compression,data=digital_out_table) self.set_property('digital_lines', '/'.join((self.MAX_name,'port0','line0:%d'%(self.n_digitals-1))), location='device_properties') if len(acquisition_table): # Table must be non empty grp.create_dataset('ACQUISITIONS',compression=config.compression,data=acquisition_table) self.set_property('analog_in_channels', ', '.join(input_attrs), location='device_properties') # TODO: move this to decorator (requires ability to set positional args with @set_passed_properties) self.set_property('clock_terminal', self.clock_terminal, location='connection_table_properties')