CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
HGCFEElectronics.cc
Go to the documentation of this file.
4 
5 #include "vdt/vdtMath.h"
6 
7 using namespace hgc_digi;
8 
9 //
10 template<class DFr>
12  toaMode_(WEIGHTEDBYE)
13 {
14  tdcResolutionInNs_ = 1e-9; // set time resolution very small by default
15 
16  fwVersion_ = ps.getParameter< uint32_t >("fwVersion");
17  edm::LogVerbatim("HGCFE") << "[HGCFEElectronics] running with version " << fwVersion_ << std::endl;
18  if( ps.exists("adcPulse") )
19  {
20  auto temp = ps.getParameter< std::vector<double> >("adcPulse");
21  for( unsigned i = 0; i < temp.size(); ++i ) {
22  adcPulse_[i] = (float)temp[i];
23  }
24  // normalize adc pulse
25  for( unsigned i = 0; i < adcPulse_.size(); ++i ) {
27  }
28  temp = ps.getParameter< std::vector<double> >("pulseAvgT");
29  for( unsigned i = 0; i < temp.size(); ++i ) {
30  pulseAvgT_[i] = (float)temp[i];
31  }
32  }
33  adcSaturation_fC_=-1.0;
34  if( ps.exists("adcNbits") )
35  {
36  uint32_t adcNbits = ps.getParameter<uint32_t>("adcNbits");
37  adcSaturation_fC_ = ps.getParameter<double>("adcSaturation_fC");
38  adcLSB_fC_=adcSaturation_fC_/pow(2.,adcNbits);
39  edm::LogVerbatim("HGCFE")
40  << "[HGCFEElectronics] " << adcNbits << " bit ADC defined"
41  << " with LSB=" << adcLSB_fC_
42  << " saturation to occur @ " << adcSaturation_fC_ << std::endl;
43  }
44 
45  tdcSaturation_fC_=-1.0;
46  if( ps.exists("tdcNbits") )
47  {
48  uint32_t tdcNbits = ps.getParameter<uint32_t>("tdcNbits");
49  tdcSaturation_fC_ = ps.getParameter<double>("tdcSaturation_fC");
50  tdcLSB_fC_=tdcSaturation_fC_/pow(2.,tdcNbits);
51  edm::LogVerbatim("HGCFE")
52  << "[HGCFEElectronics] " << tdcNbits << " bit TDC defined with LSB="
53  << tdcLSB_fC_ << " saturation to occur @ " << tdcSaturation_fC_ << std::endl;
54  }
55  if( ps.exists("adcThreshold_fC") ) adcThreshold_fC_ = ps.getParameter<double>("adcThreshold_fC");
56  if( ps.exists("tdcOnset_fC") ) tdcOnset_fC_ = ps.getParameter<double>("tdcOnset_fC");
57  if( ps.exists("toaLSB_ns") ) toaLSB_ns_ = ps.getParameter<double>("toaLSB_ns");
58  if( ps.exists("tdcChargeDrainParameterisation") ) {
59  for( auto val : ps.getParameter< std::vector<double> >("tdcChargeDrainParameterisation") ) {
60  tdcChargeDrainParameterisation_.push_back((float)val);
61  }
62  }
63  if( ps.exists("tdcResolutionInPs") ) tdcResolutionInNs_ = ps.getParameter<double>("tdcResolutionInPs")*1e-3; // convert to ns
64  if( ps.exists("toaMode") ) toaMode_ = ps.getParameter<uint32_t>("toaMode");
65 }
66 
67 
68 //
69 template<class DFr>
70 void HGCFEElectronics<DFr>::runTrivialShaper(DFr &dataFrame, HGCSimHitData& chargeColl, int thickness)
71 {
72  bool debug(false);
73 
74 #ifdef EDM_ML_DEBUG
75  for(int it=0; it<(int)(chargeColl.size()); it++) debug |= (chargeColl[it]>adcThreshold_fC_);
76 #endif
77 
78  if(debug) edm::LogVerbatim("HGCFE") << "[runTrivialShaper]" << std::endl;
79 
80  //set new ADCs
81 
82  for(int it=0; it<(int)(chargeColl.size()); it++)
83  {
84  //brute force saturation, maybe could to better with an exponential like saturation
85  const uint32_t adc=std::floor( std::min(chargeColl[it],adcSaturation_fC_) / adcLSB_fC_ );
86  HGCSample newSample;
87  newSample.set(chargeColl[it]>thickness*adcThreshold_fC_,false,0,adc);
88  dataFrame.setSample(it,newSample);
89 
90  if(debug) edm::LogVerbatim("HGCFE") << adc << " (" << chargeColl[it] << "/" << adcLSB_fC_ << ") ";
91  }
92 
93  if(debug) {
94  std::ostringstream msg;
95  dataFrame.print(msg);
96  edm::LogVerbatim("HGCFE") << msg.str() << std::endl;
97  }
98 }
99 
100 //
101 template<class DFr>
102 void HGCFEElectronics<DFr>::runSimpleShaper(DFr &dataFrame, HGCSimHitData& chargeColl, int thickness)
103 {
104  //convolute with pulse shape to compute new ADCs
105  newCharge.fill(0.f);
106  bool debug(false);
107  for(int it=0; it<(int)(chargeColl.size()); it++)
108  {
109  const float charge(chargeColl[it]);
110  if(charge==0.f) continue;
111 
112 #ifdef EDM_ML_DEBUG
113  debug|=(charge>adcThreshold_fC_);
114 #endif
115 
116  if(debug) edm::LogVerbatim("HGCFE") << "\t Redistributing SARS ADC" << charge << " @ " << it;
117 
118  for(int ipulse=-2; ipulse<(int)(adcPulse_.size())-2; ipulse++)
119  {
120  if(it+ipulse<0) continue;
121  if(it+ipulse>=(int)(dataFrame.size())) continue;
122  const float chargeLeak=charge*adcPulse_[(ipulse+2)];
123  newCharge[it+ipulse]+= chargeLeak;
124 
125  if(debug) edm::LogVerbatim("HGCFE") << " | " << it+ipulse << " " << chargeLeak;
126  }
127 
128  if(debug) edm::LogVerbatim("HGCFE") << std::endl;
129  }
130 
131  //set new ADCs
132  for(int it=0; it<(int)(newCharge.size()); it++)
133  {
134  //brute force saturation, maybe could to better with an exponential like saturation
135  const float saturatedCharge(std::min(newCharge[it],adcSaturation_fC_));
136  HGCSample newSample;
137  newSample.set(newCharge[it]>thickness*adcThreshold_fC_,false,0,floor(saturatedCharge/adcLSB_fC_));
138  dataFrame.setSample(it,newSample);
139 
140  if(debug) edm::LogVerbatim("HGCFE") << std::floor(saturatedCharge/adcLSB_fC_) << " (" << saturatedCharge << "/" << adcLSB_fC_ <<" ) " ;
141  }
142 
143  if(debug) {
144  std::ostringstream msg;
145  dataFrame.print(msg);
146  edm::LogVerbatim("HGCFE") << msg.str() << std::endl;
147  }
148 }
149 
150 //
151 template<class DFr>
152 void HGCFEElectronics<DFr>::runShaperWithToT(DFr &dataFrame, HGCSimHitData& chargeColl, HGCSimHitData& toaColl, int thickness, CLHEP::HepRandomEngine* engine)
153 {
154  busyFlags.fill(false);
155  totFlags.fill(false);
156  newCharge.fill( 0.f );
157  toaFromToT.fill( 0.f );
158 
159 #ifdef EDM_ML_DEBUG
160  constexpr bool debug_state(true);
161 #else
162  constexpr bool debug_state(false);
163 #endif
164 
165  bool debug = debug_state;
166 
167  //first identify bunches which will trigger ToT
168  //if(debug_state) edm::LogVerbatim("HGCFE") << "[runShaperWithToT]" << std::endl;
169  for(int it=0; it<(int)(chargeColl.size()); ++it)
170  {
171  debug = debug_state;
172  //if already flagged as busy it can't be re-used to trigger the ToT
173  if(busyFlags[it]) continue;
174 
175  //if below TDC onset will be handled by SARS ADC later
176  float charge = chargeColl[it];
177  if(charge < tdcOnset_fC_) {
178  debug = false;
179  continue;
180  }
181 
182  //raise TDC mode
183  float toa = toaColl[it];
184  totFlags[it]=true;
185 
186  if(debug) edm::LogVerbatim("HGCFE") << "\t q=" << charge << " fC with <toa>=" << toa << " ns, triggers ToT @ " << it << std::endl;
187 
188  //compute total charge to be integrated and integration time
189  //needs a loop as ToT will last as long as there is charge to dissipate
190  int busyBxs(0);
191  float totalCharge(charge), finalToA(toa), integTime(0);
192  while(true) {
193  //compute integration time in ns and # bunches
194  //float newIntegTime(0);
195  int poffset = 0;
196  float charge_offset = 0.f;
197  const float charge_kfC(totalCharge*1e-3);
198  if(charge_kfC<tdcChargeDrainParameterisation_[3]) {
199  //newIntegTime=tdcChargeDrainParameterisation_[0]*pow(charge_kfC,2)+tdcChargeDrainParameterisation_[1]*charge_kfC+tdcChargeDrainParameterisation_[2];
200  } else if(charge_kfC<tdcChargeDrainParameterisation_[7]) {
201  poffset = 4;
202  charge_offset = tdcChargeDrainParameterisation_[3];
203  //newIntegTime=tdcChargeDrainParameterisation_[4]*pow(charge_kfC-tdcChargeDrainParameterisation_[3],2)+tdcChargeDrainParameterisation_[5]*(charge_kfC-tdcChargeDrainParameterisation_[3])+tdcChargeDrainParameterisation_[6];
204  } else {
205  poffset = 8;
206  charge_offset = tdcChargeDrainParameterisation_[7];
207  //newIntegTime=tdcChargeDrainParameterisation_[8]*pow(charge_kfC-tdcChargeDrainParameterisation_[7],2)+tdcChargeDrainParameterisation_[9]*(charge_kfC-tdcChargeDrainParameterisation_[7])+tdcChargeDrainParameterisation_[10];
208  }
209  const float charge_mod = charge_kfC - charge_offset;
210  const float newIntegTime = ( ( tdcChargeDrainParameterisation_[poffset]*charge_mod +
211  tdcChargeDrainParameterisation_[poffset+1] )*charge_mod +
212  tdcChargeDrainParameterisation_[poffset+2] );
213 
214  const int newBusyBxs=std::floor(newIntegTime/25.f)+1;
215 
216  //if no update is needed regarding the number of bunches,
217  //then the ToT integration time has converged
218  integTime=newIntegTime;
219  if(newBusyBxs==busyBxs) break;
220 
221  //update charge integrated during ToT
222  if(debug)
223  {
224  if(busyBxs==0) edm::LogVerbatim("HGCFE") << "\t Intial busy estimate="<< integTime << " ns = " << newBusyBxs << " bxs" << std::endl;
225  else edm::LogVerbatim("HGCFE") << "\t ...integrated charge overflows initial busy estimate, interating again" << std::endl;
226  }
227 
228  //update number of busy bunches
229  busyBxs=newBusyBxs;
230 
231  //reset charge to be integrated
232  totalCharge=charge;
233  if(toaMode_==WEIGHTEDBYE) finalToA=toa*charge;
234 
235  //add leakage from previous bunches in SARS ADC mode
236  for(int jt=0; jt<it; ++jt)
237  {
238  const unsigned int deltaT=(it-jt);
239  if((deltaT+2) >= adcPulse_.size() || chargeColl[jt]==0.f || totFlags[jt] || busyFlags[jt]) continue;
240 
241  const float leakCharge = chargeColl[jt]*adcPulse_[deltaT+2];
242  totalCharge += leakCharge;
243  if(toaMode_==WEIGHTEDBYE) finalToA += leakCharge*pulseAvgT_[deltaT+2];
244 
245  if(debug) edm::LogVerbatim("HGCFE") << "\t\t leaking " << chargeColl[jt] << " fC @ deltaT=-" << deltaT << " -> +" << leakCharge << " with avgT=" << pulseAvgT_[deltaT+2] << std::endl;
246  }
247 
248  //add contamination from posterior bunches
249  for(int jt=it+1; jt<it+busyBxs && jt<dataFrame.size() ; ++jt)
250  {
251  //this charge will be integrated in TDC mode
252  //disable for SARS ADC
253  busyFlags[jt]=true;
254 
255  const float extraCharge=chargeColl[jt];
256  if(extraCharge==0.f) continue;
257  if(debug) edm::LogVerbatim("HGCFE") << "\t\t adding " << extraCharge << " fC @ deltaT=+" << (jt-it) << std::endl;
258 
259  totalCharge += extraCharge;
260  if(toaMode_==WEIGHTEDBYE) finalToA += extraCharge*toaColl[jt];
261  }
262 
263  //finalize ToA contamination
264  if(toaMode_==WEIGHTEDBYE) finalToA /= totalCharge;
265  }
266 
267  toaFromToT[it] = CLHEP::RandGaussQ::shoot(engine,finalToA,tdcResolutionInNs_);
268  newCharge[it] = (totalCharge-tdcOnset_fC_);
269 
270  if(debug) edm::LogVerbatim("HGCFE") << "\t Final busy estimate="<< integTime << " ns = " << busyBxs << " bxs" << std::endl
271  << "\t Total integrated=" << totalCharge << " fC <toa>=" << toaFromToT[it] << " (raw=" << finalToA << ") ns " << std::endl;
272 
273  //last fC (tdcOnset) are dissipated trough pulse
274  if(it+busyBxs<(int)(newCharge.size()))
275  {
276  const float deltaT2nextBx((busyBxs*25-integTime));
277  const float tdcOnsetLeakage(tdcOnset_fC_*vdt::fast_expf(-deltaT2nextBx/tdcChargeDrainParameterisation_[11]));
278  if(debug) edm::LogVerbatim("HGCFE") << "\t Leaking remainder of TDC onset " << tdcOnset_fC_
279  << " fC, to be dissipated in " << deltaT2nextBx
280  << " DeltaT/tau=" << deltaT2nextBx << " / " << tdcChargeDrainParameterisation_[11]
281  << " ns, adds " << tdcOnsetLeakage << " fC @ " << it+busyBxs << " bx (first free bx)" << std::endl;
282  newCharge[it+busyBxs] += tdcOnsetLeakage;
283  }
284  }
285 
286  //including the leakage from bunches in SARS ADC when not declared busy or in ToT
287  auto runChargeSharing = [&]() {
288  int ipulse = 0;
289  for(int it=0; it<(int)(chargeColl.size()); ++it)
290  {
291  //if busy, charge has been already integrated
292  //if(debug) edm::LogVerbatim("HGCFE") << "\t SARS ADC pulse activated @ " << it << " : ";
293  if( !totFlags[it] & !busyFlags[it] ) {
294  const int start = std::max(0,2-it);
295  const int stop = std::min((int)adcPulse_.size(),(int)newCharge.size()-it+2);
296  for(ipulse = start; ipulse < stop; ++ipulse) {
297  const int itoffset = it + ipulse - 2;
298  //notice that if the channel is already busy,
299  //it has already been affected by the leakage of the SARS ADC
300  //if(totFlags[itoffset] || busyFlags[itoffset]) continue;
301  if( !totFlags[itoffset] & !busyFlags[itoffset] ) {
302  newCharge[itoffset] += chargeColl[it]*adcPulse_[ipulse];
303  }
304  //if(debug) edm::LogVerbatim("HGCFE") << " | " << itoffset << " " << chargeColl[it]*adcPulse_[ipulse] << "( " << chargeColl[it] << "->";
305  //if(debug) edm::LogVerbatim("HGCFE") << newCharge[itoffset] << ") ";
306  }
307  }
308 
309  if(debug) edm::LogVerbatim("HGCFE") << std::endl;
310  }
311  };
312  runChargeSharing();
313 
314  //set new ADCs and ToA
315  if(debug) edm::LogVerbatim("HGCFE") << "\t final result : ";
316  const float adj_thresh = thickness*adcThreshold_fC_;
317  for(int it=0; it<(int)(newCharge.size()); it++)
318  {
319  if(debug) edm::LogVerbatim("HGCFE") << chargeColl[it] << " -> " << newCharge[it] << " ";
320 
321  HGCSample newSample;
322  if(totFlags[it] || busyFlags[it])
323  {
324  if(totFlags[it])
325  {
326  float finalToA(toaFromToT[it]);
327  while(finalToA < 0.f) finalToA+=25.f;
328  while(finalToA > 25.f) finalToA-=25.f;
329 
330  //brute force saturation, maybe could to better with an exponential like saturation
331  const float saturatedCharge(std::min(newCharge[it],tdcSaturation_fC_));
332  newSample.set(true,true,(uint16_t)(finalToA/toaLSB_ns_),(uint16_t)(std::floor(saturatedCharge/tdcLSB_fC_)));
333  }
334  else
335  {
336  newSample.set(false,true,0,0);
337  }
338  }
339  else
340  {
341  //brute force saturation, maybe could to better with an exponential like saturation
342  const float saturatedCharge(std::min(newCharge[it],adcSaturation_fC_));
343  newSample.set(newCharge[it]>adj_thresh,false,(uint16_t)0,(uint16_t)(std::floor(saturatedCharge/adcLSB_fC_)));
344  }
345  dataFrame.setSample(it,newSample);
346  }
347 
348  if(debug) {
349  std::ostringstream msg;
350  dataFrame.print(msg);
351  edm::LogVerbatim("HGCFE") << msg.str() << std::endl;
352  }
353 }
354 
355 // cause the compiler to generate the appropriate code
357 template class HGCFEElectronics<HGCEEDataFrame>;
358 template class HGCFEElectronics<HGCBHDataFrame>;
359 
int adc(sample_type sample)
get the ADC sample (12 bits)
T getParameter(std::string const &) const
int i
Definition: DBlmapReader.cc:9
void runSimpleShaper(DFr &dataFrame, hgc::HGCSimHitData &chargeColl, int thickness)
applies a shape to each time sample and propagates the tails to the subsequent time samples ...
bool exists(std::string const &parameterName) const
checks if a parameter exists
wrapper for a data word
Definition: HGCSample.h:13
#define constexpr
std::array< HGCSimData_t, nSamples > HGCSimHitData
HGCFEElectronics(const edm::ParameterSet &ps)
CTOR.
double f[11][100]
T min(T a, T b)
Definition: MathUtil.h:58
#define debug
Definition: HDRShower.cc:19
std::array< float, 6 > adcPulse_
void runTrivialShaper(DFr &dataFrame, hgc::HGCSimHitData &chargeColl, int thickness)
converts charge to digis without pulse shape
float fast_expf(float x)
std::vector< float > tdcChargeDrainParameterisation_
models the behavior of the front-end electronics
std::array< float, 6 > pulseAvgT_
void set(bool thr, bool mode, uint16_t toa, uint16_t data)
Definition: HGCSample.h:34
Power< A, B >::type pow(const A &a, const B &b)
Definition: Power.h:40
void runShaperWithToT(DFr &dataFrame, hgc::HGCSimHitData &chargeColl, hgc::HGCSimHitData &toa, int thickness, CLHEP::HepRandomEngine *engine)
implements pulse shape and switch to time over threshold including deadtime