LDMX Software
QIEEncoder.cxx
1#include "TrigScint/QIEEncoder.h"
2
3#include <bitset>
4#include <iomanip>
5
6namespace trigscint {
7
9 // Configure this instance of the encoder
10 outputCollection_ = ps.getParameter<std::string>("output_collection");
11 inputCollection_ = ps.getParameter<std::string>("input_collection");
12 inputPassName_ = ps.getParameter<std::string>("input_pass_name");
13 channelMapFileName_ = ps.getParameter<std::string>("channel_map_file");
14 nChannels_ = ps.getParameter<int>("number_channels");
15 verbose_ = ps.getParameter<bool>("verbose");
16
17 ldmx_log(debug) << "In configure, got parameters:"
18 << "\noutput_collection = " << outputCollection_
19 << "\ninput_collection = " << inputCollection_
20 << "\ninput_pass_name = " << inputPassName_
21 << "\nchannel_map_file = " << channelMapFileName_
22 << "\nnumber_channels = " << nChannels_
23 << "\nverbose = " << verbose_;
24
25 // set up channel mapping
26 channelMapFile_.open(channelMapFileName_, std::ios::in);
27 if (!channelMapFile_.is_open()) {
28 EXCEPTION_RAISE(
29 "BadMapFile",
30 "The channel mapping file cannot be opened."); // <-- appears this
31 // needs implementing
32 // first
33 ldmx_log(fatal) << "The channel mapping file cannot be opened.";
34 return;
35 }
36 int chID, elID;
37 while (!channelMapFile_.eof()) {
38 channelMapFile_ >> elID >> chID;
39 // for test beam, we will only know the elecID from
40 // the position of the word in the stream.
41 // so these need to be strictly ordered in the map.
42 // barID can always be set, or looked up, as a property of the digi.
43
44 // make this based on channel ID. this is like looking up the position in a
45 // vector of a certain value. but it's fine
46 channelMap_.insert(std::pair<int, int>(chID, elID));
47 ldmx_log(debug) << "elID " << elID << " chID " << chID;
48 }
49 if (elID != nChannels_ - 1)
50 ldmx_log(fatal) << "The set number of channels " << nChannels_
51 << " seems not to match the number from the map (+1) :"
52 << elID;
53 channelMapFile_.close();
54
55 return;
56}
57
59 ldmx_log(debug) << "QIEEncoder: produce() starts! Event number: "
60 << event.getEventHeader().getEventNumber();
61
62 std::vector<trigscint::QIEStream> qieOuts;
63 int nSamp = QIEStream::NUM_SAMPLES;
64 std::vector<int> initVec(nSamp, 0);
65 ldmx_log(debug) << "num samples = " << nSamp;
66
67 // we're keeping a list ordered in elec ID since this is the order we'll use
68 // to write them to stream
69 for (int iQ = 0; iQ < nChannels_; iQ++) {
70 QIEStream qieOut;
71 qieOut.setADC(initVec);
72 qieOut.setTDC(initVec);
73 qieOut.setCID(initVec);
74 qieOut.setElectronicsID(iQ); // assume id is index
75
76 qieOuts.push_back(qieOut);
77 }
78
79 ldmx_log(debug) << "Looking up input collection " << inputCollection_ << "_"
80 << inputPassName_;
81 const auto digis{event.getCollection<trigscint::TrigScintQIEDigis>(
82 inputCollection_, inputPassName_)};
83 ldmx_log(debug) << "Got input collection" << inputCollection_ << "_"
84 << inputPassName_;
85
86 bool isCIDunsync = false; // mismatch between CID reported by channels within
87 // the same time sample
88 bool isCIDskipped = false; // a gap in the CID increment
89 bool isCRC0malformed = false; // an issue with CRC from fiber0
90 bool isCRC1malformed = false; // an issue with CRC from fiber1
91
92 int firstCID = -1;
93 ldmx_log(debug) << "entering loop over digis ";
94 for (auto &digi : digis) {
95 int bar = digi.getChanID();
96 auto itr = channelMap_.find(bar);
97 if (itr == channelMap_.end()) { // yikes! didn't find the bar in the map
98 ldmx_log(fatal) << "Couldn't find an entry for bar " << bar
99 << "; check the (choice of) channel map!. Exiting event "
100 << event.getEventHeader().getEventNumber();
101 return;
102 }
103 int idx = itr->second; // here we're just using the order. no actual elID
104 // is assumed.
105 qieOuts.at(idx).setChannelID(bar);
106 qieOuts.at(idx).setElectronicsID(idx);
107 ldmx_log(debug) << "Channel " << bar << " elec ID "
108 << qieOuts.at(idx).getElectronicsID();
109 std::vector<int> LEtdcs; // make the LE (Leading Edge) truncation explicit
110 std::vector<uint8_t> cids;
111 for (int iS = 0; iS < nSamp; iS++) {
112 int tdc = digi.getTDC().at(iS);
113 int cid = digi.getCID().at(iS);
114 if (cids.size() > 0 && (cid % 4) != ((cids.back() + 1) % 4)) {
115 // by construction shouldn't happen in simulation. still, explicitly
116 // checking here, considering any future changes to our CID simulation.
117 isCIDskipped = true;
118 }
119 if (verbose_) { // all this is only useful for debugging
120 std::vector<uint8_t> adcs;
121 int adc = digi.getADC().at(iS);
122 uint8_t mant = adc % 64;
123 uint8_t exp = adc / 64;
124 ldmx_log(debug) << "\tSample " << iS << std::left << std::setw(6)
125 << " ADC " << adc << ",\texp " << unsigned(exp)
126 << " mant " << unsigned(mant) << ",\tTDC = " << tdc
127 << ", LE TDC = " << std::bitset<8>(tdc / 16)
128 << " and capID= " << cid;
129 adcs.push_back(64 * exp + mant);
130 ldmx_log(debug) << "Combined ADC: " << std::showbase
131 << std::bitset<8>(adcs.back()) << " and original adc "
132 << std::bitset<8>(adc) << std::dec;
133 } // if verbose
134
135 tdc /= 16; // do LE (leading edge) TDC
136 LEtdcs.push_back(tdc);
137 cids.push_back((uint8_t)cid);
138 } // over samples
139 if (firstCID == -1) {
140 // just store the 5th one, doesn't matter; if all channels
141 // are aligned then cids should match at any given time sample
142 firstCID = cids.back();
143 }
144 if (firstCID != cids.back()) {
145 isCIDunsync =
146 true; // any one channel not aligned is enough to set this bool
147 }
148 qieOuts.at(idx).setADC(digi.getADC());
149 qieOuts.at(idx).setTDC(LEtdcs);
150 } // over digis
151 if (isCIDunsync) ldmx_log(debug) << "Found unsynced CIDs!";
152 if (isCIDskipped) ldmx_log(info) << "Found skipped CIDs!";
153
154 // data format:
155 // RM ID: we don't set it for testbeam so skip for now.
156 // 16 bit trigger ID.
157 // 4 bits of flags, then 4 reserved 0 for now
158 // some 8-bit error word/checksum.
159 // all channel 8-bit ADCs.
160 // all channel TDCS.
161 // done.
162
163 uint16_t triggerID = event.getEventHeader().getEventNumber();
164 uint8_t randomChecksum =
165 30; // just some number for now. TODO implement a checksum
166 uint8_t flags = 0; // we use this to contain the four reserved 0's too
167 // put it all in, at the assigned position
168 flags |= (isCRC0malformed << QIEStream::CRC0_ERR_POS);
169 flags |= (isCRC1malformed << QIEStream::CRC1_ERR_POS);
170 flags |= (isCIDunsync << QIEStream::CID_UNSYNC_POS);
171 flags |= (isCIDskipped << QIEStream::CID_SKIP_POS);
172 ldmx_log(debug) << "FLAGS: " << std::bitset<8>(flags);
173
174 std::vector<uint8_t> outWord;
175 std::vector<uint8_t> triggerIDwords;
176 for (int iW = QIEStream::TRIGID_LEN_BYTES - 1; iW >= 0; iW--) {
177 // assume the whole 2B are written as a single 16-bit word
178 uint8_t tIDword = triggerID >> iW * 8; // shift by a byte at a time
179 triggerIDwords.push_back(tIDword);
180 outWord.push_back(tIDword);
181 }
182
183 outWord.push_back(flags);
184 outWord.push_back(randomChecksum);
185
186 if (verbose_) {
187 std::cout << "header word ";
188 for (auto word : outWord) std::cout << std::bitset<8>(word) << " ";
189 std::cout << std::endl;
190 }
191
192 // now write this in sequence: ADC of all channels, then TDC; repeat for all
193 // samples
194 for (int iS = 0; iS < nSamp; iS++) {
195 for (int iQ = 0; iQ < nChannels_; iQ++) {
196 outWord.push_back(qieOuts.at(iQ).getADC().at(iS));
197 } // over channels : ADC
198 for (int iQ = 0; iQ < nChannels_; iQ++) {
199 outWord.push_back(qieOuts.at(iQ).getTDC().at(iS));
200 } // over channels: TDC
201 } // over time samples
202
203 // in verbose mode, print this all to screen
204 if (verbose_) {
205 std::cout << "total word ";
206 int widx = 0;
207 int iWstart =
208 std::max(std::max(QIEStream::ERROR_POS, QIEStream::CHECKSUM_POS),
209 QIEStream::TRIGID_POS + (QIEStream::TRIGID_LEN_BYTES)) +
210 1; // probably overkill :D should be 4
211 for (auto word : outWord) {
212 if ((widx - iWstart) % nChannels_ == 0) {
213 int sample = (widx - iWstart) / nChannels_;
214 if (sample % 2 == 0)
215 std::cout << "\n sample " << sample / 2 << " | ";
216 else
217 std::cout << "\n TDC: ";
218 }
219 std::cout << (unsigned)word << " ";
220 // std::cout << std::bitset<8>(word) << " " ; //for binary output
221 // format
222 widx++;
223 }
224 std::cout << std::endl;
225 } // if verbose
226
227 event.add(outputCollection_, outWord);
228}
229
231 ldmx_log(debug) << "Process starts!";
232
233 return;
234}
235
237 ldmx_log(debug) << "Process ends!";
238
239 return;
240}
241
242} // namespace trigscint
243
244DECLARE_PRODUCER_NS(trigscint, QIEEncoder);
#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
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
void produce(framework::Event &event) override
Process the event and put new data products into it.
void onProcessStart() override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
void configure(framework::config::Parameters &ps) override
Configure our converter based off the configuration parameters decoded from the passed python script.
Definition QIEEncoder.cxx:8
class for storing QIE output as a binary stream
Definition QIEStream.h:25
void setADC(const std::vector< int > adc)
Store adcs of all time samples.
Definition QIEStream.h:92
void setElectronicsID(const int elecid)
Store the electronics ID.
Definition QIEStream.h:86
void setCID(const std::vector< int > cid)
Store cids of all time samples.
Definition QIEStream.h:104
void setTDC(const std::vector< int > tdc)
Store tdcs of all time samples.
Definition QIEStream.h:98
class for storing QIE output