#ifndef HADAQ_TDCPROCESSOR_H #define HADAQ_TDCPROCESSOR_H #include "hadaq/SubProcessor.h" #include "hadaq/TdcMessage.h" #include "hadaq/TdcIterator.h" #include "hadaq/TdcSubEvent.h" #include #include #include namespace hadaq { enum { FineCounterBins = 600, TotBins = 3000, ToTvalue = 20, ToThmin = 15, ToThmax = 60 }; /** \brief TDC processor * * \ingroup stream_hadaq_classes * * This is specialized sub-processor for FPGA-TDC. * Normally it should be used together with TrbProcessor, * which the only can provide data * Following levels of histograms filling are working * - 0 - none * - 1 - only basic statistic from TRB * - 2 - generic statistic over TDC channels * - 3 - basic per-channel histograms with IDs * - 4 - per-channel histograms with references **/ class TdcProcessor : public SubProcessor { friend class TrbProcessor; protected: /** TDC channel record */ struct ChannelRec { unsigned refch; /// rising_stat; /// rising_calibr; /// falling_stat; /// falling_calibr; /// tot0d_hist; ///0.01 are very special if (coarse_unit < 0) coarse_unit = hadaq::TdcMessage::CoarseUnit(); else if (coarse_unit > 0.01) coarse_unit = coarse_unit * hadaq::TdcMessage::CoarseUnit(); rising_calibr[0] = 2; // 2 values rising_calibr[1] = finemin; rising_calibr[2] = 0.; rising_calibr[3] = finemax; rising_calibr[4] = coarse_unit; for (int n = 0; n < 5; ++n) falling_calibr[n] = rising_calibr[n]; } /** Release memory used by calibration structures */ void ReleaseCalibr() { rising_stat.clear(); rising_calibr.clear(); falling_stat.clear(); falling_calibr.clear(); } /** Create ToT histogram */ void CreateToTHist() { tot0d_hist.resize(TotBins); for (unsigned n=0;n20 ns per channel base::H1handle fhTotMinusCounter{nullptr}; /// fCh; /// fDummyVect; /// *pStoreVect{nullptr}; /// fDummyFloat; /// *pStoreFloat = nullptr; /// fDummyDouble; /// *pStoreDouble = nullptr; /// fCalibrLog; /// &statistic, std::vector &calibr, bool use_linear = false, bool preliminary = false); void CopyCalibration(const std::vector &calibr, base::H1handle hcalibr, unsigned ch = 0, base::H2handle h2calibr = nullptr); bool CalibrateTot(unsigned ch, std::vector &hist, float &tot_shift, float &tot_dev, float cut = 0.); bool CheckPrintError(); bool SetChannelPrefix(unsigned ch, unsigned level = 3); bool CreateChannelHistograms(unsigned ch); double TestCanCalibrate(bool fillhist = false, std::string *status = nullptr); bool PerformAutoCalibrate(); void ClearChannelStat(unsigned ch); float ExtractCalibr(const std::vector &func, unsigned bin); /** extract calibration value */ inline float ExtractCalibrDirect(const std::vector &func, unsigned bin) { if (func.size() > 100) return func[bin]; // here only two-point linear approximation is supported // later one can extend approximation for N-points linear function if (bin <= func[1]) return func[2]; int pnt = func.size() - 2; // 3 for simple linear point if (bin >= func[pnt]) return func[pnt+1]; if (pnt > 3) { int segm = std::ceil((bin - func[1]) / (func[pnt] - func[1]) * (func[0] - 1)); // segment in linear approx pnt = 1 + segm*2; // first segment should have pnt = 3, second segment pnt = 5 and so on } return func[pnt-1] + (bin - func[pnt-2]) / (func[pnt] - func[pnt-2]) * (func[pnt+1] - func[pnt-1]); } void CreateV4CalibrTable(unsigned ch, uint32_t *table); void SetTable(uint32_t *table, unsigned addr, uint32_t value); void FindFMinMax(const std::vector &func, int nbin, int &fmin, int &fmax); void CreateBranch(TTree*) override; void AddError(unsigned code, const char *args, ...); public: /** error codes */ enum EErrors { errNoHeader, ///< no header found errChId, ///< wrong channel id errEpoch, ///< wrong/missing epoch errFine, ///< bad fine counter err3ff, ///< 0x3ff error errCh0, ///< missing channel 0 errMismatchDouble, ///< errUncknHdr, ///< unknown header errDesignId, ///< mismatch in design id errMisc ///< all other errors }; /** edges mask */ enum EEdgesMasks { edge_Rising = 1, ///< process only rising edge edge_BothIndepend = 2, ///< process rising and falling edges independent edge_ForceRising = 3, ///< use rising edge calibration for falling edge_CommonStatistic = 4 ///< accumulate common statistic for both }; TdcProcessor(TrbProcessor* trb, unsigned tdcid, unsigned numchannels = MaxNumTdcChannels, unsigned edge_mask = 1, bool ver4 = false, bool dogma = false); virtual ~TdcProcessor(); static void SetMaxBoardId(unsigned); static void SetDefaults(unsigned numfinebins = 600, unsigned totrange = 100, unsigned hist2dreduced = 10); static void SetErrorMask(unsigned mask = 0xffffffffU); static void SetAllHistos(bool on = true); static void SetIgnoreCalibrMsgs(bool on = true); static void SetHadesMonitorInterval(int tm = -1); static int GetHadesMonitorInterval(); static void SetHadesReducedMonitoring(bool on=true); static bool IsHadesReducedMonitoring(); static void SetUseDTrigForRef(bool on = true); static void SetTriggerDWindow(double low = -25, double high = 50); static void SetToTCalibr(int minstat = 100, double rms = 0.15); static void SetDefaultLinearNumPoints(int cnt = 2); static void SetUseAsDTrig(bool on = true); static void SetStoreCalibrTables(bool on = true); static void SetPreventFineCalibration(bool on = true); static void SetTimeRefKind(int kind = -1); /** Set number of TDC messages, which should be skipped from subevent before analyzing it */ void SetSkipTdcMessages(unsigned cnt = 0) { fSkipTdcMessages = cnt; } void Set400Mhz(bool on = true); void SetCustomMhz(float freq = 400.); /** When file taken with HADES and falling edges may be modified while ToT offset can be negative * Resulting fine-time offset also can be negative and therefore coarse message counter is modified * Enable flag to be able recognize and repair such shifts. * Works only if ToT offset is not less than -1 ns, otherwise overflows too many and algorithm may not work */ void SetModifiedFallingEdges(bool on = true) { fModifiedFallingEdges = on; } /** Set paired channels mode * In such case odd channels are normal rising edge but even channels are falling edge of previous one */ void SetPairedChannels(bool on = true) { fPairedChannels = on; } /** Get paired channels mode */ bool GetPairedChannels() const { return fPairedChannels; } /** Set minimal counts number for ToT calibration */ void SetTotStatLimit(int minstat = 100) { fTotStatLimit = minstat; } /** Set maximal allowed RMS for ToT histogram in ns */ void SetTotRMSLimit(double rms = 0.15) { fTotRMSLimit = rms; } /** Returns number of TDC channels */ inline unsigned NumChannels() const { return fNumChannels; } /** Returns number of fine TDC bins */ inline unsigned NumFineBins() const { return fNumFineBins; } /** Returns true if processing rising edge */ inline bool DoRisingEdge() const { return true; } /** Returns true if processing falling */ inline bool DoFallingEdge() const { return fEdgeMask > 1; } /** Returns value of edge mask */ inline unsigned GetEdgeMask() const { return fEdgeMask; } inline bool IsVersion4() const { return fVersion4; } /** Returns calibration progress */ double GetCalibrProgress() const { return fCalibrProgress; } /** Returns calibration status */ std::string GetCalibrStatus() const { return fCalibrStatus; } /** Returns calibration quality */ double GetCalibrQuality() const { return fCalibrQuality; } /** Acknowledge calibration quality */ void AcknowledgeCalibrQuality(double lvl = 1.) { if (fCalibrQuality < lvl) { fCalibrQuality = lvl; fCalibrProgress = 1.; fCalibrStatus = "Ready"; } } /** Take all messages from calibration log */ std::vector TakeCalibrLog() { std::vector res; std::swap(res, fCalibrLog); return res; } /** Get number of indexed histograms */ int GetNumHist() const { return 9; } /** Get histogram name by index */ const char* GetHistName(int k) const { switch(k) { case 0: return "RisingFine"; case 1: return "RisingCoarse"; case 2: return "RisingRef"; case 3: return "FallingFine"; case 4: return "FallingCoarse"; case 5: return "Tot"; case 6: return "RisingMult"; case 7: return "FallingMult"; case 8: return "RisingTmdsRef"; } return ""; } /** Get histogram by index and channel id */ base::H1handle GetHist(unsigned ch, int k = 0) { if (ch >= NumChannels()) return nullptr; switch (k) { case 0: return fCh[ch].fRisingFine; case 1: return nullptr; case 2: return fCh[ch].fRisingRef; case 3: return fCh[ch].fFallingFine; case 4: return nullptr; case 5: return fCh[ch].fTot; case 6: return fCh[ch].fRisingMult; case 7: return fCh[ch].fFallingMult; case 8: return fCh[ch].fRisingTmdsRef; } return nullptr; } void CreateHistograms(int *arr = nullptr); /** Assign per HLD histos */ void AssignPerHldHistos(unsigned id, base::H1handle *hHits, base::H1handle *hErrs, base::H2handle *hChHits, base::H2handle *hChErrs, base::H2handle *hChCorr, base::H2handle *hQaFine, base::H2handle *hQaToT, base::H2handle *hQaEdges, base::H2handle *hQaErrors, base::H2handle *hTot, base::H2handle *hShift, base::H1handle *hExpTot, base::H2handle *hDev, base::H2handle *hTPrev, base::H2handle *hTotCount) { fHldId = id; fHitsPerHld = hHits; fErrPerHld = hErrs; fChHitsPerHld = hChHits; fChErrPerHld = hChErrs; fChCorrPerHld = hChCorr; fQaFinePerHld = hQaFine; fQaToTPerHld = hQaToT; fQaEdgesPerHld = hQaEdges; fQaErrorsPerHld = hQaErrors; fToTPerTDCChannel = hTot; fShiftPerTDCChannel = hShift; fExpectedToTPerTDC = hExpTot; fDevPerTDCChannel = hDev; fTPreviousPerTDCChannel = hTPrev; fToTCountPerTDCChannel = hTotCount; } /** Set calibration trigger type(s) * One could specify up-to 4 different trigger types, for instance 0x1 and 0xD * First argument could be use to enable all triggers (0xFFFF, default) * or none of the triggers (-1) */ void SetCalibrTrigger(int typ1 = 0xFFFF, unsigned typ2 = 0, unsigned typ3 = 0, unsigned typ4 = 0) { fCalibrTriggerMask = 0; if (typ1<0) return; if (typ1 >= 0xFFFF) { fCalibrTriggerMask = 0xFFFF; return; } if (typ1 && (typ1<=0xF)) fCalibrTriggerMask |= (1 << typ1); if (typ2 && (typ2<=0xF)) fCalibrTriggerMask |= (1 << typ2); if (typ3 && (typ3<=0xF)) fCalibrTriggerMask |= (1 << typ3); if (typ4 && (typ4<=0xF)) fCalibrTriggerMask |= (1 << typ4); } /** Set calibration trigger mask directly, 1bit per each trigger type * if bit 0x80000000 configured, calibration will use temperature correction */ void SetCalibrTriggerMask(unsigned trigmask) { fCalibrTriggerMask = trigmask & 0x3FFF; fCalibrUseTemp = (trigmask & 0x80000000) != 0; } /** Set temperature coefficient, which is applied to calibration curves * Typical value is about 0.0044 */ void SetCalibrTempCoef(float coef) { fCalibrTempCoef = coef; } /** Set shift for the channel time stamp, which is added with temperature change */ void SetChannelTempShift(unsigned ch, float shift_per_grad) { if (ch < fCh.size()) fCh[ch].time_shift_per_grad = shift_per_grad; } /** Set channel TOT shift in nano-seconds, typical value is around 30 ns */ void SetChannelTotShift(unsigned ch, float tot_shift) { if (ch < fCh.size()) fCh[ch].tot_shift = tot_shift; } /** Returns channel TOT shift in nano-seconds */ double GetChannelTotShift(unsigned ch) const { return (ch < fCh.size()) ? fCh[ch].tot_shift : 0; } void DisableCalibrationFor(unsigned firstch, unsigned lastch = 0); void SetRefChannel(unsigned ch, unsigned refch, unsigned reftdc = 0xffff, int npoints = 5000, double left = -10., double right = 10., bool twodim = false); void SetRefTmds(unsigned ch, unsigned refch, int npoints, double left, double right); bool SetDoubleRefChannel(unsigned ch1, unsigned ch2, int npx = 200, double xmin = -10., double xmax = 10., int npy = 200, double ymin = -10., double ymax = 10.); void CreateRateHisto(int np = 1000, double xmin = 0., double xmax = 1e5); double GetTdcCoarseUnit() const; /** Configure upper limit for ToT */ void SetTotUpperLimit(double lmt = 20) { fTotUpperLimit = lmt; } /** Get configured upper limit for ToT */ double GetTotUpperLimit() const { return fTotUpperLimit; } bool EnableRefCondPrint(unsigned ch, double left = -10, double right = 10, int numprint = 0); /** If set, each hit must be supplied with epoch message */ void SetEveryEpoch(bool on) { fEveryEpoch = on; } /** Return true if each hit must be supplied with epoch message */ bool IsEveryEpoch() const { return fEveryEpoch; } bool IsRegularChannel0() const; void SetLinearCalibration(unsigned nch, unsigned finemin=30, unsigned finemax=500); /** configure auto calibration */ void SetAutoCalibration(long cnt = 100000) { fCalibrCounts = cnt % 1000000000L; fAutoCalibrOnce = (cnt>1000000000L); fAutoCalibr = (cnt >= 0); } /** Configure mode, when calibration should be start/stop explicitly */ void UseExplicitCalibration() { fAllCalibrMode = 0; } /** Return explicit calibr mode, -1 - off, 0 - normal data processing, 1 - accumulating calibration */ int GetExplicitCalibrationMode() { return fAllCalibrMode; } void BeginCalibration(long cnt); void CompleteCalibration(bool dummy = false, const std::string &filename = "", const std::string &subdir = ""); bool LoadCalibration(const std::string& fprefix); /** When specified, calibration will be written to the file * If every_time == true, write every time when automatic calibration performed, otherwise only at the end */ void SetWriteCalibration(const std::string &fprefix, bool every_time = false, bool use_linear = false) { fWriteCalibr = fprefix; fWriteEveryTime = every_time; fUseLinear = use_linear; } /** Enable linear calibrations */ void SetUseLinear(bool on = true) { fUseLinear = on; } /** Returns true is linear calibrations are configured */ bool IsUseLinear() const { return fUseLinear; } /** Set number of points in linear calibrations */ void SetLinearNumPoints(int cnt = 2) { fLinearNumPoints = (cnt < 2) ? 2 : ((cnt > 100) ? 100 : cnt); } /** Return number of points in linear calibrations */ int GetLinearNumPoints() const { return fLinearNumPoints; } void SetToTRange(double tot_0xd, double hmin, double hmax); void ConfigureToTByHwType(unsigned hwtype); /** When enabled, last hit time in the channel used for reference time calculations * By default, first hit time is used * Special case is reference to channel 0 - here all hits will be used */ void SetUseLastHit(bool on = true) { fUseLastHit = on; } /** Returns true if last hit used in reference histogram calculations */ bool IsUseLastHist() const { return fUseLastHit; } /** Return last TDC header, seen by the processor */ const TdcMessage& GetLastTdcHeader() const { return fLastTdcHeader; } /** Return last TDC header, seen by the processor */ const TdcMessage& GetLastTdcTrailer() const { return fLastTdcTrailer; } void UserPostLoop() override; /** Get ref histogram for specified channel */ base::H1handle GetChannelRefHist(unsigned ch, bool = true) { return ch < fCh.size() ? fCh[ch].fRisingRef : nullptr; } /** Clear ref histogram for specified channel */ void ClearChannelRefHist(unsigned ch, bool rising = true) { ClearH1(GetChannelRefHist(ch, rising)); } /** Enable/disable store of channel 0 in output event */ void SetCh0Enabled(bool on = true) { fCh0Enabled = on; } /** Scan all messages, find reference signals * if returned false, buffer has error and must be discarded */ bool FirstBufferScan(const base::Buffer& buf) override { return fVersion4 ? DoBuffer4Scan(buf, true) : DoBufferScan(buf, true); } /** Scan buffer for selecting messages inside trigger window */ bool SecondBufferScan(const base::Buffer& buf) override { return fVersion4 ? DoBuffer4Scan(buf, false) : DoBufferScan(buf, false); } void IncCalibration(unsigned ch, bool rising, unsigned fine, unsigned value); void ProduceCalibration(bool clear_stat = true, bool use_linear = false, bool dummy = false, bool preliminary = false); /** Access value of temperature during calibration. * Used to adjust all kind of calibrations afterwards */ float GetCalibrTemp() const { return fCalibrTemp; } /** Set temperature used for calibration */ void SetCalibrTemp(float v) { fCalibrTemp = v; } void StoreCalibration(const std::string& fname, unsigned fileid = 0); float GetCalibrFunc(unsigned ch, bool isrising, unsigned bin) { return ExtractCalibrDirect(isrising ? fCh[ch].rising_calibr : fCh[ch].falling_calibr, bin); } void Store(base::Event*) override; void ResetStore() override; unsigned TransformTdcData(hadaqs::RawSubevent* sub, uint32_t *rawdata, unsigned indx, unsigned datalen, hadaqs::RawSubevent* tgt = nullptr, unsigned tgtindx = 0); void EmulateTransform(int dummycnt); void DoHadesHistAnalysis(); void FillToTHistogram(); static TdcProcessor *CreateFromCalibr(TrbProcessor *trb, const std::string &fname); }; } #endif