/* ======================================================================== *\ ! ! * ! * This file is part of CheObs, the Modular Analysis and Reconstruction ! * Software. It is distributed to you in the hope that it can be a useful ! * and timesaving tool in analysing Data of imaging Cerenkov telescopes. ! * It is distributed WITHOUT ANY WARRANTY. ! * ! * Permission to use, copy, modify and distribute this software and its ! * documentation for any purpose is hereby granted without fee, ! * provided that the above copyright notice appears in all copies and ! * that both that copyright notice and this permission notice appear ! * in supporting documentation. It is provided "as is" without express ! * or implied warranty. ! * ! ! ! Author(s): Thomas Bretz, 1/2009 ! ! Copyright: CheObs Software Development, 2000-2009 ! ! \* ======================================================================== */ ////////////////////////////////////////////////////////////////////////////// // // MSimTrigger // // This task takes the pure analog channels and simulates a trigger // electronics. // // In a first step several channels can be summed together by a look-up table // fRouteAC. // // In a second step from these analog channels the output of a discriminator // is calculated using a threshold and optional a fixed digital signal length. // // The signal length of the digital signal emitted by the discriminator // can either be bound to the time the signal is above the threshold // defined by fDiscriminatorThreshold if fDigitalSignalLength<0 or set to a // fixed length (fDigitalSignalLength>0). // // With a second look-up table fCoincidenceMap the analog channels are // checked for coincidences. The coincidense must at least be of the length // defined by fCoincidenceTime. The earliest coincide is then stored as // trigger position. // // // For MAGIC1: // - fDigitalSignalLength between 6ns and 12ns // - fCoincidenceTime between 0.25ns to 1ns // // // Input Containers: // IntendedPulsePos [MParameterD] // MAnalogChannels // MRawRunHeader // // Output Containers: // TriggerPos [MParameterD] // MRawEvtHeader // ////////////////////////////////////////////////////////////////////////////// #include "MSimTrigger.h" #include "MLog.h" #include "MLogManip.h" #include "MParList.h" #include "MParameters.h" #include "MLut.h" #include "MArrayI.h" #include "MRawEvtHeader.h" #include "MRawRunHeader.h" #include "MAnalogSignal.h" #include "MAnalogChannels.h" #include "MDigitalSignal.h" #include "MTriggerPattern.h" #include "MPedestalCam.h" #include "MPedestalPix.h" ClassImp(MSimTrigger); using namespace std; // -------------------------------------------------------------------------- // // Default Constructor. // MSimTrigger::MSimTrigger(const char *name, const char *title) : fCamera(0), fPulsePos(0), fTrigger(0), fRunHeader(0), fEvtHeader(0), fElectronicNoise(0), fGain(0), fDiscriminatorThreshold(-1), fDigitalSignalLength(8), fCoincidenceTime(0.5), fShiftBaseline(kTRUE), fUngainSignal(kTRUE) { fName = name ? name : "MSimTrigger"; fTitle = title ? title : "Task to simulate trigger electronics"; } // -------------------------------------------------------------------------- // // Take two TObjArrays with a collection of digital signals. // Every signal from one array is compared with any from the other array. // For all signals whihc overlaps and which have an overlap time >gate // a new digital signal is created storing start time and length of overlap. // They are collected in a newly allocated TObjArray. A pointer to this array // is returned. // // Th euser gains owenership of the object, ie.e., the user is responsible of // deleting the memory. // TObjArray *MSimTrigger::CalcCoincidence(const TObjArray &arr1, const TObjArray &arr2, Float_t gate) const { TObjArray *res = new TObjArray; if (arr1.GetEntriesFast()==0 || arr2.GetEntriesFast()==0) return res; TIter Next1(&arr1); MDigitalSignal *ttl1 = 0; while ((ttl1=static_cast(Next1()))) { TIter Next2(&arr2); MDigitalSignal *ttl2 = 0; while ((ttl2=static_cast(Next2()))) { MDigitalSignal *ttl = new MDigitalSignal(*ttl1, *ttl2); if (ttl->GetLength()<=gate) { delete ttl; continue; } res->Add(ttl); } } res->SetOwner(); return res; } // -------------------------------------------------------------------------- // // Check for the necessary parameter containers. Read the luts. // Int_t MSimTrigger::PreProcess(MParList *pList) { fTrigger = (MParameterD*)pList->FindCreateObj("MParameterD", "TriggerPos"); if (!fTrigger) return kFALSE; fPulsePos = (MParameterD*)pList->FindObject("IntendedPulsePos", "MParameterD"); if (!fPulsePos) { *fLog << err << "IntendedPulsePos [MParameterD] not found... aborting." << endl; return kFALSE; } fCamera = (MAnalogChannels*)pList->FindObject("MAnalogChannels"); if (!fCamera) { *fLog << err << "MAnalogChannels not found... aborting." << endl; return kFALSE; } fElectronicNoise = 0; if (fShiftBaseline) { fElectronicNoise = (MPedestalCam*)pList->FindObject("ElectronicNoise", "MPedestalCam"); if (!fElectronicNoise) { *fLog << err << "ElectronicNoise [MPedestalCam] not found... aborting." << endl; return kFALSE; } *fLog << inf << "Baseline will be shifted back to 0 for discriminator." << endl; } fGain = 0; if (fUngainSignal) { fGain = (MPedestalCam*)pList->FindObject("Gain", "MPedestalCam"); if (!fGain) { *fLog << err << "Gain [MPedestalCam] not found... aborting." << endl; return kFALSE; } *fLog << inf << "Discriminator will be multiplied by applied gain." << endl; } fRunHeader = (MRawRunHeader*)pList->FindObject("MRawRunHeader"); if (!fRunHeader) { *fLog << err << "MRawRunHeader not found... aborting." << endl; return kFALSE; } fEvtHeader = (MRawEvtHeader*)pList->FindCreateObj("MRawEvtHeader"); if (!fEvtHeader) return kFALSE; fRouteAC.Delete(); if (!fNameRouteAC.IsNull() && fRouteAC.ReadFile(fNameRouteAC)<0) return kFALSE; fCoincidenceMap.Delete(); if (!fNameCoincidenceMap.IsNull() && fCoincidenceMap.ReadFile(fNameCoincidenceMap)<0) return kFALSE; // ---------------- Consistency checks ---------------------- if (!fRouteAC.IsEmpty() && !fCoincidenceMap.IsEmpty() && fCoincidenceMap.GetMaxIndex()>fRouteAC.GetNumRows()-1) { *fLog << err; *fLog << "ERROR - AC routing produces " << fRouteAC.GetNumRows() << " analog channels," << endl; *fLog << " but the coincidence map expects at least " << fCoincidenceMap.GetMaxIndex()+1 << " channels." << endl; return kERROR; } if (fDiscriminatorThreshold<=0) { *fLog << err << "ERROR - Discriminator threshold " << fDiscriminatorThreshold << " invalid." << endl; return kFALSE; } if (fElectronicNoise && !fRouteAC.IsEmpty() && !fRouteAC.IsDefault()) { // FIXME: Apply to analog channels when summing *fLog << warn << "WARNING - A baseline shift doesn't make sense for sum-channels... reset." << endl; fElectronicNoise = 0; } if (fGain && !fRouteAC.IsEmpty() && !fRouteAC.IsDefault()) { // FIXME: Apply to analog channels when summing *fLog << warn << "WARNING - Ungain doesn't make sense for sum-channels... reset." << endl; fGain = 0; } // ---------------- Information output ---------------------- *fLog << inf; if (fRouteAC.IsEmpty()) *fLog << "Re-routing/summing of analog channels before discriminator switched off." << endl; else *fLog << "Using " << fNameRouteAC << " for re-routing/summing of analog channels before discriminator." << endl; if (fCoincidenceMap.IsEmpty()) *fLog << "No coincidences of digital channels will be checked. Signal-above-threshold trigger applied." << endl; else *fLog << "Using " << fNameCoincidenceMap << " to check for coincidences of the digital channels." << endl; *fLog << "Using discriminator threshold of " << fDiscriminatorThreshold << endl; return kTRUE; } // -------------------------------------------------------------------------- // Int_t MSimTrigger::Process() { // Invalidate trigger fTrigger->SetVal(-1); // ================== Simulate channel bundling ==================== // FIXME: Before we can bundle the channels we have to make a copy // and simulate clipping // Check if routing should be done const Bool_t empty = fRouteAC.IsEmpty(); // If no channels are summed the number of patches stays the same const UInt_t npatch = empty ? fCamera->GetNumChannels() : fRouteAC.GetEntriesFast(); // Use the given analog channels as default out. If channels are // summed overwrite with a newly allocated set of analog channels MAnalogChannels *patches = fCamera; if (!empty) { // FIXME: Can we add gain and offset here into a new container? patches = new MAnalogChannels(npatch, fCamera->GetNumSamples()); for (UInt_t i=0; iGetFreqSampling()/1000.; const Float_t nsamp = fRunHeader->GetNumSamplesHiGain(); const Float_t pulspos = fPulsePos->GetVal()/freq; // Valid range in units of bins const Float_t min = fCamera->GetValidRangeMin()+pulspos; const Float_t max = fCamera->GetValidRangeMax()-(nsamp-pulspos); // Create an array for the individual triggers TObjArray triggers; triggers.SetOwner(); Int_t cnt = 0; Int_t rmlo = 0; Int_t rmhi = 0; for (int j=0; j(ttls[idx[0]]->Clone()); arr->SetOwner(); // compare to all other channels in this coincidence patch, one by one for (UInt_t k=1; kGetEntriesFast()>0; k++) { TObjArray *res = CalcCoincidence(*arr, *static_cast(ttls[idx[k]]), fCoincidenceTime); // Delete the original array and keep the new one delete arr; arr = res; } // Remove all signals which are not in the valid digitization range // (This is not the digitization window, but the region in which // the analog channels contain usefull data) TIter Next(arr); MDigitalSignal *ttl = 0; while ((ttl=static_cast(Next()))) { if (ttl->GetStart()Remove(ttl); rmlo++; } if (ttl->GetStart()>max) { delete arr->Remove(ttl); rmhi++; } } // Remove the empty slots arr->Compress(); cnt += arr->GetEntriesFast(); // If we have at least one trigger keep the earliest one. // FIXME: The triggers should be ordered in time automatically: To be checked! // FIXME: Simulate trigger dead-time! if (arr->GetEntriesFast()>0) triggers.Add(arr->RemoveAt(0)); // delete the allocated space delete arr; } // No trigger issued. Go on. if (triggers.GetEntriesFast()==0) { if (rmlo>0 || rmhi>0) *fLog << inf2 << rmlo << "/" << rmhi << " trigger out of valid range. No trigger raised." << endl; return kTRUE; } // There are usually not enough entries that it is worth to search // for the earliest instead of just sorting and taking the first one // FIXME: This could be improved if checking for IsSortable // is omitted triggers.Sort(); // FIXME: Jitter! (Own class?) fTrigger->SetVal(static_cast(triggers[0])->GetStart()); fTrigger->SetReadyToSave(); // Flag this event as triggered by the lvl1 trigger (FIXME?) fEvtHeader->SetTriggerPattern((UInt_t)~(MTriggerPattern::kTriggerLvl1 | (MTriggerPattern::kTriggerLvl1<<8))); fEvtHeader->SetReadyToSave(); // inf2? *fLog << inf; *fLog << cnt << " triggers left in " << triggers.GetEntriesFast() << " patches (" << rmlo << "/" << rmhi << " trigger out of valid range), T=" << fTrigger->GetVal(); *fLog << endl; return kTRUE; } // -------------------------------------------------------------------------- // // FileNameRouteac: routeac.txt // FileNameCoincidenceMap: coincidence.txt // DiscriminatorTheshold: 3.5 // DigitalSignalLength: 8 // CoincidenceTime: 0.5 // Int_t MSimTrigger::ReadEnv(const TEnv &env, TString prefix, Bool_t print) { Bool_t rc = kFALSE; if (IsEnvDefined(env, prefix, "FileNameRouteAC", print)) { rc = kTRUE; fNameRouteAC = GetEnvValue(env, prefix, "FileNameRouteAC", fNameRouteAC); } if (IsEnvDefined(env, prefix, "FileNameCoincidenceMap", print)) { rc = kTRUE; fNameCoincidenceMap = GetEnvValue(env, prefix, "FileNameCoincidenceMap", fNameCoincidenceMap); } if (IsEnvDefined(env, prefix, "DiscriminatorThreshold", print)) { rc = kTRUE; fDiscriminatorThreshold = GetEnvValue(env, prefix, "DiscriminatorThreshold", fDiscriminatorThreshold); } if (IsEnvDefined(env, prefix, "DigitalSignalLength", print)) { rc = kTRUE; fDigitalSignalLength = GetEnvValue(env, prefix, "DigitalSignalLength", fDigitalSignalLength); } if (IsEnvDefined(env, prefix, "CoincidenceTime", print)) { rc = kTRUE; fCoincidenceTime = GetEnvValue(env, prefix, "CoincidenceTime", fCoincidenceTime); } return rc; }