// ECACal.cc // Contact person: Gabriel Orebi Gann - Author and contact // Contact person: Javier Caravaca - Contact // See ECACal.hh for more details //———————————————————————// #include #include #include #include #include #include #include #include #include #include using namespace std; namespace RAT { const int kNcharge = 3; const int kNcrate = 19; const int kNcard = 16; const int kNchan = 32; const int kNcell = 16; ECACal::ECACal() { fQHS = 0.; fQHL = 0.; fQLX = 0.; fTAC = 0.; fCharge[0] = 0.; fCharge[1] = 0.; fCharge[2] = 0.; fNTotCell = kNcrate * kNcard * kNchan * kNcell; } void ECACal::BeginOfRun( DS::Run& run) { fCalbank = DB::Get()->GetLink("PMTCAL"); fECAtype = fCalbank->GetI("eca_type"); fTimeOffset = fCalbank->GetD("time_offset"); fTolLevel = fCalbank->GetI("eca_validation"); fTolADC = fCalbank->GetI("eca_adc"); fECAlogverb = fCalbank->GetI("eca_logverbosity"); fECAPDSTpattern = fCalbank->GetI("eca_pdst_pattern"); fECAPDSTrate = fCalbank->GetI("eca_pdst_rate"); fECATSLPpattern = fCalbank->GetI("eca_tslp_pattern"); fECATSLPrate = fCalbank->GetI("eca_tslp_rate"); fQHSTestMax = fCalbank->GetI("test_qhsmax"); fQHLTestMax = fCalbank->GetI("test_qhlmax"); fQLXTestMax = fCalbank->GetI("test_qlxmax"); fQHSTestMin = fCalbank->GetI("test_qhsmin"); fQHLTestMin = fCalbank->GetI("test_qhlmin"); fQLXTestMin = fCalbank->GetI("test_qlxmin"); fQLXvsQHSTest = fCalbank->GetI("test_qlxvsqhs"); fDoPed = false; fDoTslp = false; if(fECAtype%10==1)fDoPed = true; if(int((fECAtype%100)/10)==1)fDoTslp = true; //Get the pattern and rate std::stringstream pdst_pattern_rate, tslp_pattern_rate; pdst_pattern_rate << fECAPDSTpattern <<"_"<GetLink("ECA_PDST", pdst_pattern_rate.str()); fTslpbank = DB::Get()->GetLink("ECA_TSLP", tslp_pattern_rate.str()); if(fDoPed){ try{ fPedsbank->GetS("index"); } catch(DBNotFoundError){ ECACal::Die("",5,fECAPDSTpattern,fECAPDSTrate); } fPedStat = fPedsbank->GetIArray("pdst_status"); // Use this for tac tests too fPedQ.resize(3); fPedQ[0] = fPedsbank->GetFArrayFromD("pdst_qhs"); fPedQ[1] = fPedsbank->GetFArrayFromD("pdst_qhl"); fPedQ[2] = fPedsbank->GetFArrayFromD("pdst_qlx"); fPedBID = fPedsbank->GetIArray("pdst_dqid"); } if(fDoTslp){ try{ fTslpbank->GetS("index"); } catch(DBNotFoundError){ ECACal::Die("",5,fECATSLPpattern,fECATSLPrate); } fTslope = fTslpbank->GetFArrayFromD("tslp_points"); fNSlopePoints = fTslpbank->GetI("tslp_ntimes"); fNdim = static_cast(fNSlopePoints / 32)+1; fTslopeX = fTslpbank->GetFArrayFromD("tslp_times"); fTslopeStat = fTslpbank->GetIArray("tslp_status"); fTslopeBadTemp = fTslpbank->GetIArray("tslp_bad"); // Sort status words by cellfTslopeBad fTslopeBad.resize(fNTotCell); if(static_cast(fTslopeBadTemp.size()) != fNTotCell*fNdim){ warn<<"=================================================="<GetIArray("tslp_dqid"); } fDQXXbank = DB::Get()->GetLink("PMT_DQXX"); fDQIDtemp = fDQXXbank->GetIArray("dqid"); // only want 5 of 6 values / card fDQID.resize(kNcrate*kNcard*5); int keepval[6] = {1,0,1,1,1,1}; int counter = 0; for(int ncr=0;ncrGetLink("BADC"); fPedADC.resize(3); fPedADC[0] = fBADCbank->GetIArray("qhs_status"); fPedADC[1] = fBADCbank->GetIArray("qhl_status"); fPedADC[2] = fBADCbank->GetIArray("qlx_status"); fTACADC = fBADCbank->GetIArray("tac_status"); } ECACal::~ECACal() { } DS::PMTCal ECACal::CalibratePMT( const DS::PMTUncal& pmt ) { CalibrateHit( pmt.GetQHS(), pmt.GetQHL(), pmt.GetQLX(), pmt.GetTime(), pmt.GetCrateCardChannelCell() ); // Create new PMTCal and fill it DS::PMTCal newHit; newHit.SetID( pmt.GetID() ); newHit.SetQHS( GetQHS() ); newHit.SetQHL( GetQHL() ); newHit.SetQLX( GetQLX() ); newHit.SetTime( GetTime() ); newHit.SetCellID( pmt.GetCellID() ); newHit.SetChanFlags( pmt.GetChanFlags() ); newHit.SetStatus( GetStatus() ); return newHit; } void ECACal::CalibrateHit(float qhs, float qhl, float qlx, double tac, int cccc) { fQHS = 0.; fQHL = 0.; fQLX = 0.; fTAC = tac; fStatus = DS::BitMask(); fCharge[0] = qhs; fCharge[1] = qhl; fCharge[2] = qlx; if(fECAtype == 0){ ECACal::DontDie("",1,fECAtype,0); return; } if(fDoPed){ ECACal::PedestalCal(cccc); } if(fDoTslp){ ECACal::TSlopeCal(cccc); } ECACal::ValidateCalibrate(cccc); // May need to reset some Q/T values, according to validation status: if(fDoPed){ for(int i=0;i -9990.){ calibratedQ[i] = fCharge[i] - fPedQ[i][cccc]; } } /* Validate hit charges */ // 1. Check if charges are railed if( fCharge[0] >= fQHSTestMax ) fStatus.Set(28); //QHS railed if( fCharge[1] >= fQHLTestMax ) fStatus.Set(29); //QHL railed if( fCharge[2] >= fQLXTestMax ) fStatus.Set(30); //QLX railed // 2. Check if QHS/L/LX are way below pedestal if( calibratedQ[0] < fQHSTestMin ) fStatus.Set(28); // QHS potential rollover if( calibratedQ[1] < fQHLTestMin ) fStatus.Set(29); // QHL potential rollover if( calibratedQ[2] < fQLXTestMin ) fStatus.Set(30); // QLX potential rollover // 3. Check if QLX/QHS is very large if( calibratedQ[1] == 0 ){ if ( calibratedQ[2] != 0 ) fStatus.Set(31); // Pathological QLX } else if ( calibratedQ[2]/calibratedQ[1] > fQLXvsQHSTest ) fStatus.Set(31); // Pathological QLX // Update with calibrated charges for(int i=0;ins conversion void ECACal::TSlopeCal(int cccc) { double timeADC = fTAC; double timeNS = 0.; float *TslopeADC = new float[fNSlopePoints]; std::vector bad = fTslopeBad[cccc]; UInt_t TslpWord = fTslopeStat[cccc]; int ifirst = 99; int ilast = -1; int ilow = -1; int ihigh = 99; int ngood = 0; int lastgood = 99; // Last good point in curl region int curl = 0; // First, check that there are at least two non-zero calibration data points! int countpoints = 0; for(int i=0;i0.)++countpoints; } if(countpoints<2){ ECACal::DontDie("",7,0,0); delete[] TslopeADC; fTAC = -9998.0; fStatus.Set(22); return; } for(int i=0;i0){ if((TslopeADC[i]fNSlopePoints){ lastgood = i; // first good point in curl region } } } if(curl==0){ // not curl region // Count good points ++ngood; // Bracket the good points on the slope ([ifirst,ilast]) if(iilast)ilast=i; // Find the 2 closest good points that bracket timeADC ([ilow,ihigh]) // (we can assume TslopeADC is monot. inc., since any points that don't // had the flags set during validation of ECA-constant generation) if((timeADC >= TslopeADC[i]) && (i>ilow)){ ilow = i; } if((timeADC <= TslopeADC[i]) && (i=0 && ifirst=TslopeADC[ifirst])){ // We're good: time is within good range } else if(timeADC>0. && timeADC lastgood(ADC) // then we're in the double-valued region // FLAG IT! if(lastgood TslopeADC[lastgood])){ fStatus.Set(27); } // Extra checks for curl/low region, or bad slopes: // 1. never found one of the bounds // (should never get here: should be caught above, but being extra careful) if(ilow<0 || ihigh>fNSlopePoints){ fTAC = -9999.0; fStatus.Set(24); ECACal::DontDie("4",2,(int)timeADC, ilast-ifirst); delete[] TslopeADC; return; } // 2. too many gaps between the bounds of the points if((ihigh-ilow)>2){ fTAC = -9999.0; fStatus.Set(25); ECACal::DontDie("",3,cccc, ihigh-ilow); delete[] TslopeADC; return; } // If the TAC on this cell railed, flag the hit as bad // Check by checking the cell status from the processor if(BitManip::TestBit(TslpWord,12)){ fStatus.Set(26); fTAC = -9999.0; ECACal::DontDie("",4,cccc,0); delete[] TslopeADC; return; } // If ilow==ihigh, we're sitting on a TAC point if(ilow==ihigh){ // && ngood>=2){ timeNS = fTslopeX[ilow]; // Time reversal for all good times! fTAC = fTimeOffset - timeNS; delete[] TslopeADC; return; } // If the ADC value of the two points are equal the interpolation will break (/0) // NB probably means we're at the peak of the curl i.e. about to go down the big dipper // Choose middle value between the two points // Flag this as bit 28 in the hit status word (doesn't mean it's a bad hit, just good to be aware) if(TslopeADC[ihigh]==TslopeADC[ilow]){ timeNS = (fTslopeX[ilow]+fTslopeX[ihigh])/2.; // Time reversal for all good times! fTAC = fTimeOffset - timeNS; fStatus.Set(28); ECACal::DontDie("",5,cccc,0); delete[] TslopeADC; return; } // Otherwise, interpolate between the upper/lower bounds to find time: // else{ timeNS = (((timeADC-TslopeADC[ilow])*fTslopeX[ihigh]) - ((timeADC-TslopeADC[ihigh])*fTslopeX[ilow])) / ((timeADC-TslopeADC[ilow])-(timeADC-TslopeADC[ihigh])); double timecheck = ((fTslopeX[ihigh]*(timeADC-TslopeADC[ilow])) - fTslopeX[ilow]*(timeADC-TslopeADC[ihigh])) / (TslopeADC[ihigh] - TslopeADC[ilow]); if(timeNS != timecheck) ECACal::Die("",2,0,0); // } // Time reversal for all good times! fTAC = fTimeOffset - timeNS; delete[] TslopeADC; return; } // Set fStatus according to PDST tests void ECACal::ValidateQ(int cccc) { UInt_t PedWord = fPedStat[cccc]; int icrate = BitManip::GetCratewCell(cccc); int icard = BitManip::GetCardwCell(cccc); int ichan = BitManip::GetChannelwCell(cccc); // 1. Test MB and DB int offset = 80*icrate + 5*icard; if(fDQID[offset]!=fPedBID[offset]){ fStatus.Set(1); } int daughterID = (ichan/8) + 1; offset += daughterID; if(fDQID[offset]!=fPedBID[offset]){ fStatus.Set(1); } // 2. Check if ECA had any events if(BitManip::TestBit(PedWord,14)){ fStatus.Set(2); } // 3. Check sequencer from ECA run if(BitManip::TestBit(PedWord,25)){ fStatus.Set(2); } // Did we do diff tests (successfully i.e. BIDs matched) in ECA? bool doDiff = true; if(BitManip::TestBit(PedWord,16)||BitManip::TestBit(PedWord,17)){ doDiff = false; } // Pedestal checks for this cell for(int i=0;i<3;++i){ bool mean = BitManip::TestBit(PedWord,i*3+1); bool width = BitManip::TestBit(PedWord,i*3+2); bool diff = BitManip::TestBit(PedWord,i*3+3) && doDiff; // 4. Did each charge pass mean, width && diff (iff doDiff) tests? if( (mean) || (width) || (diff) ){ fStatus.Set(3+i); } // 5. Pathological cell // Did each charge fail 2 of the 3 tests? if( (mean&&width) || (mean&&diff) || (width&&diff) ){ fStatus.Set(16+i); } } // 4. (d) TAC) Pedestal checks for this cell: ONLY DO DIFF FOR TAC // Did each charge pass mean, width && diff (iff doDoff) tests? bool mean = BitManip::TestBit(PedWord,10); bool width = BitManip::TestBit(PedWord,11); bool diff = BitManip::TestBit(PedWord,12) && doDiff; if( (diff) ){ fStatus.Set(6); } // 5. (d) TAC) Pathological cell // Did each charge fail 2 of the 3 tests? if( (mean&&width) || (mean&&diff) || (width&&diff) ){ fStatus.Set(19); } // 6. Overall pedestal/TAC channel status if(BitManip::TestBit(PedWord,15)){ fStatus.Set(8); } // 7. Check CALDAC results for charge offset = 16*icrate + icard; for(int i=0;i<3;++i){ if(fPedADC[i][offset]!=0){ fStatus.Set(11+i); } } return; } // Set fStatus according to TSLP tests void ECACal::ValidateT(int cccc) { UInt_t TslpWord = fTslopeStat[cccc]; UInt_t PedWord = fPedStat[cccc]; int icrate = BitManip::GetCratewCell(cccc); int icard = BitManip::GetCardwCell(cccc); int ichan = BitManip::GetChannelwCell(cccc); // 1. Test MB and DB int offset = 80*icrate + 5*icard; if(fDQID[offset]!=fTslopeBID[offset]){ fStatus.Set(1); } int daughterID = (ichan/8) + 1; offset += daughterID; if(fDQID[offset]!=fTslopeBID[offset]){ fStatus.Set(1); } // 2. Check if ECA had any events if(BitManip::TestBit(TslpWord,14)){ fStatus.Set(2); } // 3. Check sequencer from ECA run if(BitManip::TestBit(TslpWord,26)){ fStatus.Set(2); } // 4. All TSlope cell validation bits: if( BitManip::GetBits(TslpWord, 1, 11) != 0){ fStatus.Set(7); } // 5. Pathological cell if ( (BitManip::TestBit(TslpWord,2)) || (BitManip::TestBit(TslpWord,3)) || (BitManip::TestBit(TslpWord,8)) ){ fStatus.Set(20); } // 6. Overall pedestal/TAC channel status if(BitManip::TestBit(TslpWord,13)){ fStatus.Set(8); } // 7. Frati jumps (not used atm so should never fail) if(BitManip::TestBit(TslpWord,12)){ fStatus.Set(9); } if(BitManip::TestBit(TslpWord,27)){ fStatus.Set(10); } // 8. Check CALDAC results for time offset = 16*icrate + icard; if(fTACADC[offset]!=0){ fStatus.Set(14); } // Did we do diff tests (successfully i.e. BIDs matched) in ECA? bool doDiff = true; if(BitManip::TestBit(PedWord,16)||BitManip::TestBit(PedWord,17)){ doDiff = false; } // 9. Pedestal checks for this cell: ONLY DO DIFF FOR TAC // Did TAC pass mean, width && diff (iff doDoff) tests? bool mean = BitManip::TestBit(PedWord,10); bool width = BitManip::TestBit(PedWord,11); bool diff = BitManip::TestBit(PedWord,12) && doDiff; if( (diff) ){ fStatus.Set(6); } // 5. Pathological cell // Did TAC fail 2 of the 3 tests? if( (mean&&width) || (mean&&diff) || (width&&diff) ){ fStatus.Set(19); } return; } // Applies ValidateQ and ValidateT void ECACal::ValidateCalibrate(int cccc) { if(fDoPed){ ECACal::ValidateQ(cccc); } if(fDoTslp){ ECACal::ValidateT(cccc); } // Overall pedestal/TAC cell status if( fStatus.GetBits(0, 8) != 0){ fStatus.Set(0); } return; } // Set fCharge[i] to extreme values according to fStatus void ECACal::PedCalStatus(int i) { if(fStatus.GetBits(1, 2) != 0){ // Failed at least one of MB/DB/Seq checks, no valid ECA constants fCharge[i] = -9998.0; return; } if(fTolLevel==1){ if(fStatus.Get(16 + i)){ // Pathological channel fCharge[i] = -9999.0; return; } } if(fTolLevel>=2){ if(fStatus.Get(3 + i)){ // Failed ped test fCharge[i] = -9999.0; return; } } if(fTolLevel>=4){ if(fStatus.Get(8)){ // Failed overall channel test fCharge[i] = -9999.0; return; } } return; } void ECACal::TSlpCalStatus() { if(fStatus.GetBits(1, 2) != 0){ // Failed at least one of MB/DB/Seq checks, no valid ECA constants fTAC = -9998.0; return; } if(fTolLevel==1){ if(fStatus.GetBits(19, 2) != 0){ // Pathological channel fTAC = -9999.0; return; } } if(fTolLevel>=2){ if(fStatus.GetBits(6, 2) != 0){ // Failed T ped/slope test fTAC = -9999.0; return; } } if(fTolLevel>=3){ if(fStatus.Get(3)){ // Failed QHS ped fTAC = -9999.0; return; } } if(fTolLevel>=4){ if(fStatus.Get(8)){ // Failed overall channel test fTAC = -9999.0; return; } } return; } void ECACal::PedADCStatus(int i) { if(fTolADC==0)return; if(fTolADC==1 || fTolADC==3){ if(fStatus.Get(11+i)){ // CALDAC-tagged fCharge[i] = -9999.0; return; } } return; } void ECACal::TSlpADCStatus() { if(fTolADC==0)return; if(fTolADC>=2){ if(fStatus.GetBits(9, 2) != 0){ // Frati-tagged fTAC = -9999.0; return; } } if(fTolADC==1 || fTolADC==3){ if(fStatus.Get(14)){ // CALDAC-tagged fTAC = -9999.0; return; } } return; } // Handle errors void ECACal::DontDie(std::string message, int flag_err, int info1, int info2) { if (fECAlogverb == 0) return; warn << YELLOW << " ECA calibration SETBACK: " << CLR << newline; if(flag_err==1){ // ECA selected but no ECA calibs requested warn << "You asked me to do ECA, but have not selected any ECA calibrations."<ns conversion."<ns conversion."<