12 #include <top/modules/TOPDoublePulseGenerator/TOPDoublePulseGeneratorModule.h>
13 #include <top/geometry/TOPGeometryPar.h>
16 #include <framework/datastore/StoreArray.h>
17 #include <framework/datastore/StoreObjPtr.h>
20 #include <framework/logging/Logger.h>
23 #include <framework/dataobjects/EventMetaData.h>
24 #include <top/dataobjects/TOPDigit.h>
56 setDescription(
"Generator of calibration double pulses");
57 setPropertyFlags(c_ParallelProcessingCertified);
60 addParam(
"moduleIDs", m_moduleIDs,
61 "list of slots for which to generate cal pulse, empty list means all slots.",
63 m_asicChannels.push_back(0);
64 addParam(
"asicChannels", m_asicChannels,
65 "ASIC calibration channels (0 - 7), empty list means all channels.",
67 addParam(
"timeDifference", m_timeDifference,
68 "time difference between first and second pulse [ns].", 21.87);
69 addParam(
"timeResolution", m_timeResolution,
70 "sigma of time difference [ns].", 40.0e-3);
71 addParam(
"sampleTimeIntervals", m_sampleTimeIntervals,
72 "vector of 256 sample time intervals to construct sample times. "
73 "If empty, equidistant intervals will be used.", m_sampleTimeIntervals);
74 addParam(
"useDatabase", m_useDatabase,
75 "if true, use sample times from database instead of sampleTimeIntervals.",
77 addParam(
"storageWindows", m_storageWindows,
78 "number of storage windows (old FW used 64 out of 512)", (
unsigned) 512);
79 addParam(
"outputFileName", m_outputFileName,
80 "if given, sample times will be saved as root histograms in this file",
85 TOPDoublePulseGeneratorModule::~TOPDoublePulseGeneratorModule()
87 if (m_timebase)
delete m_timebase;
90 void TOPDoublePulseGeneratorModule::initialize()
95 digits.registerInDataStore();
99 const auto* geo = TOPGeometryPar::Instance()->getGeometry();
101 if (m_moduleIDs.empty()) {
102 for (
const auto& module : geo->getModules()) {
103 m_moduleIDs.push_back(module.getModuleID());
106 for (
auto moduleID : m_moduleIDs) {
107 if (!geo->isModuleIDValid(moduleID))
108 B2ERROR(
"Invalid module ID found in input list: " << moduleID);
112 if (m_asicChannels.empty()) {
113 for (
unsigned ch = 0; ch < 8; ch++) m_asicChannels.push_back(ch);
115 for (
unsigned ch : m_asicChannels) {
117 B2ERROR(
"Invalid ASIC channel found in input list: " << ch);
123 double syncTimeBase = geo->getNominalTDC().getSyncTimeBase();
124 m_sampleTimes.setTimeAxis(syncTimeBase);
125 m_sampleDivisions = (0x1 << geo->getNominalTDC().getSubBits());
129 }
else if (m_sampleTimeIntervals.empty()) {
130 B2INFO(
"TOPDoublePulseGenerator: using equidistant sample times");
131 }
else if (m_sampleTimeIntervals.size() == 256) {
132 std::vector<double> timeAxis;
133 timeAxis.push_back(0);
134 for (
auto dt : m_sampleTimeIntervals) timeAxis.push_back(dt + timeAxis.back());
135 double rescale = 2 * syncTimeBase / timeAxis.back();
136 for (
auto& t : timeAxis) t *= rescale;
137 m_sampleTimes.setTimeAxis(timeAxis, syncTimeBase);
138 B2INFO(
"TOPDoublePulseGenerator: using sample times from steering");
140 B2ERROR(
"sampleTimeIntervals: size must be 256 or empty");
143 if (!m_outputFileName.empty()) storeSampleTimes(m_outputFileName);
148 void TOPDoublePulseGeneratorModule::beginRun()
152 if (!(*m_timebase).isValid()) {
153 B2FATAL(
"Sample time calibration requested but not available for run "
154 << evtMetaData->getRun()
155 <<
" of experiment " << evtMetaData->getExperiment());
162 void TOPDoublePulseGeneratorModule::event()
167 const auto& chMapper = TOPGeometryPar::Instance()->getChannelMapper();
168 const auto& feMapper = TOPGeometryPar::Instance()->getFrontEndMapper();
170 double timeError = m_timeResolution / sqrt(2.0);
172 for (
auto moduleID : m_moduleIDs) {
173 for (
unsigned asic = 0; asic < 64; asic++) {
176 auto pixelID = chMapper.getPixelID(channel);
181 unsigned scrodID = 0;
183 const auto* feMap = feMapper.getMap(moduleID, bs);
185 sampleTimes = (*m_timebase)->getSampleTimes(scrodID, channel % 128);
187 B2WARNING(
"No sample time calibration available for SCROD " << scrodID
188 <<
" channel " << channel % 128 <<
" - equidistant will be used");
193 double time = gRandom->Rndm() * sampleTimes->
getTimeRange();
194 unsigned window = gRandom->Rndm() * m_storageWindows;
195 double sample = sampleTimes->
getSample(window, time);
196 auto* digit = digits.appendNew(moduleID, pixelID, sample);
197 digit->setFirstWindow(window);
198 digit->setTime(time);
199 digit->setTimeError(timeError);
200 digit->setChannel(channel);
201 digit->setHitQuality(TOPDigit::c_CalPulse);
204 time += gRandom->Gaus(m_timeDifference, m_timeResolution);
205 sample = sampleTimes->
getSample(window, time);
206 digit = digits.appendNew(moduleID, pixelID, sample);
207 digit->setFirstWindow(window);
208 digit->setTime(time);
209 digit->setTimeError(timeError);
210 digit->setChannel(channel);
211 digit->setHitQuality(TOPDigit::c_CalPulse);
219 void TOPDoublePulseGeneratorModule::endRun()
223 void TOPDoublePulseGeneratorModule::terminate()
228 void TOPDoublePulseGeneratorModule::storeSampleTimes(std::string fileName)
230 if (fileName.empty())
return;
232 TFile* fout = TFile::Open(fileName.c_str(),
"recreate");
234 B2ERROR(
"Can't open the output file " << fileName);
238 const auto& feMapper = TOPGeometryPar::Instance()->getFrontEndMapper();
240 TH1F scrods(
"scrodID",
"scrod ID's mapped to slots/boardstacks", 64, -0.5, 63.5);
241 scrods.SetXTitle(
"(slot - 1) * 4 + boardstack");
242 scrods.SetYTitle(
"scrod ID");
244 for (
auto moduleID : m_moduleIDs) {
245 for (
int bs = 0; bs < 4; bs++) {
246 const auto* feMap = feMapper.getMap(moduleID, bs);
248 B2ERROR(
"No front-end mapping available for slot " << moduleID
249 <<
" boardstack " << bs);
252 unsigned scrodID = feMap->getScrodID();
253 std::string subdir =
"scrod" + std::to_string(scrodID);
254 int i = (moduleID - 1) * 4 + bs;
255 scrods.SetBinContent(i + 1, scrodID);
256 fout->mkdir(subdir.c_str());
261 for (
auto moduleID : m_moduleIDs) {
262 for (
unsigned asic = 0; asic < 64; asic++) {
264 const auto* feMap = feMapper.getMap(moduleID, bs);
265 if (!feMap)
continue;
266 unsigned scrodID = feMap->getScrodID();
267 std::string subdir =
"scrod" + std::to_string(scrodID);
268 fout->cd(subdir.c_str());
273 sampleTimes = (*m_timebase)->getSampleTimes(scrodID, channel);
275 string forWhat =
"scrod " + to_string(scrodID) +
276 " channel " + to_string(channel) +
277 " (slot" + to_string(moduleID) +
", as" + to_string(asic) +
280 saveAsHistogram(timeAxis,
"sampleTimes_ch" + to_string(channel),
281 "Generator input: sample times for " + forWhat,
282 "sample number",
"t [ns]");
283 std::vector<double> dt;
284 for (
unsigned i = 1; i < timeAxis.size(); i++) {
285 dt.push_back(timeAxis[i] - timeAxis[i - 1]);
287 saveAsHistogram(dt,
"dt_ch" + to_string(channel),
288 "Generator input: sample time bins for " + forWhat,
289 "sample number",
"#Delta t [ns]");
299 void TOPDoublePulseGeneratorModule::saveAsHistogram(
const std::vector<double>& vec,
300 const std::string& name,
301 const std::string& title,
302 const std::string& xTitle,
303 const std::string& yTitle)
const
305 if (vec.empty())
return;
307 TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
308 h.SetXTitle(xTitle.c_str());
309 h.SetYTitle(yTitle.c_str());
310 if (name.find(
"Fit") != string::npos) h.SetLineColor(2);
312 for (
unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);