// ESAF : Euso Simulation and Analysis Framework
// class FrontEndChip
// $Id: FrontEndChip.cc,v 1.41 2005/04/20 12:26:37 thea Exp $
// M. Pallavicini - created 10/4/2001
// implementation
#include <math.h>
#include <iostream>
#include "FrontEndChip.hh"
#include "PmtSignal.hh"
#include "SortVector.hh"
#include "EsafRandom.hh"
#include "ChipGtuData.hh"
#include "EEvent.hh"
#include "Photomultiplier.hh"
#include "MacroCell.hh"
#include "AnalogFrontEnd.hh"
#include "EEventFrontEndDataAdder.hh"
#include "EDetectorPhotonDataAdder.hh"
ClassImp(FrontEndChip)
// global parameters common to all chips
// we assume to have chips of the same kind for the whole photodetector
// parameters are read from configuration when the first FrontEndChip
// object is created
Double_t FrontEndChip::fgResolvTime = -1.;
Double_t FrontEndChip::fgCurrentGain = -1.;
Double_t FrontEndChip::fgCurrentThreshold = -1.;
Int_t FrontEndChip::fgCounterThreshold = 0;
Int_t FrontEndChip::fgChipCounter = 0;
Int_t FrontEndChip::fgTotalHits=0; // number of DETECTED PMT signals
Int_t FrontEndChip::fgTotalSignals=0; // number of PMT signals
ChipTriggerType FrontEndChip::fgTriggerType = kStandardChipTrigger;
//_____________________________________________________________________________
FrontEndChip::FrontEndChip(Int_t channels ) : EsafConfigurable() {
// ctor
fNumPmts = 0;
fNumPmtChannels = 0;
fId = ++fgChipCounter;
fLastTime = -kHuge;
fFirstTime = kHuge;
// the chip is squared!
fNumSide = (Int_t) sqrt( (Float_t)channels );
// get parameters if needed (static variables, done only once)
if ( fgResolvTime < 0. ) {
fgResolvTime = Conf()->GetNum("FrontEndChip.TimeResolution");
fgCurrentGain = Conf()->GetNum("FrontEndChip.Gain");
fgCurrentThreshold = Conf()->GetNum("FrontEndChip.Threshold");
fgCounterThreshold = (Int_t)Conf()->GetNum("FrontEndChip.CounterThreshold");
fgTriggerType = (ChipTriggerType)Conf()->GetNum("FrontEndChip.TriggerGroups");
}
// these parameters are specific for each chip
Double_t g_spread = Conf()->GetNum("FrontEndChip.GainSpread");
Double_t t_spread = Conf()->GetNum("FrontEndChip.ThreshSpread");
TRandom* rndm = EsafRandom::Get();
for( Int_t ch=0; ch < kMaxFeChannels; ch++ ) {
fEffectiveGain[ch] = fgCurrentGain *
( 1. + (rndm->Rndm()*2. - 1. ) * g_spread);
fEffectiveThreshold[ch] = fgCurrentThreshold *
( 1. + (rndm->Rndm()*2. - 1. ) * t_spread);
}
// reset array of vectors of PmtSignals
for(Int_t i=0; i < kMaxFeChannels; i++ )
fSignals[i] = 0;
SetEmpty();
SetSorted( false );
SetNightGlowRate(0.);
}
//_____________________________________________________________________________
FrontEndChip::~FrontEndChip() {
// destructor; destroys lists only; PmtSignals are destroyed by Pmt
Reset();
for(Int_t i=0; i < kMaxFeChannels; i++ ) {
if ( fSignals[i] ) {
delete fSignals[i]; fSignals[i] = 0;
}
}
if ( fgTotalSignals ) {
#ifdef DEBUG
Msg(EsafMsg::Debug) << "Front End Statistics" << MsgDispatch;
Msg(EsafMsg::Debug) << "Total number of seen pmt signals = " << fgTotalSignals << MsgDispatch;
Msg(EsafMsg::Debug) << "Total number of detected pe = " << fgTotalHits << MsgDispatch;
#endif /* DEBUG */
fgTotalSignals = 0;
fgTotalHits = 0;
}
}
//_____________________________________________________________________________
void FrontEndChip::AssociatePmts(Photomultiplier *p1, Photomultiplier *p2,
Photomultiplier *p3, Photomultiplier *p4) {
// attach a pmt to this chip
// the pmts must be of the same model
// pmts channels are mapped to frontend channels according the following scheme
// 1 | 2
// -----
// 3 | 4
if ( !p1 ) {
Msg(EsafMsg::Panic) << "Wrong PMT association in ElementaryCell::AssociatePmts" << MsgDispatch;
}
pPmts[0] = p1;
pPmts[1] = p2;
pPmts[2] = p3;
pPmts[3] = p4;
if ( p1 && !p2 && !p3 && !p4 ) {
fNumPmts = 1;
pPmts[0]->SetFrontEnd( this, 0, 0 );
} else if ( p1 && p2 && p3 && p4 ) {
fNumPmts = 4;
Int_t pmtside = pPmts[0]->Geometry()->Rows();
pPmts[0]->SetFrontEnd( this, 0, 0 );
pPmts[1]->SetFrontEnd( this, 0, pmtside );
pPmts[2]->SetFrontEnd( this, pmtside, 0 );
pPmts[3]->SetFrontEnd( this, pmtside, pmtside );
} else
FatalError("Wrong PMT number in FrontEndChip::AssociatePmts. Can be 1 or 4.");
fNumPmtChannels = pPmts[0]->NumChan();
for ( Int_t i(1); i<fNumPmts; i++) {
if ( pPmts[i] && pPmts[i]->NumChan() != fNumPmtChannels )
FatalError("Cannot attach different kind of PMTs to the same FE chip!");
}
if ( Channels() != (GetNumPmts()*fNumPmtChannels) )
FatalError("Mismatch between FE number of channels and total number of pmt channels.");
}
//_____________________________________________________________________________
void FrontEndChip::ResetClass() {
// reset class variables
// it is called by ElectronicsFactory when a new configuration is
// loaded and detector is re-built
fgResolvTime = -1.;
fgCurrentGain = -1.;
fgCurrentThreshold = -1.;
fgCounterThreshold = 0;
fgChipCounter = 0;
fgTotalHits = 0;
fgTotalSignals = 0;
}
//_____________________________________________________________________________
void FrontEndChip::Add(vector<PmtSignal*>* signals, Int_t ch ) {
// add a list of hit
// compute the time of the first and last hit in time order
fSignals[ch] = signals;
for(UInt_t i=0; i<signals->size(); i++) {
PmtSignal* sig = (*signals)[i];
if ( sig->Time() > fLastTime )
fLastTime = sig->Time();
if ( sig->Time() < fFirstTime )
fFirstTime = sig->Time();
}
SetEmpty( kFALSE );
SetSorted( kFALSE );
}
//_____________________________________________________________________________
void FrontEndChip::Reset() {
// reset chip to get ready for next event
// PmtSignals list are cleared and ChipGtuData cleared
for(Int_t ch=0; ch<Channels(); ch++) {
if ( fSignals[ch] ) {
for(size_t sig=0; sig < fSignals[ch]->size(); sig++) {
PmtSignal* pSig = (*(fSignals[ch]))[sig];
if ( pSig )
delete pSig;
}
fSignals[ch]->clear();
}
}
fLastTime = -kHuge;
fFirstTime = kHuge;
SetEmpty( kTRUE );
SetSorted( kFALSE );
}
//_____________________________________________________________________________
ChipGtuData* FrontEndChip::Gtu(Int_t GtuId, Double_t start, Double_t end, Bool_t doNG) {
// simulate response for a GTU between start and end
// data stored:
// counter values at the end
// digital signal on global OR output (taking into account counter thresh)
//
ChipGtuData* data = new ChipGtuData( this, GtuId );
data->SetThreshCounter( fgCounterThreshold );
Bool_t GtuEmpty = kTRUE;
Double_t tFastOr = kHuge;
TimeSort();
// reset analog front end object
Afee()->Reset( GtuId );
// compute average night glow background per pixel
Double_t mu = 0.;
if ( doNG ) {
Double_t gtl = end-start; // gtu length
mu = gtl*GetNightGlowRate(); // mean per gtu
}
// loop on all channels and hits
// for each hit in this GTU do simulation
// compute the time in which the FAST OR starts if any
for( Int_t ch=0; ch < Channels(); ch++ ) {
Int_t row = Row(ch);
Int_t col = Column(ch);
Double_t tm_prev = -kHuge;
// add night glow background if required
TRandom *rndm = EsafRandom::Get();
if ( doNG ) {
Int_t nbckhits = rndm->Poisson(mu);
for(Int_t n=0; n<nbckhits; n++) {
Double_t tt = start + rndm->Rndm()*(end-start);
data->SetCounter( ch, kTRUE );
if ( data->CheckCounter( ch ) ) {
if ( tt < tFastOr ) {
tFastOr = tt;
data->SetRowCol(row,col,tt-start);
}
}
}
}
// loop on photon signals
if ( fSignals[ ch ] ) {
// loop on hits
for( UInt_t nSig=0; nSig < fSignals[ch]->size(); nSig++) {
PmtSignal* sig = (*fSignals[ch])[nSig];
Double_t tm = sig->Time();
if ( tm > start && tm < end && ( (tm - tm_prev) > fgResolvTime ) ) {
if ( EEvent::GetCurrent() ){
EDetectorPhotonDataAdder a(sig->Id(), Pmt(ch)->Cell()->Id(),
Id(), GtuId, kFALSE, kFALSE );
EEvent::GetCurrent()->Fill(a);
}
// check for channel threshold
Double_t current = sig->Current( tm ) * fEffectiveGain[ch] * 1.e6; //microamps
fgTotalSignals++;
if ( current > fEffectiveThreshold[ch] ) {
fgTotalHits++;
data->SetCounter( ch, kFALSE );
sig->SetMadeCount( kTRUE );
if ( EEvent::GetCurrent() ){
EDetectorPhotonDataAdder a(sig->Id(), Pmt(ch)->Cell()->Id(),
Id(), GtuId, kTRUE, kFALSE );
EEvent::GetCurrent()->Fill(a);
}
}
// check for digital counter threshold
// activate X and Y logic
// time is relative to GTU start
if ( data->CheckCounter( ch ) ) {
sig->SetMadeFastOR( kTRUE );
if ( tm < tFastOr )
tFastOr = tm;
data->SetRowCol(row,col,tm-start);
}
}
tm_prev = tm;
// add photon to analog front end object
// resolving time is not relevant for analog electronics
if ( tm > start && tm < end ) {
GtuEmpty = kFALSE;
Afee()->Add(*sig,ch,GtuId);
}
}
}
}
// re-loop on all hits to count fast or properly
// after fast or has been activated, all hits are counted
// even if they are in different pixels
if ( !IsEmpty()) {
for( Int_t ch=0; ch < Channels(); ch++ ) {
Double_t tm_prev = -kHuge;
if ( fSignals[ ch ] ) {
for( UInt_t nSig=0; nSig < fSignals[ch]->size(); nSig++) {
PmtSignal* sig = (*fSignals[ch])[nSig];
Double_t tm = sig->Time();
if ( tm > start && tm < end && ( (tm - tm_prev) > fgResolvTime ) ) {
if ( tm >= tFastOr ) {
data->AddFastOr( tm-start );
sig->SetMadeFastOR( kTRUE );
Int_t mx,my;
GetPixelCellRowCol(ch,mx,my);
if ( EEvent::GetCurrent() ){
EDetectorPhotonDataAdder a(sig->Id(), Pmt(ch)->Cell()->Id(),
Id(), GtuId, kTRUE, kTRUE, mx, my);
EEvent::GetCurrent()->Fill(a);
}
}
}
tm_prev = tm;
}
}
}
}
// if there is at least one photon, do analog simulation and fill front end
// data in root file
if ( !GtuEmpty ) {
// add analog simulation infos to ChipGtuData
Afee()->Simulate( data );
}
// add front end chip information to the root event
if ( EEvent::GetCurrent() ){
EEventFrontEndDataAdder a( data );
EEvent::GetCurrent()->Fill( a );
}
return data;
}
//_____________________________________________________________________________
void FrontEndChip::GetPixelCellRowCol(Int_t ch, Int_t& r, Int_t& c) const {
// return the macrocell row and column of a given channel
r = Row( ch ) + GetCellRowOffset();
c = Column( ch ) + GetCellColOffset();
}
//_____________________________________________________________________________
void FrontEndChip::TimeSort() {
// sort all signal vectors
// each channel is independent from the others
if ( IsSorted() )
return;
for(Int_t ch=0; ch<Channels(); ch++) {
if (fSignals[ch]) {
SortVector( *(fSignals[ch]) );
}
}
SetSorted(kTRUE);
}
//_____________________________________________________________________________
void FrontEndChip::DumpSignals(Int_t chan, ostream& os) {
// dump signals on stream
// if ch negative, do all channels
Int_t start=0;
Int_t end = Channels();
if ( chan >=0 && chan < Channels() ) {
start = chan;
end = chan+1;
}
for( Int_t i = start; i < end; i++ ) {
Int_t n=0;
if ( fSignals[i] )
n = fSignals[i]->size();
Msg(EsafMsg::Info) << "Front End=" << Id() << " Channel=" << i << MsgDispatch;
Msg(EsafMsg::Info) << "Number of signals = " << n <<MsgDispatch;
if ( n ) {
for(Int_t j = 0; j < n; j++) {
PmtSignal *pSig = (*fSignals[chan])[j];
if ( pSig ) {
Msg(EsafMsg::Info) << " "<< j << " Id=" << pSig->Id()<< " t="<< pSig->Time() << MsgDispatch;
}
else {
Msg(EsafMsg::Info) << " " << j << " Invalid Signal!" << MsgDispatch;
}
}
}
}
}
//_____________________________________________________________________________
Int_t FrontEndChip::UniqueChanId(Int_t nch) {
// channel id unique for the whole detector
Photomultiplier* pmt = Pmt(nch);
if ( pmt )
return pmt->GetUniqueId(PmtChannel(nch));
else
return 0;
}
//_____________________________________________________________________________
Int_t FrontEndChip::NumActivePixels() const {
//
// Returns the number of pixel with some hits
//
Int_t tot=0;
for(Int_t ch=0; ch<Channels(); ch++) {
if (fSignals[ch]) {
if (fSignals[ch]->size()>0)
tot++;
}
}
return tot;
}
//______________________________________________________________________________
Photomultiplier* FrontEndChip::Pmt(Int_t ch) const {
//
// Pmt associated with channel ch
//
if ( ch >= Channels() ) {
Printf("FrontEndChip::Pmt(): channel out of range. Returning 0.");
return 0;
}
map<Int_t,Photomultiplier*>::const_iterator it;
Int_t id(0);
it = pPmts.begin();
Int_t pmtside = it->second->Geometry()->Rows();
switch ( GetNumPmts() ) {
// case 0:
// // empty fe
// break;
case 1:
id = 0;
break;
case 4:
if ( (ch % NumSide()) >= pmtside ) id += 1;
if ( (ch / NumSide()) >= pmtside ) id += 2;
break;
default:
Msg(EsafMsg::Panic) << "LocalPmtId: Wrong number of pmts in this frontend" << MsgDispatch;
}
it = pPmts.find(id);
return it->second;
}
//______________________________________________________________________________
Int_t FrontEndChip::PmtChannel(Int_t ch) const {
if ( GetNumPmts() == 1 )
return ch;
Int_t row, col;
PmtGeometry *geo = Pmt()->Geometry();
col = (ch % NumSide())%(geo->Rows());
row = (ch / NumSide())%(geo->Rows());
return col+geo->Rows()*row;
}