Source code for astropix_analysis.analysis

# Copyright (C) 2025 the astropix team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

"""Analysis tools the astropix chip.
"""

import numpy as np

from astropix_analysis import logger
from astropix_analysis.plt_ import plt


[docs] class Run: """Small conveinence class to read one input cvs file. dec_ord, id, payload, row, col, ts1, tsfine1, ts2, tsfine2, tsneg1, tsneg2, tstdc1, tstdc2, ts_dec1, ts_dec2, tot_us """ _NUM_ROWS = 13 _NUM_COLS = 16 def __init__(self, file_path: str) -> None: """Constructor. """ logger.info(f'Opening input file {file_path}...') try: self.dec_ord, _, _, self.row, self.col, _, _, _, _, _, _, _, _, self.rise_time, \ self.fall_time, self.tot = \ np.loadtxt(file_path, skiprows=1, delimiter=',', unpack=True) except ValueError: logger.warning("ValueError found - file is probably empty") self.dec_ord = [] self.tot = [] self.row = [] self.col = [] logger.info(f'Done, {len(self)} event(s) read.') log_file_path = str(file_path).replace('.csv', '.log') logger.info(f'Reading associated log file {log_file_path}...') with open(log_file_path, 'r', encoding='utf-8') as log_file: self.voltage_card = self._parse_line(log_file) self.digital = self._parse_line(log_file) self.bias_block = self._parse_line(log_file) self.idac = self._parse_line(log_file) self.vdac = self._parse_line(log_file) self.receiver = self._parse_line(log_file) self.options = self._parse_line(log_file, False) logger.info(f'Command-line options: {self.options}')
[docs] @staticmethod def _parse_line(log_file, split_by_colon: bool = True): """Parse one single line from a log file. """ # pylint: disable=eval-used if split_by_colon: _, data = log_file.readline().split(':', 1) else: data = log_file.readline() data = eval(data) logger.debug(data) return data
[docs] def filter_last_tot(self, maxtot=10000): """ Check for multiple trigger of the same pixel in the same buffer and return last ToT Assuming events are ordered and check that dec_ord of the following line is 0 Adding a flag to remove too-large TOT: probably a deconding issue """ filtered_tot = [] if len(self) == 0: return filtered_tot for i in range(len(self)-1): if self.dec_ord[i+1] == 0: if self.tot[i] <= maxtot: filtered_tot.append(self.tot[i]) if self.tot[i] <= maxtot: filtered_tot.append(self.tot[-1]) # always adding last line return np.array(filtered_tot)
def __len__(self) -> int: """Return the number of events in the data file. """ if isinstance(self.tot, np.float64): return 1 return len(self.tot)
[docs] def inject_pixels(self): """Return whether the charge injection was enabled. """ return self.options.inject
[docs] def trigger_threshold(self) -> float: """Return the trigger threshold. """ return self.options.threshold
[docs] def running_time(self) -> float: """ Running time in seconds """ return self.options.maxtime*60
[docs] def injection_voltage(self) -> float: """Return the injection voltage. """ return self.options.vinj
[docs] def tot_hist(self, binning: np.ndarray = None): """Make a histogram of the TOT. """ plt.figure('TOT distribution') plt.hist(self.tot, bins=binning) plt.xlabel(r'TOT [$\mu$s]')
[docs] def hit_map(self): """Make a hit map. """ plt.figure('Hitmap') binning = ( np.linspace(-0.5, self._NUM_COLS - 0.5, self._NUM_COLS + 1), np.linspace(-0.5, self._NUM_ROWS - 0.5, self._NUM_ROWS + 1) ) data, _, _, _ = plt.hist2d(self.col, self.row, bins=binning) logger.info(f'Pixel occupancy: \n{data}') plt.xlabel('Column') plt.ylabel('Row') plt.gca().set_aspect('equal') return data
# if __name__ == '__main__': # # MOVE ALL OF THIS TO A PROPER UNIT TEST! # from astropix_analysis import ASTROPIX_DATA # import os # # a file with multiple redout # file_path = os.path.join(ASTROPIX_DATA, 'runsuite_vscan_20241219-114807\\20241219-115526.csv') # run = Run(file_path) # run.hit_map() # # run.tot_hist(np.linspace(0, 500, 501)) # plt.figure('TOT distribution') # plt.hist(run.tot, bins=np.linspace(0, 500, 201), label='all entries') # plt.xlabel(r'TOT [$\mu$s]') # filtered_tot = run.filter_last_tot(1000) # print(filtered_tot, np.mean(filtered_tot), np.std(filtered_tot, ddof=1)) # # plt.figure() # plt.hist(filtered_tot, bins=np.linspace(0, 500, 201), alpha=0.5, label='highest entry only') # plt.legend() # plt.show()