Skip to main content

gondola_core/database/
tracker_transfer_fn.rs

1//! Database access & entities for gaps-online-software
2//!
3//! A local .sqlite database is shipped with gaps-online-software,
4//! pre-populated with relevant meta data for the GAPS experiment.
5// This file is part of gaps-online-software and published 
6// under the GPLv3 license
7
8use crate::prelude::*;
9
10use crate::database::schema;
11use diesel::prelude::*;
12
13use std::io::{
14  self,
15  BufRead,
16  BufReader
17};
18
19/// Tracker transfer functions connect the tracker adc to a measurement of energy
20#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
21#[diesel(table_name = schema::tof_db_trackerstriptransferfunction)]
22#[diesel(primary_key(data_id))]
23#[allow(non_snake_case)]
24#[cfg_attr(feature="pybindings", pyclass)]
25pub struct TrackerStripTransferFunction {  
26    pub data_id            : i32,
27    pub strip_id           : i32,    
28    pub volume_id          : i64,    
29    pub utc_timestamp_start: i64,    
30    pub utc_timestamp_stop : i64,    
31    pub name               : Option<String>, 
32    pub pol_a2_0           : f32, 
33    pub pol_a2_1           : f32,    
34    pub pol_a2_2           : f32, 
35    pub pol_b3_0           : f32, 
36    pub pol_b3_1           : f32, 
37    pub pol_b3_2           : f32, 
38    pub pol_b3_3           : f32, 
39    pub pol_c3_0           : f32, 
40    pub pol_c3_1           : f32, 
41    pub pol_c3_2           : f32, 
42    pub pol_c3_3           : f32, 
43    pub pol_d3_0           : f32,     
44    pub pol_d3_1           : f32, 
45    pub pol_d3_2           : f32, 
46    pub pol_d3_3           : f32, 
47} 
48
49impl TrackerStripTransferFunction {
50
51  pub fn new() -> Self {
52    Self {
53      data_id             : 0,
54      strip_id            : 0,    
55      volume_id           : 0,    
56      utc_timestamp_start : 0,    
57      utc_timestamp_stop  : 0,
58      name                : None, 
59      pol_a2_0            : 0.0, 
60      pol_a2_1            : 0.0,    
61      pol_a2_2            : 0.0, 
62      pol_b3_0            : 0.0, 
63      pol_b3_1            : 0.0, 
64      pol_b3_2            : 0.0, 
65      pol_b3_3            : 0.0, 
66      pol_c3_0            : 0.0, 
67      pol_c3_1            : 0.0, 
68      pol_c3_2            : 0.0, 
69      pol_c3_3            : 0.0, 
70      pol_d3_0            : 0.0,     
71      pol_d3_1            : 0.0, 
72      pol_d3_2            : 0.0, 
73      pol_d3_3            : 0.0, 
74    }
75  }
76  
77  pub fn parse_from_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<Self>> {
78    let file = File::open(path)?;
79    let reader = BufReader::new(file);
80    let mut transfer_fns = Vec::<Self>::new();
81    let hid_vid_map = get_hid_vid_maps().unwrap().1;
82    for line in reader.lines() {
83      let line = line?;
84      if line.starts_with("#") || line.starts_with("Layer") || line.starts_with("layer") {
85        continue;
86      }
87      let mut trfn = Self::new();
88      let parts: Vec<&str> = line.split(",").collect();
89      if parts.len() == 19 {
90        // Parse the first number as a standard decimal
91        let layer   = parts[0].parse::<u8>()
92          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
93        let row     = parts[1].parse::<u8>()
94          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
95        let module   = parts[2].parse::<u8>()
96          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
97        let channel  = parts[3].parse::<u8>()
98          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
99        trfn.strip_id  = TrackerStrip::create_stripid(layer, row, module, channel) as i32; 
100        trfn.volume_id = *hid_vid_map.get(&(trfn.strip_id as u32)).unwrap() as i64; // critical error is good here,
101        trfn.pol_a2_0  = parts[4].parse::<f32>()
102          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
103        trfn.pol_a2_1  = parts[5].parse::<f32>()
104          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
105        trfn.pol_a2_2  = parts[6].parse::<f32>()
106          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
107        trfn.pol_b3_0  = parts[7].parse::<f32>()
108          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
109        trfn.pol_b3_1  = parts[8].parse::<f32>()
110          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
111        trfn.pol_b3_2  = parts[9].parse::<f32>()
112          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
113        trfn.pol_b3_3  = parts[10].parse::<f32>()
114          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
115        trfn.pol_c3_0  = parts[11].parse::<f32>()
116          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
117        trfn.pol_c3_1  = parts[12].parse::<f32>()
118          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
119        trfn.pol_c3_2  = parts[13].parse::<f32>()
120          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
121        trfn.pol_c3_3  = parts[14].parse::<f32>()
122          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
123        trfn.pol_d3_0  = parts[15].parse::<f32>()
124          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
125        trfn.pol_d3_1  = parts[16].parse::<f32>()
126          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
127        trfn.pol_d3_2  = parts[17].parse::<f32>()
128          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
129        trfn.pol_d3_3  = parts[18].parse::<f32>()
130          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
131        transfer_fns.push(trfn); 
132      }
133    }
134    Ok(transfer_fns)
135  }
136  
137  /// Get Tracker strip transfer fns for a certain dataset 
138  ///
139  /// # Returns:
140  ///   * HashMap<u32 [strip id], TrackerStripTransferFn> 
141  pub fn as_dict_by_name(fname : &str) -> Result<HashMap<u32,Self>, ConnectionError> {
142    use schema::tof_db_trackerstriptransferfunction::dsl::*;
143    let mut strips = HashMap::<u32, Self>::new();
144    if fname == "" {
145      match Self::all() {
146        None => {
147          error!("Unable to retrive ANY TrackerStripTransferFunction");
148          return Ok(strips);
149        }
150        Some(_strips) => {
151          for k in _strips {
152            strips.insert(k.strip_id as u32, k);
153          }
154          return Ok(strips);
155        }
156      }
157    }
158    let mut conn = connect_to_db()?;
159    match tof_db_trackerstriptransferfunction.filter(
160      schema::tof_db_trackerstriptransferfunction::name.eq(fname)).load::<Self>(&mut conn) {
161      Err(err) => {
162        error!("We can't find any tracker strip transferfunction in the database! {err}");
163        return Ok(strips);
164      }
165      Ok(peds_) => {
166        for s in peds_ {
167          strips.insert(s.strip_id as u32, s );
168        }
169      }
170    }
171    return Ok(strips);
172  }
173  
174  pub fn all_names() -> Result<Vec<String>, ConnectionError> {
175    let mut conn = connect_to_db()?;
176    let mut names = Vec::<String>::new();
177    let unique_names =
178      schema::tof_db_trackerstriptransferfunction::table.select(
179      schema::tof_db_trackerstriptransferfunction::name)
180      .distinct()
181      .load::<Option<String>>(&mut conn).expect("Error getting names from db!");
182    for k in unique_names {
183      if let Some(n) = k {
184        names.push(n);
185      }
186    }
187    Ok(names)
188  }
189
190  /// Get all tracker strip transfer functions from the database
191  ///
192  /// # Returns:
193  ///   * HashMap<u32 [strip id], TrackeStripTransferFunction> 
194  pub fn all() -> Option<Vec<Self>> {
195    use schema::tof_db_trackerstriptransferfunction::dsl::*;
196    let mut conn = connect_to_db().ok()?;
197    match tof_db_trackerstriptransferfunction.load::<Self>(&mut conn) {
198      Err(err) => {
199        error!("Unable to load tracker transfer functions from db! {err}");
200        return None;
201      }
202      Ok(strips) => {
203        return Some(strips);
204      }
205    }
206  }
207
208  /// The actual transfer function for this 
209  /// strip. Calculate energy from adc values
210  pub fn transfer_fn(&self, adc : f32) -> f32 {
211    if adc < 0.0 {
212      return 0.0;
213    }
214    if adc <= 190.0 {
215      return self.pol_a2_0 + self.pol_a2_1*adc + self.pol_a2_2*(adc.powi(2));
216    }
217    if 190.0 < adc && adc <= 500.0 {
218      return self.pol_b3_0 + self.pol_b3_1*adc + self.pol_b3_2*(adc.powi(2)) + self.pol_b3_3*(adc.powi(3));
219    }
220    if 500.0 < adc && adc <= 900.0 {
221      return self.pol_c3_0 + self.pol_c3_1*adc + self.pol_c3_2*(adc.powi(2)) + self.pol_c3_3*(adc.powi(3));
222    }
223    //if 900.0 < adc && adc <= 2047.0 {
224    if 900.0 < adc && adc <= 1600.0 {
225      return self.pol_d3_0 + self.pol_d3_1*adc + self.pol_d3_2*(adc.powi(2)) + self.pol_d3_3*(adc.powi(3));
226    }
227    0.0
228  }
229}
230
231impl fmt::Display for TrackerStripTransferFunction {
232  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233    let mut repr = format!("<TrackerStripTransferFunction [{}]:", self.strip_id);
234    repr += &(format!("\n   vid           : {}", self.volume_id));
235    repr += "\n   UTC Timestamps (Begin/End):";
236    repr += &(format!("\n   {}/{}", self.utc_timestamp_start, self.utc_timestamp_stop));    
237    if self.name.is_some() {
238      repr += &(format!("\n   name     : {}", self.name.clone().unwrap())); 
239    }
240    repr += &(format!("\n  Poly A {}*adc + {}*adc + {}*(adc**2) for adc < 190", self.pol_a2_0, self.pol_a2_1, self.pol_a2_2));
241    repr += &(format!("\n  Poly B    :{}*adc + {}*adc + {}*(adc**2) + {}*(adc**3) for 190 < adc <= 500", self.pol_b3_0, self.pol_b3_1, self.pol_b3_2, self.pol_b3_3));
242    repr += &(format!("\n  Poly C    :{}*adc + {}*adc + {}*(adc**2) + {}*(adc**3) for 500 < adc <= 900", self.pol_c3_0, self.pol_c3_1, self.pol_c3_2, self.pol_c3_3));
243    repr += &(format!("\n  Poly D    :{}*adc + {}*adc + {}*(adc**2) + {}*(adc**3) for 900 < adc <= 1600>", self.pol_d3_0, self.pol_d3_1, self.pol_d3_2, self.pol_d3_3));
244    write!(f, "{}", repr)
245  }
246}
247
248#[cfg(feature="pybindings")]
249#[pymethods]
250impl TrackerStripTransferFunction {
251  
252  #[staticmethod]
253  #[pyo3(name="all")]
254  pub fn all_py() -> Option<Vec<Self>> {
255    Self::all()
256  } 
257  
258  #[staticmethod]
259  #[pyo3(name="all_names")]
260  /// Get all names for registered datasets. These
261  /// can be used in .as_dict_by_name() to query 
262  /// the db for a set of values
263  pub fn all_names_py() -> Option<Vec<String>> {
264    match Self::all_names() {
265      Err(_) => {
266        return None;
267      }
268      Ok(names) => {
269        return Some(names);
270      }
271    }
272  } 
273  
274  #[staticmethod]
275  #[pyo3(name="as_dict_by_name")]
276  pub fn all_as_dict_py(name : &str) -> Option<HashMap<u32,Self>> {
277    match Self::as_dict_by_name(name) {
278      Err(err) => {
279        error!("Unable to retrieve tracker strip transfer fn dictionary. {err}. Did you laod the setup-env.sh shell?");
280        return None;
281      }
282      Ok(_data) => {
283        return Some(_data);
284      }
285    }
286  } 
287  
288  #[getter]
289  fn get_strip_id     (&self) -> i32 {    
290    self.strip_id
291  }
292  
293  #[getter]
294  fn get_volume_id    (&self) -> i64 {    
295    self.volume_id
296  }
297  
298  #[getter]
299  fn get_utc_timestamp_start(&self) -> i64 {
300    self.utc_timestamp_start
301  }
302  
303  #[getter]
304  fn get_utc_timestamp_stop(&self) -> i64 {
305    self.utc_timestamp_stop
306  }
307  
308  #[setter]
309  fn set_utc_timestamp_start(&mut self, value : i64) {
310    self.utc_timestamp_start = value;
311  }
312  
313  #[setter]
314  fn set_utc_timestamp_stop(&mut self, value : i64) {
315    self.utc_timestamp_stop = value;
316  }
317
318  #[getter]
319  fn get_name(&self) -> Option<String> {
320    self.name.clone()
321  }
322  
323  #[setter]
324  fn set_name(&mut self, value : String) {
325    self.name = Some(value);
326  }
327
328  #[pyo3(name="transfer_fn")]
329  fn transfer_fn_py(&self, adc : f32) -> f32 {
330    if adc > 1600.0 {
331      warn!("ADC value larger than 1600! {}. Transfer fn not defined beyond 1600.", adc);
332    }
333    return self.transfer_fn(adc);
334  }
335  
336  #[staticmethod]
337  #[pyo3(name="parse_from_file")]
338  fn parse_from_file_py(fname : &str) -> Option<Vec<Self>> {
339    let result = Self::parse_from_file(fname);
340    if result.is_ok() {
341      return Some(result.unwrap());
342    } else {
343      error!("An error occured when parsing {} : '{}'", fname, result.unwrap_err());
344      return None;
345    }
346  }
347}
348
349#[cfg(feature="pybindings")]
350pythonize!(TrackerStripTransferFunction);
351
352//-------------------------------------------------
353
354#[derive(Debug,PartialEq, Clone, Insertable)]
355#[diesel(table_name = schema::tof_db_trackerstriptransferfunction)]
356#[allow(non_snake_case)]
357#[cfg_attr(feature="pybindings", pyclass)]
358struct NewTrackerStripTransferFunction {  
359  pub strip_id           : i32,    
360  pub volume_id          : i64,    
361  pub utc_timestamp_start: i64,    
362  pub utc_timestamp_stop : i64,    
363  pub name               : Option<String>, 
364  pub pol_a2_0           : f32, 
365  pub pol_a2_1           : f32,    
366  pub pol_a2_2           : f32, 
367  pub pol_b3_0           : f32, 
368  pub pol_b3_1           : f32, 
369  pub pol_b3_2           : f32, 
370  pub pol_b3_3           : f32, 
371  pub pol_c3_0           : f32, 
372  pub pol_c3_1           : f32, 
373  pub pol_c3_2           : f32, 
374  pub pol_c3_3           : f32, 
375  pub pol_d3_0           : f32,     
376  pub pol_d3_1           : f32, 
377  pub pol_d3_2           : f32, 
378  pub pol_d3_3           : f32, 
379} 
380
381impl NewTrackerStripTransferFunction {
382  pub fn from(tf : &TrackerStripTransferFunction) -> Self {
383    Self {
384      strip_id            : tf.strip_id            ,    
385      volume_id           : tf.volume_id           ,    
386      utc_timestamp_start : tf.utc_timestamp_start ,    
387      utc_timestamp_stop  : tf.utc_timestamp_stop  ,    
388      name                : tf.name.clone()        , 
389      pol_a2_0            : tf.pol_a2_0            , 
390      pol_a2_1            : tf.pol_a2_1            ,    
391      pol_a2_2            : tf.pol_a2_2            , 
392      pol_b3_0            : tf.pol_b3_0            , 
393      pol_b3_1            : tf.pol_b3_1            , 
394      pol_b3_2            : tf.pol_b3_2            , 
395      pol_b3_3            : tf.pol_b3_3            , 
396      pol_c3_0            : tf.pol_c3_0            , 
397      pol_c3_1            : tf.pol_c3_1            , 
398      pol_c3_2            : tf.pol_c3_2            , 
399      pol_c3_3            : tf.pol_c3_3            , 
400      pol_d3_0            : tf.pol_d3_0            ,     
401      pol_d3_1            : tf.pol_d3_1            , 
402      pol_d3_2            : tf.pol_d3_2            , 
403      pol_d3_3            : tf.pol_d3_3            , 
404    }
405  }
406}
407
408#[cfg_attr(feature="pybindings", pyfunction)]
409pub fn create_trk_transfer_fn_table( db_path: &str, transfer_fns: Vec<TrackerStripTransferFunction>) { 
410  use schema::tof_db_trackerstriptransferfunction::dsl::*;
411  let mut conn = SqliteConnection::establish(db_path).ok().unwrap(); 
412  let mut _query_result = diesel::sql_query("
413      CREATE TABLE IF NOT EXISTS tof_db_trackerstriptransferfunction (
414          data_id INTEGER PRIMARY KEY AUTOINCREMENT,
415          strip_id INTEGER NOT NULL,
416          volume_id BIGINT NOT NULL,
417          utc_timestamp_start BIGINT NOT NULL,
418          utc_timestamp_stop BIGINT NOT NULL,
419          name TEXT,
420          pol_a2_0 FLOAT NOT NULL, 
421          pol_a2_1 FLOAT NOT NULL,    
422          pol_a2_2 FLOAT NOT NULL, 
423          pol_b3_0 FLOAT NOT NULL, 
424          pol_b3_1 FLOAT NOT NULL, 
425          pol_b3_2 FLOAT NOT NULL, 
426          pol_b3_3 FLOAT NOT NULL, 
427          pol_c3_0 FLOAT NOT NULL, 
428          pol_c3_1 FLOAT NOT NULL, 
429          pol_c3_2 FLOAT NOT NULL, 
430          pol_c3_3 FLOAT NOT NULL, 
431          pol_d3_0 FLOAT NOT NULL,     
432          pol_d3_1 FLOAT NOT NULL, 
433          pol_d3_2 FLOAT NOT NULL, 
434          pol_d3_3 FLOAT NOT NULL 
435      )
436  ").execute(&mut conn);
437  let mut new_data = Vec::<NewTrackerStripTransferFunction>::new();
438  for p in transfer_fns {
439    let np = NewTrackerStripTransferFunction::from(&p);
440    new_data.push(np);
441  }
442  println!("Will insert {} tfns!", new_data.len());
443  _query_result = diesel::insert_into(tof_db_trackerstriptransferfunction)
444    .values(&new_data)
445    .execute(&mut conn);
446  println!("Inserted {}", _query_result.unwrap());
447}
448