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