Changeset 10093 for trunk/Mars/melectronics
- Timestamp:
- 01/07/11 15:48:35 (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Mars/melectronics/MAvalanchePhotoDiode.cc
r9565 r10093 39 39 // Hamamatsu 60x60 cells: APD(60, 0.2, 3, 8.75) 40 40 // 41 // 42 // The implementation of afterpulsing is based on 43 // A.Du, F.Retiere 44 // After-pulsing and cross-talk in multi-pixel photon counters 45 // NIM A, Volume 596, Issue 3, p. 396-401 46 // 47 // 48 // Example: 49 // 50 // APD apd(ncells, crosstalk, deadtime, recovery); 51 // apd.SetAfterpulseProb(0.14, 0.11); 52 // 53 // while (1) 54 // { 55 // // Make the chip "empty" from the influence of external photons 56 // // It also sets fTime to 0. 57 // apd.Init(freq); // freq of external noise (eg. nsb) 58 // 59 // // Now call this function for each external photon you have. The 60 // // times are relative to the the time you get by apd.GetTime() 61 // // set automatically after the call to apd.Init(). 62 // for (int i=0; i<nphot; i++) 63 // apd.HitRandomCellRelative(dt); 64 // 65 // [...] 66 // 67 // // Process and produce afterpulses until a time, wrt to GetTime(), 68 // // given by the end of your simulated window. Note that this 69 // // does not produce noise. This must have been properly simulated 70 // // up to this time already. 71 // apd.IncreaseTime(dtend); 72 // 73 // // Now you can excess the afterpulses by 74 // TIter Next(&a->GetListOfAfterpulses()); 75 // Afterpulse *ap = 0; 76 // while ((ap=static_cast<Afterpulse*>(Next()))) 77 // { 78 // if (apd.GetTime()>=dtend) 79 // continue; 80 // 81 // cout << "Amplitude: " << ap->GetAmplitude() << endl; 82 // cout << "Arrival Time: " << ap->GetTime() << endl; 83 // } 84 // } 85 // 86 // 41 87 ////////////////////////////////////////////////////////////////////////////// 42 88 #include "MAvalanchePhotoDiode.h" … … 45 91 46 92 #include "MMath.h" 93 94 #include "MLog.h" 95 #include "MLogManip.h" 47 96 48 97 ClassImp(APD); … … 77 126 APD::APD(Int_t n, Float_t prob, Float_t dt, Float_t rt) 78 127 : fHist("APD", "", n, 0.5, n+0.5, n, 0.5, n+0.5), 79 fCrosstalkProb(prob), fDeadTime(dt), fRecoveryTime(rt), fTime(-1) 128 fCrosstalkProb(prob), fDeadTime(dt), fRecoveryTime(rt), 129 fTime(-1) 80 130 { 81 131 fHist.SetDirectory(0); 82 } 83 84 // -------------------------------------------------------------------------- 85 // 86 // This is the time the cell needs after a hit until less than threshold 87 // (0.001 == one permille) of the signal is lost. 88 // 89 // If all cells of a G-APD have fired simultaneously and they are fired 90 // once more after the relaxation time (with the defaultthreshold of 0.001) 91 // the chip will show a signal which is one permille too small. 132 133 fAfterpulses.SetOwner(); 134 135 fAfterpulseProb[0] = 0; 136 fAfterpulseProb[1] = 0; 137 138 fAfterpulseTau[0] = 15; 139 fAfterpulseTau[1] = 85; 140 } 141 142 // -------------------------------------------------------------------------- 143 // 144 // This is the time a chips needs after an external signal to relax to 145 // a "virgin" state, i.e. without no influence of the external pulse 146 // above the given threshold. 147 // 148 // It takes into account the dead time of the cell, the relaxation time 149 // and the two afterpulse distributions. However, in most cases the 150 // afterpulse distribution will dominate (except they are switched off by 151 // a zero probability). 152 // 153 // FIXME: Maybe the calculation of the relaxation time could be optimized? 92 154 // 93 155 Float_t APD::GetRelaxationTime(Float_t threshold) const 94 156 { 95 return fDeadTime-TMath::Log(threshold)*fRecoveryTime; 157 // Calculate time until the probability of one of these 158 // events falls below the threshold probability. 159 const Double_t rt0 = - TMath::Log(threshold)*fRecoveryTime; 160 const Double_t rt1 = fAfterpulseProb[0]>0 ? -TMath::Log(threshold/fAfterpulseProb[0])*fAfterpulseTau[0] : 0; 161 const Double_t rt2 = fAfterpulseProb[0]>0 ? -TMath::Log(threshold/fAfterpulseProb[1])*fAfterpulseTau[1] : 0; 162 163 // Probability not between t and inf, but between t and t+dt 164 // -tau * log ( p / ( 1 - exp(- dt/tau) ) ) = t 165 166 return fDeadTime + TMath::Max(rt0, TMath::Max(rt1, rt2)); 96 167 } 97 168 … … 108 179 // probability. 109 180 // 181 // For each photon the possible afterpulses of two distributions are 182 // created and added to the list of afterpulses. This is done by calling 183 // GenerateAfterpulse for the two afterpulse-distributions. 184 // 110 185 // The total height of the signal (in units of photons) is returned. 111 186 // Note, that this can be a fractional number. … … 122 197 // y<1 || y>fHist.GetNbinsY()) 123 198 // return 0; 199 #ifdef DEBUG 200 cout << "Hit: " << t << endl; 201 #endif 124 202 125 203 // Number of the x/y cell in the one dimensional array … … 137 215 // Photons within the dead time are just ignored 138 216 if (/*hx.GetBinContent(x,y)>0 &&*/ dt<=0) 217 { 218 #ifdef DEBUG 219 cout << "Dead: " << t << " " << cont << " " << dt << endl; 220 #endif 139 221 return 0; 140 222 } 141 223 // The signal height (in units of one photon) produced after dead time 142 224 // depends on the recovery of the cell - described by an exponential. 143 225 const Float_t weight = fRecoveryTime<=0 ? 1. : 1-TMath::Exp(-dt/fRecoveryTime); 226 227 // Now we know the charge in the cell and we can generate 228 // the afterpulses with both time-constants 229 GenerateAfterpulse(cell, 0, weight, t); 230 GenerateAfterpulse(cell, 1, weight, t); 144 231 145 232 // The probability that the cell emits a photon causing crosstalk … … 201 288 // 202 289 // Check if x and y is a valid cell. If not return 0, otherwise 203 // HitCelImp(x, y, t) 290 // HitCelImp(x, y, t). HitCellImp generates Crosstalk and Afterpulses. 204 291 // 205 292 // The default time is 0. … … 217 304 // 218 305 // Determine randomly (uniformly) a cell which was hit. Return 219 // HitCellImp for this cell and the given time. 306 // HitCellImp for this cell and the given time. HitCellImp 307 // generates Crosstalk and Afterpulses 220 308 // 221 309 // The default time is 0. … … 238 326 // -------------------------------------------------------------------------- 239 327 // 240 // Sets all cells with a contents whi hcis well before the time t such that328 // Sets all cells with a contents which is well before the time t such that 241 329 // the chip is "virgin". Therefore all cells are set to a time which 242 330 // is twice the deadtime before the given time and 1000 times the recovery 243 331 // time. 244 332 // 333 // The afterpulse list is deleted. 334 // 245 335 // If deadtime and recovery time are 0 then t-1 is set. 246 336 // … … 260 350 fHist.SetEntries(1); 261 351 352 fAfterpulses.Delete(); 353 262 354 fTime = t; 263 355 } … … 267 359 // First call FillEmpty for the given time t. Then fill each cell by 268 360 // by calling HitCellImp with time t-gRandom->Exp(n/rate) with n being 269 // the total number of cells. 361 // the total number of cells. This the time at which the cell was last hit. 270 362 // 271 363 // Sets fTime to t 272 364 // 273 // The default time is 0. 365 // If the argument t is omitted it defaults to 0. 366 // 367 // Since after calling this function the chip should reflect the 368 // status at the new time fTime=t, all afterpulses are processed 369 // until this time. However, the produced random pulses might have produced 370 // new new afterpulses. 371 // 372 // All afterpulses before the new timestamp are deleted. 373 // 374 // WARNING: Note that this might not correctly reproduce afterpulses 375 // produced by earlier pulese. 274 376 // 275 377 void APD::FillRandom(Float_t rate, Float_t t) … … 282 384 const Double_t f = (nx*ny)/rate; 283 385 284 // FIXME: This is not perfect, is it? What about the dead time? 386 // FIXME: Dead time is not taken into account, 387 // possible earlier afterpulses are not produced. 285 388 286 389 for (int x=1; x<=nx; x++) … … 288 391 HitCellImp(x, y, t-MMath::RndmExp(f)); 289 392 393 // Deleting of the afterpulses before fHist.GetMinimum() won't 394 // speed things because we have to loop over them once in any case 395 396 ProcessAfterpulses(fHist.GetMinimum(), t); 397 DeleteAfterpulses(t); 398 290 399 fTime = t; 400 } 401 402 // -------------------------------------------------------------------------- 403 // 404 // Shift all times including fTime to dt (ie. add -dt to all times) 405 // This allows to set a user-defined T0 or shift T0 to fTime=0. 406 // 407 // However, T0<0 is not allowed (dt cannot be greater than fTime) 408 // 409 void APD::ShiftTime(Double_t dt) 410 { 411 if (dt>fTime) 412 { 413 gLog << warn << "APD::ShiftTime: WARNING - requested time shift results in fTime<0... ignored." << endl; 414 return; 415 } 416 417 // If reset was requested shift all times by end backwards 418 // so that fTime is now 0 419 const Int_t n = (fHist.GetNbinsX()+2)*(fHist.GetNbinsY()+2); 420 for (int i=0; i<n; i++) 421 fHist.GetArray()[i] -= dt; 422 423 fTime -= dt; 291 424 } 292 425 … … 296 429 // freq. Returns the total signal "recorded". 297 430 // 298 // fTime is set to the fTime+dt 431 // fTime is set to the fTime+dt. 299 432 // 300 433 // If you want to evolve over a default relaxation time (relax the chip 301 434 // from a signal) use Relax instead. 302 435 // 436 // Since after calling this function the chip should reflect the 437 // status at the new time fTime=fTime+dt, all afterpulses are processed 438 // until this time. However, evolving the chip until this time might 439 // have produced new afterpulses. 440 // 441 // All afterpulses before the new timestamp are deleted. 442 // 303 443 Float_t APD::Evolve(Double_t freq, Double_t dt) 304 444 { 305 const Double_t avglen = 1./freq; 306 307 const Double_t end = fTime+dt; 445 const Double_t end = fTime+dt; 308 446 309 447 Float_t hits = 0; 310 311 Double_t time = fTime; 312 while (1) 448 449 if (freq>0) 313 450 { 314 time += MMath::RndmExp(avglen); 315 if (time>end) 316 break; 317 318 hits += HitRandomCell(time); 451 const Double_t avglen = 1./freq; 452 453 Double_t time = fTime; 454 while (1) 455 { 456 const Double_t deltat = MMath::RndmExp(avglen); 457 if (time+deltat>end) 458 break; 459 460 time += deltat; 461 462 hits += HitRandomCell(time); 463 } 319 464 } 465 466 // Deleting of the afterpulses before fTime won't speed things 467 // because we have to loop over them once in any case 468 469 ProcessAfterpulses(fTime, dt); 470 DeleteAfterpulses(end); 320 471 321 472 fTime = end; … … 329 480 // dead. 330 481 // The default time is 0. 482 // 483 // Note that if you want to get a correct measure of teh number of dead cells 484 // at the time t, this function will only produce a valid count if the 485 // afterpulses have been processed up to this time. 331 486 // 332 487 Int_t APD::CountDeadCells(Float_t t) const … … 348 503 // Returs the number of cells which have a time t<=fDeadTime+fRecoveryTime. 349 504 // The default time is 0. 505 // 506 // Note that if you want to get a correct measure of teh number of dead cells 507 // at the time t, this function will only produce a valid count if the 508 // afterpulses have been processed up to this time. 350 509 // 351 510 Int_t APD::CountRecoveringCells(Float_t t) const … … 364 523 return n; 365 524 } 525 526 // -------------------------------------------------------------------------- 527 // 528 // Generate an afterpulse originating from the given cell and a pulse with 529 // charge. The afterpulse distribution to use is specified by 530 // the index. The "current" time to which the afterpulse delay refers must 531 // be given by t. 532 // 533 // A generated Afterpulse is added to the list of afterpulses 534 // 535 void APD::GenerateAfterpulse(UInt_t cell, Int_t idx, Double_t charge, Double_t t) 536 { 537 // The cell had a single avalanche with signal height weight. 538 // This cell now can produce an afterpulse photon/avalanche. 539 const Double_t p = gRandom->Uniform(); 540 541 // It's probability scales with the charge of the pulse 542 if (p>charge*fAfterpulseProb[idx]) 543 return; 544 545 // Afterpulses come with a well defined time-constant 546 // after the normal pulse 547 const Double_t dt = MMath::RndmExp(fAfterpulseTau[idx]); 548 549 fAfterpulses.Add(new Afterpulse(cell, t+dt)); 550 551 #ifdef DEBUG 552 cout << "Add : " << t << " + " << dt << " = " << t+dt << endl; 553 #endif 554 } 555 556 // -------------------------------------------------------------------------- 557 // 558 // Process afterpulses between time and time+dt. All afterpulses in the list 559 // before t=time are ignored. All afterpulses between t=time and 560 // t=time+dt are processed through HitCellImp. Afterpulses after and 561 // equal t=time+dt are skipped. 562 // 563 // Since the afterpulse list is a sorted list newly generated afterpulses 564 // are always inserted into the list behind the current entry. Consequently, 565 // afterpulses generated by afterpulses will also be processed correctly. 566 // 567 // Afterpulses with zero amplitude are deleted from the list. All other after 568 // pulses remain in the list for later evaluation. 569 // 570 void APD::ProcessAfterpulses(Float_t time, Float_t dt) 571 { 572 #ifdef DEBUG 573 cout << "Process afterpulses from " << time << " to " << time+dt << endl; 574 #endif 575 576 const Float_t end = time+dt; 577 578 TObjLink *lnk = fAfterpulses.FirstLink(); 579 while (lnk) 580 { 581 Afterpulse &ap = *static_cast<Afterpulse*>(lnk->GetObject()); 582 583 // Skip afterpulses which have been processed already 584 // or which we do not have to process anymore 585 if (ap.GetTime()<time || ap.GetAmplitude()>0) 586 { 587 lnk = lnk->Next(); 588 continue; 589 } 590 591 // No afterpulses left in correct time window 592 if (ap.GetTime()>=end) 593 break; 594 595 // Process afterpulse through HitCellImp 596 const Float_t ampl = ap.Process(*this); 597 598 // Step to the next entry already, the current one might get deleted 599 lnk = lnk->Next(); 600 601 if (ampl!=0) 602 continue; 603 604 #ifdef DEBUG 605 cout << "Del : " << ap.GetTime() << " (" << ampl << ")" << endl; 606 #endif 607 608 // The afterpulse "took place" within the dead time of the 609 // pixel ==> No afterpulse, no crosstalk. 610 delete fAfterpulses.Remove(&ap); 611 } 612 } 613 614 // -------------------------------------------------------------------------- 615 // 616 // Delete all afterpulses before and equal to t=time from the list 617 // 618 void APD::DeleteAfterpulses(Float_t time) 619 { 620 TIter Next(&fAfterpulses); 621 622 Afterpulse *ap = 0; 623 624 while ((ap = static_cast<Afterpulse*>(Next()))) 625 { 626 if (ap->GetTime()>=time) 627 break; 628 629 delete fAfterpulses.Remove(ap); 630 } 631 }
Note:
See TracChangeset
for help on using the changeset viewer.