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')
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)")
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)
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()
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, }
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'] }
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
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