libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
time_correlate.hpp
1/*
2 * This file is part of libtcspc
3 * Copyright 2019-2026 Board of Regents of the University of Wisconsin System
4 * SPDX-License-Identifier: MIT
5 */
6
7#pragma once
8
9#include "arg_wrappers.hpp"
10#include "common.hpp"
11#include "int_arith.hpp"
12#include "introspect.hpp"
13#include "numeric_traits.hpp"
14#include "processor.hpp"
15#include "time_tagged_events.hpp"
16
17#include <array>
18#include <cmath>
19#include <stdexcept>
20#include <type_traits>
21#include <utility>
22
23namespace tcspc {
24
25namespace internal {
26
27// For now, we only support setting the emitted event's abstime to the start
28// time, stop time, or midpoint. For CFD-like usage, it is potentially useful
29// to split the time in arbitrary ratios other than 1:1; this could be
30// supported, but probably should use a run-time floating point ratio, given
31// that the ratio would be something experimentally determined and not a simple
32// integer ratio.
33
34template <typename NumericTraits, bool UseStartTimeAndChannel,
35 typename Downstream>
36class time_correlate_at_start_or_stop {
37 static_assert(
38 processor<Downstream, time_correlated_detection_event<NumericTraits>>);
39
40 Downstream downstream;
41
42 public:
43 explicit time_correlate_at_start_or_stop(Downstream downstream)
44 : downstream(std::move(downstream)) {}
45
46 [[nodiscard]] auto introspect_node() const -> processor_info {
47 return processor_info(this, "time_correlate_at_start_or_stop");
48 }
49
50 [[nodiscard]] auto introspect_graph() const -> processor_graph {
51 return downstream.introspect_graph().push_entry_point(this);
52 }
53
54 template <typename NT>
55 void handle(std::array<detection_event<NT>, 2> const &event) {
56 static_assert(std::is_same_v<typename NT::abstime_type,
57 typename NumericTraits::abstime_type>);
58 static_assert(std::is_same_v<typename NT::channel_type,
59 typename NumericTraits::channel_type>);
60 auto const &anchor = UseStartTimeAndChannel ? event[0] : event[1];
61 auto const difftime =
62 convert_with_check<typename NumericTraits::difftime_type>(
63 subtract_with_check(event[1].abstime, event[0].abstime));
64 downstream.handle(time_correlated_detection_event<NumericTraits>{
65 anchor.abstime, anchor.channel, difftime});
66 }
67
68 template <typename NT>
69 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
70 void handle(std::array<detection_event<NT>, 2> &&event) {
71 handle(static_cast<std::array<detection_event<NT>, 2> const &>(event));
72 }
73
74 template <typename OtherEvent>
75 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
76 void handle(OtherEvent &&event) {
77 downstream.handle(std::forward<OtherEvent>(event));
78 }
79
80 void flush() { downstream.flush(); }
81};
82
83template <typename NumericTraits, bool UseStartChannel, typename Downstream>
84class time_correlate_at_midpoint {
85 static_assert(
86 processor<Downstream, time_correlated_detection_event<NumericTraits>>);
87
88 Downstream downstream;
89
90 public:
91 explicit time_correlate_at_midpoint(Downstream downstream)
92 : downstream(std::move(downstream)) {}
93
94 [[nodiscard]] auto introspect_node() const -> processor_info {
95 return processor_info(this, "time_correlate_at_midpoint");
96 }
97
98 [[nodiscard]] auto introspect_graph() const -> processor_graph {
99 return downstream.introspect_graph().push_entry_point(this);
100 }
101
102 template <typename NT>
103 void handle(std::array<detection_event<NT>, 2> const &event) {
104 static_assert(std::is_same_v<typename NT::abstime_type,
105 typename NumericTraits::abstime_type>);
106 static_assert(std::is_same_v<typename NT::channel_type,
107 typename NumericTraits::channel_type>);
108 auto const difftime =
109 subtract_with_check(event[1].abstime, event[0].abstime);
110 auto const abstime = event[0].abstime + difftime / 2;
111 auto const channel =
112 UseStartChannel ? event[0].channel : event[1].channel;
113 downstream.handle(time_correlated_detection_event<NumericTraits>{
114 abstime, channel,
115 convert_with_check<typename NumericTraits::difftime_type>(
116 difftime)});
117 }
118
119 template <typename NT>
120 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
121 void handle(std::array<detection_event<NT>, 2> &&event) {
122 handle(static_cast<std::array<detection_event<NT>, 2> const &>(event));
123 }
124
125 template <typename OtherEvent>
126 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
127 void handle(OtherEvent &&event) {
128 downstream.handle(std::forward<OtherEvent>(event));
129 }
130
131 void flush() { downstream.flush(); }
132};
133
134template <typename NumericTraits, bool UseStartChannel, typename Downstream>
135class time_correlate_at_fraction {
136 static_assert(
137 processor<Downstream, time_correlated_detection_event<NumericTraits>>);
138
139 double frac; // 0.0-1.0 for internal division of start-stop
140 Downstream downstream;
141
142 public:
143 explicit time_correlate_at_fraction(arg::fraction<double> fraction,
144 Downstream downstream)
145 : frac(fraction.value), downstream(std::move(downstream)) {
146 if (frac < 0.0 || frac > 1.0)
147 throw std::invalid_argument(
148 "time_correlate_at_fraction fraction must be in range [0.0, 1.0]");
149 }
150
151 [[nodiscard]] auto introspect_node() const -> processor_info {
152 return processor_info(this, "time_correlate_at_fraction");
153 }
154
155 [[nodiscard]] auto introspect_graph() const -> processor_graph {
156 return downstream.introspect_graph().push_entry_point(this);
157 }
158
159 template <typename NT>
160 void handle(std::array<detection_event<NT>, 2> const &event) {
161 static_assert(std::is_same_v<typename NT::abstime_type,
162 typename NumericTraits::abstime_type>);
163 static_assert(std::is_same_v<typename NT::channel_type,
164 typename NumericTraits::channel_type>);
165 auto const difftime =
166 subtract_with_check(event[1].abstime, event[0].abstime);
167 auto const abstime =
168 event[0].abstime +
169 static_cast<NumericTraits::abstime_type>(
170 std::llround(static_cast<double>(difftime) * frac));
171 auto const channel =
172 UseStartChannel ? event[0].channel : event[1].channel;
173 downstream.handle(time_correlated_detection_event<NumericTraits>{
174 abstime, channel,
175 convert_with_check<typename NumericTraits::difftime_type>(
176 difftime)});
177 }
178
179 template <typename NT>
180 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
181 void handle(std::array<detection_event<NT>, 2> &&event) {
182 handle(static_cast<std::array<detection_event<NT>, 2> const &>(event));
183 }
184
185 template <typename OtherEvent>
186 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
187 void handle(OtherEvent &&event) {
188 downstream.handle(std::forward<OtherEvent>(event));
189 }
190
191 void flush() { downstream.flush(); }
192};
193
194} // namespace internal
195
228template <typename NumericTraits = default_numeric_traits, typename Downstream>
229auto time_correlate_at_start(Downstream downstream) {
230 return internal::time_correlate_at_start_or_stop<NumericTraits, true,
231 Downstream>(
232 std::move(downstream));
233}
234
266template <typename NumericTraits = default_numeric_traits, typename Downstream>
267auto time_correlate_at_stop(Downstream downstream) {
268 return internal::time_correlate_at_start_or_stop<NumericTraits, false,
269 Downstream>(
270 std::move(downstream));
271}
272
311template <typename NumericTraits = default_numeric_traits,
312 bool UseStartChannel = false, typename Downstream>
313auto time_correlate_at_midpoint(Downstream downstream) {
314 return internal::time_correlate_at_midpoint<NumericTraits, UseStartChannel,
315 Downstream>(
316 std::move(downstream));
317}
318
360template <typename NumericTraits = default_numeric_traits,
361 bool UseStartChannel = false, typename Downstream>
363 Downstream downstream) {
364 return internal::time_correlate_at_fraction<NumericTraits, UseStartChannel,
365 Downstream>(
366 fraction, std::move(downstream));
367}
368
369namespace internal {
370
371template <typename NumericTraits, typename Downstream>
372 requires processor<Downstream, detection_event<NumericTraits>>
374 Downstream downstream;
375
376 public:
377 explicit remove_time_correlation(Downstream downstream)
378 : downstream(std::move(downstream)) {}
379
380 [[nodiscard]] auto introspect_node() const -> processor_info {
381 return processor_info(this, "remove_time_correlation");
382 }
383
384 [[nodiscard]] auto introspect_graph() const -> processor_graph {
385 return downstream.introspect_graph().push_entry_point(this);
386 }
387
388 template <typename NT>
389 void handle(time_correlated_detection_event<NT> const &event) {
390 static_assert(std::is_same_v<typename NT::abstime_type,
391 typename NumericTraits::abstime_type>);
392 static_assert(std::is_same_v<typename NT::channel_type,
393 typename NumericTraits::channel_type>);
394
395 downstream.handle(
396 detection_event<NumericTraits>{event.abstime, event.channel});
397 }
398
399 template <typename NT>
400 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
401 void handle(time_correlated_detection_event<NT> &&event) {
402 handle(
403 static_cast<time_correlated_detection_event<NT> const &>(event));
404 }
405
406 template <typename OtherEvent>
407 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
408 void handle(OtherEvent &&event) {
409 downstream.handle(std::forward<OtherEvent>(event));
410 }
411
412 void flush() { downstream.flush(); }
413};
414
415} // namespace internal
416
437template <typename NumericTraits = default_numeric_traits, typename Downstream>
438auto remove_time_correlation(Downstream downstream) {
439 return internal::remove_time_correlation<NumericTraits, Downstream>(
440 std::move(downstream));
441}
442
443} // namespace tcspc
auto time_correlate_at_fraction(arg::fraction< double > fraction, Downstream downstream)
Create a processor that collapses detection pairs into time-correlated detection events at a fraction...
Definition time_correlate.hpp:362
auto time_correlate_at_start(Downstream downstream)
Create a processor that collapses detection pairs into time-correlated detection events at the start ...
Definition time_correlate.hpp:229
auto time_correlate_at_midpoint(Downstream downstream)
Create a processor that collapses detection pairs into time-correlated detection events at the midpoi...
Definition time_correlate.hpp:313
auto time_correlate_at_stop(Downstream downstream)
Create a processor that collapses detection pairs into time-correlated detection events at the stop t...
Definition time_correlate.hpp:267
auto remove_time_correlation(Downstream downstream)
Create a processor that removes the difftime from detection events.
Definition time_correlate.hpp:438
libtcspc namespace.
Definition acquire.hpp:30
Function argument wrapper for fraction parameter.
Definition arg_wrappers.hpp:137