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