Skip to main content

gondola_core/tracker/
online_calibration.rs

1// This file is part of gaps-online-software and published 
2// under the GPLv3 license
3
4use crate::prelude::*;
5
6use std::io::BufRead;
7#[cfg(feature="pybindings")]
8use std::path::PathBuf;
9
10#[cfg_attr(feature="pybindings", pyclass)] 
11pub struct TrackerOnlineCalibration {
12  pub tf_map     : HashMap<u32,TrackerStripTransferFunction>, 
13  pub ped_map    : HashMap<u32,f32>,
14  pub pulser_map : HashMap<u32,bool>
15}
16
17impl TrackerOnlineCalibration {
18
19  pub fn new() -> Self {
20    Self {
21      tf_map     : HashMap::<u32,TrackerStripTransferFunction>::new(), 
22      ped_map    : HashMap::<u32,f32>::new(),
23      pulser_map : HashMap::<u32,bool>::new()
24    }
25  }
26
27  /// Load the online tracker calibration from the file 
28  /// as it was used in the GAPSI flight.
29  pub fn from_default() -> Self {
30    let cali_path  = env::var("GONDOLA_TRK_ONLINE_CAL").unwrap_or_else(|_| "".to_string());
31    Self::from_file(&cali_path)
32  }
33
34  //pub fn from_file(fname : &str) -> Self {
35  pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
36    let mut cali = Self::new();
37    let file_res = File::open(path);
38    match file_res {
39      Err(_err) => {
40        error!("Unable to open file!");
41        return cali; 
42      }
43      Ok(file) => {
44        let reader = BufReader::new(file);
45        //let mut data = Vec::new();
46
47        // Use .skip(5) to bypass the header lines
48        for line in reader.lines().skip(5) {
49          let line = line.unwrap();
50          let trimmed = line.trim();
51          if trimmed.is_empty() {
52            continue;
53          }
54          let row: Vec<f32> = trimmed
55            .split(',')
56            .map(|s| s.trim().parse::<f32>().unwrap_or(0.0))
57            .collect();
58          let mut strip = TrackerStrip::new();
59          strip.layer   = row[0] as i32;
60          strip.row     = row[1] as i32;
61          strip.module  = row[2] as i32;
62          strip.channel = row[3] as i32;
63          let mut tf    = TrackerStripTransferFunction::new();
64          let strip_id  = strip.get_stripid();
65          tf.strip_id   = strip_id as i32;
66          tf.pol_a2_0   = row[4]; 
67          tf.pol_a2_1   = row[5];    
68          tf.pol_a2_2   = row[6]; 
69          tf.pol_b3_0   = row[7]; 
70          tf.pol_b3_1   = row[8]; 
71          tf.pol_b3_2   = row[9]; 
72          tf.pol_b3_3   = row[10]; 
73          tf.pol_c3_0   = row[11]; 
74          tf.pol_c3_1   = row[12]; 
75          tf.pol_c3_2   = row[13]; 
76          tf.pol_c3_3   = row[14]; 
77          tf.pol_d3_0   = row[15];     
78          tf.pol_d3_1   = row[16]; 
79          tf.pol_d3_2   = row[17]; 
80          tf.pol_d3_3   = row[18]; 
81          let pedestal  = row[19];
82          let is_puls   = row[20] > 0.0;
83          cali.tf_map.insert(strip_id, tf);
84          cali.pulser_map.insert(strip_id, is_puls);
85          cali.ped_map.insert(strip_id, pedestal as f32);
86          //data.push(row);
87        }
88      }
89    }
90    cali
91  }
92
93  /// Fill out the energy field in the tracker hit
94  /// 
95  /// FIXME - this panics if the calibration is not 
96  /// loaded properly
97  pub fn calibrate(&self, hit : &mut TrackerHit) {
98    let strip_id = hit.get_stripid();
99    let scale   : f32 = 0.841/1000.0;
100    let mut adc : f32 = hit.adc as f32 - self.ped_map[&strip_id];
101    if adc > 1500.0 {
102      adc = 1500.0;
103    } 
104    hit.energy = scale*self.tf_map[&strip_id].transfer_fn(adc);
105  }
106
107  pub fn is_pulsed(&self, hit : &TrackerHit) -> bool {
108    self.pulser_map[&hit.get_stripid()] 
109  }
110} 
111
112#[cfg(feature="pybindings")]
113#[pymethods]
114impl TrackerOnlineCalibration {
115
116  #[new]
117  fn new_py() -> Self {
118    Self::new()
119  }
120 
121  /// Fill out the energy field in the tracker hit
122  /// 
123  /// FIXME - this panics if the calibration is not 
124  /// loaded properly
125  ///
126  /// # Arguments: 
127  ///   * hit : A tracker hit. This hit will have its 
128  ///           .energy field populated
129  #[pyo3(name="calibrate")]
130  fn calibrate_py(&self, hit : &mut TrackerHit) {
131    self.calibrate(hit);
132  }
133
134  #[pyo3(name="is_pulsed")]
135  fn is_pulsed_py(&self, hit : &TrackerHit) -> bool {
136    self.is_pulsed(hit)
137  }
138  
139  /// Load the online tracker calibration from the file 
140  /// as it was used in the GAPSI flight.
141  ///
142  /// FIXME - if this fails, we won't know
143  #[staticmethod]
144  #[pyo3(name="from_default")]
145  pub fn from_default_py() -> PyResult<Self> {
146    let cali_path  = env::var("GONDOLA_TRK_ONLINE_CAL").unwrap_or_else(|_| "".to_string());
147    Ok(Self::from_file(&cali_path))
148  }
149
150  #[staticmethod]
151  #[pyo3(name="from_file")]
152  fn from_file_py(path: String) -> PyResult<Self> {
153    // Convert the Python String to a PathBuf
154    let path_buf = PathBuf::from(path);
155    Ok(Self::from_file(path_buf))
156  }
157}
158