예제 #1
0
def plot_awg_to_plunger(result, fig=10):
    """ This function tests the analyse_awg_to_plunger function. Plotting is optional and for debugging purposes.
    
    Args:
        result:
        fig (int): index of matplotlib window

        
    """

    if not result.get('type', None) == 'awg_to_plunger':
        raise Exception('calibration result not of correct type ')

    angle = result['angle']

    ds = get_dataset(result)
    im, tr = qtt.data.dataset2image(ds)
    xscan = tr.pixel2scan(np.array([[0], [0]]))

    plt.figure(fig)
    plt.clf()
    MatPlot(ds.default_parameter_array(), num=fig)
    if angle is not None:
        rho = -(xscan[0] * np.cos(angle) - np.sin(angle) * xscan[1])

        for offset in [-20, 0, 20]:
            label = None
            if offset is 0:
                label = 'detected angle'
            qtt.pgeometry.plot2Dline(
                [np.cos(angle), -np.sin(angle), rho + offset],
                '--m',
                alpha=.6,
                label=label)
    plt.title('Detected line direction')
예제 #2
0
    def plot_waveform_verification(
        self,
        uploaded_single_waveform,
        target_single_waveform,
        waveform_difference,
        channel,
        step=1,
        t_start=None,
        t_stop=None,
        **kwargs,
    ):
        sample_rate = self.instrument.channels[channel].sample_rate()

        start_idx = int(t_start * sample_rate) if t_start is not None else None
        stop_idx = int(t_stop * sample_rate) if t_stop is not None else None
        idxs = slice(start_idx, stop_idx, step)

        arrays = [
            uploaded_single_waveform[idxs],
            target_single_waveform[idxs],
            waveform_difference[idxs],
        ]

        points = len(arrays[0])
        assert points < 2e6, (
            f"Points {points:.0f} exceeds max 2M, "
            f"please increase step to at least {2e6 // points+1}")
        if start_idx is None:
            start_idx = 0

        t_list = (start_idx + np.arange(points) * step) / sample_rate
        plot = MatPlot(arrays, x=t_list * 1e3)
        plot[0].legend(["Uploaded", "Target", "verification"])
        plot[0].set_xlabel("Time (ms)")
        plot[0].set_ylabel("Amplitude (V)")
예제 #3
0
def plot_anticrossing(ds, afit, fig=100, linewidth=2):
    """ Plot fitted anti-crossing on dataset

    Args:
        afit (dict): fit data from fit_anticrossing
        ds (None or DataSet): dataset to show
        fig (int): index of matplotlib window
        linewidth (integer): plot linewidth, default = 2

    Returns:
        -

    """
    fitpoints = afit['fitpoints']
    plt.figure(fig)
    plt.clf()

    if ds is not None:
        MatPlot(ds.default_parameter_array('diff_dir_g'), num=fig)
    cc = fitpoints['centre']
    plt.plot(cc[0], cc[1], '.m', markersize=12, label='fit centre')

    lp = fitpoints['left_point']
    hp = fitpoints['right_point']
    op = fitpoints['outer_points'].T
    ip = fitpoints['inner_points'].T
    plt.plot([float(lp[0]), float(hp[0])],
             [float(lp[1]), float(hp[1])],
             '.--m',
             linewidth=linewidth,
             markersize=10,
             label='transition line')

    for ii in range(4):
        if ii == 0:
            lbl = 'electron line'
        else:
            lbl = None
        plt.plot([op[ii, 0], ip[ii, 0]], [op[ii, 1], ip[ii, 1]],
                 '.-',
                 linewidth=linewidth,
                 color=[0, .7, 0],
                 label=lbl)
        qtt.pgeometry.plotLabels(
            np.array((op[ii, :] + ip[ii, :]) / 2).reshape((2, -1)), '%d' % ii)
예제 #4
0
def analyse_awg_to_plunger(result, method='hough', fig=None, verbose=1):
    """ Determine the awg_to_plunger ratio from a scan result
    
    Args:
        result: (qcodes.DataSet) result of measure_awg_to_plunger
        method: (str) image processing transform method, only hough supported
        fig: (str, int or None) figure number or name, if None no plot is made
        
    Returns:
        result:
    
    """
    assert (result.get('type') == 'awg_to_plunger')
    ds = get_dataset(result)

    if method == 'hough':
        import cv2
        im, tr = qtt.data.dataset2image(ds)
        imextent = tr.scan_image_extent()
        istep = tr.istep()
        ims, r = qtt.algorithms.images.straightenImage(im,
                                                       imextent,
                                                       mvx=istep,
                                                       mvy=None)
        H = r[4]

        gray = qtt.pgeometry.scaleImage(ims)
        edges = cv2.Canny(gray, 50, 150, apertureSize=3)

        lines = cv2.HoughLines(edges, 1, np.pi / 180, int(gray.shape[0] * .8))

        if lines is None:
            angle_pixel = None
            angle = None
            xscan = None
        else:
            a = lines[:, 0, 1]
            angle_pixel = np.percentile(a, 50) + 0 * np.pi / 2

            fac = 2

            xpix = np.array(
                [[0, 0],
                 [-fac * np.sin(angle_pixel), fac * np.cos(angle_pixel)]]).T
            tmp = qtt.pgeometry.projectiveTransformation(
                np.linalg.inv(H), xpix)
            xscan = tr.pixel2scan(tmp)

            #p0=tr.pixel2scan( np.array([[0],[0]]))

            def vec2angle(v):
                return np.arctan2(v[0], v[1])

            angle = vec2angle(xscan[:, 1] - xscan[:, 0])

    elif method == 'click':
        raise Exception('not implemented')
    else:
        raise Exception('method %s not implemented' % (method, ))

    result = copy.copy(result)
    result['_angle_pixel'] = angle_pixel
    result['angle'] = angle

    if verbose:
        if angle is None:
            print('analyse_awg_to_plunger: calculated angle: ? [deg]')
        else:
            print('analyse_awg_to_plunger: calculated angle: %.3f [deg]' %
                  np.rad2deg(angle))

    if angle is None:
        result['awg_to_plunger_correction'] = None
    else:
        scanratio = tr.istep_step() / tr.istep_sweep()
        if verbose >= 2:
            print('analyse_awg_to_plunger: scanratio: %.3f' % scanratio)
        result['awg_to_plunger_correction'] = np.tan(angle)

    if fig is not None:
        plt.figure(fig)
        plt.clf()
        MatPlot(ds.default_parameter_array(), num=fig)

        if 0:
            yy = []
            for ii in np.arange(-1, 2):
                theta = angle_pixel
                c = np.cos(theta)
                s = np.sin(theta)
                xpix = np.array([[-s * ii], [c * ii]])
                tmp = qtt.pgeometry.projectiveTransformation(
                    np.linalg.inv(H), xpix)
                xscan = tr.pixel2scan(tmp)
                yy += [xscan]

        if xscan is not None:
            v = xscan
            rho = v[0] * np.cos(angle) - np.sin(angle) * v[1]
            qtt.pgeometry.plot2Dline([np.cos(angle), -np.sin(angle), -rho],
                                     'm',
                                     label='angle')

        plt.figure(fig + 1)
        plt.clf()
        plt.imshow(gray)
        plt.axis('image')

        if angle_pixel is not None:
            for offset in [-20, 0, 20]:
                label = None
                if offset is 0:
                    label = 'detected angle'
                qtt.pgeometry.plot2Dline(
                    [np.cos(angle_pixel),
                     np.sin(angle_pixel), offset],
                    'm',
                    label=label)
        if angle is not None:
            plt.title('Detected line direction: angle %.2f' % (angle, ))

        plt.figure(fig + 2)
        plt.clf()
        plt.imshow(edges)
        plt.axis('image')
        plt.title('Detected edge points')

    return result
    alldata.write(write_metadata=True)

    return alldata


# scan!
alldata = myscan(station,
                 scanjob,
                 location=None,
                 liveplotwindow=None,
                 verbose=1)

# show results
print(alldata)

MatPlot(alldata.default_parameter_array())

#%% Scan again
elzermann_threshold.set(.96)
alldata = myscan(station,
                 scanjob,
                 location=None,
                 liveplotwindow=None,
                 verbose=1)

#%% Extra: aborting measurements
#
# Create a GUI to abort measurements. For this redis needs to be installed, see
# https://github.com/VandersypenQutech/qtt/blob/master/INSTALL.md

mc = qtt.start_measurement_control()
예제 #6
0
    def implement(self, sample_rate, plot=False, **kwargs):
        # If frequency is zero, use DC pulses instead
        if self.pulse.frequency == 0:
            DC_pulse = DCPulse(
                "DC_sine",
                t_start=self.pulse.t_start,
                t_stop=self.pulse.t_stop,
                amplitude=self.pulse.get_voltage(self.pulse.t_start),
            )
            return DCPulseImplementation.implement(pulse=DC_pulse,
                                                   sample_rate=sample_rate)
        settings = copy(self.settings)
        settings.update(**config.properties.get("sine_waveform_settings", {}))
        settings.update(**config.properties.get(
            "keysight_81180A_sine_waveform_settings", {}))
        settings.update(**kwargs)

        # Do not approximate frequency if the pulse is sufficiently short
        max_points_exact = settings.pop('max_points_exact', 4000)
        points = int(self.pulse.duration * sample_rate)
        if points > max_points_exact:
            self.results = pulse_to_waveform_sequence(
                frequency=self.pulse.frequency,
                sampling_rate=sample_rate,
                total_duration=self.pulse.duration,
                min_points=320,
                sample_points_multiple=32,
                plot=plot,
                **settings,
            )
        else:
            self.results = None

        if self.results is None:
            t_list = np.arange(self.pulse.t_start, self.pulse.t_stop,
                               1 / sample_rate)
            t_list = t_list[:len(t_list) // 32 *
                            32]  # Ensure pulse is multiple of 32
            if len(t_list) < 320:
                raise RuntimeError(
                    f"Sine waveform points {len(t_list)} is below "
                    f"minimum of 320. Increase pulse duration or "
                    f"sample rate. {self.pulse}")

            return {
                "waveform": self.pulse.get_voltage(t_list),
                "loops": 1,
                "waveform_initial": None,
                "waveform_tail": None,
            }

        optimum = self.results["optimum"]
        waveform_loops = max(optimum["repetitions"], 1)

        # Temporarily modify pulse frequency to ensure waveforms have full period
        original_frequency = self.pulse.frequency
        self.pulse.frequency = optimum["modified_frequency"]

        # Get waveform points for repeated segment
        t_list = self.pulse.t_start + np.arange(
            optimum["points"]) / sample_rate
        waveform_array = self.pulse.get_voltage(t_list)

        # Potentially include a waveform tail
        waveform_tail_pts = int(optimum["final_delay"] * sample_rate)
        # Waveform must be multiple of 32, if number of points is less than
        # this, there is no point in adding the waveform
        if waveform_tail_pts >= 32:
            if waveform_tail_pts < 320:  # Waveform must be at least 320 points
                # Find minimum number of loops of main waveform that are needed
                # to increase tail to be at least 320 points long
                subtract_loops = int(
                    np.ceil((320 - waveform_tail_pts) / optimum["points"]))
            else:
                subtract_loops = 0

            if waveform_loops - subtract_loops > 0:
                # Safe to subtract loops from the main waveform, add to this one
                waveform_loops -= subtract_loops
                waveform_tail_pts += subtract_loops * optimum["points"]

                t_list_tail = np.arange(
                    self.pulse.t_start + optimum["duration"] * waveform_loops,
                    self.pulse.t_stop,
                    1 / sample_rate,
                )
                t_list_tail = t_list_tail[:32 * (len(t_list_tail) // 32)]
                waveform_tail_array = self.pulse.get_voltage(t_list_tail)
            else:
                # Cannot subtract loops from the main waveform because then
                # the main waveform would not have any loops remaining
                waveform_tail_array = None
        else:
            waveform_tail_array = None

        # Reset pulse frequency to original
        self.pulse.frequency = original_frequency

        if plot:
            plot = MatPlot(subplots=(2, 1), figsize=(10, 6), sharex=True)

            ax = plot[0]
            t_list = np.arange(self.pulse.t_start, self.pulse.t_stop,
                               1 / sample_rate)
            voltages = self.pulse.get_voltage(t_list)
            ax.add(t_list, voltages, color="C0")

            # Add recreated sine pulse
            wf_voltages_main = np.tile(waveform_array, waveform_loops)
            wf_voltages_tail = waveform_tail_array
            wf_voltages = np.hstack((wf_voltages_main, wf_voltages_tail))
            t_stop = self.pulse.t_start + len(wf_voltages) / sample_rate
            wf_t_list = self.pulse.t_start + np.arange(
                len(wf_voltages)) / sample_rate
            ax.add(wf_t_list, wf_voltages, marker="o", ms=2, color="C1")

            # Add remaining marker values
            new_wf_idxs = np.arange(waveform_loops) * len(waveform_array)
            ax.plot(wf_t_list[new_wf_idxs],
                    wf_voltages[new_wf_idxs],
                    "o",
                    color="C2",
                    ms=4)
            wf_tail_idx = len(waveform_array) * waveform_loops
            ax.plot(wf_t_list[wf_tail_idx],
                    wf_voltages[wf_tail_idx],
                    "o",
                    color="C3",
                    ms=4)
            ax.set_ylabel("Amplitude (V)")

            ax = plot[1]
            ax.add(wf_t_list, wf_voltages - voltages)
            ax.set_ylabel("Amplitude error (V)")
            ax.set_xlabel("Time (s)")

            plot.tight_layout()

        return {
            "waveform": waveform_array,
            "loops": waveform_loops,
            "waveform_initial": None,
            "waveform_tail": waveform_tail_array,
        }
예제 #7
0
파일: analysis.py 프로젝트: MorelloLab/SilQ
def analyse_EPR(empty_traces: np.ndarray,
                plunge_traces: np.ndarray,
                read_traces: np.ndarray,
                sample_rate: float,
                t_skip: float,
                t_read: float,
                min_filter_proportion: float = 0.5,
                threshold_voltage: Union[float, None] = None,
                filter_traces=True,
                plot: bool = False):
    """ Analyse an empty-plunge-read sequence

    Args:
        empty_traces: 2D array of acquisition traces in empty (ionized) state
        plunge_traces: 2D array of acquisition traces in plunge (neutral) state
        read_traces: 2D array of acquisition traces in read state
        sample_rate: acquisition sample rate (per second).
        t_skip: initial time to skip for each trace (ms).
        t_read: duration of each trace to use for calculating up_proportion etc.
            e.g. for a long trace, you want to compare up proportion of start
            and end segments.
        min_filter_proportion: minimum proportion of traces that satisfy filter.
            If below this value, up_proportion etc. are not calculated.
        threshold_voltage: threshold voltage for a ``high`` voltage (blip).
            If not specified, ``find_high_low`` is used to determine threshold.

    Returns:
        Dict[str, float]:
        * **fidelity_empty** (float): proportion of empty traces that end
          ionized (high voltage). Traces are filtered out that do not start
          neutral (low voltage).
        * **voltage_difference_empty** (float): voltage difference between high
          and low state in empty traces
        * **fidelity_load** (float): proportion of plunge traces that end
          neutral (low voltage). Traces are filtered out that do not start
          ionized (high voltage).
        * **voltage_difference_load** (float): voltage difference between high
          and low state in plunge traces
        * **up_proportion** (float): proportion of read traces that have blips
          For each trace, only up to t_read is considered.
        * **dark_counts** (float): proportion of read traces that have dark
          counts. For each trace, only the final t_read is considered.
        * **contrast** (float): =up_proportion - dark_counts
        * **voltage_difference_read** (float): voltage difference between high
          and low state in read traces
        * **blips** (float): average blips per trace in read traces.
        * **mean_low_blip_duration** (float): average duration in low state.
        * **mean_high_blip_duration** (float): average duration in high state.
    """
    if plot is True:
        plot = MatPlot(subplots=3)
        plot[0].set_title('Empty')
        plot[1].set_title('Plunge')
        plot[2].set_title('Read long')
    elif plot is False:
        plot = [False] * 3

    # Analyse empty stage
    results_empty = analyse_traces(traces=empty_traces,
                                   sample_rate=sample_rate,
                                   filter='low' if filter_traces else None,
                                   min_filter_proportion=min_filter_proportion,
                                   threshold_voltage=threshold_voltage,
                                   t_skip=t_skip,
                                   plot=plot[0])

    # Analyse plunge stage
    results_load = analyse_traces(traces=plunge_traces,
                                  sample_rate=sample_rate,
                                  filter='high' if filter_traces else None,
                                  min_filter_proportion=min_filter_proportion,
                                  threshold_voltage=threshold_voltage,
                                  t_skip=t_skip,
                                  plot=plot[1])

    # Analyse read stage
    results_read = analyse_traces(traces=read_traces,
                                  sample_rate=sample_rate,
                                  filter='low' if filter_traces else None,
                                  min_filter_proportion=min_filter_proportion,
                                  threshold_voltage=threshold_voltage,
                                  t_skip=t_skip)
    results_read_begin = analyse_traces(
        traces=read_traces,
        sample_rate=sample_rate,
        filter='low' if filter_traces else None,
        min_filter_proportion=min_filter_proportion,
        threshold_voltage=threshold_voltage,
        t_read=t_read,
        segment='begin',
        t_skip=t_skip,
        plot=plot[2])
    results_read_end = analyse_traces(traces=read_traces,
                                      sample_rate=sample_rate,
                                      t_read=t_read,
                                      threshold_voltage=threshold_voltage,
                                      segment='end',
                                      t_skip=t_skip,
                                      plot=plot[2])

    return {
        'fidelity_empty':
        results_empty['end_high'],
        'voltage_difference_empty':
        results_empty['voltage_difference'],
        'fidelity_load':
        results_load['end_low'],
        'voltage_difference_load':
        results_load['voltage_difference'],
        'up_proportion':
        results_read_begin['up_proportion'],
        'contrast': (results_read_begin['up_proportion'] -
                     results_read_end['up_proportion']),
        'dark_counts':
        results_read_end['up_proportion'],
        'voltage_difference_read':
        results_read['voltage_difference'],
        'voltage_average_read':
        results_read['average_voltage'],
        'num_traces':
        results_read['num_traces'],
        'filtered_traces_idx':
        results_read['filtered_traces_idx'],
        'blips':
        results_read['blips'],
        'mean_low_blip_duration':
        results_read['mean_low_blip_duration'],
        'mean_high_blip_duration':
        results_read['mean_high_blip_duration']
    }
예제 #8
0
파일: analysis.py 프로젝트: MorelloLab/SilQ
def analyse_traces(traces: np.ndarray,
                   sample_rate: float,
                   filter: Union[str, None] = None,
                   min_filter_proportion: float = 0.5,
                   t_skip: float = 0,
                   t_read: Union[float, None] = None,
                   segment: str = 'begin',
                   threshold_voltage: Union[float, None] = None,
                   threshold_method: str = 'config',
                   plot: Union[bool, matplotlib.axis.Axis] = False,
                   plot_high_low: Union[bool, matplotlib.axis.Axis] = False):
    """ Analyse voltage, up proportions, and blips of acquisition traces

    Args:
        traces: 2D array of acquisition traces.
        sample_rate: acquisition sample rate (per second).
        filter: only use traces that begin in low or high voltage.
            Allowed values are ``low``, ``high`` or ``None`` (do not filter).
        min_filter_proportion: minimum proportion of traces that satisfy filter.
            If below this value, up_proportion etc. are not calculated.
        t_skip: initial time to skip for each trace (ms).
        t_read: duration of each trace to use for calculating up_proportion etc.
            e.g. for a long trace, you want to compare up proportion of start
            and end segments.
        segment: Use beginning or end of trace for ``t_read``.
            Allowed values are ``begin`` and ``end``.
        threshold_voltage: threshold voltage for a ``high`` voltage (blip).
            If not specified, ``find_high_low`` is used to determine threshold.
        threshold_method: Method used to determine the threshold voltage.
            Allowed methods are:

            * **mean**: average of high and low voltage.
            * **{n}\*std_low**: n standard deviations above mean low voltage,
              where n is a float (ignore slash in raw docstring).
            * **{n}\*std_high**: n standard deviations below mean high voltage,
              where n is a float (ignore slash in raw docstring).
            * **config**: Use threshold method provided in
              ``config.analysis.threshold_method`` (``mean`` if not specified)

        plot: Whether to plot traces with results.
            If True, will create a MatPlot object and add results.
            Can also pass a MatPlot axis, in which case that will be used.
            Each trace is preceded by a block that can be green (measured blip
            during start), red (no blip measured), or white (trace was filtered
            out).

    Returns:
        Dict[str, Any]:
        * **up_proportion** (float): proportion of traces that has a blip
        * **end_high** (float): proportion of traces that end with high voltage
        * **end_low** (float): proportion of traces that end with low voltage
        * **num_traces** (int): Number of traces that satisfy filter
        * **filtered_traces_idx** (np.ndarray): 1D bool array,
          True if that trace satisfies filter
        * **voltage_difference** (float): voltage difference between high and
          low voltages
        * **average_voltage** (float): average voltage over all traces
        * **threshold_voltage** (float): threshold voltage for counting a blip
          (high voltage). Is calculated if not provided as input arg.
        * **blips** (float): average blips per trace.
        * **mean_low_blip_duration** (float): average duration in low state
        * **mean_high_blip_duration** (float): average duration in high state

    Note:
        If no threshold voltage is provided, and no two peaks can be discerned,
            all results except average_voltage are set to an initial value
            (either 0 or undefined)
        If the filtered trace proportion is less than min_filter_proportion,
            the results ``up_proportion``, ``end_low``, ``end_high`` are set to an
            initial value
    """
    assert filter in [None, 'low',
                      'high'], 'filter must be None, `low`, or `high`'

    assert segment in ['begin',
                       'end'], 'segment must be either `begin` or `end`'

    # Initialize all results to None
    results = {
        'up_proportion': 0,
        'end_high': 0,
        'end_low': 0,
        'num_traces': 0,
        'filtered_traces_idx': None,
        'voltage_difference': None,
        'average_voltage': np.mean(traces),
        'threshold_voltage': None,
        'blips': None,
        'mean_low_blip_duration': None,
        'mean_high_blip_duration': None
    }

    # minimum trace idx to include (to discard initial capacitor spike)
    start_idx = round(t_skip * sample_rate)

    if plot is not False:  # Create plot for traces
        ax = MatPlot()[0] if plot is True else plot
        t_list = np.linspace(0, len(traces[0]) / sample_rate, len(traces[0]))
        print(ax.get_xlim())
        ax.add(traces,
               x=t_list,
               y=np.arange(len(traces), dtype=float),
               cmap='seismic')
        print(ax.get_xlim())
        # Modify x-limits to add blips information
        xlim = ax.get_xlim()
        xpadding = 0.05 * (xlim[1] - xlim[0])
        if segment == 'begin':
            xpadding_range = [-xpadding + xlim[0], xlim[0]]
            ax.set_xlim(-xpadding + xlim[0], xlim[1])
        else:
            xpadding_range = [xlim[1], xlim[1] + xpadding]
            ax.set_xlim(xlim[0], xlim[1] + xpadding)

    if threshold_voltage is None:  # Calculate threshold voltage if not provided
        # Histogram trace voltages to find two peaks corresponding to high and low
        high_low_results = find_high_low(traces[:, start_idx:],
                                         threshold_method=threshold_method,
                                         plot=plot_high_low)
        results['high_low_results'] = high_low_results
        results['voltage_difference'] = high_low_results['voltage_difference']
        # Use threshold voltage from high_low_results
        threshold_voltage = high_low_results['threshold_voltage']

        results['threshold_voltage'] = threshold_voltage

        if threshold_voltage is None:
            logger.debug('Could not determine threshold voltage')
            if plot is not False:
                ax.text(np.mean(xlim),
                        len(traces) + 0.5,
                        'Unknown threshold voltage',
                        horizontalalignment='center')
            return results
    else:
        # We don't know voltage difference since we skip a high_low measure.
        results['voltage_difference'] = None

    # Analyse blips (disabled because it's very slow)
    # blips_results = count_blips(traces=traces,
    #                             sample_rate=sample_rate,
    #                             threshold_voltage=threshold_voltage,
    #                             t_skip=t_skip)
    # results['blips'] = blips_results['blips']
    # results['mean_low_blip_duration'] = blips_results['mean_low_blip_duration']
    # results['mean_high_blip_duration'] = blips_results['mean_high_blip_duration']

    if filter == 'low':  # Filter all traces that do not start with low voltage
        filtered_traces_idx = edge_voltage(traces,
                                           edge='begin',
                                           state='low',
                                           start_idx=start_idx,
                                           threshold_voltage=threshold_voltage)
    elif filter == 'high':  # Filter all traces that do not start with high voltage
        filtered_traces_idx = edge_voltage(traces,
                                           edge='begin',
                                           state='high',
                                           start_idx=start_idx,
                                           threshold_voltage=threshold_voltage)
    else:  # Do not filter traces
        filtered_traces_idx = np.ones(len(traces), dtype=bool)

    results['filtered_traces_idx'] = filtered_traces_idx
    filtered_traces = traces[filtered_traces_idx]
    results['num_traces'] = len(filtered_traces)

    if len(filtered_traces) / len(traces) < min_filter_proportion:
        logger.debug(f'Not enough traces start {filter}')

        if plot is not False:
            ax.pcolormesh(xpadding_range,
                          np.arange(len(traces) + 1) - 0.5,
                          filtered_traces.reshape(1, -1),
                          cmap='RdYlGn')
            ax.text(
                np.mean(xlim),
                len(traces) + 0.5,
                f'filtered traces: {len(filtered_traces)} / {len(traces)} = '
                f'{len(filtered_traces) / len(traces):.2f} < {min_filter_proportion}',
                horizontalalignment='center')
        return results

    if t_read is not None:  # Only use a time segment of each trace
        read_pts = int(round(t_read * sample_rate))
        if segment == 'begin':
            segmented_filtered_traces = filtered_traces[:, :read_pts]
        else:
            segmented_filtered_traces = filtered_traces[:, -read_pts:]
    else:
        segmented_filtered_traces = filtered_traces

    # Calculate up proportion of traces
    up_proportion_idxs = find_up_proportion(
        segmented_filtered_traces,
        start_idx=start_idx,
        threshold_voltage=threshold_voltage,
        return_array=True)
    results['up_proportion'] = sum(up_proportion_idxs) / len(traces)

    # Calculate ratio of traces that end up with low voltage
    idx_end_low = edge_voltage(segmented_filtered_traces,
                               edge='end',
                               state='low',
                               threshold_voltage=threshold_voltage)
    results['end_low'] = np.sum(idx_end_low) / len(segmented_filtered_traces)

    # Calculate ratio of traces that end up with high voltage
    idx_end_high = edge_voltage(segmented_filtered_traces,
                                edge='end',
                                state='high',
                                threshold_voltage=threshold_voltage)
    results['end_high'] = np.sum(idx_end_high) / len(segmented_filtered_traces)

    if plot is not False:
        # Plot information on up proportion
        up_proportion_arr = 2 * up_proportion_idxs - 1
        up_proportion_arr[~filtered_traces_idx] = 0
        up_proportion_arr = up_proportion_arr.reshape(-1, 1)  # Make array 2D

        mesh = ax.pcolormesh(xpadding_range,
                             np.arange(len(traces) + 1) - 0.5,
                             up_proportion_arr,
                             cmap='RdYlGn')
        mesh.set_clim(-1, 1)

        # Add vertical line for t_read
        ax.vlines(t_read,
                  -0.5,
                  len(traces + 0.5),
                  lw=2,
                  linestyle='--',
                  color='orange')
        ax.text(t_read,
                len(traces) + 0.5,
                f't_read={t_read*1e3} ms',
                horizontalalignment='center',
                verticalalignment='bottom')
        ax.text(t_skip,
                len(traces) + 0.5,
                f't_skip={t_skip*1e3} ms',
                horizontalalignment='center',
                verticalalignment='bottom')

    return results
예제 #9
0
def analyse_awg_to_plunger(result, method='hough', fig=None):
    """ Determine the awg_to_plunger conversion factor from a 2D scan, two possible methods: 'hough' it fits the slope of the addition line and calculates the
        correction to the awg_to_plunger conversion factor from there. if this doesn't work for some reason, method 'click' can be used
        to find the addition lines by hand/eye

    Args:
        result (dic): result dictionary of the function measure_awg_to_plunger,
            shape: result = {'type': 'awg_to_plunger', 'awg_to_plunger': None, 'dataset': ds.location}
        method (str): either 'hough' or 'click'
        fig (int or None): determines of the analysis staps and the result is plotted

    Returns:
        result (dict): including to following entries:
            angle (float): angle in radians
            angle_degrees (float): angle in degrees
            correction of awg_to_plunger (float): correction factor
            dataset (str): location where the dataset is stored
            type(str): type of calibration, 'awg_to_plunger'

    """
    # getting the dataset from the result from the measure_awg_to_plunger function
    if result.get('type') != 'awg_to_plunger':
        raise AssertionError('not of type awg_to_plunger!')

    ds = get_dataset(result)

    # choosing a method;
    # the method 'hough' fits the addition line
    if method == 'hough':
        import cv2
        im, tr = qtt.data.dataset2image(ds)
        imextent = tr.scan_image_extent()
        istep = tr.istep()
        _, r = qtt.algorithms.images.straightenImage(
            im, imextent, mvx=istep, mvy=None)
        H = r[4]

        imc = cleanSensingImage(im, sigma=0.93, dy=0)

        imx, _ = straightenImage(imc, imextent, mvx=istep, verbose=0)

        imx = imx.astype(np.float64) * \
            (100. / np.percentile(imx, 99))  # scale image

        gray = qtt.pgeometry.scaleImage(imx)

        edges = cv2.Canny(gray, 50, 150, apertureSize=3)

        lines = cv2.HoughLines(edges, 1, np.pi / 180, int(gray.shape[0] * .5))
        if lines is None:
            angle_pixel = None
            angle = None
            xscan = None
            angle_deg = None
            correction = None
        else:

            a = lines[:, 0, 1]
            angle_pixel = np.percentile(a, 50) + 0 * np.pi / 2

            fac = 2

            xpix = np.array(
                [[0, 0], [-fac * np.sin(angle_pixel), fac * np.cos(angle_pixel)]]).T
            tmp = qtt.pgeometry.projectiveTransformation(np.linalg.inv(H), xpix)
            xscan = tr.pixel2scan(tmp)

            def vec2angle(v):
                return np.arctan2(v[0], v[1])
            angle = vec2angle(xscan[:, 1] - xscan[:, 0])
            correction = -1 / np.tan(angle)
            angle_deg = angle / (2 * np.pi) * 360

        # plotting the analysis steps of the data
        if fig is not None:
            plt.figure(fig + 1)
            plt.clf()
            plt.imshow(gray)
            plt.axis('image')

            if angle_pixel is not None:
                for offset in [-20, 0, 20]:
                    label = None
                    if offset is 0:
                        label = 'detected angle'
                    qtt.pgeometry.plot2Dline(
                        [np.cos(angle_pixel), np.sin(angle_pixel), offset], 'm', label=label)
            if angle is not None:
                plt.title('Detected line direction: angle %.2f' % (angle_deg,))

            plt.figure(fig + 2)
            plt.clf()
            plt.imshow(edges)
            plt.axis('image')
            plt.title('Detected edge points')

    # the method click relies on the user clicking two points to indicate the addition line
    elif method == 'click':
        if fig is not None:
            plt.figure(fig)
            plt.clf()
            MatPlot(ds.default_parameter_array(), num=fig)
            plt.draw()
            plt.pause(1e-3)

        print("Please click two different points on the addition line")
        offset, slope = click_line(fig=fig)

        angle_pixel = None
        angle = -np.pi / 2 - np.tanh(slope)
        correction = -1 / np.tan(angle)
        angle_deg = angle / (2 * np.pi) * 360

    else:
        raise Exception('method %s not implemented' % (method,))

    # filling the result dictionary
    result = copy.copy(result)
    result['_angle_pixel'] = angle_pixel
    result['angle'] = angle
    result['angle_degrees'] = angle_deg
    result['correction of awg_to_plunger'] = correction
    if method == 'click':
        result['slope'] = slope
        result['offset'] = offset

    # optional, plotting figures showing the analysis
    if fig is not None:
        plot_awg_to_plunger(result=result, fig=fig)

    return result