LDMX Software
MultiSampleECONDEventPacket.cxx
1#include "Packing/MultiSampleECONDEventPacket.h"
2
3#include <algorithm>
4
5#include "Packing/Utility/Hex.h"
6
8
9namespace packing {
10
12 : n_links_{n_links} {}
13
17
19 return std::any_of(corruption_.begin(), corruption_.end(),
20 [](bool c) { return c; });
21}
22
23std::size_t MultiSampleECONDEventPacket::resync(std::span<const uint32_t> frame,
24 std::size_t start,
25 std::size_t i_sample) const {
26 DAQSampleHeader header;
27 for (std::size_t i{start}; i < frame.size(); i++) {
28 header.from(frame[i]);
29 if (header.version_ == DAQSampleHeader::VERSION and
30 header.isEndingTrailer()) {
31 return i;
32 }
33 if (DAQSampleHeader::isValid(frame[i]) and header.i_l1a_ == i_sample and
34 (econd_id_ < 0 or
35 header.econd_id_ == static_cast<uint32_t>(econd_id_))) {
36 return i;
37 }
38 }
39 return frame.size();
40}
41
42void MultiSampleECONDEventPacket::from(std::span<const uint32_t> frame) {
43 corruption_.fill(false);
44 i_soi_ = 0;
45 econd_id_ = -1;
48 headers_.clear();
49 samples_.clear();
50
51 bool found_soi{false};
52 std::size_t offset{0};
53 DAQSampleHeader header;
54 while (offset < frame.size()) {
55 header.from(frame[offset]);
56 if (header.version_ == DAQSampleHeader::VERSION and
57 header.isEndingTrailer()) {
59 ldmx_log(trace) << "Last sample packet found at offset = " << offset
60 << " (frame.size() = " << frame.size() << ")";
61 offset++;
62 break;
63 }
64
65 bool valid =
66 DAQSampleHeader::isValid(frame[offset]) and
67 (econd_id_ < 0 or header.econd_id_ == static_cast<uint32_t>(econd_id_));
68 if (not valid) {
69 if (DAQSampleHeader::isValid(frame[offset])) {
71 }
72 auto next = resync(frame, offset + 1, samples_.size());
73 ldmx_log(debug) << Hex(frame[offset])
74 << " is not a capture header, skipping "
75 << (next - offset) << " words";
76 corruption_[RESYNC] = true;
77 n_skipped_words_ += next - offset;
78 if (next == frame.size()) {
79 corruption_[LOST_SYNC] = true;
80 }
81 offset = next;
82 continue;
83 }
84
85 if (econd_id_ < 0) econd_id_ = header.econd_id_;
86 ldmx_log(trace) << Hex(frame[offset]) << " -> " << header;
87
88 // header decoded, shift offset
89 offset++;
90
91 std::size_t i_sample{samples_.size()};
92 if (i_sample != header.i_l1a_) {
93 ldmx_log(debug) << "mismatch between transmitted index " << header.i_l1a_
94 << " and unpacking index for sample " << i_sample;
96 }
97
98 if (header.is_soi_ and not found_soi) {
99 i_soi_ = i_sample;
100 found_soi = true;
101 }
102
103 std::size_t len{header.econd_len_};
104 if (offset + len > frame.size()) {
105 ldmx_log(debug) << "capture header length " << len << " goes past the "
106 << "end of the frame by " << (offset + len - frame.size())
107 << " words";
109 len = frame.size() - offset;
110 }
111
112 headers_.push_back(header);
113 samples_.emplace_back(n_links_);
114 samples_.back().from(frame.subspan(offset, len));
115 offset += len;
116 }
117
118 if (trailer_n_samples_ < 0) {
119 corruption_[NO_TRAILER] = true;
120 } else if (static_cast<std::size_t>(trailer_n_samples_) != samples_.size()) {
121 ldmx_log(debug) << "trailer says " << trailer_n_samples_
122 << " samples but found " << samples_.size();
124 }
125
126 // only zero padding is expected after the trailer
127 if (std::any_of(frame.begin() + offset, frame.end(),
128 [](uint32_t w) { return w != 0; })) {
129 ldmx_log(debug) << "non-zero words after the ending trailer";
130 corruption_[EXTRA_WORDS] = true;
131 }
132
133 if (not samples_.empty() and not found_soi) {
134 corruption_[NO_SOI] = true;
135 }
136}
137
138} // namespace packing
Single ECON-D event packet.
const ECONDEventPacket & soi() const
get the sample of interest
int n_links_
number of links connected to the ECOND
int trailer_n_samples_
number of samples written in the trailer (-1 if no trailer)
std::size_t resync(std::span< const uint32_t > frame, std::size_t start, std::size_t i_sample) const
find the next capture header for sample i_sample or the trailer
std::size_t i_soi_
index of the sample of interest (SOI)
bool isCorrupt() const
any of the frame-level corruption flags are set
MultiSampleECONDEventPacket(int n_links)
constructor defining how many links are connected to this ECOND
int econd_id_
ID for this econ from the first capture header (-1 if none)
void from(std::span< const uint32_t > frame)
unpack the given frame into this structure
std::vector< ECONDEventPacket > samples_
samples from ECOND stored in order of transmission
@ NO_TRAILER
the frame ended without the ending trailer
@ RESYNC
a capture header was missing, words were skipped to find the next
@ NO_SOI
no sample flagged as the sample of interest
@ TRUNCATED_FRAME
capture header length goes past the end of the frame
@ LOST_SYNC
no further capture header or trailer found, rest of frame dropped
@ SAMPLE_INDEX_MISMATCH
sample index in capture header does not match its position
@ TRAILER_COUNT_MISMATCH
number of samples in the trailer does not match the number found
@ EXTRA_WORDS
non-zero words after the ending trailer
@ ECON_ID_MISMATCH
ECON ID changed within the frame.
std::vector< DAQSampleHeader > headers_
capture headers stored in order of transmission
std::size_t n_skipped_words_
number of words skipped while searching for capture headers
std::array< bool, N_CORRUPTION > corruption_
corruption flags indexed by Corruption
The capture header that the DAQ firmware inserts before each ECON-D event packet (one per sample) and...
uint32_t version_
format version
bool is_soi_
is this the sample of interest?
bool isEndingTrailer() const
A special form of this DAQ header is used to signal the end of a multi-sample sequence.
uint32_t i_l1a_
index of this sample within the event
void from(uint32_t word)
decode a DAQSampleHeader from the input word
uint32_t econd_id_
configured ID of the ECON
static const uint32_t VERSION
expected format version
uint32_t econd_len_
length of the ECON-D packet following this header in 32b words
static bool isValid(uint32_t word)
check if the decoded word looks like a capture header
static uint32_t trailerNumSamples(uint32_t word)
number of samples written in the ending trailer