LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
GeneratedEventTimestamp_plugin.cc
Go to the documentation of this file.
1 
10 // C/C++ standard libraries
11 #include <chrono>
12 #include <random>
13 
14 // framework libraries
19 
20 // Event generation namespace
21 namespace evgen {
22 
47  public:
48 
51 
52 
54  virtual art::Timestamp eventTimestamp(art::EventID const& id) override;
55 
56  private:
59 
60  }; // class GeneratedEventTimestamp
61 
62 
63 
64 
65 } // namespace evgen
66 
67 //------------------------------------------------------------------------------
68 //--- Implementation
69 //---
70 //---
71 
72 namespace evgen {
73  namespace details {
74 
75  //--------------------------------------------------------------------------
76  template <typename T>
77  struct Average {
78  using data_t = T;
79 
80  void clear() { fTotal = data_t(0); fN = 0U; }
81  void insert(data_t value) { fTotal += value; ++fN; }
82 
83  data_t n() const { return fN; }
84  data_t average() const { return fTotal / fN; }
85 
86  private:
87  unsigned int fN = 0U;
88  data_t fTotal = data_t(0);
89 
90  }; // class Average
91 
92 
93  //--------------------------------------------------------------------------
95  template <typename T>
96  auto discretize(T value, T period) {
97  auto const excess = value % period;
98  auto const base = value - excess;
99  return (excess < (period / T(2)))? base: base + period;
100  } // discretize()
101 
102 
103  //--------------------------------------------------------------------------
105  template <typename Clock, typename Unit>
107  public:
108 
111 
113  duration_t operator() () { return read_clock(); }
114 
116  static duration_t read_clock() { return timeFromEpoch(Clock::now()); }
117 
120  static duration_t currentOffsetFromEpoch();
121 
122  protected:
123 
125  template <typename TimeInterval>
126  static constexpr duration_t toDuration(TimeInterval dt)
127  {
128  return static_cast<duration_t>
129  (std::chrono::duration_cast<Unit>(dt).count());
130  }
131 
133  template <typename Rep, typename Period>
134  static constexpr auto periodToDuration()
135  { return toDuration(std::chrono::duration<Rep, Period>(1)); }
136 
138  template <typename TimePoint>
139  static duration_t timeFromEpoch(TimePoint t)
140  { return toDuration(t.time_since_epoch()); }
141 
142  }; // class TimeInUnitsBase<>
143 
144 
145  template <typename Clock, typename Duration>
147  -> duration_t
148  {
149  /*
150  * The plan is to compare the `Clock` we use with the system clock, which
151  * is guaranteed by the C++20 standard to refer to a well defined absolute
152  * time point (the UNIX epoch, January 1, 1970).
153  * Chances are that the resolution of the system clock is not as good as
154  * the one of `Clock`. If the difference between the two clocks is less
155  * than a few seconds, we attribute the difference to chance and we don't
156  * correct for it.
157  *
158  * Otherwise, the same time (almost!) is taken from the two clocks, and
159  * the difference in `Duration` units is used as a correction.
160  *
161  */
162  using clock_t = Clock;
163  using system_clock_t = std::chrono::system_clock;
164  using namespace std::chrono_literals;
165 
166  // no point in doing the exercise if we are already using the system clock
167  if (std::is_same<clock_t, system_clock_t>()) {
168  MF_LOG_DEBUG("GeneratedEventTimestamp")
169  << "Using system clock for timestamp: no offset needed.";
170  return static_cast<duration_t>(0);
171  }
172 
173  auto clock_time = clock_t::now();
174  auto sys_clock_time = system_clock_t::now();
175 
176  // if the system clock is close to our clock, of if it is ahead of it,
177  // use no offset (the latter stems from the consideration that that the
178  // two clocks are equivalent although they suffer from some jitter)
179  if (
180  (timeFromEpoch(sys_clock_time) - timeFromEpoch(clock_time))
181  < toDuration(5s)
182  )
183  {
184  MF_LOG_DEBUG("GeneratedEventTimestamp")
185  << "Offset with system clock is small ("
186  << (timeFromEpoch(sys_clock_time) - timeFromEpoch(clock_time))
187  << ", " << timeFromEpoch(sys_clock_time)
188  << " vs. " << timeFromEpoch(clock_time)
189  << "): no offset needed."
190  ;
191  return static_cast<duration_t>(0);
192  }
193 
194  //
195  // pick the largest of the resolutions for the comparison
196  //
197  using clock_period_t = typename clock_t::period;
198  using system_clock_period_t = system_clock_t::period;
199  using largest_period_t = std::conditional_t<
200  (
201  clock_period_t::num * system_clock_period_t::den
202  > system_clock_period_t::num * clock_period_t::den
203  ),
204  clock_period_t,
205  system_clock_period_t
206  >;
207  // this is the period expressed in the Duration unit
208  constexpr auto largest_period
209  = periodToDuration<typename clock_t::rep, largest_period_t>();
210 
211  //
212  // compare and round
213  //
214  constexpr unsigned int times = 10U; // average 10 samples
215  Average<duration_t> offset;
216  for (unsigned int i = 0; i < times; ++i) {
217 
218  offset.insert
219  (timeFromEpoch(sys_clock_time) - timeFromEpoch(clock_time));
220  clock_time = clock_t::now();
221  sys_clock_time = system_clock_t::now();
222 
223  } // for
224 
225  MF_LOG_DEBUG("GeneratedEventTimestamp")
226  << "System clock period: "
227  << periodToDuration<typename clock_t::rep, system_clock_period_t>()
228  << "\nUser clock period: "
229  << periodToDuration<typename clock_t::rep, clock_period_t>()
230  << "\nOffset: " << offset.average()
231  << " (rounded to: " << largest_period << ")"
232  ;
233 
234  // round off the offset with one "largest period"
235  return discretize(offset.average(), largest_period);
236 
237  } // TimeInUnitsBase<>::currentOffsetFromEpoch()
238 
239 
240  //--------------------------------------------------------------------------
243  template <typename Clock, typename Duration, typename = void>
244  class TimeInUnits: public TimeInUnitsBase<Clock, Duration> {};
245 
246 
247  // Implementation of the random-gap-filling version
248  template <typename Clock, typename Duration>
249  class TimeInUnits<Clock, Duration, typename std::enable_if<
250  (Clock::period::num * Duration::period::den > Duration::period::num * Clock::period::den)
251  >::type
252  >
253  : public TimeInUnitsBase<Clock, Duration>
254  {
256  using typename Base_t::duration_t;
257 
258  // if the period of the clock is larger than the unit we are requested,
259  // there will be some padding
260  using ClockPeriod = typename Clock::period;
261  using ReqPeriod = typename Duration::period;
262  // requested clock / unit:
263  using PeriodRatio = std::ratio<
264  ClockPeriod::num * ReqPeriod::den, ReqPeriod::num * ClockPeriod::den
265  >;
266  static constexpr intmax_t paddingfactor
267  = PeriodRatio::num / PeriodRatio::den; // > 1 enforced by enable_if<>
268 
269  public:
270  TimeInUnits(): engine(), flat(0, paddingfactor - 1) {}
271 
273  duration_t operator() ()
274  { return Base_t::read_clock() + flat(engine); }
275 
276  private:
277  std::default_random_engine engine;
278  std::uniform_int_distribution<duration_t> flat;
279  }; // class TimeInUnits<> (padded)
280 
281 
283  <std::chrono::high_resolution_clock, std::chrono::nanoseconds>;
284 
285  //--------------------------------------------------------------------------
286 
287  } // namespace details
288 } // namespace evgen
289 
290 
291 //------------------------------------------------------------------------------
293  (fhicl::ParameterSet const& pset)
296 {
297 
298  mf::LogInfo("GeneratedEventTimestamp")
299  << "Timestamp plugin: timestamp from local clock time in nanoseconds";
300  if (fOffsetFromEpoch != 0) {
301  MF_LOG_TRACE("GeneratedEventTimestamp")
302  << " Time offset from epoch: " << fOffsetFromEpoch << " ns";
303  }
304 
305 } // evgen::GeneratedEventTimestamp::GeneratedEventTimestamp()
306 
307 
308 //------------------------------------------------------------------------------
310  (art::EventID const& id)
311 {
312  // obtain from the high resolution clock the current time, from the "epoch",
313  // in nanoseconds; if the clock is less precise than the nanosecond,
314  // the precision gap is filled with randomness
315  details::ns_clock_t get_time;
316 
317  const long long int now_ns = fOffsetFromEpoch + get_time();
318 
319  // convert into a timestamp
320  art::Timestamp ts(now_ns);
321 
322  mf::LogTrace("GeneratedEventTimestamp")
323  << "Generated time stamp: " << ts.value() << " for event " << id;
324 
325  return ts;
326 } // evgen::GeneratedEventTimestamp::eventTimestamp()
327 
328 // make art aware that we have a plugin
std::ratio< ClockPeriod::num *ReqPeriod::den, ReqPeriod::num *ClockPeriod::den > PeriodRatio
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
Float_t den
Definition: plot.C:35
auto discretize(T value, T period)
Returns the multiple of period closest to value.
Plugin to assign an empty event a time stamp from the clock.
STL namespace.
static constexpr auto periodToDuration()
Returns the duration (duration_t) of a period type.
constexpr TimeValue_t value() const
Definition: Timestamp.h:23
Class reading a Clock and converting the value to a specific Unit.
static duration_t read_clock()
Reads and returns the current time the clock.
GeneratedEventTimestamp(fhicl::ParameterSet const &pset)
Constructor: nothing specific.
#define MF_LOG_TRACE(id)
std::uniform_int_distribution< duration_t > flat
art::TimeValue_t const fOffsetFromEpoch
Offset to be added to the chosen clock to get an absolute time.
double value
Definition: spectrum.C:18
#define DEFINE_ART_EMPTYEVENTTIMESTAMP_PLUGIN(klass)
static constexpr duration_t toDuration(TimeInterval dt)
Converts a std::chrono::duration into our duration metric.
std::uint64_t TimeValue_t
Definition: Timestamp.h:8
virtual art::Timestamp eventTimestamp(art::EventID const &id) override
Returns the time stamp for the specified event.
#define MF_LOG_DEBUG(id)
art::TimeValue_t duration_t
Type of the time duration as returned by this class.
art::TimeValue_t duration_t
Type of the time duration as returned by this class.
MaybeLogger_< ELseverityLevel::ELsev_success, true > LogTrace
Event Generation using GENIE, cosmics or single particles.
static duration_t timeFromEpoch(TimePoint t)
Returns the time elapsed from the epoch to t.
nanosecond nanoseconds
Alias for common language habits.
Definition: spacetime.h:136