Ejemplo n.º 1
0
class SputterMonitor(object):
    """
    Sputter monitoring class, derived from spectroscopy.py
    monitors and stores sputter parameters live during deposition
    facilitates dynamic flow adjustment

    Example:
        m = SPUTTER_Monitor(quartz, ohmmeter, film_name, sample)
        m.set_filmparameters() ... see in corresponding docstring
        m.monitor_depo()

    Args:
        quartz (Instrument): The quartz oscillator instrument measuring thickness and rate.
        ohmmeter (Instrument): The resistance measurement instrument.
        mfc (Instrument): The mass flow controller instrument.
        film_name (str): A name can be given, that is added to the directory and filename.
        sample (Sample): A sample object can be given that is stored in the h5 file.

    Attributes:
        Should be listed here... Until then, use tabbing.
    """
    def __init__(self,
                 quartz=None,
                 ohmmeter=None,
                 mfc=None,
                 film_name='',
                 sample=None):
        self.quartz = quartz
        self.ohmmeter = ohmmeter
        self.mfc = mfc

        self.film_name = film_name
        self._sample = sample

        self.comment = ''
        self.dirname = None

        self.x_set_obj = None
        self.y_set_obj = None

        self.progress_bar = False

        self._show_ideal = False

        self._fit_resistance = False
        self._fit_every = 1
        self._fit_points = 5
        self._p0 = None

        self._reference_uid = None

        self._plot_comment = ""

        self.open_qviewkit = True
        self.qviewkit_singleInstance = False

        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'sputter_deposition'
        self._measurement_object.sample = self._sample

        self._qvk_process = True

        self._duration = 60.  # Duration of monitoring
        self._resolution = 2.  # How often values should be taken

        self._target_resistance = 1000.
        self._target_thickness = 20.
        self._target_marker = False

        self._depmon_queue = Queue(
        )  # for communication with the depmon thread

    def set_duration(self, duration=60.):
        """
        Set the duration for the monitoring. Better too long than too short.

        Args:
            duration (float): Monitoring duration in seconds.
        """
        self._duration = duration

    def get_duration(self):
        """
        Get the duration for the monitoring.

        Returns:
            duration (float): Monitoring duration in seconds.
        """
        return self._duration

    def set_resolution(self, resolution=2.0):
        """
        Set the resolution for the monitoring.
        Every x second, the parameters will be read and stored.

        Args:
            resolution (float): Monitoring resolution in seconds.
        """
        self._resolution = resolution

    def get_resolution(self):
        """
        Get the resolution for the monitoring.
        Every x second, the parameters will be read and stored.

        Returns:
            resolution (float): Monitoring resolution in seconds.
        """
        return self._resolution

    def set_filmparameters(self,
                           resistance=1000.,
                           thickness=20.,
                           target_marker=False):
        """
        Set the target parameters of the film to be deposited.
        They are used to calculate the ideal trend of the measured parameters and their deviation from the ideal.

        Args:
            resistance (float): The target sheet resistance in Ohm.
            thickness (float): The target thickness in nm.
            target_marker (bool): Display a cross marking the target resistance and thickness.
        """
        self._target_resistance = resistance
        self._target_thickness = thickness
        self._target_marker = target_marker

    def get_filmparameters(self):
        """
        Get the set film parameters.

        Returns:
            Tuple containing:
                [0]: target_resistance (float): The target sheet resistance in Ohm.
                [1]: target_thickness (float): The target thickness in nm.
        """
        return self._target_resistance, self._target_thickness

    def ideal_resistance(self, thickness):
        """
        Calculates the ideal resistance at a given thickness depending on the set film parameters.

        Args:
            thickness (float): Film thickness in nm for which the ideal resistance should be calculated.

        Returns:
            The ideal resistance as a float.
        """
        try:
            return 1. / (thickness * (1. / self._target_resistance) /
                         self._target_thickness)
        except:
            return np.nan

    def ideal_trend(self):
        """
        Calculates a list of ideal resistances that is used as comparison in a view together with the real values.
        """
        t = np.linspace(1, self._target_thickness, 300)
        return [t, self.ideal_resistance(t)]

    def set_fit(self, fit=False, fit_every=1, fit_points=5, p0=None):
        """
        Settings for the fit used to estimate final resistance at target thickness and thickness at target resistance.

        Args:
            fit (bool): Turn on the fit.
            fit_every (int): Fit after every x measurement.
            fit_points (int): Fit to the last x points.
            p0 (list): Provide initial values for the curvefit.
        """
        self._fit_resistance = fit
        self._fit_every = fit_every
        self._fit_points = fit_points
        self._p0 = p0

    def set_reference_uid(self, uid=None):
        """
        Set a UID to be shown as a reference trace in the "resistance_thickness" view.

        Args:
            uid (str): The UID of the h5 file from which the resistance and thickness data is to be displayed.
        """
        try:
            ref = hdf.Data(qkit.fid[uid])
            self._ref_resistance = ref.data.resistance[:]
            self._ref_thickness = ref.data.thickness[:]
            self._reference_uid = uid
            ref.close()
            return self._reference_uid
        except:
            self._reference_uid = None
            return "Invalid UID"

    def set_resistance_4W(self, four_wire=False):
        """
        Sets 2 (default) or 4 wire measurement mode, if the device supports it.

        Args:
            four_wire = False
        """
        self.ohmmeter.set_measure_4W(four_wire)

    def get_rate(self, test_thickness):
        """
        Tests deposition rate.

        Args:
            test_thickness (in nm)
        """
        self.quartz.set_timer_zero()
        self.quartz.set_thickness_zero()
        time.sleep(3)
        while self.quartz.get_thickness() < test_thickness * 1e-2:
            time.sleep(1)
        mins, secs = self.quartz.get_time().split(':')
        t = 60 * float(mins) + float(secs)
        rate = test_thickness / t
        return rate, t

    def _theory(self, thickness, thickness_start, conductivity,
                cond_per_layer):
        """
        Calculate a prediction for the resistance at a certain film thickness,
        given the current thickness, current conductivity and added conductivity per layer.

        Args:
            thickness (float): Film thickness for which the resistance shall be predicted
            thickness_start (float): Film thickness at the start of the prediction.
            conductivity (float): Film conductivity at the start of the prediction.
            cond_per_layer (float): Added conductivity per layer for the remaining film growth.

        Returns:
            The projected film resistance (float).
        """
        t_added = thickness - thickness_start
        try:
            return 1. / (conductivity + t_added * cond_per_layer)
        except:
            return np.nan

    def _linear(self, x, a, b):
        """
        Helper function providing a linear fitting function.
        """
        return a * x + b

    def _fit_layer(self, t_points, R_points):
        """
        Fit the changing film resistance to extract the gained conductivity per layer.
        This is then used to project the final film resistance with the provided target film parameters.

        Args:
            t_points (numpy array): Thickness data from which the conductivity per layer is to be extracted.
            R_points (numpy array): Resistance data from which the conductivity per layer is to be extracted.

        Returns:
            Tuple containing:
                [0]: t_final (float): The predicted thickness at which the target resistance will be reached.
                [1]: R_final (float): The predicted resistance that will be reached at the target thickness.
                [2]: popt[0] (float): Conductivity per layer as obtained by linear fit.
        """
        # ToDo: Store all fits, Store time of fit to sync it to other datasets, revise theory.
        for i in R_points:
            if i == 0:
                return [np.nan, np.nan, np.nan]

        try:
            c_target = 1. / self._target_resistance
            c_points = 1. / R_points
            popt, _ = curve_fit(self._linear, t_points, c_points, p0=self._p0)
            R_final = self._theory(self._target_thickness, t_points[0],
                                   c_points[0], popt[0])
            t_final = ((c_target - c_points[-1]) / popt[0]) + t_points[-1]
            return [t_final, R_final, popt[0]]

        except:
            return [np.nan, np.nan, np.nan]

    def _reciprocal(self, thickness, cond_per_layer):
        """
        Function used by the curvefit routine.

        Args:
            thickness: Film thickness in nm.
            cond_per_layer: Film conductance per layer.

        Returns:
            The resistance for the given parameters as a float.
        """
        try:
            return 1. / (thickness * cond_per_layer)
        except:
            return np.nan

    def _fit_trend(self, t_points, R_points):
        """
        Do the fit used to estimate final resistance at target thickness and thickness at target resistance.

        Args:
            t_points: The last x points of film thickness in nm.
            R_points: The last x points of film resistivity in Ohm.

        Returns:
            List of [R_final, t_final] with
            R_final (float): Estimated final resistance at target thickness.
            t_final (float): Estimated thickness at target resistance.
        """
        for i in t_points:
            if i == 0:
                return [np.nan, np.nan]

        try:
            self._popt, _ = curve_fit(self._reciprocal,
                                      t_points,
                                      R_points,
                                      p0=self._p0)

            t_final = 1. / (self._target_resistance * self._popt[0])
            R_final = 1. / (self._target_thickness * self._popt[0])

            return [t_final, R_final]

        except:
            return [np.nan, np.nan]

    def _prepare_measurement_quartz(self):
        """
        All the relevant settings from the quartz are updated and called.
        """
        # self.quartz.get_all()
        # self.quartz.get_frequency() # Store it somewhere
        pass

    def _prepare_measurement_ohmmeter(self):
        """
        All the relevant settings from the ohmmeter are updated and called.
        """
        # self.ohmmeter.get_all()
        pass

    def _prepare_measurement_mfc(self):
        """
        All the relevant settings from the ohmmeter are updated and called.
        """
        # self.mfc.get_all()
        self.Ar_channel = self.mfc.predef_channels['Ar']
        self.ArO_channel = self.mfc.predef_channels['ArO']

        pass

    def _prepare_monitoring_file(self):
        """
        Creates the output .h5-file with distinct the required datasets and views.
        At this point all measurement parameters are known and put in the output file.
        """
        self._data_file = hdf.Data(name=self._file_name, mode='a')
        self._measurement_object.uuid = self._data_file._uuid
        self._measurement_object.hdf_relpath = self._data_file._relpath
        self._measurement_object.instruments = qkit.instruments.get_instrument_names(
        )

        self._measurement_object.save()
        self._mo = self._data_file.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        # write logfile and instrument settings
        self._write_settings_dataset()
        self._log = waf.open_log_file(self._data_file.get_filepath())
        '''
        Time record
        '''
        self._data_time = self._data_file.add_value_vector('time',
                                                           x=None,
                                                           unit='s')
        '''
        Quartz datasets
        '''
        self._data_rate = self._data_file.add_value_vector(
            'rate', x=self._data_time, unit='nm/s', save_timestamp=False)
        self._data_thickness = self._data_file.add_value_vector(
            'thickness', x=self._data_time, unit='nm', save_timestamp=False)
        '''
        Ohmmeter datasets
        '''
        self._data_resistance = self._data_file.add_value_vector(
            'resistance', x=self._data_time, unit='Ohm', save_timestamp=False)
        self._data_conductance = self._data_file.add_value_vector(
            'conductance', x=self._data_time, unit='S', save_timestamp=False)
        self._data_deviation_abs = self._data_file.add_value_vector(
            'deviation_absolute',
            x=self._data_time,
            unit='Ohm',
            save_timestamp=False)
        self._data_deviation_rel = self._data_file.add_value_vector(
            'deviation_relative',
            x=self._data_time,
            unit='relative',
            save_timestamp=False)
        '''
        MFC datasets
        '''
        # FIXME: units?
        if self.mfc:
            self._data_pressure = self._data_file.add_value_vector(
                'pressure',
                x=self._data_time,
                unit='ubar',
                save_timestamp=False)
            self._data_Ar_flow = self._data_file.add_value_vector(
                'Ar_flow',
                x=self._data_time,
                unit='sccm',
                save_timestamp=False)
            self._data_ArO_flow = self._data_file.add_value_vector(
                'ArO_flow',
                x=self._data_time,
                unit='sccm',
                save_timestamp=False)
        '''
        Calculate ideal trend and create record
        '''
        self._thickness_coord = self._data_file.add_coordinate(
            'thickness_coord', unit='nm')
        self._thickness_coord.add(self.ideal_trend()[0])
        if self._show_ideal:
            self._data_ideal = self._data_file.add_value_vector(
                'ideal_resistance',
                x=self._thickness_coord,
                unit='Ohm',
                save_timestamp=False)
            self._data_ideal.append(self.ideal_trend()[1])
        '''
        Create target marker
        '''
        if self._target_marker:
            self._target_thickness_line = self._data_file.add_value_vector(
                'target_thickness', x=None, unit='nm', save_timestamp=False)
            self._target_thickness_line.append([
                0.8 * self._target_thickness, self._target_thickness,
                self._target_thickness, self._target_thickness,
                self._target_thickness, 1.2 * self._target_thickness
            ])
            self._target_resistance_line = self._data_file.add_value_vector(
                'target_resistance', x=None, unit='Ohm', save_timestamp=False)
            self._target_resistance_line.append([
                self._target_resistance, self._target_resistance,
                1.2 * self._target_resistance, 0.8 * self._target_resistance,
                self._target_resistance, self._target_resistance
            ])
            self._target_conductance_line = self._data_file.add_value_vector(
                'target_conductance', x=None, unit='S', save_timestamp=False)
            self._target_conductance_line.append([
                1. / self._target_resistance, 1. / self._target_resistance,
                1. / 1.2 / self._target_resistance,
                1. / 0.8 / self._target_resistance,
                1. / self._target_resistance, 1. / self._target_resistance
            ])
        '''
        Estimation datasets
        '''
        if self._fit_resistance:
            self._thickness_estimation = self._data_file.add_value_vector(
                'thickness_estimation',
                x=None,
                unit='nm',
                save_timestamp=False)
            self._resistance_estimation = self._data_file.add_value_vector(
                'resistance_estimation',
                x=None,
                unit='Ohm',
                save_timestamp=False)
            self._last_resistance_fit = self._data_file.add_value_vector(
                'last_resistance_fit',
                x=None,
                unit='Ohm',
                save_timestamp=False)
        '''
        Reference dataset
        '''
        if not self._reference_uid == None:
            self._thickness_reference = self._data_file.add_value_vector(
                'thickness_reference', x=None, unit='nm', save_timestamp=False)
            self._thickness_reference.append(self._ref_thickness)
            self._resistance_reference = self._data_file.add_value_vector(
                'reference_' + self._reference_uid,
                x=None,
                unit='Ohm',
                save_timestamp=False)
            self._resistance_reference.append(self._ref_resistance)
        '''
        Create Views
        '''
        self._resist_view = self._data_file.add_view('resistance_thickness',
                                                     x=self._data_thickness,
                                                     y=self._data_resistance)
        if self._show_ideal:
            self._resist_view.add(x=self._thickness_coord, y=self._data_ideal)
        if self._target_marker:
            self._resist_view.add(x=self._target_thickness_line,
                                  y=self._target_resistance_line)
        if not self._reference_uid == None:
            self._resist_view.add(x=self._thickness_reference,
                                  y=self._resistance_reference)
        if self._fit_resistance:
            self._resist_view.add(x=self._thickness_coord,
                                  y=self._last_resistance_fit)

        self._conductance_view = self._data_file.add_view(
            'conductance_thickness',
            x=self._data_thickness,
            y=self._data_conductance)
        if self._target_marker:
            self._conductance_view.add(x=self._target_thickness_line,
                                       y=self._target_conductance_line)

        self._deviation_abs_view = self._data_file.add_view(
            'deviation_absolute',
            x=self._data_thickness,
            y=self._data_deviation_abs)

        self._deviation_rel_view = self._data_file.add_view(
            'deviation_relative',
            x=self._data_thickness,
            y=self._data_deviation_rel)
        '''
        Create comment
        '''
        if self.comment:
            self._data_file.add_comment(self.comment)
        '''
        Open GUI
        '''
        if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
            self._qvk_process.terminate()  # terminate an old qviewkit instance

    def _write_settings_dataset(self):
        """
        Writes a dataset containing the settings of the measurement instruments.
        """
        self._settings = self._data_file.add_textlist('settings')
        settings = waf.get_instrument_settings(self._data_file.get_filepath())
        self._settings.append(settings)

    def _init_depmon(self):
        """
        Initializes the measurement, the required files and instruments.
        Should only be called by the main functions monitor_depo or start_depmon.
        """
        self._measurement_object.measurement_func = 'sputter_monitoring'
        self._measurement_object.x_axis = 'time'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = True

        if not self.dirname:
            self.dirname = 'SPUTTER_monitoring'
        self._file_name = self.dirname.replace(' ', '').replace(',', '_')
        if self.film_name:
            self._file_name += '_' + self.film_name

        self.x_vec = np.arange(0, self._duration, self._resolution)

        self._prepare_measurement_quartz()
        self._prepare_measurement_ohmmeter()
        if self.mfc:
            self._prepare_measurement_mfc()

        self._prepare_monitoring_file()

    def _acquire(self, mon_data):
        """
        Acquires and stores current information from the instruments.
        Should only be called by main functions monitor_depo or start_depmon.
        """
        self._data_time.append(time.time() - mon_data.t0)

        # mon_data.resistance.append(self.ohmmeter.get_resistance())
        mon_data.resistance = np.append(mon_data.resistance,
                                        self.ohmmeter.get_resistance())
        if self.quartz:
            rate = self.quartz.get_rate(nm=True)
            # mon_data.thickness.append(self.quartz.get_thickness(nm=True))
            mon_data.thickness = np.append(mon_data.thickness,
                                           self.quartz.get_thickness(nm=True))
        if self.mfc:
            pressure = self.mfc.get_pressure()
            Ar_flow = self.mfc.get_flow(self.Ar_channel)
            ArO_flow = self.mfc.get_flow(self.ArO_channel)

        if mon_data.resistance[-1] > 1.e9:
            mon_data.resistance[-1] = np.nan
        self._data_resistance.append(mon_data.resistance[-1])
        if not mon_data.resistance[-1] == 0:
            self._data_conductance.append(1. / mon_data.resistance[-1])
        else:
            self._data_conductance.append(np.nan)
        if self.quartz:
            self._data_rate.append(rate)
            self._data_thickness.append(mon_data.thickness[-1])
        if self.mfc:
            self._data_pressure.append(pressure)
            self._data_Ar_flow.append(Ar_flow)
            self._data_ArO_flow.append(ArO_flow)
        if self.quartz:
            deviation_abs = mon_data.resistance[-1] - self.ideal_resistance(
                mon_data.thickness[-1])
            deviation_rel = deviation_abs / self._target_resistance

            self._data_deviation_abs.append(deviation_abs)
            self._data_deviation_rel.append(deviation_rel)

        if ((self._fit_resistance) and (mon_data.it % self._fit_every == 0)
                and (len(mon_data.resistance) >= self._fit_points)):
            """
            # OLD ROUTINE
            estimation = self._fit_trend(mon_data.thickness[-self._fit_points:None],
                                         mon_data.resistance[-self._fit_points:None])
            self._thickness_estimation.append(estimation[0])
            self._resistance_estimation.append(estimation[1])
            self._last_resistance_fit.append(self._reciprocal(self.ideal_trend()[0],self._popt[0]), reset=True)
            """
            estimation = self._fit_layer(
                mon_data.thickness[-self._fit_points:None],
                mon_data.resistance[-self._fit_points:None])
            self._thickness_estimation.append(estimation[0])
            self._resistance_estimation.append(estimation[1])
            self._last_resistance_fit.append(self._theory(
                self.ideal_trend()[0], mon_data.thickness[-1],
                1. / mon_data.resistance[-1], estimation[2]),
                                             reset=True)

    def monitor_depo(self):
        """
        Main sputter deposition monitoring function.

        Consider using the threaded version by calling start_depmon().

        Records the film resistance, thickness and deposition rate live during the sputter process.
        Stores everything into a h5 file.
        Provides measures to estimate the resulting resistance of the final film and thereby
        facilitates live adjustments to the sputter parameters.

        Note:
            set_duration and set_resolution should be called before to set the monitoring length and time resolution.
            set_filmparameters should be called before to provide the actual target values.
        """

        self._init_depmon()

        if self.progress_bar:
            self._p = Progress_Bar(
                self._duration / self._resolution,
                'EVAP_timetrace ' + self.dirname,
                self._resolution)  # FIXME: Doesn't make much sense...

        print('Monitoring deposition...')
        sys.stdout.flush()

        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(
                self._data_file.get_filepath(),
                datasets=['views/resistance_thickness'])

        try:
            """
            Initialize the data lists.
            """
            class MonData(object):
                resistance = np.array([])
                thickness = np.array([])

            mon_data = MonData()
            """
            Main loop:
            """
            mon_data.t0 = time.time(
            )  # note: Windows has limitations on time precision (about 16ms)
            for mon_data.it, _ in enumerate(self.x_vec):
                self._acquire(mon_data)

                if self.progress_bar:
                    self._p.iterate()

                # calculate the time when the next iteration should take place
                ti = mon_data.t0 + (float(mon_data.it) + 1) * self._resolution
                # wait until the total dt(iteration) has elapsed
                while time.time() < ti:
                    time.sleep(0.05)

        except KeyboardInterrupt:
            print('Monitoring interrupted by user.')

        except Exception as e:
            print(e)
            print(e.__doc__)
            print(e.message)

        finally:
            self._end_measurement()

    def _monitor_depo_bg(self):
        """
        Main sputter deposition monitoring function, threaded version

        Should only be called by main function start_depmon.

        Records the film resistance, thickness and deposition rate live during the sputter process.
        Stores everything into a h5 file.
        Provides measures to estimate the resulting resistance of the final film and thereby
        facilitates live adjustments to the sputter parameters.

        Note:
            set_duration and set_resolution should be called before to set the monitoring length and time resolution.
            set_filmparameters should be called before to provide the actual target values.
        """
        class StopPauseExeption(Exception):
            pass

        class StartPauseExeption(Exception):
            pass

        """
        A few boilerplate functions for the thread management:
        continue, stop, status, etc...
        """

        def stop():
            raise StopIteration

        def do_nothing():
            pass

        def status():
            self._depmon_queue.put(loop_status)

        def cont():
            raise StopPauseExeption

        def pause():
            raise StartPauseExeption

        loop_status = 0
        tasks = {}
        tasks[0] = stop
        tasks[1] = do_nothing
        tasks[2] = status
        tasks[3] = pause
        tasks[4] = cont
        """
        Initialize the data lists.
        """

        class MonData(object):
            resistance = np.array([])
            thickness = np.array([])

        mon_data = MonData()
        """
        Main loop:
        """
        mon_data.it = 0
        mon_data.t0 = time.time(
        )  # note: Windows has limitations on time precision (about 16ms)?
        while True:
            self._acquire(mon_data)

            # Handle external commands
            try:
                tasks.get(self._depmon_queue.get(False), do_nothing)()
                self._depmon_queue.task_done()
            except Empty:
                pass
            except StartPauseExeption:
                print('monitoring paused')
                while True:
                    try:
                        time.sleep(0.1)
                        tasks.get(self._depmon_queue.get(False), do_nothing)()
                        self._depmon_queue.task_done()
                    except StopPauseExeption:
                        print('monitoring restarted')
                        break
                    except Empty:
                        pass
            except StopPauseExeption:
                pass
            except StopIteration:
                break

            # calculate the time when the next iteration should take place
            mon_data.it += 1
            ti = mon_data.t0 + (float(mon_data.it)) * self._resolution
            loop_status = ti  # for now we simply return the runtime
            # wait until the total dt(iteration) has elapsed
            while time.time() < ti:
                time.sleep(0.05)

        self._end_measurement()

    def start_depmon(self):
        """
        Starts the main sputter deposition monitoring function background process (depmon).

        Records the film resistance, thickness and deposition rate live during the sputter process.
        Stores everything into a h5 file.
        Provides measures to estimate the resulting resistance of the final film and thereby
        facilitates live adjustments to the sputter parameters.

        Note:
            set_duration and set_resolution should be called before to set the monitoring length and time resolution.
            set_filmparameters should be called before to provide the actual target values.

        Hint:
            Check with 'list_depmon_threads()'
            Stop with 'stop_depmon()'
        """
        print('Monitoring deposition...')
        sys.stdout.flush()

        self._init_depmon()

        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(
                self._data_file.get_filepath(),
                datasets=['views/resistance_thickness'],
                refresh=1.)

        self._depmon = Thread(target=self._monitor_depo_bg, name="depmon-1")
        self._depmon.daemon = True
        self._depmon.start()

    def stop_depmon(self):
        """
        Stop the deposition monitoring thread.
        Can be executed several times if more than one monitoring thread is running.

        Hint:
            Check with 'list_depmon_threads()'
        """
        self._depmon_queue.put(0)

    def status_depmon(self):
        """
        Give a heartbeat of the deposition monitoring thread (in the moment just the runtime).
        """
        self._depmon_queue.put(2)
        print(self._depmon_queue.get())
        self._depmon_queue.task_done()

    def pause_depmon(self):
        print('Pause Monitoring deposition...')
        self._depmon_queue.put(3)

    def continue_depmon(self):
        print('Continue Monitoring deposition...')
        self._depmon_queue.put(4)

    def list_depmon_threads(self):
        """
        List all bg deposition monitoring threads.
        """
        for thread in threading.enumerate():
            if thread.getName()[:3] == "dep":
                print(thread)

    def _end_measurement(self):
        """
        The data file is closed and file path is printed.
        """
        print(self._data_file.get_filepath())
        # qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment)
        # #old version where we have to wait for the plots
        t = threading.Thread(
            target=qviewkit.save_plots,
            args=[self._data_file.get_filepath(), self._plot_comment])
        t.start()
        self._data_file.close_file()
        waf.close_log_file(self._log)
        self.dirname = None
Ejemplo n.º 2
0
class spectrum(object):
    """
    usage: similar to the old spectroscopy file.However class generates hdf vectors/matrices depending on the number
    of traces of your signal_analyzer.

    m = spectrum(sig_analyzer = specki)

    m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA')
    m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz')

    m.gen_fit_function(...)      several times

    m.measure_XX()
    functions that must be additionally implemented in your signal analyzer's driver
    get_y_unit(trace) - returns y-unit of your trace
    get_active_traces() - returns number of traces
    get_trace_name(trace) - returns name of your trace
    """
    def __init__(self, sig_analyzer, exp_name='', sample=None):

        self.sig_analyzer = sig_analyzer
        self.averaging_start_ready = "start_measurement" in self.sig_analyzer.get_function_names() \
                                     and "ready" in self.sig_analyzer.get_function_names()
        if not self.averaging_start_ready:
            logging.warning(
                __name__ + ': With your signal analyzer driver (' +
                self.sig_analyzer.get_type() + '),'
                ' I can not see when a measurement is complete. '
                'So I only wait for a specific time and hope the VNA has finished. '
                'Please consider implemeting the necessary functions into your driver.'
            )
        self.exp_name = exp_name
        self._sample = sample

        self.landscape = None
        self.span = 200e6  # [Hz]
        self.tdx = 0.002  # [s]
        self.tdy = 0.002  # [s]

        self.comment = ''
        self.dirname = None

        self.x_set_obj = None
        self.y_set_obj = None

        self.progress_bar = True
        self._plot_comment = ""

        self.set_log_function()

        self.open_qviewkit = True
        self.qviewkit_singleInstance = False

        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'spectroscopy'
        self._measurement_object.sample = self._sample

        self._qvk_process = False

    def set_log_function(self,
                         func=None,
                         name=None,
                         unit=None,
                         log_dtype=None):
        '''
        A function (object) can be passed to the measurement loop which is excecuted before every x iteration
        but after executing the x_object setter in 2D measurements and before every line (but after setting
        the x value) in 3D measurements.
        The return value of the function of type float or similar is stored in a value vector in the h5 file.

        Call without any arguments to delete all log functions. The timestamp is automatically saved.

        func: function object in list form
        name: name of logging parameter appearing in h5 file, default: 'log_param'
        unit: unit of logging parameter, default: ''
        log_dtype: h5 data type, default: 'f' (float32)
        '''

        if name == None:
            try:
                name = ['log_param'] * len(func)
            except Exception:
                name = None
        if unit == None:
            try:
                unit = [''] * len(func)
            except Exception:
                unit = None
        if log_dtype == None:
            try:
                log_dtype = ['f'] * len(func)
            except Exception:
                log_dtype = None

        self.log_function = []
        self.log_name = []
        self.log_unit = []
        self.log_dtype = []

        if func != None:
            for i, f in enumerate(func):
                self.log_function.append(f)
                self.log_name.append(name[i])
                self.log_unit.append(unit[i])
                self.log_dtype.append(log_dtype[i])

    def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit=""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        x_vec (array): conains the sweeping values
        x_coordname (string)
        x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        x_unit (string): optional
        '''
        self.x_vec = x_vec
        self.x_coordname = x_coordname
        self.x_set_obj = x_set_obj
        self.delete_fit_function()
        self.x_unit = x_unit

    def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit=""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        y_vec (array): contains the sweeping values
        y_coordname (string)
        y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        y_unit (string): optional
        '''
        self.y_vec = y_vec
        self.y_coordname = y_coordname
        self.y_set_obj = y_set_obj
        self.delete_fit_function()
        self.y_unit = y_unit

    def gen_fit_function(self, curve_f, curve_p, units='', p0=[-1, 0.1, 7]):
        '''
        curve_f: 'parab', 'hyp', specifies the fit function to be employed
        curve_p: set of points that are the basis for the fit in the format [[x1,x2,x3,...],[y1,y2,y3,...]], frequencies in Hz
        units: set this to 'Hz' in order to avoid large values that cause the fit routine to diverge
        p0 (optional): start parameters for the fit, must be an 1D array of length 3 ([a,b,c,d]),
           where for the parabula p0[3] will be ignored
        The parabolic function takes the form  y = a*(x-b)**2 + c , where (a,b,c) = p0
        The hyperbolic function takes the form y = sqrt[ a*(x-b)**2 + c ], where (a,b,c) = p0

        adds a trace to landscape
        '''
        if not qkit.module_available("scipy"):
            raise ImportError('scipy not available.')

        if not self.landscape:
            self.landscape = []

        x_fit = curve_p[0]
        if units == 'Hz':
            y_fit = np.array(curve_p[1]) * 1e-9
        else:
            y_fit = np.array(curve_p[1])

        try:
            multiplier = 1
            if units == 'Hz':
                multiplier = 1e9

            fit_fct = None
            if curve_f == 'parab':
                fit_fct = self.f_parab
            elif curve_f == 'hyp':
                fit_fct = self.f_hyp
            elif curve_f == 'lin':
                fit_fct = self.f_lin
                p0 = p0[:2]
            else:
                print('function type not known...aborting')
                raise ValueError

            popt, pcov = curve_fit(fit_fct, x_fit, y_fit, p0=p0)
            self.landscape.append(multiplier * fit_fct(self.x_vec, *popt))

        except Exception as message:
            print('fit not successful:', message)
            popt = p0

    def _prepare_measurement_sig_analyzer(self):
        '''
        all the relevant settings from the vna are updated and called
        '''

        self.sig_analyzer.get_all()
        # ttip.get_temperature()
        self._nop = self.sig_analyzer.get_nop()
        self._sweeptime_averages = self.sig_analyzer.get_sweeptime_averages()
        self._freqpoints = self.sig_analyzer.get_freqpoints()
        self.num_traces = self.sig_analyzer.get_active_traces()
        self.traces_names = []
        self.units = []
        for i in range(self.num_traces):
            self.traces_names.append(self.sig_analyzer.get_trace_name(
                i + 1))  # must be implemented in driver
            self.units.append(
                self.sig_analyzer.get_y_unit(i +
                                             1))  # must also be impelmented

        if self.averaging_start_ready:
            self.sig_analyzer.pre_measurement()

    def _prepare_measurement_file(self):
        '''
        creates the output .h5-file with distinct dataset structures for each measurement type.
        at this point all measurement parameters are known and put in the output file
        '''

        self._data_file = hdf.Data(name=self._file_name, mode='a')
        self._measurement_object.uuid = self._data_file._uuid
        self._measurement_object.hdf_relpath = self._data_file._relpath
        self._measurement_object.instruments = qkit.instruments.get_instrument_names(
        )

        self._measurement_object.save()
        self._mo = self._data_file.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        # write logfile and instrument settings
        self._write_settings_dataset()
        self._log = waf.open_log_file(self._data_file.get_filepath())
        self._data_freq = self._data_file.add_coordinate('frequency',
                                                         unit='Hz')
        self._data_freq.add(self._freqpoints)
        self._data = []  # empty list as we have a variable number of channels

        if self._scan_1D:
            for i in range(self.num_traces):
                self._data.append(
                    self._data_file.add_value_vector(self.traces_names[i],
                                                     x=self._data_freq,
                                                     unit=self.units[i],
                                                     save_timestamp=True))
        if self._scan_2D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname,
                                                          unit=self.x_unit)
            self._data_x.add(self.x_vec)
            for i in range(self.num_traces):
                self._data.append(
                    self._data_file.add_value_matrix(self.traces_names[i],
                                                     x=self._data_x,
                                                     y=self._data_freq,
                                                     unit=self.units[i],
                                                     save_timestamp=True))
            if self.log_function != None:  # use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(
                        self._data_file.add_value_vector(
                            self.log_name[i],
                            x=self._data_x,
                            unit=self.log_unit[i],
                            dtype=self.log_dtype[i]))

        if self._scan_3D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname,
                                                          unit=self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_y = self._data_file.add_coordinate(self.y_coordname,
                                                          unit=self.y_unit)
            self._data_y.add(self.y_vec)
            for i in range(self.num_traces):
                self._data.append(
                    self._data_file.add_value_box(self.traces_names[i],
                                                  x=self._data_x,
                                                  y=self.data_y,
                                                  z=self._data_freq,
                                                  unit=self.units[i],
                                                  save_timestamp=True))
            if self.log_function != None:  # use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(
                        self._data_file.add_value_vector(
                            self.log_name[i],
                            x=self._data_x,
                            unit=self.log_unit[i],
                            dtype=self.log_dtype[i]))

        if self.comment:
            self._data_file.add_comment(self.comment)

        if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
            self._qvk_process.terminate()  # terminate an old qviewkit instance

    def _write_settings_dataset(self):
        self._settings = self._data_file.add_textlist('settings')
        settings = waf.get_instrument_settings(self._data_file.get_filepath())
        self._settings.append(settings)

    def measure_1D(self, rescan=True, web_visible=True):
        '''
        measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        rescan: If True (default), the averages on the VNA are cleared and a new measurement is started.
                If False, it will directly take the data from the VNA without waiting.
        '''
        self._scan_1D = True
        self._scan_2D = False
        self._scan_3D = False
        self._scan_time = False

        self._measurement_object.measurement_func = 'measure_1D'
        self._measurement_object.x_axis = 'frequency'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = 'signal_analyzer_tracedata'
        self._file_name = self.dirname.replace(' ', '').replace(',', '_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name
        self._prepare_measurement_sig_analyzer()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(),
                                              datasets=self.traces_names)
        print('recording trace...')
        sys.stdout.flush()

        qkit.flow.start()
        if rescan:
            if self.averaging_start_ready:
                self.sig_analyzer.start_measurement()
                ti = time()
                if self.progress_bar:
                    self._p = Progress_Bar(self.sig_analyzer.get_averages(),
                                           self.dirname,
                                           self.sig_analyzer.get_sweeptime())
                qkit.flow.sleep(.2)
                while not self.sig_analyzer.ready():
                    if time() - ti > self.sig_analyzer.get_sweeptime(
                            query=False):
                        if self.progress_bar: self._p.iterate()
                        ti = time()
                    qkit.flow.sleep(.2)
                if self.progress_bar:
                    while self._p.progr < self._p.max_it:
                        self._p.iterate()
            else:  # not tested!!!!!!
                self.sig_analyzer.avg_clear()
                if self.sig_analyzer.get_averages(
                ) == 1 or self.sig_analyzer.get_Average(
                ) == False:  # no averaging
                    if self.progress_bar:
                        self._p = Progress_Bar(
                            1, self.dirname, self.sig_analyzer.get_sweeptime())
                    qkit.flow.sleep(
                        self.sig_analyzer.get_sweeptime())  # wait single sweep
                    if self.progress_bar: self._p.iterate()
                else:  # with averaging
                    if self.progress_bar:
                        self._p = Progress_Bar(
                            self.sig_analyzer.get_averages(), self.dirname,
                            self.sig_analyzer.get_sweeptime())
                    if "avg_status" in self.sig_analyzer.get_function_names():
                        for a in range(self.sig_analyzer.get_averages()):
                            while self.sig_analyzer.avg_status() <= a:
                                qkit.flow.sleep(
                                    .2
                                )  # maybe one would like to adjust this at a later point
                            if self.progress_bar: self._p.iterate()
                    else:  # old style
                        for a in range(self.sig_analyzer.get_averages()):
                            qkit.flow.sleep(self.sig_analyzer.get_sweeptime()
                                            )  # wait single sweep time
                            if self.progress_bar: self._p.iterate()

        for i in range(self.num_traces):
            self._data[i].append(self.sig_analyzer.get_tracedata(i + 1))

        qkit.flow.end()
        self._end_measurement()

    def measure_2D(self, web_visible=True):
        '''
        measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        for all parameters x_vec in x_obj
        '''

        if not self.x_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = True
        self._scan_3D = False
        self._scan_time = False

        self._measurement_object.measurement_func = 'measure_2D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = 'frequency'
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname
        self._file_name = '2D_' + self.dirname.replace(' ', '').replace(
            ',', '_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar:
            self._p = Progress_Bar(len(self.x_vec),
                                   '2D signal analyzer sweep ' + self.dirname,
                                   self.sig_analyzer.get_sweeptime_averages())

        self._prepare_measurement_sig_analyzer()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""

        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(),
                                              datasets=self.traces_names)
        self._measure()

    def measure_3D(self, web_visible=True):
        '''
        measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec.
        sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured.

        optional: measure method to perform the measurement according to landscape, if set
        self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s)
        note: make sure to have properly set x,y vectors before generating traces
        '''
        if not self.x_set_obj or not self.y_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = False
        self._scan_3D = True
        self._scan_time = False

        self._measurement_object.measurement_func = 'measure_3D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = self.y_coordname
        self._measurement_object.z_axis = 'frequency'
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname + ', ' + self.y_coordname
        self._file_name = '3D_' + self.dirname.replace(' ', '').replace(
            ',', '_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar:
            self._p = Progress_Bar(
                len(self.x_vec) * len(self.y_vec),
                '3D signal analyzer sweep ' + self.dirname,
                self.sig_analyzer.get_sweeptime_averages())
        self._prepare_measurement_sig_analyzer()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        """only middle point in freq array is plotted vs x and y"""
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(),
                                              datasets=self.traces_names)

        ### ???
        if self.landscape:
            self.center_freqs = np.array(self.landscape).T
        else:
            self.center_freqs = []  # load default sequence
            for i in range(len(self.x_vec)):
                self.center_freqs.append([0])

        self._measure()

    def _measure(self):
        '''
        measures and plots the data depending on the measurement type.
        the measurement loops feature the setting of the objects and saving the data in the .h5 file.
        '''
        qkit.flow.start()
        try:
            """
            loop: x_obj with parameters from x_vec
            """
            for ix, x in enumerate(self.x_vec):
                self.x_set_obj(x)
                sleep(self.tdx)

                if self.log_function != None:
                    for i, f in enumerate(self.log_function):
                        self._log_value[i].append(float(f()))

                #### not tested
                if self._scan_3D:
                    for y in self.y_vec:
                        """
                        loop: y_obj with parameters from y_vec (only 3D measurement)
                        """
                        if (
                                np.min(
                                    np.abs(self.center_freqs[ix] - y *
                                           np.ones(len(self.center_freqs[ix])))
                                ) > self.span / 2.
                        ) and self.landscape:  # if point is not of interest (not close to one of the functions)
                            data = []
                            for i in range(self.num_traces):
                                data.append(np.zeros(int(self._nop)))
                        else:
                            self.y_set_obj(y)
                            sleep(self.tdy)
                            if self.averaging_start_ready:
                                self.sig_analyzer.start_measurement()
                                qkit.flow.sleep(
                                    .2
                                )  # just to make sure, the ready command does not *still* show ready
                                while not self.sig_analyzer.ready():
                                    qkit.flow.sleep(.2)
                            else:
                                self.sig_analyzer.avg_clear()
                                qkit.flow.sleep(self._sweeptime_averages)

                            # if "avg_status" in self.sig_analyzer.get_function_names():
                            #       while self.sig_analyzer.avg_status() < self.sig_analyzer.get_averages():
                            #            qkit.flow.sleep(.2) #maybe one would like to adjust this at a later point
                            """ measurement """
                            for i in range(self.num_traces):
                                data[i] = self.sig_analyzer.get_tracedata(i +
                                                                          1)
                                self._data[i].append(data[i])

                        if self.progress_bar:
                            self._p.iterate()
                        qkit.flow.sleep()
                    """
                    filling of value-box is done here.
                    after every y-loop the data is stored the next 2d structure
                    """
                    for i in range(self.num_traces):
                        self._data[i].next_matrix()

                if self._scan_2D:
                    if self.averaging_start_ready:
                        self.sig_analyzer.start_measurement()
                        qkit.flow.sleep(
                            .2
                        )  # just to make sure, the ready command does not *still* show ready
                        while not self.sig_analyzer.ready():
                            qkit.flow.sleep(.2)
                    else:
                        self.sig_analyzer.avg_clear()
                        qkit.flow.sleep(self._sweeptime_averages)
                    """ measurement """
                    for i in range(self.num_traces):
                        self._data[i].append(
                            self.sig_analyzer.get_tracedata(i + 1))

                    if self.progress_bar:
                        self._p.iterate()
                    qkit.flow.sleep()
        finally:
            self._end_measurement()
            qkit.flow.end()

    def _end_measurement(self):
        '''
        the data file is closed and filepath is printed
        '''
        print(self._data_file.get_filepath())
        # qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots
        t = threading.Thread(
            target=qviewkit.save_plots,
            args=[self._data_file.get_filepath(), self._plot_comment])
        t.start()
        self._data_file.close_file()
        waf.close_log_file(self._log)
        self.dirname = None
        if self.averaging_start_ready: self.sig_analyzer.post_measurement()

    def delete_fit_function(self, n=None):
        '''
        delete single fit function n (with 0 being the first one generated) or the complete landscape for n not specified
        '''

        if n:
            self.landscape = np.delete(self.landscape, n, axis=0)
        else:
            self.landscape = None

    def plot_fit_function(self, num_points=100):
        '''
        try:
            x_coords = np.linspace(self.x_vec[0], self.x_vec[-1], num_points)
        except Exception as message:
            print 'no x axis information specified', message
            return
        '''
        if not qkit.module_available("matplotlib"):
            raise ImportError("matplotlib not found.")
        if self.landscape:
            for trace in self.landscape:
                try:
                    # plt.clear()
                    plt.plot(self.x_vec, trace)
                    plt.fill_between(self.x_vec,
                                     trace + float(self.span) / 2,
                                     trace - float(self.span) / 2,
                                     alpha=0.5)
                except Exception:
                    print('invalid trace...skip')
            plt.axhspan(self.y_vec[0],
                        self.y_vec[-1],
                        facecolor='0.5',
                        alpha=0.5)
            plt.show()
        else:
            print('No trace generated.')

    def set_span(self, span):
        self.span = span

    def get_span(self):
        return self.span

    def set_tdx(self, tdx):
        self.tdx = tdx

    def set_tdy(self, tdy):
        self.tdy = tdy

    def get_tdx(self):
        return self.tdx

    def get_tdy(self):
        return self.tdy

    def f_parab(self, x, a, b, c):
        return a * (x - b)**2 + c

    def f_hyp(self, x, a, b, c):
        "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]"
        return np.sqrt(a * (x - b)**2 + c)

    def f_lin(self, x, a, b):
        return a * x + b

    def set_plot_comment(self, comment):
        '''
        Small comment to add at the end of plot pics for more information i.e. good for wiki entries.
        '''
        self._plot_comment = comment
Ejemplo n.º 3
0
class spectrum(object):
    '''
    usage:

    m = spectrum(vna = vna1)

    m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA')
    m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz')

    m.gen_fit_function(...)      several times

    m.measure_XX()
    '''
    def __init__(self, vna, exp_name='', sample=None):

        self.vna = vna
        self.averaging_start_ready = "start_measurement" in self.vna.get_function_names(
        ) and "ready" in self.vna.get_function_names()
        if not self.averaging_start_ready:
            logging.warning(
                __name__ + ': With your VNA instrument driver (' +
                self.vna.get_type() +
                '), I can not see when a measurement is complete. So I only wait for a specific time and hope the VNA has finished. Please consider implemeting the necessary functions into your driver.'
            )
        self.exp_name = exp_name
        self._sample = sample

        self.landscape = None
        self.span = 200e6  #[Hz]
        self.tdx = 0.002  #[s]
        self.tdy = 0.002  #[s]

        self.comment = ''
        self.dirname = None

        self.x_set_obj = None
        self.y_set_obj = None

        self.progress_bar = True
        self._fit_resonator = False
        self._plot_comment = ""

        self.set_log_function()

        self.open_qviewkit = True
        self.qviewkit_singleInstance = False

        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'spectroscopy'
        self._measurement_object.sample = self._sample

        self._qvk_process = False

        self.number_of_timetraces = 1  #relevant in time domain mode

    def set_log_function(self,
                         func=None,
                         name=None,
                         unit=None,
                         log_dtype=None):
        '''
        A function (object) can be passed to the measurement loop which is excecuted before every x iteration 
        but after executing the x_object setter in 2D measurements and before every line (but after setting 
        the x value) in 3D measurements.
        The return value of the function of type float or similar is stored in a value vector in the h5 file.

        Call without any arguments to delete all log functions. The timestamp is automatically saved.

        func: function object in list form
        name: name of logging parameter appearing in h5 file, default: 'log_param'
        unit: unit of logging parameter, default: ''
        log_dtype: h5 data type, default: 'f' (float32)
        '''

        if name == None:
            try:
                name = ['log_param'] * len(func)
            except Exception:
                name = None
        if unit == None:
            try:
                unit = [''] * len(func)
            except Exception:
                unit = None
        if log_dtype == None:
            try:
                log_dtype = ['f'] * len(func)
            except Exception:
                log_dtype = None

        self.log_function = []
        self.log_name = []
        self.log_unit = []
        self.log_dtype = []

        if func != None:
            for i, f in enumerate(func):
                self.log_function.append(f)
                self.log_name.append(name[i])
                self.log_unit.append(unit[i])
                self.log_dtype.append(log_dtype[i])

    def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit=""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        x_vec (array): conains the sweeping values
        x_coordname (string)
        x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        x_unit (string): optional
        '''
        self.x_vec = x_vec
        self.x_coordname = x_coordname
        self.x_set_obj = x_set_obj
        self.delete_fit_function()
        self.x_unit = x_unit

    def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit=""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        y_vec (array): contains the sweeping values
        y_coordname (string)
        y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        y_unit (string): optional
        '''
        self.y_vec = y_vec
        self.y_coordname = y_coordname
        self.y_set_obj = y_set_obj
        self.delete_fit_function()
        self.y_unit = y_unit

    def gen_fit_function(self, curve_f, curve_p, units='', p0=[-1, 0.1, 7]):
        '''
        curve_f: 'parab', 'hyp', specifies the fit function to be employed
        curve_p: set of points that are the basis for the fit in the format [[x1,x2,x3,...],[y1,y2,y3,...]], frequencies in Hz
        units: set this to 'Hz' in order to avoid large values that cause the fit routine to diverge
        p0 (optional): start parameters for the fit, must be an 1D array of length 3 ([a,b,c,d]), 
           where for the parabula p0[3] will be ignored 
        The parabolic function takes the form  y = a*(x-b)**2 + c , where (a,b,c) = p0
        The hyperbolic function takes the form y = sqrt[ a*(x-b)**2 + c ], where (a,b,c) = p0
        
        adds a trace to landscape
        '''

        if not self.landscape:
            self.landscape = []

        x_fit = curve_p[0]
        if units == 'Hz':
            y_fit = np.array(curve_p[1]) * 1e-9
        else:
            y_fit = np.array(curve_p[1])

        try:
            multiplier = 1
            if units == 'Hz':
                multiplier = 1e9

            fit_fct = None
            if curve_f == 'parab':
                fit_fct = self.f_parab
            elif curve_f == 'hyp':
                fit_fct = self.f_hyp
            elif curve_f == 'lin':
                fit_fct = self.f_lin
                p0 = p0[:2]
            else:
                print 'function type not known...aborting'
                raise ValueError

            popt, pcov = curve_fit(fit_fct, x_fit, y_fit, p0=p0)
            self.landscape.append(multiplier * fit_fct(self.x_vec, *popt))

        except Exception as message:
            print 'fit not successful:', message
            popt = p0

    def _prepare_measurement_vna(self):
        '''
        all the relevant settings from the vna are updated and called
        '''

        self.vna.get_all()
        #ttip.get_temperature()
        self._nop = self.vna.get_nop()
        self._sweeptime_averages = self.vna.get_sweeptime_averages()
        self._freqpoints = self.vna.get_freqpoints()

        if self.averaging_start_ready: self.vna.pre_measurement()

    def _prepare_measurement_file(self):
        '''
        creates the output .h5-file with distinct dataset structures for each measurement type.
        at this point all measurement parameters are known and put in the output file
        '''

        self._data_file = hdf.Data(name=self._file_name)
        self._measurement_object.uuid = self._data_file._uuid
        self._measurement_object.hdf_relpath = self._data_file._relpath
        self._measurement_object.instruments = qt.instruments.get_instruments()

        self._measurement_object.save()
        self._mo = self._data_file.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        # write logfile and instrument settings
        self._write_settings_dataset()
        self._log = waf.open_log_file(self._data_file.get_filepath())

        if not self._scan_time:
            self._data_freq = self._data_file.add_coordinate('frequency',
                                                             unit='Hz')
            self._data_freq.add(self._freqpoints)

        if self._scan_1D:
            self._data_real = self._data_file.add_value_vector(
                'real', x=self._data_freq, unit='', save_timestamp=True)
            self._data_imag = self._data_file.add_value_vector(
                'imag', x=self._data_freq, unit='', save_timestamp=True)
            self._data_amp = self._data_file.add_value_vector(
                'amplitude',
                x=self._data_freq,
                unit='arb. unit',
                save_timestamp=True)
            self._data_pha = self._data_file.add_value_vector(
                'phase', x=self._data_freq, unit='rad', save_timestamp=True)

        if self._scan_2D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname,
                                                          unit=self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_amp = self._data_file.add_value_matrix(
                'amplitude',
                x=self._data_x,
                y=self._data_freq,
                unit='arb. unit',
                save_timestamp=True)
            self._data_pha = self._data_file.add_value_matrix(
                'phase',
                x=self._data_x,
                y=self._data_freq,
                unit='rad',
                save_timestamp=True)

            if self.log_function != None:  #use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(
                        self._data_file.add_value_vector(
                            self.log_name[i],
                            x=self._data_x,
                            unit=self.log_unit[i],
                            dtype=self.log_dtype[i]))

            if self._nop < 10:
                """creates view: plot middle point vs x-parameter, for qubit measurements"""
                self._data_amp_mid = self._data_file.add_value_vector(
                    'amplitude_midpoint',
                    unit='arb. unit',
                    x=self._data_x,
                    save_timestamp=True)
                self._data_pha_mid = self._data_file.add_value_vector(
                    'phase_midpoint',
                    unit='rad',
                    x=self._data_x,
                    save_timestamp=True)
                #self._view = self._data_file.add_view("amplitude vs. " + self.x_coordname, x = self._data_x, y = self._data_amp[self._nop/2])

        if self._scan_3D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname,
                                                          unit=self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_y = self._data_file.add_coordinate(self.y_coordname,
                                                          unit=self.y_unit)
            self._data_y.add(self.y_vec)

            if self._nop == 0:  #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online.
                self._data_amp = self._data_file.add_value_matrix(
                    'amplitude',
                    x=self._data_x,
                    y=self._data_y,
                    unit='arb. unit',
                    save_timestamp=False)
                self._data_pha = self._data_file.add_value_matrix(
                    'phase',
                    x=self._data_x,
                    y=self._data_y,
                    unit='rad',
                    save_timestamp=False)
            else:
                self._data_amp = self._data_file.add_value_box(
                    'amplitude',
                    x=self._data_x,
                    y=self._data_y,
                    z=self._data_freq,
                    unit='arb. unit',
                    save_timestamp=False)
                self._data_pha = self._data_file.add_value_box(
                    'phase',
                    x=self._data_x,
                    y=self._data_y,
                    z=self._data_freq,
                    unit='rad',
                    save_timestamp=False)

            if self.log_function != None:  #use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(
                        self._data_file.add_value_vector(
                            self.log_name[i],
                            x=self._data_x,
                            unit=self.log_unit[i],
                            dtype=self.log_dtype[i]))

        if self._scan_time:
            self._data_freq = self._data_file.add_coordinate('frequency',
                                                             unit='Hz')
            self._data_freq.add([self.vna.get_centerfreq()])

            self._data_time = self._data_file.add_coordinate('time', unit='s')
            self._data_time.add(
                np.arange(0, self._nop, 1) * self.vna.get_sweeptime() /
                (self._nop - 1))

            self._data_x = self._data_file.add_coordinate('trace_number',
                                                          unit='')
            self._data_x.add(np.arange(0, self.number_of_timetraces, 1))

            self._data_amp = self._data_file.add_value_matrix(
                'amplitude',
                x=self._data_x,
                y=self._data_time,
                unit='lin. mag.',
                save_timestamp=False)
            self._data_pha = self._data_file.add_value_matrix(
                'phase',
                x=self._data_x,
                y=self._data_time,
                unit='rad.',
                save_timestamp=False)

        if self.comment:
            self._data_file.add_comment(self.comment)

        if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
            self._qvk_process.terminate()  #terminate an old qviewkit instance

    def _write_settings_dataset(self):
        self._settings = self._data_file.add_textlist('settings')
        settings = waf.get_instrument_settings(self._data_file.get_filepath())
        self._settings.append(settings)

    def measure_1D(self, rescan=True, web_visible=True):
        '''
        measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. 
                If False, it will directly take the data from the VNA without waiting.
        '''
        self._scan_1D = True
        self._scan_2D = False
        self._scan_3D = False
        self._scan_time = False

        self._measurement_object.measurement_func = 'measure_1D'
        self._measurement_object.x_axis = 'frequency'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = 'VNA_tracedata'
        self._file_name = self.dirname.replace(' ', '').replace(',', '_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name
        self._prepare_measurement_vna()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(),
                                              datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath())
        print 'recording trace...'
        sys.stdout.flush()

        qt.mstart()
        if rescan:
            if self.averaging_start_ready:
                self.vna.start_measurement()
                ti = time()
                if self.progress_bar:
                    self._p = Progress_Bar(self.vna.get_averages(),
                                           self.dirname,
                                           self.vna.get_sweeptime())
                qt.msleep(.2)
                while not self.vna.ready():
                    if time() - ti > self.vna.get_sweeptime(query=False):
                        if self.progress_bar: self._p.iterate()
                        ti = time()
                    qt.msleep(.2)
                if self.progress_bar:
                    while self._p.progr < self._p.max_it:
                        self._p.iterate()
            else:
                self.vna.avg_clear()
                if self.vna.get_averages() == 1 or self.vna.get_Average(
                ) == False:  #no averaging
                    if self.progress_bar:
                        self._p = Progress_Bar(1, self.dirname,
                                               self.vna.get_sweeptime())
                    qt.msleep(self.vna.get_sweeptime())  #wait single sweep
                    if self.progress_bar: self._p.iterate()
                else:  #with averaging
                    if self.progress_bar:
                        self._p = Progress_Bar(self.vna.get_averages(),
                                               self.dirname,
                                               self.vna.get_sweeptime())
                    if "avg_status" in self.vna.get_function_names():
                        for a in range(self.vna.get_averages()):
                            while self.vna.avg_status() <= a:
                                qt.msleep(
                                    .2
                                )  #maybe one would like to adjust this at a later point
                            if self.progress_bar: self._p.iterate()
                    else:  #old style
                        for a in range(self.vna.get_averages()):
                            qt.msleep(self.vna.get_sweeptime()
                                      )  #wait single sweep time
                            if self.progress_bar: self._p.iterate()

        data_amp, data_pha = self.vna.get_tracedata()
        data_real, data_imag = self.vna.get_tracedata('RealImag')

        self._data_amp.append(data_amp)
        self._data_pha.append(data_pha)
        self._data_real.append(data_real)
        self._data_imag.append(data_imag)
        if self._fit_resonator:
            self._do_fit_resonator()

        qt.mend()
        self._end_measurement()

    def measure_2D(self, web_visible=True):
        '''
        measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        for all parameters x_vec in x_obj
        '''

        if not self.x_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = True
        self._scan_3D = False
        self._scan_time = False

        self._measurement_object.measurement_func = 'measure_2D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = 'frequency'
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname
        self._file_name = '2D_' + self.dirname.replace(' ', '').replace(
            ',', '_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar:
            self._p = Progress_Bar(len(self.x_vec),
                                   '2D VNA sweep ' + self.dirname,
                                   self.vna.get_sweeptime_averages())

        self._prepare_measurement_vna()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self._nop < 10:
            if self.open_qviewkit:
                self._qvk_process = qviewkit.plot(
                    self._data_file.get_filepath(),
                    datasets=['amplitude_midpoint', 'phase_midpoint'])
        else:
            if self.open_qviewkit:
                self._qvk_process = qviewkit.plot(
                    self._data_file.get_filepath(),
                    datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath())
        self._measure()

    def measure_3D(self, web_visible=True):
        '''
        measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured.

        optional: measure method to perform the measurement according to landscape, if set
        self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s)
        note: make sure to have properly set x,y vectors before generating traces
        '''
        if not self.x_set_obj or not self.y_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = False
        self._scan_3D = True
        self._scan_time = False

        self._measurement_object.measurement_func = 'measure_3D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = self.y_coordname
        self._measurement_object.z_axis = 'frequency'
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname + ', ' + self.y_coordname
        self._file_name = '3D_' + self.dirname.replace(' ', '').replace(
            ',', '_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar:
            self._p = Progress_Bar(
                len(self.x_vec) * len(self.y_vec),
                '3D VNA sweep ' + self.dirname,
                self.vna.get_sweeptime_averages())

        self._prepare_measurement_vna()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        """only middle point in freq array is plotted vs x and y"""
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(),
                                              datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath())

        if self.landscape:
            self.center_freqs = np.array(self.landscape).T
        else:
            self.center_freqs = []  #load default sequence
            for i in range(len(self.x_vec)):
                self.center_freqs.append([0])

        self._measure()

    def measure_timetrace(self, web_visible=True):
        '''
        measure method to record a single VNA timetrace, this only makes sense when span is set to 0 Hz!,
        tested only with KEYSIGHT E5071C ENA and its corresponding qkit driver
        LGruenhaupt 11/2016
        '''
        if self.vna.get_span() > 0:
            print 'VNA span not set to 0 Hz... aborting'
            return

        self._scan_1D = False
        self._scan_2D = False
        self._scan_3D = False
        self._scan_time = True

        self._measurement_object.measurement_func = 'measure_timetrace'
        self._measurement_object.x_axis = 'time'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = 'VNA_timetrace'
        self._file_name = self.dirname.replace(' ', '').replace(',', '_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        self.x_vec = np.arange(0, self.number_of_timetraces, 1)

        self._prepare_measurement_vna()
        self._prepare_measurement_file()

        if self.progress_bar:
            self._p = Progress_Bar(self.number_of_timetraces,
                                   'VNA timetrace ' + self.dirname,
                                   self.vna.get_sweeptime_averages())

        print 'recording timetrace(s)...'
        sys.stdout.flush()

        qt.mstart()
        try:
            """
            loop: x_obj with parameters from x_vec
            """

            for i, x in enumerate(self.x_vec):

                if self.log_function != None:
                    for i, f in enumerate(self.log_function):
                        self._log_value[i].append(float(f()))

                if self.averaging_start_ready:  #LG 11/2016
                    self.vna.start_measurement()
                    ti = time(
                    )  #changed from time.time() to time() - LGruenhaupt OCT_2016
                    tp = time()  #added to enable use of progress bar
                    #self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime_averages()) moved to line 303
                    ''' This is to prevent the vna.ready() function from timing out. LG NOV/16 '''
                    if self.vna.get_Average():
                        print 'this function only makes sense without averaging'
                        qt.mend()
                        self._end_measuremt()
                    else:
                        #self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime())
                        qt.msleep(self.vna.get_sweeptime())
                        #self._p.iterate()

                        while not self.vna.ready():
                            qt.msleep(
                                .2
                            )  #this is just to check if the measurement has finished

                        data_amp, data_pha = self.vna.get_tracedata()
                        self._data_amp.append(data_amp)
                        self._data_pha.append(data_pha)
                qt.msleep()
                if self.progress_bar:
                    self._p.iterate()

                else:
                    print 'not implemented for this VNA, only works with Keysight ENA 5071C'
                    qt.mend()
                    self._end_measurement()

        except Exception as e:
            print e.__doc__
            print e.message
        finally:
            self._end_measurement()
            qt.mend()

    def _measure(self):
        '''
        measures and plots the data depending on the measurement type.
        the measurement loops feature the setting of the objects and saving the data in the .h5 file.
        '''
        qt.mstart()
        try:
            """
            loop: x_obj with parameters from x_vec
            """
            for ix, x in enumerate(self.x_vec):
                self.x_set_obj(x)
                sleep(self.tdx)

                if self.log_function != None:
                    for i, f in enumerate(self.log_function):
                        self._log_value[i].append(float(f()))

                if self._scan_3D:
                    for y in self.y_vec:
                        """
                        loop: y_obj with parameters from y_vec (only 3D measurement)
                        """
                        if (
                                np.min(
                                    np.abs(self.center_freqs[ix] - y *
                                           np.ones(len(self.center_freqs[ix])))
                                ) > self.span / 2.
                        ) and self.landscape:  #if point is not of interest (not close to one of the functions)
                            data_amp = np.zeros(int(self._nop))
                            data_pha = np.zeros(int(
                                self._nop))  #fill with zeros
                        else:
                            self.y_set_obj(y)
                            sleep(self.tdy)
                            if self.averaging_start_ready:
                                self.vna.start_measurement()
                                qt.msleep(
                                    .2
                                )  #just to make sure, the ready command does not *still* show ready
                                while not self.vna.ready():
                                    qt.msleep(.2)
                            else:
                                self.vna.avg_clear()
                                qt.msleep(self._sweeptime_averages)

                            #if "avg_status" in self.vna.get_function_names():
                            #       while self.vna.avg_status() < self.vna.get_averages():
                            #            qt.msleep(.2) #maybe one would like to adjust this at a later point
                            """ measurement """
                            data_amp, data_pha = self.vna.get_tracedata()

                        if self._nop == 0:  # this does not work yet.
                            print data_amp[0], data_amp, self._nop
                            self._data_amp.append(data_amp[0])
                            self._data_pha.append(data_pha[0])
                        else:
                            self._data_amp.append(data_amp)
                            self._data_pha.append(data_pha)
                        if self._fit_resonator:
                            self._do_fit_resonator()
                        if self.progress_bar:
                            self._p.iterate()
                        qt.msleep()
                    """
                    filling of value-box is done here.
                    after every y-loop the data is stored the next 2d structure
                    """
                    self._data_amp.next_matrix()
                    self._data_pha.next_matrix()

                if self._scan_2D:
                    if self.averaging_start_ready:
                        self.vna.start_measurement()
                        qt.msleep(
                            .2
                        )  #just to make sure, the ready command does not *still* show ready
                        while not self.vna.ready():
                            qt.msleep(.2)
                    else:
                        self.vna.avg_clear()
                        qt.msleep(self._sweeptime_averages)
                    """ measurement """
                    data_amp, data_pha = self.vna.get_tracedata()
                    self._data_amp.append(data_amp)
                    self._data_pha.append(data_pha)
                    if self._nop < 10:
                        #print data_amp[self._nop/2]
                        self._data_amp_mid.append(
                            float(data_amp[self._nop / 2]))
                        self._data_pha_mid.append(
                            float(data_pha[self._nop / 2]))

                    if self._fit_resonator:
                        self._do_fit_resonator()
                    if self.progress_bar:
                        self._p.iterate()
                    qt.msleep()
        except Exception as e:
            print e.__doc__
            print e.message
        finally:
            self._end_measurement()
            qt.mend()

    def _end_measurement(self):
        '''
        the data file is closed and filepath is printed
        '''
        print self._data_file.get_filepath()
        #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots
        t = threading.Thread(
            target=qviewkit.save_plots,
            args=[self._data_file.get_filepath(), self._plot_comment])
        t.start()
        self._data_file.close_file()
        waf.close_log_file(self._log)
        self.dirname = None
        if self.averaging_start_ready: self.vna.post_measurement()

    def set_resonator_fit(self,
                          fit_resonator=True,
                          fit_function='',
                          f_min=None,
                          f_max=None):
        '''
        sets fit parameter for resonator

        fit_resonator (bool): True or False, default: True (optional)
        fit_function (string): function which will be fitted to the data (optional)
        f_min (float): lower frequency boundary for the fitting function, default: None (optional)
        f_max (float): upper frequency boundary for the fitting function, default: None (optional)
        fit types: 'lorentzian','skewed_lorentzian','circle_fit_reflection', 'circle_fit_notch','fano'
        '''
        if not fit_resonator:
            self._fit_resonator = False
            return
        self._functions = {
            'lorentzian': 0,
            'skewed_lorentzian': 1,
            'circle_fit_reflection': 2,
            'circle_fit_notch': 3,
            'fano': 5,
            'all_fits': 5
        }
        try:
            self._fit_function = self._functions[fit_function]
        except KeyError:
            logging.error(
                'Fit function not properly set. Must be either \'lorentzian\', \'skewed_lorentzian\', \'circle_fit_reflection\', \'circle_fit_notch\', \'fano\', or \'all_fits\'.'
            )
        else:
            self._fit_resonator = True
            self._f_min = f_min
            self._f_max = f_max

    def _do_fit_resonator(self):
        '''
        calls fit function in resonator class
        fit function is specified in self.set_fit, with boundaries f_mim and f_max
        only the last 'slice' of data is fitted, since we fit live while measuring.
        '''

        if self._fit_function == 0:  #lorentzian
            self._resonator.fit_lorentzian(f_min=self._f_min,
                                           f_max=self._f_max)
        if self._fit_function == 1:  #skewed_lorentzian
            self._resonator.fit_skewed_lorentzian(f_min=self._f_min,
                                                  f_max=self._f_max)
        if self._fit_function == 2:  #circle_reflection
            self._resonator.fit_circle(reflection=True,
                                       f_min=self._f_min,
                                       f_max=self._f_max)
        if self._fit_function == 3:  #circle_notch
            self._resonator.fit_circle(notch=True,
                                       f_min=self._f_min,
                                       f_max=self._f_max)
        if self._fit_function == 4:  #fano
            self._resonator.fit_fano(f_min=self._f_min, f_max=self._f_max)
        #if self._fit_function == 5: #all fits
        #self._resonator.fit_all_fits(f_min=self._f_min, f_max = self._f_max)

    def delete_fit_function(self, n=None):
        '''
        delete single fit function n (with 0 being the first one generated) or the complete landscape for n not specified
        '''

        if n:
            self.landscape = np.delete(self.landscape, n, axis=0)
        else:
            self.landscape = None

    def plot_fit_function(self, num_points=100):
        '''
        try:
            x_coords = np.linspace(self.x_vec[0], self.x_vec[-1], num_points)
        except Exception as message:
            print 'no x axis information specified', message
            return
        '''
        if self.landscape:
            for trace in self.landscape:
                try:
                    #plt.clear()
                    plt.plot(self.x_vec, trace)
                    plt.fill_between(self.x_vec,
                                     trace + float(self.span) / 2,
                                     trace - float(self.span) / 2,
                                     alpha=0.5)
                except Exception:
                    print 'invalid trace...skip'
            plt.axhspan(self.y_vec[0],
                        self.y_vec[-1],
                        facecolor='0.5',
                        alpha=0.5)
            plt.show()
        else:
            print 'No trace generated.'

    def set_span(self, span):
        self.span = span

    def get_span(self):
        return self.span

    def set_tdx(self, tdx):
        self.tdx = tdx

    def set_tdy(self, tdy):
        self.tdy = tdy

    def get_tdx(self):
        return self.tdx

    def get_tdy(self):
        return self.tdy

    def f_parab(self, x, a, b, c):
        return a * (x - b)**2 + c

    def f_hyp(self, x, a, b, c):
        "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]"
        return np.sqrt(a * (x - b)**2 + c)

    def f_lin(self, x, a, b):
        return a * x + b

    def set_plot_comment(self, comment):
        '''
        Small comment to add at the end of plot pics for more information i.e. good for wiki entries.
        '''
        self._plot_comment = comment
Ejemplo n.º 4
0
class spectrum(object):
    '''
    usage:

    m = spectrum(vna = vna1)

    m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA')
    m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz')

    m.gen_fit_function(...)      several times

    m.measure_XX()
    '''

    def __init__(self, vna, exp_name = '', sample = None):

        self.vna = vna
        self.averaging_start_ready = "start_measurement" in self.vna.get_function_names() and "ready" in self.vna.get_function_names()
        if not self.averaging_start_ready: logging.warning(__name__ + ': With your VNA instrument driver (' + self.vna.get_type() + '), I can not see when a measurement is complete. So I only wait for a specific time and hope the VNA has finished. Please consider implemeting the necessary functions into your driver.')
        self.exp_name = exp_name
        self._sample = sample

        self.landscape = None
        self.span = 200e6    #[Hz]
        self.tdx = 0.002   #[s]
        self.tdy = 0.002   #[s]

        self.comment = ''
        self.dirname = None

        self.x_set_obj = None
        self.y_set_obj = None

        self.progress_bar = True
        self._fit_resonator = False
        self._plot_comment=""

        self.set_log_function()
        
        self.open_qviewkit = True
        self.qviewkit_singleInstance = False
        
        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'spectroscopy'
        self._measurement_object.sample = self._sample
        
        self._qvk_process = False
        
        self.number_of_timetraces = 1   #relevant in time domain mode
        
        

    def set_log_function(self, func=None, name = None, unit = None, log_dtype = None):
        '''
        A function (object) can be passed to the measurement loop which is excecuted before every x iteration 
        but after executing the x_object setter in 2D measurements and before every line (but after setting 
        the x value) in 3D measurements.
        The return value of the function of type float or similar is stored in a value vector in the h5 file.

        Call without any arguments to delete all log functions. The timestamp is automatically saved.

        func: function object in list form
        name: name of logging parameter appearing in h5 file, default: 'log_param'
        unit: unit of logging parameter, default: ''
        log_dtype: h5 data type, default: 'f' (float32)
        '''

        if name == None:
            try:
                name = ['log_param']*len(func)
            except Exception:
                name = None
        if unit == None:
            try:
                unit = ['']*len(func)
            except Exception:
                unit = None
        if log_dtype == None:
            try:
                log_dtype = ['f']*len(func)
            except Exception:
                log_dtype = None

        self.log_function = []
        self.log_name = []
        self.log_unit = []
        self.log_dtype = []

        if func != None:
            for i,f in enumerate(func):
                self.log_function.append(f)
                self.log_name.append(name[i])
                self.log_unit.append(unit[i])
                self.log_dtype.append(log_dtype[i])

    def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit = ""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        x_vec (array): conains the sweeping values
        x_coordname (string)
        x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        x_unit (string): optional
        '''
        self.x_vec = x_vec
        self.x_coordname = x_coordname
        self.x_set_obj = x_set_obj
        self.delete_fit_function()
        self.x_unit = x_unit

    def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit = ""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        y_vec (array): contains the sweeping values
        y_coordname (string)
        y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        y_unit (string): optional
        '''
        self.y_vec = y_vec
        self.y_coordname = y_coordname
        self.y_set_obj = y_set_obj
        self.delete_fit_function()
        self.y_unit = y_unit

    def gen_fit_function(self, curve_f, curve_p, units = '', p0 = [-1,0.1,7]):
        '''
        curve_f: 'parab', 'hyp', specifies the fit function to be employed
        curve_p: set of points that are the basis for the fit in the format [[x1,x2,x3,...],[y1,y2,y3,...]], frequencies in Hz
        units: set this to 'Hz' in order to avoid large values that cause the fit routine to diverge
        p0 (optional): start parameters for the fit, must be an 1D array of length 3 ([a,b,c,d]), 
           where for the parabula p0[3] will be ignored 
        The parabolic function takes the form  y = a*(x-b)**2 + c , where (a,b,c) = p0
        The hyperbolic function takes the form y = sqrt[ a*(x-b)**2 + c ], where (a,b,c) = p0
        
        adds a trace to landscape
        '''

        if not self.landscape:
            self.landscape = []

        x_fit = curve_p[0]
        if units == 'Hz':
            y_fit = np.array(curve_p[1])*1e-9
        else:
            y_fit = np.array(curve_p[1])

        try:
            if curve_f == 'parab':
                "remove the last item"
                #p0.pop()
                popt, pcov = curve_fit(self.f_parab, x_fit, y_fit, p0=p0)
                if units == 'Hz':
                    self.landscape.append(1e9*self.f_parab(self.x_vec, *popt))
                else:
                    self.landscape.append(self.f_parab(self.x_vec, *popt))
            elif curve_f == 'hyp':
                popt, pcov = curve_fit(self.f_hyp, x_fit, y_fit, p0=p0)
                print popt
                if units == 'Hz':
                    self.landscape.append(1e9*self.f_hyp(self.x_vec, *popt))
                else:
                    self.landscape.append(self.f_hyp(self.x_vec, *popt))
            else:
                print 'function type not known...aborting'
                raise ValueError
        except Exception as message:
            print 'fit not successful:', message
            popt = p0

    def _prepare_measurement_vna(self):
        '''
        all the relevant settings from the vna are updated and called
        '''

        self.vna.get_all()
        #ttip.get_temperature()
        self._nop = self.vna.get_nop()
        self._sweeptime_averages = self.vna.get_sweeptime_averages()
        self._freqpoints = self.vna.get_freqpoints()

        if self.averaging_start_ready: self.vna.pre_measurement()

    def _prepare_measurement_file(self):
        '''
        creates the output .h5-file with distinct dataset structures for each measurement type.
        at this point all measurement parameters are known and put in the output file
        '''

        self._data_file = hdf.Data(name=self._file_name)
        self._measurement_object.uuid = self._data_file._uuid
        self._measurement_object.hdf_relpath = self._data_file._relpath
        self._measurement_object.instruments = qt.instruments.get_instruments()

        self._measurement_object.save()
        self._mo = self._data_file.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        # write logfile and instrument settings
        self._write_settings_dataset()
        self._log = waf.open_log_file(self._data_file.get_filepath())

        if not self._scan_time:
            self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz')
            self._data_freq.add(self._freqpoints)

        if self._scan_1D:
            self._data_real = self._data_file.add_value_vector('real', x = self._data_freq, unit = '', save_timestamp = True)
            self._data_imag = self._data_file.add_value_vector('imag', x = self._data_freq, unit = '', save_timestamp = True)
            self._data_amp = self._data_file.add_value_vector('amplitude', x = self._data_freq, unit = 'arb. unit', save_timestamp = True)
            self._data_pha = self._data_file.add_value_vector('phase', x = self._data_freq, unit = 'rad', save_timestamp = True)

        if self._scan_2D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_freq, unit = 'arb. unit', save_timestamp = True)
            self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_freq, unit='rad', save_timestamp = True)

            if self.log_function != None:   #use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i]))

            if self._nop < 10:
                """creates view: plot middle point vs x-parameter, for qubit measurements"""
                self._data_amp_mid = self._data_file.add_value_vector('amplitude_midpoint', unit = 'arb. unit', x = self._data_x, save_timestamp = True)
                self._data_pha_mid = self._data_file.add_value_vector('phase_midpoint', unit = 'rad', x = self._data_x, save_timestamp = True)
                #self._view = self._data_file.add_view("amplitude vs. " + self.x_coordname, x = self._data_x, y = self._data_amp[self._nop/2])

        if self._scan_3D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_y = self._data_file.add_coordinate(self.y_coordname, unit = self.y_unit)
            self._data_y.add(self.y_vec)
            
            if self._nop == 0:   #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online.
                self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_y,  unit = 'arb. unit',   save_timestamp = False)
                self._data_pha = self._data_file.add_value_matrix('phase',     x = self._data_x, y = self._data_y,  unit = 'rad', save_timestamp = False)
            else:
                self._data_amp = self._data_file.add_value_box('amplitude', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'arb. unit', save_timestamp = False)
                self._data_pha = self._data_file.add_value_box('phase', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'rad', save_timestamp = False)
                
            if self.log_function != None:   #use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i]))

        if self._scan_time:
            self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz')
            self._data_freq.add([self.vna.get_centerfreq()])
            
            self._data_time = self._data_file.add_coordinate('time', unit = 's')
            self._data_time.add(np.arange(0,self._nop,1)*self.vna.get_sweeptime()/(self._nop-1))
            
            self._data_x = self._data_file.add_coordinate('trace_number', unit = '')
            self._data_x.add(np.arange(0, self.number_of_timetraces, 1))
            
            self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_time, unit = 'lin. mag.', save_timestamp = False)
            self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_time, unit = 'rad.', save_timestamp = False)
                    
        if self.comment:
            self._data_file.add_comment(self.comment)
            
        if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
            self._qvk_process.terminate() #terminate an old qviewkit instance

    def _write_settings_dataset(self):
        self._settings = self._data_file.add_textlist('settings')
        settings = waf.get_instrument_settings(self._data_file.get_filepath())
        self._settings.append(settings)

    def measure_1D(self, rescan = True, web_visible = True):
        '''
        measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. 
                If False, it will directly take the data from the VNA without waiting.
        '''
        self._scan_1D = True
        self._scan_2D = False
        self._scan_3D = False
        self._scan_time = False
        
        self._measurement_object.measurement_func = 'measure_1D'
        self._measurement_object.x_axis = 'frequency'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = 'VNA_tracedata'
        self._file_name = self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name
        self._prepare_measurement_vna()
        self._prepare_measurement_file()

        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath()) 
        print 'recording trace...'
        sys.stdout.flush()

        qt.mstart()
        if rescan:
            if self.averaging_start_ready:
                self.vna.start_measurement()
                ti = time()
                if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime())
                qt.msleep(.2)
                while not self.vna.ready():
                    if time()-ti > self.vna.get_sweeptime(query=False):
                        if self.progress_bar: self._p.iterate()
                        ti = time()
                    qt.msleep(.2)
                if self.progress_bar:
                    while self._p.progr < self._p.max_it:
                        self._p.iterate()
            else:
                self.vna.avg_clear()
                if self.vna.get_averages() == 1 or self.vna.get_Average() == False:   #no averaging
                    if self.progress_bar:self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime())
                    qt.msleep(self.vna.get_sweeptime())      #wait single sweep
                    if self.progress_bar: self._p.iterate()
                else:   #with averaging
                    if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime())
                    if "avg_status" in self.vna.get_function_names():
                        for a in range(self.vna.get_averages()):
                            while self.vna.avg_status() <= a:
                                qt.msleep(.2) #maybe one would like to adjust this at a later point
                            if self.progress_bar: self._p.iterate()
                    else: #old style
                        for a in range(self.vna.get_averages()):
                            qt.msleep(self.vna.get_sweeptime())      #wait single sweep time
                            if self.progress_bar: self._p.iterate()

        data_amp, data_pha = self.vna.get_tracedata()
        data_real, data_imag = self.vna.get_tracedata('RealImag')

        self._data_amp.append(data_amp)
        self._data_pha.append(data_pha)
        self._data_real.append(data_real)
        self._data_imag.append(data_imag)
        if self._fit_resonator:
            self._do_fit_resonator()

        qt.mend()
        self._end_measurement()

    def measure_2D(self, web_visible = True):
        '''
        measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        for all parameters x_vec in x_obj
        '''

        if not self.x_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = True
        self._scan_3D = False
        self._scan_time = False
        
        self._measurement_object.measurement_func = 'measure_2D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = 'frequency'
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname
        self._file_name = '2D_' + self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar: self._p = Progress_Bar(len(self.x_vec),'2D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages())

        self._prepare_measurement_vna()
        self._prepare_measurement_file()

        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self._nop < 10:
            if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude_midpoint', 'phase_midpoint'])
        else:
            if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath())
        self._measure()


    def measure_3D(self, web_visible = True):
        '''
        measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured.

        optional: measure method to perform the measurement according to landscape, if set
        self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s)
        note: make sure to have properly set x,y vectors before generating traces
        '''
        if not self.x_set_obj or not self.y_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = False
        self._scan_3D = True
        self._scan_time = False
        
        self._measurement_object.measurement_func = 'measure_3D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = self.y_coordname
        self._measurement_object.z_axis = 'frequency'
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname + ', ' + self.y_coordname
        self._file_name = '3D_' + self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar: self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),'3D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages())

        self._prepare_measurement_vna()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        """only middle point in freq array is plotted vs x and y"""
        if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath())

        if self.landscape:
            self.center_freqs = np.array(self.landscape).T
        else:
            self.center_freqs = []     #load default sequence
            for i in range(len(self.x_vec)):
                self.center_freqs.append([0])

        self._measure()
        
        
    def measure_timetrace(self, web_visible = True):
        '''
        measure method to record a single VNA timetrace, this only makes sense when span is set to 0 Hz!,
        tested only with KEYSIGHT E5071C ENA and its corresponding qkit driver
        LGruenhaupt 11/2016
        '''
        if self.vna.get_span() > 0: 
            print 'VNA span not set to 0 Hz... aborting'
            return
            
        self._scan_1D = False
        self._scan_2D = False
        self._scan_3D = False
        self._scan_time = True
        
        self._measurement_object.measurement_func = 'measure_timetrace'
        self._measurement_object.x_axis = 'time'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible
        
        if not self.dirname:
            self.dirname = 'VNA_timetrace'
        self._file_name = self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name
        
        self.x_vec = np.arange(0,self.number_of_timetraces,1)
        
        self._prepare_measurement_vna()
        self._prepare_measurement_file()
        
        if self.progress_bar: self._p = Progress_Bar(self.number_of_timetraces,'VNA timetrace '+self.dirname,self.vna.get_sweeptime_averages())
        
        print 'recording timetrace(s)...'
        sys.stdout.flush()

        qt.mstart()
        try:
            """
            loop: x_obj with parameters from x_vec
            """

            for i, x in enumerate(self.x_vec):
                
                if self.log_function != None:
                    for i,f in enumerate(self.log_function):
                        self._log_value[i].append(float(f()))
                        
                if self.averaging_start_ready: #LG 11/2016
                    self.vna.start_measurement()
                    ti = time() #changed from time.time() to time() - LGruenhaupt OCT_2016
                    tp = time() #added to enable use of progress bar
                    #self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime_averages()) moved to line 303
                    
                    ''' This is to prevent the vna.ready() function from timing out. LG NOV/16 '''
                    if self.vna.get_Average():
                        print 'this function only makes sense without averaging'
                        qt.mend()
                        self._end_measuremt()
                    else:
                        #self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime())
                        qt.msleep(self.vna.get_sweeptime())
                        #self._p.iterate()
                        
                        while not self.vna.ready(): qt.msleep(.2) #this is just to check if the measurement has finished
                        
                        data_amp, data_pha = self.vna.get_tracedata()
                        self._data_amp.append(data_amp)
                        self._data_pha.append(data_pha)
                qt.msleep()       
                if self.progress_bar:
                    self._p.iterate()
                
                        
                else: 
                    print 'not implemented for this VNA, only works with Keysight ENA 5071C'
                    qt.mend()
                    self._end_measurement()
                    
        except Exception as e:
            print e.__doc__
            print e.message        
        finally:
            self._end_measurement()
            qt.mend()

    def _measure(self):
        '''
        measures and plots the data depending on the measurement type.
        the measurement loops feature the setting of the objects and saving the data in the .h5 file.
        '''
        qt.mstart()
        try:
            """
            loop: x_obj with parameters from x_vec
            """
            for ix, x in enumerate(self.x_vec):
                self.x_set_obj(x)
                sleep(self.tdx)
                
                if self.log_function != None:
                    for i,f in enumerate(self.log_function):
                        self._log_value[i].append(float(f()))

                if self._scan_3D:
                    for y in self.y_vec:
                        """
                        loop: y_obj with parameters from y_vec (only 3D measurement)
                        """
                        if (np.min(np.abs(self.center_freqs[ix]-y*np.ones(len(self.center_freqs[ix])))) > self.span/2.) and self.landscape:    #if point is not of interest (not close to one of the functions)
                            data_amp = np.zeros(int(self._nop))
                            data_pha = np.zeros(int(self._nop))      #fill with zeros
                        else:
                            self.y_set_obj(y)
                            sleep(self.tdy)
                            if self.averaging_start_ready:
                                self.vna.start_measurement()
                                qt.msleep(.2) #just to make sure, the ready command does not *still* show ready
                                while not self.vna.ready():
                                    qt.msleep(.2)
                            else:
                                self.vna.avg_clear()
                                qt.msleep(self._sweeptime_averages)
                                
                            #if "avg_status" in self.vna.get_function_names():
                            #       while self.vna.avg_status() < self.vna.get_averages():
                            #            qt.msleep(.2) #maybe one would like to adjust this at a later point
                            
                            """ measurement """
                            data_amp, data_pha = self.vna.get_tracedata()

                        if self._nop == 0: # this does not work yet.
                           print data_amp[0], data_amp, self._nop
                           self._data_amp.append(data_amp[0])
                           self._data_pha.append(data_pha[0])
                        else:
                           self._data_amp.append(data_amp)
                           self._data_pha.append(data_pha)
                        if self._fit_resonator:
                            self._do_fit_resonator()
                        if self.progress_bar:
                            self._p.iterate()
                        qt.msleep()
                    """
                    filling of value-box is done here.
                    after every y-loop the data is stored the next 2d structure
                    """
                    self._data_amp.next_matrix()
                    self._data_pha.next_matrix()

                if self._scan_2D:
                    if self.averaging_start_ready:
                        self.vna.start_measurement()
                        qt.msleep(.2) #just to make sure, the ready command does not *still* show ready
                        while not self.vna.ready():
                            qt.msleep(.2)
                    else:
                        self.vna.avg_clear()
                        qt.msleep(self._sweeptime_averages)
                    """ measurement """
                    data_amp, data_pha = self.vna.get_tracedata()
                    self._data_amp.append(data_amp)
                    self._data_pha.append(data_pha)
                    if self._nop < 10:
                        #print data_amp[self._nop/2]
                        self._data_amp_mid.append(float(data_amp[self._nop/2]))
                        self._data_pha_mid.append(float(data_pha[self._nop/2]))
                        
                    if self._fit_resonator:
                        self._do_fit_resonator()
                    if self.progress_bar:
                        self._p.iterate()
                    qt.msleep()
        except Exception as e:
            print e.__doc__
            print e.message        
        finally:
            self._end_measurement()
            qt.mend()

    def _end_measurement(self):
        '''
        the data file is closed and filepath is printed
        '''
        print self._data_file.get_filepath()
        #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots
        t = threading.Thread(target=qviewkit.save_plots,args=[self._data_file.get_filepath(),self._plot_comment])
        t.start()
        self._data_file.close_file()
        waf.close_log_file(self._log)
        self.dirname = None
        if self.averaging_start_ready: self.vna.post_measurement()
        

    def set_resonator_fit(self,fit_resonator=True,fit_function='',f_min=None,f_max=None):
        '''
        sets fit parameter for resonator

        fit_resonator (bool): True or False, default: True (optional)
        fit_function (string): function which will be fitted to the data (optional)
        f_min (float): lower frequency boundary for the fitting function, default: None (optional)
        f_max (float): upper frequency boundary for the fitting function, default: None (optional)
        fit types: 'lorentzian','skewed_lorentzian','circle_fit_reflection', 'circle_fit_notch','fano'
        '''
        if not fit_resonator:
            self._fit_resonator = False
            return
        self._functions = {'lorentzian':0,'skewed_lorentzian':1,'circle_fit_reflection':2,'circle_fit_notch':3,'fano':5,'all_fits':5}
        try:
            self._fit_function = self._functions[fit_function]
        except KeyError:
            logging.error('Fit function not properly set. Must be either \'lorentzian\', \'skewed_lorentzian\', \'circle_fit_reflection\', \'circle_fit_notch\', \'fano\', or \'all_fits\'.')
        else:
            self._fit_resonator = True
            self._f_min = f_min
            self._f_max = f_max

    def _do_fit_resonator(self):
        '''
        calls fit function in resonator class
        fit function is specified in self.set_fit, with boundaries f_mim and f_max
        only the last 'slice' of data is fitted, since we fit live while measuring.
        '''

        if self._fit_function == 0: #lorentzian
            self._resonator.fit_lorentzian(f_min=self._f_min, f_max = self._f_max)
        if self._fit_function == 1: #skewed_lorentzian
            self._resonator.fit_skewed_lorentzian(f_min=self._f_min, f_max = self._f_max)
        if self._fit_function == 2: #circle_reflection
            self._resonator.fit_circle(reflection = True, f_min=self._f_min, f_max = self._f_max)
        if self._fit_function == 3: #circle_notch
            self._resonator.fit_circle(notch = True, f_min=self._f_min, f_max = self._f_max)
        if self._fit_function == 4: #fano
            self._resonator.fit_fano(f_min=self._f_min, f_max = self._f_max)
        #if self._fit_function == 5: #all fits
            #self._resonator.fit_all_fits(f_min=self._f_min, f_max = self._f_max)

    def delete_fit_function(self, n = None):
        '''
        delete single fit function n (with 0 being the first one generated) or the complete landscape for n not specified
        '''

        if n:
            self.landscape = np.delete(self.landscape, n, axis=0)
        else:
            self.landscape = None

    def plot_fit_function(self, num_points = 100):
        '''
        try:
            x_coords = np.linspace(self.x_vec[0], self.x_vec[-1], num_points)
        except Exception as message:
            print 'no x axis information specified', message
            return
        '''
        if self.landscape:
            for trace in self.landscape:
                try:
                    #plt.clear()
                    plt.plot(self.x_vec, trace)
                    plt.fill_between(self.x_vec, trace+float(self.span)/2, trace-float(self.span)/2, alpha=0.5)
                except Exception:
                    print 'invalid trace...skip'
            plt.axhspan(self.y_vec[0], self.y_vec[-1], facecolor='0.5', alpha=0.5)
            plt.show()
        else:
            print 'No trace generated.'

    def set_span(self, span):
        self.span = span

    def get_span(self):
        return self.span

    def set_tdx(self, tdx):
        self.tdx = tdx

    def set_tdy(self, tdy):
        self.tdy = tdy

    def get_tdx(self):
        return self.tdx

    def get_tdy(self):
        return self.tdy

    def f_parab(self,x,a,b,c):
        return a*(x-b)**2+c

    def f_hyp(self,x,a,b,c):
        "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]"
        return np.sqrt(a*(x-b)**2+c)

    def set_plot_comment(self, comment):
        '''
        Small comment to add at the end of plot pics for more information i.e. good for wiki entries.
        '''
        self._plot_comment=comment
Ejemplo n.º 5
0
class QmQkitWrapper:
    def __init__(self):

        self.exp_name = None
        self.dirname = ""  # TODO new name
        self.comment = ""
        self.sourcecode = ""
        self.sample = None

        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'TimeDomain'
        self._measurement_object.sample = self.sample

        # data
        self.coords = {}
        self.values = {}

    # Decorator to start qkit
    def measure(func):
        def wrapper(self, *args, **kwargs):

            save = False
            if 'save' in kwargs:
                save = kwargs['save']
                kwargs.pop('save', None)

            if save:
                qkit.flow.start()

            output = func(self, *args, **kwargs)

            if save:
                if self.dirname:
                    self._file_name = 'QM_experiment_' + self.exp_name + '_' + self.dirname
                else:
                    self._file_name = 'QM_experiment_' + self.exp_name
                self._file_name = self._file_name.replace(' ', '').replace(
                    ',', '_')

                # Save function arguments and qm program
                astring = ""
                for value in args:
                    astring += "{}, ".format(value)

                kstring = ""
                for key, value in kwargs.items():
                    kstring += "{} = {}, ".format(key, value)

                self.sourcecode = astring + kstring + "\n\n\n" + inspect.getsource(
                    func)

                self._prepare_measurement_file()
                self.store_data()

                qkit.flow.end()

                self.close_files()

                print('Measurement complete: {:s}'.format(
                    self._data_file.get_filepath()))

            else:
                print('Measurement complete')

            return output

        return wrapper

    def _prepare_measurement_file(self):
        '''
        creates the output .h5-file with distinct dataset structures for each measurement type.
        at this point all measurement parameters are known and put in the output file
        '''

        self._data_file = hdf.Data(name=self._file_name, mode='a')
        self._measurement_object.uuid = self._data_file._uuid
        self._measurement_object.hdf_relpath = self._data_file._relpath
        self._measurement_object.instruments = qkit.instruments.get_instrument_names(
        )

        self._measurement_object.save()
        self._mo = self._data_file.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        # instrument settings and logfile
        self._settings = self._data_file.add_textlist('settings')
        settings = waf.get_instrument_settings(self._data_file.get_filepath())
        self._settings.append(settings)

        self._log_file = waf.open_log_file(self._data_file.get_filepath())

    def close_files(self):

        # save plots and close files
        # t = threading.Thread(target=qviewkit.save_plots, args=[self._data_file.get_filepath()])
        # t.start()

        self._data_file.flush()
        self._data_file.close_file()
        waf.close_log_file(self._log_file)

        # TODO open qkit
        # if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
        #    self._qvk_process.terminate()  # terminate an old qviewkit instance

    def store_data(self):
        """

        """
        coord_dic = {}

        for key in self.coords:
            coord_vec = self.coords[key][0]
            unit = self.coords[key][1]

            coords_file = self._data_file.add_coordinate(key, unit=unit)
            coords_file.add(coord_vec)
            coord_dic[key] = coords_file

        for key in self.values:
            values = self.values[key][0]
            coord_key_list = self.values[key][1]
            unit = self.values[key][2]

            if len(coord_key_list) == 1:
                value_file = self._data_file.add_value_vector(
                    key, x=coord_dic[coord_key_list[0]], unit=unit)
                value_file.append(values)
            elif len(coord_key_list) == 2:
                value_file = self._data_file.add_value_matrix(
                    key,
                    x=coord_dic[coord_key_list[0]],
                    y=coord_dic[coord_key_list[1]],
                    unit=unit)
                # file initialization - workaround
                value_file.append(values[0, :])
            elif len(coord_key_list) == 3:
                value_file = self._data_file.add_value_box(
                    key,
                    x=coord_dic[coord_key_list[0]],
                    y=coord_dic[coord_key_list[1]],
                    z=coord_dic[coord_key_list[2]],
                    unit=unit)
                # file initialization - workaround
                value_file.append(values[0, 0, :])

            value_file.ds.resize(values.shape)
            value_file.ds[:] = values

        # source code
        sourcecode_file = self._data_file.add_textlist('sourcecode')
        sourcecode_file.append(self.sourcecode)

        # comment
        if self.comment:
            self._data_file.add_comment(self.comment)
Ejemplo n.º 6
0
class Measure_td(object):
    '''
    useage:

    m = Measure_td()

    m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current (mA)',coil.set_current)
    m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency (Hz)',mw_src1.set_frequency)

    m.measure_XX()
    Generally, we want to use
    ReadoutTrace = True -> if we want to record the readout pulse or
    AWGTrace = True -> if we have a N different time steps in the AWG (Not used, this is done via the mode variable now.)

    ToDO (S1, 09/2017):
        - Include LogFunctions
        - Include 2D_AWG with pre-averaging
        - Check multi-tone readout
    '''
    def __init__(self, sample, readout=None):
        self.sample = sample
        if readout is None:
            self.readout = sample.readout
        else:
            self.readout = readout

        self.comment = None
        self.mode = None
        self.x_set_obj = None
        self.y_set_obj = None
        self.z_set_obj = None

        self.dirname = None
        self.data_dir = None
        self.plotSuffix = ''
        self.hold = False

        self.show_progress_bar = True

        self.ReadoutTrace = False

        self.open_qviewkit = True
        self.create_averaged_data = False

        self.qviewkit_singleInstance = True
        self._qvk_process = False
        self._plot_comment = ''
        self.multiplex_attribute = "readout_pulse_frequency"
        self.multiplex_unit = "Hz"
        self.init = iniTD(sample)

        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'timedomain'
        self._measurement_object.sample = self.sample

        self.state_0_cal = None
        self.state_1_cal = None

    def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit=None):
        self.x_vec = x_vec
        self.x_coordname = x_coordname
        self.x_set_obj = x_set_obj
        if x_unit is None:
            logging.warning(__name__ + ': Unit of the x-axis is not set.')
            self.x_unit = ''
        else:
            self.x_unit = x_unit

    def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit=None):
        self.y_vec = y_vec
        self.y_coordname = y_coordname
        self.y_set_obj = y_set_obj
        if y_unit is None:
            logging.warning(__name__ + ': Unit of the y-axis is not set.')
            self.y_unit = ''
        else:
            self.y_unit = y_unit

    def set_z_parameters(self, z_vec, z_coordname, z_set_obj, z_unit=None):
        self.z_vec = z_vec
        self.z_coordname = z_coordname
        self.z_set_obj = z_set_obj
        if z_unit is None:
            logging.warning(__name__ + ': Unit of the z-axis is not set.')
            self.z_unit = ''
        else:
            self.z_unit = z_unit

    def measure_1D(self):
        if self.x_set_obj is None:
            raise ValueError('x-axes parameters not properly set')

        self.mode = 1  # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG, 4:3D_AWG
        self._prepare_measurement_file()

        if self.show_progress_bar:
            p = Progress_Bar(len(self.x_vec), name=self.dirname)
        try:
            # measurement loop
            for x in self.x_vec:
                self.x_set_obj(x)
                qkit.flow.sleep()
                self._append_data()
                if self.show_progress_bar: p.iterate()
        finally:
            self._end_measurement()

    def measure_2D(self):
        if self.x_set_obj is None or self.y_set_obj is None:
            raise ValueError('Axes parameters not properly set')
        if self.ReadoutTrace:
            raise ValueError(
                'ReadoutTrace is currently not supported for 2D measurements')

        self.mode = 2  # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG, 4:3D_AWG
        self._prepare_measurement_file()

        if self.show_progress_bar:
            p = Progress_Bar(len(self.x_vec) * len(self.y_vec),
                             name=self.dirname)
        try:
            # measurement loop
            for x in self.x_vec:
                self.x_set_obj(x)
                for y in self.y_vec:
                    qkit.flow.sleep()
                    self.y_set_obj(y)
                    qkit.flow.sleep()
                    self._append_data()
                    if self.show_progress_bar: p.iterate()
                for i in range(self.ndev):
                    self._hdf_amp[i].next_matrix()
                    self._hdf_pha[i].next_matrix()
        finally:
            self._end_measurement()

    def measure_1D_AWG(self, iterations=100):
        '''
        use AWG sequence for x_vec, averaging over iterations
        '''
        self.y_vec = range(iterations)
        self.y_coordname = 'iteration'
        self.y_set_obj = lambda y: True
        self.y_unit = ''
        self.create_averaged_data = True
        self.avg_complex_sum = np.zeros_like(self.x_vec)
        try:
            return self.measure_2D_AWG(iterations=1)
        finally:
            self.create_averaged_data = False  # This is ALWAYS done after the return! Looks strange and it really is, but it works.

    def measure_2D_AWG(self, iterations=1):
        '''
        x_vec is sequence in AWG
        '''

        if self.y_set_obj is None:
            raise ValueError('y-axes parameters not properly set')

        qkit.flow.sleep(
        )  # if stop button was pressed by now, abort without creating data files

        if iterations > 1:
            self.z_vec = range(iterations)
            self.z_coordname = 'iteration'
            self.z_set_obj = lambda z: True
            self.z_unit = ''

            self.measure_3D_AWG()

            # For 3D measurements with iterations, averages are only created at the end to get a consistent averaging base.
            hdf_file = hdf.Data(self._hdf.get_filepath())
            for j in range(self.ndev):
                amp = np.array(hdf_file["/entry/data0/amplitude_%i" % j])
                pha = np.array(hdf_file["/entry/data0/phase_%i" % j])
                amp_avg = sum(amp[i] for i in range(iterations)) / iterations
                pha_avg = sum(pha[i] for i in range(iterations)) / iterations
                hdf_amp_avg = hdf_file.add_value_matrix('amplitude_avg_%i' % i,
                                                        x=self._hdf_y,
                                                        y=self._hdf_x,
                                                        unit='a.u.')
                hdf_pha_avg = hdf_file.add_value_matrix('phase_avg_%i' % i,
                                                        x=self._hdf_y,
                                                        y=self._hdf_x,
                                                        unit='rad')

                for i in range(len(self.y_vec)):
                    hdf_amp_avg.append(amp_avg[i])
                    hdf_pha_avg.append(pha_avg[i])
            hdf_file.close_file()

        else:
            self.mode = 3  # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG, 4:3D_AWG
            self._prepare_measurement_file()
            # if self.ndev > 1: raise ValueError('Multiplexed readout is currently not supported for 2D measurements')
            if self.show_progress_bar:
                p = Progress_Bar(len(self.y_vec), name=self.dirname)
            try:
                # measurement loop
                for it in range(len(self.y_vec)):
                    qkit.flow.sleep(
                    )  # better done during measurement (waiting for trigger)
                    self.y_set_obj(self.y_vec[it])
                    self._append_data(iteration=it)
                    if self.show_progress_bar: p.iterate()
            finally:
                self._end_measurement()

    def measure_3D_AWG(self):
        '''
        x_vec is sequence in AWG
        '''

        if self.z_set_obj is None or self.y_set_obj is None:
            raise ValueError('x-axes parameters not properly set')
        if self.ReadoutTrace:
            raise ValueError(
                'ReadoutTrace is currently not supported for 3D_AWG measurements'
            )

        self.mode = 4  # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG, 4:3D_AWG
        self._prepare_measurement_file()

        if self.show_progress_bar:
            p = Progress_Bar(len(self.y_vec) * len(self.z_vec),
                             name=self.dirname)
        try:
            # measurement loop
            for z in self.z_vec:
                self.z_set_obj(z)
                for y in self.y_vec:
                    qkit.flow.sleep()
                    self.y_set_obj(y)
                    qkit.flow.sleep()
                    self._append_data()
                    if self.show_progress_bar: p.iterate()
                for i in range(self.ndev):
                    self._hdf_amp[i].next_matrix()
                    self._hdf_pha[i].next_matrix()
        finally:
            self._end_measurement()

    def measure_1D_ddc_time_trace(self):
        """
        measures the time evolution of your transmission / reflection
        in your network by performing a digital down conversion
        :return: None
        """
        time_end = float(self.sample.mspec.get_samples()
                         ) / self.sample.mspec.get_samplerate()
        time_array = np.linspace(0, time_end, self.sample.mspec.get_samples())
        self.set_x_parameters(time_array, 'time', True, 'sec')
        self.mode = 1  # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG
        self._prepare_measurement_file()
        try:
            qkit.flow.sleep()
            self._append_data(ddc=True)
        finally:
            self._end_measurement()

    def measure_2D_ddc_time_trace(self):
        """
        Performs a digital down conversion for exactly one value in your awg sequence. But you can sweep other
        parameters, such as mw-power or so.
        :return:
        """
        if self.y_set_obj is None:
            raise ValueError('y-axes parameters not properly set')
        time_end = float(self.sample.mspec.get_samples()
                         ) / self.sample.mspec.get_samplerate()
        time_array = np.linspace(0, time_end, self.sample.mspec.get_samples())
        self.set_x_parameters(time_array, 'time', True, 'sec')

        self.mode = 2  # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG
        self._prepare_measurement_file()

        if self.show_progress_bar:
            p = Progress_Bar(len(self.y_vec), name=self.dirname)
        try:
            for y in self.y_vec:
                qkit.flow.sleep()
                self.y_set_obj(y)
                qkit.flow.sleep()
                self._append_data(ddc=True)
                if self.show_progress_bar: p.iterate()
        finally:
            self._end_measurement()

    def measure_1D_awg_ddc_timetrace(self):
        """
        Performs a digital down conversion of your readout trace for every value in your y-vector,
        whereas y_vec should be awg sequence. This function is useful for magnon cavity experiments.
        x-vec is automatically set by acquisition window.
        (Also, all data are there at once)
        :return:
        """
        if self.y_vec is None:
            raise ValueError('y-axes parameters not properly set')
        time_end = float(self.sample.mspec.get_samples()
                         ) / self.sample.mspec.get_samplerate()
        time_array = np.linspace(0, time_end, self.sample.mspec.get_samples())
        self.set_x_parameters(time_array, 'time', True, 'sec')

        self.mode = 2  # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG
        self._prepare_measurement_file()

        try:
            qkit.flow.sleep()
            self._append_data(ddc=True)
        finally:
            self._end_measurement()

    def measure_1D_awg_1D_ddc_timetrace(self):
        """
        Performs a digital down conversion of your readout trace for every value in your y-vector
        while also sweeping another parameter. y_vec should be awg sequence and z_vec/obj the sweeping parameter.
        x-vec is automatically set by acquisition window.
        :return:
        """
        if (self.y_vec is None) or (self.z_set_obj is None):
            raise ValueError('Axes parameters not properly set')
        time_end = float(self.sample.mspec.get_samples()
                         ) / self.sample.mspec.get_samplerate()
        time_array = np.linspace(0, time_end, self.sample.mspec.get_samples())
        self.set_x_parameters(time_array, 'time', True, 'sec')
        self.mode = 4
        self._prepare_measurement_file()
        try:
            for z in self.z_vec:
                self.z_set_obj(z)
                qkit.flow.sleep()
                self._append_data(ddc=True)
                for i in range(self.ndev):
                    self._hdf_amp[i].next_matrix()
                    self._hdf_pha[i].next_matrix()
                if self.ReadoutTrace:
                    self._hdf_I.next_matrix()
                    self._hdf_Q.next_matrix()
        finally:
            self._end_measurement()

    def _prepare_measurement_file(self):
        qkit.flow.start()
        if self.dirname is None:
            self.dirname = self.x_coordname

        self.ndev = len(self.readout.get_tone_freq(
        ))  # returns array of readout freqs (=1 for non-multiplexed readout)

        self._hdf = hdf.Data(name=self.dirname, mode='a')
        self._hdf_x = self._hdf.add_coordinate(self.x_coordname,
                                               unit=self.x_unit)
        self._hdf_x.add(self.x_vec)

        self._measurement_object.uuid = self._hdf._uuid
        self._measurement_object.hdf_relpath = self._hdf._relpath
        self._measurement_object.instruments = qkit.instruments.get_instrument_names(
        )

        self._measurement_object.save()
        self._mo = self._hdf.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        self._settings = self._hdf.add_textlist('settings')
        settings = waf.get_instrument_settings(self._hdf.get_filepath())
        self._settings.append(settings)

        self._log = waf.open_log_file(self._hdf.get_filepath())

        self._hdf_readout_frequencies = self._hdf.add_coordinate(
            self.multiplex_attribute, unit=self.multiplex_unit)
        self._hdf_readout_frequencies.add(self.readout.get_tone_freq())

        if self.state_0_cal and self.state_1_cal and self.mode > 2:
            #Adds calibration of ground and excited state of qubit for visualization of population during measurement
            self._hdf_state_0 = self._hdf.add_coordinate("State 0 Calibration")
            self._hdf_state_0.add(self.state_0_cal)

            self._hdf_state_1 = self._hdf.add_coordinate("State 1 Calibration")
            self._hdf_state_1.add(self.state_1_cal)

        if self.ReadoutTrace:
            self._hdf_TimeTraceAxis = self._hdf.add_coordinate(
                'recorded timepoint', unit='s')
            self._hdf_TimeTraceAxis.add(
                np.arange(self.sample.mspec.get_samples()) /
                self.readout.get_adc_clock())

        if self.mode == 1:  # 1D
            self._hdf_amp = []
            self._hdf_pha = []
            for i in range(self.ndev):
                self._hdf_amp.append(
                    self._hdf.add_value_vector('amplitude_%i' % i,
                                               x=self._hdf_x,
                                               unit='a.u.'))
                self._hdf_pha.append(
                    self._hdf.add_value_vector('phase_%i' % i,
                                               x=self._hdf_x,
                                               unit='rad'))
            if self.ReadoutTrace:
                self._hdf_I = self._hdf.add_value_matrix(
                    'I_TimeTrace',
                    x=self._hdf_x,
                    y=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)
                self._hdf_Q = self._hdf.add_value_matrix(
                    'Q_TimeTrace',
                    x=self._hdf_x,
                    y=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)

        elif self.mode == 2:  # 2D
            self._hdf_y = self._hdf.add_coordinate(self.y_coordname,
                                                   unit=self.y_unit)
            self._hdf_y.add(self.y_vec)
            self._hdf_amp = []
            self._hdf_pha = []
            for i in range(self.ndev):
                self._hdf_amp.append(
                    self._hdf.add_value_matrix('amplitude_%i' % i,
                                               x=self._hdf_x,
                                               y=self._hdf_y,
                                               unit='a.u.'))
                self._hdf_pha.append(
                    self._hdf.add_value_matrix('phase_%i' % i,
                                               x=self._hdf_x,
                                               y=self._hdf_y,
                                               unit='rad'))
            if self.ReadoutTrace:
                # TODO: One dimension missing here?
                self._hdf_I = self._hdf.add_value_matrix(
                    'I_TimeTrace',
                    x=self._hdf_y,
                    y=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)
                self._hdf_Q = self._hdf.add_value_matrix(
                    'Q_TimeTrace',
                    x=self._hdf_y,
                    y=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)

        elif self.mode == 3:  # 1D_AWG/2D_AWG
            self._hdf_y = self._hdf.add_coordinate(self.y_coordname,
                                                   unit=self.y_unit)
            self._hdf_y.add(self.y_vec)
            self._hdf_amp = []
            self._hdf_pha = []
            self._hdf_p1 = []
            for i in range(self.ndev):
                self._hdf_amp.append(
                    self._hdf.add_value_matrix('amplitude_%i' % i,
                                               x=self._hdf_y,
                                               y=self._hdf_x,
                                               unit='a.u.'))
                self._hdf_pha.append(
                    self._hdf.add_value_matrix('phase_%i' % i,
                                               x=self._hdf_y,
                                               y=self._hdf_x,
                                               unit='rad'))
                if self.state_0_cal and self.state_1_cal:
                    self._hdf_p1.append(
                        self._hdf.add_value_matrix("p1_%i" % i,
                                                   x=self._hdf_y,
                                                   y=self._hdf_x,
                                                   unit=""))
            if self.ReadoutTrace:
                self._hdf_I = self._hdf.add_value_box(
                    'I_TimeTrace',
                    x=self._hdf_y,
                    y=self._hdf_x,
                    z=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)
                self._hdf_Q = self._hdf.add_value_box(
                    'Q_TimeTrace',
                    x=self._hdf_y,
                    y=self._hdf_x,
                    z=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)

        elif self.mode == 4:  # 3D_AWG
            self._hdf_y = self._hdf.add_coordinate(self.y_coordname,
                                                   unit=self.y_unit)
            self._hdf_y.add(self.y_vec)
            self._hdf_z = self._hdf.add_coordinate(self.z_coordname,
                                                   unit=self.z_unit)
            self._hdf_z.add(self.z_vec)
            self._hdf_amp = []
            self._hdf_pha = []
            for i in range(self.ndev):
                self._hdf_amp.append(
                    self._hdf.add_value_box('amplitude_%i' % i,
                                            x=self._hdf_z,
                                            y=self._hdf_y,
                                            z=self._hdf_x,
                                            unit='a.u.'))
                self._hdf_pha.append(
                    self._hdf.add_value_box('phase_%i' % i,
                                            x=self._hdf_z,
                                            y=self._hdf_y,
                                            z=self._hdf_x,
                                            unit='rad'))
                if self.state_0_cal and self.state_1_cal:
                    self._hdf_p1.append(
                        self._hdf.add_value_box("p1_%i" % i,
                                                x=self._hdf_z,
                                                y=self._hdf_y,
                                                z=self._hdf_x,
                                                unit=""))
            if self.ReadoutTrace:
                self._hdf_I = self._hdf.add_value_box(
                    'I_TimeTrace',
                    x=self._hdf_z,
                    y=self._hdf_y,
                    z=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)
                self._hdf_Q = self._hdf.add_value_box(
                    'Q_TimeTrace',
                    x=self._hdf_y,
                    y=self._hdf_y,
                    z=self._hdf_TimeTraceAxis,
                    unit='V',
                    save_timestamp=False)

        if self.create_averaged_data:
            self._hdf_amp_avg = []
            self._hdf_pha_avg = []
            for i in range(self.ndev):
                self._hdf_amp_avg.append(
                    self._hdf.add_value_vector('amplitude_avg_%i' % i,
                                               x=self._hdf_x,
                                               unit='a.u.'))
                self._hdf_pha_avg.append(
                    self._hdf.add_value_vector('phase_avg_%i' % i,
                                               x=self._hdf_x,
                                               unit='rad'))

        if self.comment:
            self._hdf.add_comment(self.comment)
        self._hdf.hf.hf.attrs['default_ds'] = ['data0/amplitude_%i' % i for i in range(min(5,self.ndev))] +\
                                              ['data0/phase_%i' % i for i in range(min(5,self.ndev))]
        if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
            self._qvk_process.terminate()  # terminate an old qviewkit instance
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(
                self._hdf.get_filepath(),
                datasets=[
                    'amplitude_%i' % i for i in range(min(5, self.ndev))
                ] + ['phase_%i' % i for i in range(min(5, self.ndev))])

        try:
            self.readout.start()
        except AttributeError:
            pass

    def _append_data(self, iteration=0, ddc=None):
        if self.ReadoutTrace:
            ampliData, phaseData, Is, Qs = self.readout.readout(timeTrace=True,
                                                                ddc=ddc)
        else:
            ampliData, phaseData = self.readout.readout(timeTrace=False,
                                                        ddc=ddc)

        if len(ampliData.shape) < 3:  # "normal" measurements
            for i in range(self.ndev):
                self._hdf_amp[i].append(np.atleast_1d(ampliData.T[i]))
                self._hdf_pha[i].append(np.atleast_1d(phaseData.T[i]))
                if self.state_0_cal and self.state_1_cal and self.mode > 2:
                    I_iter = np.atleast_1d(ampliData.T[i]) * np.sin(
                        np.atleast_1d(phaseData.T[i]))
                    Q_iter = np.atleast_1d(ampliData.T[i]) * np.cos(
                        np.atleast_1d(phaseData.T[i]))
                    self._hdf_p1[i].append(
                        ((self.state_1_cal[0][0] - self.state_0_cal[0][0]) *
                         (I_iter - self.state_0_cal[0][0]) +
                         (self.state_1_cal[1][0] - self.state_0_cal[1][0]) *
                         (Q_iter - self.state_0_cal[1][0])) /
                        ((self.state_1_cal[0][0] - self.state_0_cal[0][0])**2 +
                         (self.state_1_cal[1][0] - self.state_0_cal[1][0])**2))
            if self.ReadoutTrace:
                if self.mode < 3:  # mode 2 not yet fully supported but working for DDC timetrace experiments
                    self._hdf_I.append(Is)
                    self._hdf_Q.append(Qs)
                elif self.mode == 3:  # mode 4 not supported for 3D_awg yet
                    for ix in range(len(self.x_vec)):
                        self._hdf_I.append(Is[:, ix])
                        self._hdf_Q.append(Qs[:, ix])
                    self._hdf_I.next_matrix()
                    self._hdf_Q.next_matrix()

        else:  # for AWG DDC ReadoutTrace, all data are there at once
            for i in range(self.ndev):
                for j in range(ampliData.T.shape[2]):
                    self._hdf_amp[i].append(np.atleast_1d(ampliData.T[i, :,
                                                                      j]))
                    self._hdf_pha[i].append(np.atleast_1d(phaseData.T[i, :,
                                                                      j]))
                    if self.ReadoutTrace:
                        self._hdf_I.append(Is[:, j])
                        self._hdf_Q.append(Qs[:, j])

        if self.create_averaged_data:
            if iteration == 0:
                self.avg_complex_sum = ampliData * np.exp(1j * phaseData)
                for i in range(self.ndev):
                    self._hdf_amp_avg[i].append(np.atleast_1d(ampliData.T[i]))
                    self._hdf_pha_avg[i].append(np.atleast_1d(phaseData.T[i]))
            else:
                self.avg_complex_sum += ampliData * np.exp(1j * phaseData)
                amp_avg = np.abs(self.avg_complex_sum / (iteration + 1))
                pha_avg = np.angle(self.avg_complex_sum / (iteration + 1))
                for i in range(self.ndev):
                    self._hdf_amp_avg[i].ds.write_direct(
                        np.ascontiguousarray(np.atleast_1d(amp_avg.T[i])))
                    self._hdf_pha_avg[i].ds.write_direct(
                        np.ascontiguousarray(np.atleast_1d(pha_avg.T[i])))
                    self._hdf_pha_avg[i].ds.attrs['iteration'] = iteration + 1
                    self._hdf_amp_avg[i].ds.attrs['iteration'] = iteration + 1
                self._hdf.flush()

    def _end_measurement(self):
        try:
            self.readout.cleanup()
        except AttributeError:
            pass
        t = threading.Thread(
            target=qviewkit.save_plots,
            args=[self._hdf.get_filepath(), self._plot_comment])
        t.start()
        self._hdf.close_file()
        waf.close_log_file(self._log)
        qkit.flow.end()

    def set_plot_comment(self, comment):
        '''
        Small comment to add at the end of plot pics for more information i.e. good for wiki entries.
        '''
        self._plot_comment = comment

    def add_qubit_state_cal(self, filepath_to_0, filepath_to_1):
        state_0_data = hdf.Data(filepath_to_0)
        state_1_data = hdf.Data(filepath_to_1)

        self.state_0_cal = (state_0_data.data.amplitude_avg_0[:] *
                            np.sin(state_0_data.data.phase_avg_0[:]),
                            state_0_data.data.amplitude_avg_0[:] *
                            np.cos(state_0_data.data.phase_avg_0[:]))
        self.state_1_cal = (state_1_data.data.amplitude_avg_0[:] *
                            np.sin(state_1_data.data.phase_avg_0[:]),
                            state_1_data.data.amplitude_avg_0[:] *
                            np.cos(state_1_data.data.phase_avg_0[:]))

        state_0_data.close_file()
        state_1_data.close_file()
Ejemplo n.º 7
0
class transport(object):
    '''
    usage:

    m = spectrum(vna = vna1)

    m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA')
    m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz')

    m.gen_fit_function(...)      several times

    m.measure_XX()
    '''

    def __init__(self, IV_Device, exp_name = '', sample = None):

        self.IVD = IV_Device
        
        
        self.exp_name = exp_name
        self._sample = sample



        self.comment = ''
        self.dirname = None

        self.x_set_obj = None
        self.y_set_obj = None

        self.progress_bar = True

        self._plot_comment=""

        self.set_log_function()
        
        self.open_qviewkit = True
        self.qviewkit_singleInstance = False
        
        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'transport'
        self._measurement_object.sample = self._sample
        
        self._qvk_process = False
        
        self.number_of_timetraces = 1   #relevant in time domain mode
        
        self._web_visible = True
        
        self.sweep = self.sweeps()
        
        self._Fraunhofer = True
        
        
    def add_sweep_4quadrants(self, start, stop, step, offset=0):
        self.sweep.add_sweep(start+offset, stop+offset, step)
        self.sweep.add_sweep(stop+offset, start+offset, -step)
        self.sweep.add_sweep(start+offset, -stop+offset, -step)
        self.sweep.add_sweep(-stop+offset, start+offset, step)
        

    def set_log_function(self, func=None, name = None, unit = None, log_dtype = None):
        '''
        A function (object) can be passed to the measurement loop which is excecuted before every x iteration 
        but after executing the x_object setter in 2D measurements and before every line (but after setting 
        the x value) in 3D measurements.
        The return value of the function of type float or similar is stored in a value vector in the h5 file.

        Call without any arguments to delete all log functions. The timestamp is automatically saved.

        func: function object in list form
        name: name of logging parameter appearing in h5 file, default: 'log_param'
        unit: unit of logging parameter, default: ''
        log_dtype: h5 data type, default: 'f' (float32)
        '''

        if name == None:
            try:
                name = ['log_param']*len(func)
            except Exception:
                name = None
        if unit == None:
            try:
                unit = ['']*len(func)
            except Exception:
                unit = None
        if log_dtype == None:
            try:
                log_dtype = ['f']*len(func)
            except Exception:
                log_dtype = None

        self.log_function = []
        self.log_name = []
        self.log_unit = []
        self.log_dtype = []

        if func != None:
            for i,f in enumerate(func):
                self.log_function.append(f)
                self.log_name.append(name[i])
                self.log_unit.append(unit[i])
                self.log_dtype.append(log_dtype[i])

    def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit = ""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        x_vec (array): conains the sweeping values
        x_coordname (string)
        x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        x_unit (string): optional
        '''
        self.x_vec = x_vec
        self.x_coordname = x_coordname
        self.x_set_obj = x_set_obj
        #self.delete_fit_function()
        self.x_unit = x_unit

    def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit = ""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        y_vec (array): contains the sweeping values
        y_coordname (string)
        y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        y_unit (string): optional
        '''
        self.y_vec = y_vec
        self.y_coordname = y_coordname
        self.y_set_obj = y_set_obj
        self.delete_fit_function()
        self.y_unit = y_unit

    def set_web_visible(self, web_visible = True):
        '''
        Sets the web_visible parameter for the measurement class
        Input:
        web_visible = True (Default) | False
        '''
        self._web_visible = web_visible
        
    def set_sweep_type(self, sweep_type = 1):
        '''
        # FIXME: HR this should go into the IVD driver 
        Sets the  sweep type, in the moment only simple sweep types are defined: 
        Input:
        sweep_type:
            0: single sweep START -> END 
            1: double sweep START -> END -> START (default)
            2: triple sweep START -> END -> START -> -END
            3: quad sweep   START -> END -> START -> -END -> START
            ...
        
        '''
        # define the number of datasets for each sweep type
        self.IV_sweep_types = { 0:1 , 1:2, 2:3, 3:4 }
        self.IV_sweep_type = sweep_type

    def get_sweep_type(self):
        return self.IV_sweep_type
    def get_num_ds_from_sweep_type(self,sweep_type):
        # should be self.IVD.IV_sweep_types[sweep_type]
        return self.IV_sweep_types[sweep_type]
        
        
        
    def _prepare_measurement_IVD(self):
        '''
        all the relevant settings from the vna are updated and called
        '''

        self.IVD.get_all()
        #ttip.get_temperature() 
        # bias mode is either  current=0 or voltage=1
        self._bias_mode = self.IVD.get_bias_mode()
        
        #self._nop = self.IVD.get_nop()
        #self._sweeptime_averages = self.IVD.get_sweeptime_averages()
        #self._freqpoints = self.IVD.get_freqpoints()

        #if self.averaging_start_ready: self.vna.pre_measurement()

    def _prepare_measurement_file(self):
        '''
        creates the output .h5-file with distinct dataset structures for each measurement type.
        at this point all measurement parameters are known and put in the output file
        '''
        print ('filename '+self._file_name)
        self._data_file = hdf.Data(name=self._file_name)
        self._measurement_object.uuid = self._data_file._uuid
        self._measurement_object.hdf_relpath = self._data_file._relpath
        self._measurement_object.instruments = qt.instruments.get_instruments()

        #self._measurement_object.save()
        self._mo = self._data_file.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        # write logfile and instrument settings
        #self._write_settings_dataset()
        #self._log = waf.open_log_file(self._data_file.get_filepath())

        #if not self._scan_time:
        #    self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz')
        #    self._data_freq.add(self._freqpoints)

        self._data_I  = []
        self._data_V  = []
        #self._data_dVdI  = []
        if self._scan_1D:
            if self._bias_mode:# current bias
                #self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz')
                for st in range(self.sweep.get_nos()):
                    self._data_V.append(self._data_file.add_value_vector('V_'+str(st), unit = 'V', save_timestamp = False))
                    self._data_I.append(self._data_file.add_value_vector('I_'+str(st), x = self._data_V[st], unit = 'A', save_timestamp = False))
                    #self._data_dVdI.append(self._data_file.add_value_vector('_data_dVdI_'+str(st), x = self._data_V[st], unit = 'V/A', save_timestamp = False))
                    
                IV   = self._data_file.add_view('IV', x = self._data_V[0], y = self._data_I[0])
                #dVdI = self._data_file.add_view('dVdI', x = self._data_I[0] , y = self._data_dVdI[0])
                for i in range(1, self.sweep.get_nos()):
                    IV.add(x=self._data_V[i],y=self._data_I[i])
                    #dVdI.add(x=self._data_I[i],y=self._data_dVdI[i])
                    

        if self._scan_2D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit)
            self._data_x.add(self.x_vec)
            
            for st in range(self.sweep.get_nos()):
                self._data_I.append(self._data_file.add_value_vector('I_'+str(st), unit = 'A', save_timestamp = False))
                self._data_V.append(self._data_file.add_value_matrix('V_'+str(st), x = self._data_x, y = self._data_I[st], unit = 'V', save_timestamp = False))
                #self._data_dVdI.append(self._data_file.add_value_matrix('dVdI_'+str(st), x = self._data_x, y = self._data_I[st], unit = 'V/A', save_timestamp = False))
            
            if self._Fraunhofer:
                self._data_Ic = []
                for st in range(self.sweep.get_nos()):
                    self._data_Ic.append(self._data_file.add_value_vector('Ic_'+str(st), x = self._data_x, unit = 'A', save_timestamp = False))
                
                Fraunhofer = self._data_file.add_view('Fraunhofer', x=self._data_x, y=self._data_Ic[0])
                for i in range(1, self.sweep.get_nos()):
                    Fraunhofer.add(x=self._data_x, y=self._data_Ic[i])
                
                
            #if self.log_function != None:   #use logging
            #    self._log_value = []
            #    for i in range(len(self.log_function)):
            #        self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i]))

        if self._scan_3D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_y = self._data_file.add_coordinate(self.y_coordname, unit = self.y_unit)
            self._data_y.add(self.y_vec)
            
            if self._nop == 0:   #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online.
                self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_y,  unit = 'arb. unit',   save_timestamp = False)
                self._data_pha = self._data_file.add_value_matrix('phase',     x = self._data_x, y = self._data_y,  unit = 'rad', save_timestamp = False)
            else:
                self._data_amp = self._data_file.add_value_box('amplitude', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'arb. unit', save_timestamp = False)
                self._data_pha = self._data_file.add_value_box('phase', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'rad', save_timestamp = False)
                
            if self.log_function != None:   #use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i]))
                    
        if self.comment:
            self._data_file.add_comment(self.comment)
            
        if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
            self._qvk_process.terminate() #terminate an old qviewkit instance

    def _write_settings_dataset(self):
        self._settings = self._data_file.add_textlist('settings')
        settings = waf.get_instrument_settings(self._data_file.get_filepath())
        self._settings.append(settings)

    def _wait_progress_bar(self):
        ti = time()
        if self.progress_bar: 
            self._p = Progress_Bar(self.IVD.get_averages(),self.dirname,self.IVD.get_sweeptime())
        qt.msleep(.2)
        # wait for data
        while not self.IVD.ready():
            if time()-ti > self.IVD.get_sweeptime(query=False):
                if self.progress_bar: self._p.iterate()
                ti = time()
            qt.msleep(.2)
        
        if self.progress_bar:
            while self._p.progr < self._p.max_it:
                self._p.iterate()
                
    def measure_1D(self, sweep_type=1):
        '''
        measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. 
                If False, it will directly take the data from the VNA without waiting.
        '''
        
        self._sweep_type = sweep_type
        self._scan_1D = True
        self._scan_2D = False
        self._scan_3D = False
        self._scan_time = False
        
        self._measurement_object.measurement_func = 'measure_1D'
        self._measurement_object.x_axis = 'frequency'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = self._web_visible
        if not self.dirname:
            self.dirname = 'IVD_tracedata'
        self._file_name = self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name
            
        # prepare storage
        self._prepare_measurement_IVD()
        self._prepare_measurement_file()


        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['I_0', 'V_0'])
        print('recording trace...')
        sys.stdout.flush()

        qt.mstart()
        
        self.sweep.create_iterator()
        self.IVD.set_status(True)
        for st in range(self.sweep.get_nos()):
            #print st
            self.IVD.set_sweep_parameters(self.sweep.get_sweep())
            data_bias, data_sense = self.IVD.take_IV()
            self._data_I[st].append(data_bias)
            self._data_V[st].append(data_sense)
            #self._data_dVdI[st].append(np.gradient(self._data_V[st])/np.gradient(self._data_I[st]))
        self.IVD.set_status(False)


        qt.mend()
        self._end_measurement()




    def measure_2D(self):
        '''
        measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        for all parameters x_vec in x_obj
        '''

        if not self.x_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = True
        self._scan_3D = False
        
        
        self._measurement_object.measurement_func = 'measure_2D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = 'current'
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = self._web_visible

        if not self.dirname:
            self.dirname = self.x_coordname
        self._file_name = '2D_' + self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        #if self.progress_bar: self._p = Progress_Bar(len(self.x_vec),'2D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages())

        self._prepare_measurement_IVD()
        self._prepare_measurement_file()

        '''opens qviewkit to plot measurement, amp and pha are opened by default'''
        
        if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['I_0', 'V_0'])
        
        self._measure()
#
#
#    def measure_3D(self):
#        '''
#        measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured.
#
#        optional: measure method to perform the measurement according to landscape, if set
#        self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s)
#        note: make sure to have properly set x,y vectors before generating traces
#        '''
#        if not self.x_set_obj or not self.y_set_obj:
#            logging.error('axes parameters not properly set...aborting')
#            return
#        self._scan_1D = False
#        self._scan_2D = False
#        self._scan_3D = True
#        self._scan_time = False
#        
#        self._measurement_object.measurement_func = 'measure_3D'
#        self._measurement_object.x_axis = self.x_coordname
#        self._measurement_object.y_axis = self.y_coordname
#        self._measurement_object.z_axis = 'frequency'
#        self._measurement_object.web_visible = self._web_visible
#
#        if not self.dirname:
#            self.dirname = self.x_coordname + ', ' + self.y_coordname
#        self._file_name = '3D_' + self.dirname.replace(' ', '').replace(',','_')
#        if self.exp_name:
#            self._file_name += '_' + self.exp_name
#
#        if self.progress_bar: self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),'3D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages())
#
#        self._prepare_measurement_vna()
#        self._prepare_measurement_file()
#        '''opens qviewkit to plot measurement, amp and pha are opened by default'''
#        '''only middle point in freq array is plotted vs x and y'''
#        if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase'])
#        if self._fit_resonator:
#            self._resonator = resonator(self._data_file.get_filepath())
#
#        if self.landscape:
#            self.center_freqs = np.array(self.landscape).T
#        else:
#            self.center_freqs = []     #load default sequence
#            for i in range(len(self.x_vec)):
#                self.center_freqs.append([0])
#
#        self._measure()
  
        

    def _measure(self):
        '''
        measures and plots the data depending on the measurement type.
        the measurement loops feature the setting of the objects and saving the data in the .h5 file.
        '''
        qt.mstart()
        try:
            """
            loop: x_obj with parameters from x_vec
            """
            self.IVD.set_status(True)
            for ix, x in enumerate(self.x_vec):
                self.x_set_obj(x)
                sleep(1)
                
                #if self.log_function != None:
                #    for i,f in enumerate(self.log_function):
                #        self._log_value[i].append(float(f()))

                

                if self._scan_2D:
                    """ measurement """
                    self.sweep.create_iterator()
                    for st in range(self.sweep.get_nos()):
                        self.IVD.set_sweep_parameters(self.sweep.get_sweep())
                        data_bias, data_sense = self.IVD.take_IV(channel=1)
                        self._data_I[st].add(data_bias)
                        self._data_V[st].append(data_sense)
                        #self._data_dVdI[st].append(np.array(np.gradient(self._data_V[st]))[-1]/np.gradient(self._data_I[st]))
                        
                        if self._Fraunhofer:
                            self._IVC = IV_curve()
                            self._data_Ic[st].append(self._IVC.get_Ic(V=data_sense, I=data_bias, direction=self.IVD._direction))
                    
                    
                    #if self.progress_bar:
                    #    self._p.iterate()
                    qt.msleep()
            self.IVD.set_status(False)
        except Exception as e:
            print e.__doc__
            print e.message        
        finally:
            self._end_measurement()
            self.IVD.set_status(False, 1)
            #self.IVD.ramp_current(0, 100e-6, channel=2)
            self.IVD.set_status(False, 2)
            qt.mend()

    def _end_measurement(self):
        '''
        the data file is closed and filepath is printed
        '''
        print self._data_file.get_filepath()
        #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots
        t = threading.Thread(target=qviewkit.save_plots,args=[self._data_file.get_filepath(),self._plot_comment])
        t.start()
        self._data_file.close_file()
        #waf.close_log_file(self._log)
        self.dirname = None
        


    def set_span(self, span):
        self.span = span

    def get_span(self):
        return self.span

    def set_tdx(self, tdx):
        self.tdx = tdx

    def set_tdy(self, tdy):
        self.tdy = tdy

    def get_tdx(self):
        return self.tdx

    def get_tdy(self):
        return self.tdy

    def f_parab(self,x,a,b,c):
        return a*(x-b)**2+c

    def f_hyp(self,x,a,b,c):
        "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]"
        return np.sqrt(a*(x-b)**2+c)

    def set_plot_comment(self, comment):
        '''
        Small comment to add at the end of plot pics for more information i.e. good for wiki entries.
        '''
        self._plot_comment=comment
    
    
    class sweeps(object):
        def __init__(self, name='default'):
            self._starts = []
            self._stops  = []
            self._steps  = []
            self.create_iterator()
        
        def create_iterator(self):
            self._start_iter = iter(self._starts)
            self._stop_iter = iter(self._stops)
            self._step_iter = iter(self._steps)
            
            
        def add_sweep(self, start, stop, step):
            self._starts.append(start)
            self._stops.append(stop)
            self._steps.append(step)
            
            
        def reset_sweeps(self):
            self._starts = []
            self._stops  = []
            self._stops  = []
            
        
        def get_sweep(self):
            return (self._start_iter.next(),
                    self._stop_iter.next(),
                    self._step_iter.next())
                    
        def get_nos(self):
            return len(self._starts)
            
            
        def print_sweeps(self):
            print(self._starts, self._stops, self._steps)
            
            
Ejemplo n.º 8
0
class spectrum(object):
    '''
    usage:

    m = spectrum(vna = vna1)

    m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA')
    m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz')

    m.gen_fit_function(...)      several times

    m.measure_XX()
    '''

    def __init__(self, IV_Device, exp_name = '', sample = None):

        self.IVD = IV_Device
        
        
        self.exp_name = exp_name
        self._sample = sample



        self.comment = ''
        self.dirname = None

        self.x_set_obj = None
        self.y_set_obj = None

        self.progress_bar = True

        self._plot_comment=""

        self.set_log_function()
        
        self.open_qviewkit = True
        self.qviewkit_singleInstance = False
        
        self._measurement_object = Measurement()
        self._measurement_object.measurement_type = 'transport'
        self._measurement_object.sample = self._sample
        
        self._qvk_process = False
        
        self.number_of_timetraces = 1   #relevant in time domain mode
        
        

    def set_log_function(self, func=None, name = None, unit = None, log_dtype = None):
        '''
        A function (object) can be passed to the measurement loop which is excecuted before every x iteration 
        but after executing the x_object setter in 2D measurements and before every line (but after setting 
        the x value) in 3D measurements.
        The return value of the function of type float or similar is stored in a value vector in the h5 file.

        Call without any arguments to delete all log functions. The timestamp is automatically saved.

        func: function object in list form
        name: name of logging parameter appearing in h5 file, default: 'log_param'
        unit: unit of logging parameter, default: ''
        log_dtype: h5 data type, default: 'f' (float32)
        '''

        if name == None:
            try:
                name = ['log_param']*len(func)
            except Exception:
                name = None
        if unit == None:
            try:
                unit = ['']*len(func)
            except Exception:
                unit = None
        if log_dtype == None:
            try:
                log_dtype = ['f']*len(func)
            except Exception:
                log_dtype = None

        self.log_function = []
        self.log_name = []
        self.log_unit = []
        self.log_dtype = []

        if func != None:
            for i,f in enumerate(func):
                self.log_function.append(f)
                self.log_name.append(name[i])
                self.log_unit.append(unit[i])
                self.log_dtype.append(log_dtype[i])

    def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit = ""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        x_vec (array): conains the sweeping values
        x_coordname (string)
        x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        x_unit (string): optional
        '''
        self.x_vec = x_vec
        self.x_coordname = x_coordname
        self.x_set_obj = x_set_obj
        self.delete_fit_function()
        self.x_unit = x_unit

    def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit = ""):
        '''
        Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep.
        For every x value all y values are swept
        Input:
        y_vec (array): contains the sweeping values
        y_coordname (string)
        y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power())
        y_unit (string): optional
        '''
        self.y_vec = y_vec
        self.y_coordname = y_coordname
        self.y_set_obj = y_set_obj
        self.delete_fit_function()
        self.y_unit = y_unit

        def set_sweep_type(self, sweep_type = 1):
        '''
        Sets the  sweep type, in the moment only simple sweep types are defined: 
        Input:
        sweep_type:
            0: single sweep START -> END 
            1: double sweep START -> END -> START (default)
            2: triple sweep START -> END -> START -> -END
            3: quad sweep   START -> END -> START -> -END -> START
        
        '''
        self.IV_sweep_type = sweep_type


    def _prepare_measurement_IVD(self):
        '''
        all the relevant settings from the vna are updated and called
        '''

        self.IVD.get_all()
        #ttip.get_temperature() 
        # bias mode is either  current=0 or voltage=1
        self._bias_mode = self.IVD.get_bias_mode()
        
        self._nop = self.IVD.get_nop()
        self._sweeptime_averages = self.IVD.get_sweeptime_averages()
        #self._freqpoints = self.IVD.get_freqpoints()

        if self.averaging_start_ready: self.vna.pre_measurement()

    def _prepare_measurement_file(self):
        '''
        creates the output .h5-file with distinct dataset structures for each measurement type.
        at this point all measurement parameters are known and put in the output file
        '''

        self._data_file = hdf.Data(name=self._file_name)
        self._measurement_object.uuid = self._data_file._uuid
        self._measurement_object.hdf_relpath = self._data_file._relpath
        self._measurement_object.instruments = qt.instruments.get_instruments()

        self._measurement_object.save()
        self._mo = self._data_file.add_textlist('measurement')
        self._mo.append(self._measurement_object.get_JSON())

        # write logfile and instrument settings
        self._write_settings_dataset()
        self._log = waf.open_log_file(self._data_file.get_filepath())

        #if not self._scan_time:
        #    self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz')
        #    self._data_freq.add(self._freqpoints)

        
        if self._scan_1D:
            if self._bias_mode:# current bias
                #self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz')
                for st in range(self.sweep_type):
                    self._data_I[st] = self._data_file.add_value_vector('I_'+str(st), unit = 'A', save_timestamp = True)
                    self._data_V[st] = self._data_file.add_value_vector('V_'+str(st), x = self._data_I, unit = 'V', save_timestamp = True)
                

        if self._scan_2D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_freq, unit = 'arb. unit', save_timestamp = True)
            self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_freq, unit='rad', save_timestamp = True)

            if self.log_function != None:   #use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i]))

            if self._nop < 10:
                """creates view: plot middle point vs x-parameter, for qubit measurements"""
                self._data_amp_mid = self._data_file.add_value_vector('amplitude_midpoint', unit = 'arb. unit', x = self._data_x, save_timestamp = True)
                self._data_pha_mid = self._data_file.add_value_vector('phase_midpoint', unit = 'rad', x = self._data_x, save_timestamp = True)
                #self._view = self._data_file.add_view("amplitude vs. " + self.x_coordname, x = self._data_x, y = self._data_amp[self._nop/2])

        if self._scan_3D:
            self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit)
            self._data_x.add(self.x_vec)
            self._data_y = self._data_file.add_coordinate(self.y_coordname, unit = self.y_unit)
            self._data_y.add(self.y_vec)
            
            if self._nop == 0:   #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online.
                self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_y,  unit = 'arb. unit',   save_timestamp = False)
                self._data_pha = self._data_file.add_value_matrix('phase',     x = self._data_x, y = self._data_y,  unit = 'rad', save_timestamp = False)
            else:
                self._data_amp = self._data_file.add_value_box('amplitude', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'arb. unit', save_timestamp = False)
                self._data_pha = self._data_file.add_value_box('phase', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'rad', save_timestamp = False)
                
            if self.log_function != None:   #use logging
                self._log_value = []
                for i in range(len(self.log_function)):
                    self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i]))

        if self._scan_time:
            self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz')
            self._data_freq.add([self.vna.get_centerfreq()])
            
            self._data_time = self._data_file.add_coordinate('time', unit = 's')
            self._data_time.add(np.arange(0,self._nop,1)*self.vna.get_sweeptime()/(self._nop-1))
            
            self._data_x = self._data_file.add_coordinate('trace_number', unit = '')
            self._data_x.add(np.arange(0, self.number_of_timetraces, 1))
            
            self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_time, unit = 'lin. mag.', save_timestamp = False)
            self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_time, unit = 'rad.', save_timestamp = False)
                    
        if self.comment:
            self._data_file.add_comment(self.comment)
            
        if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process:
            self._qvk_process.terminate() #terminate an old qviewkit instance

    def _write_settings_dataset(self):
        self._settings = self._data_file.add_textlist('settings')
        settings = waf.get_instrument_settings(self._data_file.get_filepath())
        self._settings.append(settings)

    def measure_1D(self, rescan = True, web_visible = True):
        '''
        measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. 
                If False, it will directly take the data from the VNA without waiting.
        '''
        self._scan_1D = True
        self._scan_2D = False
        self._scan_3D = False
        self._scan_time = False
        
        self._measurement_object.measurement_func = 'measure_1D'
        self._measurement_object.x_axis = 'frequency'
        self._measurement_object.y_axis = ''
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = 'IVD_tracedata'
        self._file_name = self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name
            
        # prepare storage
        self._prepare_measurement_IVD()
        self._prepare_measurement_file()


        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self.open_qviewkit:
            self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['I_0', 'V_0'])
        print('recording trace...')
        sys.stdout.flush()

        qt.mstart()
        if rescan:
            if self.averaging_start_ready:
                self.vna.start_measurement()
                ti = time()
                if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime())
                qt.msleep(.2)
                while not self.vna.ready():
                    if time()-ti > self.vna.get_sweeptime(query=False):
                        if self.progress_bar: self._p.iterate()
                        ti = time()
                    qt.msleep(.2)
                if self.progress_bar:
                    while self._p.progr < self._p.max_it:
                        self._p.iterate()
            else:
                self.vna.avg_clear()
                if self.vna.get_averages() == 1 or self.vna.get_Average() == False:   #no averaging
                    if self.progress_bar:self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime())
                    qt.msleep(self.vna.get_sweeptime())      #wait single sweep
                    if self.progress_bar: self._p.iterate()
                else:   #with averaging
                    if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime())
                    if "avg_status" in self.vna.get_function_names():
                        for a in range(self.vna.get_averages()):
                            while self.vna.avg_status() <= a:
                                qt.msleep(.2) #maybe one would like to adjust this at a later point
                            if self.progress_bar: self._p.iterate()
                    else: #old style
                        for a in range(self.vna.get_averages()):
                            qt.msleep(self.vna.get_sweeptime())      #wait single sweep time
                            if self.progress_bar: self._p.iterate()

        data_amp, data_pha = self.vna.get_tracedata()
        data_real, data_imag = self.vna.get_tracedata('RealImag')

        self._data_amp.append(data_amp)
        self._data_pha.append(data_pha)
        self._data_real.append(data_real)
        self._data_imag.append(data_imag)
        if self._fit_resonator:
            self._do_fit_resonator()

        qt.mend()
        self._end_measurement()
"""
    def measure_2D(self, web_visible = True):
        '''
        measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA
        for all parameters x_vec in x_obj
        '''

        if not self.x_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = True
        self._scan_3D = False
        self._scan_time = False
        
        self._measurement_object.measurement_func = 'measure_2D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = 'frequency'
        self._measurement_object.z_axis = ''
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname
        self._file_name = '2D_' + self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar: self._p = Progress_Bar(len(self.x_vec),'2D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages())

        self._prepare_measurement_vna()
        self._prepare_measurement_file()

        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        if self._nop < 10:
            if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude_midpoint', 'phase_midpoint'])
        else:
            if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath())
        self._measure()


    def measure_3D(self, web_visible = True):
        '''
        measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured.

        optional: measure method to perform the measurement according to landscape, if set
        self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s)
        note: make sure to have properly set x,y vectors before generating traces
        '''
        if not self.x_set_obj or not self.y_set_obj:
            logging.error('axes parameters not properly set...aborting')
            return
        self._scan_1D = False
        self._scan_2D = False
        self._scan_3D = True
        self._scan_time = False
        
        self._measurement_object.measurement_func = 'measure_3D'
        self._measurement_object.x_axis = self.x_coordname
        self._measurement_object.y_axis = self.y_coordname
        self._measurement_object.z_axis = 'frequency'
        self._measurement_object.web_visible = web_visible

        if not self.dirname:
            self.dirname = self.x_coordname + ', ' + self.y_coordname
        self._file_name = '3D_' + self.dirname.replace(' ', '').replace(',','_')
        if self.exp_name:
            self._file_name += '_' + self.exp_name

        if self.progress_bar: self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),'3D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages())

        self._prepare_measurement_vna()
        self._prepare_measurement_file()
        """opens qviewkit to plot measurement, amp and pha are opened by default"""
        """only middle point in freq array is plotted vs x and y"""
        if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase'])
        if self._fit_resonator:
            self._resonator = resonator(self._data_file.get_filepath())

        if self.landscape:
            self.center_freqs = np.array(self.landscape).T
        else:
            self.center_freqs = []     #load default sequence
            for i in range(len(self.x_vec)):
                self.center_freqs.append([0])

        self._measure()
"""        
        

    def _measure(self):
        '''
        measures and plots the data depending on the measurement type.
        the measurement loops feature the setting of the objects and saving the data in the .h5 file.
        '''
        qt.mstart()
        try:
            """
            loop: x_obj with parameters from x_vec
            """
            for ix, x in enumerate(self.x_vec):
                self.x_set_obj(x)
                sleep(self.tdx)
                
                if self.log_function != None:
                    for i,f in enumerate(self.log_function):
                        self._log_value[i].append(float(f()))

                if self._scan_3D:
                    for y in self.y_vec:
                        """
                        loop: y_obj with parameters from y_vec (only 3D measurement)
                        """
                        if (np.min(np.abs(self.center_freqs[ix]-y*np.ones(len(self.center_freqs[ix])))) > self.span/2.) and self.landscape:    #if point is not of interest (not close to one of the functions)
                            data_amp = np.zeros(int(self._nop))
                            data_pha = np.zeros(int(self._nop))      #fill with zeros
                        else:
                            self.y_set_obj(y)
                            sleep(self.tdy)
                            if self.averaging_start_ready:
                                self.vna.start_measurement()
                                qt.msleep(.2) #just to make sure, the ready command does not *still* show ready
                                while not self.vna.ready():
                                    qt.msleep(.2)
                            else:
                                self.vna.avg_clear()
                                qt.msleep(self._sweeptime_averages)
                                
                            #if "avg_status" in self.vna.get_function_names():
                            #       while self.vna.avg_status() < self.vna.get_averages():
                            #            qt.msleep(.2) #maybe one would like to adjust this at a later point
                            
                            """ measurement """
                            data_amp, data_pha = self.vna.get_tracedata()

                        if self._nop == 0: # this does not work yet.
                           print data_amp[0], data_amp, self._nop
                           self._data_amp.append(data_amp[0])
                           self._data_pha.append(data_pha[0])
                        else:
                           self._data_amp.append(data_amp)
                           self._data_pha.append(data_pha)
                        if self._fit_resonator:
                            self._do_fit_resonator()
                        if self.progress_bar:
                            self._p.iterate()
                        qt.msleep()
                    """
                    filling of value-box is done here.
                    after every y-loop the data is stored the next 2d structure
                    """
                    self._data_amp.next_matrix()
                    self._data_pha.next_matrix()

                if self._scan_2D:
                    if self.averaging_start_ready:
                        self.vna.start_measurement()
                        qt.msleep(.2) #just to make sure, the ready command does not *still* show ready
                        while not self.vna.ready():
                            qt.msleep(.2)
                    else:
                        self.vna.avg_clear()
                        qt.msleep(self._sweeptime_averages)
                    """ measurement """
                    data_amp, data_pha = self.vna.get_tracedata()
                    self._data_amp.append(data_amp)
                    self._data_pha.append(data_pha)
                    if self._nop < 10:
                        #print data_amp[self._nop/2]
                        self._data_amp_mid.append(float(data_amp[self._nop/2]))
                        self._data_pha_mid.append(float(data_pha[self._nop/2]))
                        
                    if self._fit_resonator:
                        self._do_fit_resonator()
                    if self.progress_bar:
                        self._p.iterate()
                    qt.msleep()
        except Exception as e:
            print e.__doc__
            print e.message        
        finally:
            self._end_measurement()
            qt.mend()

    def _end_measurement(self):
        '''
        the data file is closed and filepath is printed
        '''
        print self._data_file.get_filepath()
        #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots
        t = threading.Thread(target=qviewkit.save_plots,args=[self._data_file.get_filepath(),self._plot_comment])
        t.start()
        self._data_file.close_file()
        waf.close_log_file(self._log)
        self.dirname = None
        


    def set_span(self, span):
        self.span = span

    def get_span(self):
        return self.span

    def set_tdx(self, tdx):
        self.tdx = tdx

    def set_tdy(self, tdy):
        self.tdy = tdy

    def get_tdx(self):
        return self.tdx

    def get_tdy(self):
        return self.tdy

    def f_parab(self,x,a,b,c):
        return a*(x-b)**2+c

    def f_hyp(self,x,a,b,c):
        "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]"
        return np.sqrt(a*(x-b)**2+c)

    def set_plot_comment(self, comment):
        '''
        Small comment to add at the end of plot pics for more information i.e. good for wiki entries.
        '''
        self._plot_comment=comment