LDMX Software
ZCCMDecoder.cxx
1#include "TrigScint/ZCCMDecoder.h"
2
3#include <bitset>
4#include <iostream>
5
6#include "TrigScint/Event/QIEStream.h"
7#include "TrigScint/Event/TrigScintQIEDigis.h"
8
9namespace trigscint {
10
12 // Configure this instance of the encoder
13 output_collection_ = ps.get<std::string>("output_collection");
14 input_collection_ = ps.get<std::string>("input_collection");
15 input_pass_name_ = ps.get<std::string>("input_pass_name");
16 module_map_file_name_ = ps.get<std::string>("module_map_file");
17 channel_map_file_name_ = ps.get<std::string>("channel_map_file");
18 n_channels_ = ps.get<int>("number_channels");
19 n_samples_ = ps.get<int>("number_time_samples");
20 is_real_data_ = ps.get<bool>("is_real_data");
21 // daq_junk_words_len_bytes_ = ps.get<int>("daq_extra_header_bytes");
22 // daq_junk_tail_len_bits_ = ps.get<int>("daq_trailing_header_bits");
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 << "\nmodule_map_file = " << module_map_file_name_
29 << "\nchannel_map_file = " << channel_map_file_name_
30 << "\nnumber_channels = " << n_channels_
31 << "\nnumber_time_samples = " << n_samples_
32 << "\nis_real_data = " << is_real_data_;
33
34 channel_map_file_.open(channel_map_file_name_, std::ios::in);
35 if (!channel_map_file_.is_open()) {
36 EXCEPTION_RAISE("BadMapFile", "The channel mapping file cannot be opened.");
37 return;
38 }
39 int n_chan_from_map = -1; // we will in effect count newlines
40 int ch_id, el_id;
41 while (!channel_map_file_.eof()) {
42 channel_map_file_ >> el_id >> ch_id;
43 // make the map based on electronics ID, to look up channel ID.
44 // the reason is, the elecID is given by the position of the word in
45 // the output. 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 channel_map_.insert(std::pair<int, int>(el_id, ch_id));
52 ldmx_log(debug) << el_id << " chID " << ch_id;
53 n_chan_from_map++; // will increment on last newline as well
54 }
55 channel_map_file_.close();
56 if (n_chan_from_map != n_channels_)
57 ldmx_log(fatal) << "The set number of channels: " << n_channels_
58 << " does not match the number obtained from the map :"
59 << n_chan_from_map;
60
61 // and similarly for the lane-->module mapping
62
63 module_map_file_.open(module_map_file_name_, std::ios::in);
64 if (!module_map_file_.is_open()) {
65 ldmx_log(fatal) << "The module mapping file cannot be opened.";
66 return;
67 }
68 int module_id, lane_id;
69 while (!module_map_file_.eof()) {
70 module_map_file_ >> lane_id >> module_id;
71 // make the map based on lane ID, to look up module ID.
72 // here make the lane the key (other way around when encoding)
73 module_map_.insert(std::pair<int, int>(lane_id, module_id));
74 ldmx_log(debug) << "lane " << lane_id << " --> module ID " << module_id;
75 modules_used_[module_id] = 1;
76 }
77 module_map_file_.close();
78 int n_lanes = n_channels_ / ZCCMOutput::NUM_CHAN_PER_LANE;
79 if (lane_id != n_lanes - 1)
80 ldmx_log(fatal) << "The set number of lanes " << n_lanes
81 << " seems not to match the number obtained from the map :"
82 << lane_id + 1;
83
84 return;
85}
86
88 ldmx_log(debug) << "ZCCMDecoder: produce() starts! Event number: "
89 << event.getEventHeader().getEventNumber();
90
91 // this has to be configurable for now, to read real data
92 int n_samp = n_samples_;
93 ldmx_log(debug) << "num samples = " << n_samp;
94
95 ldmx_log(debug) << "Looking up input collection " << input_collection_ << "_"
96 << input_pass_name_;
97 const auto event_output{
98 event.getCollection<uint8_t>(input_collection_, input_pass_name_)};
99 ldmx_log(debug) << "Got input collection " << input_collection_ << "_"
100 << input_pass_name_;
101
102 /* -- PROCESS THE EVENT INFORMATION -- */
103 uint32_t time_stamp = 0;
104 for (int i_w = 0; i_w < ZCCMOutput::TIMESTAMP_LEN_BYTES; i_w++) {
105 int pos = ZCCMOutput::TIMESTAMP_POS + i_w;
106 uint8_t time_word = event_output.at(pos);
107 ldmx_log(debug) << "time stamp word at position " << pos
108 << " (with iW = " << i_w
109 << ") = " << std::bitset<8>(time_word);
110 time_stamp |= (time_word << i_w * 8); // shift by a byte at a time
111 }
112 // This time_stamp is local to the TS
113 event.getEventHeader().setIntParameter("TS Timestamp", time_stamp);
114
115 /* -- TS event header done; read the channel contents -- */
116
117 /*
118 in a loop over samples, loop over nlanes
119 get ADC, TDC, flags and lane nb
120 lane number uses a full byte
121 the flags word contains
122 - capID (2b)
123 - CE (1b, some channel alignment error flag)
124 - BC0 (most of the time 0, set to 1 with fixed frequency. if channels
125 are aligned, then BC0=1 should occur in the same time sample for all of
126 them)
127 - 4 trailing reserved 0's
128 */
129
130 std::map<int, std::vector<int>> adc_map;
131 std::map<int, std::vector<int>> tdc_map;
132 std::map<int, std::vector<int>> cid_map;
133 std::map<int, std::vector<int>> bc0_map;
134 std::map<int, std::vector<int>> ce_map;
135 // we need one output digi collection per pad
136 std::vector<std::vector<trigscint::TrigScintQIEDigis>> out_digis;
137 std::size_t n_modules =
138 std::ranges::count_if(modules_used_, [](int x) { return x != 0; });
139 // default construct (empty) vectors of digis separated into modules
140 out_digis.resize(n_modules);
141
142 // read in words from the output. line them up per channel and time sample.
143 // channels are in the electronics ordering
144 int word_length = ZCCMOutput::SAMPLE_WORD_LEN_BYTES;
145 int num_messages =
146 ((int)event_output.size() - ZCCMOutput::EVENTDATA_POS) / word_length;
147
148 // with one message per time sample per lane, we expect nSample*nLanes
149 // messages
150 int n_lanes = n_channels_ / ZCCMOutput::NUM_CHAN_PER_LANE;
151 int num_expected_messages = n_samp * n_lanes;
152 if (num_messages != num_expected_messages)
153 ldmx_log(warn) << "Unexpected stream length! Num messages is "
154 << num_messages << ", expect " << num_expected_messages;
155
156 // read in a lane stream at a time with a while loop
157 // operate on bytes, but i_word increments for each meassage i.e. each lane
158 int i_word = 0;
159 int start_lane = -1;
160 int sample_nb = -1;
161
162 while (i_word < num_messages) { // update the position to read the word from
163 uint word_pos = ZCCMOutput::EVENTDATA_POS + i_word * word_length;
164
165 uint16_t empty =
166 event_output.at(word_pos + ZCCMOutput::EMPTY_WORD_SAMPLE_WORD_POS);
167 uint8_t lane = event_output.at(word_pos + ZCCMOutput::LANE_SAMPLE_WORD_POS);
168 uint8_t flag =
169 event_output.at(word_pos + ZCCMOutput::FLAGS_SAMPLE_WORD_POS);
170
171 ldmx_log(debug) << "Start of message " << i_word << ".\n\tEmpty word is "
172 << std::bitset<16>(empty) << ", read at position "
173 << word_pos + ZCCMOutput::EMPTY_WORD_SAMPLE_WORD_POS
174 << ". \n\tFlag word is " << std::bitset<8>(flag)
175 << ", read at position "
176 << word_pos + ZCCMOutput::FLAGS_SAMPLE_WORD_POS
177 << ". \n\tLane is " << std::bitset<8>(lane)
178 << ", read at position "
179 << word_pos + ZCCMOutput::LANE_SAMPLE_WORD_POS << ".";
180
181 if (start_lane == -1) { // it is unset
182 start_lane = lane; // store the first one
183 ldmx_log(debug) << "Set time sample start lane to " << start_lane;
184 }
185 // extract the flag info
186 int cid{(flag >> ZCCMOutput::CAPID_POS_IN_FLAG) &
188 ldmx_log(trace) << "Got Cap ID " << cid;
189 bool bc0{static_cast<bool>((flag >> ZCCMOutput::BC0_POS_IN_FLAG) &
191 ldmx_log(trace) << "Got BC0 flag " << bc0;
192 bool ce{static_cast<bool>((flag >> ZCCMOutput::CE_POS_IN_FLAG) &
194 ldmx_log(trace) << "Got CE flag " << ce;
195 int empty_bits{(flag >> ZCCMOutput::EMPTY_FLAG_WORD_POS_IN_FLAG) &
197 if (empty_bits)
198 ldmx_log(fatal) << "Empty bits of flag not empty: " << empty_bits;
199
200 // TODO: check that BC0 bit is aligned across all lanes
201 // TODO: keep track of the CID cycling and flag when a CID is skipped
202 // bool is_cid_unsync{static_cast<bool>((flags >>
203 // ZCCMOutput::CID_UNSYNC_POS) &
204 // Mask8<ZCCMOutput::FLAG_SIZE_BITS>::M)};
205
206 // how do we know we're at the next time sample?
207 // 1. i_word has incremented by the number of lanes
208 // // i.e i_word % nLanes = 0
209 // 2. we're seeing the first lane again
210 // it is good, then, to check that these agree.
211
212 if (lane == start_lane) // we have cycled (or just started)
213 sample_nb++;
214
215 uint8_t cid_val = cid;
216
217 // get channel-by-channel info (ADC and TDC for this time sample)
218 for (int i_c = 0; i_c < ZCCMOutput::NUM_CHAN_PER_LANE; i_c++) {
219 if (i_word >= num_expected_messages) {
220 ldmx_log(fatal)
221 << "More words than expected! Breaking event data loop in sample "
222 << sample_nb << " at lane = " << int(lane) << ", channel nb "
223 << i_c;
224 break;
225 }
226 uint8_t adc_val =
227 event_output.at(word_pos + ZCCMOutput::ADC_SAMPLE_WORD_POS + i_c);
228 uint8_t tdc_val =
229 event_output.at(word_pos + ZCCMOutput::TDC_SAMPLE_WORD_POS + i_c);
230 // define a unique elecID for each channel in the readout, comprising of
231 // lane, module, channelNB then use that to look up bar ID in the the maps
232 int module = module_map_[lane];
233 int elec_id = 100 * lane + 10 * module + i_c; // numbering scheme: LLMC
234
235 ldmx_log(trace) << "got ADC value " << (unsigned)adc_val
236 << " and TDC value " << (unsigned)tdc_val
237 << " at channel idx " << i_c << " with elec id "
238 << elec_id;
239 if (adc_map.find(elec_id) == adc_map.end()) { // we have a new channel
240 std::vector<int> adcs(n_samp, 0);
241 adc_map.insert(std::pair<int, std::vector<int>>(elec_id, adcs));
242 }
243 adc_map[elec_id].at(sample_nb) = adc_val;
244 // perhaps overly prudent to check for TDC and CID as well, might tag them
245 // along on ADC
246 if (tdc_map.find(elec_id) == tdc_map.end()) { // we have a new channel
247 std::vector<int> tdcs(n_samp, 0);
248 tdc_map.insert(std::pair<int, std::vector<int>>(elec_id, tdcs));
249 }
250 tdc_map[elec_id].at(sample_nb) = tdc_val;
251 if (cid_map.find(elec_id) == cid_map.end()) { // we have a new channel
252 std::vector<int> cids(n_samp, 0);
253 cid_map.insert(std::pair<int, std::vector<int>>(elec_id, cids));
254 std::vector<int> bc0s(n_samp, 0);
255 bc0_map.insert(std::pair<int, std::vector<int>>(elec_id, bc0s));
256 std::vector<int> ces(n_samp, 0);
257 ce_map.insert(std::pair<int, std::vector<int>>(elec_id, ces));
258 }
259 cid_map[elec_id].at(sample_nb) = cid_val;
260 bc0_map[elec_id].at(sample_nb) = bc0;
261 ce_map[elec_id].at(sample_nb) = ce;
262 } // over channels
263 ldmx_log(debug) << "Done with lane " << int(lane) << " which we think is "
264 << i_word % n_lanes << " in sample " << sample_nb;
265
266 if (int(lane) != i_word % n_lanes)
267 ldmx_log(fatal) << "Lane ordering has been messed up! Expect lane "
268 << i_word % n_lanes << ", but we got lane " << int(lane)
269 << " in sample " << sample_nb;
270 // shift to next word
271 i_word++;
272 } // while event data words left in the stream
273
274 ldmx_log(debug) << "Done reading in header, ADC and TDC for event "
275 << event.getEventNumber();
276
277 // Reading step done.
278 /* -- make digis from all the info stored in maps -- */
279 for (std::map<int, std::vector<int>>::iterator itr = adc_map.begin();
280 itr != adc_map.end(); ++itr) {
282 digi.setADC(itr->second);
283 if (channel_map_.find(itr->first) == channel_map_.end()) {
284 ldmx_log(fatal)
285 << "Couldn't find the bar ID corresponding to electronics ID "
286 << itr->first << "!! Skipping.";
287 continue;
288 }
289 int bar = channel_map_[itr->first];
290 digi.setElecID(itr->first);
291 digi.setChanID(bar);
292 int module = itr->first / 10 % 10;
293 digi.setModuleID(module);
294 int lane = itr->first / 100;
295 digi.setLaneID(lane);
296 digi.setTDC(tdc_map[itr->first]);
297 digi.setCID(cid_map[itr->first]);
298 digi.setBC0(bc0_map[itr->first]);
299 digi.setCE(ce_map[itr->first]);
300 if (bar == 0)
301 ldmx_log(debug) << "for bar 0, got time since spill "
302 << digi.getTimeSinceSpill();
303 out_digis[module].push_back(digi);
304 ldmx_log(debug) << "Iterator points to key " << itr->first
305 << " and mapped channel supposedly is " << bar;
306 ldmx_log(debug) << "Made digi with elecID = " << digi.getElecID()
307 << ", barID = " << digi.getChanID() << ", third adc value "
308 << digi.getADC().at(2) << " and third tdc "
309 << digi.getTDC().at(2);
310 }
311
312 // name them as ordered
313 for (uint i = 0; i < n_modules; i++)
314 event.add(output_collection_ + Form("%i", i + 1), out_digis[i]);
315}
316
318 ldmx_log(debug) << "Process starts!";
319
320 return;
321}
322
324 ldmx_log(debug) << "Process ends!";
325
326 return;
327}
328
329} // namespace trigscint
330
#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
void add(const std::string &collectionName, T &obj)
Adds an object to the event bus.
Definition Event.h:193
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
class for storing QIE output
void setLaneID(const int laneid)
Store the lane (fiber) ID.
uint32_t getTimeSinceSpill() const
Store the event time since spill counter.
std::vector< int > getADC() const
Get ADCs of all time samples.
void setCID(const std::vector< int > cid)
Store cids of all time samples.
void setTDC(const std::vector< int > tdc)
Store tdcs of all time samples.
void setBC0(const std::vector< int > bc0)
Store bc0s of all time samples.
void setChanID(const int chanid)
Store the channel ID.
int getElecID() const
Get electronics ID.
int getChanID() const
Get channel ID.
std::vector< int > getTDC() const
Get tdcs of all time samples.
void setModuleID(const int moduleid)
Store the module ID (pad number-1)
void setCE(const std::vector< int > ce)
Store ces 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.
std::string channel_map_file_name_
the channel mapping
Definition ZCCMDecoder.h:36
void produce(framework::Event &event) override
Process the event and put new data products into it.
void configure(framework::config::Parameters &ps) override
Configure our converter based off the configuration parameters decoded from the passed python script.
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
void onProcessStart() override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
std::string module_map_file_name_
the module mapping
Definition ZCCMDecoder.h:40
static const int TIMESTAMP_POS
for each time sample, every lane each sends a message: [ 1B ADC for 6 channels, 2B empty,...
Definition ZCCMOutput.h:103