6 #include "vdt/vdtMath.h" 17 adcSaturation_fC_{-1.0},
20 tdcSaturation_fC_{-1.0},
24 tdcResolutionInNs_{1
e-9},
25 targetMIPvalue_ADC_{},
28 eventTimeOffset_ns_{{0.02, 0.02, 0.02}},
30 toaMode_(WEIGHTEDBYE) {
31 edm::LogVerbatim(
"HGCFE") <<
"[HGCFEElectronics] running with version " << fwVersion_ << std::endl;
32 if (ps.exists(
"adcPulse")) {
33 auto temp = ps.getParameter<std::vector<double> >(
"adcPulse");
34 for (
unsigned i = 0;
i <
temp.size(); ++
i) {
38 for (
unsigned i = 0;
i < adcPulse_.size(); ++
i) {
39 adcPulse_[
i] = adcPulse_[
i] / adcPulse_[2];
41 temp = ps.getParameter<std::vector<double> >(
"pulseAvgT");
42 for (
unsigned i = 0;
i <
temp.size(); ++
i) {
46 if (ps.exists(
"adcNbits")) {
47 uint32_t
adcNbits = ps.getParameter<uint32_t>(
"adcNbits");
48 adcSaturation_fC_ = ps.getParameter<
double>(
"adcSaturation_fC");
51 <<
" with LSB=" << adcLSB_fC_ <<
" saturation to occur @ " << adcSaturation_fC_
55 if (ps.exists(
"tdcNbits")) {
56 tdcNbits_ = ps.getParameter<uint32_t>(
"tdcNbits");
57 setTDCfsc(ps.getParameter<
double>(
"tdcSaturation_fC"));
58 edm::LogVerbatim(
"HGCFE") <<
"[HGCFEElectronics] " << tdcNbits_ <<
" bit TDC defined with LSB=" << tdcLSB_fC_
59 <<
" saturation to occur @ " << tdcSaturation_fC_
60 <<
" (NB lowered by 1 part in a million)" << std::endl;
62 if (ps.exists(
"targetMIPvalue_ADC"))
63 targetMIPvalue_ADC_ = ps.getParameter<uint32_t>(
"targetMIPvalue_ADC");
64 if (ps.exists(
"adcThreshold_fC"))
65 adcThreshold_fC_ = ps.getParameter<
double>(
"adcThreshold_fC");
66 if (ps.exists(
"tdcOnset_fC"))
67 tdcOnset_fC_ = ps.getParameter<
double>(
"tdcOnset_fC");
68 if (ps.exists(
"tdcForToAOnset_fC")) {
69 auto temp = ps.getParameter<std::vector<double> >(
"tdcForToAOnset_fC");
70 if (
temp.size() == tdcForToAOnset_fC_.size()) {
71 std::copy_n(
temp.begin(),
temp.size(), tdcForToAOnset_fC_.begin());
73 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA thresholds ";
76 if (ps.exists(
"toaLSB_ns"))
77 toaLSB_ns_ = ps.getParameter<
double>(
"toaLSB_ns");
78 if (ps.exists(
"tdcChargeDrainParameterisation")) {
79 for (
auto val : ps.getParameter<std::vector<double> >(
"tdcChargeDrainParameterisation")) {
80 tdcChargeDrainParameterisation_.push_back((
float)
val);
83 if (ps.exists(
"tdcResolutionInPs"))
84 tdcResolutionInNs_ = ps.getParameter<
double>(
"tdcResolutionInPs") * 1
e-3;
85 if (ps.exists(
"toaMode"))
86 toaMode_ = ps.getParameter<uint32_t>(
"toaMode");
88 if (ps.exists(
"jitterNoise_ns")) {
89 auto temp = ps.getParameter<std::vector<double> >(
"jitterNoise_ns");
90 if (
temp.size() == jitterNoise_ns_.size()) {
91 std::copy_n(
temp.begin(),
temp.size(), jitterNoise_ns_.begin());
93 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA jitterNoise ";
96 if (ps.exists(
"jitterConstant_ns")) {
97 auto temp = ps.getParameter<std::vector<double> >(
"jitterConstant_ns");
98 if (
temp.size() == jitterConstant_ns_.size()) {
99 std::copy_n(
temp.begin(),
temp.size(), jitterConstant_ns_.begin());
101 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA jitterConstant ";
109 DFr& dataFrame,
HGCSimHitData& chargeColl, uint32_t thrADC,
float lsbADC, uint32_t gainIdx,
float maxADC) {
113 for (
int it = 0;
it < (
int)(chargeColl.size());
it++)
114 debug |= (chargeColl[
it] > adcThreshold_fC_);
126 maxADC = adcSaturation_fC_ * (1 - 1
e-6);
127 for (
int it = 0;
it < (
int)(chargeColl.size());
it++) {
129 const uint32_t
adc = std::floor(
std::min(chargeColl[
it], maxADC) / lsbADC);
131 newSample.
set(
adc > thrADC,
false, gainIdx, 0,
adc);
132 dataFrame.setSample(
it, newSample);
139 std::ostringstream
msg;
140 dataFrame.print(
msg);
157 for (
int it = 0;
it < (
int)(chargeColl.size());
it++) {
169 for (
int ipulse = -2; ipulse < (
int)(
adcPulse.size()) - 2; ipulse++) {
172 if (
it + ipulse >= (
int)(dataFrame.size()))
175 newCharge[
it + ipulse] += chargeLeak;
185 for (
int it = 0;
it < (
int)(newCharge.size());
it++) {
187 const uint32_t
adc = std::floor(
std::min(newCharge[
it], maxADC) / lsbADC);
189 newSample.
set(
adc > thrADC,
false, gainIdx, 0,
adc);
190 dataFrame.setSample(
it, newSample);
197 std::ostringstream
msg;
198 dataFrame.print(
msg);
208 CLHEP::HepRandomEngine* engine,
217 busyFlags.fill(
false);
218 totFlags.fill(
false);
219 toaFlags.fill(
false);
221 toaFromToT.fill(0.
f);
229 bool debug = debug_state;
236 maxADC = adcSaturation_fC_;
250 if (toaColl[fireBX] != 0.
f && chargeColl[fireBX] > tdcForToAOnset_fC_[
thickness - 1]) {
251 timeToA = toaColl[fireBX];
252 float sensor_noise = noiseWidth <= 0 ? noise_fC_[
thickness - 1] : noiseWidth;
254 float jitter = chargeColl[fireBX] == 0 ? 0 : (
noise / chargeColl[fireBX]);
256 timeToA = CLHEP::RandGaussQ::shoot(engine, timeToA, jitter);
257 else if (tdcResolutionInNs_ != 0)
258 timeToA = CLHEP::RandGaussQ::shoot(engine, timeToA, tdcResolutionInNs_);
259 timeToA += eventTimeOffset_ns_[
thickness - 1];
260 if (timeToA >= 0.
f && timeToA <= 25.
f)
261 toaFlags[fireBX] =
true;
267 for (
int it = 0;
it < (
int)(chargeColl.size()); ++
it) {
273 if (tdcOnsetAuto < 0) {
274 tdcOnsetAuto = tdcOnset_fC_;
295 float totalCharge(
charge), finalToA(toa), integTime(0);
300 float charge_offset = 0.f;
301 const float charge_kfC(totalCharge * 1
e-3);
302 if (charge_kfC < tdcChargeDrainParameterisation_[3]) {
304 }
else if (charge_kfC < tdcChargeDrainParameterisation_[7]) {
306 charge_offset = tdcChargeDrainParameterisation_[3];
310 charge_offset = tdcChargeDrainParameterisation_[7];
313 const float charge_mod = charge_kfC - charge_offset;
314 const float newIntegTime =
315 ((tdcChargeDrainParameterisation_[poffset] * charge_mod + tdcChargeDrainParameterisation_[poffset + 1]) *
317 tdcChargeDrainParameterisation_[poffset + 2]);
319 const int newBusyBxs = std::floor(newIntegTime / 25.
f) + 1;
323 integTime = newIntegTime;
324 if (newBusyBxs == busyBxs)
330 edm::LogVerbatim(
"HGCFE") <<
"\t Intial busy estimate=" << integTime <<
" ns = " << newBusyBxs <<
" bxs" 333 edm::LogVerbatim(
"HGCFE") <<
"\t ...integrated charge overflows initial busy estimate, interating again" 338 busyBxs = newBusyBxs;
342 if (toaMode_ == WEIGHTEDBYE)
346 for (
int jt = 0; jt <
it; ++jt) {
347 const unsigned int deltaT = (
it - jt);
348 if ((deltaT + 2) >=
adcPulse.size() || chargeColl[jt] == 0.f || totFlags[jt] || busyFlags[jt])
351 const float leakCharge = chargeColl[jt] *
adcPulse[deltaT + 2];
352 totalCharge += leakCharge;
353 if (toaMode_ == WEIGHTEDBYE)
354 finalToA += leakCharge * pulseAvgT_[deltaT + 2];
357 edm::LogVerbatim(
"HGCFE") <<
"\t\t leaking " << chargeColl[jt] <<
" fC @ deltaT=-" << deltaT <<
" -> +" 358 << leakCharge <<
" with avgT=" << pulseAvgT_[deltaT + 2] << std::endl;
362 for (
int jt =
it + 1; jt <
it + busyBxs && jt < dataFrame.size(); ++jt) {
365 busyFlags[jt] =
true;
367 const float extraCharge = chargeColl[jt];
368 if (extraCharge == 0.
f)
371 edm::LogVerbatim(
"HGCFE") <<
"\t\t adding " << extraCharge <<
" fC @ deltaT=+" << (jt -
it) << std::endl;
373 totalCharge += extraCharge;
374 if (toaMode_ == WEIGHTEDBYE)
375 finalToA += extraCharge * toaColl[jt];
379 if (toaMode_ == WEIGHTEDBYE)
380 finalToA /= totalCharge;
386 edm::LogVerbatim(
"HGCFE") <<
"\t Final busy estimate=" << integTime <<
" ns = " << busyBxs <<
" bxs" << std::endl
387 <<
"\t Total integrated=" << totalCharge <<
" fC <toa>=" << toaFromToT[
it]
388 <<
" (raw=" << finalToA <<
") ns " << std::endl;
391 if (
it + busyBxs < (
int)(newCharge.size())) {
392 const float deltaT2nextBx((busyBxs * 25 - integTime));
393 const float tdcOnsetLeakage(
tdcOnset *
vdt::fast_expf(-deltaT2nextBx / tdcChargeDrainParameterisation_[11]));
396 << deltaT2nextBx <<
" DeltaT/tau=" << deltaT2nextBx <<
" / " 397 << tdcChargeDrainParameterisation_[11] <<
" ns, adds " << tdcOnsetLeakage <<
" fC @ " 398 <<
it + busyBxs <<
" bx (first free bx)" << std::endl;
399 newCharge[
it + busyBxs] += tdcOnsetLeakage;
404 auto runChargeSharing = [&]() {
406 for (
int it = 0;
it < (
int)(chargeColl.size()); ++
it) {
409 if (!totFlags[
it] && !busyFlags[
it]) {
412 for (ipulse =
start; ipulse <
stop; ++ipulse) {
413 const int itoffset =
it + ipulse - 2;
417 if (!totFlags[itoffset] && !busyFlags[itoffset]) {
418 newCharge[itoffset] += chargeColl[
it] *
adcPulse[ipulse];
450 for (
int it = 0;
it < (
int)(newCharge.size());
it++) {
455 if (totFlags[
it] || busyFlags[
it]) {
458 const float saturatedCharge(
std::min(newCharge[
it], tdcSaturation_fC_));
461 true,
true, gainIdx, (uint16_t)(timeToA / toaLSB_ns_), (uint16_t)(std::floor(saturatedCharge / tdcLSB_fC_)));
465 newSample.
set(
false,
true, gainIdx, 0, 0);
469 const uint16_t
adc = std::floor(
std::min(newCharge[
it], maxADC) / lsbADC);
471 newSample.
set(
adc > thrADC,
false, gainIdx, (uint16_t)(timeToA / toaLSB_ns_),
adc);
475 dataFrame.setSample(
it, newSample);
479 std::ostringstream
msg;
480 dataFrame.print(
msg);
Log< level::Info, true > LogVerbatim
T getParameter(std::string const &) const
void runShaperWithToT(DFr &dataFrame, hgc::HGCSimHitData &chargeColl, hgc::HGCSimHitData &toa, CLHEP::HepRandomEngine *engine, uint32_t thrADC, float lsbADC, uint32_t gainIdx, float maxADC, int thickness, float tdcOnsetAuto, float noiseWidth, const hgc_digi::FEADCPulseShape &adcPulse)
implements pulse shape and switch to time over threshold including deadtime
std::array< HGCSimData_t, nSamples > HGCSimHitData
HGCFEElectronics(const edm::ParameterSet &ps)
CTOR.
void setToAValid(bool toaFired)
void runSimpleShaper(DFr &dataFrame, hgc::HGCSimHitData &chargeColl, uint32_t thrADC, float lsbADC, uint32_t gainIdx, float maxADC, const hgc_digi::FEADCPulseShape &adcPulse)
applies a shape to each time sample and propagates the tails to the subsequent time samples ...
void runTrivialShaper(DFr &dataFrame, hgc::HGCSimHitData &chargeColl, uint32_t thrADC, float lsbADC, uint32_t gainIdx, float maxADC)
converts charge to digis without pulse shape
models the behavior of the front-end electronics
void set(bool thr, bool mode, uint16_t gain, uint16_t toa, uint16_t data)
Power< A, B >::type pow(const A &a, const B &b)
uint16_t *__restrict__ uint16_t const *__restrict__ adc
std::array< float, 6 > FEADCPulseShape