diff --git a/bdaq53/analysis/analysis.py b/bdaq53/analysis/analysis.py index 5205385c85523eadebf38c5e4a7ee3b7a6cdd829..d9b776ea059a7d6d0ee8b0b28bf6ba69ca555f11 100644 --- a/bdaq53/analysis/analysis.py +++ b/bdaq53/analysis/analysis.py @@ -34,7 +34,7 @@ class Analysis(object): def __init__(self, raw_data_file=None, analyzed_data_file=None, hitor_calib_file=None, store_hits=False, cluster_hits=False, analyze_tdc=False, use_tdc_trigger_dist=False, - analyze_ptot=False, align_method=0, chunk_size=1000000, **_): + align_method=0, chunk_size=1000000, **_): ''' Parameters ---------- @@ -58,9 +58,6 @@ class Analysis(object): If True use trigger distance (delay between Hitor and Trigger) in TDC word interpretation. If False use instead TDC timestamp from TDC word. Default is False. - analyze_ptot : boolean - If analyze_ptot is True, RD53B ToT words will be interpreted as PTOT words (contain precision ToT and ToA). - Only possible for RD53B. align_method : integer Methods to do event alignment 0: New event when number of event headers exceeds number of @@ -79,7 +76,6 @@ class Analysis(object): self.chunk_size = chunk_size self.align_method = align_method self.analyze_tdc = analyze_tdc - self.analyze_ptot = analyze_ptot self.use_tdc_trigger_dist = use_tdc_trigger_dist self.hitor_calib_file = hitor_calib_file @@ -96,15 +92,22 @@ class Analysis(object): self.prev_trg_number = -1 self.last_chunk = False - self._get_run_config() + # Get/Set configs, chip types, ... + self._get_configs() self._set_chip_type() self._set_chip_receiver_id() + + # After getting chip settings, tell analysis to use PToT words (contain precision ToT and ToA) + self.analyze_ptot = self.chip_settings.get('use_ptot', False) + + # Setup clusterizer self._setup_clusterizer() self.threshold_map = np.ones(shape=(self.columns, self.rows)) * -1 self.noise_map = np.ones_like(self.threshold_map) * -1 self.chi2_map = np.zeros_like(self.threshold_map) * -1 + def __enter__(self): return self @@ -112,11 +115,12 @@ class Analysis(object): if exc_type is not None: self.log.exception('Exception during analysis', exc_info=(exc_type, exc_value, traceback)) - def _get_run_config(self): + def _get_configs(self): ''' Load run config to allow analysis routines to access these info ''' with tb.open_file(self.raw_data_file, 'r') as in_file: self.run_config = au.ConfigDict(in_file.root.configuration_in.scan.run_config[:]) self.scan_config = au.ConfigDict(in_file.root.configuration_in.scan.scan_config[:]) + self.chip_settings = au.ConfigDict(in_file.root.configuration_in.chip.settings[:]) def _get_trigger_pattern(self): ''' @@ -463,7 +467,6 @@ class Analysis(object): def analyze_data(self): self.log.info('Analyzing data...') self.chunk_offset = 0 - ptot = False with tb.open_file(self.raw_data_file) as in_file: n_words = in_file.root.raw_data.shape[0] meta_data = in_file.root.meta_data[:] @@ -494,7 +497,6 @@ class Analysis(object): if len(ptot_table) > 0: ptot_table_stretched = np.array(self.stretch_ptot_data(ptot_table)) n_scan_params = np.max(ptot_table_stretched['scan_param_id']) + 1 - ptot = True except KeyError: self.log.info("No ptot_table_stretched to scan_param_id map detected. Using readout_number to map scan_param_id.") par_range = self._range_of_parameter(meta_data) @@ -699,11 +701,11 @@ class Analysis(object): complevel=5, fletcher32=False)) - if scan_id in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'autorange_threshold_scan', 'crosstalk_scan']: + if scan_id in ['threshold_scan', 'fast_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan']: n_injections = self.scan_config['n_injections'] hist_scurve = hist_occ.reshape((self.rows * self.columns, -1)) - if scan_id in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'crosstalk_scan']: + if scan_id in ['threshold_scan', 'fast_threshold_scan', 'crosstalk_scan']: scan_params = [v - self.scan_config['VCAL_MED'] for v in range(self.scan_config['VCAL_HIGH_start'], self.scan_config['VCAL_HIGH_stop'], self.scan_config['VCAL_HIGH_step'])] self.threshold_map, self.noise_map, self.chi2_map = au.fit_scurves_multithread(hist_scurve, scan_params, n_injections, optimize_fit_range=False, rows=self.rows) diff --git a/bdaq53/analysis/plotting.py b/bdaq53/analysis/plotting.py index 03cd4611003967ed20e8ca1d5dd77dd2a337f89a..91629a89cab0065f720d3989e7bd9c97fe1377b4 100644 --- a/bdaq53/analysis/plotting.py +++ b/bdaq53/analysis/plotting.py @@ -129,6 +129,7 @@ class Plotting(object): self.scan_config = au.ConfigDict(root.configuration_in.scan.scan_config[:]) self.run_config = au.ConfigDict(root.configuration_in.scan.run_config[:]) + self.chip_settings = au.ConfigDict(root.configuration_in.chip.settings[:]) self.cols = 400 self.chip_type = 'RD53A' try: @@ -152,6 +153,7 @@ class Plotting(object): self.scan_params = None self.registers = au.ConfigDict(root.configuration_in.chip.registers[:]) + self.use_ptot = self.chip_settings.get('use_ptot', False) if self.run_config['scan_id'] not in ['adc_tuning', 'dac_linearity_scan', 'seu_test', 'pixel_register_scan']: self.enable_mask = self._mask_disabled_pixels(root.configuration_in.chip.use_pixel[:], self.scan_config) @@ -171,7 +173,7 @@ class Plotting(object): self.HistRelBCID = root.HistRelBCID[:] self.HistBCIDError = root.HistBCIDError[:] self.HistTrigID = root.HistTrigID[:] - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'global_threshold_tuning', 'in_time_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan', 'merged_bumps_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'global_threshold_tuning', 'in_time_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan', 'merged_bumps_scan']: self.ThresholdMap = root.ThresholdMap[:, :] self.Chi2Map = root.Chi2Map[:, :] self.NoiseMap = root.NoiseMap[:] @@ -191,7 +193,7 @@ class Plotting(object): self.HistTotCalStd = root.HistTotCalStd[:] self.TOThist = root.TOThist[:] - if self.run_config['scan_id'] in ['hitor_calibration', 'hitor_calibration_ptot']: + if self.run_config['scan_id'] in ['hitor_calibration']: self.HistTdc = root.hist_2d_tdc_vcal[:] self.HistTotCalMean = root.hist_tot_mean[:] self.HistTotCalStd = root.hist_tot_std[:] @@ -202,7 +204,7 @@ class Plotting(object): if self.run_config['scan_id'] in ['dac_linearity_scan', 'adc_tuning']: self.DAC_data = root.dac_data[:] - if 'ptot' in self.run_config['scan_id']: + if self.use_ptot: self.HistPToT = root.HistPToT[:] self.HistPToA = root.HistPToA[:] @@ -247,17 +249,17 @@ class Plotting(object): self.create_occupancy_map() if self.run_config['scan_id'] in ['source_scan', 'ext_trigger_scan', 'source_scan_random_trigger', 'source_scan_injection']: self.create_fancy_occupancy() - if 'ptot' in self.run_config['scan_id']: + if self.use_ptot: self.create_ptot_plot() else: self.create_tot_plot() - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'in_time_threshold_scan', 'hitor_calibration', 'hitor_calibration_ptot', 'tot_calibration', 'autorange_threshold_scan']: - if 'ptot' in self.run_config['scan_id']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'in_time_threshold_scan', 'hitor_calibration', 'tot_calibration', 'autorange_threshold_scan']: + if self.use_ptot: self.create_ptot_hist() else: self.create_tot_hist() self.create_rel_bcid_hist() - if 'ptot' in self.run_config['scan_id']: + if self.use_ptot: self.create_ptot_map() else: self.create_tot_map() @@ -265,13 +267,13 @@ class Plotting(object): self.create_rel_bcid_map() self.create_bcid_error_plot() self.create_trigger_id_map() - if 'ptot' in self.run_config['scan_id']: + if self.use_ptot: self.create_ptoa_map() - if self.run_config['scan_id'] in ['analog_scan', 'threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'autorange_threshold_scan', 'in_time_threshold_scan', 'noise_occupancy_scan', 'global_threshold_tuning', 'ext_trigger_scan', 'source_scan', 'source_scan_random_trigger', 'source_scan_injection', 'crosstalk_scan', 'bump_connection_bias_thr_shift_scan']: + if self.run_config['scan_id'] in ['analog_scan', 'threshold_scan', 'fast_threshold_scan', 'autorange_threshold_scan', 'in_time_threshold_scan', 'noise_occupancy_scan', 'global_threshold_tuning', 'ext_trigger_scan', 'source_scan', 'source_scan_random_trigger', 'source_scan_injection', 'crosstalk_scan', 'bump_connection_bias_thr_shift_scan']: self.create_tdac_plot() self.create_tdac_map() self.create_hit_pix_plot() - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'in_time_threshold_scan', 'autorange_threshold_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'in_time_threshold_scan', 'autorange_threshold_scan']: self.create_scurves_plot() self.create_threshold_plot() self.create_stacked_threshold_plot() @@ -286,7 +288,7 @@ class Plotting(object): self.create_noise_map() if self.run_config['scan_id'] == 'tot_calibration': self.create_tot_perpixel_plot() - if self.run_config['scan_id'] in ['hitor_calibration', 'hitor_calibration_ptot']: + if self.run_config['scan_id'] in ['hitor_calibration']: self.create_tdc_hist() self.create_tdc_tot_perpixel_plot() if self.run_config['scan_id'] in ['crosstalk_scan']: @@ -328,7 +330,7 @@ class Plotting(object): def create_occupancy_map(self): try: - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'autorange_threshold_scan', 'global_threshold_tuning', 'injection_delay_scan', 'in_time_threshold_scan', 'injection_delay_scan', 'crosstalk_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'autorange_threshold_scan', 'global_threshold_tuning', 'injection_delay_scan', 'in_time_threshold_scan', 'injection_delay_scan', 'crosstalk_scan']: title = 'Integrated occupancy' z_max = 'maximum' else: @@ -347,7 +349,7 @@ class Plotting(object): def create_three_way(self): try: - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'autorange_threshold_scan', 'global_threshold_tuning', 'in_time_threshold_scan', 'crosstalk_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'autorange_threshold_scan', 'global_threshold_tuning', 'in_time_threshold_scan', 'crosstalk_scan']: title = 'Integrated occupancy' else: title = 'Occupancy' @@ -383,7 +385,7 @@ class Plotting(object): _, counts = np.unique(zdim, return_counts=True) all_hits = np.sum(counts) zdim = np.where(counts > (all_hits * 0.005))[0] + offset - self._plot_occupancy(hist=np.ma.masked_array(au.get_mean_from_histogram(self.HistPToT.sum(axis=(2)), np.arange(self.HistPToT.shape[3]), axis=2), self.enable_mask).T, suffix='tot_map', z_min=zdim[0], z_max=zdim[-1], show_sum=False, title='Mean PToT') + self._plot_occupancy(hist=np.ma.masked_array(au.get_mean_from_histogram(self.HistPToT.sum(axis=(2)), np.arange(self.HistPToT.shape[3]), axis=2), self.enable_mask).T, z_label='Precision ToT', suffix='ptot_map', z_min=zdim[0], z_max=zdim[-1], show_sum=False, title='Mean PToT') except Exception: self.log.error('Could not create ptot map!') @@ -393,7 +395,7 @@ class Plotting(object): _, counts = np.unique(zdim, return_counts=True) all_hits = np.sum(counts) zdim = np.where(counts > (all_hits * 0.005))[0] - self._plot_occupancy(hist=np.ma.masked_array(au.get_mean_from_histogram(self.HistPToA.sum(axis=(2)), np.arange(self.HistPToA.shape[3]), axis=2), self.enable_mask).T, suffix='toa_map', z_min=zdim[0], z_max=zdim[-1], show_sum=False, title='Mean Toa') + self._plot_occupancy(hist=np.ma.masked_array(au.get_mean_from_histogram(self.HistPToA.sum(axis=(2)), np.arange(self.HistPToA.shape[3]), axis=2), self.enable_mask).T, z_label='Precision ToA', suffix='ptoa_map', z_min=zdim[0], z_max=zdim[-1], show_sum=False, title='Mean PToA') except Exception: self.log.error('Could not create ptoa map!') @@ -419,10 +421,6 @@ class Plotting(object): self.log.error('Could not create tot plots per pixel!') def create_tdc_tot_perpixel_plot(self, n_random_pixels=3): - if 'ptot' in self.run_config['scan_id']: - use_ptot = True - else: - use_ptot = False try: if 'VCAL_HIGH_start' in self.scan_config: scan_parameter_range = [v - self.scan_config['VCAL_MED'] for v in @@ -441,8 +439,7 @@ class Plotting(object): self.HistTdcCalStd[col, row], self.lookup_table[col, row], scan_parameter_range, - use_ptot=use_ptot, - title='Mean TOT/TDC for pixel ({0}, {1})'.format(col, row), + title='Mean ToT/TDC for pixel ({0}, {1})'.format(col, row), suffix='perpixel_{0}'.format(i)) except Exception: self.log.error('Could not create tot/tdc plots per pixel!') @@ -467,7 +464,7 @@ class Plotting(object): def create_scurves_plot(self, scan_parameter_name='Scan parameter'): try: - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'in_time_threshold_scan', 'crosstalk_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'in_time_threshold_scan', 'crosstalk_scan']: scan_parameter_name = '$\\Delta$ VCAL' electron_axis = True scan_parameter_range = [v - self.scan_config['VCAL_MED'] for v in @@ -597,7 +594,7 @@ class Plotting(object): def create_threshold_plot(self, logscale=False, scan_parameter_name='Scan parameter'): try: title = 'Threshold distribution for enabled pixels' - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'in_time_threshold_scan', 'crosstalk_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'in_time_threshold_scan', 'crosstalk_scan']: plot_range = [v - self.scan_config['VCAL_MED'] for v in range(self.scan_config['VCAL_HIGH_start'], self.scan_config['VCAL_HIGH_stop'] + 1, self.scan_config['VCAL_HIGH_step'])] @@ -626,6 +623,7 @@ class Plotting(object): x_axis_title=scan_parameter_name, title=title, log_y=logscale, + y_axis_title='# of pixels', print_failed_fits=True, suffix='threshold_distribution') except Exception as e: @@ -637,13 +635,13 @@ class Plotting(object): if flavor == 'SYNC': return min_tdac, max_tdac, range_tdac, _ = rd53a.get_tdac_range(rd53a.get_flavor(self.scan_config['stop_column'] - 1)) - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'in_time_threshold_scan', 'crosstalk_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'in_time_threshold_scan', 'crosstalk_scan']: plot_range = [v - self.scan_config['VCAL_MED'] for v in range(self.scan_config['VCAL_HIGH_start'], self.scan_config['VCAL_HIGH_stop'] + 1, self.scan_config['VCAL_HIGH_step'])] scan_parameter_name = '$\\Delta$ VCAL' electron_axis = True - elif self.run_config['scan_id'] in ['fast_threshold_scan', 'fast_threshold_scan_ptot']: + elif self.run_config['scan_id'] in ['fast_threshold_scan']: plot_range = np.array(self.scan_params[:]['vcal_high'] - self.scan_params[:]['vcal_med'], dtype=np.float) scan_parameter_name = '$\\Delta$ VCAL' electron_axis = True @@ -664,6 +662,7 @@ class Plotting(object): plot_range=plot_range, electron_axis=electron_axis, x_axis_title=scan_parameter_name, + y_axis_title='# of pixels', title='Threshold distribution for enabled pixels', suffix='tdac_threshold_distribution', min_tdac=min(min_tdac, max_tdac), @@ -707,7 +706,7 @@ class Plotting(object): def create_noise_plot(self, logscale=False, scan_parameter_name='Scan parameter'): try: plot_range = None - if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'in_time_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan']: + if self.run_config['scan_id'] in ['threshold_scan', 'fast_threshold_scan', 'in_time_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan']: scan_parameter_name = '$\\Delta$ VCAL' electron_axis = True elif self.run_config['scan_id'] == 'global_threshold_tuning': @@ -724,7 +723,7 @@ class Plotting(object): electron_axis=electron_axis, use_electron_offset=False, x_axis_title=scan_parameter_name, - y_axis_title='# of hits', + y_axis_title='# of pixels', log_y=logscale, print_failed_fits=True, suffix='noise_distribution') @@ -771,7 +770,7 @@ class Plotting(object): plot_range=plot_range, title='TDAC distribution for enabled pixels', x_axis_title='TDAC', - y_axis_title='# of hits', + y_axis_title='# of pixels', align='center', suffix='tdac_distribution') except Exception: @@ -1168,7 +1167,7 @@ class Plotting(object): ax.text(0.01, 0.9, text, fontsize=10) ax.text(-0.1, -0.11, 'Software version: {0}'.format(sw_ver), fontsize=3) - if scan_id in ['threshold_scan', 'fast_threshold_scan', 'fast_threshold_scan_ptot', 'autorange_threshold_scan', 'crosstalk_scan']: + if scan_id in ['threshold_scan', 'fast_threshold_scan', 'autorange_threshold_scan', 'crosstalk_scan']: txt = 'Charge calibration: $y \\; [e^-] = x \\; [\\Delta VCAL] \\cdot ({0:1.2f} \\pm {1:1.2f}) \\; [\\frac{{e^-}}{{\\Delta VCAL}}] + ({2:1.0f} \\pm {3:1.2f}) \\; [e^-]$'.format(self.calibration['e_conversion_slope'], self.calibration['e_conversion_slope_error'], self.calibration['e_conversion_offset'], @@ -1871,11 +1870,14 @@ class Plotting(object): def _plot_tot_perpixel(self, mean_tot, std_tot, scan_parameter_range, ylabel='ToT Code', title='Tot curve ', suffix='tot_perpixel'): + if self.use_ptot: + tot_label = 'PTOT' + else: + tot_label = 'TOT' x_bins = scan_parameter_range - fig = Figure() ax = fig.add_subplot(111) - ax.errorbar(x_bins, mean_tot, yerr=std_tot, fmt='o', label='TOT') + ax.errorbar(x_bins, mean_tot, yerr=std_tot, fmt='o', label=tot_label) ax.ticklabel_format(useOffset=True) ticks_x = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x)) @@ -1889,8 +1891,8 @@ class Plotting(object): self._add_text(fig) self._save_plots(fig, suffix=suffix) - def _plot_tdc_tot_perpixel(self, mean_tot, mean_tdc, std_tot, std_tdc, lookup_values, scan_parameter_range, use_ptot=False, title='Tot/TDC curve ', suffix='Tot/TDC perpixel'): - if use_ptot: + def _plot_tdc_tot_perpixel(self, mean_tot, mean_tdc, std_tot, std_tdc, lookup_values, scan_parameter_range, title='Tot/TDC curve ', suffix='Tot/TDC perpixel'): + if self.use_ptot: tot_to_ns = 1.5625 # Precision TOT uses 640 MHz sampling clock tot_label = 'PTOT' else: diff --git a/bdaq53/analysis/rd53a_analysis.py b/bdaq53/analysis/rd53a_analysis.py index 73f5eec6f8ba5ccbbe8f305866ec602f12e0b4dc..0e6bbf477fcbd315eccdce8d861c9b91a6ff2b24 100644 --- a/bdaq53/analysis/rd53a_analysis.py +++ b/bdaq53/analysis/rd53a_analysis.py @@ -334,6 +334,9 @@ def interpret_data(rawdata, hits, hist_occ, hist_tot, tdc_value[index] = tdc_value_buffer[index] tdc_timestamp[index] = tdc_timestamp_buffer[index] tdc_status[index] = tdc_status_buffer[index] + tdc_value_buffer[:] = 2 ** 16 - 1 + tdc_timestamp_buffer[:] = 0 + tdc_status_buffer[:] = 0 tdc_word[:] = False event_status_tdc = 0 diff --git a/bdaq53/analysis/rd53b_analysis.py b/bdaq53/analysis/rd53b_analysis.py index 27c825232979c9f20e78484e99471293b9024a4a..a899e4246c77da3cbe62f55431f9c80ede71c917 100755 --- a/bdaq53/analysis/rd53b_analysis.py +++ b/bdaq53/analysis/rd53b_analysis.py @@ -272,6 +272,9 @@ def interpret_data(rawdata, hits, hist_occ, hist_tot, tdc_status[index] = tdc_status_buffer[index] tdc_word[:] = False event_status_tdc = 0 + tdc_value_buffer[:] = 2 ** 16 - 1 + tdc_timestamp_buffer[:] = 0 + tdc_status_buffer[:] = 0 if is_new_event(n_tags, n_trigger, start_tag, tag, prev_tag, last_tag, trg_header, event_status, is_tag_offset, align_method): # Set event errors of old event diff --git a/bdaq53/chips/ITkPixV1.py b/bdaq53/chips/ITkPixV1.py index a49ae757c711e07affab4f1f74ed1d3db8d09ec1..7cd9069a79d70d036a5eabfb8927002aab3602ad 100755 --- a/bdaq53/chips/ITkPixV1.py +++ b/bdaq53/chips/ITkPixV1.py @@ -581,6 +581,8 @@ class ITkPixV1(ChipBase): self.calibration = ITkPixV1Calibration(self.configuration['calibration']) + self.ptot_enabled = False # Flag to indicate if PTOT mode is enabled + def init(self): if self.bdaq.board_version != 'SIMULATION': self.write_trimbits() # Make sure that trimbits are written @@ -1495,6 +1497,25 @@ class ITkPixV1(ChipBase): def get_tdac_range(self, fe): return get_tdac_range(fe) + def enable_ptot(self): + ''' Enable and configure PTOT mode. + ''' + ptot_del = 110 + self.log.info('Enabling PTOT mode') + self._enable_col_precision_tot(range(0, 54)) + # Set enable bits for PToT, PToA and configure PToT latency + self.registers['ToTConfig'].write(int(format(12, '04b') + format(ptot_del, '09b'), 2)) + self.registers['TriggerConfig'].write(511) + self.ptot_enabled = True + + def disable_ptot(self): + self.log.info('Disabling PTOT mode') + self._enable_col_precision_tot(None) + self.registers['ToTConfig'].write(int(format(0, '04b') + format(500, '09b'), 2)) + self.registers['TriggerConfig'].reset() + self.ptot_enabled = False + + if __name__ == '__main__': ITkPixV1_chip = ITkPixV1() diff --git a/bdaq53/chips/rd53a.py b/bdaq53/chips/rd53a.py index 62548a5a42ccf0114a0aa9302b2efb8cc172aa7d..b12a9a38be7913543e5ce522021aa2f85c817e77 100644 --- a/bdaq53/chips/rd53a.py +++ b/bdaq53/chips/rd53a.py @@ -1367,6 +1367,10 @@ class RD53A(ChipBase): def get_tdac_range(self, fe): return get_tdac_range(fe) + def configure_ptot(self): + raise NotImplementedError('RD53A has no PTOT mode!') + + if __name__ == '__main__': rd53a_chip = RD53A() diff --git a/bdaq53/chips/shift_and_inject.py b/bdaq53/chips/shift_and_inject.py new file mode 100755 index 0000000000000000000000000000000000000000..191df59be95809c832db356564ab8bb50c195044 --- /dev/null +++ b/bdaq53/chips/shift_and_inject.py @@ -0,0 +1,209 @@ +# +# ------------------------------------------------------------ +# Copyright (c) All rights reserved +# SiLab, Institute of Physics, University of Bonn +# ------------------------------------------------------------ +# + +''' + This module takes care of the mask shifting and injection of the supported chips + in order to keep actual scans cleaner. +''' + + +import numpy as np + + +def shift_and_inject_digital(scan, n_injections, pbar=None, scan_param_id=0, masks=['injection', 'enable'], pattern='default'): + ''' Regular mask shift and digital injection function. + + Parameters: + ---------- + scan : scan object + BDAQ53 scan object + n_injections : int + Number of injections per loop. + pbar : tqdm progressbar + Tqdm progressbar + scan_param_id : int + Scan parameter id of actual scan loop + masks : list + List of masks ('injection', 'enable', 'hitbus') which should be shifted during scan loop. + pattern : string + BDAQ53 injection patter ('default', 'hitbus', ...) + ''' + + if scan.chip_settings['chip_type'].lower() == 'itkpixv1': + # Change scan loop parameters in case ptot is enabled + if scan.chip.ptot_enabled: + pattern = 'ptot' if pattern == 'default' else pattern + if 'hitbus' not in masks: + masks.append('hitbus') + latency = 23 + cal_edge_width = 32 + else: + latency = 124 + cal_edge_width = 4 + else: + latency = 121 + cal_edge_width = 10 + + for fe, active_pixels in scan.chip.masks.shift(masks=masks, pattern=pattern): + if not fe == 'skipped': + if scan.chip_settings['chip_type'].lower() == 'itkpixv1': + if scan.chip.ptot_enabled: + scan.add_ptot_table_data(cmd_repetitions=n_injections, scan_param_id=scan_param_id, active_pixels=active_pixels) + scan.chip.inject_digital(repetitions=n_injections, latency=latency, cal_edge_width=cal_edge_width) + if pbar is not None: + pbar.update(1) + + +def shift_and_inject(scan, n_injections, pbar=None, scan_param_id=0, masks=['injection', 'enable'], pattern='default', cache=False, skip_empty=True, send_trigger=True): + ''' Regular mask shift and analog injection function. + + Parameters: + ---------- + scan : scan object + BDAQ53 scan object + n_injections : int + Number of injections per loop. + pbar : tqdm progressbar + Tqdm progressbar + scan_param_id : int + Scan parameter id of actual scan loop + masks : list + List of masks ('injection', 'enable', 'hitbus') which should be shifted during scan loop. + pattern : string + BDAQ53 injection patter ('default', 'hitbus', ...) + cache : boolean + If True use mask caching for speedup. Default is False. + skip_empty : boolean + If True skip empty mask steps for speedup. Default is True. + send_trigger : boolean + If False do not send trigger command to chip. Default is True. + ''' + if scan.chip_settings['chip_type'].lower() == 'itkpixv1': + # Change scan loop parameters in case ptot is enabled + if scan.chip.ptot_enabled: + pattern = 'ptot' if pattern == 'default' else pattern + if 'hitbus' not in masks: + masks = masks + ['hitbus'] + latency = 23 + core_column_loops = 8 # Every 8th core columns will be activated per scan step. + else: + latency = 121 + core_column_loops = 1 # All core columns will be activated per scan step. + else: + latency = 122 + core_column_loops = 1 # All core columns will be activated per scan step. + + for fe, active_pixels in scan.chip.masks.shift(masks=masks, pattern=pattern, cache=cache, skip_empty=skip_empty): + if not fe == 'skipped': + for n in range(core_column_loops): # ITkPixV1 needs additional loop in order to not activate all core columns at the same time. + if scan.chip_settings['chip_type'].lower() == 'itkpixv1': + if scan.chip.ptot_enabled: + # Enable only specific core columns. + scan.chip.enable_core_col_clock(core_cols=[i + n for i in range(0, 50, core_column_loops)]) + scan.add_ptot_table_data(cmd_repetitions=n_injections, scan_param_id=scan_param_id, active_pixels=active_pixels) + if (not fe == 'skipped' or not skip_empty) and fe == 'SYNC': + scan.chip.inject_analog_single(repetitions=n_injections, latency=latency, send_ecr=True, send_trigger=send_trigger) + elif (not fe == 'skipped' or not skip_empty): + scan.chip.inject_analog_single(repetitions=n_injections, latency=latency, send_trigger=send_trigger) + if pbar is not None: + pbar.update(1) + + +def shift_and_inject_fast(scan, n_injections, vcal_high_range, scan_param_id_range, pbar=None, masks=['injection', 'enable'], pattern='default'): + ''' Faster implementation of mask shift and injection function. + + Parameters: + ---------- + scan : scan object + BDAQ53 scan object + n_injections : int + Number of injections per loop. + vcal_high_range : list + List of VCAL_HIGH values which should be scanned. + scan_param_id_range : list + List of scan parameter ids. + pbar : tqdm progressbar + Tqdm progressbar + masks : list + List of masks ('injection', 'enable', 'hitbus') which should be shifted during scan loop. + pattern : string + BDAQ53 injection patter ('default', 'hitbus', ...) + ''' + + if scan.chip_settings['chip_type'].lower() == 'itkpixv1': + # Change scan loop parameters in case ptot is enabled + if scan.chip.ptot_enabled: + pattern = 'ptot' if pattern == 'default' else pattern + if 'hitbus' not in masks: + masks.append('hitbus') + latency = 23 + wait_cycles = 200 + core_column_loops = 8 # Every 8th core columns will be activated per scan step. + else: + latency = 121 + wait_cycles = 300 + core_column_loops = 1 # All core columns will be activated per scan step. + else: + latency = 9 + wait_cycles = 300 + core_column_loops = 1 # All core columns will be activated per scan step. + + if scan.chip_settings['chip_type'].lower() == 'rd53a': + trigger_latency = scan.chip.registers['LATENCY_CONFIG'].get() + scan.chip.registers['LATENCY_CONFIG'].write(latency * 4 + 12) + + for fe, active_pixels in (scan.chip.masks.shift(masks=masks, pattern=pattern, cache=True)): + for cnt, vcal_high in enumerate(vcal_high_range): + scan_param_id = scan_param_id_range[cnt] + scan.chip.registers['VCAL_HIGH'].write(vcal_high) + if not fe == 'skipped': + for n in range(core_column_loops): # ITkPixV1 needs additional loop in order to not activate all core columns at the same time. + if scan.chip_settings['chip_type'].lower() == 'itkpixv1': + if scan.chip.ptot_enabled: + # Enable only specific core columns. + scan.chip.enable_core_col_clock(core_cols=[i + n for i in range(0, 50, core_column_loops)]) + scan.add_ptot_table_data(cmd_repetitions=n_injections, scan_param_id=scan_param_id, active_pixels=active_pixels) + else: + scan.add_trigger_table_data(cmd_repetitions=n_injections, scan_param_id=scan_param_id) + else: + scan.add_trigger_table_data(cmd_repetitions=n_injections, scan_param_id=scan_param_id) + if fe == 'SYNC': + scan.chip.inject_analog_single(repetitions=n_injections, latency=latency, + wait_cycles=wait_cycles, send_ecr=True) + else: + scan.chip.inject_analog_single(repetitions=n_injections, latency=latency, + wait_cycles=wait_cycles) + if pbar is not None: + pbar.update(1) + vcal_high_range = np.flip(vcal_high_range) + scan_param_id_range = np.flip(scan_param_id_range) + + # FIXME: Workaround in order to reset trigger latency to value before scan. Otherwise consecutive scans have wrong trigger latency and therefore no hits. + if scan.chip_settings['chip_type'].lower() == 'rd53a': + scan.chip.registers['LATENCY_CONFIG'].write(trigger_latency) + + +def get_scan_loop_mask_steps(scan, pattern='default'): + ''' Returns total number of mask steps for specific pattern + + Parameters: + ---------- + scan : scan object + BDAQ53 scan object + pattern : string + BDAQ53 injection patter ('default', 'hitbus', ...) + ''' + + if scan.chip_settings['chip_type'].lower() == 'itkpixv1': + # Change scan loop parameters in case ptot is enabled + if scan.chip.ptot_enabled: + pattern = 'ptot' if pattern == 'default' else pattern + return scan.chip.masks.get_mask_steps(pattern=pattern) + + +if __name__ == '__main__': + pass diff --git a/bdaq53/scans/calibrate_hitor.py b/bdaq53/scans/calibrate_hitor.py index 34e0de19c1367842980547fd87b68cd6a9403738..bc34ea37c9eaeb8bffcd360c41c1fca7b79e0d2f 100644 --- a/bdaq53/scans/calibrate_hitor.py +++ b/bdaq53/scans/calibrate_hitor.py @@ -20,6 +20,7 @@ import tables as tb from scipy import interpolate from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting from bdaq53.analysis import analysis_utils as au @@ -75,7 +76,9 @@ class HitorCalib(ScanBase): ''' values = VCAL_HIGH_values - pbar = tqdm(total=self.chip.masks.get_mask_steps(pattern='hitbus') * len(values), unit=' Mask steps') + pattern = 'hitbus' + masks = ['enable', 'injection', 'hitbus'] + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self, pattern=pattern) * len(values), unit=' Mask steps') self.enable_hitor(True) self.bdaq.enable_tdc_modules() @@ -84,12 +87,7 @@ class HitorCalib(ScanBase): self.chip.setup_analog_injection(vcal_high=value, vcal_med=VCAL_MED) self.store_scan_par_values(scan_param_id=scan_param_id, vcal_high=value, vcal_med=VCAL_MED) with self.readout(scan_param_id=scan_param_id): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='hitbus'): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) - pbar.update(1) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id, masks=masks, pattern=pattern) self.bdaq.disable_tdc_modules() self.enable_hitor(False) @@ -108,11 +106,12 @@ class HitorCalib(ScanBase): self.configuration['bench']['analysis']['store_hits'] = True self.configuration['bench']['analysis']['analyze_tdc'] = True + self.configuration['bench']['analysis']['use_tdc_trigger_dist'] = True with analysis.Analysis(raw_data_file=self.output_filename + '.h5', **self.configuration['bench']['analysis']) as a: a.analyze_data() max_scan_param_id = a.get_scan_param_values(scan_parameter='vcal_high').shape[0] chunk_size = a.chunk_size - tot_max = 16 + tot_max = 512 if a.analyze_ptot else 16 tdc_max = 500 n_cols = a.columns n_rows = a.rows @@ -149,7 +148,7 @@ class HitorCalib(ScanBase): selection = np.logical_and(hits['tdc_status'] == 1, hits['tdc_value'] < tdc_max) hits = hits[selection] tdc_value = hits['tdc_value'] - tot_value = hits['tot'] + tot_value = hits['ptot'] if a.analyze_ptot else hits['tot'] scan_param_id = hits['scan_param_id'] col = hits['col'] row = hits['row'] diff --git a/bdaq53/scans/calibrate_hitor_ptot.py b/bdaq53/scans/calibrate_hitor_ptot.py deleted file mode 100644 index c949288a2d827f26cc47c5bda405a62f6457fc5e..0000000000000000000000000000000000000000 --- a/bdaq53/scans/calibrate_hitor_ptot.py +++ /dev/null @@ -1,298 +0,0 @@ -# -# ------------------------------------------------------------ -# Copyright (c) All rights reserved -# SiLab, Institute of Physics, University of Bonn -# ------------------------------------------------------------ -# - -''' - Creates the hitor calibration per pixel using the charge injection cicuit. In order to prevent wrong TDC measurements - (e.g. due to AFE overshoot, ...) only TDC words are written to the data stream if a valid (in time) trigger is measured - with the hit (HitOr output). For this, TX0 has to be connected with RX0 (as short as possible) in order to connect - the trigger signal (CMD_LOOP_START_PULSE) to the trigger input of the TDC module. Check the wiki for a detailed instruction. - If the hit delay is not within the measurement range (255 x 1.5625 ns) adjust the delay of pulser_cmd_start_loop. - If a hit delay measurement is not needed, set the `EN_TRIGGER_DIST` register of the TDC module to zero. -''' - -from tqdm import tqdm -import numpy as np -import tables as tb -from scipy import interpolate - -from bdaq53.system.scan_base import ScanBase -from bdaq53.analysis import analysis -from bdaq53.analysis import plotting -from bdaq53.analysis import analysis_utils as au - -scan_configuration = { - 'start_column': 0, - 'stop_column': 16, - 'start_row': 0, - 'stop_row': 16, - - 'VCAL_MED': 500, - 'VCAL_HIGH_values': [1500, 1800, 2000, 2300, 2500, 3000], -} - - -class HitorCalibPToT(ScanBase): - scan_id = 'hitor_calibration_ptot' - - def _configure(self, start_column=0, stop_column=400, start_row=0, stop_row=192, ptot_del=60, VCAL_MED=1000, **_): - ''' - Parameters - ---------- - start_column : int [0:400] - First column to scan - stop_column : int [0:400] - Column to stop the scan. This column is excluded from the scan. - start_row : int [0:192] - First row to scan - stop_row : int [0:192] - Row to stop the scan. This row is excluded from the scan. - - VCAL_MED : int - VCAL_MED DAC value. - VCAL_HIGH : int - VCAL_HIGH DAC value. - ptot_del : int - Trigger delay for the precision tot circuit. - ''' - - if not self.chip.chip_type.lower() in ['itkpixv1', 'crocv1']: - raise NotImplementedError('Ptot is not available in %s, you can run this scan with either itkpixv1 or crocv1' % (self.chip.chip_type)) - - self.chip.masks['enable'][start_column:stop_column, start_row:stop_row] = True - self.chip.masks['injection'] = self.chip.masks['enable'] - self.chip.masks['hitbus'] = self.chip.masks['enable'] - self.chip.masks.apply_disable_mask() - self.chip.masks.update(force=True) - - # Precision TOT mode - self.chip._enable_col_precision_tot(range(0, 54)) - self.chip.registers['ToTConfig'].write(int(format(12, '04b') + format(ptot_del, '09b'), 2)) - self.chip.registers['TriggerConfig'].write(20) - - # Start CMD LOOP PULSER, 160 MHz - self.bdaq['pulser_cmd_start_loop'].set_en(True) - self.bdaq['pulser_cmd_start_loop'].set_width(400) - self.bdaq['pulser_cmd_start_loop'].set_delay(80) - self.bdaq['pulser_cmd_start_loop'].set_repeat(1) - # Configure LEMO mux such that CMD_LOOP_SIGNAL is assigned to LEMO_TX0 - self.bdaq.set_LEMO_MUX(connector='LEMO_MUX_TX0', value=1) - - # Configure all four TDC modules - self.bdaq.configure_tdc_modules() - - def _scan(self, n_injections=100, latency=17, VCAL_MED=500, VCAL_HIGH_values=range(800, 4001, 200), **_): - ''' - Digital scan main loop - - Parameters - ---------- - n_injections : int - Number of injections. - ''' - - values = VCAL_HIGH_values - pbar = tqdm(total=self.chip.masks.get_mask_steps(pattern='hitbus'), unit=' Mask steps') - - self.enable_hitor(True) - self.bdaq.enable_tdc_modules() - - for scan_param_id, value in enumerate(values): - self.chip.setup_analog_injection(vcal_high=value, vcal_med=VCAL_MED) - self.store_scan_par_values(scan_param_id=scan_param_id, vcal_high=value, vcal_med=VCAL_MED) - with self.readout(scan_param_id=scan_param_id): - for fe, active_pixels in (self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='hitbus')): - if not fe == 'skipped': - self.add_ptot_table_data(cmd_repetitions=n_injections, scan_param_id=scan_param_id, active_pixels=active_pixels) - self.chip.inject_analog_single(repetitions=n_injections, latency=latency) - pbar.update(1) - - self.bdaq.disable_tdc_modules() - self.enable_hitor(False) - - pbar.close() - self.log.success('Scan finished') - - def _analyze(self): - def _mask_disabled_pixels(enable_mask, scan_config): - mask = np.invert(enable_mask) - mask[:scan_config['start_column'], :] = True - mask[scan_config['stop_column']:, :] = True - mask[:, :scan_config['start_row']] = True - mask[:, scan_config['stop_row']:] = True - return mask - - self.configuration['bench']['analysis']['store_hits'] = True - self.configuration['bench']['analysis']['analyze_tdc'] = True - self.configuration['bench']['analysis']['use_tdc_trigger_dist'] = True - self.configuration['bench']['analysis']['analyze_ptot'] = True - with analysis.Analysis(raw_data_file=self.output_filename + '.h5', **self.configuration['bench']['analysis']) as a: - a.analyze_data() - max_scan_param_id = a.get_scan_param_values(scan_parameter='vcal_high').shape[0] - chunk_size = a.chunk_size - tot_max = 512 # For ptot mode - tdc_max = 500 - n_cols = a.columns - n_rows = a.rows - - # Create col, row, tot histograms from hits - with tb.open_file(self.output_filename + '_interpreted.h5', 'r+') as io_file: - hist_tot_mean = np.zeros(shape=(n_cols, n_rows, max_scan_param_id), dtype=np.float32) - hist_tdc_mean = np.zeros(shape=(n_cols, n_rows, max_scan_param_id), dtype=np.float32) - hist_tot_std = np.zeros(shape=(n_cols, n_rows, max_scan_param_id), dtype=np.float32) - hist_tdc_std = np.zeros(shape=(n_cols, n_rows, max_scan_param_id), dtype=np.float32) - - bin_positions_tot = np.tile(np.arange(tot_max), (n_cols, n_rows)).reshape(n_cols, n_rows, tot_max) - bin_positions_tdc = np.tile(np.arange(tdc_max), (n_cols, n_rows)).reshape(n_cols, n_rows, tdc_max) - hist_2d_tdc_vcal = np.zeros(shape=(max_scan_param_id, tdc_max), dtype=np.float32) - - old_scan_par = -1 - is_new_scan_par = True - hist_tot = np.zeros(shape=(n_cols, n_rows, tot_max)) - hist_tdc = np.zeros(shape=(n_cols, n_rows, tdc_max)) - self.log.info('Creating Histograms...') - # Yield all hits from the same scan parameter. Analysis allows that one scan parameter can be yieled more than once - pbar = tqdm(total=max_scan_param_id, unit=' Scan parameters') - for par, hits in au.hits_of_parameter(hits=io_file.root.Hits, chunk_size=chunk_size): - # Save mean and std of TOT and TDC for each pixel of actual scan parameter - if par != old_scan_par and old_scan_par != -1: - hist_tot_mean[:, :, old_scan_par] = au.get_mean_from_histogram(hist_tot, bin_positions=np.arange(tot_max), axis=2) - hist_tdc_mean[:, :, old_scan_par] = au.get_mean_from_histogram(hist_tdc, bin_positions=np.arange(tdc_max), axis=2) - hist_tot_std[:, :, old_scan_par] = au.get_std_from_histogram(hist_tot, bin_positions_tot, axis=2) - hist_tdc_std[:, :, old_scan_par] = au.get_std_from_histogram(hist_tdc, bin_positions_tdc, axis=2) - is_new_scan_par = True - pbar.update(1) - - # Select only good tdc values - selection = np.logical_and(hits['tdc_status'] == 1, hits['tdc_value'] < tdc_max) - hits = hits[selection] - tdc_value = hits['tdc_value'] - tot_value = hits['ptot'] - scan_param_id = hits['scan_param_id'] - col = hits['col'] - row = hits['row'] - - if is_new_scan_par: - # Histogram for each pixel TOT and TDC - hist_tot = au.hist_3d_index(col, row, tot_value, shape=(n_cols, n_rows, tot_max)) - hist_tdc = au.hist_3d_index(col, row, tdc_value, shape=(n_cols, n_rows, tdc_max)) - is_new_scan_par = False - else: - hist_tot += au.hist_3d_index(col, row, tot_value, shape=(n_cols, n_rows, tot_max)) - hist_tdc += au.hist_3d_index(col, row, tdc_value, shape=(n_cols, n_rows, tdc_max)) - - # Histogram delta vcal values and tdc value - x_edges = range(0, max_scan_param_id + 1) - y_edges = range(0, tdc_max + 1) - hist, _, _ = np.histogram2d(x=scan_param_id, - y=tdc_value, - bins=(x_edges, y_edges)) - hist_2d_tdc_vcal += hist - old_scan_par = par - - # Save mean and std of TOT and TDC for each pixel of last scan parameter - hist_tot_mean[:, :, par] = au.get_mean_from_histogram(hist_tot, bin_positions=np.arange(tot_max), axis=2) - hist_tdc_mean[:, :, par] = au.get_mean_from_histogram(hist_tdc, bin_positions=np.arange(tdc_max), axis=2) - hist_tot_std[:, :, par] = au.get_std_from_histogram(hist_tot, bin_positions_tot, axis=2) - hist_tdc_std[:, :, par] = au.get_std_from_histogram(hist_tdc, bin_positions_tdc, axis=2) - pbar.update(1) - pbar.close() - - def create_lookup_table(): - - ''' Create lookup table (TDC to DeltaVCAL) for each pixel using interpolation. - ''' - - scan_config = au.ConfigDict(io_file.root.configuration_in.scan.scan_config[:]) - charge_dvcal_values = io_file.root.configuration_in.scan.scan_params[:]['vcal_high'] - io_file.root.configuration_in.scan.scan_params[:]['vcal_med'] - enable_mask = ~_mask_disabled_pixels(io_file.root.configuration_in.chip.use_pixel[:], scan_config) - start_column = scan_config['start_column'] - stop_column = scan_config['stop_column'] - start_row = scan_config['start_row'] - stop_row = scan_config['stop_row'] - failed_interpolations = 0 - - # Lookup table: Delta VCAL values for all TDC values until max TDC in steps of 1 for each pixel - look_up_table = np.full_like(hist_tdc, fill_value=np.nan, dtype=np.float32) - - self.log.info('Interpolation Starting...') - for col in range(start_column, stop_column): - for row in range(start_row, stop_row): - if enable_mask[col, row]: - tdc_values = hist_tdc_mean[col, row, :] - selection = ~np.isnan(tdc_values) - try: - # It was observed that first degree interpolation gives more robust result between consecutive points while second degree some time induces weird behavior - dvcal = np.arange(np.min(charge_dvcal_values[selection]), np.max(charge_dvcal_values[selection]), 1) - spline = interpolate.splrep(charge_dvcal_values[selection], tdc_values[selection], s=0, k=1) # create spline interpolation - spline_eval = interpolate.splev(dvcal, spline) # evaluate spline - charge_dvcal_interpolation = np.interp(np.arange(look_up_table.shape[-1]), spline_eval, dvcal) # create evaluation for every TDC value - look_up_table[col, row, int(spline_eval[0]):int(spline_eval[-1])] = charge_dvcal_interpolation[int(spline_eval[0]):int(spline_eval[-1])] # only fill lookup table from lowest upto highest measured TDC value - except(TypeError, ValueError): # in case interpolation failed - failed_interpolations += 1 - self.log.debug('Could not do interpolation for pixel (%i, %i)' % (col, row)) - - self.log.info('%i Interpolations out of %i failed.' % (failed_interpolations, np.count_nonzero(enable_mask))) - - return look_up_table - - lookup_table = create_lookup_table() - # Store histograms - io_file.create_carray(io_file.root, - name='hist_2d_tdc_vcal', - title='2D Hist TDC vs DeltaVCAL', - obj=hist_2d_tdc_vcal, - filters=tb.Filters(complib='blosc', - complevel=5, - fletcher32=False)) - - io_file.create_carray(io_file.root, - name='hist_tot_mean', - title='Mean tot calibration histogram', - obj=hist_tot_mean, - filters=tb.Filters(complib='blosc', - complevel=5, - fletcher32=False)) - - io_file.create_carray(io_file.root, - name='hist_tdc_mean', - title='Mean Tdc calibration histogram', - obj=hist_tdc_mean, - filters=tb.Filters(complib='blosc', - complevel=5, - fletcher32=False)) - io_file.create_carray(io_file.root, - name='hist_tot_std', - title='Std tot calibration histogram', - obj=hist_tot_std, - filters=tb.Filters(complib='blosc', - complevel=5, - fletcher32=False)) - - io_file.create_carray(io_file.root, - name='hist_tdc_std', - title='Std Tdc calibration histogram', - obj=hist_tdc_std, - filters=tb.Filters(complib='blosc', - complevel=5, - fletcher32=False)) - - io_file.create_carray(io_file.root, - name='lookup_table', - title=r'Lookup table for TDC to DeltaVCAL conversion', - obj=lookup_table, - filters=tb.Filters(complib='blosc', - complevel=5, - fletcher32=False)) - - if self.configuration['bench']['analysis']['create_pdf']: - with plotting.Plotting(analyzed_data_file=a.analyzed_data_file, save_png=False) as p: - p.create_standard_plots() - - -if __name__ == '__main__': - with HitorCalibPToT(scan_config=scan_configuration) as scan: - scan.start() diff --git a/bdaq53/scans/calibrate_tot.py b/bdaq53/scans/calibrate_tot.py index 358e43960e80615c0f73a2b0b30e27f3b3d6d02f..80f329d675ae334df2d00b06e1de8e1635ce753a 100644 --- a/bdaq53/scans/calibrate_tot.py +++ b/bdaq53/scans/calibrate_tot.py @@ -14,6 +14,7 @@ import numpy as np import tables as tb from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting from bdaq53.analysis import analysis_utils as au @@ -106,17 +107,13 @@ class TotCalibration(ScanBase): vcal_high_range = VCAL_HIGH_values - pbar = tqdm(total=self.chip.masks.get_mask_steps() * len(vcal_high_range), unit=' Mask steps') + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self) * len(vcal_high_range), unit=' Mask steps') for scan_param_id, value in enumerate(vcal_high_range): self.chip.setup_analog_injection(vcal_high=value, vcal_med=VCAL_MED) self.store_scan_par_values(scan_param_id=scan_param_id, vcal_high=value, vcal_med=VCAL_MED) with self.readout(scan_param_id=scan_param_id): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection']): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) - pbar.update(1) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id) + pbar.close() self.log.success('Scan finished') @@ -126,18 +123,23 @@ class TotCalibration(ScanBase): a.analyze_data() values = np.array(a.get_scan_param_values(scan_parameter='vcal_high'), dtype=np.float) chunk_size = a.chunk_size - tot_max = 16 + tot_max = 512 if a.analyze_ptot else 16 + n_cols = a.columns + n_rows = a.rows + # Create col, row, tot histograms from hits with tb.open_file(self.output_filename + '_interpreted.h5', "r+") as io_file: - shape_3d = (400 * 192, values.shape[0], tot_max) + shape_3d = (n_cols * n_rows, values.shape[0], tot_max) hist_3d = np.zeros(shape=shape_3d, dtype=np.uint8) # Loop over all words in the actual raw data file in chunks self.log.info('Histogramming hits...') for i in tqdm(range(0, io_file.root.Hits.shape[0], chunk_size)): hits = io_file.root.Hits[i:i + chunk_size] - channel = hits["col"] + 400 * hits["row"] - hist_3d += au.hist_3d_index(x=channel, y=hits["scan_param_id"], z=hits["tot"], shape=shape_3d) + channel = hits["col"] + n_cols * hits["row"] + scan_param_id = hits["scan_param_id"] + tot_value = hits['ptot'] if a.analyze_ptot else hits['tot'] + hist_3d += au.hist_3d_index(x=channel, y=scan_param_id, z=tot_value, shape=shape_3d) # Create pixel, scan par id, tot hist io_file.create_carray(io_file.root, @@ -155,7 +157,7 @@ class TotCalibration(ScanBase): complevel=5, fletcher32=False)) - result = np.zeros(shape=(400 * 192, values.shape[0])) + result = np.zeros(shape=(n_cols * n_rows, values.shape[0])) self.log.info('Calculate RMS per pixel...') get_rms_from_2d_hist(hist_3d, np.arange(tot_max), result) io_file.create_carray(io_file.root, diff --git a/bdaq53/scans/scan_analog.py b/bdaq53/scans/scan_analog.py index ad6cc82ee15f23c7dcfcc63acff6f063a14aea93..b36ad8b2858ff02286662eeedc8f487643a7dd22 100755 --- a/bdaq53/scans/scan_analog.py +++ b/bdaq53/scans/scan_analog.py @@ -13,6 +13,7 @@ from tqdm import tqdm from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting @@ -74,15 +75,9 @@ class AnalogScan(ScanBase): VCAL_HIGH_start : int VCAL_HIGH DAC value. ''' - pbar = tqdm(total=self.chip.masks.get_mask_steps(), unit=' Mask steps') - with self.readout(): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection']): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) - pbar.update(1) - + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self), unit=' Mask steps') + with self.readout(scan_param_id=0): + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=0) pbar.close() self.log.success('Scan finished') diff --git a/bdaq53/scans/scan_analog_ptot.py b/bdaq53/scans/scan_analog_ptot.py deleted file mode 100755 index bccc38889be82a7bc23add06ebb3b1556be88430..0000000000000000000000000000000000000000 --- a/bdaq53/scans/scan_analog_ptot.py +++ /dev/null @@ -1,106 +0,0 @@ -# -# ------------------------------------------------------------ -# Copyright (c) All rights reserved -# SiLab, Institute of Physics, University of Bonn -# ------------------------------------------------------------ -# - -''' - This basic scan injects a specified charge into - enabled pixels to test the analog front-end. -''' - -from tqdm import tqdm - -from bdaq53.system.scan_base import ScanBase -from bdaq53.analysis import analysis -from bdaq53.analysis import plotting - -scan_configuration = { - 'start_column': 0, - 'stop_column': 400, - 'start_row': 0, - 'stop_row': 384, - - 'VCAL_MED': 500, - 'VCAL_HIGH': 2000, -} - - -class AnalogScanPToT(ScanBase): - scan_id = 'analog_scan_ptot' - - def _configure(self, start_column=0, stop_column=400, start_row=0, stop_row=192, ptot_del=110, VCAL_MED=1000, VCAL_HIGH=4000, **_): - ''' - Parameters - ---------- - start_column : int [0:400] - First column to scan - stop_column : int [0:400] - Column to stop the scan. This column is excluded from the scan. - start_row : int [0:192] - First row to scan - stop_row : int [0:192] - Row to stop the scan. This row is excluded from the scan. - - VCAL_MED : int - VCAL_MED DAC value. - VCAL_HIGH : int - VCAL_HIGH DAC value. - ptot_del : int - Trigger delay for the precision tot circuit. - ''' - - if not self.chip.chip_type.lower() in ['itkpixv1', 'crocv1']: - raise NotImplementedError('Ptot is not available in %s, you can run this scan with either itkpixv1 or crocv1' % (self.chip.chip_type)) - - self.chip.masks['enable'][start_column:stop_column, start_row:stop_row] = True - - self.chip.masks['injection'] = self.chip.masks['enable'] - self.chip.masks['hitbus'] = self.chip.masks['enable'] - self.chip.masks.apply_disable_mask() - - # self.chip.masks.load_logo_mask(['injection']) - - self.chip.masks.update(force=True) - - self.chip.setup_analog_injection(vcal_high=VCAL_HIGH, vcal_med=VCAL_MED, fine_delay=12) - self.chip._enable_col_precision_tot(range(0, 54)) - self.chip.registers['ToTConfig'].write(int(format(12, '04b') + format(ptot_del, '09b'), 2)) - self.chip.registers['TriggerConfig'].write(511) - - def _scan(self, n_injections=100, latency=23, **_): - ''' - Digital scan main loop - - Parameters - ---------- - n_injections : int - Number of injections. - ''' - - pbar = tqdm(total=self.chip.masks.get_mask_steps(pattern='ptot'), unit=' Mask steps') - with self.readout(): - for fe, active_pixels in (self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='ptot')): - if not fe == 'skipped': - for n in range(8): - self.chip.enable_core_col_clock(core_cols=[i + n for i in range(0, 50, 8)]) - self.add_ptot_table_data(n_injections, 0, active_pixels) - self.chip.inject_analog_single(latency=latency, write=True, repetitions=n_injections) - pbar.update(1) - pbar.close() - self.log.success('Scan finished') - - def _analyze(self): - self.configuration['bench']['analysis']['analyze_ptot'] = True - with analysis.Analysis(raw_data_file=self.output_filename + '.h5', **self.configuration['bench']['analysis']) as a: - a.analyze_data() - - if self.configuration['bench']['analysis']['create_pdf']: - with plotting.Plotting(analyzed_data_file=a.analyzed_data_file, save_png=False) as p: - p.create_standard_plots() - - -if __name__ == '__main__': - with AnalogScanPToT(scan_config=scan_configuration) as scan: - scan.start() diff --git a/bdaq53/scans/scan_crosstalk.py b/bdaq53/scans/scan_crosstalk.py index a68c588787e73923035aa42f0cc39fc7bd8aa415..7494e37da64a9525997c632c5563cf4a4dad4b3a 100755 --- a/bdaq53/scans/scan_crosstalk.py +++ b/bdaq53/scans/scan_crosstalk.py @@ -16,6 +16,7 @@ import numpy as np from tqdm import tqdm from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting @@ -92,16 +93,11 @@ class CrosstalkScan(ScanBase): self.log.info('Using {0} pattern.'.format(injection_type)) - pbar = tqdm(total=self.chip.masks.get_mask_steps(pattern=injection_type) * len(vcal_high_range), unit=' Mask steps') + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self, pattern=injection_type) * len(vcal_high_range), unit=' Mask steps') for scan_param_id, vcal_high in enumerate(vcal_high_range): self.chip.setup_analog_injection(vcal_high=vcal_high, vcal_med=VCAL_MED) with self.readout(scan_param_id=scan_param_id): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], pattern=injection_type, cache=True, skip_empty=False): - if fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - else: - self.chip.inject_analog_single(repetitions=n_injections) - pbar.update(1) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id, pattern=injection_type, skip_empty=False, cache=True) pbar.close() self.log.success('Scan finished') diff --git a/bdaq53/scans/scan_digital.py b/bdaq53/scans/scan_digital.py index e9d45299eaa7ada712cf13fa5b808b06cbe4e92f..388beb6aec0f8ceb94fb4904515799abb9bf33f2 100755 --- a/bdaq53/scans/scan_digital.py +++ b/bdaq53/scans/scan_digital.py @@ -13,6 +13,7 @@ from tqdm import tqdm from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject_digital, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting @@ -61,13 +62,10 @@ class DigitalScan(ScanBase): n_injections : int Number of injections. ''' - pbar = tqdm(total=self.chip.masks.get_mask_steps(), unit=' Mask steps') - with self.readout(): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection']): - if not fe == 'skipped': - self.chip.inject_digital(repetitions=n_injections) - pbar.update(1) + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self), unit=' Mask steps') + with self.readout(scan_param_id=0): + shift_and_inject_digital(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=0) pbar.close() self.log.success('Scan finished') diff --git a/bdaq53/scans/scan_digital_ptot.py b/bdaq53/scans/scan_digital_ptot.py deleted file mode 100755 index 6f247d1369b394be599db4145de5dd4712bc6854..0000000000000000000000000000000000000000 --- a/bdaq53/scans/scan_digital_ptot.py +++ /dev/null @@ -1,99 +0,0 @@ -# -# ------------------------------------------------------------ -# Copyright (c) All rights reserved -# SiLab, Institute of Physics, University of Bonn -# ------------------------------------------------------------ -# - -''' - This basic scan injects a digital pulse into - enabled pixels to test the digital part of the chip. -''' - -from tqdm import tqdm - -from bdaq53.system.scan_base import ScanBase -from bdaq53.analysis import analysis -from bdaq53.analysis import plotting - -scan_configuration = { - 'start_column': 0, - 'stop_column': 400, - 'start_row': 0, - 'stop_row': 384, -} - - -class DigitalScanPToT(ScanBase): - scan_id = 'digital_scan_ptot' - - def _configure(self, start_column=0, stop_column=400, start_row=0, stop_row=192, fine_delay=9, ptot_del=60, **_): - ''' - Parameters - ---------- - start_column : int [0:400] - First column to scan - stop_column : int [0:400] - Column to stop the scan. This column is excluded from the scan. - start_row : int [0:192] - First row to scan - stop_row : int [0:192] - Row to stop the scan. This row is excluded from the scan. - fine_delay : int [0:64] - Injection delay in steps of 1.28GHz. - ptot_del : int - Trigger delay for the precision tot circuit. - ''' - if not self.chip.chip_type.lower() in ['itkpixv1', 'crocv1']: - raise NotImplementedError('Ptot is not available in %s, you can run this scan with either itkpixv1 or crocv1' % (self.chip.chip_type)) - - self.chip.masks['enable'][start_column:stop_column, start_row:stop_row] = True - - self.chip.masks['injection'] = self.chip.masks['enable'] - self.chip.masks['hitbus'] = self.chip.masks['enable'] - self.chip.masks.apply_disable_mask() - - # self.chip.masks.load_logo_mask(['injection']) - - self.chip.masks.update(force=True) - - self.chip.setup_digital_injection(fine_delay=fine_delay) - self.chip._enable_col_precision_tot(range(0, 54)) - - self.chip.registers['ToTConfig'].write(int(format(12, '04b') + format(ptot_del, '09b'), 2)) - self.chip.registers['TriggerConfig'].write(20) - - def _scan(self, cal_edge_width=32, n_injections=100, **_): - ''' - Digital scan main loop - - Parameters - ---------- - n_injections : int - Number of injections. - cal_edge_width : [0:64] - Width of digital injection pulse. - ''' - pbar = tqdm(total=self.chip.masks.get_mask_steps(pattern='ptot'), unit=' Mask steps') - with self.readout(): - for fe, active_pixels in (self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='ptot')): - if not fe == 'skipped': - self.add_ptot_table_data(n_injections, 0, active_pixels) - self.chip.inject_digital(repetitions=n_injections, latency=17, cal_edge_width=cal_edge_width) - pbar.update(1) - pbar.close() - self.log.success('Scan finished') - - def _analyze(self): - self.configuration['bench']['analysis']['analyze_ptot'] = True - with analysis.Analysis(raw_data_file=self.output_filename + '.h5', **self.configuration['bench']['analysis']) as a: - a.analyze_data() - - if self.configuration['bench']['analysis']['create_pdf']: - with plotting.Plotting(analyzed_data_file=a.analyzed_data_file, save_png=False) as p: - p.create_standard_plots() - - -if __name__ == '__main__': - with DigitalScanPToT(scan_config=scan_configuration) as scan: - scan.start() diff --git a/bdaq53/scans/scan_in_time_threshold.py b/bdaq53/scans/scan_in_time_threshold.py index 34405e03f1e3a9a7b29117e1fbf3060e02c95731..3cccb1651b4673d8959cc28c9898d564898178a8 100644 --- a/bdaq53/scans/scan_in_time_threshold.py +++ b/bdaq53/scans/scan_in_time_threshold.py @@ -22,6 +22,7 @@ from shutil import copyfile import os from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting from bdaq53.chips import rd53a @@ -89,21 +90,14 @@ class InTimeThrScan(ScanBase): obj=delay_map, filters=tb.Filters(complib='blosc', complevel=5, fletcher32=False)) - pbar = tqdm(total=self.chip.masks.get_mask_steps() * len(vcal_high_range) * len(finedelay_range), unit=' Mask steps') - cnt = 0 + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self) * len(vcal_high_range) * len(finedelay_range), unit=' Mask steps') for scan_param_id, vcal_high in enumerate(vcal_high_range): for delay in finedelay_range: self.chip.setup_analog_injection(vcal_high=vcal_high, vcal_med=VCAL_MED, fine_delay=delay) - self.store_scan_par_values(scan_param_id=cnt, delay_id=scan_param_id * 16 + int(delay)) - with self.readout(scan_param_id=cnt): - cnt += 1 - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - pbar.update(1) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) - pbar.update(1) + self.store_scan_par_values(scan_param_id=scan_param_id, delay_id=scan_param_id * 16 + int(delay)) + with self.readout(scan_param_id=scan_param_id): + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id, cache=True) + pbar.close() self.log.success('Scan finished') diff --git a/bdaq53/scans/scan_injection_delay.py b/bdaq53/scans/scan_injection_delay.py index 34e513275637435006df2ce355c8dc944bdb27b4..2987f0f0af5ae206945e245a4e306245cad22a53 100755 --- a/bdaq53/scans/scan_injection_delay.py +++ b/bdaq53/scans/scan_injection_delay.py @@ -15,6 +15,7 @@ import tables as tb from tqdm import tqdm from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting @@ -67,17 +68,12 @@ class InjDelayScan(ScanBase): VCAL_HIGH : int VCAL_HIGH DAC value. ''' - pbar = tqdm(total=self.chip.masks.get_mask_steps() * 16, unit=' Mask steps') + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self) * 16, unit=' Mask steps') for scan_param_id in range(16): self.chip.setup_analog_injection(vcal_high=VCAL_HIGH, vcal_med=VCAL_MED, fine_delay=scan_param_id) self.store_scan_par_values(scan_param_id=scan_param_id, fine_delay=scan_param_id) with self.readout(scan_param_id=scan_param_id): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) - pbar.update(1) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id, cache=True) pbar.close() self.log.success('Scan finished') diff --git a/bdaq53/scans/scan_source_injection.py b/bdaq53/scans/scan_source_injection.py index d3b061fb1162ce6696fde849536c02426f9c0eba..82fa3cf886f4eafac04f67e7792e78a664f79f43 100644 --- a/bdaq53/scans/scan_source_injection.py +++ b/bdaq53/scans/scan_source_injection.py @@ -15,6 +15,7 @@ from tqdm import tqdm from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting @@ -82,19 +83,15 @@ class SourceScanInj(ScanBase): self.chip.masks.update(force=True) self.chip.setup_analog_injection(vcal_high=2500, vcal_med=500) - def _scan(self, n_injections=1, **_): + def _scan(self, n_injections=1, **kwargs): + print(n_injections, kwargs) self.enable_hitor(True) self.bdaq.enable_ext_trigger() # enable external trigger self.bdaq.enable_tlu_module() # enable TLU module - pbar = tqdm(total=self.chip.masks.get_mask_steps(), unit=' Mask steps') + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self), unit=' Mask steps') with self.readout(): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection']): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections, send_trigger=False) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections, send_trigger=False) - pbar.update(1) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, send_trigger=False) pbar.close() self.log.success('Scan finished') diff --git a/bdaq53/scans/scan_threshold.py b/bdaq53/scans/scan_threshold.py index 05da00b098d461aada802de33118d3830bf05d2b..b9552605b9036aa6a483bb9fea64d7650c1596ee 100755 --- a/bdaq53/scans/scan_threshold.py +++ b/bdaq53/scans/scan_threshold.py @@ -14,6 +14,7 @@ from tqdm import tqdm import numpy as np from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting @@ -83,16 +84,11 @@ class ThresholdScan(ScanBase): vcal_high_range = range(VCAL_HIGH_start, VCAL_HIGH_stop, VCAL_HIGH_step) - pbar = tqdm(total=self.chip.masks.get_mask_steps() * len(vcal_high_range), unit=' Mask steps') + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self) * len(vcal_high_range), unit=' Mask steps') for scan_param_id, vcal_high in enumerate(vcal_high_range): self.chip.setup_analog_injection(vcal_high=vcal_high, vcal_med=VCAL_MED) with self.readout(scan_param_id=scan_param_id): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) - pbar.update(1) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id) pbar.close() self.log.success('Scan finished') diff --git a/bdaq53/scans/scan_threshold_autorange.py b/bdaq53/scans/scan_threshold_autorange.py index 51c4f0a7b15638b6d4927126b4cf4ff885fd41f0..19d3a4c531d172dc7c6bb6613da68477971b67e6 100644 --- a/bdaq53/scans/scan_threshold_autorange.py +++ b/bdaq53/scans/scan_threshold_autorange.py @@ -20,6 +20,7 @@ from tqdm import tqdm import numpy as np from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject from bdaq53.analysis import analysis from bdaq53.analysis import plotting from bdaq53.analysis import online as oa @@ -107,11 +108,7 @@ class ThresholdScan(ScanBase): self.log.info('Search for maxium response...') self.chip.setup_analog_injection(vcal_high=VCAL_HIGH_stop, vcal_med=VCAL_MED) with self.readout(scan_param_id=scan_param_id, callback=self.analyze_data_online): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id, cache=True) occupancy = self.data.hist_occ.get() # Interpret raw data and create occupancy histogram max_n_pix = np.count_nonzero(occupancy >= n_injections * max_hits) @@ -126,11 +123,7 @@ class ThresholdScan(ScanBase): self.chip.setup_analog_injection(vcal_high=vcal_high, vcal_med=VCAL_MED) with self.readout(scan_param_id=scan_param_id, callback=self.analyze_data_online): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections) + shift_and_inject(scan=self, n_injections=n_injections, pbar=pbar, scan_param_id=scan_param_id, cache=True) if self.data.start_data_taking: self.store_scan_par_values(scan_param_id=scan_param_id, vcal_high=vcal_high, vcal_med=VCAL_MED) diff --git a/bdaq53/scans/scan_threshold_fast.py b/bdaq53/scans/scan_threshold_fast.py index a292f977a2d9f99eff8f377c7d3258563607a4b3..565f150cdac50d707a281919656cc81438227e7a 100755 --- a/bdaq53/scans/scan_threshold_fast.py +++ b/bdaq53/scans/scan_threshold_fast.py @@ -19,6 +19,7 @@ from tqdm import tqdm import numpy as np from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject_fast, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting @@ -36,7 +37,7 @@ scan_configuration = { } -class ThresholdScan(ScanBase): +class FastThresholdScan(ScanBase): scan_id = 'fast_threshold_scan' def _configure(self, start_column=0, stop_column=400, start_row=0, stop_row=192, TDAC=None, **_): @@ -58,6 +59,8 @@ class ThresholdScan(ScanBase): self.chip.masks['enable'][start_column:stop_column, start_row:stop_row] = True self.chip.masks['injection'][start_column:stop_column, start_row:stop_row] = True self.chip.masks.apply_disable_mask() + # Write TLU words on every CMD repetition. Needed for analysis. + self.bdaq.trigger_on_cmd_loop_start() if TDAC is not None: if type(TDAC) == int: @@ -91,23 +94,9 @@ class ThresholdScan(ScanBase): self.log.info('Starting scan...') self.chip.setup_analog_injection(vcal_high=VCAL_HIGH_start, vcal_med=VCAL_MED) - self.chip.registers['LATENCY_CONFIG'].write(CAL_EDGE_latency * 4 + 12) - pbar = tqdm(total=self.chip.masks.get_mask_steps() * len(vcal_high_range), unit=' Mask steps') + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self) * len(vcal_high_range), unit=' Mask steps') with self.readout(): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): - for cnt, vcal_high in enumerate(vcal_high_range): - scan_param_id = scan_param_id_range[cnt] - self.chip.registers['VCAL_HIGH'].write(vcal_high) - self.add_trigger_table_data(n_injections, scan_param_id) - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(repetitions=n_injections, latency=CAL_EDGE_latency, - wait_cycles=wait_cycles, send_ecr=True) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections, latency=CAL_EDGE_latency, - wait_cycles=wait_cycles) - pbar.update(1) - vcal_high_range = np.flip(vcal_high_range) - scan_param_id_range = np.flip(scan_param_id_range) + shift_and_inject_fast(scan=self, n_injections=n_injections, vcal_high_range=vcal_high_range, scan_param_id_range=scan_param_id_range, pbar=pbar) pbar.close() self.log.success('Scan finished') @@ -131,5 +120,5 @@ class ThresholdScan(ScanBase): if __name__ == '__main__': - with ThresholdScan(scan_config=scan_configuration) as scan: + with FastThresholdScan(scan_config=scan_configuration) as scan: scan.start() diff --git a/bdaq53/scans/scan_threshold_ptot.py b/bdaq53/scans/scan_threshold_ptot.py deleted file mode 100755 index 31a9539bb8f56e7d6dac7bd5fa193b768cb8df59..0000000000000000000000000000000000000000 --- a/bdaq53/scans/scan_threshold_ptot.py +++ /dev/null @@ -1,125 +0,0 @@ -# -# ------------------------------------------------------------ -# Copyright (c) All rights reserved -# SiLab, Institute of Physics, University of Bonn -# ------------------------------------------------------------ -# - -''' - This script scans over different amounts of injected charge - to find the effective threshold of the enabled pixels. -''' - -from tqdm import tqdm - -from bdaq53.system.scan_base import ScanBase -from bdaq53.analysis import analysis -from bdaq53.analysis import plotting -import numpy as np - -scan_configuration = { - 'start_column': 0, - 'stop_column': 400, - 'start_row': 0, - 'stop_row': 384, - - 'VCAL_MED': 500, - 'VCAL_HIGH_start': 500, - 'VCAL_HIGH_stop': 1200, - 'VCAL_HIGH_step': 20 -} - - -class ThresholdScanPToT(ScanBase): - scan_id = 'fast_threshold_scan_ptot' - - def _configure(self, start_column=0, stop_column=400, start_row=0, stop_row=192, ptot_del=60, **_): - ''' - Parameters - ---------- - start_column : int [0:400] - First column to scan - stop_column : int [0:400] - Column to stop the scan. This column is excluded from the scan. - start_row : int [0:192] - First row to scan - stop_row : int [0:192] - Row to stop the scan. This row is excluded from the scan. - ptot_del : int - Trigger delay for the precision tot circuit. - ''' - - if not self.chip.chip_type.lower() in ['itkpixv1', 'crocv1']: - raise NotImplementedError('Ptot is not available in %s, you can run this scan with either itkpixv1 or crocv1' % (self.chip.chip_type)) - - self.chip.masks['enable'][start_column:stop_column, start_row:stop_row] = True - - self.chip.masks['injection'] = self.chip.masks['enable'] - self.chip.masks['hitbus'] = self.chip.masks['enable'] - self.chip.masks.apply_disable_mask() - - self.chip.masks.update(force=True) - - self.chip._enable_col_precision_tot(range(0, 54)) - self.chip.registers['ToTConfig'].write(int(format(12, '04b') + format(ptot_del, '09b'), 2)) - self.chip.registers['TriggerConfig'].write(20) - - def _scan(self, n_injections=100, VCAL_MED=500, VCAL_HIGH_start=1000, VCAL_HIGH_stop=4000, VCAL_HIGH_step=100, wait_cycles=200, **_): - ''' - Threshold scan main loop - - Parameters - ---------- - n_injections : int - Number of injections. - - VCAL_MED : int - VCAL_MED DAC value. - VCAL_HIGH_start : int - First VCAL_HIGH value to scan. - VCAL_HIGH_stop : int - VCAL_HIGH value to stop the scan. This value is excluded from the scan. - VCAL_HIGH_step : int - VCAL_HIGH interval. - ''' - - vcal_high_range = range(VCAL_HIGH_start, VCAL_HIGH_stop, VCAL_HIGH_step) - scan_param_id_range = range(0, len(vcal_high_range)) - - self.log.info('Starting scan...') - self.chip.setup_analog_injection(vcal_high=VCAL_HIGH_start, vcal_med=VCAL_MED) - pbar = tqdm(total=self.chip.masks.get_mask_steps(pattern='ptot') * len(vcal_high_range), unit=' Mask steps') - with self.readout(): - for fe, active_pixels in (self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='ptot')): - for cnt, vcal_high in enumerate(vcal_high_range): - scan_param_id = scan_param_id_range[cnt] - self.chip.registers['VCAL_HIGH'].write(vcal_high) - for n in range(8): - self.add_ptot_table_data(n_injections, scan_param_id, active_pixels) - self.chip.enable_core_col_clock(core_cols=[i + n for i in range(0, 50, 8)]) - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(repetitions=n_injections, latency=17, - wait_cycles=wait_cycles, send_ecr=True) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=n_injections, latency=17, - wait_cycles=wait_cycles) - pbar.update(1) - vcal_high_range = np.flip(vcal_high_range) - scan_param_id_range = np.flip(scan_param_id_range) - - pbar.close() - self.log.success('Scan finished') - - def _analyze(self): - self.configuration['bench']['analysis']['analyze_ptot'] = True - with analysis.Analysis(raw_data_file=self.output_filename + '.h5', **self.configuration['bench']['analysis']) as a: - a.analyze_data() - - if self.configuration['bench']['analysis']['create_pdf']: - with plotting.Plotting(analyzed_data_file=a.analyzed_data_file, save_png=False) as p: - p.create_standard_plots() - - -if __name__ == '__main__': - with ThresholdScanPToT(scan_config=scan_configuration) as scan: - scan.start() diff --git a/bdaq53/scans/test_aurora.py b/bdaq53/scans/test_aurora.py index a9aa563632bf965eafbf493fde2c68fbb6146992..dfe435ebd720de1cf850c2907c33c0d1e83a9594 100644 --- a/bdaq53/scans/test_aurora.py +++ b/bdaq53/scans/test_aurora.py @@ -1,4 +1,4 @@ -from bdaq53.scans.scan_digital_ptot import DigitalScanPToT +from bdaq53.scans.scan_digital import DigitalScan import yaml import os import bdaq53 @@ -34,7 +34,7 @@ class TestAuroraLanes(unittest.TestCase): expected_hits = (stop_column - start_column) * (stop_row - start_row) * n_injections for n in range(4): bench_config['modules']['module_0']['chip_0']['receiver'] = 'rx' + str(n) - with DigitalScanPToT(scan_config=scan_configuration, bench_config=bench_config) as scan: + with DigitalScan(scan_config=scan_configuration, bench_config=bench_config) as scan: scan.start() output_filename = scan.output_filename with tb.open_file(output_filename + '_interpreted.h5') as in_file: diff --git a/bdaq53/scans/tune_tot.py b/bdaq53/scans/tune_tot.py index c039aa53b68debc569701f072d7f89db00151cf8..4345f408f4308c877c58a990329f42656144e564 100755 --- a/bdaq53/scans/tune_tot.py +++ b/bdaq53/scans/tune_tot.py @@ -13,6 +13,7 @@ from tqdm import tqdm import numpy as np from bdaq53.system.scan_base import ScanBase +from bdaq53.chips.shift_and_inject import shift_and_inject, get_scan_loop_mask_steps from bdaq53.analysis import analysis from bdaq53.analysis import plotting from bdaq53.analysis import online as oa @@ -106,7 +107,7 @@ class TotTuning(ScanBase): feedback_current, actual_tot_mean, scan_par_values = {}, {}, {} self.log.info('Tune ToT for: ' + ', '.join(self.data.active_FEs)) - pbar = tqdm(total=self.chip.masks.get_mask_steps() * max_iterations, unit=' Mask steps') + pbar = tqdm(total=get_scan_loop_mask_steps(scan=self) * max_iterations, unit=' Mask steps') succesfull_FEs = [] # List of succesfully tuned FEs # Binary search loop until max iteration is reached or all FEs were tuned @@ -123,13 +124,8 @@ class TotTuning(ScanBase): # Store scan parameter values self.store_scan_par_values(scan_param_id=n_iterations, **scan_par_values) # Take data - with self.readout(callback=self.analyze_data_online): - for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): - if not fe == 'skipped' and fe == 'SYNC': - self.chip.inject_analog_single(send_ecr=True, repetitions=self.data.n_injections) - elif not fe == 'skipped': - self.chip.inject_analog_single(repetitions=self.data.n_injections) - pbar.update(1) + with self.readout(scan_param_id=n_iterations, callback=self.analyze_data_online): + shift_and_inject(scan=self, n_injections=self.data.n_injections, pbar=pbar, scan_param_id=n_iterations, cache=True) # Get mean of ToT from online analysis tot = self.data.tot_hist.get() diff --git a/bdaq53/system/scan_base.py b/bdaq53/system/scan_base.py index c0d621d9a57315e2a0867c955afaaa77fc2d51a0..6544badd2b219a53eed0372191d8916cf32853e9 100755 --- a/bdaq53/system/scan_base.py +++ b/bdaq53/system/scan_base.py @@ -290,6 +290,8 @@ class ScanBase(object): # Generate external trigger words every CMD repetition using TLU module. Needed for proper event building. if self.chip_settings['chip_type'].lower() == 'itkpixv1': self.bdaq.trigger_on_cmd_loop_start() + if self.chip_settings['use_ptot']: + self.chip.enable_ptot() try: if not self.initialized: @@ -318,6 +320,9 @@ class ScanBase(object): # Disable tlu module in case it was enabled. if self.bdaq.tlu_module_enabled: self.bdaq.disable_tlu_module() + if self.chip_settings['chip_type'].lower() == 'itkpixv1': + if self.chip.ptot_enabled: + self.chip.disable_ptot() # Add status info self._set_readout_status() @@ -1003,6 +1008,10 @@ class ScanBase(object): if self.chip_settings['use_good_pixels_diff']: self.chip.masks.apply_good_pixel_mask_diff() + if self.chip_settings['chip_type'].lower() == 'itkpixv1': + if self.chip_settings['use_ptot']: + # Automatically enable hitbus if PToT mode is enabled + self.chip.masks['hitbus'] = self.chip.masks['enable'] self.chip.masks.update(force=True) # write all masks to chip diff --git a/bdaq53/testbench.yaml b/bdaq53/testbench.yaml index 66bb928d34305e0f148815118f5c3b96cbb41f85..a3926aa80e1e3a5d75788a65af3eff855610d1df 100644 --- a/bdaq53/testbench.yaml +++ b/bdaq53/testbench.yaml @@ -28,8 +28,29 @@ modules: record_chip_status: True # Add chip statuses to the output files after the scan (link errors and powering infos) use_good_pixels_diff: False send_data: "tcp://127.0.0.1:5500" # Socket address of online monitor - - + + # module_1: # Arbitrary name of module, defines folder name with chip sub folders + # identifier: "unknown" # Module/wafer/PCB identifier, has to be given (e.g. SCC number) + # powersupply: + # lv_name: LV-0 + # lv_voltage: 1.7 + # lv_current_limit: 2.0 + # # hv_name: HV-0 + # # hv_voltage: 5 + # # hv_current_limit: 1e-6 + # power_cycle: False # power cycle all chip of this module before scan start + # chip_0: # Arbitrary name of chip, defines folder name with chip data + # chip_sn: "0x0002" + # chip_type: "itkpixv1" + # chip_id: 15 + # receiver: "rx4" # Aurora receiver channel (ranges from 'rx0' to 'rxN', N board-dependent) + # chip_config_file: # If defined: use config from in file (either .cfg.yaml or .h5). If not defined use chip config of latest scan and std. config if no previous scan exists + # record_chip_status: True # Add chip statuses to the output files after the scan (link errors and powering infos) + # use_good_pixels_diff: False + # use_ptot: True # Enable PTOT mode + # send_data: "tcp://127.0.0.1:5500" # Socket address of online monitor + + # chip_1: # ... # module_1: @@ -78,7 +99,7 @@ analysis: store_hits: False # store hit table cluster_hits: False # store cluster data analyze_tdc: False # analyze TDC words - analyze_ptot: False # analyze PTOT words (only possible for RD53B) + analyze_ptot: False # analyze PTOT words (only possible for RD53B) use_tdc_trigger_dist: False # analyze TDC to TRG distance align_method: 0 # how to detect new events chunk_size: 1000000 # scales amount of data in RAM (~150 MB) diff --git a/bdaq53/tests/ITkPixV1/test_rd53/test_SimDigitalScan.py b/bdaq53/tests/ITkPixV1/test_rd53/test_SimDigitalScan.py index 492eb5a198e31a423853a78db68c0ee86fbb19c3..ad342ae1bb15bf16c7835c59183405a312ca71f9 100755 --- a/bdaq53/tests/ITkPixV1/test_rd53/test_SimDigitalScan.py +++ b/bdaq53/tests/ITkPixV1/test_rd53/test_SimDigitalScan.py @@ -44,6 +44,7 @@ class TestDigitalScan(unittest.TestCase): bench_config['modules']['module_0']['chip_0']['chip_type'] = 'ITkPixV1' bench_config['modules']['module_0']['chip_0']['chip_id'] = 15 bench_config['modules']['module_0']['chip_0']['receiver'] = 'rx0' + bench_config['modules']['module_0']['chip_0']['use_ptot'] = False bench_config['general']['abort_on_rx_error'] = False # simulation too slow for RX sync check logging.info('Starting digital scan test') diff --git a/bdaq53/tests/test_software/test_analysis.py b/bdaq53/tests/test_software/test_analysis.py index ac555b7a909bcb8133d1aa8838a08121f95d6dac..2889a1ff9a47fbb110d419a8a828c62281265684 100644 --- a/bdaq53/tests/test_software/test_analysis.py +++ b/bdaq53/tests/test_software/test_analysis.py @@ -116,9 +116,9 @@ class TestAnalysis(unittest.TestCase): def test_ana_scan_ana_ptot(self): ''' Test analysis of ptot analog scan data ''' - from bdaq53.scans.scan_analog_ptot import AnalogScanPToT + from bdaq53.scans.scan_analog import AnalogScan bench_config_copy = deepcopy(self.bench_config) # Make copy of bench config in order to prevent modification of default bench config (scans modify analysis part of bench config) which makes some test failing - analog_ptot = AnalogScanPToT(bench_config=bench_config_copy) + analog_ptot = AnalogScan(bench_config=bench_config_copy) analog_ptot.output_filename = os.path.join(data_folder, 'analog_scan_ptot') analog_ptot.configuration = {'bench': bench_config_copy} analog_ptot._analyze() @@ -130,9 +130,9 @@ class TestAnalysis(unittest.TestCase): def test_chunked_ana_ptot(self): ''' Test analysis of ptot analog scan data ''' - from bdaq53.scans.scan_analog_ptot import AnalogScanPToT + from bdaq53.scans.scan_analog import AnalogScan bench_config_copy = deepcopy(self.bench_config) # Make copy of bench config in order to prevent modification of default bench config (scans modify analysis part of bench config) which makes some test failing - analog_ptot = AnalogScanPToT(bench_config=bench_config_copy) + analog_ptot = AnalogScan(bench_config=bench_config_copy) analog_ptot.output_filename = os.path.join(data_folder, 'analog_scan_ptot') analog_ptot.configuration = {'bench': bench_config_copy} analog_ptot._analyze() @@ -446,9 +446,9 @@ class TestAnalysis(unittest.TestCase): def test_calibrate_hitor_ptot_ana(self): ''' Test analysis of calibrate hitor ptot scan (TDC analysis) ''' - from bdaq53.scans.calibrate_hitor_ptot import HitorCalibPToT + from bdaq53.scans.calibrate_hitor import HitorCalib bench_config_copy = deepcopy(self.bench_config) # Make copy of bench config in order to prevent modification of default bench config (scans modify analysis part of bench config) which makes some test failing - cal_hitor = HitorCalibPToT(bench_config=bench_config_copy) + cal_hitor = HitorCalib(bench_config=bench_config_copy) cal_hitor.output_filename = os.path.join(data_folder, 'hitor_calibration_ptot') cal_hitor.configuration = {'bench': bench_config_copy} cal_hitor._analyze() @@ -461,10 +461,10 @@ class TestAnalysis(unittest.TestCase): def test_calibrate_hitor_ptot_ana_chunked(self): ''' Test chunked analysis of calibrate hitor ptot scan (TDC analysis) ''' - from bdaq53.scans.calibrate_hitor_ptot import HitorCalibPToT + from bdaq53.scans.calibrate_hitor import HitorCalib bench_config_copy = deepcopy(self.bench_config) # Make copy of bench config in order to prevent modification of default bench config (scans modify analysis part of bench config) which makes some test failing bench_config_copy['analysis']['chunk_size'] = 3989 - cal_hitor = HitorCalibPToT(bench_config=bench_config_copy) + cal_hitor = HitorCalib(bench_config=bench_config_copy) cal_hitor.output_filename = os.path.join(data_folder, 'hitor_calibration_ptot') cal_hitor.configuration = {'bench': bench_config_copy} cal_hitor._analyze() diff --git a/bdaq53/tests/test_software/test_scan_base.py b/bdaq53/tests/test_software/test_scan_base.py index 7ebdb1f176baeb46f1788a56563504fd141ec0d2..976569d1d3ea5630fecce7e54aed461e076c2601 100644 --- a/bdaq53/tests/test_software/test_scan_base.py +++ b/bdaq53/tests/test_software/test_scan_base.py @@ -516,6 +516,7 @@ class TestScanBase(unittest.TestCase): # Change std. bench config bench_config = copy.deepcopy(self.bench_config) bench_config['modules']['module_0']['chip_0']['chip_type'] = 'itkpixv1' + bench_config['modules']['module_0']['chip_0']['use_ptot'] = False with scan_digital.DigitalScan(bench_config=bench_config) as scan: scan.start() self.assertTrue(self.check_scan_success()) diff --git a/bdaq53/tests/test_software/test_scans.py b/bdaq53/tests/test_software/test_scans.py index ada9d0c8fd5ced16f7e96a5e114032fce5ed8960..f9829e36336a985ba2fcf9cf8e7ef18c37eb36c7 100644 --- a/bdaq53/tests/test_software/test_scans.py +++ b/bdaq53/tests/test_software/test_scans.py @@ -106,9 +106,6 @@ class TestScans(unittest.TestCase): 'TimewalkScan', # takes too long?! 'PixelRegisterScan', # has custom analysis 'BumpConnThrShScan', # needs periphery - 'DigitalScanPToT', # ptot is only available in rd53b - 'AnalogScanPToT', # ptot is only available in rd53b - 'ThresholdScanPToT' # ptot is only available in rd53b ] # Skip analysis in scans analysis that need data and otherwise fails skip_analysis = ['InTimeThrScan', # complex analysis that crashed on empty data diff --git a/bdaq53/tests/test_software/test_shift_and_inject.py b/bdaq53/tests/test_software/test_shift_and_inject.py new file mode 100644 index 0000000000000000000000000000000000000000..8172ab06b09204454fde54843f4c0956c7a5bf28 --- /dev/null +++ b/bdaq53/tests/test_software/test_shift_and_inject.py @@ -0,0 +1,539 @@ +# +# ------------------------------------------------------------ +# Copyright (c) All rights reserved +# SiLab, Institute of Physics, University of Bonn +# ------------------------------------------------------------ +# + +import os +import unittest +from unittest import mock +import numpy as np +import yaml + +import bdaq53 # noqa: E731 +from bdaq53.tests import bdaq_mock +from bdaq53.tests import utils # noqa: E731 + +from bdaq53.scans.scan_analog import AnalogScan +from bdaq53.scans.scan_digital import DigitalScan +from bdaq53.scans.scan_threshold_fast import FastThresholdScan +from bdaq53.scans.scan_crosstalk import CrosstalkScan +from bdaq53.scans.scan_in_time_threshold import InTimeThrScan +from bdaq53.scans.scan_source_injection import SourceScanInj + + +bdaq53_path = os.path.dirname(bdaq53.__file__) +data_folder = os.path.abspath(os.path.join(bdaq53_path, 'tests', 'test_software', 'output_data')) +bench_config = os.path.abspath(os.path.join(bdaq53_path, 'testbench.yaml')) + + +class TestShiftInject(unittest.TestCase): + + scan_configuration = { + 'start_column': 0, + 'stop_column': 400, + 'start_row': 0, + 'stop_row': 192, + 'n_injections': 100, + + 'VCAL_MED': 500, + 'VCAL_HIGH': 1300} + + @classmethod + def setUpClass(cls): + super(TestShiftInject, cls).setUpClass() + + # Load standard bench config to change in test cases + with open(bench_config) as f: + cls.bench_config = yaml.full_load(f) + + @classmethod + def tearDownClass(cls): + utils.try_remove(os.path.join(data_folder)) + + def old_scan_loop(self, n_injections=100, **kwargs): + for fe, _ in self.chip.masks.shift(masks=['enable', 'injection']): + if not fe == 'skipped' and fe == 'SYNC': + self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) + elif not fe == 'skipped': + self.chip.inject_analog_single(repetitions=n_injections) + + def old_scan_loop_digital(self, n_injections=100, **kwargs): + for fe, _ in self.chip.masks.shift(masks=['enable', 'injection']): + if not fe == 'skipped': + self.chip.inject_digital(repetitions=n_injections) + + def old_scan_loop_crosstalk(self, injection_type='cross_injection', n_injections=100, VCAL_MED=0, VCAL_HIGH_start=0, VCAL_HIGH_stop=4096, VCAL_HIGH_step=102, **_): + vcal_high_range = range(VCAL_HIGH_start, VCAL_HIGH_stop, VCAL_HIGH_step) + for scan_param_id, vcal_high in enumerate(vcal_high_range): + self.chip.setup_analog_injection(vcal_high=vcal_high, vcal_med=VCAL_MED) + for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], pattern=injection_type, cache=True, skip_empty=False): + if fe == 'SYNC': + self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) + else: + self.chip.inject_analog_single(repetitions=n_injections) + + def old_scan_loop_intime_threshold(self, start_column=0, stop_column=400, start_row=0, stop_row=192, n_injections=100, VCAL_MED=500, VCAL_HIGH_start=1000, VCAL_HIGH_stop=4000, VCAL_HIGH_step=100, **_): + vcal_high_range = range(VCAL_HIGH_start, VCAL_HIGH_stop, VCAL_HIGH_step) + finedelay_range = [15] # Case if no finedelay map is provided + for scan_param_id, vcal_high in enumerate(vcal_high_range): + for delay in finedelay_range: + self.chip.setup_analog_injection(vcal_high=vcal_high, vcal_med=VCAL_MED, fine_delay=delay) + for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): + if not fe == 'skipped' and fe == 'SYNC': + self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections) + elif not fe == 'skipped': + self.chip.inject_analog_single(repetitions=n_injections) + + def old_scan_loop_source_scan_inj(self, n_injections=1, **kwargs): + for fe, _ in self.chip.masks.shift(masks=['enable', 'injection']): + if not fe == 'skipped' and fe == 'SYNC': + self.chip.inject_analog_single(send_ecr=True, repetitions=n_injections, send_trigger=False) + elif not fe == 'skipped': + self.chip.inject_analog_single(repetitions=n_injections, send_trigger=False) + + def old_scan_loop_ptot(self, n_injections=100, latency=23, **kwargs): + for fe, active_pixels in (self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='ptot')): + if not fe == 'skipped': + for n in range(8): + self.chip.enable_core_col_clock(core_cols=[i + n for i in range(0, 50, 8)]) + self.chip.inject_analog_single(repetitions=n_injections, latency=latency) + + def old_scan_loop_digital_ptot(self, n_injections=100, latency=23, cal_edge_width=32, **kwargs): + for fe, active_pixels in (self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='ptot')): + if not fe == 'skipped': + self.chip.inject_digital(repetitions=n_injections, latency=23, cal_edge_width=cal_edge_width) + + def old_scan_loop_fast(self, n_injections=100, VCAL_MED=500, VCAL_HIGH_start=1000, VCAL_HIGH_stop=4000, VCAL_HIGH_step=100, CAL_EDGE_latency=9, wait_cycles=300, **_): + self.chip.setup_analog_injection(vcal_high=VCAL_HIGH_start, vcal_med=VCAL_MED) + if self.chip.chip_type.lower() == 'rd53a': + trigger_latency = self.chip.registers['LATENCY_CONFIG'].get() + self.chip.registers['LATENCY_CONFIG'].write(CAL_EDGE_latency * 4 + 12) + vcal_high_range = range(VCAL_HIGH_start, VCAL_HIGH_stop, VCAL_HIGH_step) + scan_param_id_range = range(0, len(vcal_high_range)) + for fe, _ in self.chip.masks.shift(masks=['enable', 'injection'], cache=True): + for cnt, vcal_high in enumerate(vcal_high_range): + self.chip.registers['VCAL_HIGH'].write(vcal_high) + if not fe == 'skipped' and fe == 'SYNC': + self.chip.inject_analog_single(repetitions=n_injections, latency=CAL_EDGE_latency, + wait_cycles=wait_cycles, send_ecr=True) + elif not fe == 'skipped': + self.chip.inject_analog_single(repetitions=n_injections, latency=CAL_EDGE_latency, + wait_cycles=wait_cycles) + vcal_high_range = np.flip(vcal_high_range) + scan_param_id_range = np.flip(scan_param_id_range) + if self.chip.chip_type.lower() == 'rd53a': + self.chip.registers['LATENCY_CONFIG'].write(trigger_latency) + + def old_scan_loop_fast_ptot(self, n_injections=100, VCAL_MED=500, VCAL_HIGH_start=1000, VCAL_HIGH_stop=4000, VCAL_HIGH_step=100, wait_cycles=200, **kwargs): + vcal_high_range = range(VCAL_HIGH_start, VCAL_HIGH_stop, VCAL_HIGH_step) + scan_param_id_range = range(0, len(vcal_high_range)) + self.chip.setup_analog_injection(vcal_high=VCAL_HIGH_start, vcal_med=VCAL_MED) + for fe, active_pixels in (self.chip.masks.shift(masks=['enable', 'injection', 'hitbus'], pattern='ptot', cache=True)): + for cnt, vcal_high in enumerate(vcal_high_range): + self.chip.registers['VCAL_HIGH'].write(vcal_high) + for n in range(8): + self.chip.enable_core_col_clock(core_cols=[i + n for i in range(0, 50, 8)]) + if not fe == 'skipped' and fe == 'SYNC': + self.chip.inject_analog_single(repetitions=n_injections, latency=23, + wait_cycles=200, send_ecr=True) + elif not fe == 'skipped': + self.chip.inject_analog_single(repetitions=n_injections, latency=23, + wait_cycles=200) + vcal_high_range = np.flip(vcal_high_range) + scan_param_id_range = np.flip(scan_param_id_range) + + def test_shift_inject_loop_rd53a_digital(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53A ''' + + self.bench_config['modules'] = {'module_0': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0001", 'chip_type': "rd53a", 'chip_id': 0, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.rd53a.RD53A.write_command', side_effect=store_cmd): + with DigitalScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + cmds_old = cmds.copy() + + cmds = [] + with DigitalScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_digital + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_rd53a(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53A ''' + + self.bench_config['modules'] = {'module_0': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0001", 'chip_type': "rd53a", 'chip_id': 0, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.rd53a.RD53A.write_command', side_effect=store_cmd): + with AnalogScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + cmds_old = cmds.copy() + + cmds = [] + with AnalogScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_fast_rd53a(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53A and fast injection loop ''' + + self.bench_config['modules'] = {'module_0': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0001", 'chip_type': "rd53a", 'chip_id': 0, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.rd53a.RD53A.write_command', side_effect=store_cmd): + with FastThresholdScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + cmds_old = cmds.copy() + + cmds = [] + with FastThresholdScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_fast + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_rd53b(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53B ''' + + self.bench_config['modules'] = {'module_1': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0002", 'chip_type': "itkpixv1", 'chip_id': 15, 'use_ptot': False, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.ITkPixV1.ITkPixV1.write_command', side_effect=store_cmd): + with AnalogScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + cmds_old = cmds.copy() + + cmds = [] + with AnalogScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_rd53b_ptot_digital(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53B and PToT enabled ''' + + self.bench_config['modules'] = {'module_1': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0002", 'chip_type': "itkpixv1", 'chip_id': 15, 'use_ptot': True, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.ITkPixV1.ITkPixV1.write_command', side_effect=store_cmd): + with DigitalScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + cmds_old = cmds.copy() + + with DigitalScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_digital_ptot + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_rd53b_ptot(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53B and PToT enabled ''' + + self.bench_config['modules'] = {'module_1': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0002", 'chip_type': "itkpixv1", 'chip_id': 15, 'use_ptot': True, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.ITkPixV1.ITkPixV1.write_command', side_effect=store_cmd): + with AnalogScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + cmds_old = cmds.copy() + + with AnalogScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_ptot + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_fast_ptot(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53B and PToT enabled and fast injection loop ''' + + self.bench_config['modules'] = {'module_1': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0002", 'chip_type': "itkpixv1", 'chip_id': 15, 'use_ptot': True, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.ITkPixV1.ITkPixV1.write_command', side_effect=store_cmd): + with FastThresholdScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + cmds_old = cmds.copy() + + with FastThresholdScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_fast_ptot + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_crosstalk_rd53a(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53A ''' + + self.bench_config['modules'] = {'module_0': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0001", 'chip_type': "rd53a", 'chip_id': 0, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.rd53a.RD53A.write_command', side_effect=store_cmd): + with CrosstalkScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + cmds_old = cmds.copy() + + cmds = [] + with CrosstalkScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_crosstalk + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_intime_threshold_rd53a(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53A ''' + + self.bench_config['modules'] = {'module_0': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0001", 'chip_type': "rd53a", 'chip_id': 0, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.rd53a.RD53A.write_command', side_effect=store_cmd): + with InTimeThrScan(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + cmds_old = cmds.copy() + + cmds = [] + with InTimeThrScan(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_intime_threshold + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + def test_shift_inject_loop_source_scan_inj_rd53a(self): + ''' Test if new shift and inject function sends the same commands as old one, in case of RD53A ''' + + self.bench_config['modules'] = {'module_0': + {'identifier': "unknown", 'power_cycle': False, + 'chip_0': {'chip_sn': "0x0001", 'chip_type': "rd53a", 'chip_id': 0, + 'receiver': "rx0", 'chip_config_file': None, 'record_chip_status': True, + 'use_good_pixels_diff': False, 'send_data': "tcp://127.0.0.1:5500"}}} + + cmds = [] # store all send command unraveled + + # Use hardware mocks to be able to test without hardware + bhm = bdaq_mock.BdaqMock(n_chips=1) + bhm.start() + + def store_cmd(cmd, repetitions=100): + cmds.extend(cmd) + + with mock.patch('bdaq53.chips.rd53a.RD53A.write_command', side_effect=store_cmd): + with SourceScanInj(self.scan_configuration, bench_config=self.bench_config) as scan: + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + cmds_old = cmds.copy() + + cmds = [] + with SourceScanInj(self.scan_configuration, bench_config=self.bench_config) as scan: + self.chip = scan.chip + scan._scan = self.old_scan_loop_source_scan_inj + scan.configure() + cmds = [] # Reset command list, since only insterested in commands sent during scan for this test + scan.scan() + + # Use numpy arrays with data since they can be checked for equality much faster + a, b = np.array(cmds_old, dtype=np.int16), np.array(cmds, dtype=np.int16) + self.assertTrue(np.array_equal(a, b)) + + bhm.stop() + + +if __name__ == '__main__': + unittest.main() diff --git a/data/fixtures/analog_scan_ptot.h5 b/data/fixtures/analog_scan_ptot.h5 index 7611a22b35f9a32ee142bfd2168e4b57bf0b26e1..dca574c72f95da7d6f89bed08662d8bcc8a27123 100644 --- a/data/fixtures/analog_scan_ptot.h5 +++ b/data/fixtures/analog_scan_ptot.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:475a93ced3b6c109fa583d38f4a2d5c3803c3b2f13f2e050214d1b48e6e9449f -size 59984469 +oid sha256:d7f3f39bebe40eb794839bc5e8fabfaf9a5d6aba787a70d66e65042a9247f6f8 +size 80343812 diff --git a/data/fixtures/analog_scan_ptot_interpreted_result.h5 b/data/fixtures/analog_scan_ptot_interpreted_result.h5 index acb549851dd0e2a6f11bd46645f2536d9367f0d5..e433023953b63c1a03854799e533a36eecf131d2 100644 --- a/data/fixtures/analog_scan_ptot_interpreted_result.h5 +++ b/data/fixtures/analog_scan_ptot_interpreted_result.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7711a58fc63d8fa19653b0b630ffa14edce75328c2876c6b9ce61162bbc90443 -size 8292067 +oid sha256:ef564c525bf07210fd3b1de832ee84c71d30a5b37f665e77dfcd78510d722c16 +size 7836421 diff --git a/data/fixtures/hitor_calibration_interpreted_result.h5 b/data/fixtures/hitor_calibration_interpreted_result.h5 index 0c4fb9a007f2014782f1fdd3c6611b864bac2d36..f598adae008bdc725d2cd6d9099f72e9df4ca1fc 100644 --- a/data/fixtures/hitor_calibration_interpreted_result.h5 +++ b/data/fixtures/hitor_calibration_interpreted_result.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6235d6bf792bd22d396eee514832964aeeffa6d6cd126131179394902617aefe -size 5497370 +oid sha256:541f9c3bce50429d893f38695294c82b3a641b55122f281d0cfba28f597b3e98 +size 5265796 diff --git a/data/fixtures/hitor_calibration_ptot.h5 b/data/fixtures/hitor_calibration_ptot.h5 index 5f290fa6f3f6244e2e084c87ef9e4e531192e698..e74c6c2c855487fc1eb180f73fdbe36fe692350e 100644 --- a/data/fixtures/hitor_calibration_ptot.h5 +++ b/data/fixtures/hitor_calibration_ptot.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b31c609776c5cd063fefad18d24863d44d72ebc2590b818ae17cfc84914110ad -size 5333972 +oid sha256:bc9fdb0948912e15690754bb1d4a6858f86908d13d2f8b8493d06c078287c5c2 +size 6314221 diff --git a/data/fixtures/hitor_calibration_ptot_interpreted_result.h5 b/data/fixtures/hitor_calibration_ptot_interpreted_result.h5 index 90c30de9ec035b9077daff9c499d62d15b5dd353..45ea5bef69603fa2947c4b2a307f3c987094af30 100644 --- a/data/fixtures/hitor_calibration_ptot_interpreted_result.h5 +++ b/data/fixtures/hitor_calibration_ptot_interpreted_result.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94243029779783fb575f0aa45f2d73b280c5e62937f161b0be4b71bf3c9b60a8 -size 15820484 +oid sha256:9444752f0ac51daf8a3db032065dba8579b405761bb3e4e3894a4820b96fc38e +size 16753871