5 #include "vdt/vdtMath.h"
16 adcSaturation_fC_{-1.0},
19 tdcSaturation_fC_{-1.0},
23 tdcResolutionInNs_{1
e-9},
24 targetMIPvalue_ADC_{},
26 jitterConstant2_ns_{},
28 toaMode_(WEIGHTEDBYE) {
29 edm::LogVerbatim(
"HGCFE") <<
"[HGCFEElectronics] running with version " << fwVersion_ << std::endl;
30 if (ps.exists(
"adcPulse")) {
31 auto temp = ps.getParameter<std::vector<double> >(
"adcPulse");
32 for (
unsigned i = 0;
i <
temp.size(); ++
i) {
36 for (
unsigned i = 0;
i < adcPulse_.size(); ++
i) {
37 adcPulse_[
i] = adcPulse_[
i] / adcPulse_[2];
39 temp = ps.getParameter<std::vector<double> >(
"pulseAvgT");
40 for (
unsigned i = 0;
i <
temp.size(); ++
i) {
44 if (ps.exists(
"adcNbits")) {
45 uint32_t
adcNbits = ps.getParameter<uint32_t>(
"adcNbits");
46 adcSaturation_fC_ = ps.getParameter<
double>(
"adcSaturation_fC");
49 <<
" with LSB=" << adcLSB_fC_ <<
" saturation to occur @ " << adcSaturation_fC_
53 if (ps.exists(
"tdcNbits")) {
54 uint32_t
tdcNbits = ps.getParameter<uint32_t>(
"tdcNbits");
55 tdcSaturation_fC_ = ps.getParameter<
double>(
"tdcSaturation_fC");
59 tdcSaturation_fC_ *= (1. - 1
e-6);
61 <<
" saturation to occur @ " << tdcSaturation_fC_
62 <<
" (NB lowered by 1 part in a million)" << std::endl;
64 if (ps.exists(
"targetMIPvalue_ADC"))
65 targetMIPvalue_ADC_ = ps.getParameter<uint32_t>(
"targetMIPvalue_ADC");
66 if (ps.exists(
"adcThreshold_fC"))
67 adcThreshold_fC_ = ps.getParameter<
double>(
"adcThreshold_fC");
68 if (ps.exists(
"tdcOnset_fC"))
69 tdcOnset_fC_ = ps.getParameter<
double>(
"tdcOnset_fC");
70 if (ps.exists(
"tdcForToAOnset_fC")) {
71 auto temp = ps.getParameter<std::vector<double> >(
"tdcForToAOnset_fC");
72 if (
temp.size() == tdcForToAOnset_fC_.size()) {
73 std::copy_n(
temp.begin(),
temp.size(), tdcForToAOnset_fC_.begin());
75 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA thresholds ";
78 if (ps.exists(
"toaLSB_ns"))
79 toaLSB_ns_ = ps.getParameter<
double>(
"toaLSB_ns");
80 if (ps.exists(
"tdcChargeDrainParameterisation")) {
81 for (
auto val : ps.getParameter<std::vector<double> >(
"tdcChargeDrainParameterisation")) {
82 tdcChargeDrainParameterisation_.push_back((
float)
val);
85 if (ps.exists(
"tdcResolutionInPs"))
86 tdcResolutionInNs_ = ps.getParameter<
double>(
"tdcResolutionInPs") * 1
e-3;
87 if (ps.exists(
"toaMode"))
88 toaMode_ = ps.getParameter<uint32_t>(
"toaMode");
90 if (ps.exists(
"jitterNoise_ns")) {
91 auto temp = ps.getParameter<std::vector<double> >(
"jitterNoise_ns");
92 if (
temp.size() == jitterNoise2_ns_.size()) {
93 std::copy_n(
temp.begin(),
temp.size(), jitterNoise2_ns_.begin());
95 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA jitterNoise ";
98 if (ps.exists(
"jitterConstant_ns")) {
99 auto temp = ps.getParameter<std::vector<double> >(
"jitterConstant_ns");
100 if (
temp.size() == jitterConstant2_ns_.size()) {
101 std::copy_n(
temp.begin(),
temp.size(), jitterConstant2_ns_.begin());
103 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA jitterConstant ";
111 DFr& dataFrame,
HGCSimHitData& chargeColl, uint32_t thrADC,
float lsbADC, uint32_t gainIdx,
float maxADC) {
115 for (
int it = 0; it < (
int)(chargeColl.size()); it++)
116 debug |= (chargeColl[it] > adcThreshold_fC_);
128 maxADC = adcSaturation_fC_ * (1 - 1
e-6);
129 for (
int it = 0; it < (
int)(chargeColl.size()); it++) {
131 const uint32_t
adc = std::floor(
std::min(chargeColl[it], maxADC) / lsbADC);
133 newSample.
set(
adc > thrADC,
false, gainIdx, 0,
adc);
134 dataFrame.setSample(it, newSample);
141 std::ostringstream
msg;
142 dataFrame.print(
msg);
150 DFr& dataFrame,
HGCSimHitData& chargeColl, uint32_t thrADC,
float lsbADC, uint32_t gainIdx,
float maxADC) {
154 for (
int it = 0; it < (
int)(chargeColl.size()); it++) {
155 const float charge(chargeColl[it]);
166 for (
int ipulse = -2; ipulse < (
int)(adcPulse_.size()) - 2; ipulse++) {
169 if (it + ipulse >= (
int)(dataFrame.size()))
171 const float chargeLeak =
charge * adcPulse_[(ipulse + 2)];
172 newCharge[it + ipulse] += chargeLeak;
182 for (
int it = 0; it < (
int)(newCharge.size()); it++) {
184 const uint32_t
adc = std::floor(
std::min(newCharge[it], maxADC) / lsbADC);
186 newSample.
set(
adc > thrADC,
false, gainIdx, 0,
adc);
187 dataFrame.setSample(it, newSample);
194 std::ostringstream
msg;
195 dataFrame.print(
msg);
205 CLHEP::HepRandomEngine* engine,
211 busyFlags.fill(
false);
212 totFlags.fill(
false);
213 toaFlags.fill(
false);
215 toaFromToT.fill(0.
f);
218 constexpr
bool debug_state(
true);
220 constexpr
bool debug_state(
false);
223 bool debug = debug_state;
233 if (toaColl[fireBX] != 0.
f) {
234 timeToA = toaColl[fireBX];
235 float jitter = getTimeJitter(chargeColl[fireBX],
thickness);
237 timeToA = CLHEP::RandGaussQ::shoot(engine, timeToA, jitter);
238 else if (tdcResolutionInNs_ != 0)
239 timeToA = CLHEP::RandGaussQ::shoot(engine, timeToA, tdcResolutionInNs_);
240 if (timeToA >= 0.
f && timeToA <= 25.
f)
241 toaFlags[fireBX] =
true;
247 for (
int it = 0; it < (
int)(chargeColl.size()); ++it) {
254 float charge = chargeColl[it];
255 if (
charge < tdcOnset_fC_) {
266 edm::LogVerbatim(
"HGCFE") <<
"\t q=" <<
charge <<
" fC with <toa>=" << toa <<
" ns, triggers ToT @ " << it
272 float totalCharge(
charge), finalToA(toa), integTime(0);
277 float charge_offset = 0.f;
278 const float charge_kfC(totalCharge * 1
e-3);
279 if (charge_kfC < tdcChargeDrainParameterisation_[3]) {
281 }
else if (charge_kfC < tdcChargeDrainParameterisation_[7]) {
283 charge_offset = tdcChargeDrainParameterisation_[3];
287 charge_offset = tdcChargeDrainParameterisation_[7];
290 const float charge_mod = charge_kfC - charge_offset;
291 const float newIntegTime =
292 ((tdcChargeDrainParameterisation_[poffset] * charge_mod + tdcChargeDrainParameterisation_[poffset + 1]) *
294 tdcChargeDrainParameterisation_[poffset + 2]);
296 const int newBusyBxs = std::floor(newIntegTime / 25.
f) + 1;
300 integTime = newIntegTime;
301 if (newBusyBxs == busyBxs)
307 edm::LogVerbatim(
"HGCFE") <<
"\t Intial busy estimate=" << integTime <<
" ns = " << newBusyBxs <<
" bxs"
310 edm::LogVerbatim(
"HGCFE") <<
"\t ...integrated charge overflows initial busy estimate, interating again"
315 busyBxs = newBusyBxs;
319 if (toaMode_ == WEIGHTEDBYE)
323 for (
int jt = 0; jt < it; ++jt) {
324 const unsigned int deltaT = (it - jt);
325 if ((deltaT + 2) >= adcPulse_.size() || chargeColl[jt] == 0.f || totFlags[jt] || busyFlags[jt])
328 const float leakCharge = chargeColl[jt] * adcPulse_[deltaT + 2];
329 totalCharge += leakCharge;
330 if (toaMode_ == WEIGHTEDBYE)
331 finalToA += leakCharge * pulseAvgT_[deltaT + 2];
334 edm::LogVerbatim(
"HGCFE") <<
"\t\t leaking " << chargeColl[jt] <<
" fC @ deltaT=-" << deltaT <<
" -> +"
335 << leakCharge <<
" with avgT=" << pulseAvgT_[deltaT + 2] << std::endl;
339 for (
int jt = it + 1; jt < it + busyBxs && jt < dataFrame.size(); ++jt) {
342 busyFlags[jt] =
true;
344 const float extraCharge = chargeColl[jt];
345 if (extraCharge == 0.
f)
348 edm::LogVerbatim(
"HGCFE") <<
"\t\t adding " << extraCharge <<
" fC @ deltaT=+" << (jt - it) << std::endl;
350 totalCharge += extraCharge;
351 if (toaMode_ == WEIGHTEDBYE)
352 finalToA += extraCharge * toaColl[jt];
356 if (toaMode_ == WEIGHTEDBYE)
357 finalToA /= totalCharge;
359 newCharge[it] = (totalCharge - tdcOnset_fC_);
362 edm::LogVerbatim(
"HGCFE") <<
"\t Final busy estimate=" << integTime <<
" ns = " << busyBxs <<
" bxs" << std::endl
363 <<
"\t Total integrated=" << totalCharge <<
" fC <toa>=" << toaFromToT[it]
364 <<
" (raw=" << finalToA <<
") ns " << std::endl;
367 if (it + busyBxs < (
int)(newCharge.size())) {
368 const float deltaT2nextBx((busyBxs * 25 - integTime));
369 const float tdcOnsetLeakage(tdcOnset_fC_ *
vdt::fast_expf(-deltaT2nextBx / tdcChargeDrainParameterisation_[11]));
371 edm::LogVerbatim(
"HGCFE") <<
"\t Leaking remainder of TDC onset " << tdcOnset_fC_ <<
" fC, to be dissipated in "
372 << deltaT2nextBx <<
" DeltaT/tau=" << deltaT2nextBx <<
" / "
373 << tdcChargeDrainParameterisation_[11] <<
" ns, adds " << tdcOnsetLeakage <<
" fC @ "
374 << it + busyBxs <<
" bx (first free bx)" << std::endl;
375 newCharge[it + busyBxs] += tdcOnsetLeakage;
380 auto runChargeSharing = [&]() {
382 for (
int it = 0; it < (
int)(chargeColl.size()); ++it) {
385 if (!totFlags[it] & !busyFlags[it]) {
387 const int stop =
std::min((
int)adcPulse_.size(), (
int)newCharge.size() - it + 2);
388 for (ipulse =
start; ipulse < stop; ++ipulse) {
389 const int itoffset = it + ipulse - 2;
393 if (!totFlags[itoffset] & !busyFlags[itoffset]) {
394 newCharge[itoffset] += chargeColl[it] * adcPulse_[ipulse];
429 maxADC = adcSaturation_fC_;
430 for (
int it = 0; it < (
int)(newCharge.size()); it++) {
432 edm::LogVerbatim(
"HGCFE") << chargeColl[it] <<
" -> " << newCharge[it] <<
" ";
435 if (totFlags[it] || busyFlags[it]) {
438 const float saturatedCharge(
std::min(newCharge[it], tdcSaturation_fC_));
441 true,
true, gainIdx, (uint16_t)(timeToA / toaLSB_ns_), (uint16_t)(std::floor(saturatedCharge / tdcLSB_fC_)));
445 newSample.
set(
false,
true, gainIdx, 0, 0);
449 const uint16_t
adc = std::floor(
std::min(newCharge[it], maxADC) / lsbADC);
451 newSample.
set(
adc > thrADC,
false, gainIdx, (uint16_t)(timeToA / toaLSB_ns_),
adc);
455 dataFrame.setSample(it, newSample);
459 std::ostringstream
msg;
460 dataFrame.print(
msg);