Пример #1
0
 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)
Пример #2
0
 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)
Пример #3
0
 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)
Пример #4
0
    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()
Пример #5
0
    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"))
Пример #6
0
    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"))
Пример #7
0
    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"))
Пример #8
0
    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"))
Пример #10
0
    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"))