All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TriggerDecoder_tool.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////
2 //
3 // File: TriggerDecoder_tool.cc
4 //
5 // Description: Starting point for extracting ICARUS trigger fragment information into LArSoft object TBD
6 //
7 // Author: Jacob Zettlemoyer, FNAL
8 //
9 ///////////////////////////////////////////////
10 
11 #include "art/Framework/Core/EDProducer.h"
12 #include "art/Framework/Principal/Event.h"
13 #include "art/Framework/Principal/Handle.h"
14 #include "art/Framework/Services/Registry/ServiceHandle.h"
15 #include "art/Persistency/Common/PtrMaker.h"
16 #include "art/Utilities/ToolMacros.h"
17 #include "cetlib/cpu_timer.h"
18 #include "fhiclcpp/ParameterSet.h"
19 #include "messagefacility/MessageLogger/MessageLogger.h"
20 
23 #include "lardataalg/Utilities/quantities/spacetime.h" // util::quantities::nanosecond
24 #include "lardataobj/RawData/ExternalTrigger.h" //JCZ: TBD, placeholder for now to represent the idea
25 #include "lardataobj/RawData/TriggerData.h" // raw::Trigger
26 #include "lardataobj/Simulation/BeamGateInfo.h" //JCZ:, another placeholder I am unsure if this and above will be combined at some point into a dedicated object
27 
28 #include "sbndaq-artdaq-core/Overlays/ICARUS/ICARUSTriggerUDPFragment.hh"
29 
35 #include "icarusalg/Utilities/BinaryDumpUtils.h" // hexdump() DEBUG
36 
37 #include <cstdlib>
38 #include <iostream>
39 #include <iomanip> // std::setw(), std::setfill()
40 #include <string_view>
41 #include <memory>
42 
43 
44 using namespace std::string_literals;
45 
46 namespace daq
47 {
48 
49  /**
50  * @brief Tool decoding the trigger information from DAQ.
51  *
52  * Produces:
53  * * `std::vector<raw::ExternalTrigger>` containing the absolute trigger time
54  * stamp from the White Rabbit system, and a trigger count;
55  * it always includes a single entry (zero _might_ be supported).
56  * * `std::vector<raw::Trigger>` containing:
57  * * `TriggerTime()`: the relative time of the trigger as reported in the
58  * White Rabbit timestamp, measured in the
59  * @ref DetectorClocksElectronicsTime "electronics time scale" (for
60  * ICARUS it will always be
61  * `detinfo::DetectorClocksData::TriggerTime()`).
62  * * `BeamGateTime()`: relative time of the announced arrival of the beam
63  * (currently not available) also in
64  * @ref DetectorClocksElectronicsTime "electronics time scale".
65  * * `TriggerCount()`: the trigger count from the beginning of the run.
66  * * `TriggerBits()`: includes the beam(s) with an open gate when the
67  * trigger happened (currently only one beam gate per trigger);
68  * definitions are in `sbn::beamType` namespace.
69  *
70  * It always includes a single entry (zero _might_ be supported).
71  * * `std::vector<sim::BeamGateInfo>` containing information on the "main"
72  * beam gate associated to each trigger (a way to say that if by any
73  * chance there are more than one bits set for the trigger, this gate
74  * will pick only one of them):
75  * * `Start()`: relative time of the announced arrival of the beam
76  * (currently not available), in
77  * @ref DetectorClocksSimulationTime "simulation time scale".
78  * * `Width()`: duration of the gate, in nanoseconds; currently set to a
79  * nominal value.
80  * * `BeamType()`: the type of the beam gate being described (BNB, NuMI).
81  * * `sbn::ExtraTriggerInfo`: the most complete collection of information,
82  * duplicating also some from the other data products. Some of the
83  * information is not available yet: if a count is not available, its
84  * value is set to `0` (which is an invalid value because their valid
85  * range starts with `1` since they include the current event), and if a
86  * timestamp is not available it is set to
87  * `sbn::ExtraTriggerInfo::NoTimestamp`; these two conditions can be
88  * checked with static methods
89  * `sbn::ExtraTriggerInfo::isValidTimestamp()` and
90  * `sbn::ExtraTriggerInfo::isValidCount()` respectively.
91  * Note that differently from the usual, this is a _single object_, not
92  * a collection; also, this data product has no instance name.
93  * The information already available includes:
94  * * `sourceType`: the type of beam or trigger source, a value from
95  * `sbn::triggerSource` (equivalent to `raw::Trigger::TriggerBits()`,
96  * but in the form of an enumerator rather than a bit mask).
97  * * `triggerTimestamp`: same as `raw::ExternalTrigger::GetTrigTime()`
98  * (nanoseconds from the Epoch, Coordinated Universal Time).
99  * * `beamGateTimestamp`: absolute time of the beam gate opening as
100  * reported by the trigger hardware, directly comparable with
101  * `triggerTimestamp` (same scale and unit).
102  * * `triggerID`: same as `raw::ExternalTrigger::GetTrigID()`. Should
103  * match the event number.
104  * * `gateID`: the count of gates since the beginning of the run, as
105  * reported by the trigger hardware.
106  *
107  * Besides the main data product (empty instance name) an additional
108  * `std::vector<raw::ExternalTrigger>` data product with instance name
109  * `"previous"` is also produced, which relays the same kind of information
110  * but for the _previous_ trigger. This information also comes from the
111  * trigger DAQ. If no previous trigger is available, this collection will be
112  * empty.
113  *
114  *
115  * Timestamps and corrections
116  * ---------------------------
117  *
118  * The reference trigger time is driven by the trigger fragment time, which
119  * is expected to have been derived from the actual trigger time from the
120  * White Rabbit system properly corrected to UTC by the board reader.
121  *
122  * All absolute timestamps are corrected to be on that same scale.
123  * The absolute timestamps related to the White Rabbit time are added an
124  * offset to achieve this correction; this offset is stored in the data
125  * product (`sbn::ExtraTriggerInfo::WRtimeToTriggerTime`).
126  *
127  */
128  class TriggerDecoder : public IDecoder
129  {
131  public:
132  explicit TriggerDecoder(fhicl::ParameterSet const &pset);
133 
134  virtual void produces(art::ProducesCollector&) override;
135  virtual void configure(const fhicl::ParameterSet&) override;
136  virtual void initializeDataProducts() override;
137  virtual void process_fragment(const artdaq::Fragment &fragment) override;
138  virtual void outputDataProducts(art::Event &event) override;
139 
140  private:
141  using TriggerCollection = std::vector<raw::ExternalTrigger>;
142  using TriggerPtr = std::unique_ptr<TriggerCollection>;
143  using RelativeTriggerCollection = std::vector<raw::Trigger>;
144  using BeamGateInfoCollection = std::vector<sim::BeamGateInfo>;
145  using BeamGateInfoPtr = std::unique_ptr<BeamGateInfoCollection>;
146  using ExtraInfoPtr = std::unique_ptr<sbn::ExtraTriggerInfo>;
149  std::unique_ptr<RelativeTriggerCollection> fRelTrigger;
152  bool fDiagnosticOutput; //< Produces large number of diagnostic messages, use with caution!
153  bool fDebug; //< Use this for debugging this tool
154  //Add in trigger data member information once it is selected, current LArSoft object likely not enough as is
155 
156  // uint64_t fLastTimeStamp = 0;
157 
158  long fLastEvent = 0;
159 
160  detinfo::DetectorTimings const fDetTimings; ///< Detector clocks and timings.
161 
162  /// Creates a `ICARUSTriggerInfo` from a generic fragment.
163  icarus::ICARUSTriggerUDPFragment makeTriggerFragment
164  (artdaq::Fragment const& fragment) const;
165 
166  /// Parses the trigger data packet with the "standard" parser.
167  icarus::ICARUSTriggerInfo parseTriggerString(std::string_view data) const;
168 
169  /// Name of the data product instance for the current trigger.
170  static std::string const CurrentTriggerInstanceName;
171 
172  /// Name of the data product instance for the previous trigger.
173  static std::string const PreviousTriggerInstanceName;
174 
175  static constexpr double UnknownBeamTime = std::numeric_limits<double>::max();
176 
177  /// Codes of gate types from the trigger hardware.
179  static constexpr int BNB { 1 };
180  static constexpr int NuMI { 2 };
181  static constexpr int OffbeamBNB { 3 };
182  static constexpr int OffbeamNuMI { 4 };
183  };
184 
185  static constexpr nanoseconds BNBgateDuration { 1600. };
186  static constexpr nanoseconds NuMIgateDuration { 9500. };
187 
188  static std::string_view firstLine
189  (std::string const& s, std::string const& endl = "\0\n\r"s);
190 
191  /// Combines second and nanosecond counts into a 64-bit timestamp.
192  static std::uint64_t makeTimestamp(unsigned int s, unsigned int ns)
193  { return s * 1000000000ULL + ns; }
194  /// Returns the difference `a - b`.
195  static long long int timestampDiff(std::uint64_t a, std::uint64_t b)
196  { return static_cast<long long int>(a) - static_cast<long long int>(b); }
197 
198  };
199 
200 
201  std::string const TriggerDecoder::CurrentTriggerInstanceName {};
202  std::string const TriggerDecoder::PreviousTriggerInstanceName { "previous" };
203 
204 
205  TriggerDecoder::TriggerDecoder(fhicl::ParameterSet const &pset)
206  : fDetTimings
207  { art::ServiceHandle<detinfo::DetectorClocksService>()->DataForJob() }
208  {
209  this->configure(pset);
210  }
211 
212 
213  void TriggerDecoder::produces(art::ProducesCollector& collector)
214  {
215  collector.produces<TriggerCollection>(CurrentTriggerInstanceName);
216  collector.produces<TriggerCollection>(PreviousTriggerInstanceName);
220  }
221 
222 
223  void TriggerDecoder::configure(fhicl::ParameterSet const &pset)
224  {
225  fDiagnosticOutput = pset.get<bool>("DiagnosticOutput", false);
226  fDebug = pset.get<bool>("Debug", false);
227  return;
228  }
229 
231  {
232  //use until different object chosen
233  //fTrigger = new raw::Trigger();
234  fTrigger = std::make_unique<TriggerCollection>();
235  fPrevTrigger = std::make_unique<TriggerCollection>();
236  fRelTrigger = std::make_unique<RelativeTriggerCollection>();
238  fTriggerExtra = std::make_unique<sbn::ExtraTriggerInfo>();
239  return;
240  }
241 
242 
243  icarus::ICARUSTriggerUDPFragment TriggerDecoder::makeTriggerFragment
244  (artdaq::Fragment const& fragment) const
245  {
246  try {
247  return icarus::ICARUSTriggerUDPFragment { fragment };
248  }
249  catch(std::exception const& e) {
250  mf::LogSystem("TriggerDecoder")
251  << "Error while creating trigger fragment from:\n"
252  << sbndaq::dumpFragment(fragment)
253  << "\nError message: " << e.what();
254  throw;
255  }
256  catch(...) {
257  mf::LogSystem("TriggerDecoder")
258  << "Unidentified exception while creating trigger fragment from:"
259  << sbndaq::dumpFragment(fragment);
260  throw;
261  }
262  } // TriggerDecoder::parseTriggerString()
263 
264 
265  icarus::ICARUSTriggerInfo TriggerDecoder::parseTriggerString
266  (std::string_view data) const
267  {
268  try {
269  return icarus::parse_ICARUSTriggerString(data.data());
270  }
271  catch(std::exception const& e) {
272  mf::LogSystem("TriggerDecoder")
273  << "Error while running standard parser on " << data.length()
274  << "-char long trigger string:\n==>|" << data
275  << "|<==\nError message: " << e.what();
276  throw;
277  }
278  catch(...) {
279  mf::LogSystem("TriggerDecoder")
280  << "Unidentified exception while running standard parser on "
281  << data.length() << "-char long trigger string:\n==>|" << data << "|.";
282  throw;
283  }
284  } // TriggerDecoder::parseTriggerString()
285 
286 
287 
288  void TriggerDecoder::process_fragment(const artdaq::Fragment &fragment)
289  {
290  // artdaq_ts is reworked by the trigger board reader to match the corrected
291  // trigger time; to avoid multiple (potentially inconsistent) corrections,
292  // the decoder trusts it and references all the times with respect to it.
293  uint64_t const artdaq_ts = fragment.timestamp();
294  icarus::ICARUSTriggerUDPFragment frag { makeTriggerFragment(fragment) };
295  std::string data = frag.GetDataString();
296  char *buffer = const_cast<char*>(data.c_str());
297 
298  icarus::ICARUSTriggerInfo datastream_info = parseTriggerString(buffer);
299  uint64_t const raw_wr_ts // this is raw, unadultered, uncorrected
300  = makeTimestamp(frag.getWRSeconds(), frag.getWRNanoSeconds());
301 
302  // correction (explicitly converted to signed)
303  int64_t const WRtimeToTriggerTime
304  = static_cast<int64_t>(artdaq_ts) - raw_wr_ts;
305  auto const correctWRtime = [WRtimeToTriggerTime](uint64_t time)
306  { return time + WRtimeToTriggerTime; };
307  assert(correctWRtime(raw_wr_ts) == artdaq_ts);
308 
309  // --- END ---- TEMPORARY --------------------------------------------------
310  int gate_type = datastream_info.gate_type;
311  long delta_gates_bnb [[maybe_unused]] = frag.getDeltaGatesBNB();
312  long delta_gates_numi [[maybe_unused]] = frag.getDeltaGatesOther(); //this is actually NuMI due to abrupt changes in trigger board logic
313  long delta_gates_other [[maybe_unused]] = frag.getDeltaGatesNuMI();
314  uint64_t lastTrigger = 0;
315 
316  // --- BEGIN -- TEMPORARY --------------------------------------------------
317  // remove this part when the beam gate timestamp is available via fragment
318  // or via the parser
319  auto const parsedData = icarus::details::KeyedCSVparser{}(firstLine(data));
320  unsigned int beamgate_count { std::numeric_limits<unsigned int>::max() };
321  std::uint64_t beamgate_ts { artdaq_ts }; // we cheat
322  /* [20210717, petrillo@slac.stanford.edu] `(pBeamGateInfo->nValues() == 3)`:
323  * this is an attempt to "support" a few Run0 runs (6017 to roughly 6043)
324  * which have the beam gate information truncated; this workaround should
325  * be removed when there is enough ICARUS data that these runs become
326  * uninteresting.
327  */
328  if (auto pBeamGateInfo = parsedData.findItem("Beam_TS");
329  pBeamGateInfo && (pBeamGateInfo->nValues() == 3)
330  ) {
331  // if gate information is found, it must be complete
332  beamgate_count = pBeamGateInfo->getNumber<unsigned int>(0U);
333 
334  uint64_t const raw_bg_ts = makeTimestamp( // raw and uncorrected too
335  pBeamGateInfo->getNumber<unsigned int>(1U),
336  pBeamGateInfo->getNumber<unsigned int>(2U)
337  );
338 
339  // assuming the raw times from the fragment are on the same time scale
340  // (same offset corrections)
341  beamgate_ts += raw_bg_ts - raw_wr_ts;
342 
343  } // if has gate information
344  // --- END ---- TEMPORARY --------------------------------------------------
345 
347  {
348  std::cout << "Full Timestamp = " << artdaq_ts
349  << "\nBeam gate " << beamgate_count << " at "
350  << (beamgate_ts/1'000'000'000) << "." << std::setfill('0')
351  << std::setw(9) << (beamgate_ts%1'000'000'000) << std::setfill(' ')
352  << " s (" << timestampDiff(beamgate_ts, artdaq_ts)
353  << " ns relative to trigger)" << std::endl;
354 
355  // note that this parsing is independent from the one used above
356  std::string_view const dataLine = firstLine(data);
357  try {
358  auto const parsedData = icarus::details::KeyedCSVparser{}(dataLine);
359  std::cout << "Parsed data (from " << dataLine.size() << " characters): "
360  << parsedData << std::endl;
361  }
363  mf::LogError("TriggerDecoder")
364  << "Error parsing " << dataLine.length()
365  << "-char long trigger string:\n==>|" << dataLine
366  << "|<==\nError message: " << e.what() << std::endl;
367  throw;
368  }
369 
370  if (fDebug) { // this grows tiresome quickly when processing many events
371  std::cout << "Trigger packet content:\n" << dataLine
372  << "\nFull trigger fragment dump:"
373  << sbndaq::dumpFragment(fragment) << std::endl;
374  }
375  }
376 
377  //
378  // extra trigger info
379  //
380  sbn::triggerSource beamGateBit;
381  switch (gate_type) {
382  case TriggerGateTypes::BNB: beamGateBit = sbn::triggerSource::BNB; break;
383  case TriggerGateTypes::NuMI: beamGateBit = sbn::triggerSource::NuMI; break;
384  case TriggerGateTypes::OffbeamBNB: beamGateBit = sbn::triggerSource::OffbeamBNB; break;
385  case TriggerGateTypes::OffbeamNuMI: beamGateBit = sbn::triggerSource::OffbeamNuMI; break;
386  default: beamGateBit = sbn::triggerSource::Unknown;
387  } // switch gate_type
388 
389  fTriggerExtra->sourceType = beamGateBit;
390  fTriggerExtra->triggerTimestamp = artdaq_ts;
391  fTriggerExtra->beamGateTimestamp = beamgate_ts;
392  fTriggerExtra->triggerID = datastream_info.wr_event_no;
393  fTriggerExtra->gateID = datastream_info.gate_id;
394  /* TODO (may need to add WRtimeToTriggerTime to some timestamps):
395  fTriggerExtra->triggerCount
396  fTriggerExtra->gateCount
397  fTriggerExtra->gateCountFromPreviousTrigger
398  fTriggerExtra->anyTriggerCountFromPreviousTrigger
399  fTriggerExtra->anyGateCountFromPreviousTrigger
400  fTriggerExtra->anyPreviousTriggerSourceType
401  fTriggerExtra->anyGateCountFromAnyPreviousTrigger
402  fTriggerExtra->previousTriggerTimestamp
403  fTriggerExtra->anyPreviousTriggerTimestamp
404  */
405  fTriggerExtra->WRtimeToTriggerTime = WRtimeToTriggerTime;
406 
407  //
408  // absolute time trigger (raw::ExternalTrigger)
409  //
410  fTrigger->emplace_back
411  (fTriggerExtra->triggerID, fTriggerExtra->triggerTimestamp);
412 
413  //
414  // previous absolute time trigger (raw::ExternalTrigger)
415  //
416  if(fTriggerExtra->triggerID == 1)
417  {
418  fLastEvent = 0;
419  }
420  else
421  {
422  fLastEvent = fTriggerExtra->triggerID - 1;
423  lastTrigger = frag.getLastTimestampBNB();
424  fPrevTrigger->emplace_back(fLastEvent, lastTrigger);
425  }
426 
427  //
428  // beam gate
429  //
430  // beam gate - trigger: hope it's negative...
431  nanoseconds const gateStartFromTrigger{
432  static_cast<double>(timestampDiff
433  (fTriggerExtra->beamGateTimestamp, fTriggerExtra->triggerTimestamp)
434  )
435  }; // narrowing!!
436  auto const elecGateStart = fDetTimings.TriggerTime() + gateStartFromTrigger;
437  auto const simGateStart = fDetTimings.toSimulationTime(elecGateStart);
438  switch (gate_type) {
440  fBeamGateInfo->emplace_back
441  (simGateStart.value(), BNBgateDuration.value(), sim::kBNB);
442  break;
444  fBeamGateInfo->emplace_back
445  (simGateStart.value(), NuMIgateDuration.value(), sim::kNuMI);
446  break;
448  fBeamGateInfo->emplace_back
449  (simGateStart.value(), BNBgateDuration.value(), sim::kBNB);
450  break;
452  fBeamGateInfo->emplace_back
453  (simGateStart.value(), NuMIgateDuration.value(), sim::kNuMI);
454  break;
455  default:
456  mf::LogWarning("TriggerDecoder") << "Unsupported gate type #" << gate_type;
457  } // switch gate_type
458 
459  //
460  // relative time trigger (raw::Trigger)
461  //
462  fRelTrigger->emplace_back(
463  static_cast<unsigned int>(datastream_info.wr_event_no), // counter
464  fDetTimings.TriggerTime().value(), // trigger_time
465  elecGateStart.value(), // beamgate_time
466  mask(beamGateBit) // bits
467  );
468 
469  //Once we have full trigger data object, set up and place information into there
470  return;
471  }
472 
473  void TriggerDecoder::outputDataProducts(art::Event &event)
474  {
475  //Place trigger data object into raw data store
476  event.put(std::move(fTrigger), CurrentTriggerInstanceName);
477  event.put(std::move(fRelTrigger), CurrentTriggerInstanceName);
478  event.put(std::move(fPrevTrigger), PreviousTriggerInstanceName);
479  event.put(std::move(fBeamGateInfo), CurrentTriggerInstanceName);
480  event.put(std::move(fTriggerExtra));
481  return;
482  }
483 
484  std::string_view TriggerDecoder::firstLine
485  (std::string const& s, std::string const& endl /* = "\0\n\r" */)
486  {
487  return { s.data(), std::min(s.find_first_of(endl), s.size()) };
488  }
489 
490 
491  DEFINE_ART_CLASS_TOOL(TriggerDecoder)
492 
493 }
494 
495 
496 
497 
498 
499 
Definitions of the trigger bits for SBN.
virtual void produces(art::ProducesCollector &) override
The space point building should output the hit collection for those hits which combine to form space ...
Parser to fill a KeyValuesData structure out of a character buffer.
detinfo::DetectorTimings const fDetTimings
Detector clocks and timings.
bool fDiagnosticOutput
Produces large number of diagnostic messages, use with caution!
IDecoder interface class definiton.
Definition: IDecoder.h:34
virtual void initializeDataProducts() override
Initialize any data products the tool will output.
This provides an art tool interface definition for tools which &quot;decode&quot; artdaq fragments into LArSoft...
std::unique_ptr< TriggerCollection > TriggerPtr
static constexpr nanoseconds NuMIgateDuration
std::unique_ptr< sbn::ExtraTriggerInfo > ExtraInfoPtr
virtual void process_fragment(const artdaq::Fragment &fragment) override
Given a set of recob hits, run DBscan to form 3D clusters.
constexpr value_t value() const
Returns the value of the quantity.
Definition: quantities.h:609
static std::string const CurrentTriggerInstanceName
Name of the data product instance for the current trigger.
BeamGateInfoPtr fBeamGateInfo
std::vector< sim::BeamGateInfo > BeamGateInfoCollection
Simple parser for comma-separated text.
std::unique_ptr< BeamGateInfoCollection > BeamGateInfoPtr
static std::string const PreviousTriggerInstanceName
Name of the data product instance for the previous trigger.
NuMI.
Definition: BeamTypes.h:12
Interface to detinfo::DetectorClocks.
process_name gaushit a
virtual void configure(const fhicl::ParameterSet &) override
Interface for configuring the particular algorithm tool.
icarus::ICARUSTriggerUDPFragment makeTriggerFragment(artdaq::Fragment const &fragment) const
Creates a ICARUSTriggerInfo from a generic fragment.
constexpr mask_t< EnumType > mask(EnumType bit, OtherBits...otherBits)
Returns a mask with all specified bits set.
Additional information on trigger.
bool fDebug
Use this for debugging this tool.
A value measured in the specified unit.
Definition: quantities.h:566
simulation_time toSimulationTime(FromTime time) const
Converts a time point into simulation time scale.
virtual void outputDataProducts(art::Event &event) override
Output the data products to the event store.
Utility to dump a artDAQ fragment on screen.
electronics_time TriggerTime() const
std::unique_ptr< RelativeTriggerCollection > fRelTrigger
static long long int timestampDiff(std::uint64_t a, std::uint64_t b)
Returns the difference a - b.
static std::uint64_t makeTimestamp(unsigned int s, unsigned int ns)
Combines second and nanosecond counts into a 64-bit timestamp.
BNB.
Definition: BeamTypes.h:11
details::DumpFragWrap dumpFragment(artdaq::Fragment const &frag)
Dump a artDAQ fragment into an output stream.
then echo File list $list not found else cat $list while read file do echo $file sed s
Definition: file_to_url.sh:60
Dimensioned variables representing space or time quantities.
A class exposing an upgraded interface of detinfo::DetectorClocksData.
std::vector< raw::Trigger > RelativeTriggerCollection
nanosecond_as<> nanosecond
Type of time stored in nanoseconds, in double precision.
Definition: spacetime.h:136
std::vector< raw::ExternalTrigger > TriggerCollection
Tool decoding the trigger information from DAQ.
Codes of gate types from the trigger hardware.
do i e
triggerSource
Type of beam or beam gate or other trigger source.
Definition: BeamBits.h:97
Data product holding additional trigger information.
static constexpr nanoseconds BNBgateDuration
static std::string_view firstLine(std::string const &s, std::string const &endl="\0\n\r"s)
BEGIN_PROLOG could also be cout
Functions to dump the content of binary data chunks to console.
icarus::ICARUSTriggerInfo parseTriggerString(std::string_view data) const
Parses the trigger data packet with the &quot;standard&quot; parser.