1#[cfg(feature = "pybindings")]
3use pyo3::pyclass;
4
5cfg_if::cfg_if! {
6 if #[cfg(feature = "random")] {
7 use crate::FromRandom;
8 extern crate rand;
9 use rand::Rng;
10 }
11}
12
13use std::fmt;
14use crate::serialization::{
18 Serialization,
19 Packable,
20 SerializationError,
21 search_for_u16,
22 parse_u8,
23 parse_u16,
24 parse_u32,
25 parse_u64,
26};
27
28use crate::packets::{
29 PacketType
30};
31
32use crate::events::{
34 EventStatus,
35 TofEventSummary,
36 transcode_trigger_sources,
37};
38
39pub const LTB_CH0 : u16 = 0x3 ;
90pub const LTB_CH1 : u16 = 0xc ;
91pub const LTB_CH2 : u16 = 0x30 ;
92pub const LTB_CH3 : u16 = 0xc0 ;
93pub const LTB_CH4 : u16 = 0x300 ;
94pub const LTB_CH5 : u16 = 0xc00 ;
95pub const LTB_CH6 : u16 = 0x3000;
96pub const LTB_CH7 : u16 = 0xc000;
97pub const LTB_CHANNELS : [u16;8] = [
98 LTB_CH0,
99 LTB_CH1,
100 LTB_CH2,
101 LTB_CH3,
102 LTB_CH4,
103 LTB_CH5,
104 LTB_CH6,
105 LTB_CH7
106];
107
108#[derive(Debug, Copy, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
115#[repr(u8)]
116#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
117pub enum TriggerType {
118 Unknown = 0u8,
119 Any = 1u8,
121 Track = 2u8,
122 TrackCentral = 3u8,
123 Gaps1044 = 9u8,
124 Gaps = 4u8,
125 Gaps633 = 5u8,
126 Gaps422 = 6u8,
127 Gaps211 = 7u8,
128 TrackUmbCentral = 8u8,
129 UmbCube = 21u8,
132 UmbCubeZ = 22u8,
134 UmbCorCube = 23u8,
136 CorCubeSide = 24u8,
138 Umb3Cube = 25u8,
140 Poisson = 100u8,
142 Forced = 101u8,
143 FixedRate = 102u8,
144 ConfigurableTrigger = 200u8,
148}
149
150impl fmt::Display for TriggerType {
151 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152 let r = serde_json::to_string(self).unwrap_or(
153 String::from("ERROR: DeserializationError!"));
154 write!(f, "<TriggerType: {}>", r)
155 }
156}
157
158impl TriggerType {
159 pub fn to_u8(&self) -> u8 {
160 match self {
161 TriggerType::Unknown => {
162 return 0;
163 }
164 TriggerType::Poisson => {
165 return 100;
166 }
167 TriggerType::Forced => {
168 return 101;
169 }
170 TriggerType::FixedRate => {
171 return 102;
172 }
173 TriggerType::Any => {
174 return 1;
175 }
176 TriggerType::Track => {
177 return 2;
178 }
179 TriggerType::TrackCentral => {
180 return 3;
181 }
182 TriggerType::Gaps => {
183 return 4;
184 }
185 TriggerType::Gaps633 => {
186 return 5;
187 }
188 TriggerType::Gaps422 => {
189 return 6;
190 }
191 TriggerType::Gaps211 => {
192 return 7;
193 }
194 TriggerType::TrackUmbCentral => {
195 return 8;
196 }
197 TriggerType::Gaps1044 => {
198 return 9;
199 }
200 TriggerType::UmbCube => {
201 return 21;
202 }
203 TriggerType::UmbCubeZ => {
204 return 22;
205 }
206 TriggerType::UmbCorCube => {
207 return 23;
208 }
209 TriggerType::CorCubeSide => {
210 return 24;
211 }
212 TriggerType::Umb3Cube => {
213 return 25;
214 }
215 TriggerType::ConfigurableTrigger => {
216 return 200;
217 }
218 }
219 }
220}
221
222impl From<u8> for TriggerType {
223 fn from(value: u8) -> Self {
224 match value {
225 0 => TriggerType::Unknown,
226 100 => TriggerType::Poisson,
227 101 => TriggerType::Forced,
228 102 => TriggerType::FixedRate,
229 1 => TriggerType::Any,
230 2 => TriggerType::Track,
231 3 => TriggerType::TrackCentral,
232 4 => TriggerType::Gaps,
233 5 => TriggerType::Gaps633,
234 6 => TriggerType::Gaps422,
235 7 => TriggerType::Gaps211,
236 8 => TriggerType::TrackUmbCentral,
237 9 => TriggerType::Gaps1044,
238 21 => TriggerType::UmbCube,
239 22 => TriggerType::UmbCubeZ,
240 23 => TriggerType::UmbCorCube,
241 24 => TriggerType::CorCubeSide,
242 25 => TriggerType::Umb3Cube,
243 200 => TriggerType::ConfigurableTrigger,
244 _ => TriggerType::Unknown
245 }
246 }
247}
248
249#[cfg(feature = "random")]
250impl FromRandom for TriggerType {
251
252 fn from_random() -> Self {
253 let choices = [
254 TriggerType::Unknown,
255 TriggerType::Poisson,
256 TriggerType::Forced,
257 TriggerType::FixedRate,
258 TriggerType::Any,
259 TriggerType::Track,
260 TriggerType::TrackCentral,
261 TriggerType::Gaps,
262 TriggerType::Gaps633,
263 TriggerType::Gaps422,
264 TriggerType::Gaps211,
265 TriggerType::TrackUmbCentral,
266 TriggerType::UmbCube,
267 TriggerType::UmbCubeZ,
268 TriggerType::UmbCorCube,
269 TriggerType::CorCubeSide,
270 TriggerType::Umb3Cube,
271 TriggerType::ConfigurableTrigger,
272 TriggerType::Gaps1044,
273 ];
274 let mut rng = rand::thread_rng();
275 let idx = rng.gen_range(0..choices.len());
276 choices[idx]
277 }
278}
279
280#[derive(Debug, Copy, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
285#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
286#[repr(u8)]
287pub enum LTBThreshold {
288 NoHit = 0u8,
289 Hit = 1u8,
291 Beta = 2u8,
293 Veto = 3u8,
295 Unknown = 255u8
298}
299
300impl fmt::Display for LTBThreshold {
301 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302 let r = serde_json::to_string(self).unwrap_or(
303 String::from("ERROR: DeserializationError!"));
304 write!(f, "<LTBThreshold: {}>", r)
305 }
306}
307
308impl From<u8> for LTBThreshold {
309 fn from(value: u8) -> Self {
310 match value {
311 0 => LTBThreshold::NoHit,
312 1 => LTBThreshold::Hit,
313 2 => LTBThreshold::Beta,
314 3 => LTBThreshold::Veto,
315 _ => LTBThreshold::Unknown
316 }
317 }
318}
319
320#[cfg(feature = "random")]
321impl FromRandom for LTBThreshold {
322
323 fn from_random() -> Self {
324 let choices = [
325 LTBThreshold::NoHit,
326 LTBThreshold::Hit,
327 LTBThreshold::Beta,
328 LTBThreshold::Veto,
329 LTBThreshold::Unknown
330 ];
331 let mut rng = rand::thread_rng();
332 let idx = rng.gen_range(0..choices.len());
333 choices[idx]
334 }
335}
336
337#[derive(Debug, Clone, PartialEq)]
346pub struct MasterTriggerEvent {
347 pub event_status : EventStatus,
348 pub event_id : u32,
349 pub timestamp : u32,
352 pub tiu_timestamp : u32,
354 pub tiu_gps32 : u32,
356 pub tiu_gps16 : u16,
357 pub crc : u32,
358 pub trigger_source : u16,
361 pub dsi_j_mask : u32,
362 pub channel_mask : Vec<u16>,
363 pub mtb_link_mask : u64,
364}
365
366impl MasterTriggerEvent {
367 pub const VERSION : u8 = 3;
370
371 pub fn new() -> Self {
372 Self {
373 event_status : EventStatus::Unknown,
374 event_id : 0,
375 timestamp : 0,
376 tiu_timestamp : 0,
377 tiu_gps32 : 0,
378 tiu_gps16 : 0,
379 crc : 0,
380 trigger_source : 0,
381 dsi_j_mask : 0,
382 channel_mask : Vec::<u16>::new(),
383 mtb_link_mask : 0,
384 }
385 }
386
387 pub fn get_rb_link_ids(&self) -> Vec<u8> {
389 let mut links = Vec::<u8>::new();
390 for k in 0..64 {
391 if (self.mtb_link_mask >> k) as u64 & 0x1 == 1 {
392 links.push(k as u8);
393 }
394 }
395 links
396 }
397
398 pub fn get_trigger_hits(&self) -> Vec<(u8, u8, (u8, u8), LTBThreshold)> {
411 let mut hits = Vec::<(u8,u8,(u8,u8),LTBThreshold)>::with_capacity(5);
412 let physical_channels = [(1u8, 2u8), (3u8,4u8), (5u8, 6u8), (7u8, 8u8),
413 (9u8, 10u8), (11u8,12u8), (13u8, 14u8), (15u8, 16u8)];
414 let n_masks_needed = self.dsi_j_mask.count_ones();
416 if self.channel_mask.len() < n_masks_needed as usize {
417 error!("We need {} hit masks, but only have {}! This is bad!", n_masks_needed, self.channel_mask.len());
418 return hits;
419 }
420 let mut n_mask = 0;
421 trace!("Expecting {} hit masks", n_masks_needed);
422 trace!("ltb channels {:?}", self.dsi_j_mask);
423 trace!("hit masks {:?}", self.channel_mask);
424 for k in 0..32 {
428 if (self.dsi_j_mask >> k) as u32 & 0x1 == 1 {
429 let mut dsi = 0u8;
430 let mut j = 0u8;
431 if k < 5 {
432 dsi = 1;
433 j = k as u8 + 1;
434 } else if k < 10 {
435 dsi = 2;
436 j = k as u8 - 5 + 1;
437 } else if k < 15 {
438 dsi = 3;
439 j = k as u8- 10 + 1;
440 } else if k < 20 {
441 dsi = 4;
442 j = k as u8- 15 + 1;
443 } else if k < 25 {
444 dsi = 5;
445 j = k as u8 - 20 + 1;
446 }
447 let channels = self.channel_mask[n_mask];
451 for (i,ch) in LTB_CHANNELS.iter().enumerate() {
452 let ph_chn = physical_channels[i];
454 let thresh_bits = ((channels & ch) >> (i*2)) as u8;
457 if thresh_bits > 0 { hits.push((dsi, j, ph_chn, LTBThreshold::from(thresh_bits)));
460 }
461 }
462 n_mask += 1;
463 } }
465 hits
466 }
467
468 #[deprecated(since = "0.10.3", note = "The timestamp of the gs is simply only 32 bits")]
487 pub fn get_timestamp_gps48(&self) -> u64 {
488 ((self.tiu_gps16 as u64) << 32) | self.tiu_gps32 as u64
489 }
490
491 pub fn get_timestamp_gps(&self) -> u32 {
492 self.tiu_gps32
493 }
494
495
496 pub fn get_timestamp_abs48(&self) -> u64 {
498 let gps = self.get_timestamp_gps() as u64;
499 let mut timestamp = self.timestamp as u64;
500 if timestamp < self.tiu_timestamp as u64 {
501 timestamp += u32::MAX as u64 + 1;
503 }
504 let gps_mult = match 100_000_000u64.checked_mul(gps) {
505 Some(result) => result,
507 None => {
508 0 }
512 };
513
514 let ts = gps_mult + (timestamp - self.tiu_timestamp as u64);
515 ts
516 }
517
518 pub fn get_trigger_sources(&self) -> Vec<TriggerType> {
525 transcode_trigger_sources(self.trigger_source)
526 }
527
528 pub fn get_global_trigger_soures(&self) -> Vec<TriggerType> {
533 let mut t_types = Vec::<TriggerType>::new();
534
535 let track_umb_central_trigger = self.trigger_source >> 11 & 0x1 == 1;
536 if track_umb_central_trigger{
537 t_types.push(TriggerType::TrackUmbCentral);
538 }
539 let central_track_trigger
540 = self.trigger_source >> 13 & 0x1 == 1;
541 if central_track_trigger {
542 t_types.push(TriggerType::TrackCentral);
543 }
544 let track_trigger = self.trigger_source >> 14 & 0x1 == 1;
545 if track_trigger {
546 t_types.push(TriggerType::Track);
547 }
548 let any_trigger = self.trigger_source >> 15 & 0x1 == 1;
549 if any_trigger {
550 t_types.push(TriggerType::Any);
551 }
552 t_types
553 }
554
555 pub fn is_trace_suppressed(&self) -> bool {
556 let is_trace_suppressed = self.trigger_source >> 12 & 0x1 == 1;
557 is_trace_suppressed
558 }
559}
560
561impl Packable for MasterTriggerEvent {
562 const PACKET_TYPE : PacketType = PacketType::MasterTrigger;
563}
564
565
566impl Serialization for MasterTriggerEvent {
567
568 const SIZE : usize = 0;
570 const TAIL : u16 = 0x5555;
571 const HEAD : u16 = 0xAAAA;
572
573 fn to_bytestream(&self) -> Vec::<u8> {
574 let mut bs = Vec::<u8>::with_capacity(MasterTriggerEvent::SIZE);
575 bs.extend_from_slice(&MasterTriggerEvent::HEAD.to_le_bytes());
576 bs.push(self.event_status as u8);
577 bs.extend_from_slice(&self.event_id.to_le_bytes());
578 bs.extend_from_slice(&self.timestamp.to_le_bytes());
579 bs.extend_from_slice(&self.tiu_timestamp.to_le_bytes());
580 bs.extend_from_slice(&self.tiu_gps32.to_le_bytes());
581 bs.extend_from_slice(&self.tiu_gps16.to_le_bytes());
582 bs.extend_from_slice(&self.crc.to_le_bytes());
583 bs.extend_from_slice(&self.trigger_source.to_le_bytes());
584 bs.extend_from_slice(&self.dsi_j_mask.to_le_bytes());
585 let n_channel_masks = self.channel_mask.len();
586 bs.push(n_channel_masks as u8);
587 for k in 0..n_channel_masks {
588 bs.extend_from_slice(&self.channel_mask[k].to_le_bytes());
589 }
590 bs.extend_from_slice(&self.mtb_link_mask.to_le_bytes());
591 bs.extend_from_slice(&MasterTriggerEvent::TAIL.to_le_bytes());
592 bs
593 }
594
595 fn from_bytestream(stream : &Vec<u8>,
596 pos : &mut usize)
597 -> Result<Self, SerializationError> {
598 let mut mt = Self::new();
599 let header = parse_u16(stream, pos);
600 if header != Self::HEAD {
601 return Err(SerializationError::HeadInvalid);
602 }
603 mt.event_status = parse_u8 (stream, pos).into();
604 mt.event_id = parse_u32(stream, pos);
605 mt.timestamp = parse_u32(stream, pos);
606 mt.tiu_timestamp = parse_u32(stream, pos);
607 mt.tiu_gps32 = parse_u32(stream, pos);
608 mt.tiu_gps16 = parse_u16(stream, pos);
609 mt.crc = parse_u32(stream, pos);
610 mt.trigger_source = parse_u16(stream, pos);
611 mt.dsi_j_mask = parse_u32(stream, pos);
612 let n_channel_masks = parse_u8(stream, pos);
613 for _ in 0..n_channel_masks {
614 mt.channel_mask.push(parse_u16(stream, pos));
615 }
616 mt.mtb_link_mask = parse_u64(stream, pos);
617 let tail = parse_u16(stream, pos);
618 if tail != Self::TAIL {
619 error!("Invalid tail signature {}!", tail);
620 mt.event_status = EventStatus::TailWrong;
621 match search_for_u16(Self::TAIL, stream, *pos) {
624 Ok(tail_pos) => {
625 error!("The tail was invalid, but we found a suitable end marker. The data format seems incompatible though, so the MasterTriggerEvents is probably rubbish!");
626 mt.event_status = EventStatus::IncompatibleData;
627 *pos = tail_pos + 2;
628 },
629 Err(err) => {
630 error!("Tail invalid, we assume the data format is incompatible, however, we could not do anything about it! {err}");
631 }
632 }
633 }
634 Ok(mt)
635 }
636}
637
638impl Default for MasterTriggerEvent {
639 fn default() -> Self {
640 Self::new()
641 }
642}
643
644impl fmt::Display for MasterTriggerEvent {
645 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
646 let mut repr = String::from("<MasterTriggerEvent");
647 repr += &(format!("\n EventStatus : {}", self.event_status));
648 repr += &(format!("\n EventID : {}", self.event_id));
649 repr += "\n ** trigger sources **";
650 for k in self.get_trigger_sources() {
651 repr += &(format!("\n {}", k));
652 }
653 if self.get_global_trigger_soures().len() > 0 {
654 repr += "\n -- global";
655 for k in self.get_global_trigger_soures() {
656 repr += &(format!("\n {}", k));
657 }
658 }
659 repr += "\n ** ** timestamps ** **";
660 repr += &(format!("\n timestamp : {}", self.timestamp));
661 repr += &(format!("\n tiu_timestamp : {}", self.tiu_timestamp));
662 repr += &(format!("\n gps_timestamp : {}", self.tiu_gps32));
663 repr += &(format!("\n absolute 48bit: {}", self.get_timestamp_abs48()));
664 repr += "\n -- -- --";
665 repr += &(format!("\n crc : {}", self.crc));
666 repr += &(format!("\n ** ** TRIGGER HITS (DSI/J/CH) [{} LTBS] ** **", self.dsi_j_mask.count_ones()));
667 for k in self.get_trigger_hits() {
668 repr += &(format!("\n => {}/{}/({},{}) ({}) ", k.0, k.1, k.2.0, k.2.1, k.3));
669 }
670 repr += "\n ** ** MTB LINK IDs ** **";
671 let mut mtblink_str = String::from("\n => ");
672 for k in self.get_rb_link_ids() {
673 mtblink_str += &(format!("{} ", k))
674 }
675 repr += &mtblink_str;
676 repr += &(format!("\n == Trigger hits {}, expected RBEvents {}",
677 self.get_trigger_hits().len(),
678 self.get_rb_link_ids().len()));
679 repr += ">";
680 write!(f,"{}", repr)
681 }
682}
683
684impl From<&TofEventSummary> for MasterTriggerEvent {
685 fn from(tes: &TofEventSummary) -> Self {
686 let mut mte = MasterTriggerEvent::new();
687 mte.event_status = tes.status;
688 mte.event_id = tes.event_id;
689 mte.trigger_source = tes.trigger_sources;
690 mte.tiu_gps32 = tes.timestamp32;
691 mte.tiu_gps16 = tes.timestamp16;
692 mte.dsi_j_mask = tes.dsi_j_mask;
693 mte.channel_mask = tes.channel_mask.clone();
694 mte.mtb_link_mask = tes.mtb_link_mask;
695 mte
696 }
697}
698
699#[cfg(feature="random")]
700impl FromRandom for MasterTriggerEvent {
701
702 fn from_random() -> Self {
703 let mut event = Self::new();
704 let mut rng = rand::thread_rng();
705 event.event_id = rng.gen::<u32>();
708 event.timestamp = rng.gen::<u32>();
709 event.tiu_timestamp = rng.gen::<u32>();
710 event.tiu_gps32 = rng.gen::<u32>();
711 event.tiu_gps16 = rng.gen::<u16>();
712 event.crc = rng.gen::<u32>();
713 event.trigger_source = rng.gen::<u16>();
714 event.dsi_j_mask = rng.gen::<u32>();
715 let n_channel_masks = rng.gen::<u8>();
716 for _ in 0..n_channel_masks {
717 event.channel_mask.push(rng.gen::<u16>());
718 }
719 event.mtb_link_mask = rng.gen::<u64>();
720 event
721 }
722}
723
724#[test]
725#[cfg(feature = "random")]
726fn test_trigger_type() {
727 for _ in 0..100 {
728 let ttype = TriggerType::from_random();
729 let ttype_u8 = ttype.to_u8();
730 let u8_ttype = TriggerType::from(ttype_u8);
731 assert_eq!(ttype, u8_ttype);
732 }
733}
734
735#[cfg(all(test,feature = "random"))]
736mod test_mastertriggerevent {
737 use crate::serialization::Serialization;
738 use crate::FromRandom;
739 use crate::events::MasterTriggerEvent;
740
741 #[test]
742 fn serialization_mastertriggerevent() {
743 let data = MasterTriggerEvent::from_random();
744 let test = MasterTriggerEvent::from_bytestream(&data.to_bytestream(), &mut 0).unwrap();
745 assert_eq!(data, test);
746 }
747}
748