LDMX Software
QIEDecoder.cxx
1#include "TrigScint/QIEDecoder.h"
2
3#include <TMath.h>
4#include <TTimeStamp.h>
5
6#include <bitset>
7#include <iostream>
8
9#include "TrigScint/Event/QIEStream.h"
10#include "TrigScint/Event/TrigScintQIEDigis.h"
11
12namespace trigscint {
13
15 // Configure this instance of the encoder
16 output_collection_ = ps.get<std::string>("output_collection");
17 input_collection_ = ps.get<std::string>("input_collection");
18 input_pass_name_ = ps.get<std::string>("input_pass_name");
19 channel_map_file_name_ = ps.get<std::string>("channel_map_file");
20 n_channels_ = ps.get<int>("number_channels");
21 n_samples_ = ps.get<int>("number_time_samples");
22 is_real_data_ = ps.get<bool>("is_real_data");
23
24 ldmx_log(debug) << "In configure, got parameters:" << "\noutput_collection = "
25 << output_collection_
26 << "\ninput_collection = " << input_collection_
27 << "\ninput_pass_name = " << input_pass_name_
28 << "\nchannel_map_file = " << channel_map_file_name_
29 << "\nnumber_channels = " << n_channels_
30 << "\nnumber_time_samples = " << n_samples_
31 << "\nis_real_data = " << is_real_data_;
32
33 channel_map_file_.open(channel_map_file_name_, std::ios::in);
34 if (!channel_map_file_.is_open()) {
35 EXCEPTION_RAISE(
36 "BadMapFile",
37 "The channel mapping file cannot be opened."); // <-- appears this
38 // needs implementing
39 // first
40 ldmx_log(fatal) << "The channel mapping file cannot be opened.";
41 return;
42 }
43 int ch_id, el_id;
44 while (!channel_map_file_.eof()) {
45 channel_map_file_ >> el_id >> ch_id;
46 // make the map based on electronics ID, to look up channel ID.
47 // the reason is, we will only know the elecID from the position
48 // of the word in the stream. so these need to be strictly ordered.
49 // writing the right bar in the right position is easier if we can just
50 // read this map from beginning to end.
51 // barID can always be set, or looked up, as a property of the digi.
52
53 // here make the elecID the key (other way around when encoding)
54 channel_map_.insert(std::pair<int, int>(el_id, ch_id));
55 ldmx_log(debug) << el_id << " chID " << ch_id;
56 }
57 channel_map_file_.close();
58 if (el_id != n_channels_ - 1)
59 ldmx_log(fatal) << "The set number of channels " << n_channels_
60 << " seems not to match the number from the map (+1) :"
61 << el_id;
62 return;
63}
64
66 ldmx_log(debug) << "QIEDecoder: produce() starts! Event number: "
67 << event.getEventHeader().getEventNumber();
68
69 // turns out this need to be configurable for now, to read real data
70 int n_samp = n_samples_; // QIEStream::NUM_SAMPLES ;
71 ldmx_log(debug) << "num samples = " << n_samp;
72
73 ldmx_log(debug) << "Looking up input collection " << input_collection_ << "_"
74 << input_pass_name_;
75 const auto event_stream{
76 event.getCollection<uint8_t>(input_collection_, input_pass_name_)};
77 ldmx_log(debug) << "Got input collection" << input_collection_ << "_"
78 << input_pass_name_;
79
80 uint32_t time_epoch = 0;
81 // these don't have to be in any particular order, position is anyway looked
82 // up from definition in header
83 for (int i_w = 0; i_w < QIEStream::TIMESTAMP_LEN_BYTES; i_w++) {
84 int pos = QIEStream::TIMESTAMP_POS + i_w;
85 uint8_t time_word = event_stream.at(pos);
86 ldmx_log(debug) << "time stamp word at position " << pos
87 << " (with iW = " << i_w
88 << ") = " << std::bitset<8>(time_word);
89 time_epoch |= (time_word << i_w * 8); // shift by a byte at a time
90 }
91
92 uint32_t time_clock = 0;
93 for (int i_w = 0; i_w < QIEStream::TIMESTAMPCLOCK_LEN_BYTES; i_w++) {
94 int pos = QIEStream::TIMESTAMPCLOCK_POS + i_w;
95 uint8_t time_word = event_stream.at(pos);
96 ldmx_log(debug) << "time stamp ns word at position " << pos
97 << " (with iW = " << i_w
98 << ") = " << std::bitset<8>(time_word);
99 time_clock |= (time_word << i_w * 8); // shift by a byte at a time
100 }
101
102 uint32_t time_spill = 0;
103 ldmx_log(debug) << "Before starting, timeSpill = " << time_spill << " ("
104 << std::bitset<64>(time_spill) << ", or, " << std::hex
105 << time_spill << std::dec << ") counts since start of spill";
106
107 for (int i_w = 0; i_w < QIEStream::TIMESINCESPILL_LEN_BYTES; i_w++) {
108 int pos = QIEStream::TIMESINCESPILL_POS + i_w;
109 uint8_t time_word = event_stream.at(pos);
110 ldmx_log(debug) << "time since spill word at position " << pos
111 << " (with iW = " << i_w
112 << ") = " << std::bitset<8>(time_word);
113 time_spill |= (time_word << i_w * 8); // shift by a byte at a time
114 }
115 ldmx_log(debug) << "time stamp words are : " << time_epoch << " ("
116 << std::bitset<64>(time_epoch) << ") and " << time_clock
117 << " (" << std::bitset<64>(time_clock)
118 << ") clock ticks, and " << time_spill << " ("
119 << std::bitset<64>(time_spill) << ", or, " << std::hex
120 << time_spill << std::dec << ") counts since start of spill";
121
122 int sig_bits_skip = 6; // the first 6 bits are part of something else.
123 int divisor = TMath::Power(2, 32 - sig_bits_skip);
124 // remove them by taking remainder in division by
125 // the values of the last skipped bit
126 time_spill = time_spill % divisor;
127 ldmx_log(debug) << "After taking it mod 2^" << 32 - sig_bits_skip
128 << " (which is " << divisor << ", spill time is "
129 << time_spill;
130 event.getEventHeader().setIntParameter("timeSinceSpill", time_spill);
131
132 TTimeStamp* time_stamp = new TTimeStamp(time_epoch);
133 event.getEventHeader().setTimestamp(*time_stamp);
134
135 // trigger ID event number
136 uint32_t trigger_id = 0;
137
138 for (int i_w = 0; i_w < QIEStream::TRIGID_LEN_BYTES; i_w++) {
139 // assume the whole 3B are written as a single 24 bits word
140 int pos = QIEStream::TRIGID_POS + i_w;
141 uint8_t t_i_dword = event_stream.at(pos);
142 ldmx_log(debug) << "trigger word at position " << pos
143 << " (with iW = " << i_w
144 << ") = " << std::bitset<8>(t_i_dword);
145 trigger_id |= (t_i_dword << i_w * 8); // shift by a byte at a time
146 }
147
148 // ldmx_log(debug) << " got triggerID " << std::bitset<16>(triggerID) ;
149 ldmx_log(debug) << " got triggerID " << std::bitset<32>(trigger_id);
150
151 if (trigger_id != event.getEventHeader().getEventNumber()) {
152 // this probably only applies to digi emulation,
153 // unless an event number is explicitly set in unpacking
154 ldmx_log(fatal) << "Got event number mismatch: framework reports "
155 << event.getEventHeader().getEventNumber()
156 << ", stream says " << trigger_id;
157 }
158
159 // error word
160 /* the error word contains
161 - 4 trailing reserved 0's, for now
162 - isCIDunsync : if there is a mismatch between CID reported by channels
163 within the same time sample
164 - isCIDskipped : if there is a gap in the CID increment of a channel
165 beweeen samples
166 - isCRC0malformed : if there was an issue with CRC from fiber0
167 - isCRC1malformed : if there was an issue with CRC from fiber1
168 */
169 uint8_t flags = event_stream.at(QIEStream::ERROR_POS);
170
171 bool is_ci_dskipped{static_cast<bool>((flags >> QIEStream::CID_SKIP_POS) &
173 bool is_ci_dunsync{static_cast<bool>((flags >> QIEStream::CID_UNSYNC_POS) &
175 // These are unused, should they be? FIXME
176 // bool isCRC1malformed{static_cast<bool>((flags >> QIEStream::CRC1_ERR_POS) &
177 // Mask8<QIEStream::FLAG_SIZE_BITS>::m)};
178 // bool isCRC0malformed{static_cast<bool>((flags >> QIEStream::CRC0_ERR_POS) &
179 // Mask8<QIEStream::FLAG_SIZE_BITS>::m)};
180
181 // checksum
182 // really, this is just empty for now.
183 // TODO: implement a checksum set/get
184 uint8_t reference_checksum = 0;
185 int checksum{(flags >> QIEStream::CHECKSUM_POS) &
187 // eventStream.at(QIEStream::CRC0_ERR_POS)
188 // QIEStream::CHECKSUM_POS);
189 if (checksum != reference_checksum)
190 ldmx_log(fatal) << "Got checksum mismatch: expected "
191 << (int)reference_checksum << ", stream says " << checksum;
192 if (is_ci_dunsync) ldmx_log(debug) << "Found unsynced CIDs!";
193 if (is_ci_dskipped) ldmx_log(fatal) << "Found skipped CIDs!";
194
195 /* -- TS event header done; read the channel contents -- */
196 std::vector<trigscint::TrigScintQIEDigis> out_digis;
197 std::map<int, std::vector<int>> ad_cmap;
198 std::map<int, std::vector<int>> td_cmap;
199
200 // read in words from the stream. line them up per channel and time sample.
201 // channels are in the electronics ordering
202 int i_wstart =
203 std::max(std::max(QIEStream::ERROR_POS, QIEStream::CHECKSUM_POS),
204 QIEStream::TRIGID_POS + (QIEStream::TRIGID_LEN_BYTES)) +
205 1;
206 // make sure we're at end of header
207 int n_words = n_samp * n_channels_ * 2 + i_wstart;
208 // 1 ADC, 1 TDC per channel per sample,
209 // + the words in the header
210 int i_word = i_wstart;
211 ldmx_log(debug) << "Event parsing starts at vector idx " << i_wstart
212 << " and nWords = " << n_words;
213 // outer loop: over nSamples
214 // inner loop over nChannels to get ADCs, then repeat to get TDCs
215 for (int i_s = 0; i_s < n_samp; i_s++) {
216 for (int i_q = 0; i_q < n_channels_; i_q++) {
217 if (i_word >= n_words) {
218 ldmx_log(fatal)
219 << "More words than expected! Breaking ADC loop in sample " << i_s
220 << " at iQ = " << i_q;
221 break;
222 }
223 uint8_t val = event_stream.at(i_word);
224 if (val > 0) { // add only the digis with non-zero ADC value
225 ldmx_log(debug) << "got ADC value " << (unsigned)val
226 << " at channel (elec) idx " << i_q;
227 if (ad_cmap.find(i_q) == ad_cmap.end()) { // we have a new channel
228 std::vector<int> adcs(n_samp, 0);
229 ad_cmap.insert(std::pair<int, std::vector<int>>(i_q, adcs));
230 }
231 ad_cmap[i_q].at(i_s) = val;
232 }
233 i_word++;
234 }
235 for (int i_q = 0; i_q < n_channels_; i_q++) {
236 if (i_word >= n_words) {
237 ldmx_log(debug)
238 << "More words than expected! Breaking TDC loop in sample " << i_s
239 << " at iQ = " << i_q;
240 break;
241 }
242 uint8_t val = event_stream.at(i_word);
243 if (val > 0) { // TODO: check if this channel is also present in ADC map?
244 // in the end?
245 ldmx_log(debug) << "got TDC value " << (unsigned)val
246 << " at channel (elec) idx " << i_q;
247 ;
248 if (td_cmap.find(i_q) == td_cmap.end()) { // we have a new channel
249 std::vector<int> tdcs(n_samp, 0);
250 td_cmap.insert(std::pair<int, std::vector<int>>(i_q, tdcs));
251 }
252 // this is LETDC; only the two most significant bits included
253 // they are shipped as least significant bits --> shift them
254 // TDCmap[iQ].at(iS) = (val + 1) * 16; // want LE TDC = 3 to correspond
255 // to
256 // 64 > 49 (which is maxTDC in sim)
257 td_cmap[i_q].at(i_s) = val; // have full 6-bit TDC at SLAC
258 }
259 i_word++;
260 }
261 ldmx_log(debug) << "Done with sample " << i_s;
262 }
263
264 ldmx_log(debug) << "Done reading in header, ADC and TDC for event "
265 << trigger_id;
266 for (std::map<int, std::vector<int>>::iterator itr = ad_cmap.begin();
267 itr != ad_cmap.end(); ++itr) {
269 digi.setADC(itr->second);
270 if (channel_map_.find(itr->first) == channel_map_.end()) {
271 ldmx_log(fatal)
272 << "Couldn't find the bar ID corresponding to electronics ID "
273 << itr->first << "!! Skipping.";
274 continue;
275 }
276 int bar = channel_map_[itr->first];
277 digi.setElecID(itr->first);
278 digi.setChanID(bar);
279 digi.setTDC(td_cmap[itr->first]);
280 digi.setTimeSinceSpill(time_spill);
281 if (bar == 0)
282 ldmx_log(debug) << "for bar 0, got time since spill "
283 << digi.getTimeSinceSpill();
284 out_digis.push_back(digi);
285 ldmx_log(debug) << "Iterator points to key " << itr->first
286 << " and mapped channel supposedly is " << bar;
287 ldmx_log(debug) << "Made digi with elecID = " << digi.getElecID()
288 << ", barID = " << digi.getChanID() << ", third adc value "
289 << digi.getADC().at(2) << " and third tdc "
290 << digi.getTDC().at(2);
291 }
292
293 event.add(output_collection_, out_digis);
294}
295
297 ldmx_log(debug) << "Process starts!";
298
299 return;
300}
301
303 ldmx_log(debug) << "Process ends!";
304
305 return;
306}
307
308} // namespace trigscint
309
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Implements an event buffer system for storing event data.
Definition Event.h:40
ldmx::EventHeader & getEventHeader()
Get the event header.
Definition Event.h:57
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
int getEventNumber() const
Return the event number.
Definition EventHeader.h:78
void onProcessStart() override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
void configure(framework::config::Parameters &ps) override
Configure our converter based off the configuration parameters decoded from the passed python script.
std::string channel_map_file_name_
the channel mapping
Definition QIEDecoder.h:35
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
void produce(framework::Event &event) override
Process the event and put new data products into it.
class for storing QIE output
uint32_t getTimeSinceSpill() const
Store the event time since spill counter.
std::vector< int > getADC() const
Get ADCs of all time samples.
void setTDC(const std::vector< int > tdc)
Store tdcs of all time samples.
void setChanID(const int chanid)
Store the channel ID.
int getElecID() const
Get electronics ID.
void setTimeSinceSpill(const uint32_t timeSpill)
Store the event time since spill counter.
int getChanID() const
Get channel ID.
std::vector< int > getTDC() const
Get tdcs of all time samples.
void setElecID(const int elecid)
Store the electronics ID.
void setADC(const std::vector< int > adc)
Store adcs of all time samples.