def get_raw_data_from_buffer(self, filter_func=None, converter_func=None): '''Reads local data buffer and returns raw data array. Returns ------- data : np.array An array containing data words from the local data buffer. ''' if self._is_running: raise RuntimeError('Readout thread running') if not self.fill_buffer: logging.warning('Data buffer is not activated') return [ convert_data_array(data_array_from_data_iterable(data_iterable), filter_func=filter_func, converter_func=converter_func) for data_iterable in self._data_buffer ]
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 mask_steps = 3 enable_mask_steps = [ 0 ] # one mask step to increase speed, no effect on precision 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=mask_steps)[0] self.write_target_charge() for feedback_bit in self.feedback_tune_bits: # reset all GDAC bits self.set_prmp_vbpf_bit(feedback_bit, bit_value=0) additional_scan = True last_bit_result = self.n_injections_feedback tot_mean_best = 0 feedback_best = self.register.get_global_register_value("PrmpVbpf") for feedback_bit in self.feedback_tune_bits: if additional_scan: self.set_prmp_vbpf_bit(feedback_bit) logging.info( 'PrmpVbpf setting: %d, 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, 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): scan_loop(self, cal_lvl1_command, repeat_command=self.n_injections_feedback, mask_steps=mask_steps, enable_mask_steps=enable_mask_steps, 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) self.raw_data_file.append( self.fifo_readout.data, scan_parameters=self.scan_parameters._asdict()) tots = convert_data_array( data_array_from_data_iterable(self.fifo_readout.data), filter_func=is_data_record, converter_func=get_tot_array_from_data_record_array) mean_tot = np.mean(tots) if np.isnan(mean_tot): logging.error( "No hits, ToT calculation not possible, tuning will fail") 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 = %f' % mean_tot) self.tot_array, _ = np.histogram(a=tots, range=(0, 16), bins=16) if self.plot_intermediate_steps: plot_tot(hist=self.tot_array, 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 and mean_tot < self.target_tot: self.set_prmp_vbpf_bit(feedback_bit, bit_value=0) logging.info('Mean ToT = %f < %d ToT, set bit %d = 0' % (mean_tot, self.target_tot, feedback_bit)) if feedback_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False last_bit_result = mean_tot self.feedback_tune_bits.append( 0) # bit 0 has to be scanned twice else: logging.info( 'Scanned bit 0 = 0 with %f instead of %f for scanned bit 0 = 1' % (mean_tot, last_bit_result)) if (abs(mean_tot - self.target_tot) > abs(last_bit_result - self.target_tot) ): # if bit 0 = 0 is worse than bit 0 = 1, so go back self.set_prmp_vbpf_bit(feedback_bit, bit_value=1) mean_tot = last_bit_result logging.info('Set bit 0 = 1') else: logging.info('Set bit 0 = 0') if abs(mean_tot - self.target_tot) > abs(tot_mean_best - self.target_tot): logging.info( "Binary search converged to non optimal value, take best measured value instead" ) mean_tot = tot_mean_best self.register.set_global_register_value( "PrmpVbpf", feedback_best) if self.register.get_global_register_value( "PrmpVbpf") == 0 or self.register.get_global_register_value( "PrmpVbpf") == 254: logging.warning('PrmpVbpf reached minimum/maximum value') if abs(mean_tot - self.target_tot) > 2 * self.max_delta_tot: logging.warning( 'Global feedback tuning failed. Delta ToT = %f > %f. 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")) self.feedback_best = self.register.get_global_register_value( "PrmpVbpf")
def scan(self): scan_parameter_range = [ self.register.get_global_register_value("Vthin_AltFine"), 0 ] if self.scan_parameters.Vthin_AltFine[0]: scan_parameter_range[0] = self.scan_parameters.Vthin_AltFine[0] if self.scan_parameters.Vthin_AltFine[1]: scan_parameter_range[1] = self.scan_parameters.Vthin_AltFine[1] steps = 1 if self.scan_parameters.Step: steps = self.scan_parameters.Step lvl1_command = self.register.get_commands( "LV1")[0] + self.register.get_commands( "zeros", length=self.trigger_rate_limit)[0] self.total_scan_time = int(lvl1_command.length() * 25 * (10**-9) * self.n_triggers) disabled_pixels_limit_cnt = int(self.disabled_pixels_limit * 336 * 80) preselected_pixels = invert_pixel_mask( self.register.get_pixel_register_value('Enable')).sum() disabled_pixels = 0 for reg_val in range(scan_parameter_range[0], scan_parameter_range[1] - 1, -1): if self.stop_run.is_set(): break self.register.create_restore_point(name=str(reg_val)) logging.info('Scanning Vthin_AltFine %d' % reg_val) commands = [] commands.extend(self.register.get_commands("ConfMode")) self.register.set_global_register_value( "Vthin_AltFine", reg_val) # set number of consecutive triggers commands.extend( self.register.get_commands("WrRegister", name=["Vthin_AltFine"])) # setting FE into RunMode commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands) step = 0 while True: if self.stop_run.is_set(): break step += 1 logging.info('Step %d / %d at Vthin_AltFine %d' % (step, steps, reg_val)) logging.info('Estimated scan time: %ds' % self.total_scan_time) with self.readout(Vthin_AltFine=reg_val, Step=step): got_data = False start = time() self.register_utils.send_command(lvl1_command, repeat=self.n_triggers, wait_for_finish=False, set_length=True, clear_memory=False) while not self.stop_run.wait(0.1): if self.register_utils.is_ready: if got_data: self.progressbar.finish() logging.info('Finished sending %d triggers' % self.n_triggers) break if not got_data: if self.fifo_readout.data_words_per_second() > 0: got_data = True logging.info('Taking data...') self.progressbar = progressbar.ProgressBar( widgets=[ '', progressbar.Percentage(), ' ', progressbar.Bar(marker='*', left='|', right='|'), ' ', progressbar.Timer() ], maxval=self.total_scan_time, poll=10, term_width=80).start() else: try: self.progressbar.update(time() - start) except ValueError: pass self.raw_data_file.append( self.fifo_readout.data, scan_parameters=self.scan_parameters._asdict()) col_arr, row_arr = 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) occ_hist, _, _ = np.histogram2d(col_arr, row_arr, bins=(80, 336), range=[[1, 80], [1, 336]]) occ_mask = np.zeros(shape=occ_hist.shape, dtype=np.dtype('>u1')) # noisy pixels are set to 1 occ_mask[occ_hist > self.occupancy_limit * self.n_triggers * self.consecutive_lvl1] = 1 # plot_occupancy(occ_hist.T, title='Occupancy', filename=self.scan_data_filename + '_noise_occ_' + str(reg_val) + '_' + str(step) + '.pdf') tdac_reg = self.register.get_pixel_register_value('TDAC') decrease_pixel_mask = np.logical_and(occ_mask > 0, tdac_reg > 0) disable_pixel_mask = np.logical_and(occ_mask > 0, tdac_reg == 0) enable_reg = self.register.get_pixel_register_value('Enable') enable_mask = np.logical_and( enable_reg, invert_pixel_mask(disable_pixel_mask)) if np.logical_and(occ_mask > 0, enable_reg == 0).sum(): logging.warning('Received data from disabled pixels') # disabled_pixels += disable_pixel_mask.sum() # can lead to wrong values if the enable reg is corrupted disabled_pixels = invert_pixel_mask( enable_mask).sum() - preselected_pixels if disabled_pixels > disabled_pixels_limit_cnt: logging.info( 'Limit of disabled pixels reached: %d (limit %d)... stopping scan' % (disabled_pixels, disabled_pixels_limit_cnt)) self.register.restore(name=str(reg_val)) break else: logging.info('Increasing threshold of %d pixel(s)' % (decrease_pixel_mask.sum(), )) logging.info( 'Disabling %d pixel(s), total number of disabled pixel(s): %d' % (disable_pixel_mask.sum(), disabled_pixels)) tdac_reg[decrease_pixel_mask] -= 1 # TODO self.register.set_pixel_register_value('TDAC', tdac_reg) self.register.set_pixel_register_value( 'Enable', enable_mask) commands = [] commands.extend(self.register.get_commands("ConfMode")) commands.extend( self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=False, name='TDAC')) commands.extend( self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=False, name='Enable')) commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands) if occ_mask.sum( ) == 0 or step == steps or decrease_pixel_mask.sum( ) < disabled_pixels_limit_cnt: self.register.clear_restore_points(name=str(reg_val)) self.last_tdac_distribution = self.register.get_pixel_register_value( 'TDAC') self.last_occupancy_hist = occ_hist.copy() self.last_occupancy_mask = occ_mask.copy() self.last_reg_val = reg_val self.last_step = step break else: logging.info( 'Found noisy pixels... repeat tuning step for Vthin_AltFine %d' % (reg_val, )) if disabled_pixels > disabled_pixels_limit_cnt: self.last_good_threshold = self.register.get_global_register_value( "Vthin_AltFine") self.last_good_tdac = self.register.get_pixel_register_value( 'TDAC') self.last_good_enable_mask = self.register.get_pixel_register_value( 'Enable') break
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 enable_mask_steps = [] 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() additional_scan = True lastBitResult = np.zeros(shape=self.register.get_pixel_register_value("TDAC").shape, dtype=self.register.get_pixel_register_value("TDAC").dtype) self.set_start_tdac() self.occupancy_best = np.full(shape=(80, 336), fill_value=self.n_injections_tdac) # array to store the best occupancy (closest to Ninjections/2) of the pixel self.tdac_mask_best = self.register.get_pixel_register_value("TDAC") tdac_tune_bits = self.tdac_tune_bits[:] for scan_parameter_value, tdac_bit in enumerate(tdac_tune_bits): if additional_scan: self.set_tdac_bit(tdac_bit) logging.info('TDAC setting: bit %d = 1', tdac_bit) else: self.set_tdac_bit(tdac_bit, bit_value=0) logging.info('TDAC setting: bit %d = 0', tdac_bit) self.write_tdac_config() with self.readout(TDAC=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_tdac, mask_steps=self.mask_steps, enable_mask_steps=enable_mask_steps, 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]]) select_better_pixel_mask = abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2) pixel_with_too_high_occupancy_mask = occupancy_array > self.n_injections_tdac / 2 self.occupancy_best[select_better_pixel_mask] = occupancy_array[select_better_pixel_mask] if self.plot_intermediate_steps: plot_three_way(occupancy_array.transpose(), title="Occupancy (TDAC tuning bit " + str(tdac_bit) + ")", x_axis_title='Occupancy', filename=self.plots_filename, maximum=self.n_injections_tdac) tdac_mask = self.register.get_pixel_register_value("TDAC") self.tdac_mask_best[select_better_pixel_mask] = tdac_mask[select_better_pixel_mask] if tdac_bit > 0: tdac_mask[pixel_with_too_high_occupancy_mask] = tdac_mask[pixel_with_too_high_occupancy_mask] & ~(1 << tdac_bit) self.register.set_pixel_register_value("TDAC", tdac_mask) if tdac_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False lastBitResult = occupancy_array.copy() tdac_tune_bits.append(0) # bit 0 has to be scanned twice else: tdac_mask[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] = tdac_mask[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] | (1 << tdac_bit) occupancy_array[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] = lastBitResult[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] self.occupancy_best[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] = occupancy_array[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] self.tdac_mask_best[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] = tdac_mask[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] self.register.set_pixel_register_value("TDAC", self.tdac_mask_best) # set value for meta scan self.write_tdac_config()
def scan(self): self.start_condition_triggered = False # set to true if the start condition is true once self.stop_condition_triggered = False # set to true if the stop condition is true once self.start_at = 0.01 # if more than start_at*activated_pixel see at least one hit the precise scanning is started self.stop_at = 0.95 # if more than stop_at*activated_pixel see the maximum numbers of injection, the scan is stopped self.record_data = False # set to true to activate data storage, so far not everything is recorded to ease data analysis scan_parameter_range = [0, (2 ** self.register.global_registers['PlsrDAC']['bitlength'] - 1)] if self.scan_parameters.PlsrDAC[0]: scan_parameter_range[0] = self.scan_parameters.PlsrDAC[0] if self.scan_parameters.PlsrDAC[1]: scan_parameter_range[1] = self.scan_parameters.PlsrDAC[1] logging.info("Scanning %s from %d to %d", 'PlsrDAC', scan_parameter_range[0], scan_parameter_range[1]) self.scan_parameter_value = scan_parameter_range[0] # set to start value self.search_distance = self.search_distance self.data_points = 0 # counter variable to count the data points already recorded, have to be at least minimum_data_ponts # calculate DCs to scan from the columns to ignore enable_double_columns = range(0, 40) if 1 in self.ignore_columns: enable_double_columns.remove(0) if set((78, 79, 80)).issubset(self.ignore_columns): enable_double_columns.remove(39) for double_column in range(1, 39): if set((double_column * 2, (double_column * 2) + 1)).issubset(self.ignore_columns): enable_double_columns.remove(double_column) logging.info("Use DCs: %s", str(enable_double_columns)) self.select_arr_columns = range(0, 80) for column in self.ignore_columns: self.select_arr_columns.remove(column - 1) while self.scan_parameter_value <= scan_parameter_range[1]: # scan as long as scan parameter is smaller than defined maximum if self.stop_run.is_set(): break commands = [] commands.extend(self.register.get_commands("ConfMode")) self.register.set_global_register_value('PlsrDAC', self.scan_parameter_value) commands.extend(self.register.get_commands("WrRegister", name=['PlsrDAC'])) self.register_utils.send_commands(commands) with self.readout(PlsrDAC=self.scan_parameter_value, reset_sram_fifo=True, fill_buffer=True, clear_buffer=True, callback=self.handle_data if self.record_data else None): cal_lvl1_command = self.register.get_commands("CAL")[0] + self.register.get_commands("zeros", length=40)[0] + self.register.get_commands("LV1")[0] scan_loop(self, cal_lvl1_command, repeat_command=self.n_injections, use_delay=True, mask_steps=self.mask_steps, enable_mask_steps=self.enable_mask_steps, enable_double_columns=enable_double_columns, 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=False, mask=invert_pixel_mask(self.register.get_pixel_register_value('Enable')) if self.use_enable_mask else None, double_column_correction=self.pulser_dac_correction) if not self.start_condition_triggered or self.data_points > self.minimum_data_points: # speed up, only create histograms when needed. Python is much too slow here. if not self.start_condition_triggered and not self.record_data: logging.info('Testing for start condition: %s %d', 'PlsrDAC', self.scan_parameter_value) if not self.stop_condition_triggered and self.record_data: logging.info('Testing for stop condition: %s %d', 'PlsrDAC', self.scan_parameter_value) col, row = 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) if np.any(np.logical_and(col < 1, col > 80)) or np.any(np.logical_and(row < 1, row > 336)): # filter bad data records that can happen logging.warning('There are undefined %d data records (e.g. random data)', np.count_nonzero(np.logical_and(col < 1, col > 80)) + np.count_nonzero(np.logical_and(row < 1, row > 336))) col, row = col[np.logical_and(col > 0, col <= 80)], row[np.logical_and(row > 0, row < 336)] occupancy_array = hist_2d_index(col - 1, row - 1, shape=(80, 336)) self.scan_condition(occupancy_array) # start condition is met for the first time if self.start_condition_triggered and not self.record_data: self.scan_parameter_value = self.scan_parameter_value - self.search_distance + self.step_size if self.scan_parameter_value < 0: self.scan_parameter_value = 0 logging.info('Starting threshold scan at %s %d', 'PlsrDAC', self.scan_parameter_value) self.scan_parameter_start = self.scan_parameter_value self.record_data = True continue # saving data if self.record_data: self.data_points = self.data_points + 1 # stop condition is met for the first time if self.stop_condition_triggered and self.record_data: logging.info('Stopping threshold scan at %s %d', 'PlsrDAC', self.scan_parameter_value) break # increase scan parameter value if not self.start_condition_triggered: self.scan_parameter_value = self.scan_parameter_value + self.search_distance else: self.scan_parameter_value = self.scan_parameter_value + self.step_size if self.scan_parameter_value >= scan_parameter_range[1]: logging.warning("Reached maximum of PlsrDAC range... stopping scan")
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 enable_mask_steps = [] 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)[0] self.write_target_charge() additional_scan = True lastBitResult = np.zeros(shape=self.register.get_pixel_register_value("FDAC").shape, dtype=self.register.get_pixel_register_value("FDAC").dtype) self.set_start_fdac() self.tot_mean_best = np.empty(shape=(80, 336)) # array to store the best occupancy (closest to Ninjections/2) of the pixel self.tot_mean_best.fill(0) self.fdac_mask_best = self.register.get_pixel_register_value("FDAC") for scan_parameter_value, fdac_bit in enumerate(self.fdac_tune_bits): if additional_scan: self.set_fdac_bit(fdac_bit) logging.info('FDAC setting: bit %d = 1', fdac_bit) else: self.set_fdac_bit(fdac_bit, bit_value=0) logging.info('FDAC setting: bit %d = 0', fdac_bit) self.write_fdac_config() with self.readout(FDAC=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_fdac, mask_steps=self.mask_steps, enable_mask_steps=enable_mask_steps, 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) col_row_tot = np.column_stack(convert_data_array(data_array_from_data_iterable(self.fifo_readout.data), filter_func=is_data_record, converter_func=get_col_row_tot_array_from_data_record_array)) tot_array = np.histogramdd(col_row_tot, bins=(80, 336, 16), range=[[1, 80], [1, 336], [0, 15]])[0] tot_mean_array = np.average(tot_array, axis=2, weights=range(0, 16)) * sum(range(0, 16)) / self.n_injections_fdac select_better_pixel_mask = abs(tot_mean_array - self.target_tot) <= abs(self.tot_mean_best - self.target_tot) pixel_with_too_small_mean_tot_mask = tot_mean_array < self.target_tot self.tot_mean_best[select_better_pixel_mask] = tot_mean_array[select_better_pixel_mask] if self.plot_intermediate_steps: plot_three_way(hist=tot_mean_array.transpose().transpose(), title="Mean ToT (FDAC tuning bit " + str(fdac_bit) + ")", x_axis_title='mean ToT', filename=self.plots_filename, minimum=0, maximum=15) fdac_mask = self.register.get_pixel_register_value("FDAC") self.fdac_mask_best[select_better_pixel_mask] = fdac_mask[select_better_pixel_mask] if fdac_bit > 0: fdac_mask[pixel_with_too_small_mean_tot_mask] = fdac_mask[pixel_with_too_small_mean_tot_mask] & ~(1 << fdac_bit) self.register.set_pixel_register_value("FDAC", fdac_mask) if fdac_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False lastBitResult = tot_mean_array.copy() self.fdac_tune_bits.append(0) # bit 0 has to be scanned twice else: fdac_mask[abs(tot_mean_array - self.target_tot) > abs(lastBitResult - self.target_tot)] = fdac_mask[abs(tot_mean_array - self.target_tot) > abs(lastBitResult - self.target_tot)] | (1 << fdac_bit) tot_mean_array[abs(tot_mean_array - self.target_tot) > abs(lastBitResult - self.target_tot)] = lastBitResult[abs(tot_mean_array - self.target_tot) > abs(lastBitResult - self.target_tot)] self.tot_mean_best[abs(tot_mean_array - self.target_tot) <= abs(self.tot_mean_best - self.n_injections_fdac / 2)] = tot_mean_array[abs(tot_mean_array - self.target_tot) <= abs(self.tot_mean_best - self.n_injections_fdac / 2)] self.fdac_mask_best[abs(tot_mean_array - self.target_tot) <= abs(self.tot_mean_best - self.n_injections_fdac / 2)] = fdac_mask[abs(tot_mean_array - self.target_tot) <= abs(self.tot_mean_best - self.n_injections_fdac / 2)] self.register.set_pixel_register_value("FDAC", self.fdac_mask_best) # set value for meta scan self.write_fdac_config()
def scan(self): scan_parameter_range = [self.register.get_global_register_value("Vthin_AltFine"), 0] if self.scan_parameters.Vthin_AltFine[0]: scan_parameter_range[0] = self.scan_parameters.Vthin_AltFine[0] if self.scan_parameters.Vthin_AltFine[1]: scan_parameter_range[1] = self.scan_parameters.Vthin_AltFine[1] steps = 1 if self.scan_parameters.Step: steps = self.scan_parameters.Step lvl1_command = self.register.get_commands("LV1")[0] + self.register.get_commands("zeros", length=self.trigger_rate_limit)[0] self.total_scan_time = int(lvl1_command.length() * 25 * (10 ** -9) * self.n_triggers) disabled_pixels_limit_cnt = int(self.disabled_pixels_limit * 336 * 80) preselected_pixels = invert_pixel_mask(self.register.get_pixel_register_value('Enable')).sum() disabled_pixels = 0 for reg_val in range(scan_parameter_range[0], scan_parameter_range[1] - 1, -1): if self.stop_run.is_set(): break self.register.create_restore_point(name=str(reg_val)) logging.info('Scanning Vthin_AltFine %d', reg_val) commands = [] commands.extend(self.register.get_commands("ConfMode")) self.register.set_global_register_value("Vthin_AltFine", reg_val) # set number of consecutive triggers commands.extend(self.register.get_commands("WrRegister", name=["Vthin_AltFine"])) # setting FE into RunMode commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands) step = 0 while True: if self.stop_run.is_set(): break self.histograming.reset() step += 1 logging.info('Step %d / %d at Vthin_AltFine %d', step, steps, reg_val) logging.info('Estimated scan time: %ds', self.total_scan_time) with self.readout(Vthin_AltFine=reg_val, Step=step, reset_sram_fifo=True, fill_buffer=True, clear_buffer=True, callback=self.handle_data): got_data = False start = time() self.register_utils.send_command(lvl1_command, repeat=self.n_triggers, wait_for_finish=False, set_length=True, clear_memory=False) while not self.stop_run.wait(0.1): if self.register_utils.is_ready: if got_data: self.progressbar.finish() logging.info('Finished sending %d triggers', self.n_triggers) break if not got_data: if self.fifo_readout.data_words_per_second() > 0: got_data = True logging.info('Taking data...') self.progressbar = progressbar.ProgressBar(widgets=['', progressbar.Percentage(), ' ', progressbar.Bar(marker='*', left='|', right='|'), ' ', progressbar.Timer()], maxval=self.total_scan_time, poll=10, term_width=80).start() else: try: self.progressbar.update(time() - start) except ValueError: pass # Use fast C++ hit histograming to save time raw_data = np.ascontiguousarray(data_array_from_data_iterable(self.fifo_readout.data), dtype=np.uint32) self.interpreter.interpret_raw_data(raw_data) self.interpreter.store_event() # force to create latest event self.histograming.add_hits(self.interpreter.get_hits()) occ_hist = self.histograming.get_occupancy()[:, :, 0] # noisy pixels are set to 1 occ_mask = np.zeros(shape=occ_hist.shape, dtype=np.dtype('>u1')) occ_mask[occ_hist > self.occupancy_limit * self.n_triggers * self.consecutive_lvl1] = 1 tdac_reg = self.register.get_pixel_register_value('TDAC') decrease_pixel_mask = np.logical_and(occ_mask > 0, tdac_reg > 0) disable_pixel_mask = np.logical_and(occ_mask > 0, tdac_reg == 0) enable_reg = self.register.get_pixel_register_value('Enable') enable_mask = np.logical_and(enable_reg, invert_pixel_mask(disable_pixel_mask)) if np.logical_and(occ_mask > 0, enable_reg == 0).sum(): logging.warning('Received data from disabled pixels') # disabled_pixels += disable_pixel_mask.sum() # can lead to wrong values if the enable reg is corrupted disabled_pixels = invert_pixel_mask(enable_mask).sum() - preselected_pixels if disabled_pixels > disabled_pixels_limit_cnt: logging.info('Limit of disabled pixels reached: %d (limit %d)... stopping scan' % (disabled_pixels, disabled_pixels_limit_cnt)) self.register.restore(name=str(reg_val)) break else: logging.info('Increasing threshold of %d pixel(s)', decrease_pixel_mask.sum()) logging.info('Disabling %d pixel(s), total number of disabled pixel(s): %d', disable_pixel_mask.sum(), disabled_pixels) tdac_reg[decrease_pixel_mask] -= 1 self.register.set_pixel_register_value('TDAC', tdac_reg) self.register.set_pixel_register_value('Enable', enable_mask) commands = [] commands.extend(self.register.get_commands("ConfMode")) commands.extend(self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=False, name='TDAC')) commands.extend(self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=False, name='Enable')) commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands) if occ_mask.sum() == 0 or step == steps or decrease_pixel_mask.sum() < disabled_pixels_limit_cnt: self.register.clear_restore_points(name=str(reg_val)) self.last_tdac_distribution = self.register.get_pixel_register_value('TDAC') self.last_occupancy_hist = occ_hist.copy() self.last_occupancy_mask = occ_mask.copy() self.last_reg_val = reg_val self.last_step = step break else: logging.info('Found %d noisy pixels... repeat tuning step for Vthin_AltFine %d', occ_mask.sum(), reg_val) if disabled_pixels > disabled_pixels_limit_cnt: self.last_good_threshold = self.register.get_global_register_value("Vthin_AltFine") self.last_good_tdac = self.register.get_pixel_register_value('TDAC') self.last_good_enable_mask = self.register.get_pixel_register_value('Enable') break
def scan(self): scan_parameter_range = [self.register.get_global_register_value("Vthin_AltFine"), 0] if self.scan_parameters.Vthin_AltFine[0]: scan_parameter_range[0] = self.scan_parameters.Vthin_AltFine[0] if self.scan_parameters.Vthin_AltFine[1]: scan_parameter_range[1] = self.scan_parameters.Vthin_AltFine[1] steps = 1 if self.scan_parameters.Step: steps = self.scan_parameters.Step lvl1_command = self.register.get_commands("LV1")[0] + self.register.get_commands("zeros", length=self.trigger_rate_limit)[0] self.total_scan_time = int(lvl1_command.length() * 25 * (10 ** -9) * self.n_triggers) preselected_pixels = invert_pixel_mask(self.register.get_pixel_register_value('Enable')).sum() disabled_pixels_limit_cnt = int(self.disabled_pixels_limit * self.register.get_pixel_register_value('Enable').sum()) disabled_pixels = 0 self.last_reg_val = deque([None] * self.increase_threshold, maxlen=self.increase_threshold + 1) self.last_step = deque([None] * self.increase_threshold, maxlen=self.increase_threshold + 1) self.last_good_threshold = deque([None] * self.increase_threshold, maxlen=self.increase_threshold + 1) self.last_good_tdac = deque([None] * self.increase_threshold, maxlen=self.increase_threshold + 1) self.last_good_enable_mask = deque([None] * self.increase_threshold, maxlen=self.increase_threshold + 1) self.last_occupancy_hist = deque([None] * self.increase_threshold, maxlen=self.increase_threshold + 1) self.last_occupancy_mask = deque([None] * self.increase_threshold, maxlen=self.increase_threshold + 1) for reg_val in range(scan_parameter_range[0], scan_parameter_range[1] - 1, -1): if self.stop_run.is_set(): break logging.info('Scanning Vthin_AltFine %d', reg_val) commands = [] commands.extend(self.register.get_commands("ConfMode")) self.register.set_global_register_value("Vthin_AltFine", reg_val) # set number of consecutive triggers commands.extend(self.register.get_commands("WrRegister", name=["Vthin_AltFine"])) # setting FE into RunMode commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands) step = 0 while True: if self.stop_run.is_set(): break self.histogram.reset() step += 1 logging.info('Step %d / %d at Vthin_AltFine %d', step, steps, reg_val) logging.info('Estimated scan time: %ds', self.total_scan_time) with self.readout(Vthin_AltFine=reg_val, Step=step, reset_sram_fifo=True, fill_buffer=True, clear_buffer=True, callback=self.handle_data): got_data = False start = time() self.register_utils.send_command(lvl1_command, repeat=self.n_triggers, wait_for_finish=False, set_length=True, clear_memory=False) while not self.stop_run.wait(0.1): if self.register_utils.is_ready: if got_data: self.progressbar.finish() logging.info('Finished sending %d triggers', self.n_triggers) break if not got_data: if self.fifo_readout.data_words_per_second() > 0: got_data = True logging.info('Taking data...') self.progressbar = progressbar.ProgressBar(widgets=['', progressbar.Percentage(), ' ', progressbar.Bar(marker='*', left='|', right='|'), ' ', progressbar.Timer()], maxval=self.total_scan_time, poll=10, term_width=80).start() else: try: self.progressbar.update(time() - start) except ValueError: pass # Use fast C++ hit histogramming to save time raw_data = np.ascontiguousarray(data_array_from_data_iterable(self.fifo_readout.data), dtype=np.uint32) self.interpreter.interpret_raw_data(raw_data) self.interpreter.store_event() # force to create latest event self.histogram.add_hits(self.interpreter.get_hits()) occ_hist = self.histogram.get_occupancy()[:, :, 0] # noisy pixels are set to 1 occ_mask = np.zeros(shape=occ_hist.shape, dtype=np.dtype('>u1')) occ_mask[occ_hist > self.abs_occ_limit] = 1 tdac_reg = self.register.get_pixel_register_value('TDAC') decrease_pixel_mask = np.logical_and(occ_mask > 0, tdac_reg > 0) disable_pixel_mask = np.logical_and(occ_mask > 0, tdac_reg == 0) enable_reg = self.register.get_pixel_register_value('Enable') enable_mask = np.logical_and(enable_reg, invert_pixel_mask(disable_pixel_mask)) if np.logical_and(occ_mask > 0, enable_reg == 0).sum(): logging.warning('Received data from disabled pixels') # disabled_pixels += disable_pixel_mask.sum() # can lead to wrong values if the enable reg is corrupted disabled_pixels = invert_pixel_mask(enable_mask).sum() - preselected_pixels if disabled_pixels > disabled_pixels_limit_cnt: logging.info('Limit of disabled pixels reached: %d (limit %d)... stopping scan' % (disabled_pixels, disabled_pixels_limit_cnt)) break else: logging.info('Increasing threshold of %d pixel(s)', decrease_pixel_mask.sum()) logging.info('Disabling %d pixel(s), total number of disabled pixel(s): %d', disable_pixel_mask.sum(), disabled_pixels) tdac_reg[decrease_pixel_mask] -= 1 self.register.set_pixel_register_value('TDAC', tdac_reg) self.register.set_pixel_register_value('Enable', enable_mask) commands = [] commands.extend(self.register.get_commands("ConfMode")) commands.extend(self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=False, name='TDAC')) commands.extend(self.register.get_commands("WrFrontEnd", same_mask_for_all_dc=False, name='Enable')) commands.extend(self.register.get_commands("RunMode")) self.register_utils.send_commands(commands) if occ_mask.sum() == 0 or step == steps or decrease_pixel_mask.sum() < disabled_pixels_limit_cnt: self.last_reg_val.appendleft(reg_val) self.last_step.appendleft(step) self.last_good_threshold.appendleft(self.register.get_global_register_value("Vthin_AltFine")) self.last_good_tdac.appendleft(self.register.get_pixel_register_value("TDAC")) self.last_good_enable_mask.appendleft(self.register.get_pixel_register_value("Enable")) self.last_occupancy_hist.appendleft(occ_hist.copy()) self.last_occupancy_mask.appendleft(occ_mask.copy()) break else: logging.info('Found %d noisy pixels... repeat tuning step for Vthin_AltFine %d', occ_mask.sum(), reg_val) if disabled_pixels > disabled_pixels_limit_cnt or scan_parameter_range[1] == reg_val: break
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 mask_steps = 3 enable_mask_steps = [] 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=mask_steps)[0] self.write_target_threshold() additional_scan = True lastBitResult = np.zeros(shape=self.register.get_pixel_register_value("TDAC").shape, dtype=self.register.get_pixel_register_value("TDAC").dtype) self.set_start_tdac() self.occupancy_best = np.empty(shape=(80, 336)) # array to store the best occupancy (closest to Ninjections/2) of the pixel self.occupancy_best.fill(self.n_injections_tdac) self.tdac_mask_best = self.register.get_pixel_register_value("TDAC") for scan_parameter_value, tdac_bit in enumerate(self.tdac_tune_bits): if additional_scan: self.set_tdac_bit(tdac_bit) logging.info('TDAC setting: bit %d = 1', tdac_bit) else: self.set_tdac_bit(tdac_bit, bit_value=0) logging.info('TDAC setting: bit %d = 0', tdac_bit) self.write_tdac_config() with self.readout(TDAC=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_tdac, mask_steps=mask_steps, enable_mask_steps=enable_mask_steps, 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]]) select_better_pixel_mask = abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2) pixel_with_too_high_occupancy_mask = occupancy_array > self.n_injections_tdac / 2 self.occupancy_best[select_better_pixel_mask] = occupancy_array[select_better_pixel_mask] if self.plot_intermediate_steps: plotThreeWay(occupancy_array.transpose(), title="Occupancy (TDAC tuning bit " + str(tdac_bit) + ")", x_axis_title='Occupancy', filename=self.plots_filename, maximum=self.n_injections_tdac) tdac_mask = self.register.get_pixel_register_value("TDAC") self.tdac_mask_best[select_better_pixel_mask] = tdac_mask[select_better_pixel_mask] if tdac_bit > 0: tdac_mask[pixel_with_too_high_occupancy_mask] = tdac_mask[pixel_with_too_high_occupancy_mask] & ~(1 << tdac_bit) self.register.set_pixel_register_value("TDAC", tdac_mask) if tdac_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False lastBitResult = occupancy_array.copy() self.tdac_tune_bits.append(0) # bit 0 has to be scanned twice else: tdac_mask[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] = tdac_mask[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] | (1 << tdac_bit) occupancy_array[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] = lastBitResult[abs(occupancy_array - self.n_injections_tdac / 2) > abs(lastBitResult - self.n_injections_tdac / 2)] self.occupancy_best[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] = occupancy_array[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] self.tdac_mask_best[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] = tdac_mask[abs(occupancy_array - self.n_injections_tdac / 2) <= abs(self.occupancy_best - self.n_injections_tdac / 2)] self.register.set_pixel_register_value("TDAC", self.tdac_mask_best) # set value for meta scan self.write_tdac_config()
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 enable_mask_steps = [] 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() additional_scan = True lastBitResult = np.zeros( shape=self.register.get_pixel_register_value("FDAC").shape, dtype=self.register.get_pixel_register_value("FDAC").dtype) self.set_start_fdac() self.tot_mean_best = np.full( shape=(80, 336), fill_value=0 ) # array to store the best occupancy (closest to Ninjections/2) of the pixel self.fdac_mask_best = self.register.get_pixel_register_value("FDAC") fdac_tune_bits = self.fdac_tune_bits[:] for scan_parameter_value, fdac_bit in enumerate(fdac_tune_bits): if additional_scan: self.set_fdac_bit(fdac_bit) logging.info('FDAC setting: bit %d = 1', fdac_bit) else: self.set_fdac_bit(fdac_bit, bit_value=0) logging.info('FDAC setting: bit %d = 0', fdac_bit) self.write_fdac_config() with self.readout(FDAC=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_fdac, mask_steps=self.mask_steps, enable_mask_steps=enable_mask_steps, 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) col_row_tot = np.column_stack( 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_tot_array_from_data_record_array )) tot_array = np.histogramdd(col_row_tot, bins=(80, 336, 16), range=[[1, 80], [1, 336], [0, 15]])[0] tot_mean_array = np.average( tot_array, axis=2, weights=range(0, 16)) * sum(range( 0, 16)) / self.n_injections_fdac select_better_pixel_mask = abs( tot_mean_array - self.target_tot) <= abs(self.tot_mean_best - self.target_tot) pixel_with_too_small_mean_tot_mask = tot_mean_array < self.target_tot self.tot_mean_best[select_better_pixel_mask] = tot_mean_array[ select_better_pixel_mask] if self.plot_intermediate_steps: plot_three_way(hist=tot_mean_array.transpose().transpose(), title="Mean ToT (FDAC tuning bit " + str(fdac_bit) + ")", x_axis_title='mean ToT', filename=self.plots_filename, minimum=0, maximum=15) fdac_mask = self.register.get_pixel_register_value("FDAC") self.fdac_mask_best[select_better_pixel_mask] = fdac_mask[ select_better_pixel_mask] if fdac_bit > 0: fdac_mask[pixel_with_too_small_mean_tot_mask] = fdac_mask[ pixel_with_too_small_mean_tot_mask] & ~(1 << fdac_bit) self.register.set_pixel_register_value("FDAC", fdac_mask) if fdac_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False lastBitResult = tot_mean_array.copy() fdac_tune_bits.append(0) # bit 0 has to be scanned twice else: fdac_mask[abs(tot_mean_array - self.target_tot) > abs( lastBitResult - self.target_tot )] = fdac_mask[abs(tot_mean_array - self.target_tot) > abs( lastBitResult - self.target_tot)] | (1 << fdac_bit) tot_mean_array[abs(tot_mean_array - self.target_tot) > abs( lastBitResult - self.target_tot)] = lastBitResult[ abs(tot_mean_array - self.target_tot) > abs(lastBitResult - self.target_tot)] self.tot_mean_best[ abs(tot_mean_array - self.target_tot) <= abs( self.tot_mean_best - self.n_injections_fdac / 2)] = tot_mean_array[ abs(tot_mean_array - self.target_tot) <= abs( self.tot_mean_best - self.n_injections_fdac / 2)] self.fdac_mask_best[ abs(tot_mean_array - self.target_tot) <= abs( self.tot_mean_best - self.n_injections_fdac / 2)] = fdac_mask[ abs(tot_mean_array - self.target_tot) <= abs( self.tot_mean_best - self.n_injections_fdac / 2)] self.register.set_pixel_register_value( "FDAC", self.fdac_mask_best) # set value for meta scan self.write_fdac_config()
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 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 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, 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_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) col_row_tot_array = np.column_stack( 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_tot_array_from_data_record_array )) 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: 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() 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): 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() 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): self.start_condition_triggered = False # set to true if the start condition is true once self.stop_condition_triggered = False # set to true if the stop condition is true once self.start_at = 0.01 # if more than start_at*activated_pixel see at least one hit the precise scanning is started self.stop_at = 0.95 # if more than stop_at*activated_pixel see the maximum numbers of injection, the scan is stopped self.record_data = False # set to true to activate data storage, so far not everything is recorded to ease data analysis scan_parameter_range = [ 0, (2**self.register.global_registers['PlsrDAC']['bitlength']) ] if self.scan_parameters.PlsrDAC[0]: scan_parameter_range[0] = self.scan_parameters.PlsrDAC[0] if self.scan_parameters.PlsrDAC[1]: scan_parameter_range[1] = self.scan_parameters.PlsrDAC[1] logging.info( "Scanning %s from %d to %d" % ('PlsrDAC', scan_parameter_range[0], scan_parameter_range[1])) self.scan_parameter_value = scan_parameter_range[ 0] # set to start value self.search_distance = self.search_distance self.data_points = 0 # counter variable to count the data points already recorded, have to be at least minimum_data_ponts # calculate DCs to scan from the columns to ignore enable_double_columns = range(0, 40) if 1 in self.ignore_columns: enable_double_columns.remove(0) if set((78, 79, 80)).issubset(self.ignore_columns): enable_double_columns.remove(39) for double_column in range(1, 39): if set((double_column * 2, (double_column * 2) + 1)).issubset(self.ignore_columns): enable_double_columns.remove(double_column) logging.info("Use DCs: %s" % str(enable_double_columns)) self.select_arr_columns = range(0, 80) for column in self.ignore_columns: self.select_arr_columns.remove(column - 1) while self.scan_parameter_value <= scan_parameter_range[ 1]: # scan as long as scan parameter is smaller than defined maximum if self.stop_run.is_set(): break if self.record_data: logging.info( "Scan step %d (%s %d)" % (self.data_points, 'PlsrDAC', self.scan_parameter_value)) commands = [] commands.extend(self.register.get_commands("ConfMode")) self.register.set_global_register_value('PlsrDAC', self.scan_parameter_value) commands.extend( self.register.get_commands("WrRegister", name=['PlsrDAC'])) self.register_utils.send_commands(commands) with self.readout(PlsrDAC=self.scan_parameter_value): cal_lvl1_command = self.register.get_commands( "CAL")[0] + self.register.get_commands( "zeros", length=40)[0] + self.register.get_commands("LV1")[0] scan_loop(self, cal_lvl1_command, repeat_command=self.n_injections, use_delay=True, mask_steps=self.mask_steps, enable_mask_steps=self.enable_mask_steps, enable_double_columns=enable_double_columns, 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=False, mask=invert_pixel_mask( self.register.get_pixel_register_value('Enable')) if self.use_enable_mask else None, double_column_correction=self.pulser_dac_correction) if not self.start_condition_triggered or self.data_points > self.minimum_data_points: # speed up, only create histograms when needed. Python is much too slow here. if not self.start_condition_triggered and not self.record_data: logging.info('Testing for start condition: %s %d' % ('PlsrDAC', self.scan_parameter_value)) if not self.stop_condition_triggered and self.record_data: logging.info('Testing for stop condition: %s %d' % ('PlsrDAC', self.scan_parameter_value)) col, row = 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) # using self written histogrammer in C++ occupancy_array = hist_2d_index(col - 1, row - 1, shape=(80, 336)) # using numpy # occupancy_array = np.histogram2d(col, row, bins=(80, 336), range=[[1, 80], [1, 336]])[0] self.scan_condition(occupancy_array) # start condition is met for the first time if self.start_condition_triggered and not self.record_data: self.scan_parameter_value = self.scan_parameter_value - self.search_distance + self.step_size if self.scan_parameter_value < 0: self.scan_parameter_value = 0 logging.info('Starting threshold scan at %s %d' % ('PlsrDAC', self.scan_parameter_value)) self.scan_parameter_start = self.scan_parameter_value self.record_data = True continue # saving data if self.record_data: self.data_points = self.data_points + 1 self.raw_data_file.append( self.fifo_readout.data, scan_parameters=self.scan_parameters._asdict()) # stop condition is met for the first time if self.stop_condition_triggered and self.record_data: logging.info('Stopping threshold scan at %s %d' % ('PlsrDAC', self.scan_parameter_value)) break # increase scan parameter value if not self.start_condition_triggered: self.scan_parameter_value = self.scan_parameter_value + self.search_distance else: self.scan_parameter_value = self.scan_parameter_value + self.step_size if self.scan_parameter_value >= scan_parameter_range[1]: logging.warning( "Reached maximum of PlsrDAC range... stopping scan")
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 enable_mask_steps = [0] # one mask step to increase speed, no effect on precision 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)[0] self.write_target_charge() for feedback_bit in self.feedback_tune_bits: # reset all GDAC bits self.set_prmp_vbpf_bit(feedback_bit, bit_value=0) additional_scan = True last_bit_result = self.n_injections_feedback tot_mean_best = 0 feedback_best = self.register.get_global_register_value("PrmpVbpf") for feedback_bit in self.feedback_tune_bits: if additional_scan: self.set_prmp_vbpf_bit(feedback_bit) logging.info('PrmpVbpf setting: %d, 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, 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, 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_feedback, mask_steps=self.mask_steps, enable_mask_steps=enable_mask_steps, 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) tots = convert_data_array(data_array_from_data_iterable(self.fifo_readout.data), filter_func=is_data_record, converter_func=get_tot_array_from_data_record_array) mean_tot = np.mean(tots) if np.isnan(mean_tot): logging.error("No hits, ToT calculation not possible, tuning will fail") 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 = %f', mean_tot) self.tot_array, _ = np.histogram(a=tots, range=(0, 16), bins=16) if self.plot_intermediate_steps: plot_tot(hist=self.tot_array, 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 and mean_tot < self.target_tot: self.set_prmp_vbpf_bit(feedback_bit, bit_value=0) logging.info('Mean ToT = %f < %d ToT, set bit %d = 0', mean_tot, self.target_tot, feedback_bit) if feedback_bit == 0: if additional_scan: # scan bit = 0 with the correct value again additional_scan = False last_bit_result = mean_tot self.feedback_tune_bits.append(0) # bit 0 has to be scanned twice else: logging.info('Scanned bit 0 = 0 with %f instead of %f for scanned bit 0 = 1', mean_tot, last_bit_result) if(abs(mean_tot - self.target_tot) > abs(last_bit_result - self.target_tot)): # if bit 0 = 0 is worse than bit 0 = 1, so go back self.set_prmp_vbpf_bit(feedback_bit, bit_value=1) mean_tot = last_bit_result logging.info('Set bit 0 = 1') else: logging.info('Set bit 0 = 0') if abs(mean_tot - self.target_tot) > abs(tot_mean_best - self.target_tot): logging.info("Binary search converged to non optimal value, take best measured value instead") mean_tot = tot_mean_best self.register.set_global_register_value("PrmpVbpf", feedback_best) if self.register.get_global_register_value("PrmpVbpf") == 0 or self.register.get_global_register_value("PrmpVbpf") == 254: logging.warning('PrmpVbpf reached minimum/maximum value') if self.fail_on_warning: raise RuntimeWarning('PrmpVbpf reached minimum/maximum value') if abs(mean_tot - self.target_tot) > 2 * self.max_delta_tot: logging.warning('Global feedback tuning failed. Delta ToT = %f > %f. PrmpVbpf = %d', abs(mean_tot - self.target_tot), self.max_delta_tot, self.register.get_global_register_value("PrmpVbpf")) if self.fail_on_warning: raise RuntimeWarning('Global feedback tuning failed.') else: logging.info('Tuned PrmpVbpf to %d', self.register.get_global_register_value("PrmpVbpf")) self.feedback_best = self.register.get_global_register_value("PrmpVbpf")