Universita' di GenovaINFN Sezione di Genova  
AIRWATCH / EUSO Genova

FrontEndChip - source file

// ESAF : Euso Simulation and Analysis Framework
// class FrontEndChip
// $Id: FrontEndChip.cc,v 1.43 2005/11/07 22:12:43 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();
                //
                // check if the channel has recovered from the last signal
                //
                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 );
                        //
                        // update photon's data
                        //
                        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);
                    }
                }

                // dead time reset
                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];

                    // skip signals which did not overtake the threshold
                    if ( !sig->MadeCount()) continue;

                    Double_t tm = sig->Time();
                    if ( tm >= start && tm < end && ( (tm - tm_prev) > fgResolvTime ) 
                            && 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;

}

About Us | EUSO Official Website | Web pages created by Roberto Pesce and Alessandro Thea - Last Update Wed Nov 16 16:57:39 2005 Wed Nov 16 16:29:22 2005