6 #include "vdt/vdtMath.h"
8 using namespace hgc_digi;
31 if (ps.
exists(
"adcPulse")) {
33 for (
unsigned i = 0;
i <
temp.size(); ++
i) {
41 for (
unsigned i = 0;
i <
temp.size(); ++
i) {
45 if (ps.
exists(
"adcNbits")) {
46 uint32_t adcNbits = ps.
getParameter<uint32_t>(
"adcNbits");
49 edm::LogVerbatim(
"HGCFE") <<
"[HGCFEElectronics] " << adcNbits <<
" bit ADC defined"
50 <<
" with LSB=" << adcLSB_fC_ <<
" saturation to occur @ " << adcSaturation_fC_
54 if (ps.
exists(
"tdcNbits")) {
59 <<
" (NB lowered by 1 part in a million)" << std::endl;
61 if (ps.
exists(
"targetMIPvalue_ADC"))
63 if (ps.
exists(
"adcThreshold_fC"))
65 if (ps.
exists(
"tdcOnset_fC"))
67 if (ps.
exists(
"tdcForToAOnset_fC")) {
72 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA thresholds ";
75 if (ps.
exists(
"toaLSB_ns"))
77 if (ps.
exists(
"tdcChargeDrainParameterisation")) {
78 for (
auto val : ps.
getParameter<std::vector<double> >(
"tdcChargeDrainParameterisation")) {
82 if (ps.
exists(
"tdcResolutionInPs"))
87 if (ps.
exists(
"jitterNoise_ns")) {
92 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA jitterNoise ";
95 if (ps.
exists(
"jitterConstant_ns")) {
100 throw cms::Exception(
"BadConfiguration") <<
" HGCFEElectronics wrong size for ToA jitterConstant ";
108 DFr& dataFrame,
HGCSimHitData& chargeColl, uint32_t thrADC,
float lsbADC, uint32_t gainIdx,
float maxADC) {
112 for (
int it = 0; it < (int)(chargeColl.size()); it++)
126 for (
int it = 0; it < (int)(chargeColl.size()); it++) {
128 const uint32_t
adc = std::floor(
std::min(chargeColl[it], maxADC) / lsbADC);
130 newSample.
set(adc > thrADC,
false, gainIdx, 0, adc);
131 dataFrame.setSample(it, newSample);
138 std::ostringstream
msg;
139 dataFrame.print(msg);
156 for (
int it = 0; it < (int)(chargeColl.size()); it++) {
157 const float charge(chargeColl[it]);
166 edm::LogVerbatim(
"HGCFE") <<
"\t Redistributing SARS ADC" << charge <<
" @ " << it;
168 for (
int ipulse = -2; ipulse < (int)(adcPulse.size()) - 2; ipulse++) {
171 if (it + ipulse >= (
int)(dataFrame.size()))
173 const float chargeLeak = charge * adcPulse[(ipulse + 2)];
184 for (
int it = 0; it < (int)(
newCharge.size()); it++) {
188 newSample.
set(adc > thrADC,
false, gainIdx, 0, adc);
189 dataFrame.setSample(it, newSample);
196 std::ostringstream
msg;
197 dataFrame.print(msg);
207 CLHEP::HepRandomEngine* engine,
222 constexpr
bool debug_state(
true);
224 constexpr
bool debug_state(
false);
227 bool debug = debug_state;
232 double tdcOnset(maxADC);
249 if (toaColl[fireBX] != 0.
f) {
250 timeToA = toaColl[fireBX];
253 timeToA = CLHEP::RandGaussQ::shoot(engine, timeToA, jitter);
256 if (timeToA >= 0.
f && timeToA <= 25.
f)
263 for (
int it = 0; it < (int)(chargeColl.size()); ++it) {
269 if (tdcOnsetAuto < 0) {
273 float charge = chargeColl[it];
274 if (charge < tdcOnset) {
285 edm::LogVerbatim(
"HGCFE") <<
"\t q=" << charge <<
" fC with <toa>=" << toa <<
" ns, triggers ToT @ " << it
291 float totalCharge(charge), finalToA(toa), integTime(0);
296 float charge_offset = 0.f;
297 const float charge_kfC(totalCharge * 1
e-3);
309 const float charge_mod = charge_kfC - charge_offset;
310 const float newIntegTime =
315 const int newBusyBxs = std::floor(newIntegTime / 25.
f) + 1;
319 integTime = newIntegTime;
320 if (newBusyBxs == busyBxs)
326 edm::LogVerbatim(
"HGCFE") <<
"\t Intial busy estimate=" << integTime <<
" ns = " << newBusyBxs <<
" bxs"
329 edm::LogVerbatim(
"HGCFE") <<
"\t ...integrated charge overflows initial busy estimate, interating again"
334 busyBxs = newBusyBxs;
342 for (
int jt = 0; jt < it; ++jt) {
343 const unsigned int deltaT = (it - jt);
344 if ((deltaT + 2) >= adcPulse.size() || chargeColl[jt] == 0.f ||
totFlags[jt] ||
busyFlags[jt])
347 const float leakCharge = chargeColl[jt] * adcPulse[deltaT + 2];
348 totalCharge += leakCharge;
350 finalToA += leakCharge *
pulseAvgT_[deltaT + 2];
353 edm::LogVerbatim(
"HGCFE") <<
"\t\t leaking " << chargeColl[jt] <<
" fC @ deltaT=-" << deltaT <<
" -> +"
354 << leakCharge <<
" with avgT=" << pulseAvgT_[deltaT + 2] << std::endl;
358 for (
int jt = it + 1; jt < it + busyBxs && jt < dataFrame.size(); ++jt) {
363 const float extraCharge = chargeColl[jt];
364 if (extraCharge == 0.
f)
367 edm::LogVerbatim(
"HGCFE") <<
"\t\t adding " << extraCharge <<
" fC @ deltaT=+" << (jt - it) << std::endl;
369 totalCharge += extraCharge;
371 finalToA += extraCharge * toaColl[jt];
376 finalToA /= totalCharge;
379 newCharge[it] = (totalCharge - tdcOnset);
382 edm::LogVerbatim(
"HGCFE") <<
"\t Final busy estimate=" << integTime <<
" ns = " << busyBxs <<
" bxs" << std::endl
383 <<
"\t Total integrated=" << totalCharge <<
" fC <toa>=" <<
toaFromToT[it]
384 <<
" (raw=" << finalToA <<
") ns " << std::endl;
387 if (it + busyBxs < (
int)(
newCharge.size())) {
388 const float deltaT2nextBx((busyBxs * 25 - integTime));
391 edm::LogVerbatim(
"HGCFE") <<
"\t Leaking remainder of TDC onset " << tdcOnset <<
" fC, to be dissipated in "
392 << deltaT2nextBx <<
" DeltaT/tau=" << deltaT2nextBx <<
" / "
394 << it + busyBxs <<
" bx (first free bx)" << std::endl;
395 newCharge[it + busyBxs] += tdcOnsetLeakage;
400 auto runChargeSharing = [&]() {
402 for (
int it = 0; it < (int)(chargeColl.size()); ++it) {
408 for (ipulse = start; ipulse < stop; ++ipulse) {
409 const int itoffset = it + ipulse - 2;
414 newCharge[itoffset] += chargeColl[it] * adcPulse[ipulse];
446 for (
int it = 0; it < (int)(
newCharge.size()); it++) {
457 true,
true, gainIdx, (uint16_t)(timeToA /
toaLSB_ns_), (uint16_t)(std::floor(saturatedCharge /
tdcLSB_fC_)));
461 newSample.
set(
false,
true, gainIdx, 0, 0);
465 const uint16_t
adc = std::floor(
std::min(newCharge[it], maxADC) / lsbADC);
467 newSample.
set(adc > thrADC,
false, gainIdx, (uint16_t)(timeToA /
toaLSB_ns_), adc);
471 dataFrame.setSample(it, newSample);
475 std::ostringstream
msg;
476 dataFrame.print(msg);
std::array< bool, hgc::nSamples > busyFlags
Log< level::Info, true > LogVerbatim
std::array< float, 3 > jitterConstant2_ns_
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, const hgc_digi::FEADCPulseShape &adcPulse)
implements pulse shape and switch to time over threshold including deadtime
bool exists(std::string const ¶meterName) const
checks if a parameter exists
std::array< float, 3 > jitterNoise2_ns_
hgc::HGCSimHitData toaFromToT
std::array< HGCSimData_t, nSamples > HGCSimHitData
float getTimeJitter(float totalCharge, int thickness)
HGCFEElectronics(const edm::ParameterSet &ps)
CTOR.
uint32_t targetMIPvalue_ADC_
hgc_digi::FEADCPulseShape adcPulse_
std::vector< float > noise_fC_
void setToAValid(bool toaFired)
T getParameter(std::string const &) const
std::array< bool, hgc::nSamples > totFlags
hgc_digi::FEADCPulseShape pulseAvgT_
void setTDCfsc(float newTDCfsc)
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
std::vector< float > tdcChargeDrainParameterisation_
models the behavior of the front-end electronics
void set(bool thr, bool mode, uint16_t gain, uint16_t toa, uint16_t data)
hgc::HGCSimHitData newCharge
Power< A, B >::type pow(const A &a, const B &b)
uint16_t *__restrict__ uint16_t const *__restrict__ adc
std::array< float, 6 > FEADCPulseShape
std::array< float, 3 > tdcForToAOnset_fC_
std::array< bool, hgc::nSamples > toaFlags