diff --git a/algo/detectors/t0/T0ReadoutConfig.cxx b/algo/detectors/t0/T0ReadoutConfig.cxx new file mode 100644 index 0000000000000000000000000000000000000000..662ba589db43cc995db49a6e510f9e3810480a20 --- /dev/null +++ b/algo/detectors/t0/T0ReadoutConfig.cxx @@ -0,0 +1,180 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer] */ + +#include "T0ReadoutConfig.h" + +#include "CbmTofAddress.h" + +#include <Logger.h> + +#include <bitset> +#include <iomanip> + +#include "gDpbMessv100.h" + +using namespace std; + +namespace cbm::algo +{ + // --- Constructor ------------------------------------------------------------------ + T0ReadoutConfig::T0ReadoutConfig() { Init(); } + // ------------------------------------------------------------------------------------ + + // --- Destructor ----------------------------------------------------------------- + T0ReadoutConfig::~T0ReadoutConfig() {} + // ------------------------------------------------------------------------------------ + + // --- Equipment IDs -------------------------------------------------------------- + std::vector<uint16_t> T0ReadoutConfig::GetEquipmentIds() + { + std::vector<uint16_t> result; + for (auto& entry : fReadoutMap) + result.push_back(entry.first); + return result; + } + // ------------------------------------------------------------------------------------ + + // --- Number of elinks for a component / equipment ------------------------------- + size_t T0ReadoutConfig::GetNumElinks(uint16_t equipmentId) + { + size_t result = 0; + auto it = fReadoutMap.find(equipmentId); + if (it != fReadoutMap.end()) result = fReadoutMap[equipmentId].size(); + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Mapping (equimentId, elink) -> address[channel] ------------------------------ + std::vector<uint32_t> T0ReadoutConfig::Map(uint16_t equipmentId, uint16_t elinkId) + { + std::vector<uint32_t> result; + auto equipIter = fReadoutMap.find(equipmentId); + if (equipIter != fReadoutMap.end()) { + if (elinkId < equipIter->second.size()) { result = equipIter->second.at(elinkId); } + } + return result; + } + // ------------------------------------------------------------------------------------ + + void T0ReadoutConfig::Init() + { + // This here refers to the mCBM 2022 setup. + // Taken from CbmMcbm2018TofPar in combination with macro/beamtime/mcbm2022/mBmonCriPar.par + + // Array to hold the unique IDs (equipment ID) for all TOF DPBs + uint16_t eqId[numComp] = {0xabf3, 0xabf2, 0xabf1, 0xabf0}; + + // Constructing the map (equipmentId, eLink, channel) -> (TOF address) + const uint32_t numElinksPerComp = numFebsPerComp * numAsicsPerFeb; + const uint32_t numChanPerComp = numChanPerAsic * numElinksPerComp; + + // Constructs the fviRpcChUId array + BuildChannelsUidMap(); + + for (uint16_t comp = 0; comp < numComp; comp++) { + uint16_t equipment = eqId[comp]; + fReadoutMap[equipment].resize(numElinksPerComp); + + for (uint16_t elink = 0; elink < numElinksPerComp; elink++) { + fReadoutMap[equipment][elink].resize(numChanPerAsic); + + for (uint16_t channel = 0; channel < numChanPerAsic; channel++) { + + const uint32_t chanInComp = elink * numChanPerAsic + channel; + uint32_t chanInSys = comp * numChanPerComp + chanInComp; + + { // hack? perhaps can be removed + const int numFullFlims = 8; + if (comp > numFullFlims) { chanInSys -= (comp - numFullFlims) * numChanPerComp / 2; } + } + + const uint32_t chanUId = fviRpcChUId[chanInSys]; + fReadoutMap[equipment][elink][channel] = chanUId; + } //# channel + } //# elink + } //# component + } + + // ------------------------------------------------------------------------- + void T0ReadoutConfig::BuildChannelsUidMap() + { + const uint32_t numAsics = numComp * numFebsPerComp * numAsicsPerFeb; + const uint32_t numChan = numAsics * numChanPerAsic; + fviRpcChUId.resize(numChan); + + uint32_t uCh = 0; + for (uint32_t uGbtx = 0; uGbtx < numCrob; ++uGbtx) { + const uint32_t uCh0 = uCh; + switch (rpcType[uGbtx]) { + case 5: { + /// Special Treatment for the T0 diamond + BuildChannelsUidMapT0(uCh, uGbtx); + break; + } + case 99: { + /// Special Treatment for the 2022 T0 diamond, keep past behavior for older data! + BuildChannelsUidMapT0_2022(uCh, uGbtx); + break; + } + case -1: { + LOG(info) << " Found unused GBTX link at uCh = " << uCh; + uCh += 160; + break; + } + default: { + LOG(error) << "Invalid T0 Type specifier for GBTx " << std::setw(2) << uGbtx << ": " << rpcType[uGbtx]; + } + } // switch (rpcType[uGbtx]) + if ((int32_t)(uCh - uCh0) != numFebsPerComp * numAsicsPerFeb * numChanPerAsic / 2) { + LOG(fatal) << "T0 mapping error for Gbtx " << uGbtx << ", diff = " << uCh - uCh0; + } + } + } + + // ------------------------------------------------------------------------- + void T0ReadoutConfig::BuildChannelsUidMapT0(uint32_t& uCh, uint32_t uGbtx) + { + LOG(info) << " Map diamond " << moduleId[uGbtx] << " at GBTX " << uGbtx << " - uCh = " << uCh; + for (uint32_t uGet4 = 0; uGet4 < numElinksPerCrob; ++uGet4) { + for (uint32_t uGet4Ch = 0; uGet4Ch < numChanPerAsic; ++uGet4Ch) { + /// Mapping for the 2022 beamtime + if (uGet4 < 32 && 0 == uGet4Ch && -1 < moduleId[uGbtx]) { + uint32_t uChannelT0 = uGet4 + 32 * rpcSide[uGbtx]; + uChannelT0 /= 8; + fviRpcChUId[uCh] = CbmTofAddress::GetUniqueAddress(moduleId[uGbtx], 0, uChannelT0, 0, rpcType[uGbtx]); + printf(" T0 channel: %u from GBTx %2u, indx %d address %08x", uChannelT0, uGbtx, uCh, fviRpcChUId[uCh]); + } // Valid T0 channel + else { + fviRpcChUId[uCh] = 0; + } // Invalid T0 channel + uCh++; + } + } + } + + // ------------------------------------------------------------------------- + void T0ReadoutConfig::BuildChannelsUidMapT0_2022(uint32_t& uCh, uint32_t uGbtx) + { + LOG(info) << " Map 2022 diamond " << moduleId[uGbtx] << " at GBTX " << uGbtx << " - uCh = " << uCh; + for (uint32_t uGet4 = 0; uGet4 < numElinksPerCrob; ++uGet4) { + for (uint32_t uGet4Ch = 0; uGet4Ch < numChanPerAsic; ++uGet4Ch) { + /// Mapping for the 2022 beamtime + if (-1 < moduleId[uGbtx] && uGet4 < 32 && 0 == uGet4 % 4 && 0 == uGet4Ch) { + /// 1 channel per physical GET4, 2 links per physical GET4, 4 physical GET4s per GBTx, 1 GBTx per comp. + /// 16 channels for one side, 16 for the other + uint32_t uChannelT0 = (uGet4 / 8 + 4 * (uGbtx / 2)) % 16; + /// Type hard-coded to allow different parameter values to separate 2022 T0 and pre-2022 T0 + fviRpcChUId[uCh] = CbmTofAddress::GetUniqueAddress(moduleId[uGbtx], 0, uChannelT0, rpcSide[uGbtx], 5); + printf(" Bmon channel: %u side %u from GBTx %2u, indx %d address %08x \n", uChannelT0, rpcSide[uGbtx], uGbtx, + uCh, fviRpcChUId[uCh]); + } // Valid T0 channel + else { + fviRpcChUId[uCh] = 0; + } // Invalid T0 channel + uCh++; + } + } + } +} /* namespace cbm::algo */ diff --git a/algo/detectors/t0/T0ReadoutConfig.h b/algo/detectors/t0/T0ReadoutConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..a12f3a396a8f5959b2c93aaf10a5c5d19a3da6ad --- /dev/null +++ b/algo/detectors/t0/T0ReadoutConfig.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer] */ + +// ------------------------------------------------------------------------- +// ----- T0ReadoutConfig header file ----- +// ----- Created 22/02/22 by P.-A. Loizeau ----- +// ----- Modified 07/12/18 by A Kumar ----- +// ------------------------------------------------------------------------- + +#ifndef ALGO_DETECTORS_T0_T0READOUTCONFIG_H +#define ALGO_DETECTORS_T0_T0READOUTCONFIG_H + +#include <cstdint> +#include <map> +#include <vector> + +namespace cbm::algo +{ + class T0ReadoutConfig { + + public: + /** @brief Constructor **/ + T0ReadoutConfig(); + + /** @brief Destructor **/ + virtual ~T0ReadoutConfig(); + + /** @brief Equipment in the configuration + ** @return Vector of equipment IDs + **/ + std::vector<uint16_t> GetEquipmentIds(); + + /** @brief Number of elinks of a component + ** @param Equipment ID + ** @return Number of elinks + **/ + size_t GetNumElinks(uint16_t equipmentId); + + /** @brief API: Mapping from component and elink to addresses per channel + ** @param equipId Equipment identifier (component) + ** @param elink Elink number within component + ** @return Vector of TOF addresses, indexed via channel number + */ + std::vector<uint32_t> Map(uint16_t equipId, uint16_t elink); + + private: + // --- T0 readout map + // --- Map index: (equipment, elink, channel), map value: (TOF address) + std::map<uint16_t, std::vector<std::vector<uint32_t>>> fReadoutMap = {}; + + /** @brief Initialisation of readout map **/ + void Init(); + + /// Constants + /// Taken from mBmonCriPar.par + static const uint16_t numComp = 4; // Total number of TOF DPBs in system + static const uint32_t numFebsPerComp = 10; // Number of FEEs which are connected to one GDPB + static const uint32_t numAsicsPerFeb = 8; // Number of ASICs connected in each FEB for TOF + static const uint32_t numChanPerAsic = 4; // Number of channels in each ASIC + static const uint32_t numCrob = 8; // Total number of Gbtx links + + /// Taken from CbmMcbm2018TofPar.h + static const uint32_t numFebsPerCrob = 5; // Number of FEBs connected to each CROB for mTof 2019 + static const uint32_t numElinksPerCrob = numAsicsPerFeb * numFebsPerCrob; + + // Module Identifier connected to Gbtx link, has to match geometry + const int32_t moduleId[numCrob] = {0, -1, 0, -1, 0, -1, 0, -1}; + + // type of Rpcs connected to Gbtx link + const int32_t rpcType[numCrob] = {99, -1, 99, -1, 99, -1, 99, -1}; + + // side of Rpcs connected to Gbtx link, i.e. 0 or 1 + const int32_t rpcSide[numCrob] = {0, 0, 0, 0, 0, 0, 0, 0}; + + std::vector<int32_t> fviRpcChUId = {}; // UID/address for each channel, build from type, side and module + + void BuildChannelsUidMap(); + void BuildChannelsUidMapT0(uint32_t& uCh, uint32_t uGbtx); + void BuildChannelsUidMapT0_2022(uint32_t& uCh, uint32_t uGbtx); + }; + +} /* namespace cbm::algo */ + +#endif //ALGO_DETECTORS_T0_T0READOUTCONFIG_H diff --git a/algo/detectors/t0/UnpackT0.cxx b/algo/detectors/t0/UnpackT0.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f8fc5a25d00a7b935c67f801f2add8e840308859 --- /dev/null +++ b/algo/detectors/t0/UnpackT0.cxx @@ -0,0 +1,156 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer] */ + +#include "UnpackT0.h" + +#include <cassert> +#include <sstream> +#include <utility> +#include <vector> + +#include <cmath> + +using std::unique_ptr; +using std::vector; + +namespace cbm::algo +{ + + // ---- Algorithm execution --------------------------------------------- + UnpackT0::resultType UnpackT0::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) + { + + // --- Output data + resultType result = {}; + + // --- Current Timeslice start time in epoch units. Note that it is always a multiple of epochs + // --- and the epoch is a multiple of ns. + fCurrentTsTime = static_cast<uint64_t>(tTimeslice / critof001::kuEpochInNs) % critof001::kulEpochCycleEp; + + // ---Â Number of messages in microslice + auto msSize = msDescr.size; + if (msSize % sizeof(critof001::Message) != 0) { + result.second.fNumErrInvalidMsSize++; + return result; + } + const uint32_t numMessages = msSize / sizeof(critof001::Message); + if (numMessages < 2) { + result.second.fNumErrInvalidMsSize++; + return result; + } + + // --- Interpret MS content as sequence of SMX messages + auto message = reinterpret_cast<const critof001::Message*>(msContent); + + // --- The first message in the MS is expected to be of type EPOCH. + if (message[0].getMessageType() != critof001::MSG_EPOCH) { + result.second.fNumErrInvalidFirstMessage++; + return result; + } + + { // --- Check that first epoch matches with the microslice index + const uint64_t msStartEpoch = + static_cast<uint64_t>(msDescr.idx / critof001::kuEpochInNs) % critof001::kulEpochCycleEp; + if (message[0].getGdpbEpEpochNb() != msStartEpoch) { + result.second.fNumErrInvalidStartEpoch++; + return result; + } + } + + // --- The last message in the MS is expected to be EndOfMs. + if (!message[numMessages - 1].isEndOfMs()) { + result.second.fNumErrInvalidLastMessage++; + return result; + } + //Check if last message is "EndOfMs"!! Maybe loop to messageNr < numMessages - 1 + + // --- Message loop + for (uint32_t messageNr = 0; messageNr < numMessages; messageNr++) { + + // --- Action depending on message type + switch (message[messageNr].getMessageType()) { + + case critof001::MSG_HIT: { + ProcessHitMessage(message[messageNr], result.first, result.second); + break; + } + case critof001::MSG_EPOCH: { + ProcessEpochMessage(message[messageNr]); + break; + } + case critof001::MSG_SLOWC: { + // Fill error + break; + } + case critof001::MSG_SYST: { + // Fill error + break; + } + default: { + result.second.fNumNonHitOrTsbMessage++; + break; + } + } //? Message type + } //# Messages + + return result; + } + // -------------------------------------------------------------------------- + + + // ----- Process hit message -------------------------------------------- + inline void UnpackT0::ProcessHitMessage(const critof001::Message& message, vector<CbmTofDigi>& digiVec, + UnpackT0MonitorData& monitor) const + { + // IGNORES: + // - Duplicate messages + // - (0 == uChanUId) + // - (fviRpcChUId.size() < uRemappedChannelNrInSys) + // - successive digis with same time + // (these are filtered in original version but not here) + // also: does not apply new "remap digis" hack, and always includes timeslice overlap + + // --- Check eLink and get parameters + const uint32_t elink = message.getGet4Idx(); + if (elink >= fParams.fElinkParams.size()) { + monitor.fNumErrElinkOutOfRange++; + return; + } + const UnpackT0ElinkPar& elinkPar = fParams.fElinkParams.at(elink); + + const uint32_t channel = message.getGdpbHitChanId(); + const uint32_t channelUId = (elinkPar.fChannelUId)[channel]; + + double messageTime = message.getMsgFullTimeD(fCurrentEpochInTs) - elinkPar.fTimeOffset; + const double charge = (double) message.getGdpbHit32Tot(); //cast from uint32_t + + /* + std::stringstream ss; + ss << " elink " << elink << " channel " << channel << " channelUId " << channelUId << " charge " << charge << " time " << message.getGdpbHitFullTs() << '\n'; + std::cout << ss.str(); + exit(0); +*/ + // --- Create output digi + digiVec.emplace_back(channelUId, messageTime, charge); + } + // -------------------------------------------------------------------------- + + + // ----- Process an epoch message --------------------------------------- + inline void UnpackT0::ProcessEpochMessage(const critof001::Message& message) + { + const uint64_t epoch = message.getGdpbEpEpochNb(); + + // --- Calculate epoch relative to timeslice start time; correct for epoch cycles + if (fCurrentTsTime <= epoch) { fCurrentEpochInTs = epoch - fCurrentTsTime; } + else { + fCurrentEpochInTs = epoch + critof001::kulEpochCycleEp - fCurrentTsTime; + } + //Problem if MS spans multiple epoch cycles? + } + // -------------------------------------------------------------------------- + + +} /* namespace cbm::algo */ diff --git a/algo/detectors/t0/UnpackT0.h b/algo/detectors/t0/UnpackT0.h new file mode 100644 index 0000000000000000000000000000000000000000..6ade5e58166b88637b7e57c5f15e224b965be49b --- /dev/null +++ b/algo/detectors/t0/UnpackT0.h @@ -0,0 +1,130 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer] */ + +#ifndef CBM_ALGO_UNPACKT0_H +#define CBM_ALGO_UNPACKT0_H 1 + + +#include "CbmTofDigi.h" + +#include "MicrosliceDescriptor.hpp" +#include "Timeslice.hpp" + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <vector> + +#include "CriGet4Mess001.h" + +namespace cbm::algo +{ + + + /** @struct UnpackT0ElinkPar + ** @author Volker Friese <v.friese@gsi.de> + ** @since 25 November 2021 + ** @brief T0 Unpacking parameters for one eLink / ASIC + **/ + struct UnpackT0ElinkPar { + std::vector<uint32_t> fChannelUId; ///< CbmT0Address for different channels + uint64_t fTimeOffset = 0.; ///< Time calibration parameter + }; + + + /** @struct UnpackT0Par + ** @author Volker Friese <v.friese@gsi.de> + ** @since 25 November 2021 + ** @brief Parameters required for the STS unpacking (specific to one component) + **/ + struct UnpackT0Par { + uint32_t fNumChansPerAsic = 0; ///< Number of channels per ASIC + uint32_t fNumAsicsPerModule = 0; ///< Number of ASICS per module + std::vector<UnpackT0ElinkPar> fElinkParams = {}; ///< Parameters for each eLink + }; + + + /** @struct UnpackT0Moni + ** @author Volker Friese <v.friese@gsi.de> + ** @since 2 December 2021 + ** @brief Monitoring data for STS unpacking + **/ + struct UnpackT0MonitorData { + uint32_t fNumNonHitOrTsbMessage = 0; + uint32_t fNumErrElinkOutOfRange = 0; ///< Elink not contained in parameters + uint32_t fNumErrInvalidFirstMessage = 0; ///< First message is not EPOCH + uint32_t fNumErrInvalidLastMessage = 0; ///< Last message is not EndOfMs + uint32_t fNumErrInvalidMsSize = 0; ///< Microslice size is not multiple of message size + uint32_t fNumErrTimestampOverflow = 0; ///< Overflow in 64 bit time stamp + uint32_t fNumErrInvalidStartEpoch = 0; ///< Microslice index doesn't match first epoch + bool HasErrors() + { + uint32_t numErrors = fNumNonHitOrTsbMessage + fNumErrElinkOutOfRange + fNumErrInvalidFirstMessage + + fNumErrInvalidMsSize + fNumErrTimestampOverflow; + return (numErrors > 0 ? true : false); + } + }; + + + /** @class UnpackT0 + ** @author Pierre-Alain Loizeau <p.-a.loizeau@gsi.de> + ** @author Volker Friese <v.friese@gsi.de> + ** @since 25 November 2021 + ** @brief Unpack algorithm for STS + **/ + class UnpackT0 { + + public: + typedef std::pair<std::vector<CbmTofDigi>, UnpackT0MonitorData> resultType; + + + /** @brief Default constructor **/ + UnpackT0() {}; + + + /** @brief Destructor **/ + ~UnpackT0() {}; + + + /** @brief Algorithm execution + ** @param msContent Microslice payload + ** @param msDescr Microslice descriptor + ** @param tTimeslice Unix start time of timeslice [ns] + ** @return STS digi data + **/ + resultType operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice); + + /** @brief Set the parameter container + ** @param params Pointer to parameter container + **/ + void SetParams(std::unique_ptr<UnpackT0Par> params) { fParams = *(std::move(params)); } + + + private: // methods + /** @brief Process a hit message + ** @param message SMX message (32-bit word) + ** @param digiVec Vector to append the created digi to + ** @param monitor Reference to monitor object + **/ + void ProcessHitMessage(const critof001::Message& message, std::vector<CbmTofDigi>& digiVec, + UnpackT0MonitorData& monitor) const; + + /** @brief Process an epoch message (TS_MSB) + ** @param message SMX message (32-bit word) + **/ + void ProcessEpochMessage(const critof001::Message& message); + + + private: // members + uint64_t fCurrentTsTime = 0; ///< Unix time of timeslice in units of epoch length + uint32_t fCurrentEpochInTs = 0; ///< Current epoch number relative to timeslice start epoch + UnpackT0Par fParams = {}; ///< Parameter container + }; + + +} /* namespace cbm::algo */ + +#endif /* CBM_ALGO_UNPACKT0_H */