libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
picoquant_t2.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 "common.hpp"
10#include "core.hpp"
11#include "data_types.hpp"
12#include "int_arith.hpp"
13#include "int_types.hpp"
14#include "introspect.hpp"
15#include "npint.hpp"
16#include "npint_ops.hpp"
17#include "processor.hpp"
18#include "read_integers.hpp"
19#include "time_tagged_events.hpp"
20
21#include <array>
22#include <cstddef>
23#include <ostream>
24#include <span>
25#include <stdexcept>
26#include <type_traits>
27#include <utility>
28
29// When editing this file, maintain the partial symmetry with picoquant_t3.hpp.
30
31namespace tcspc {
32
33// PicoQuant raw time tag event ("TTTR") formats are documented in the html
34// files contained in this repository:
35// https://github.com/PicoQuant/PicoQuant-Time-Tagged-File-Format-Demos
36
37// Vendor documentation does not specify, but the 32-bit records are to be
38// viewed as little-endian integers when interpreting the documented bit
39// locations.
40
41// The two T2 formats (pqt2_picoharp300_event and basic_pqt2_event) use
42// matching member names for static polymorphism. This allows
43// decode_pqt2<PQT2Event> to handle 3 different formats with the same code.
44
56 std::array<std::byte, 4> bytes;
57
64 static constexpr i32 overflow_period = 210698240;
65
69 [[nodiscard]] constexpr auto channel() const noexcept -> u8np {
70 return read_u8_at<3>(std::span(bytes)) >> 4;
71 }
72
77 [[nodiscard]] constexpr auto timetag() const noexcept -> u32np {
78 return read_u32le_at<0>(std::span(bytes)) & 0x0fff'ffff_u32np;
79 }
80
86 [[nodiscard]] constexpr auto external_marker_timetag() const noexcept
87 -> u32np {
88 // For markers, the low 4 bits of the time tag are used to store the
89 // marker bits, giving markers 1/16 the time resolution (the actual
90 // time resolution for markers is even lower, in the 10s of ns range).
91 // Avoid leaving the marker bits in the timestamp.
92 return timetag() & ~0x0f_u32np;
93 }
94
98 [[nodiscard]] constexpr auto is_special() const noexcept -> bool {
99 return channel() == 15_u8np;
100 }
101
105 [[nodiscard]] constexpr auto is_timetag_overflow() const noexcept -> bool {
106 return is_special() && (timetag() & 0x0f_u32np) == 0_u32np;
107 }
108
113 [[nodiscard]] static constexpr auto timetag_overflow_count() noexcept
114 -> u32np {
115 return 1_u32np;
116 }
117
121 [[nodiscard]] static constexpr auto is_sync_event() noexcept -> bool {
122 return false;
123 }
124
128 [[nodiscard]] constexpr auto is_external_marker() const noexcept -> bool {
129 return is_special() && (timetag() & 0x0f_u32np) != 0_u32np;
130 }
131
136 [[nodiscard]] constexpr auto external_marker_bits() const noexcept
137 -> u8np {
138 return u8np(timetag()) & 0x0f_u8np;
139 }
140
150 static constexpr auto make_nonspecial(u32np timetag, u8np channel)
152 if (channel > 14_u8np)
153 throw std::invalid_argument(
154 "pqt2_picoharp300_event channel must be in the range 0-14");
155 return make_from_fields(channel, timetag);
156 }
157
163 static constexpr auto make_timetag_overflow() noexcept
165 return make_from_fields(15_u8np, 0_u32np);
166 }
167
178 static constexpr auto make_external_marker(u32np timetag, u8np marker_bits)
180 if (marker_bits == 0_u8np)
181 throw std::invalid_argument(
182 "pqt2_picoharp300_event marker_bits must not be zero");
183 return make_from_fields(15_u8np,
184 (timetag & ~0x0f_u32np) |
185 (u32np(marker_bits) & 0x0f_u32np));
186 }
187
189 friend auto operator==(pqt2_picoharp300_event const &lhs,
190 pqt2_picoharp300_event const &rhs) noexcept
191 -> bool = default;
192
194 friend auto operator<<(std::ostream &stream,
195 pqt2_picoharp300_event const &event)
196 -> std::ostream & {
197 return stream << "pqt2_picoharp(channel="
198 << unsigned(event.channel().value())
199 << ", timetag=" << event.timetag() << ")";
200 }
201
202 private:
203 static constexpr auto make_from_fields(u8np channel, u32np timetag)
206 std::byte(u8np(timetag).value()),
207 std::byte(u8np(timetag >> 8).value()),
208 std::byte(u8np(timetag >> 16).value()),
209 std::byte(
210 ((channel << 4) | (u8np(timetag >> 24) & 0x0f_u8np)).value()),
211 }};
212 }
213};
214
229template <i32 OverflowPeriod, bool IsOverflowAlwaysSingle>
234 std::array<std::byte, 4> bytes;
235
242 static constexpr i32 overflow_period = OverflowPeriod;
243
247 [[nodiscard]] constexpr auto channel() const noexcept -> u8np {
248 return (read_u8_at<3>(std::span(bytes)) & 0x7f_u8np) >> 1;
249 }
250
254 [[nodiscard]] constexpr auto timetag() const noexcept -> u32np {
255 return read_u32le_at<0>(std::span(bytes)) & 0x01ff'ffff_u32np;
256 }
257
261 [[nodiscard]] constexpr auto external_marker_timetag() const noexcept
262 -> u32np {
263 return timetag();
264 }
265
269 [[nodiscard]] constexpr auto is_special() const noexcept -> bool {
270 return (read_u8_at<3>(std::span(bytes)) & (1_u8np << 7)) != 0_u8np;
271 }
272
276 [[nodiscard]] constexpr auto is_timetag_overflow() const noexcept -> bool {
277 return is_special() && channel() == 63_u8np;
278 }
279
284 [[nodiscard]] constexpr auto timetag_overflow_count() const noexcept
285 -> u32np {
286 if (IsOverflowAlwaysSingle)
287 return 1_u32np;
288 return timetag();
289 }
290
294 [[nodiscard]] constexpr auto is_sync_event() const noexcept -> bool {
295 return is_special() && channel() == 0_u8np;
296 }
297
301 [[nodiscard]] constexpr auto is_external_marker() const noexcept -> bool {
302 return is_special() && channel() > 0_u8np && channel() <= 15_u8np;
303 }
304
309 [[nodiscard]] constexpr auto external_marker_bits() const noexcept
310 -> u8np {
311 return channel();
312 }
313
323 static constexpr auto make_nonspecial(u32np timetag, u8np channel)
325 return make_from_fields(false, channel, timetag);
326 }
327
338 static constexpr auto make_timetag_overflow(u32np count)
340 static_assert(
341 not IsOverflowAlwaysSingle,
342 "multiple time tag overflow is not available in HydraHarp V1 format");
343 return make_from_fields(true, 63_u8np, count);
344 }
345
351 static constexpr auto make_timetag_overflow() noexcept
353 return make_from_fields(true, 63_u8np, 1_u32np);
354 }
355
363 static constexpr auto make_sync(u32np timetag) noexcept
365 return make_from_fields(true, 0_u8np, timetag);
366 }
367
377 static constexpr auto make_external_marker(u32np timetag, u8np marker_bits)
379 if (marker_bits == 0_u8np || (marker_bits & ~0x0f_u8np) != 0_u8np)
380 throw std::invalid_argument(
381 "basic_pqt2_event marker_bits must be in range 1-15");
382 return make_from_fields(true, marker_bits & 0x3f_u8np, timetag);
383 }
384
386 friend auto operator==(basic_pqt2_event const &lhs,
387 basic_pqt2_event const &rhs) noexcept
388 -> bool = default;
389
391 friend auto operator<<(std::ostream &stream, basic_pqt2_event const &event)
392 -> std::ostream & {
393 static constexpr auto version = IsOverflowAlwaysSingle ? 1 : 2;
394 return stream << "pqt2_hydraharpv" << version
395 << "(special=" << event.is_special()
396 << ", channel=" << unsigned(event.channel().value())
397 << ", timetag=" << event.timetag() << ")";
398 }
399
400 private:
401 static constexpr auto make_from_fields(bool special, u8np channel,
403 return basic_pqt2_event{{
404 std::byte(u8np(timetag).value()),
405 std::byte(u8np(timetag >> 8).value()),
406 std::byte(u8np(timetag >> 16).value()),
407 std::byte(((u8np(u8(special)) << 7) |
408 ((channel & 0x3f_u8np) << 1) |
409 (u8np(timetag >> 24) & 0x01_u8np))
410 .value()),
411 }};
412 }
413};
414
423
433
434namespace internal {
435
436// Common implementation for decode_pqt2_*.
437// PQT2Event is the binary record event class.
438template <typename DataTypes, typename PQT2Event, typename Downstream>
439class decode_pqt2 {
443
444 // 32-bit abstime can work for a few seconds, though 64-bit is recommended.
445 static_assert(sizeof(typename DataTypes::abstime_type) >= 4);
446 static_assert(std::in_range<typename DataTypes::channel_type>(63));
447
448 using abstime_type = typename DataTypes::abstime_type;
449
450 abstime_type timetag_base = 0;
451
452 Downstream downstream;
453
454 LIBTCSPC_NOINLINE void issue_warning(char const *message) {
455 downstream.handle(warning_event{message});
456 }
457
458 public:
459 explicit decode_pqt2(Downstream downstream)
460 : downstream(std::move(downstream)) {}
461
462 [[nodiscard]] auto introspect_node() const -> processor_info {
463 return processor_info(this, "decode_pqt2");
464 }
465
466 [[nodiscard]] auto introspect_graph() const -> processor_graph {
467 return downstream.introspect_graph().push_entry_point(this);
468 }
469
470 template <typename Event>
471 requires std::convertible_to<std::remove_cvref_t<Event>, PQT2Event>
472 void handle(Event &&event) {
473 if (event.is_timetag_overflow()) {
474 timetag_base += abstime_type(PQT2Event::overflow_period) *
475 event.timetag_overflow_count().value();
476 return downstream.handle(
477 time_reached_event<DataTypes>{timetag_base});
478 }
479
480 // In the case where the overflow period is smaller than one plus
481 // the maximum representable time tag (PicoHarp 300 and HydraHarp
482 // V1), any invalid time tags will be caught when (externally)
483 // checking for monotonicity. So we do not check here.
484
485 if (not event.is_special() || event.is_sync_event()) {
486 abstime_type const timetag =
487 timetag_base + event.timetag().value();
488 downstream.handle(detection_event<DataTypes>{
489 timetag, event.is_special() ? -1 : event.channel().value()});
490 } else if (event.is_external_marker()) {
491 abstime_type const timetag =
492 timetag_base + event.external_marker_timetag().value();
493 for_each_set_bit(u32np(event.external_marker_bits()), [&](int b) {
494 downstream.handle(marker_event<DataTypes>{timetag, b});
495 });
496 } else {
497 issue_warning("invalid special event encountered");
498 }
499 }
500
501 template <typename Event>
502 requires(
503 not std::convertible_to<std::remove_cvref_t<Event>, PQT2Event> and
505 void handle(Event &&event) {
506 downstream.handle(std::forward<Event>(event));
507 }
508
509 void flush() { downstream.flush(); }
510};
511
512} // namespace internal
513
536template <typename DataTypes = default_data_types, typename Downstream>
537auto decode_pqt2_picoharp300(Downstream downstream) {
538 return internal::decode_pqt2<DataTypes, pqt2_picoharp300_event,
539 Downstream>(std::move(downstream));
540}
541
567template <typename DataTypes = default_data_types, typename Downstream>
568auto decode_pqt2_hydraharpv1(Downstream downstream) {
569 return internal::decode_pqt2<DataTypes, pqt2_hydraharpv1_event,
570 Downstream>(std::move(downstream));
571}
572
599template <typename DataTypes = default_data_types, typename Downstream>
600auto decode_pqt2_generic(Downstream downstream) {
601 return internal::decode_pqt2<DataTypes, pqt2_generic_event, Downstream>(
602 std::move(downstream));
603}
604
605} // namespace tcspc
Value type representing a directed acyclic graph of processors.
Definition introspect.hpp:198
Value type representing metadata of a processor.
Definition introspect.hpp:58
Concept that is satisfied when a processor handles the given event types.
Definition processor.hpp:118
Concept that is satisfied when a processor handles the given event types and flush.
Definition processor.hpp:143
basic_pqt2_event< 33554432, false > pqt2_generic_event
Binary record interpretation for HydraHarp V2, MultiHarp, TimeHarp 260, and PicoHarp 330 "Generic" T2...
Definition picoquant_t2.hpp:432
basic_pqt2_event< 33552000, true > pqt2_hydraharpv1_event
Binary record interpretation for HydraHarp V1 T2 format.
Definition picoquant_t2.hpp:422
npint< u32 > u32np
Non-promoted unsigned 32-bit integer.
Definition npint.hpp:306
constexpr auto read_u8_at(std::span< T, N > bytes) noexcept -> u8np
Read an 8-bit unsigned integer from bytes at offset.
Definition read_integers.hpp:77
constexpr auto read_u32le_at(std::span< T, N > bytes) noexcept -> u32np
Read a little-endian 32-bit unsigned integer from bytes at offset.
Definition read_integers.hpp:102
npint< u8 > u8np
Non-promoted unsigned 8-bit integer.
Definition npint.hpp:292
auto decode_pqt2_hydraharpv1(Downstream downstream)
Create a processor that decodes PicoQuant HydraHarp V1 T2 events.
Definition picoquant_t2.hpp:568
auto decode_pqt2_generic(Downstream downstream)
Create a processor that decodes PicoQuant HydraHarp V2, MultiHarp, TimeHarp 260, and PicoHarp 330 "Ge...
Definition picoquant_t2.hpp:600
auto decode_pqt2_picoharp300(Downstream downstream)
Create a processor that decodes PicoQuant PicoHarp 300 T2 events.
Definition picoquant_t2.hpp:537
auto count(access_tracker< count_access > &&tracker, Downstream downstream)
Create a processor that counts events of a given type.
Definition count.hpp:313
std::int32_t i32
Short name for int32_t.
Definition int_types.hpp:42
std::uint8_t u8
Short name for uint8_t.
Definition int_types.hpp:24
libtcspc namespace.
Definition acquire.hpp:29
Implementation for binary record interpretation for HydraHarp, MultiHarp, TimeHarp 260,...
Definition picoquant_t2.hpp:230
static constexpr auto make_nonspecial(u32np timetag, u8np channel) -> basic_pqt2_event
Make an event representing a non-special (photon) event.
Definition picoquant_t2.hpp:323
constexpr auto is_timetag_overflow() const noexcept -> bool
Determine if this event represents a time tag overflow.
Definition picoquant_t2.hpp:276
static constexpr auto make_external_marker(u32np timetag, u8np marker_bits) -> basic_pqt2_event
Make an event representing an external marker.
Definition picoquant_t2.hpp:377
friend auto operator<<(std::ostream &stream, basic_pqt2_event const &event) -> std::ostream &
Stream insertion operator.
Definition picoquant_t2.hpp:391
std::array< std::byte, 4 > bytes
Definition picoquant_t2.hpp:234
friend auto operator==(basic_pqt2_event const &lhs, basic_pqt2_event const &rhs) noexcept -> bool=default
Equality comparison operator.
constexpr auto timetag_overflow_count() const noexcept -> u32np
Read the time tag overflow count if this event represents a time tag overflow.
Definition picoquant_t2.hpp:284
constexpr auto timetag() const noexcept -> u32np
Read the time tag if this event is a non-special event.
Definition picoquant_t2.hpp:254
constexpr auto is_external_marker() const noexcept -> bool
Determine if this event represents external markers.
Definition picoquant_t2.hpp:301
constexpr auto external_marker_timetag() const noexcept -> u32np
Read the time tag if this event is an external marker event.
Definition picoquant_t2.hpp:261
static constexpr i32 overflow_period
Definition picoquant_t2.hpp:242
static constexpr auto make_sync(u32np timetag) noexcept -> basic_pqt2_event
Make an event representing a sync event.
Definition picoquant_t2.hpp:363
constexpr auto is_special() const noexcept -> bool
Determine if this event is a special event.
Definition picoquant_t2.hpp:269
constexpr auto external_marker_bits() const noexcept -> u8np
Read the marker bits (mask) if this event represents external markers.
Definition picoquant_t2.hpp:309
constexpr auto is_sync_event() const noexcept -> bool
Determine if this event represents a sync event.
Definition picoquant_t2.hpp:294
constexpr auto channel() const noexcept -> u8np
Read the channel if this event is a non-special event.
Definition picoquant_t2.hpp:247
static constexpr auto make_timetag_overflow(u32np count) -> basic_pqt2_event
Make an event representing a time tag overflow.
Definition picoquant_t2.hpp:338
static constexpr auto make_timetag_overflow() noexcept -> basic_pqt2_event
Make an event representing a single time tag overflow.
Definition picoquant_t2.hpp:351
Event indicating a detected count.
Definition time_tagged_events.hpp:236
Event indicating a timing marker.
Definition time_tagged_events.hpp:320
Binary record interpretation for PicoHarp 300 T2 Format.
Definition picoquant_t2.hpp:52
constexpr auto is_special() const noexcept -> bool
Determine if this event is a special event.
Definition picoquant_t2.hpp:98
static constexpr i32 overflow_period
The time tag overflow period of this event type.
Definition picoquant_t2.hpp:64
static constexpr auto is_sync_event() noexcept -> bool
Determine if this event represents a sync event.
Definition picoquant_t2.hpp:121
static constexpr auto timetag_overflow_count() noexcept -> u32np
Read the time tag overflow count if this event represents a time tag overflow.
Definition picoquant_t2.hpp:113
constexpr auto external_marker_bits() const noexcept -> u8np
Read the marker bits (mask) if this event represents external markers.
Definition picoquant_t2.hpp:136
static constexpr auto make_timetag_overflow() noexcept -> pqt2_picoharp300_event
Make an event representing a time tag overflow.
Definition picoquant_t2.hpp:163
static constexpr auto make_external_marker(u32np timetag, u8np marker_bits) -> pqt2_picoharp300_event
Make an event representing an external marker.
Definition picoquant_t2.hpp:178
constexpr auto external_marker_timetag() const noexcept -> u32np
Read the time tag if this event is an external marker event.
Definition picoquant_t2.hpp:86
friend auto operator<<(std::ostream &stream, pqt2_picoharp300_event const &event) -> std::ostream &
Stream insertion operator.
Definition picoquant_t2.hpp:194
constexpr auto is_timetag_overflow() const noexcept -> bool
Determine if this event represents a time tag overflow.
Definition picoquant_t2.hpp:105
constexpr auto channel() const noexcept -> u8np
Read the channel if this event is a non-special event.
Definition picoquant_t2.hpp:69
constexpr auto timetag() const noexcept -> u32np
Read the time tag if this event is a non-special event (not external marker event).
Definition picoquant_t2.hpp:77
constexpr auto is_external_marker() const noexcept -> bool
Determine if this event represents external markers.
Definition picoquant_t2.hpp:128
friend auto operator==(pqt2_picoharp300_event const &lhs, pqt2_picoharp300_event const &rhs) noexcept -> bool=default
Equality comparison operator.
std::array< std::byte, 4 > bytes
Bytes of the 32-bit raw device event.
Definition picoquant_t2.hpp:56
static constexpr auto make_nonspecial(u32np timetag, u8np channel) -> pqt2_picoharp300_event
Make an event representing a non-special (photon) event.
Definition picoquant_t2.hpp:150
Event indicating latest abstime reached.
Definition time_tagged_events.hpp:41
An event type indicating a warning.
Definition core.hpp:31