Esempio n. 1
0
    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)
Esempio n. 3
0
    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']
Esempio n. 5
0
    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
Esempio n. 6
0
    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')
Esempio n. 8
0
 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)
Esempio n. 10
0
 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')