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 "data_types.hpp"
12#include "int_arith.hpp"
13#include "introspect.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 DataTypes, bool UseStartTimeAndChannel, typename Downstream>
35class time_correlate_at_start_or_stop {
36 static_assert(
37 processor<Downstream, time_correlated_detection_event<DataTypes>>);
38
39 Downstream downstream;
40
41 public:
42 explicit time_correlate_at_start_or_stop(Downstream downstream)
43 : downstream(std::move(downstream)) {}
44
45 [[nodiscard]] auto introspect_node() const -> processor_info {
46 return processor_info(this, "time_correlate_at_start_or_stop");
47 }
48
49 [[nodiscard]] auto introspect_graph() const -> processor_graph {
50 return downstream.introspect_graph().push_entry_point(this);
51 }
52
53 template <typename DT>
54 void handle(std::array<detection_event<DT>, 2> const &event) {
55 static_assert(std::is_same_v<typename DT::abstime_type,
56 typename DataTypes::abstime_type>);
57 static_assert(std::is_same_v<typename DT::channel_type,
58 typename DataTypes::channel_type>);
59 auto const &anchor = UseStartTimeAndChannel ? event[0] : event[1];
60 auto const difftime =
61 convert_with_check<typename DataTypes::difftime_type>(
62 subtract_with_check(event[1].abstime, event[0].abstime));
63 downstream.handle(time_correlated_detection_event<DataTypes>{
64 anchor.abstime, anchor.channel, difftime});
65 }
66
67 template <typename DT>
68 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
69 void handle(std::array<detection_event<DT>, 2> &&event) {
70 handle(static_cast<std::array<detection_event<DT>, 2> const &>(event));
71 }
72
73 template <typename OtherEvent>
74 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
75 void handle(OtherEvent &&event) {
76 downstream.handle(std::forward<OtherEvent>(event));
77 }
78
79 void flush() { downstream.flush(); }
80};
81
82template <typename DataTypes, bool UseStartChannel, typename Downstream>
83class time_correlate_at_midpoint {
84 static_assert(
85 processor<Downstream, time_correlated_detection_event<DataTypes>>);
86
87 Downstream downstream;
88
89 public:
90 explicit time_correlate_at_midpoint(Downstream downstream)
91 : downstream(std::move(downstream)) {}
92
93 [[nodiscard]] auto introspect_node() const -> processor_info {
94 return processor_info(this, "time_correlate_at_midpoint");
95 }
96
97 [[nodiscard]] auto introspect_graph() const -> processor_graph {
98 return downstream.introspect_graph().push_entry_point(this);
99 }
100
101 template <typename DT>
102 void handle(std::array<detection_event<DT>, 2> const &event) {
103 static_assert(std::is_same_v<typename DT::abstime_type,
104 typename DataTypes::abstime_type>);
105 static_assert(std::is_same_v<typename DT::channel_type,
106 typename DataTypes::channel_type>);
107 auto const difftime =
108 subtract_with_check(event[1].abstime, event[0].abstime);
109 auto const abstime = event[0].abstime + difftime / 2;
110 auto const channel =
111 UseStartChannel ? event[0].channel : event[1].channel;
112 downstream.handle(time_correlated_detection_event<DataTypes>{
113 abstime, channel,
114 convert_with_check<typename DataTypes::difftime_type>(difftime)});
115 }
116
117 template <typename DT>
118 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
119 void handle(std::array<detection_event<DT>, 2> &&event) {
120 handle(static_cast<std::array<detection_event<DT>, 2> const &>(event));
121 }
122
123 template <typename OtherEvent>
124 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
125 void handle(OtherEvent &&event) {
126 downstream.handle(std::forward<OtherEvent>(event));
127 }
128
129 void flush() { downstream.flush(); }
130};
131
132template <typename DataTypes, bool UseStartChannel, typename Downstream>
133class time_correlate_at_fraction {
134 static_assert(
135 processor<Downstream, time_correlated_detection_event<DataTypes>>);
136
137 double frac; // 0.0-1.0 for internal division of start-stop
138 Downstream downstream;
139
140 public:
141 explicit time_correlate_at_fraction(arg::fraction<double> fraction,
142 Downstream downstream)
143 : frac(fraction.value), downstream(std::move(downstream)) {
144 if (frac < 0.0 || frac > 1.0)
145 throw std::invalid_argument(
146 "time_correlate_at_fraction fraction must be in range [0.0, 1.0]");
147 }
148
149 [[nodiscard]] auto introspect_node() const -> processor_info {
150 return processor_info(this, "time_correlate_at_fraction");
151 }
152
153 [[nodiscard]] auto introspect_graph() const -> processor_graph {
154 return downstream.introspect_graph().push_entry_point(this);
155 }
156
157 template <typename DT>
158 void handle(std::array<detection_event<DT>, 2> const &event) {
159 static_assert(std::is_same_v<typename DT::abstime_type,
160 typename DataTypes::abstime_type>);
161 static_assert(std::is_same_v<typename DT::channel_type,
162 typename DataTypes::channel_type>);
163 auto const difftime =
164 subtract_with_check(event[1].abstime, event[0].abstime);
165 auto const abstime =
166 event[0].abstime +
167 static_cast<typename DataTypes::abstime_type>(
168 std::llround(static_cast<double>(difftime) * frac));
169 auto const channel =
170 UseStartChannel ? event[0].channel : event[1].channel;
171 downstream.handle(time_correlated_detection_event<DataTypes>{
172 abstime, channel,
173 convert_with_check<typename DataTypes::difftime_type>(difftime)});
174 }
175
176 template <typename DT>
177 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
178 void handle(std::array<detection_event<DT>, 2> &&event) {
179 handle(static_cast<std::array<detection_event<DT>, 2> const &>(event));
180 }
181
182 template <typename OtherEvent>
183 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
184 void handle(OtherEvent &&event) {
185 downstream.handle(std::forward<OtherEvent>(event));
186 }
187
188 void flush() { downstream.flush(); }
189};
190
191} // namespace internal
192
225template <typename DataTypes = default_data_types, typename Downstream>
226auto time_correlate_at_start(Downstream downstream) {
227 return internal::time_correlate_at_start_or_stop<DataTypes, true,
228 Downstream>(
229 std::move(downstream));
230}
231
263template <typename DataTypes = default_data_types, typename Downstream>
264auto time_correlate_at_stop(Downstream downstream) {
265 return internal::time_correlate_at_start_or_stop<DataTypes, false,
266 Downstream>(
267 std::move(downstream));
268}
269
308template <typename DataTypes = default_data_types,
309 bool UseStartChannel = false, typename Downstream>
310auto time_correlate_at_midpoint(Downstream downstream) {
311 return internal::time_correlate_at_midpoint<DataTypes, UseStartChannel,
312 Downstream>(
313 std::move(downstream));
314}
315
357template <typename DataTypes = default_data_types,
358 bool UseStartChannel = false, typename Downstream>
360 Downstream downstream) {
361 return internal::time_correlate_at_fraction<DataTypes, UseStartChannel,
362 Downstream>(
363 fraction, std::move(downstream));
364}
365
366namespace internal {
367
368template <typename DataTypes, typename Downstream> class negate_difftime {
369 static_assert(
370 processor<Downstream, time_correlated_detection_event<DataTypes>>);
371
372 Downstream downstream;
373
374 public:
375 explicit negate_difftime(Downstream downstream)
376 : downstream(std::move(downstream)) {}
377
378 [[nodiscard]] auto introspect_node() const -> processor_info {
379 return processor_info(this, "negate_difftime");
380 }
381
382 [[nodiscard]] auto introspect_graph() const -> processor_graph {
383 return downstream.introspect_graph().push_entry_point(this);
384 }
385
386 template <typename DT>
387 void handle(time_correlated_detection_event<DT> const &event) {
388 static_assert(std::is_same_v<typename DT::difftime_type,
389 typename DataTypes::difftime_type>);
390 static_assert(
391 std::is_signed_v<typename DT::difftime_type>,
392 "difftime_type of time_correlated_detection_event used with negate_difftime must be a signed integer type");
393 time_correlated_detection_event<DT> copy(event);
394 copy.difftime =
395 static_cast<typename DT::difftime_type>(-event.difftime);
396 downstream.handle(std::move(copy));
397 }
398
399 template <typename DT>
400 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
401 void handle(time_correlated_detection_event<DT> &&event) {
402 handle(
403 static_cast<time_correlated_detection_event<DT> 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
415template <typename DataTypes, typename Downstream>
416class remove_time_correlation {
417 static_assert(processor<Downstream, detection_event<DataTypes>>);
418
419 Downstream downstream;
420
421 public:
422 explicit remove_time_correlation(Downstream downstream)
423 : downstream(std::move(downstream)) {}
424
425 [[nodiscard]] auto introspect_node() const -> processor_info {
426 return processor_info(this, "remove_time_correlation");
427 }
428
429 [[nodiscard]] auto introspect_graph() const -> processor_graph {
430 return downstream.introspect_graph().push_entry_point(this);
431 }
432
433 template <typename DT>
434 void handle(time_correlated_detection_event<DT> const &event) {
435 static_assert(std::is_same_v<typename DT::abstime_type,
436 typename DataTypes::abstime_type>);
437 static_assert(std::is_same_v<typename DT::channel_type,
438 typename DataTypes::channel_type>);
439
440 downstream.handle(
441 detection_event<DataTypes>{event.abstime, event.channel});
442 }
443
444 template <typename DT>
445 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
446 void handle(time_correlated_detection_event<DT> &&event) {
447 handle(
448 static_cast<time_correlated_detection_event<DT> const &>(event));
449 }
450
451 template <typename OtherEvent>
452 requires handler_for<Downstream, std::remove_cvref_t<OtherEvent>>
453 void handle(OtherEvent &&event) {
454 downstream.handle(std::forward<OtherEvent>(event));
455 }
456
457 void flush() { downstream.flush(); }
458};
459
460} // namespace internal
461
483template <typename DataTypes = default_data_types, typename Downstream>
484auto negate_difftime(Downstream downstream) {
485 return internal::negate_difftime<DataTypes, Downstream>(
486 std::move(downstream));
487}
488
508template <typename DataTypes = default_data_types, typename Downstream>
509auto remove_time_correlation(Downstream downstream) {
510 return internal::remove_time_correlation<DataTypes, Downstream>(
511 std::move(downstream));
512}
513
514} // 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:359
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:226
auto negate_difftime(Downstream downstream)
Create a processor that changes the sign of difftime in time-correlated detection events.
Definition time_correlate.hpp:484
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:310
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:264
auto remove_time_correlation(Downstream downstream)
Create a processor that removes the difftime from detection events.
Definition time_correlate.hpp:509
libtcspc namespace.
Definition acquire.hpp:29
Function argument wrapper for fraction parameter.
Definition arg_wrappers.hpp:137