All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PMTDecoder_tool.cc
Go to the documentation of this file.
1 /**
2  * @file icaruscode/Decode/DecoderTools/PMTDecoder_tool.cc
3  *
4  * @brief This tool provides "standard" 3D hits built (by this tool) from 2D hits
5  *
6  * @author Andrea Scarpelli (ascarpell@bnl.gov)
7  *
8  */
9 
10 // Framework Includes
11 #include "art_root_io/TFileService.h"
12 #include "art/Framework/Principal/Run.h"
13 #include "art/Framework/Principal/Event.h"
14 #include "art/Framework/Principal/Handle.h"
15 #include "art/Framework/Services/Registry/ServiceHandle.h"
16 #include "art/Framework/Core/ProducesCollector.h"
17 #include "art/Framework/Core/ConsumesCollector.h"
18 #include "art/Utilities/ToolConfigTable.h"
19 #include "art/Utilities/ToolMacros.h"
20 #include "canvas/Persistency/Provenance/EventID.h"
21 #include "canvas/Persistency/Provenance/Timestamp.h"
22 // #include "cetlib/cpu_timer.h"
23 #include "cetlib_except/exception.h"
24 #include "fhiclcpp/types/TableAs.h"
25 #include "fhiclcpp/types/Atom.h"
26 #include "fhiclcpp/types/OptionalAtom.h"
27 #include "fhiclcpp/types/Sequence.h"
28 #include "messagefacility/MessageLogger/MessageLogger.h"
29 
30 // LArSoft includes
33 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
36 #include "lardataalg/Utilities/quantities/spacetime.h" // nanoseconds
37 #include "lardataalg/Utilities/intervals_fhicl.h" // for nanoseconds in FHiCL
43 
44 #include "sbndaq-artdaq-core/Overlays/Common/CAENV1730Fragment.hh"
45 
49 #include "icarusalg/Utilities/FHiCLutils.h" // util::fhicl::getOptionalValue()
50 #include "icarusalg/Utilities/BinaryDumpUtils.h" // icarus::ns::util::bin()
51 #include "sbnobj/Common/PMT/Data/PMTconfiguration.h" // sbn::PMTconfiguration
52 
53 // ROOT libraries
54 #include "TTree.h"
55 
56 // std includes
57 #include <ostream>
58 #include <algorithm> // std::lower_bound()
59 #include <string>
60 #include <vector>
61 #include <tuple>
62 #include <optional>
63 #include <memory>
64 #include <cmath> // std::floor()
65 #include <cassert>
66 
67 
68 //------------------------------------------------------------------------------------------------------------------------------------------
69 using namespace util::quantities::time_literals;
70 
71 //------------------------------------------------------------------------------------------------------------------------------------------
72 // implementation follows
73 
74 namespace daq { class PMTDecoder; }
75 /**
76  * @brief Turns PMT readout fragments from DAQ into LArSoft data products.
77  *
78  * The tool can read fragments from CAEN V1730 readout boards delivered by
79  * artDAQ.
80  *
81  * This decoder must support both a off-line mode (for storage and downstream
82  * processing) and a on-line mode (for monitoring).
83  * In particular, the on-line workflow is such that it may not be possible to
84  * access the FHiCL configuration of the job and therefore the PMT configuration
85  * data (see `icarus::PMTconfigurationExtraction` module).
86  *
87  *
88  * Configuration
89  * --------------
90  *
91  * The set of supported parameters can be seen on command line by running
92  * `lar --print-description PMTDecoder`.
93  *
94  * Description of the configuration parameters:
95  * * `DiagnosticOutput` (flag, default: `false`): enables additional console
96  * output, including dumping of the fragments (that is huge output).
97  * * `PMTconfigTag` (data product tag, optional): if specified, the pre-trigger
98  * buffer duration is read from there; although optional, it is strongly
99  * recommended that this information be provided, since it is essential for
100  * the correct timing of the PMT waveforms (see
101  * @ref icarus_PMTDecoder_timestamps "the discussion on time stamps below").
102  * * `BoardSetup` (list of board setup information): each entry specifies some
103  * information about a specific readout board; the boards are identified by
104  * their name; if a board is found in input that has no setup information,
105  * some time corrections are not applied (see
106  * @ref icarus_PMTDecoder_timestamps "the discussion on time stamps below").
107  * Each entry is in the form of a table:
108  * * `Name` (string, mandatory): the name of the board
109  * (e.g. `"icaruspmtwwtop01"`); this is use to match the setup
110  * information to a fragment ID in the PMT configuration.
111  * * `FragmentID` (integral, optional): if specified, allows the corrections
112  * using setup information to be applied even when no PMT configuration is
113  * provided (if neither PMT configuration nor setup information including
114  * `FragmentID` is available, no time correction is applied).
115  * * `TriggerDelay` (nanoseconds, default: 0 ns): measured delay from the
116  * primitive trigger time to the execution of the PMT trigger; specify
117  * the unit! (e.g. `"43 ns"`).
118  * * `RequireKnownBoards` (flag, default: `true`): if set, the readout boards
119  * in input must each have a setup configuration (`BoardSetup`) *and* must
120  * be present in the PMT DAQ configuration, or an exception is thrown;
121  * if not set, readout boards in input will be processed even when their
122  * setup or DAQ configuration is not known, at the cost of fewer corrections
123  * on the timestamps (which should then be considered unreliable).
124  * * `RequireBoardConfig` (flag, default: `true`): if set, the readout boards
125  * which have a setup (`BoardSetup`) are required to be included in the DAQ
126  * configuration of the input file, or an exception is thrown; if not set,
127  * missing readout boards are unnoticed.
128  * * `TriggerTag` (data product tag, mandatory): tag for the information
129  * (currently required to be a collection of `raw::ExternalTrigger`,
130  * in the future it should become `raw::Trigger`);
131  * * `DataTrees` (list of strings, default: none): list of data trees to be
132  * produced; if none (default), then `TFileService` is not required.
133  * * `LogCategory` (string, default: `PMTDecoder`): name of the message facility
134  * category where the output is sent.
135  *
136  *
137  * Requirements
138  * -------------
139  *
140  * Services required include:
141  *
142  * * `IICARUSChannelMap` for the association of fragments to LArSoft channel ID;
143  * * `DetectorClocksService` for the correct decoding of the time stamps
144  * (always required, even when dumbed-down timestamp decoding is requested);
145  * * `TFileService` only if the production of trees or plots is requested.
146  *
147  *
148  * Waveform time stamp
149  * --------------------
150  *
151  * @anchor icarus_PMTDecoder_timestamps
152  *
153  * All waveforms on the same readout board share the same timestamp.
154  *
155  * The time stamp of the waveform is defined as the time when the first sample
156  * of the waveform started (that is, if the sample represent the value of the
157  * signal in an interval of 2 ns, the time stamp is pointing at the beginning
158  * of those 2 ns). Whether we can honour that definition, though, is a different
159  * matter.
160  * The representation of the time stamp is in the
161  * @ref DetectorClocksElectronicsTime "electronics time scale".
162  *
163  * There are two "types" of waveforms: the ones acquired at global trigger time,
164  * and the ones acquired because of a "trigger primitive" which did not upgrade
165  * to global (likely because not in coincidence with the beam gate).
166  * In both cases, it is the same type of signal, a trigger primitive from
167  * the NI7820 FPGA, which initializes the acquisition of the waveform.
168  * Every delay between when that signal is emitted and when the PMT trigger is
169  * executed shifts the time stamp of the waveform backward.
170  *
171  * We assign the the time stamp of the waveforms matching the global trigger
172  * as follow:
173  * * the base time is the global trigger time; this effectively defines the
174  * electronics time scale, so its representation is a fixed number that is
175  * configured in LArSoft and can be accessed with
176  * `DetectorClocksData::TriggerTime()`;
177  * * the delay of the propagation from the trigger board to the readout board
178  * is subtracted to the timestamp; this value must be independently measured
179  * and provided to this decoder via tool configuration as setup information
180  * (`TriggerDelay`); if not present in the setup, this delay is not considred;
181  * * upon receiving the trigger, the readout board will keep some of the samples
182  * already digitized, in what we call pre-trigger buffer; the size of this
183  * buffer is a fixed number of samples which is specified in DAQ as a fraction
184  * of the complete buffer that is _post-trigger_; this amount, converted in
185  * time, is subtracted to the trigger time to point back to the beginning of
186  * the waveform instead that to the trigger primitive time. The necessary
187  * information is read from the PMT configuration (`PMTconfigTag`); if no
188  * configuration is available, this offset is not subtracted; note that this
189  * is a major shift (typically, a few microseconds) that should be always
190  * included.
191  *
192  * We do not assign the the time stamp of the waveforms not matching the global
193  * trigger because we have no clue how to do that. Ok, that is a to-do!
194  *
195  * Each V1730 event record includes a trigger time tag (TTT), which is the value
196  * of an internal counter of the board at the time the board received a trigger.
197  * This can be used to relate the various waveforms (and the various fragments)
198  * in the _art_ event.
199  *
200  *
201  * Data trees
202  * -----------
203  *
204  * The tool supports the following ROOT trees production on demand:
205  *
206  * * `PMTfragments`: data pertaining a single fragment; each entry is about a
207  * single fragment, and it includes full event ID, event time stamp (from
208  * _art_, i.e. the one assigned to the event by artDAQ), the ID of the
209  * fragment the entry is describing, and then the rest of the data, including:
210  * * `TTT` (64-bit integer): (Extended) Trigger Time Tag value, in readout
211  * board ticks (each worth 8 ns) from the last board reset;
212  * currently the value includes the 31-bit counter and in addition the
213  * overflow bit as MSB; the overflow bit is set by the readout board
214  * the first time the counter passes its limit (2^31) and wraps, and never
215  * cleared until the next board reset.
216  * While the tree has room for the 48-bit version (ETTT), the rest of the
217  * decoder does not yet support it.
218  * * `trigger` (64-bit signed integer): global trigger time (from the
219  * trigger decoder), in nanoseconds from The Epoch.
220  * * `triggerSec`, `triggerNS` (32-bit integer each): same time as `trigger`
221  * branch, split into second and nanosecond components.
222  * * `fragTime` (64-bit signed integer), `fragTimeSec` (32-bit signed
223  * integer): the timestamp of the PMT fragment, assigned by the board
224  * reader constructing the fragment.
225  *
226  *
227  *
228  * Technical notes
229  * ----------------
230  *
231  * In order to correctly reconstruct the time stamp, this tool needs several
232  * pieces of information.
233  * These include the size of the pre-trigger buffer, which is set by the readout
234  * board configuration, and the delay between the global trigger and the time
235  * that trigger is received and acted upon in the readout board, which needs to
236  * be measured.
237  * The first category of information, from readout board configuration, are read
238  * from the input file (`sbn::PMTconfiguration`), while the second category
239  * needs to be specified in the tool FHiCL configuration.
240  *
241  * PMT configuration is optional, in the sense that it can be omitted; in that
242  * case, some standard values will be used for it.
243  * For a board to be served, an entry of that board must be present in the
244  * tool configuration (`BoardSetup`). It is an error for a fragment in input not
245  * to have an entry for the corresponding board setup.
246  *
247  * The tool code extract the needed information and matches it into a
248  * sort-of-database keyed by fragment ID, so that it can be quickly applied
249  * when decoding a fragment. The matching is performed by board name.
250  *
251  *
252  * Glossary
253  * ---------
254  *
255  * * **setup**, **[PMT] configuration**: this is jargon specific to this tool.
256  * Information about a readout board can come from two sources: the "setup"
257  * is information included in the `BoardSetup` configuration list of this
258  * tool; the "PMT configuration" is information included in the DAQ
259  * configuration that is delivered via `PMTconfigTag`.
260  * * **TTT**: trigger time tag, from the V1730 event record (31 bits); may be:
261  * * **ETTT**: extended trigger time tag, from the V1730 event record (48 bits).
262  * * **trigger delay**: time point when a V1730 board processes a (PMT) trigger
263  * signal (and increments the TTT register) with respect to the time of the
264  * time stamp of the (SPEXi) global trigger that acquired the event.
265  *
266  */
268 {
269 
270  /// Enumerate the supported data trees.
271  enum class DataTrees: std::size_t {
272  Fragments, ///< Information about fragments
273  N ///< Counter.
274  };
275  using TreeNameList_t
276  = std::array<std::string, static_cast<std::size_t>(DataTrees::N)>;
277  static TreeNameList_t const TreeNames;
278 
279  /// Returns a string with all supported tree names.
280  static std::string listTreeNames(std::string const& sep = "\n");
281 
282 public:
283 
285 
286  /// Data structure for trigger time.
288  static_assert(sizeof(int) >= 4U);
289  struct Split_t {
290  int seconds = std::numeric_limits<int>::min(); ///< The ongoing second.
291  /// Nanoseconds from the start of current second.
292  unsigned int nanoseconds = std::numeric_limits<unsigned int>::max();
293  }; // Split_t
294 
295  /// Trigger time in nanoseconds from The Epoch.
296  long long int time = std::numeric_limits<long long int>::min();
297  /// Trigger time in nanoseconds from The Epoch (in components).
299 
300  constexpr SplitTimestamp_t() = default;
301  constexpr SplitTimestamp_t(int sec, unsigned int ns);
302  constexpr SplitTimestamp_t(long long int triggerTime);
303  }; // SplitTimestamp_t
304 
305 
306  // --- BEGIN -- FHiCL configuration ----------------------------------------
307 
308  /// Configuration of the V1730 readout board setup.
310 
311  fhicl::Atom<std::string> Name {
312  fhicl::Name("Name"),
313  fhicl::Comment("board name, as specified in the DAQ configuration")
314  };
315 
316  fhicl::OptionalAtom<unsigned int> FragmentID {
317  fhicl::Name("FragmentID"),
318  fhicl::Comment("ID of the fragments associated with the board")
319  };
320 
321  fhicl::Atom<nanoseconds> TriggerDelay {
322  fhicl::Name("TriggerDelay"),
323  fhicl::Comment
324  ("from delay from the trigger timestamp to the PMT trigger [ns]"),
325  0_ns
326  };
327 
328  }; // struct BoardSetupConfig
329 
330 
331  /// Main tool configuration.
332  struct Config {
333 
334  using Name = fhicl::Name;
335  using Comment = fhicl::Comment;
336 
337  fhicl::Atom<bool> DiagnosticOutput {
338  Name("DiagnosticOutput"),
339  Comment("enable additional console output"),
340  false // default
341  };
342 
343  fhicl::Atom<bool> RequireKnownBoards {
344  Name("RequireKnownBoards"),
345  Comment("all readout boards in input must be known (setup+PMT configuration)"),
346  true
347  };
348 
349  fhicl::Atom<bool> RequireBoardConfig {
350  Name("RequireBoardConfig"),
351  Comment("all readout boards in setup must have a matching PMT configuration"),
352  true
353  };
354 
355  fhicl::OptionalAtom<art::InputTag> PMTconfigTag {
356  Name("PMTconfigTag"),
357  Comment("input tag for the PMT readout board configuration information")
358  };
359 
360  fhicl::Sequence<fhicl::TableAs<details::BoardSetup_t, BoardSetupConfig>> BoardSetup {
361  Name("BoardSetup"),
362  Comment("list of the setup settings for all relevant V1730 boards")
363  };
364 
365  fhicl::Atom<art::InputTag> TriggerTag {
366  Name("TriggerTag"),
367  Comment("input tag for the global trigger object (raw::ExternalTrigger)")
368  };
369 
370  fhicl::Sequence<std::string> DataTrees {
371  fhicl::Name("DataTrees"),
372  fhicl::Comment("produces the specified ROOT trees (" + listTreeNames(",") + ")"),
373  std::vector<std::string>{} // default
374  };
375 
376  fhicl::Atom<std::string> LogCategory {
377  Name("LogCategory"),
378  Comment("name of the category for message stream"),
379  "PMTDecoder" // default
380  };
381 
382  }; // Config
383 
384  using Parameters = art::ToolConfigTable<Config>;
385 
386  // --- END ---- FHiCL configuration ----------------------------------------
387 
388 
389  /**
390  * @brief Constructor
391  *
392  * @param params configuration parameter set
393  */
394  explicit PMTDecoder(Parameters const& params);
395 
396  /// I hereby declare I will consume trigger and PMT configuration products.
397  virtual void consumes(art::ConsumesCollector& consumerColl) override;
398 
399  /**
400  * @brief Each algorithm may have different objects it wants "produced" so use this to
401  * let the top level producer module "know" what it is outputting
402  */
403  virtual void produces(art::ProducesCollector&) override;
404 
405  /// Reconfiguration is not supported: all configuration at construction time.
406  virtual void configure(const fhicl::ParameterSet&) override;
407 
408  /// Reads the PMT configuration from the run.
409  virtual void setupRun(art::Run const& run) override;
410 
411  /// Will read trigger information one day if needed.
412  virtual void setupEvent(art::Event const& event) override;
413 
414  /**
415  * @brief Initialize any data products the tool will output
416  *
417  */
418  virtual void initializeDataProducts() override;
419 
420 
421  /**
422  * @brief Given a set of recob hits, run DBscan to form 3D clusters
423  *
424  * @param fragment The artdaq fragment to process
425  * @param rawDigitColllection The output RawDigits
426  */
427  virtual void process_fragment(const artdaq::Fragment &fragment) override;
428 
429  /**
430  * @brief Output the data products to the event store
431  *
432  * @param event The event store objects
433  */
434  virtual void outputDataProducts(art::Event& event) override;
435 
436 private:
437 
438  /// Information used in decoding from a board.
440  std::string const name;
443  };
444 
445  // --- BEGIN -- Configuration parameters -----------------------------------
446  bool const fDiagnosticOutput; ///< If true will spew endless messages to output.
447 
448  bool const fRequireKnownBoards; ///< Whether info on all input boards is required.
449  bool const fRequireBoardConfig; ///< Whether setup info on all boards is required.
450 
451  std::optional<art::InputTag> const fPMTconfigTag; ///< Input tag of the PMT configuration.
452 
453  art::InputTag const fTriggerTag; ///< Input tag of the global trigger.
454 
455  std::vector<details::BoardSetup_t> const fBoardSetup; ///< All board setup settings.
456 
457  std::string const fLogCategory; ///< Message facility category.
458 
459  // --- END ---- Configuration parameters -----------------------------------
460 
461 
462  // --- BEGIN -- Services ---------------------------------------------------
463 
464  geo::GeometryCore const& fGeometry; ///< Geometry service provider.
465 
466  /// Interface to LArSoft configuration for detector timing.
468 
469  icarusDB::IICARUSChannelMap const& fChannelMap; ///< Fragment/channel mapping database.
470 
471  // --- END ---- Services ---------------------------------------------------
472 
473 
474  // --- BEGIN -- Cached values ----------------------------------------------
475 
476  /// Duration of the optical detector readout sampling tick (i.e. 2 ns; shh!).
478 
479  /// Trigger time as reported by `DetectorClocks` service.
481 
482  // --- END ---- Cached values ----------------------------------------------
483 
484 
485  using OpDetWaveformCollection = std::vector<raw::OpDetWaveform>;
486  using OpDetWaveformCollectionPtr = std::unique_ptr<OpDetWaveformCollection>;
487 
488  OpDetWaveformCollectionPtr fOpDetWaveformCollection; ///< The output data collection pointer
489 
490  /// Find the information on a readout boards by fragment ID.
491  std::optional<details::BoardInfoLookup> fBoardInfoLookup;
492 
493 
494  /// Trigger timestamp for the current event.
496 
497 
498  /// Returns whether PMT configuration information is expected to be available.
499  bool hasPMTconfiguration() const { return fPMTconfigTag.has_value(); }
500 
501  /// Updates the PMT configuration cache. How? Dunno. Placeholder.
502  bool UpdatePMTConfiguration(sbn::PMTconfiguration const* PMTconfig);
503 
504 
505  /**
506  * @brief Returns a lookup object with board setup and configuration info.
507  * @param PMTconfig the PMT configuration, if available
508  * @return an object working like lookup table for all fragment information
509  *
510  * This method merges the setup information from the tool configuration with
511  * the PMT configuration specified in the argument, and returns an object
512  * that can look up all the information as a single record, with the
513  * fragment ID as key. In addition, a few intermediate quantities ("facts",
514  * see `BoardFacts_t`) are computed and stored in this object.
515  *
516  * If a fragment ID is missing, it means that no PMT configuration was
517  * provided and that the setup information did not include a fragment ID.
518  * If some information (configuration or setup) is missing, the "facts"
519  * depending on the missing information will have default values.
520  */
521  details::BoardInfoLookup matchBoardConfigurationAndSetup
522  (sbn::PMTconfiguration const* PMTconfig) const;
523 
524  /// Puts together all the needed information for a board.
525  NeededBoardInfo_t fetchNeededBoardInfo
526  (details::BoardInfoLookup::BoardInfo_t const* boardInfo, unsigned int fragmentID) const;
527 
528 
529  /**
530  * @brief Returns the count of set bits for each set bit.
531  * @tparam NBits the number of bits to test
532  * @param value the bit mask to be analyzed
533  * @return a pair: `first` an array with the `NBits` values,
534  * `second` the number of bits set
535  *
536  * Much better to go with an example.
537  * Let's process `setBitIndices<16U>(0x6701)`: `value` is `0110 0111 0000 0001`,
538  * and we request the `16` least significant bits (`NBits`).
539  * There are six bit that are set, in position 0, 8, 9, 10, 13 and 14.
540  * The returned value includes the number of set bits (6) as the `second`
541  * element of the pair, an as `first` an array of 16 indices, one for each bit
542  * position.
543  * Its values are `0` at `[0]`, `1` at `[8]`, `2` at `[9]`, `3` at `[10]`,
544  * `4` at `[13]` and `5` at `[14]`. That is, each value represents the number
545  * of that bit in the list of set bits. All the other indices, associated to
546  * the bits which are not set, are assigned a value that is equal or larger than
547  * the number of bits set (i.e. `6` or larger).
548  */
549  template <std::size_t NBits, typename T>
550  constexpr std::pair<std::array<std::size_t, NBits>, std::size_t>
551  setBitIndices(T value) noexcept;
552 
553 
554  // --- BEGIN -- Trees and their data ---------------------------------------
555 
556  /// Data structure for basic event information in simple ROOT trees.
558  unsigned int run; ///< Run number.
559  unsigned int subrun; ///< Subrun number.
560  unsigned int event; ///< Event number.
561  SplitTimestamp_t timestamp; ///< Event timestamp (seconds from the epoch).
562  }; // TreeData_EventID_t
563 
564  /// Structure collecting all data for a fragment ROOT tree.
565  struct TreeFragment_t {
566  struct Data_t: public TreeData_EventID_t {
567 
568  int fragmentID = 0; ///< ID of the fragment of this entry.
569  unsigned long int TriggerTimeTag = 0; ///< Trigger time tag from the fragment.
570 
571  SplitTimestamp_t trigger; ///< Global trigger time.
572 
573  SplitTimestamp_t fragTime; ///< PMT fragment time stamp.
574 
575  }; // Data_t
576 
578  TTree* tree = nullptr;
579  }; // TreeFragment_t
580 
581 
582  std::unique_ptr<TreeData_EventID_t> fEventInfo; ///< Event ID for trees.
583  std::unique_ptr<TreeFragment_t> fTreeFragment; ///< Tree with fragment information.
584 
585  // --- END ---- Trees and their data ---------------------------------------
586 
587  // --- BEGIN -- Tree-related methods ---------------------------------------
588 
589  /// Declares the use of event information.
590  void usesEventInfo();
591 
592  /// Initializes all requested data trees.
593  void initTrees(std::vector<std::string> const& treeNames);
594 
595  /// Initializes the event ID part of a tree.
596  void initEventIDtree(TTree& tree, TreeData_EventID_t& data);
597 
598  /// Initializes the fragment data tree (`fTreeFragment`).
599  void initFragmentsTree();
600 
601  /// Fills the base information of a tree data entry from an _art_ event.
602  void fillTreeEventID
603  (art::Event const& event, TreeData_EventID_t& treeData) const;
604 
605  /// Assigns the cached event information to the specified tree data.
606  void assignEventInfo(TreeData_EventID_t& treeData) const;
607 
608  /// Static initialization.
609  static TreeNameList_t initTreeNames();
610 
611  // --- END ---- Tree-related methods ---------------------------------------
612 
613 }; // class daq::PMTDecoder
614 
615 
616 namespace daq {
617 
618  /// Special function `fhicl::TableAs` uses to convert BoardSetupConfig.
620 
622  return {
623  config.Name() // name
624  , getOptionalValue(config.FragmentID)
625  .value_or(details::BoardSetup_t::NoFragmentID) // fragmentID
626  , config.TriggerDelay() // triggerDelay
627  };
628  } // convert(BoardSetupConfig)
629 
630 } // namespace daq
631 
632 
633 //------------------------------------------------------------------------------------------- -----------------------------------------------
634 constexpr daq::PMTDecoder::SplitTimestamp_t::SplitTimestamp_t(int sec, unsigned int ns)
635  : time { static_cast<long long int>(sec) * 1'000'000'000LL + ns }
636  , split { sec, ns }
637  {}
638 
639 constexpr daq::PMTDecoder::SplitTimestamp_t::SplitTimestamp_t(long long int triggerTime)
640  : time { triggerTime }
641  , split {
642  static_cast<int>(time / 1'000'000'000), // seconds
643  static_cast<unsigned int>(time % 1'000'000'000) // nanoseconds
644  }
645  {}
646 
647 
648 namespace daq {
649  std::ostream& operator<< (std::ostream& out, PMTDecoder::SplitTimestamp_t const& time) {
650  out << time.split.seconds << '.'
651  << std::setfill('0') << std::setw(9) << time.split.nanoseconds;
652  return out;
653  } // operator<< (std::ostream&, PMTDecoder::SplitTimestamp_t)
654 } // namespace daq
655 
656 
657 //------------------------------------------------------------------------------------------------------------------------------------------
658 daq::PMTDecoder::TreeNameList_t const daq::PMTDecoder::TreeNames
659  = daq::PMTDecoder::initTreeNames();
660 
661 auto daq::PMTDecoder::initTreeNames() -> TreeNameList_t {
662  TreeNameList_t names;
663  names[static_cast<std::size_t>(DataTrees::Fragments)] = "PMTfragments";
664  return names;
665 } // daq::PMTDecoder::initTreeNames()
666 
667 std::string daq::PMTDecoder::listTreeNames(std::string const& sep /* = " " */) {
668  std::string l;
669  for (std::string const& name: TreeNames) {
670  if (!l.empty()) l += sep;
671  l += '\'';
672  l += name;
673  l += '\'';
674  } // for
675  return l;
676 } // daq::PMTDecoder::listTreeNames()
677 
678 
679 //------------------------------------------------------------------------------------------------------------------------------------------
681  : fDiagnosticOutput{ params().DiagnosticOutput() }
682  , fRequireKnownBoards{ params().RequireKnownBoards() }
683  , fRequireBoardConfig{ params().RequireBoardConfig() }
684  , fPMTconfigTag{ ::util::fhicl::getOptionalValue(params().PMTconfigTag) }
685  , fTriggerTag{ params().TriggerTag() }
686  , fBoardSetup{ params().BoardSetup() }
687  , fLogCategory{ params().LogCategory() }
688  , fGeometry{ *(lar::providerFrom<geo::Geometry const>()) }
689  , fDetTimings
690  { art::ServiceHandle<detinfo::DetectorClocksService const>()->DataForJob() }
691  , fChannelMap{ *(art::ServiceHandle<icarusDB::IICARUSChannelMap const>{}) }
692  , fOpticalTick{ fDetTimings.OpticalClockPeriod() }
693  , fNominalTriggerTime{ fDetTimings.TriggerTime() }
694 {
695 
696  initTrees(params().DataTrees());
697 
698  mf::LogInfo log(fLogCategory);
699  log << "Configuration:"
700  << "\n * boards with setup: " << fBoardSetup.size();
701  if (fPMTconfigTag)
702  log << "\n * PMT configuration from '" << fPMTconfigTag->encode() << "'";
703  else
704  log << "\n * PMT configuration not used (and some corrections will be skipped)";
705  if (fRequireKnownBoards) {
706  log << "\n * all readout boards in input must be known (from `"
707  << params().BoardSetup.name() << "` or `"
708  << params().PMTconfigTag.name() << "`)"
709  ;
710  }
711  else {
712  log << "\n * readout boards with no information (from neither `"
713  << params().BoardSetup.name() << "` or `"
714  << params().PMTconfigTag.name()
715  << "`) are processed at the best we can (skipping corrections)"
716  ;
717  }
718  if (fRequireBoardConfig) {
719  log << "\n * all readout boards in `"
720  << params().BoardSetup.name()
721  << "` must appear in the PMT configuration from `"
722  << params().PMTconfigTag.name() << "`"
723  ;
724  }
725  else {
726  log << "\n * all readout boards in `"
727  << params().BoardSetup.name()
728  << "` may lack a matching PMT configuration from `"
729  << params().PMTconfigTag.name() << "`"
730  ;
731  }
732 
733 } // daq::PMTDecoder::PMTDecoder()
734 
735 //------------------------------------------------------------------------------------------------------------------------------------------
736 
737 
738 void daq::PMTDecoder::consumes(art::ConsumesCollector& consumerColl) {
739  if (fPMTconfigTag)
740  consumerColl.consumes<sbn::PMTconfiguration>(*fPMTconfigTag);
741  consumerColl.consumes<std::vector<raw::ExternalTrigger>>(fTriggerTag);
742 }
743 
744 
745 void daq::PMTDecoder::produces(art::ProducesCollector& collector)
746 {
747  collector.produces<OpDetWaveformCollection>();
748 }
749 
750 //------------------------------------------------------------------------------------------------------------------------------------------
751 void daq::PMTDecoder::configure(fhicl::ParameterSet const&) {
752  // Configuration all happens during construction
753  throw cet::exception("PMTDecoder")
754  << "This tool does not support reconfiguration.\n";
755 } // PMTDecoder::configure()
756 
757 
758 //------------------------------------------------------------------------------------------------------------------------------------------
759 void daq::PMTDecoder::setupRun(art::Run const& run)
760 {
761  sbn::PMTconfiguration const* PMTconfig = fPMTconfigTag
762  ? run.getHandle<sbn::PMTconfiguration>(*fPMTconfigTag).product(): nullptr;
763 
764  UpdatePMTConfiguration(PMTconfig);
765 }
766 
767 
768 //------------------------------------------------------------------------------------------------------------------------------------------
769 void daq::PMTDecoder::setupEvent(art::Event const& event)
770 {
771  //
772  // global trigger
773  //
774  auto const& triggers
775  = event.getProduct<std::vector<raw::ExternalTrigger>>(fTriggerTag);
776  if (triggers.size() != 1) {
777  // if this is hit, the decoder needs some development to correctly deal
778  // with input with no trigger, or more than one
779  throw cet::exception("PMTDecoder")
780  << "Found " << triggers.size() << " triggers from '"
781  << fTriggerTag.encode() << "', can deal only with 1.\n";
782  }
783  fTriggerTimestamp = { triggers.front().GetTrigTime() };
784  mf::LogTrace(fLogCategory)
785  << "Trigger time ('" << fTriggerTag.encode() << "'): " << fTriggerTimestamp << " s";
786 
787  //
788  // event ID
789  //
790 
791  // if needed, fill the record with the basic information of the event
792  if (fEventInfo) fillTreeEventID(event, *fEventInfo);
793 
794 } // daq::PMTDecoder::setupEvent()
795 
796 
797 //------------------------------------------------------------------------------------------------------------------------------------------
799 {
800  fOpDetWaveformCollection = OpDetWaveformCollectionPtr(new OpDetWaveformCollection);
801 }
802 
803 
804 template <std::size_t NBits, typename T>
805 constexpr std::pair<std::array<std::size_t, NBits>, std::size_t>
807 
808  std::pair<std::array<std::size_t, NBits>, std::size_t> res;
809  auto& [ indices, nSetBits ] = res;
810  for (std::size_t& index: indices) {
811  index = (value & 1)? nSetBits++: NBits;
812  value >>= 1;
813  } // for
814  return res;
815 
816 } // daq::PMTDecoder::setBitIndices()
817 
818 void daq::PMTDecoder::process_fragment(const artdaq::Fragment &artdaqFragment)
819 {
820  size_t const fragment_id = artdaqFragment.fragmentID();
821  size_t const eff_fragment_id = artdaqFragment.fragmentID() & 0x0fff;
822 
823  // convert fragment to Nevis fragment
824  sbndaq::CAENV1730Fragment fragment(artdaqFragment);
825  sbndaq::CAENV1730FragmentMetadata metafrag = *fragment.Metadata();
826  sbndaq::CAENV1730Event evt = *fragment.Event();
827  sbndaq::CAENV1730EventHeader header = evt.Header;
828 
829  size_t nChannelsPerBoard = metafrag.nChannels; //fragment.nChannelsPerBoard();
830 
831  //std::cout << "Decoder:channels_per_board: " << nChannelsPerBoard << std::endl;
832 
833  uint32_t ev_size_quad_bytes = header.eventSize;
834  uint32_t evt_header_size_quad_bytes = sizeof(sbndaq::CAENV1730EventHeader)/sizeof(uint32_t);
835  uint32_t data_size_double_bytes = 2*(ev_size_quad_bytes - evt_header_size_quad_bytes);
836  uint32_t nSamplesPerChannel = data_size_double_bytes/nChannelsPerBoard;
837  uint16_t const enabledChannels = header.ChannelMask();
838 
839  artdaq::Fragment::timestamp_t const fragmentTimestamp = artdaqFragment.timestamp();
840 
841  unsigned int const time_tag = header.triggerTimeTag;
842 
843  size_t boardId = nChannelsPerBoard * eff_fragment_id;
844 
845  // chDataMap tells where in the buffer each digitizer channel is;
846  // if nowhere, then the answer is a number no smaller than nEnabledChannels
847  auto const [ chDataMap, nEnabledChannels ] = setBitIndices<16U>(enabledChannels);
848 
849  if (fDiagnosticOutput)
850  {
851  mf::LogVerbatim(fLogCategory)
852  << "----> PMT Fragment ID: " << std::hex << fragment_id << std::dec
853  << ", boardID: " << boardId
854  << ", nChannelsPerBoard: " << nChannelsPerBoard
855  << ", nSamplesPerChannel: " << nSamplesPerChannel
856  << ", enabled (" << nEnabledChannels << "): " << icarus::ns::util::bin(enabledChannels)
857  << "\n size: " << ev_size_quad_bytes
858  << ", data size: " << data_size_double_bytes
859  << ", samples/channel: " << nSamplesPerChannel
860  << ", trigger time tag: " << time_tag
861  << ", time stamp: " << (fragmentTimestamp / 1'000'000'000UL)
862  << "." << (fragmentTimestamp % 1'000'000'000UL) << " s"
863  ;
864  }
865 
866  const uint16_t* data_begin = reinterpret_cast<const uint16_t*>(artdaqFragment.dataBeginBytes() + sizeof(sbndaq::CAENV1730EventHeader));
867 
868  // Recover the information for this fragment
869  if (fChannelMap.hasPMTDigitizerID(eff_fragment_id))
870  {
871  assert(fBoardInfoLookup);
872 
873  /*
874  * The trigger time is always the nominal one, because that is the
875  * reference time of the whole DAQ (PMT, TPC...).
876  * We only need to know how sooner than the trigger the V1730 buffer
877  * starts. Oh, and the delay from the global trigger time to when
878  * the readout board receives and processes the trigger signal.
879  */
880  details::BoardInfoLookup::BoardInfo_t const* boardInfo = fBoardInfoLookup->findBoardInfo(fragment_id);
881  if (!boardInfo) {
882  if (fRequireKnownBoards) {
883  cet::exception e("PMTDecoder");
884  e << "Input fragment has ID " << fragment_id
885  << " which has no associated board information (`BoardSetup`";
886  if (!hasPMTconfiguration()) e << " + `.FragmentID`";
887  throw e << ").\n";
888  }
889  }
890  else {
891  assert(boardInfo->fragmentID == fragment_id);
892  assert(boardInfo->setup);
893  }
894 
895  NeededBoardInfo_t const info = fetchNeededBoardInfo(boardInfo, fragment_id);
896 
897  nanoseconds const preTriggerTime = info.preTriggerTime;
898  nanoseconds const PMTtriggerDelay = info.PMTtriggerDelay;
899  auto const timeStamp
900  = fNominalTriggerTime - PMTtriggerDelay - preTriggerTime;
901  mf::LogTrace(fLogCategory) << "V1730 board '" << info.name
902  << "' has data starting at electronics time " << timeStamp
903  << " = " << fNominalTriggerTime
904  << " - " << PMTtriggerDelay << " - " << preTriggerTime
905  ;
906 
907  const icarusDB::DigitizerChannelChannelIDPairVec& digitizerChannelVec
908  = fChannelMap.getChannelIDPairVec(eff_fragment_id);
909 
910  std::optional<mf::LogVerbatim> diagOut;
911  if (fDiagnosticOutput) {
912  diagOut.emplace(fLogCategory);
913  (*diagOut)
914  << " " << digitizerChannelVec.size() << " channels:";
915  }
916  // Allocate the vector outside the loop just since we'll reuse it over and over...
917  std::vector<uint16_t> wvfm(nSamplesPerChannel);
918 
919  // track what we do and what we want to
920  uint16_t attemptedChannels = 0;
921 
922  for(auto const [ digitizerChannel, channelID ]: digitizerChannelVec)
923  {
924  if (diagOut)
925  (*diagOut) << " " << digitizerChannel << " [=> " << channelID << "];";
926 
927  attemptedChannels |= (1 << digitizerChannel);
928 
929  std::size_t const channelPosInData = chDataMap[digitizerChannel];
930  if (channelPosInData >= nEnabledChannels) {
931  mf::LogTrace(fLogCategory)
932  << "Digitizer channel " << digitizerChannel
933  << " [=> " << channelID << "] skipped because not enabled.";
934  continue;
935  }
936 
937  std::size_t const ch_offset = channelPosInData * nSamplesPerChannel;
938 
939  std::copy_n(data_begin + ch_offset, nSamplesPerChannel, wvfm.begin());
940 
941  mf::LogTrace(fLogCategory)
942  << "PMT channel " << channelID << " has " << wvfm.size()
943  << " samples (read from entry #" << channelPosInData
944  << " in fragment data) starting at electronics time " << timeStamp;
945  fOpDetWaveformCollection->emplace_back(timeStamp.value(), channelID, wvfm);
946  }
947  if (diagOut) diagOut.reset(); // destroys and therefore prints out
948  if (attemptedChannels != enabledChannels) {
949  // this is mostly a warning; regularly, for example,
950  // we effectively have 15 channels per board; but all 16 are enabled,
951  // so one channel is not decoded at all
952  mf::LogTrace log(fLogCategory);
953  log << "Not all data read:";
954  for (int const bit: util::counter(16U)) {
955  uint16_t const mask = (1 << bit);
956  bool const attempted = bool(attemptedChannels & mask);
957  bool const enabled = bool(enabledChannels & mask);
958  if (attempted == enabled) continue;
959  if (!enabled) // and attempted
960  log << "\n requested channel " << bit << " was not enabled";
961  if (!attempted) // and enabled
962  log << "\n data for enabled channel " << bit << " was ignored";
963  } // for
964  } // if request and availability did not match
965 
966  }
967  else {
968  mf::LogError(fLogCategory)
969  << "*** PMT could not find channel information for fragment: "
970  << fragment_id;
971  }
972 
973  if (fDiagnosticOutput) {
974  mf::LogVerbatim(fLogCategory)
975  << " - size of output collection: " << fOpDetWaveformCollection->size();
976  }
977 
978  if (fTreeFragment) {
979  fTreeFragment->data.fragmentID = fragment_id;
980  fTreeFragment->data.TriggerTimeTag = time_tag;
981  fTreeFragment->data.trigger = fTriggerTimestamp;
982  fTreeFragment->data.fragTime = { static_cast<long long int>(fragmentTimestamp) };
983  assignEventInfo(fTreeFragment->data);
984  fTreeFragment->tree->Fill();
985  } // if fTreeFragment
986 
987 } // PMTDecoder::process_fragment()
988 
989 
990 void daq::PMTDecoder::outputDataProducts(art::Event& event)
991 {
992  // Want the RawDigits to be sorted in channel order... has to be done somewhere so why not now?
993  std::sort(fOpDetWaveformCollection->begin(),fOpDetWaveformCollection->end(),[](const auto& left,const auto&right){return left.ChannelNumber() < right.ChannelNumber();});
994 
995  // Now transfer ownership to the event store
996  event.put(std::move(fOpDetWaveformCollection));
997 
998 }
999 
1000 //------------------------------------------------------------------------------------------------------------------------------------------
1002 
1003  fBoardInfoLookup.emplace(matchBoardConfigurationAndSetup(PMTconfig));
1004 
1005  mf::LogDebug(fLogCategory) << "Board information as cached:\n" << *fBoardInfoLookup;
1006 
1007  return true;
1008 } // daq::PMTDecoder::UpdatePMTConfiguration()
1009 
1010 
1011 //------------------------------------------------------------------------------------------------------------------------------------------
1013  (sbn::PMTconfiguration const* PMTconfig) const -> details::BoardInfoLookup
1014 {
1015  /*
1016  * We need to support the case where no PMT configuration is known
1017  * (that is the standard situation in the online monitor).
1018  * The "strategy" is that in such cases we give up the correct time stamp
1019  * decoding; if the setup information contains a fragment ID, it may be
1020  * possible to do a little better, that is to use the setup information
1021  * (this is not possible without knowing the fragment ID that each bit of
1022  * setup information pertains).
1023  *
1024  * So the cases for a board are:
1025  * * setup information is not present: encountering such a board will cause
1026  * an exception to be thrown (implemented elsewhere)
1027  * * PMT configuration and setup present: full configuration
1028  * * exception thrown if setup fragment ID is present and inconsistent
1029  * * PMT configuration not present: a general warning is printed;
1030  * * boards with setup fragment ID information: add setup information
1031  * to the "database" for the board: it will be used for partial
1032  * timestamp reconstruction
1033  * * boards without setup fragment ID information: board will not be
1034  * added into the database; no specific correction will be performed;
1035  * a warning is printed for each board
1036  *
1037  */
1038 
1039  // dictionary of board configurations (if any)
1040  std::vector<std::pair<std::string, sbn::V1730Configuration const*>> configByName;
1041  if (PMTconfig) {
1042  if (!PMTconfig->boards.empty())
1043  configByName.reserve(PMTconfig->boards.size());
1044  for (sbn::V1730Configuration const& boardConfig: PMTconfig->boards)
1045  configByName.emplace_back(boardConfig.boardName, &boardConfig);
1046  std::sort(configByName.begin(), configByName.end()); // sorted by board name
1047  } // if we have configuration
1048 
1049 
1050  auto findPMTconfig = [this,&configByName]
1051  (std::string const& name) -> sbn::V1730Configuration const*
1052  {
1053  if (!hasPMTconfiguration()) return nullptr;
1054  auto const* ppBoardConfig = details::binarySearch(configByName, name);
1055  if (!ppBoardConfig) {
1056  if (!fRequireBoardConfig) return nullptr;
1057  throw cet::exception("PMTDecoder")
1058  << "No DAQ configuration found for PMT readout board '"
1059  << name << "'\n"
1060  << "If this is expected, you may skip this check by setting "
1061  << "PMTDecoder tool configuration `RequireBoardConfig` to `false`.\n";
1062  }
1063  return ppBoardConfig->second;
1064  };
1065 
1066  // the filling is driven by boards configured in the tool
1067  // (which is how a setup entry is mandatory)
1068  details::BoardInfoLookup::Database_t boardInfoByFragment;
1069 
1070  for (details::BoardSetup_t const& boardSetup: fBoardSetup) {
1071 
1072  std::string const& boardName = boardSetup.name;
1073 
1074  sbn::V1730Configuration const* pBoardConfig = findPMTconfig(boardName);
1075 
1076  if (pBoardConfig) {
1077  // fragment ID from configuration and setup must match if both present
1078  if (boardSetup.hasFragmentID() && (boardSetup.fragmentID != pBoardConfig->fragmentID))
1079  {
1080  throw cet::exception("PMTDecoder")
1081  << "Board '" << boardName << "' has fragment ID "
1082  << std::hex << pBoardConfig->fragmentID << std::dec
1083  << " but it is set up as "
1084  << std::hex << boardSetup.fragmentID << std::dec
1085  << "!\n";
1086  } // if fragment ID in setup
1087  }
1088  else {
1089  if (boardSetup.hasFragmentID()) {
1090  mf::LogPrint(fLogCategory)
1091  << "Board '" << boardName
1092  << "' has no configuration information:"
1093  " some time stamp corrections will be skipped.";
1094  // to avoid this, make a PMT configuration available from input file
1095  }
1096  else {
1097  mf::LogPrint(fLogCategory)
1098  << "Board '" << boardName
1099  << "' can't be associated to a fragment ID: its time stamp corrections will be skipped.";
1100  // to avoid this, add a `BoardSetup.FragmentID` entry for it in the
1101  // configuration of this tool, or make a PMT configuration available
1102  continue; // no entry for this board at all
1103  }
1104  }
1105 
1106  unsigned int const fragmentID
1107  = pBoardConfig? pBoardConfig->fragmentID: boardSetup.fragmentID;
1108  assert(fragmentID != details::BoardSetup_t::NoFragmentID);
1109 
1110  nanoseconds const preTriggerTime
1111  = pBoardConfig
1112  ? (pBoardConfig->bufferLength * (1.0f - pBoardConfig->postTriggerFrac)) * fOpticalTick
1113  : nanoseconds{ 0.0 }
1114  ;
1115 
1116  details::BoardFacts_t boardFacts {
1117  preTriggerTime // ditto
1118  };
1119 
1120  boardInfoByFragment.push_back({
1121  fragmentID, // fragmentID
1122  &boardSetup, // setup
1123  pBoardConfig, // config
1124  std::move(boardFacts) // facts
1125  });
1126  } // for
1127 
1128  return details::BoardInfoLookup{ std::move(boardInfoByFragment) };
1129 
1130 } // daq::PMTDecoder::matchBoardConfigurationAndSetup()
1131 
1132 
1133 //------------------------------------------------------------------------------------------------------------------------------------------
1135  (details::BoardInfoLookup::BoardInfo_t const* boardInfo, unsigned int fragmentID) const
1136  -> NeededBoardInfo_t
1137 {
1138 
1139  return NeededBoardInfo_t{
1140  // name
1141  ((boardInfo && boardInfo->config)? boardInfo->config->boardName: ("<ID=" + std::to_string(fragmentID)))
1142  // preTriggerTime
1143  , (boardInfo? boardInfo->facts.preTriggerTime: nanoseconds{ 0.0 })
1144  // PMTtriggerDelay
1145  , ((boardInfo && boardInfo->setup)? boardInfo->setup->triggerDelay: nanoseconds{ 0.0 })
1146  };
1147 
1148 } // daq::PMTDecoder::fetchNeededBoardInfo()
1149 
1150 
1151 //------------------------------------------------------------------------------------------------------------------------------------------
1152 /*template <std::size_t NBits, typename T>
1153 constexpr std::pair<std::array<std::size_t, NBits>, std::size_t>
1154 daq::PMTDecoder::setBitIndices(T value) noexcept{
1155 
1156  std::pair<std::array<std::size_t, NBits>, std::size_t> res;
1157  auto& [ indices, nSetBits ] = res;
1158  for (std::size_t& index: indices) {
1159  index = (value & 1)? nSetBits++: NBits;
1160  value >>= 1;
1161  } // for
1162  return res;
1163 
1164 } // daq::PMTDecoder::setBitIndices()
1165 */
1166 
1167 //------------------------------------------------------------------------------------------------------------------------------------------
1168 void daq::PMTDecoder::initTrees(std::vector<std::string> const& treeNames) {
1169 
1170  auto findTree = [](std::string const& name)
1171  {
1172  return static_cast<PMTDecoder::DataTrees>(
1175  );
1176  };
1177 
1178  for (std::string const& name: treeNames) {
1179  switch (findTree(name)) {
1180  case DataTrees::Fragments: initFragmentsTree(); break;
1181  case DataTrees::N:
1182  default:
1183  throw cet::exception("PMTdecoder")
1184  << "initTrees(): no data tree supported with name '" << name << "'.\n";
1185  } // switch
1186  } // for names
1187 
1188 } // daq::PMTDecoder::initTrees()
1189 
1190 
1191 //------------------------------------------------------------------------------------------------------------------------------------------
1192 void daq::PMTDecoder::initEventIDtree(TTree& tree, TreeData_EventID_t& data) {
1193 
1194  usesEventInfo(); // this tree includes event information
1195 
1196  tree.Branch("run", &data.run);
1197  tree.Branch("subrun", &data.subrun);
1198  tree.Branch("event", &data.event);
1199  tree.Branch("timestamp", &data.timestamp.time, "timestamp/L");
1200 
1201 } // daq::PMTDecoder::initEventIDtree()
1202 
1203 
1205 
1206  if (fTreeFragment) return;
1207 
1208  TTree* tree = art::ServiceHandle<art::TFileService>()
1209  ->make<TTree>("PMTfragments", "PMT fragment data");
1210 
1211  fTreeFragment = std::make_unique<TreeFragment_t>();
1212  fTreeFragment->tree = tree;
1213  auto& data = fTreeFragment->data;
1214 
1215  initEventIDtree(*tree, data);
1216 
1217  tree->Branch("fragmentID", &data.fragmentID);
1218  tree->Branch("fragTime", &data.fragTime.time, "fragTime/L"); // ROOT 6.24 can't detect 64-bit
1219  tree->Branch("fragTimeSec", &data.fragTime.split.seconds);
1220  tree->Branch("TTT", &data.TriggerTimeTag, "TTT/l"); // ROOT 6.24 can't detect 64-bit
1221  tree->Branch("trigger", &data.trigger.time, "trigger/L"); // ROOT 6.24 can't detect 64-bit
1222  tree->Branch("triggerSec", &data.trigger.split.seconds);
1223  tree->Branch("triggerNS", &data.trigger.split.nanoseconds);
1224 
1225 } // daq::PMTDecoder::initFragmentsTree()
1226 
1227 
1229 
1230  // the allocation of fEventInfo is the flag for event information usage
1231  if (!fEventInfo) fEventInfo = std::make_unique<TreeData_EventID_t>();
1232 
1233 } // daq::PMTDecoder::usesEventInfo()
1234 
1235 
1237  (TreeData_EventID_t& treeData) const
1238 {
1239 
1240  assert(fEventInfo);
1241  treeData = *fEventInfo; // nice slicing
1242 
1243 } // daq::PMTDecoder::assignEventInfo()
1244 
1245 
1247  (art::Event const& event, TreeData_EventID_t& treeData) const
1248 {
1249  art::EventID const& id = event.id();
1250  treeData.run = id.run();
1251  treeData.subrun = id.subRun();
1252  treeData.event = id.event();
1253 
1254  art::Timestamp const& timestamp = event.time();
1255  treeData.timestamp
1256  = { static_cast<int>(timestamp.timeHigh()), timestamp.timeLow() };
1257 
1258 } // daq::PMTDecoder::fillTreeEventID()
1259 
1260 
1261 //------------------------------------------------------------------------------------------------------------------------------------------
1262 DEFINE_ART_CLASS_TOOL(daq::PMTDecoder)
1263 
detinfo::DetectorTimings const fDetTimings
Interface to LArSoft configuration for detector timing.
SplitTimestamp_t timestamp
Event timestamp (seconds from the epoch).
std::string boardName
Name (mnemonic) of the board.
virtual void setupEvent(art::Event const &event) override
Will read trigger information one day if needed.
virtual void initializeDataProducts() override
Initialize any data products the tool will output.
Structure collecting all data for a fragment ROOT tree.
std::vector< DigitizerChannelChannelIDPair > DigitizerChannelChannelIDPairVec
Utilities related to art service access.
PMTDecoder(Parameters const &params)
Constructor.
icarusDB::IICARUSChannelMap const & fChannelMap
Fragment/channel mapping database.
std::vector< details::BoardSetup_t > const fBoardSetup
All board setup settings.
std::optional< typename Optional::value_type > getOptionalValue(Optional const &parameter)
Returns the value of an optional parameter as std::optional.
Definition: FHiCLutils.h:188
IDecoder interface class definiton.
Definition: IDecoder.h:34
SplitTimestamp_t fTriggerTimestamp
Trigger timestamp for the current event.
Data structure for basic event information in simple ROOT trees.
Utility class for fast lookup of board data by fragment ID.
DataTrees
Enumerate the supported data trees.
Definition of util::enumerate().
std::array< std::string, static_cast< std::size_t >(DataTrees::N)> TreeNameList_t
bool const fRequireKnownBoards
Whether info on all input boards is required.
bool hasPMTconfiguration() const
Returns whether PMT configuration information is expected to be available.
This provides an art tool interface definition for tools which &quot;decode&quot; artdaq fragments into LArSoft...
void assignEventInfo(TreeData_EventID_t &treeData) const
Assigns the cached event information to the specified tree data.
walls no right
Definition: selectors.fcl:105
fhicl::Atom< std::string > Name
virtual void setupRun(art::Run const &run) override
Reads the PMT configuration from the run.
bool UpdatePMTConfiguration(sbn::PMTconfiguration const *PMTconfig)
Updates the PMT configuration cache. How? Dunno. Placeholder.
Information used in decoding from a board.
static constexpr bool
Some helpers for PMT decoder tool.
geo::GeometryCore const & fGeometry
Geometry service provider.
virtual void produces(art::ProducesCollector &) override
Each algorithm may have different objects it wants &quot;produced&quot; so use this to let the top level produc...
std::vector< BoardInfo_t > Database_t
void fillTreeEventID(art::Event const &event, TreeData_EventID_t &treeData) const
Fills the base information of a tree data entry from an art event.
pure virtual base interface for detector clocks
constexpr details::BinObj< T > bin(T value)
Returns a wrapper to print the specified data in binary format.
unsigned int fragmentID
DAQ fragment ID.
Interface to detinfo::DetectorClocks.
std::unique_ptr< TreeData_EventID_t > fEventInfo
Event ID for trees.
unsigned int bufferLength
Ticks in each buffer (recordLength).
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
void initEventIDtree(TTree &tree, TreeData_EventID_t &data)
Initializes the event ID part of a tree.
NeededBoardInfo_t fetchNeededBoardInfo(details::BoardInfoLookup::BoardInfo_t const *boardInfo, unsigned int fragmentID) const
Puts together all the needed information for a board.
Configuration of the V1730 readout board setup.
Access the description of detector geometry.
std::optional< art::InputTag > const fPMTconfigTag
Input tag of the PMT configuration.
virtual void process_fragment(const artdaq::Fragment &fragment) override
Given a set of recob hits, run DBscan to form 3D clusters.
long long int time
Trigger time in nanoseconds from The Epoch.
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:285
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
void usesEventInfo()
Declares the use of event information.
constexpr mask_t< EnumType > mask(EnumType bit, OtherBits...otherBits)
Returns a mask with all specified bits set.
second seconds
Alias for common language habits.
Definition: spacetime.h:88
bool const fDiagnosticOutput
If true will spew endless messages to output.
void initFragmentsTree()
Initializes the fragment data tree (fTreeFragment).
SplitTimestamp_t trigger
Global trigger time.
constexpr std::array< std::size_t, geo::vect::dimension< Vector >)> indices()
Returns a sequence of indices valid for a vector of the specified type.
detinfo::timescales::electronics_time const fNominalTriggerTime
Trigger time as reported by DetectorClocks service.
process_name PMTconfig
unsigned int subrun
Subrun number.
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
std::unique_ptr< OpDetWaveformCollection > OpDetWaveformCollectionPtr
bool const fRequireBoardConfig
Whether setup info on all boards is required.
BEGIN_PROLOG vertical distance to the surface Name
walls no left
Definition: selectors.fcl:105
Information from the configuration of PMT readout.
Test of util::counter and support utilities.
static constexpr unsigned int NoFragmentID
Special value to mark the absence of fragment ID information.
Description of geometry of one entire detector.
static TreeNameList_t const TreeNames
An interval (duration, length, distance) between two quantity points.
Definition: intervals.h:114
Coll::value_type const * binarySearch(Coll const &coll, Key const &key, KeyExtractor &&extractKey)
Record of information about a readout board.
nanoseconds_as<> nanoseconds
Type of time interval stored in nanoseconds, in double precision.
Definition: spacetime.h:270
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
constexpr SplitTimestamp_t()=default
Utilities to read interval and point quantity FHiCL configuration.
virtual void consumes(art::ConsumesCollector &consumerColl) override
I hereby declare I will consume trigger and PMT configuration products.
std::string const fLogCategory
Message facility category.
std::string to_string(WindowPattern const &pattern)
process_name sequence::icarus_stage0_EastHits_TPC physics sequence::icarus_stage0_EastHits_TPC physics pathNUMI physics streamNUMI outputs outBNB drop *_ *_ *_DAQ drop *_daqTPCROI_ *_ drop *_decon1droi_ *_ drop *_decon1DroiTPC *_ *_ *outputs outNUMI drop *_ *_ *_DAQ drop *_daqTPCROI_ *_ drop *_decon1droi_ *_ drop *_decon1DroiTPC *_ *_ *physics producers daqPMT DecoderTool RequireBoardConfig
Dimensioned variables representing space or time quantities.
Data structure for trigger time.
A class exposing an upgraded interface of detinfo::DetectorClocksData.
process_name largeant stream1 can override from command line with o or output physics producers generator N
std::optional< details::BoardInfoLookup > fBoardInfoLookup
Find the information on a readout boards by fragment ID.
details::BoardInfoLookup matchBoardConfigurationAndSetup(sbn::PMTconfiguration const *PMTconfig) const
Returns a lookup object with board setup and configuration info.
OpDetWaveformCollectionPtr fOpDetWaveformCollection
The output data collection pointer.
void initTrees(std::vector< std::string > const &treeNames)
Initializes all requested data trees.
do i e
art::ToolConfigTable< Config > Parameters
then echo fcl name
Simple helper functions to deal with FHiCL.
float postTriggerFrac
Fraction of the waveform after the trigger signal (postPercent).
temporary value
Split_t split
Trigger time in nanoseconds from The Epoch (in components).
Main tool configuration.
TCEvent evt
Definition: DataStructs.cxx:8
virtual void outputDataProducts(art::Event &event) override
Output the data products to the event store.
fhicl::Atom< nanoseconds > TriggerDelay
Information of the setup of a V1730 readout board.
virtual void configure(const fhicl::ParameterSet &) override
Reconfiguration is not supported: all configuration at construction time.
nanoseconds const fOpticalTick
Duration of the optical detector readout sampling tick (i.e. 2 ns; shh!).
Class containing configuration for PMT readout.
timescale_traits< ElectronicsTimeCategory >::time_point_t electronics_time
A point in time on the electronics time scale.
unsigned int event
Event number.
std::vector< raw::OpDetWaveform > OpDetWaveformCollection
SplitTimestamp_t fragTime
PMT fragment time stamp.
TimeTrackTreeStorage::TriggerInputSpec_t convert(TimeTrackTreeStorage::Config::TriggerSpecConfig const &config)
art framework interface to geometry description
Functions to dump the content of binary data chunks to console.
Turns PMT readout fragments from DAQ into LArSoft data products.
std::unique_ptr< TreeFragment_t > fTreeFragment
Tree with fragment information.
fhicl::OptionalAtom< unsigned int > FragmentID
art::InputTag const fTriggerTag
Input tag of the global trigger.
std::vector< sbn::V1730Configuration > boards
Configuration of all PMT readout boards.
nanosecond nanoseconds
Alias for common language habits.
Definition: spacetime.h:139
constexpr std::pair< std::array< std::size_t, NBits >, std::size_t > setBitIndices(T value) noexcept
Returns the count of set bits for each set bit.
Class containing configuration for a V1730 board.