def configure(self): self.dut['Multimeter'].init() logging.info('Initialized multimeter %s' % self.dut['Multimeter'].get_name()) commands = [] commands.extend(self.register.get_commands("ConfMode")) enable_mask = make_pixel_mask( steps=self.mask_steps, shift=0, default=0, value=1 ) # Activate pixels for injection, although they are not read out map( lambda mask_name: self.register.set_pixel_register_value( mask_name, enable_mask), self.enable_shift_masks) commands.extend( self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=True, name=self.enable_shift_masks, joint_write=True)) self.register.set_global_register_value('Colpr_Mode', 0) self.register.set_global_register_value('ExtDigCalSW', 0) self.register.set_global_register_value( 'ExtAnaCalSW', 1) # Route Vcal to external pin commands.extend( self.register.get_commands("WrRegister", name=[ 'Colpr_Addr', 'Colpr_Mode', 'ExtDigCalSW', 'ExtAnaCalSW' ])) commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands)
def configure(self): self.dut['Multimeter'].init() logging.info('Initialized multimeter %s' % self.dut['Multimeter'].get_name()) commands = [] commands.extend(self.register.get_commands("ConfMode")) enable_mask = make_pixel_mask(steps=self.mask_steps, shift=0, default=0, value=1) # Activate pixels for injection, although they are not read out map(lambda mask_name: self.register.set_pixel_register_value(mask_name, enable_mask), self.enable_shift_masks) commands.extend(self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=True, name=self.enable_shift_masks, joint_write=True)) self.register.set_global_register_value('Colpr_Mode', 0) self.register.set_global_register_value('ExtDigCalSW', 0) self.register.set_global_register_value('ExtAnaCalSW', 1) # Route Vcal to external pin commands.extend(self.register.get_commands("WrRegister", name=['Colpr_Addr', 'Colpr_Mode', 'ExtDigCalSW', 'ExtAnaCalSW'])) commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands)
def set_xtalk_mask(): frame = inspect.currentframe() if frame.f_back.f_locals['index'] == 0: mask = make_pixel_mask( steps=self.mask_steps, shift=frame.f_back.f_locals['mask_step']) mask = make_xtalk_mask(mask) map( lambda mask_name: self.register.set_pixel_register_value( mask_name, mask), self.disable_shift_masks) commands = [] commands.append(self.register.get_commands("ConfMode")[0]) commands.extend( self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=True, name=self.xtalk_shift_mask, joint_write=True)) commands.append(self.register.get_commands("RunMode")[0]) self.register_utils.send_commands(commands, concatenate=True)
def scan(self): if not self.plots_filename: self.plots_filename = PdfPages(self.output_filename + '.pdf') self.close_plots = True else: self.close_plots = False cal_lvl1_command = self.register.get_commands( "CAL")[0] + self.register.get_commands( "zeros", length=40)[0] + self.register.get_commands( "LV1")[0] + self.register.get_commands( "zeros", mask_steps=self.mask_steps_gdac)[0] self.write_target_threshold() for gdac_bit in self.gdac_tune_bits: # reset all GDAC bits self.set_gdac_bit(gdac_bit, bit_value=0, send_command=False) last_bit_result = self.n_injections_gdac decreased_threshold = False # needed to determine if the FE is noisy all_bits_zero = True def bits_set(int_type): int_type = int(int_type) position = 0 bits_set = [] while (int_type): if (int_type & 1): bits_set.append(position) position += 1 int_type = int_type >> 1 return bits_set # calculate selected pixels from the mask and the disabled columns select_mask_array = np.zeros(shape=(80, 336), dtype=np.uint8) if not self.enable_mask_steps_gdac: self.enable_mask_steps_gdac = range(self.mask_steps_gdac) for mask_step in self.enable_mask_steps_gdac: select_mask_array += make_pixel_mask(steps=self.mask_steps_gdac, shift=mask_step) for column in bits_set( self.register.get_global_register_value("DisableColumnCnfg")): logging.info('Deselect double column %d' % column) select_mask_array[column, :] = 0 additional_scan = True occupancy_best = 0 gdac_best = self.register_utils.get_gdac() for gdac_bit in self.gdac_tune_bits: if additional_scan: self.set_gdac_bit(gdac_bit) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info('GDAC setting: %d, bit %d = 1', scan_parameter_value, gdac_bit) else: self.set_gdac_bit(gdac_bit, bit_value=0) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info('GDAC setting: %d, bit %d = 0', scan_parameter_value, gdac_bit) with self.readout(GDAC=scan_parameter_value, reset_sram_fifo=True, fill_buffer=True, clear_buffer=True, callback=self.handle_data): scan_loop(self, cal_lvl1_command, repeat_command=self.n_injections_gdac, mask_steps=self.mask_steps_gdac, enable_mask_steps=self.enable_mask_steps_gdac, enable_double_columns=None, same_mask_for_all_dc=True, eol_function=None, digital_injection=False, enable_shift_masks=self.enable_shift_masks, disable_shift_masks=self.disable_shift_masks, restore_shift_masks=True, mask=None, double_column_correction=self.pulser_dac_correction) occupancy_array, _, _ = np.histogram2d(*convert_data_array( data_array_from_data_iterable(self.fifo_readout.data), filter_func=is_data_record, converter_func=get_col_row_array_from_data_record_array), bins=(80, 336), range=[[1, 80], [1, 336]]) self.occ_array_sel_pixel = np.ma.array( occupancy_array, mask=np.logical_not(np.ma.make_mask(select_mask_array)) ) # take only selected pixel into account by creating a mask median_occupancy = np.ma.median(self.occ_array_sel_pixel) if abs(median_occupancy - self.n_injections_gdac / 2) < abs(occupancy_best - self.n_injections_gdac / 2): occupancy_best = median_occupancy gdac_best = self.register_utils.get_gdac() if self.plot_intermediate_steps: plot_three_way(self.occ_array_sel_pixel.transpose(), title="Occupancy (GDAC " + str(scan_parameter_value) + " with tuning bit " + str(gdac_bit) + ")", x_axis_title='Occupancy', filename=self.plots_filename, maximum=self.n_injections_gdac) if abs( median_occupancy - self.n_injections_gdac / 2 ) < self.max_delta_threshold and gdac_bit > 0: # abort if good value already found to save time logging.info( 'Median = %f, good result already achieved (median - Ninj/2 < %f), skipping not varied bits', median_occupancy, self.max_delta_threshold) break if median_occupancy == 0 and decreased_threshold and all_bits_zero: logging.info('Chip may be noisy') if gdac_bit > 0: if ( median_occupancy < self.n_injections_gdac / 2 ): # set GDAC bit to 0 if the occupancy is too lowm, thus decrease threshold logging.info('Median = %f < %f, set bit %d = 0', median_occupancy, self.n_injections_gdac / 2, gdac_bit) self.set_gdac_bit(gdac_bit, bit_value=0) decreased_threshold = True else: # set GDAC bit to 1 if the occupancy is too high, thus increase threshold logging.info('Median = %f > %f, leave bit %d = 1', median_occupancy, self.n_injections_gdac / 2, gdac_bit) decreased_threshold = False all_bits_zero = False elif gdac_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False last_bit_result = self.occ_array_sel_pixel.copy() self.gdac_tune_bits.append( self.gdac_tune_bits[-1] ) # the last tune bit has to be scanned twice else: last_bit_result_median = np.median( last_bit_result[select_mask_array > 0]) logging.info('Scanned bit 0 = 0 with %f instead of %f', median_occupancy, last_bit_result_median) if abs(median_occupancy - self.n_injections_gdac / 2) > abs( last_bit_result_median - self.n_injections_gdac / 2 ): # if bit 0 = 0 is worse than bit 0 = 1, so go back self.set_gdac_bit(gdac_bit, bit_value=1) logging.info('Set bit 0 = 1') self.occ_array_sel_pixel = last_bit_result median_occupancy = np.ma.median( self.occ_array_sel_pixel) else: logging.info('Set bit 0 = 0') if abs(occupancy_best - self.n_injections_gdac / 2) < abs( median_occupancy - self.n_injections_gdac / 2): logging.info( "Binary search converged to non optimal value, take best measured value instead" ) median_occupancy = occupancy_best self.register_utils.set_gdac(gdac_best, send_command=False) self.gdac_best = self.register_utils.get_gdac() if np.all((((self.gdac_best & (1 << np.arange(16)))) > 0 ).astype(int)[self.gdac_tune_bits[:-2]] == 1): logging.warning('Selected GDAC bits reached maximum value') elif np.all((((self.gdac_best & (1 << np.arange(16)))) > 0 ).astype(int)[self.gdac_tune_bits] == 0): logging.warning('Selected GDAC bits reached minimum value') if abs(median_occupancy - self.n_injections_gdac / 2) > 2 * self.max_delta_threshold: logging.warning( 'Global threshold tuning failed. Delta threshold = %f > %f. Vthin_AltCoarse / Vthin_AltFine = %d / %d', abs(median_occupancy - self.n_injections_gdac / 2), self.max_delta_threshold, self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine")) else: logging.info( 'Tuned GDAC to Vthin_AltCoarse / Vthin_AltFine = %d / %d', self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine")) self.gdac_best = self.register_utils.get_gdac()
def scan(self): cal_lvl1_command = self.register.get_commands( "CAL")[0] + self.register.get_commands( "zeros", length=40)[0] + self.register.get_commands("LV1")[0] self.write_target_threshold() scan_parameter_range = [ (2**self.register.global_registers['Vthin_AltFine']['bitlength']), 0 ] # high to low if self.start_gdac: scan_parameter_range[0] = self.start_gdac if self.gdac_lower_limit: scan_parameter_range[1] = self.gdac_lower_limit scan_parameter_range = np.arange(scan_parameter_range[0], scan_parameter_range[1] - 1, self.step_size) logging.info("Scanning %s from %d to %d", 'GDAC', scan_parameter_range[0], scan_parameter_range[-1]) def bits_set(int_type): int_type = int(int_type) position = 0 bits_set = [] while (int_type): if (int_type & 1): bits_set.append(position) position += 1 int_type = int_type >> 1 return bits_set # calculate selected pixels from the mask and the disabled columns select_mask_array = np.zeros(shape=(80, 336), dtype=np.uint8) self.occ_array_sel_pixels_best = select_mask_array.copy() self.occ_array_desel_pixels_best = select_mask_array.copy() if not self.enable_mask_steps_gdac: self.enable_mask_steps_gdac = range(self.mask_steps) for mask_step in self.enable_mask_steps_gdac: select_mask_array += make_pixel_mask(steps=self.mask_steps, shift=mask_step) for column in bits_set( self.register.get_global_register_value("DisableColumnCnfg")): logging.info('Deselect double column %d' % column) select_mask_array[column, :] = 0 gdac_values = [] gdac_occupancies = [] gdac_occ_array_sel_pixels = [] gdac_occ_array_desel_pixels = [] median_occupancy_last_step = None for scan_parameter_value in scan_parameter_range: if self.stop_run.is_set(): break self.register_utils.set_gdac(scan_parameter_value) with self.readout(GDAC=scan_parameter_value, fill_buffer=True): scan_loop(self, command=cal_lvl1_command, repeat_command=self.n_injections_gdac, mask_steps=self.mask_steps, enable_mask_steps=self.enable_mask_steps_gdac, enable_double_columns=None, same_mask_for_all_dc=self.same_mask_for_all_dc, eol_function=None, digital_injection=False, enable_shift_masks=self.enable_shift_masks, disable_shift_masks=self.disable_shift_masks, restore_shift_masks=True, mask=None, double_column_correction=self.pulser_dac_correction) data = convert_data_array( array=self.read_data(), filter_func=is_data_record, converter_func=get_col_row_array_from_data_record_array) occupancy_array, _, _ = np.histogram2d(*data, bins=(80, 336), range=[[1, 80], [1, 336]]) occ_array_sel_pixels = np.ma.array( occupancy_array, mask=np.logical_not(np.ma.make_mask(select_mask_array)) ) # take only selected pixel into account by using the mask occ_array_desel_pixels = np.ma.array( occupancy_array, mask=np.ma.make_mask(select_mask_array) ) # take only de-selected pixel into account by using the inverted mask median_occupancy = np.ma.median(occ_array_sel_pixels) percentile_noise_occupancy = np.percentile( occ_array_desel_pixels.compressed(), 99.0) occupancy_almost_zero = np.allclose(median_occupancy, 0) no_noise = np.allclose(percentile_noise_occupancy, 0) gdac_values.append(self.register_utils.get_gdac()) gdac_occupancies.append(median_occupancy) gdac_occ_array_sel_pixels.append(occ_array_sel_pixels.copy()) gdac_occ_array_desel_pixels.append(occ_array_desel_pixels.copy()) self.occ_array_sel_pixels_best = occ_array_sel_pixels.copy() self.occ_array_desel_pixels_best = occ_array_desel_pixels.copy() # abort early if threshold is found if no_noise and not occupancy_almost_zero and ( median_occupancy_last_step is not None and median_occupancy >= median_occupancy_last_step ) and median_occupancy >= self.n_injections_gdac / 2.0: break if no_noise and not occupancy_almost_zero: median_occupancy_last_step = median_occupancy else: median_occupancy_last_step = 0.0 if not self.stop_run.is_set(): # select best GDAC value sorted_indices = np.argsort(np.array(gdac_values)) occupancy_sorted = np.array(gdac_occupancies)[sorted_indices] gdac_sorted = np.sort(gdac_values) gdac_min_idx = np.where( occupancy_sorted >= self.n_injections_gdac / 2.0)[0][-1] occupancy_sorted_sel = occupancy_sorted[gdac_min_idx:] gdac_sorted_sel = gdac_sorted[gdac_min_idx:] best_index_sel = np.abs( np.array(occupancy_sorted_sel) - self.n_injections_gdac / 2.0).argmin() best_index = sorted_indices[gdac_min_idx:][::-1][best_index_sel] gdac_best = gdac_values[best_index] median_occupancy = gdac_occupancies[best_index] self.register_utils.set_gdac(gdac_best, send_command=False) # for plotting self.occ_array_sel_pixels_best = gdac_occ_array_sel_pixels[ best_index] self.occ_array_desel_pixels_best = gdac_occ_array_desel_pixels[ best_index] self.gdac_best = self.register_utils.get_gdac() if abs(median_occupancy - self.n_injections_gdac / 2.0 ) > self.max_delta_threshold and not self.stop_run.is_set(): if np.all((((self.gdac_best & (1 << np.arange( self.register.global_registers['Vthin_AltFine'] ['bitlength'] + self.register. global_registers['Vthin_AltFine']['bitlength'])))) > 0 ).astype(int) == 0): if self.fail_on_warning: raise RuntimeWarning( 'Selected GDAC bits reached minimum value') else: logging.warning( 'Selected GDAC bits reached minimum value') else: if self.fail_on_warning: raise RuntimeWarning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d' % (abs(median_occupancy - self.n_injections_gdac / 2.0), self.max_delta_threshold, self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine"))) else: logging.warning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d', abs(median_occupancy - self.n_injections_gdac / 2.0), self.max_delta_threshold, self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine")) else: logging.info( 'Tuned GDAC to Vthin_AltCoarse / Vthin_AltFine = %d / %d', self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine"))
def scan(self): def bits_set(int_type): int_type = int(int_type) position = 0 bits_set = [] while(int_type): if(int_type & 1): bits_set.append(position) position += 1 int_type = int_type >> 1 return bits_set # calculate selected pixels from the mask and the disabled columns select_mask_array = np.zeros(shape=(80, 336), dtype=np.uint8) if not self.enable_mask_steps_feedback: self.enable_mask_steps_feedback = range(self.mask_steps) for mask_step in self.enable_mask_steps_feedback: select_mask_array += make_pixel_mask(steps=self.mask_steps, shift=mask_step) for column in bits_set(self.register.get_global_register_value("DisableColumnCnfg")): logging.info('Deselect double column %d' % column) select_mask_array[column, :] = 0 cal_lvl1_command = self.register.get_commands("CAL")[0] + self.register.get_commands("zeros", length=40)[0] + self.register.get_commands("LV1")[0] self.write_target_charge() for feedback_bit in self.feedback_tune_bits: # reset all feedback bits self.set_prmp_vbpf_bit(feedback_bit, bit_value=0) additional_scan = True tot_mean_best = 0.0 feedback_best = self.register.get_global_register_value("PrmpVbpf") feedback_tune_bits = self.feedback_tune_bits[:] for feedback_bit in feedback_tune_bits: if self.stop_run.is_set(): break if additional_scan: self.set_prmp_vbpf_bit(feedback_bit, bit_value=1) logging.info('PrmpVbpf setting: %d, set bit %d = 1', self.register.get_global_register_value("PrmpVbpf"), feedback_bit) else: self.set_prmp_vbpf_bit(feedback_bit, bit_value=0) logging.info('PrmpVbpf setting: %d, set bit %d = 0', self.register.get_global_register_value("PrmpVbpf"), feedback_bit) scan_parameter_value = self.register.get_global_register_value("PrmpVbpf") with self.readout(PrmpVbpf=scan_parameter_value, fill_buffer=True): scan_loop(self, command=cal_lvl1_command, repeat_command=self.n_injections_feedback, mask_steps=self.mask_steps, enable_mask_steps=self.enable_mask_steps_feedback, enable_double_columns=None, same_mask_for_all_dc=self.same_mask_for_all_dc, eol_function=None, digital_injection=False, enable_shift_masks=self.enable_shift_masks, disable_shift_masks=self.disable_shift_masks, restore_shift_masks=True, mask=None, double_column_correction=self.pulser_dac_correction) data = convert_data_array(array=self.read_data(), filter_func=is_data_record, converter_func=get_col_row_tot_array_from_data_record_array) col_row_tot_array = np.column_stack(data) occupancy_array, _, _ = np.histogram2d(col_row_tot_array[:, 0], col_row_tot_array[:, 1], bins=(80, 336), range=[[1, 80], [1, 336]]) occupancy_array = np.ma.array(occupancy_array, mask=np.logical_not(np.ma.make_mask(select_mask_array))) # take only selected pixel into account by creating a mask occupancy_array = np.ma.masked_where(occupancy_array > self.n_injections_feedback, occupancy_array) col_row_tot_hist = np.histogramdd(col_row_tot_array, bins=(80, 336, 16), range=[[1, 80], [1, 336], [0, 15]])[0] tot_mean_array = np.average(col_row_tot_hist, axis=2, weights=range(0, 16)) * sum(range(0, 16)) / self.n_injections_feedback tot_mean_array = np.ma.array(tot_mean_array, mask=occupancy_array.mask) # keep noisy pixels out mean_tot = np.ma.mean(tot_mean_array) if abs(mean_tot - self.target_tot) < abs(tot_mean_best - self.target_tot): tot_mean_best = mean_tot feedback_best = self.register.get_global_register_value("PrmpVbpf") logging.info('Mean ToT = %.2f', mean_tot) tot_array = col_row_tot_array[:, 2] self.tot_hist, _ = np.histogram(a=tot_array, range=(0, 16), bins=16) if self.plot_intermediate_steps: plot_tot(hist=self.tot_hist, title='ToT distribution (PrmpVbpf ' + str(scan_parameter_value) + ')', filename=self.plots_filename) # if abs(mean_tot - self.target_tot) < self.max_delta_tot and feedback_bit > 0: # abort if good value already found to save time # logging.info('Good result already achieved, skipping missing bits') # break if feedback_bit > 0: # TODO: if feedback is to high, no hits if mean_tot < self.target_tot: self.set_prmp_vbpf_bit(feedback_bit, bit_value=0) logging.info('Mean ToT = %.2f < %.2f ToT, set bit %d = 0', mean_tot, self.target_tot, feedback_bit) else: logging.info('Mean ToT = %.2f > %.2f ToT, keep bit %d = 1', mean_tot, self.target_tot, feedback_bit) elif feedback_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False last_mean_tot = mean_tot last_tot_hist = self.tot_hist.copy() feedback_tune_bits.append(0) # bit 0 has to be scanned twice else: logging.info('Measured %.2f with bit 0 = 0 and %.2f with bit 0 = 1', mean_tot, last_mean_tot) if(abs(mean_tot - self.target_tot) > abs(last_mean_tot - self.target_tot)): # if bit 0 = 0 is worse than bit 0 = 1, so go back logging.info('Set bit 0 = 1') self.set_prmp_vbpf_bit(0, bit_value=1) self.tot_hist = last_tot_hist.copy() mean_tot = last_mean_tot else: logging.info('Keep bit 0 = 0') # select best Feedback value if abs(mean_tot - self.target_tot) > abs(tot_mean_best - self.target_tot): logging.info("Binary search converged to non-optimal value, apply best Feedback value, change PrmpVbpf from %d to %d", self.register.get_global_register_value("PrmpVbpf"), feedback_best) mean_tot = tot_mean_best self.register.set_global_register_value("PrmpVbpf", feedback_best) self.feedback_best = self.register.get_global_register_value("PrmpVbpf") if abs(mean_tot - self.target_tot) > 2 * self.max_delta_tot and not self.stop_run.is_set(): if np.all((((self.feedback_best & (1 << np.arange(self.register.global_registers['PrmpVbpf']['bitlength'])))) > 0).astype(int)[self.feedback_tune_bits] == 1): if self.fail_on_warning: raise RuntimeWarning('Selected Feedback bits reached maximum value') else: logging.warning('Selected Feedback bits reached maximum value') elif np.all((((self.feedback_best & (1 << np.arange(self.register.global_registers['PrmpVbpf']['bitlength'])))) > 0).astype(int)[self.feedback_tune_bits] == 0): if self.fail_on_warning: raise RuntimeWarning('Selected Feedback bits reached minimum value') else: logging.warning('Selected Feedback bits reached minimum value') else: if self.fail_on_warning: raise RuntimeWarning('Global feedback tuning failed. Delta ToT = %.2f > %.2f. PrmpVbpf = %d' % (abs(mean_tot - self.target_tot), self.max_delta_tot, self.register.get_global_register_value("PrmpVbpf"))) else: logging.warning('Global feedback tuning failed. Delta ToT = %.2f > %.2f. PrmpVbpf = %d', abs(mean_tot - self.target_tot), self.max_delta_tot, self.register.get_global_register_value("PrmpVbpf")) else: logging.info('Tuned PrmpVbpf to %d', self.register.get_global_register_value("PrmpVbpf"))
def scan(self): if not self.plots_filename: self.plots_filename = PdfPages(self.output_filename + '.pdf') self.close_plots = True else: self.close_plots = False cal_lvl1_command = self.register.get_commands( "CAL")[0] + self.register.get_commands( "zeros", length=40)[0] + self.register.get_commands("LV1")[0] self.write_target_threshold() for gdac_bit in self.gdac_tune_bits: # reset all GDAC bits self.set_gdac_bit(gdac_bit, bit_value=0, send_command=False) def bits_set(int_type): int_type = int(int_type) position = 0 bits_set = [] while (int_type): if (int_type & 1): bits_set.append(position) position += 1 int_type = int_type >> 1 return bits_set # calculate selected pixels from the mask and the disabled columns select_mask_array = np.zeros(shape=(80, 336), dtype=np.uint8) self.occ_array_sel_pixels_best = select_mask_array.copy() self.occ_array_desel_pixels_best = select_mask_array.copy() if not self.enable_mask_steps_gdac: self.enable_mask_steps_gdac = range(self.mask_steps) for mask_step in self.enable_mask_steps_gdac: select_mask_array += make_pixel_mask(steps=self.mask_steps, shift=mask_step) for column in bits_set( self.register.get_global_register_value("DisableColumnCnfg")): logging.info('Deselect double column %d' % column) select_mask_array[column, :] = 0 additional_scan = True additional_scan_ongoing = False occupancy_best = 0.0 last_good_gdac_bit = self.gdac_tune_bits[0] last_good_gdac_scan_step = 0 gdac_tune_bits_permutation = 0 gdac_best = self.register_utils.get_gdac() gdac_tune_bits = self.gdac_tune_bits[:] min_gdac_with_occupancy = None for gdac_scan_step, gdac_bit in enumerate(gdac_tune_bits): if additional_scan: self.set_gdac_bit(gdac_bit, bit_value=1, send_command=True) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info('GDAC setting: %d, set bit %d = 1', scan_parameter_value, gdac_bit) else: self.set_gdac_bit(gdac_bit, bit_value=0, send_command=True) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info('GDAC setting: %d, set bit %d = 0', scan_parameter_value, gdac_bit) with self.readout(GDAC=scan_parameter_value, reset_sram_fifo=True, fill_buffer=True, clear_buffer=True, callback=self.handle_data): scan_loop(self, command=cal_lvl1_command, repeat_command=self.n_injections_gdac, mask_steps=self.mask_steps, enable_mask_steps=self.enable_mask_steps_gdac, enable_double_columns=None, same_mask_for_all_dc=self.same_mask_for_all_dc, eol_function=None, digital_injection=False, enable_shift_masks=self.enable_shift_masks, disable_shift_masks=self.disable_shift_masks, restore_shift_masks=True, mask=None, double_column_correction=self.pulser_dac_correction) occupancy_array, _, _ = np.histogram2d(*convert_data_array( data_array_from_data_iterable(self.fifo_readout.data), filter_func=logical_and(is_fe_word, is_data_record), converter_func=get_col_row_array_from_data_record_array), bins=(80, 336), range=[[1, 80], [1, 336]]) occ_array_sel_pixels = np.ma.array( occupancy_array, mask=np.logical_not(np.ma.make_mask(select_mask_array)) ) # take only selected pixel into account by using the mask occ_array_desel_pixels = np.ma.array( occupancy_array, mask=np.ma.make_mask(select_mask_array) ) # take only de-selected pixel into account by using the inverted mask median_occupancy = np.ma.median(occ_array_sel_pixels) noise_occupancy = np.ma.median(occ_array_desel_pixels) occupancy_almost_zero = np.allclose(median_occupancy, 0) no_noise = np.allclose(noise_occupancy, 0) if abs(median_occupancy - self.n_injections_gdac / 2) < abs(occupancy_best - self.n_injections_gdac / 2): occupancy_best = median_occupancy gdac_best = self.register_utils.get_gdac() self.occ_array_sel_pixels_best = occ_array_sel_pixels.copy() self.occ_array_desel_pixels_best = occ_array_desel_pixels.copy( ) if self.plot_intermediate_steps: plot_three_way(self.occ_array_sel_pixel.transpose(), title="Occupancy (GDAC " + str(scan_parameter_value) + " with tuning bit " + str(gdac_bit) + ")", x_axis_title='Occupancy', filename=self.plots_filename, maximum=self.n_injections_gdac) if not occupancy_almost_zero and no_noise: if min_gdac_with_occupancy is None: min_gdac_with_occupancy = self.register_utils.get_gdac() else: min_gdac_with_occupancy = min( min_gdac_with_occupancy, self.register_utils.get_gdac()) if gdac_bit > 0: # GDAC too low, no hits if occupancy_almost_zero and no_noise and self.register_utils.get_gdac( ) < min_gdac_with_occupancy: logging.info( 'Median = %.2f > %.2f, GDAC possibly too low, keep bit %d = 1', median_occupancy, self.n_injections_gdac / 2, gdac_bit) # GDAC too high, less hits, decrease GDAC elif no_noise and median_occupancy < ( self.n_injections_gdac / 2 ): # set GDAC bit to 0 if the occupancy is too low, thus decrease threshold try: next_gdac_bit = gdac_tune_bits[gdac_scan_step + 1] except IndexError: next_gdac_bit = None # check if new value is below lower limit if self.gdac_lower_limit and ( next_gdac_bit is not None and self.register_utils.get_gdac() - 2**gdac_bit + 2**next_gdac_bit < self.gdac_lower_limit) or ( next_gdac_bit is None and self.register_utils.get_gdac() - 2**gdac_bit < self.gdac_lower_limit): logging.info( 'Median = %.2f < %.2f, reaching lower GDAC limit, keep bit %d = 1', median_occupancy, self.n_injections_gdac / 2, gdac_bit) else: logging.info('Median = %.2f < %.2f, set bit %d = 0', median_occupancy, self.n_injections_gdac / 2, gdac_bit) self.set_gdac_bit( gdac_bit, bit_value=0, send_command=False ) # do not write, might be too low, do this in next iteration # GDAC too low, more hits else: logging.info('Median = %.2f > %.2f, keep bit %d = 1', median_occupancy, self.n_injections_gdac / 2, gdac_bit) elif gdac_bit == 0: if not additional_scan_ongoing and ( (occupancy_almost_zero and no_noise) or not no_noise ) and len(self.gdac_tune_bits) > last_good_gdac_scan_step + 2: self.set_gdac_bit(0, bit_value=0, send_command=False) # turn off LSB if len( gdac_tune_bits ) == gdac_scan_step + 1 and gdac_tune_bits_permutation == 0: # min. 2 bits for bin search self.set_gdac_bit( last_good_gdac_bit, bit_value=1, send_command=False) # always enable highest bit gdac_tune_bits.extend( self.gdac_tune_bits[last_good_gdac_scan_step + 1:] ) # repeat all scan stept from last bit for gdac_clear_bit in self.gdac_tune_bits[: last_good_gdac_scan_step]: self.set_gdac_bit(gdac_clear_bit, bit_value=0, send_command=False) if 2**last_good_gdac_scan_step == 1: # last step, cleanup last_good_gdac_bit = self.gdac_tune_bits[ last_good_gdac_scan_step + 1] last_good_gdac_scan_step += 1 else: gdac_tune_bits_permutation += 1 else: gdac_tune_bits_permutation_header = map( int, bin(gdac_tune_bits_permutation)[2:].zfill( last_good_gdac_scan_step)) for gdac_permutation_bit, gdac_permutation_bit_value in enumerate( gdac_tune_bits_permutation_header): self.set_gdac_bit( self.gdac_tune_bits[gdac_permutation_bit], bit_value=gdac_permutation_bit_value, send_command=False) gdac_tune_bits.extend( self.gdac_tune_bits[last_good_gdac_scan_step + 1:]) if 2**last_good_gdac_scan_step > gdac_tune_bits_permutation + 1: gdac_tune_bits_permutation += 1 else: # last step, cleanup gdac_tune_bits_permutation = 0 last_good_gdac_bit = self.gdac_tune_bits[ last_good_gdac_scan_step + 1] last_good_gdac_scan_step += 1 elif additional_scan: # scan bit = 0 with the correct value again additional_scan = False additional_scan_ongoing = True last_occ_array_sel_pixels = occ_array_sel_pixels.copy() last_occ_array_desel_pixels = occ_array_desel_pixels.copy() gdac_tune_bits.append( 0) # the last tune bit has to be scanned twice else: additional_scan_ongoing = False last_median_occupancy = np.ma.median( last_occ_array_sel_pixels) logging.info( 'Measured %.2f with bit 0 = 0 with and %.2f with bit 0 = 1', median_occupancy, last_median_occupancy) if abs(median_occupancy - self.n_injections_gdac / 2) > abs( last_median_occupancy - self.n_injections_gdac / 2 ): # if bit 0 = 0 is worse than bit 0 = 1, so go back logging.info('Set bit 0 = 1') self.set_gdac_bit(0, bit_value=1, send_command=True) occ_array_sel_pixels = last_occ_array_sel_pixels.copy() occ_array_desel_pixels = last_occ_array_desel_pixels.copy( ) median_occupancy = last_median_occupancy else: logging.info('Keep bit 0 = 0') # select best GDAC value if abs(occupancy_best - self.n_injections_gdac / 2) < abs(median_occupancy - self.n_injections_gdac / 2): logging.info( "Binary search converged to non-optimal value, apply best GDAC value, change GDAC from %d to %d", self.register_utils.get_gdac(), gdac_best) median_occupancy = occupancy_best self.register_utils.set_gdac(gdac_best, send_command=False) self.gdac_best = self.register_utils.get_gdac() if abs(median_occupancy - self.n_injections_gdac / 2) > self.max_delta_threshold: if np.all((((self.gdac_best & (1 << np.arange( self.register.global_registers['Vthin_AltFine'] ['bitlength'] + self.register.global_registers['Vthin_AltFine'] ['bitlength'])))) > 0).astype(int)[self.gdac_tune_bits] == 1): if self.fail_on_warning: raise RuntimeWarning( 'Selected GDAC bits reached maximum value') else: logging.warning('Selected GDAC bits reached maximum value') elif np.all((((self.gdac_best & (1 << np.arange( self.register.global_registers['Vthin_AltFine'] ['bitlength'] + self.register.global_registers['Vthin_AltFine'] ['bitlength'])))) > 0).astype(int)[self.gdac_tune_bits] == 0): if self.fail_on_warning: raise RuntimeWarning( 'Selected GDAC bits reached minimum value') else: logging.warning('Selected GDAC bits reached minimum value') else: if self.fail_on_warning: raise RuntimeWarning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d' % (abs(median_occupancy - self.n_injections_gdac / 2), self.max_delta_threshold, self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine"))) else: logging.warning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d', abs(median_occupancy - self.n_injections_gdac / 2), self.max_delta_threshold, self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine")) else: logging.info( 'Tuned GDAC to Vthin_AltCoarse / Vthin_AltFine = %d / %d', self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine"))
def scan(self): if not self.plots_filename: self.plots_filename = PdfPages(self.output_filename + ".pdf") self.close_plots = True else: self.close_plots = False cal_lvl1_command = ( self.register.get_commands("CAL")[0] + self.register.get_commands("zeros", length=40)[0] + self.register.get_commands("LV1")[0] + self.register.get_commands("zeros", mask_steps=self.mask_steps_gdac)[0] ) self.write_target_threshold() for gdac_bit in self.gdac_tune_bits: # reset all GDAC bits self.set_gdac_bit(gdac_bit, bit_value=0, send_command=False) last_bit_result = self.n_injections_gdac decreased_threshold = False # needed to determine if the FE is noisy all_bits_zero = True def bits_set(int_type): int_type = int(int_type) position = 0 bits_set = [] while int_type: if int_type & 1: bits_set.append(position) position += 1 int_type = int_type >> 1 return bits_set # calculate selected pixels from the mask and the disabled columns select_mask_array = np.zeros(shape=(80, 336), dtype=np.uint8) if not self.enable_mask_steps_gdac: self.enable_mask_steps_gdac = range(self.mask_steps_gdac) for mask_step in self.enable_mask_steps_gdac: select_mask_array += make_pixel_mask(steps=self.mask_steps_gdac, shift=mask_step) for column in bits_set(self.register.get_global_register_value("DisableColumnCnfg")): logging.info("Deselect double column %d" % column) select_mask_array[column, :] = 0 additional_scan = True occupancy_best = 0 gdac_best = self.register_utils.get_gdac() for gdac_bit in self.gdac_tune_bits: if additional_scan: self.set_gdac_bit(gdac_bit) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info("GDAC setting: %d, bit %d = 1", scan_parameter_value, gdac_bit) else: self.set_gdac_bit(gdac_bit, bit_value=0) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info("GDAC setting: %d, bit %d = 0", scan_parameter_value, gdac_bit) with self.readout( GDAC=scan_parameter_value, reset_sram_fifo=True, fill_buffer=True, clear_buffer=True, callback=self.handle_data, ): scan_loop( self, cal_lvl1_command, repeat_command=self.n_injections_gdac, mask_steps=self.mask_steps_gdac, enable_mask_steps=self.enable_mask_steps_gdac, enable_double_columns=None, same_mask_for_all_dc=True, eol_function=None, digital_injection=False, enable_shift_masks=self.enable_shift_masks, disable_shift_masks=self.disable_shift_masks, restore_shift_masks=True, mask=None, double_column_correction=self.pulser_dac_correction, ) occupancy_array, _, _ = np.histogram2d( *convert_data_array( data_array_from_data_iterable(self.fifo_readout.data), filter_func=is_data_record, converter_func=get_col_row_array_from_data_record_array, ), bins=(80, 336), range=[[1, 80], [1, 336]] ) self.occ_array_sel_pixel = np.ma.array( occupancy_array, mask=np.logical_not(np.ma.make_mask(select_mask_array)) ) # take only selected pixel into account by creating a mask median_occupancy = np.ma.median(self.occ_array_sel_pixel) if abs(median_occupancy - self.n_injections_gdac / 2) < abs(occupancy_best - self.n_injections_gdac / 2): occupancy_best = median_occupancy gdac_best = self.register_utils.get_gdac() if self.plot_intermediate_steps: plot_three_way( self.occ_array_sel_pixel.transpose(), title="Occupancy (GDAC " + str(scan_parameter_value) + " with tuning bit " + str(gdac_bit) + ")", x_axis_title="Occupancy", filename=self.plots_filename, maximum=self.n_injections_gdac, ) if ( abs(median_occupancy - self.n_injections_gdac / 2) < self.max_delta_threshold and gdac_bit > 0 ): # abort if good value already found to save time logging.info( "Median = %f, good result already achieved (median - Ninj/2 < %f), skipping not varied bits", median_occupancy, self.max_delta_threshold, ) break if median_occupancy == 0 and decreased_threshold and all_bits_zero: logging.info("Chip may be noisy") if gdac_bit > 0: if ( median_occupancy < self.n_injections_gdac / 2 ): # set GDAC bit to 0 if the occupancy is too lowm, thus decrease threshold logging.info( "Median = %f < %f, set bit %d = 0", median_occupancy, self.n_injections_gdac / 2, gdac_bit ) self.set_gdac_bit(gdac_bit, bit_value=0) decreased_threshold = True else: # set GDAC bit to 1 if the occupancy is too high, thus increase threshold logging.info( "Median = %f > %f, leave bit %d = 1", median_occupancy, self.n_injections_gdac / 2, gdac_bit ) decreased_threshold = False all_bits_zero = False elif gdac_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False last_bit_result = self.occ_array_sel_pixel.copy() self.gdac_tune_bits.append(self.gdac_tune_bits[-1]) # the last tune bit has to be scanned twice else: last_bit_result_median = np.median(last_bit_result[select_mask_array > 0]) logging.info("Scanned bit 0 = 0 with %f instead of %f", median_occupancy, last_bit_result_median) if abs(median_occupancy - self.n_injections_gdac / 2) > abs( last_bit_result_median - self.n_injections_gdac / 2 ): # if bit 0 = 0 is worse than bit 0 = 1, so go back self.set_gdac_bit(gdac_bit, bit_value=1) logging.info("Set bit 0 = 1") self.occ_array_sel_pixel = last_bit_result median_occupancy = np.ma.median(self.occ_array_sel_pixel) else: logging.info("Set bit 0 = 0") if abs(occupancy_best - self.n_injections_gdac / 2) < abs( median_occupancy - self.n_injections_gdac / 2 ): logging.info("Binary search converged to non optimal value, take best measured value instead") median_occupancy = occupancy_best self.register_utils.set_gdac(gdac_best, send_command=False) self.gdac_best = self.register_utils.get_gdac() if np.all((((self.gdac_best & (1 << np.arange(16)))) > 0).astype(int)[self.gdac_tune_bits[:-2]] == 1): logging.warning("Selected GDAC bits reached maximum value") elif np.all((((self.gdac_best & (1 << np.arange(16)))) > 0).astype(int)[self.gdac_tune_bits] == 0): logging.warning("Selected GDAC bits reached minimum value") if abs(median_occupancy - self.n_injections_gdac / 2) > 2 * self.max_delta_threshold: logging.warning( "Global threshold tuning failed. Delta threshold = %f > %f. Vthin_AltCoarse / Vthin_AltFine = %d / %d", abs(median_occupancy - self.n_injections_gdac / 2), self.max_delta_threshold, self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine"), ) else: logging.info( "Tuned GDAC to Vthin_AltCoarse / Vthin_AltFine = %d / %d", self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine"), ) self.gdac_best = self.register_utils.get_gdac()
def scan(self): if not self.plots_filename: self.plots_filename = PdfPages(self.output_filename + '.pdf') self.close_plots = True else: self.close_plots = False cal_lvl1_command = self.register.get_commands( "CAL")[0] + self.register.get_commands( "zeros", length=40)[0] + self.register.get_commands("LV1")[0] self.write_target_threshold() scan_parameter_range = [ (2**self.register.global_registers['Vthin_AltFine']['bitlength']), 0 ] # high to low if self.scan_parameters.GDAC[0]: scan_parameter_range[0] = self.scan_parameters.GDAC[0] if self.scan_parameters.GDAC[1]: scan_parameter_range[1] = self.scan_parameters.GDAC[1] scan_parameter_range = range(scan_parameter_range[0], scan_parameter_range[1] - 1, self.step_size) logging.info("Scanning %s from %d to %d", 'GDAC', scan_parameter_range[0], scan_parameter_range[-1]) def bits_set(int_type): int_type = int(int_type) position = 0 bits_set = [] while (int_type): if (int_type & 1): bits_set.append(position) position += 1 int_type = int_type >> 1 return bits_set # calculate selected pixels from the mask and the disabled columns select_mask_array = np.zeros(shape=(80, 336), dtype=np.uint8) self.occ_array_sel_pixels_best = select_mask_array.copy() self.occ_array_desel_pixels_best = select_mask_array.copy() if not self.enable_mask_steps_gdac: self.enable_mask_steps_gdac = range(self.mask_steps) for mask_step in self.enable_mask_steps_gdac: select_mask_array += make_pixel_mask(steps=self.mask_steps, shift=mask_step) for column in bits_set( self.register.get_global_register_value("DisableColumnCnfg")): logging.info('Deselect double column %d' % column) select_mask_array[column, :] = 0 occupancy_best = 0.0 median_occupancy_last_step = 0.0 gdac_best = self.register_utils.get_gdac() for gdac_scan_step, scan_parameter_value in enumerate( scan_parameter_range): self.register_utils.set_gdac(scan_parameter_value) with self.readout(GDAC=scan_parameter_value, reset_sram_fifo=True, fill_buffer=True, clear_buffer=True, callback=self.handle_data): scan_loop(self, command=cal_lvl1_command, repeat_command=self.n_injections_gdac, mask_steps=self.mask_steps, enable_mask_steps=self.enable_mask_steps_gdac, enable_double_columns=None, same_mask_for_all_dc=self.same_mask_for_all_dc, eol_function=None, digital_injection=False, enable_shift_masks=self.enable_shift_masks, disable_shift_masks=self.disable_shift_masks, restore_shift_masks=True, mask=None, double_column_correction=self.pulser_dac_correction) occupancy_array, _, _ = np.histogram2d(*convert_data_array( data_array_from_data_iterable(self.fifo_readout.data), filter_func=logical_and(is_fe_word, is_data_record), converter_func=get_col_row_array_from_data_record_array), bins=(80, 336), range=[[1, 80], [1, 336]]) occ_array_sel_pixels = np.ma.array( occupancy_array, mask=np.logical_not(np.ma.make_mask(select_mask_array)) ) # take only selected pixel into account by using the mask occ_array_desel_pixels = np.ma.array( occupancy_array, mask=np.ma.make_mask(select_mask_array) ) # take only de-selected pixel into account by using the inverted mask median_occupancy = np.ma.median(occ_array_sel_pixels) noise_occupancy = np.ma.median(occ_array_desel_pixels) occupancy_almost_zero = np.allclose(median_occupancy, 0) no_noise = np.allclose(noise_occupancy, 0) if no_noise and not occupancy_almost_zero and abs( median_occupancy - self.n_injections_gdac / 2) < abs(occupancy_best - self.n_injections_gdac / 2): occupancy_best = median_occupancy gdac_best = self.register_utils.get_gdac() self.occ_array_sel_pixels_best = occ_array_sel_pixels.copy() self.occ_array_desel_pixels_best = occ_array_desel_pixels.copy( ) if self.plot_intermediate_steps: plot_three_way(self.occ_array_sel_pixel.transpose(), title="Occupancy (GDAC " + str(scan_parameter_value) + ")", x_axis_title='Occupancy', filename=self.plots_filename, maximum=self.n_injections_gdac) if no_noise and not occupancy_almost_zero and median_occupancy >= median_occupancy_last_step and median_occupancy >= self.n_injections_gdac / 2: break if no_noise and not occupancy_almost_zero: median_occupancy_last_step = median_occupancy else: median_occupancy_last_step = 0.0 self.register_utils.set_gdac(gdac_best, send_command=False) median_occupancy = occupancy_best self.gdac_best = self.register_utils.get_gdac() if abs(median_occupancy - self.n_injections_gdac / 2) > self.max_delta_threshold: if np.all((((self.gdac_best & (1 << np.arange( self.register.global_registers['Vthin_AltFine'] ['bitlength'] + self.register.global_registers['Vthin_AltFine'] ['bitlength'])))) > 0).astype(int) == 0): if self.fail_on_warning: raise RuntimeWarning( 'Selected GDAC bits reached minimum value') else: logging.warning('Selected GDAC bits reached minimum value') else: if self.fail_on_warning: raise RuntimeWarning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d' % (abs(median_occupancy - self.n_injections_gdac / 2), self.max_delta_threshold, self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine"))) else: logging.warning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d', abs(median_occupancy - self.n_injections_gdac / 2), self.max_delta_threshold, self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine")) else: logging.info( 'Tuned GDAC to Vthin_AltCoarse / Vthin_AltFine = %d / %d', self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine"))
def scan(self): cal_lvl1_command = self.register.get_commands( "CAL")[0] + self.register.get_commands( "zeros", length=40)[0] + self.register.get_commands("LV1")[0] self.write_target_threshold() def bits_set(int_type): int_type = int(int_type) position = 0 bits_set = [] while (int_type): if (int_type & 1): bits_set.append(position) position += 1 int_type = int_type >> 1 return bits_set # calculate selected pixels from the mask and the disabled columns select_mask_array = np.zeros(shape=(80, 336), dtype=np.uint8) if not self.enable_mask_steps_gdac: self.enable_mask_steps_gdac = range(self.mask_steps) for mask_step in self.enable_mask_steps_gdac: select_mask_array += make_pixel_mask(steps=self.mask_steps, shift=mask_step) for column in bits_set( self.register.get_global_register_value("DisableColumnCnfg")): logging.info('Deselect double column %d' % column) select_mask_array[column, :] = 0 gdacs_above_threshold = [] additional_scan_ongoing = False last_good_gdac_bit = self.gdac_tune_bits[0] last_good_gdac_scan_step = 0 gdac_tune_bits_permutation = 0 gdac_values = [] gdac_occupancies = [] gdac_occ_array_sel_pixels = [] gdac_occ_array_desel_pixels = [] gdac_tune_bits = self.gdac_tune_bits[:] min_gdac_with_occupancy = None for gdac_scan_step, gdac_bit in enumerate(gdac_tune_bits): if self.stop_run.is_set(): break # set all higher GDAC bits gdac_tune_bits_permutation_header = map( int, bin(2**last_good_gdac_scan_step - 1 - gdac_tune_bits_permutation)[2:].zfill( last_good_gdac_scan_step))[-last_good_gdac_scan_step:] for gdac_permutation_bit, gdac_permutation_bit_value in enumerate( gdac_tune_bits_permutation_header): self.set_gdac_bit(self.gdac_tune_bits[gdac_permutation_bit], bit_value=gdac_permutation_bit_value, send_command=False) # clear all lower GDAC bits for clear_gdac_bit in self.gdac_tune_bits: if clear_gdac_bit < gdac_bit: self.set_gdac_bit(clear_gdac_bit, bit_value=0, send_command=False) if additional_scan_ongoing: self.set_gdac_bit(gdac_bit, bit_value=0, send_command=True) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info('GDAC setting: %d, set bit %d = 0', scan_parameter_value, gdac_bit) else: # default self.set_gdac_bit(gdac_bit, bit_value=1, send_command=False) scan_parameter_value = ( self.register.get_global_register_value("Vthin_AltCoarse") << 8 ) + self.register.get_global_register_value("Vthin_AltFine") logging.info('GDAC setting: %d, set bit %d = 1', scan_parameter_value, gdac_bit) # check if GDAC values are too low or were already scanned if not additional_scan_ongoing and ( (self.register_utils.get_gdac() in gdac_values) or (self.gdac_lower_limit and self.register_utils.get_gdac() < self.gdac_lower_limit) or (min_gdac_with_occupancy and self.register_utils.get_gdac() <= min_gdac_with_occupancy)): if gdac_tune_bits_permutation + 1 == 2**last_good_gdac_scan_step: # next permutation step gdac_tune_bits_permutation = 0 last_good_gdac_scan_step += 1 else: gdac_tune_bits_permutation += 1 for i in range(len(gdac_tune_bits) - (gdac_scan_step + 1)): gdac_tune_bits.pop() gdac_tune_bits.extend( self.gdac_tune_bits[last_good_gdac_scan_step:] ) # repeat all scan steps from last bit continue # write GDAC self.register_utils.set_gdac(self.register_utils.get_gdac(), send_command=True) # start scan loop with self.readout(GDAC=scan_parameter_value, fill_buffer=True): scan_loop(self, command=cal_lvl1_command, repeat_command=self.n_injections_gdac, mask_steps=self.mask_steps, enable_mask_steps=self.enable_mask_steps_gdac, enable_double_columns=None, same_mask_for_all_dc=self.same_mask_for_all_dc, eol_function=None, digital_injection=False, enable_shift_masks=self.enable_shift_masks, disable_shift_masks=self.disable_shift_masks, restore_shift_masks=True, mask=None, double_column_correction=self.pulser_dac_correction) # calculate arrays from data data = convert_data_array( array=self.read_data(), filter_func=is_data_record, converter_func=get_col_row_array_from_data_record_array) occupancy_array, _, _ = np.histogram2d(*data, bins=(80, 336), range=[[1, 80], [1, 336]]) occ_array_sel_pixels = np.ma.array( occupancy_array, mask=np.logical_not(np.ma.make_mask(select_mask_array)) ) # take only selected pixel into account by using the mask occ_array_desel_pixels = np.ma.array( occupancy_array, mask=np.ma.make_mask(select_mask_array) ) # take only de-selected pixel into account by using the inverted mask median_occupancy = np.ma.median(occ_array_sel_pixels) percentile_noise_occupancy = np.percentile( occ_array_desel_pixels.compressed(), 99.0) occupancy_almost_zero = np.allclose(median_occupancy, 0) no_noise = np.allclose(percentile_noise_occupancy, 0) gdac_values.append(self.register_utils.get_gdac()) gdac_occupancies.append(median_occupancy) gdac_occ_array_sel_pixels.append(occ_array_sel_pixels.copy()) gdac_occ_array_desel_pixels.append(occ_array_desel_pixels.copy()) self.occ_array_sel_pixels_best = occ_array_sel_pixels.copy() self.occ_array_desel_pixels_best = occ_array_desel_pixels.copy() if len(gdac_values) >= 2: for index, scanned_gdac in enumerate(gdac_values[:-1]): if (self.register_utils.get_gdac() < scanned_gdac and median_occupancy <= gdac_occupancies[index] and gdac_occupancies[index] != 0): if min_gdac_with_occupancy is None: min_gdac_with_occupancy = self.register_utils.get_gdac( ) else: min_gdac_with_occupancy = max( min_gdac_with_occupancy, self.register_utils.get_gdac()) if (scanned_gdac < self.register_utils.get_gdac() and gdac_occupancies[index] <= median_occupancy and median_occupancy != 0): if min_gdac_with_occupancy is None: min_gdac_with_occupancy = scanned_gdac else: min_gdac_with_occupancy = max( min_gdac_with_occupancy, scanned_gdac) for gdac_above_threshold in gdacs_above_threshold: if gdac_above_threshold <= min_gdac_with_occupancy: # check for valid values gdacs_above_threshold.remove(gdac_above_threshold) if gdac_scan_step + 1 == len( gdac_tune_bits): # last GDAC scan step if not additional_scan_ongoing and ( (occupancy_almost_zero and no_noise) or not gdacs_above_threshold or (self.gdac_lower_limit and self.register_utils.get_gdac() < self.gdac_lower_limit) or (min_gdac_with_occupancy and self.register_utils.get_gdac() <= min_gdac_with_occupancy) or not no_noise ) and len( self.gdac_tune_bits ) >= last_good_gdac_scan_step + 2: # min. 2 bits for bin search self.set_gdac_bit( gdac_bit, bit_value=0, send_command=False) # clear current tuning bit if gdac_tune_bits_permutation + 1 == 2**last_good_gdac_scan_step: # next permutation step gdac_tune_bits_permutation = 0 last_good_gdac_scan_step += 1 else: gdac_tune_bits_permutation += 1 gdac_tune_bits.extend( self.gdac_tune_bits[last_good_gdac_scan_step:] ) # repeat all scan steps from last bit elif gdac_bit == 0 and not additional_scan_ongoing: # scan bit 0 = 1 additional_scan_ongoing = True last_occ_array_sel_pixels = occ_array_sel_pixels.copy() last_occ_array_desel_pixels = occ_array_desel_pixels.copy() last_median_occupancy = median_occupancy gdac_tune_bits.append( 0) # the last tune bit has to be scanned twice elif gdac_bit == 0 and additional_scan_ongoing: # scan bit 0 = 0 additional_scan_ongoing = False logging.info( 'Measured %.2f with bit 0 = 0 with and %.2f with bit 0 = 1', median_occupancy, last_median_occupancy) if (abs(median_occupancy - self.n_injections_gdac / 2.0) >= abs(last_median_occupancy - self.n_injections_gdac / 2.0) ) or ( last_median_occupancy >= self.n_injections_gdac / 2.0 ): # if bit 0 = 0 is worse than bit 0 = 1, so go back logging.info('Set bit 0 = 1') self.set_gdac_bit( 0, bit_value=1, send_command=True) # write GDAC value occ_array_sel_pixels = last_occ_array_sel_pixels.copy() occ_array_desel_pixels = last_occ_array_desel_pixels.copy( ) median_occupancy = last_median_occupancy else: logging.info('Keep bit 0 = 0') else: # regular GDAC scan step # GDAC too low, no hits if (self.gdac_lower_limit and self.register_utils.get_gdac() < self.gdac_lower_limit ) or (min_gdac_with_occupancy and self.register_utils.get_gdac() <= min_gdac_with_occupancy) or not no_noise: logging.info( 'Median = %.2f > %.2f, GDAC possibly too low, keep bit %d = 1', median_occupancy, self.n_injections_gdac / 2.0, gdac_bit) # GDAC too high, less hits, decrease GDAC elif median_occupancy < self.n_injections_gdac / 2.0: # set GDAC bit to 0 if the occupancy is too low, thus decrease threshold logging.info('Median = %.2f < %.2f, set bit %d = 0', median_occupancy, self.n_injections_gdac / 2.0, gdac_bit) self.set_gdac_bit( gdac_bit, bit_value=0, send_command=False ) # do not write, might be too low, do this in next iteration # GDAC too low, more hits, increase GDAC else: gdacs_above_threshold.append( self.register_utils.get_gdac()) logging.info('Median = %.2f > %.2f, keep bit %d = 1', median_occupancy, self.n_injections_gdac / 2.0, gdac_bit) if not self.stop_run.is_set(): # select best GDAC value sorted_indices = np.argsort(np.array(gdac_values)) occupancy_sorted = np.array(gdac_occupancies)[sorted_indices] gdac_sorted = np.sort(gdac_values) try: diff_occupancy = occupancy_sorted[1:] - occupancy_sorted[:-1] gdac_min_idx = np.where(diff_occupancy > 0)[0][-1] + 1 except IndexError: gdac_min_idx = None occupancy_sorted_sel = occupancy_sorted[gdac_min_idx:] best_index_sel = np.abs( np.array(occupancy_sorted_sel[::-1]) - self.n_injections_gdac / 2.0).argmin() best_index = sorted_indices[gdac_min_idx:][::-1][best_index_sel] gdac_best = gdac_values[best_index] median_occupancy = gdac_occupancies[best_index] # for plotting self.occ_array_sel_pixels_best = gdac_occ_array_sel_pixels[ best_index] self.occ_array_desel_pixels_best = gdac_occ_array_desel_pixels[ best_index] if gdac_best != self.register_utils.get_gdac(): logging.info( "Binary search converged to non-optimal value, apply best GDAC value, change GDAC from %d to %d", self.register_utils.get_gdac(), gdac_best) self.register_utils.set_gdac(gdac_best, send_command=False) self.gdac_best = self.register_utils.get_gdac() if abs(median_occupancy - self.n_injections_gdac / 2.0) > abs( self.n_injections_gdac * 0.01 * self.max_delta_threshold) and not self.stop_run.is_set(): if np.all((((self.gdac_best & (1 << np.arange( self.register.global_registers['Vthin_AltFine'] ['bitlength'] + self.register. global_registers['Vthin_AltFine']['bitlength'])))) > 0 ).astype(int)[self.gdac_tune_bits] == 1): if self.fail_on_warning: raise RuntimeWarning( 'Selected GDAC bits reached maximum value') else: logging.warning( 'Selected GDAC bits reached maximum value') elif np.all((((self.gdac_best & (1 << np.arange( self.register.global_registers['Vthin_AltFine'] ['bitlength'] + self.register. global_registers['Vthin_AltFine']['bitlength'])))) > 0 ).astype(int)[self.gdac_tune_bits] == 0): if self.fail_on_warning: raise RuntimeWarning( 'Selected GDAC bits reached minimum value') else: logging.warning( 'Selected GDAC bits reached minimum value') else: if self.fail_on_warning: raise RuntimeWarning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d' % (abs(median_occupancy - self.n_injections_gdac / 2.0), abs(self.n_injections_gdac * 0.01 * self.max_delta_threshold), self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine"))) else: logging.warning( 'Global threshold tuning failed. Delta threshold = %.2f > %.2f. Vthin_AltCoarse / Vthin_AltFine = %d / %d', abs(median_occupancy - self.n_injections_gdac / 2.0), abs(self.n_injections_gdac * 0.01 * self.max_delta_threshold), self.register.get_global_register_value( "Vthin_AltCoarse"), self.register.get_global_register_value( "Vthin_AltFine")) else: logging.info( 'Tuned GDAC to Vthin_AltCoarse / Vthin_AltFine = %d / %d', self.register.get_global_register_value("Vthin_AltCoarse"), self.register.get_global_register_value("Vthin_AltFine"))