diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 82c06d3b3b25a6fbb87fb0ac5f38c0e69e354adc..8aa73488fbdb581fe3c51c652246e443175391cf 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -26,6 +26,8 @@ set(SRCS detectors/bmon/UnpackBmon.cxx detectors/trd/TrdReadoutConfig.cxx detectors/trd/UnpackTrd.cxx + detectors/trd2d/Trd2dReadoutConfig.cxx + detectors/trd2d/UnpackTrd2d.cxx ) set(BUILD_INFO_CXX ${CMAKE_CURRENT_BINARY_DIR}/base/BuildInfo.cxx) @@ -59,6 +61,7 @@ target_include_directories(Algo ${CMAKE_CURRENT_SOURCE_DIR}/detectors/tof ${CMAKE_CURRENT_SOURCE_DIR}/detectors/bmon ${CMAKE_CURRENT_SOURCE_DIR}/detectors/trd + ${CMAKE_CURRENT_SOURCE_DIR}/detectors/trd2d ) target_link_libraries(Algo diff --git a/algo/detectors/trd2d/Trd2dReadoutConfig.cxx b/algo/detectors/trd2d/Trd2dReadoutConfig.cxx new file mode 100644 index 0000000000000000000000000000000000000000..115f4bc699c4116a6d6fabc502bb1752ce8c2768 --- /dev/null +++ b/algo/detectors/trd2d/Trd2dReadoutConfig.cxx @@ -0,0 +1,169 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Volker Friese [committer] */ + +#include "Trd2dReadoutConfig.h" + +//#include "CbmTrdAddress.h" + +#include <cassert> +#include <iomanip> +#include <iostream> +#include <iterator> + +using std::pair; +using std::setw; + +namespace cbm::algo +{ + + // --- Constructor ------------------------------------------------------------------ + Trd2dReadoutConfig::Trd2dReadoutConfig() {} + + // ------------------------------------------------------------------------------------ + + + // --- Destructor ----------------------------------------------------------------- + Trd2dReadoutConfig::~Trd2dReadoutConfig() {} + // ------------------------------------------------------------------------------------ + + + // --- Equipment IDs -------------------------------------------------------------- + std::vector<uint16_t> Trd2dReadoutConfig::GetEquipmentIds() + { + std::vector<uint16_t> result; + for (auto& entry : fReadoutMap) + result.push_back(entry.first); + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Number of Asics for a component / equipment ------------------------------- + size_t Trd2dReadoutConfig::GetNumAsics(uint16_t equipmentId) + { + size_t result = 0; + auto it = fChannelMap.find(equipmentId); + if (it != fChannelMap.end()) result = fChannelMap[equipmentId].size(); + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Number of Channels for a component / equipment, asic pair --------------------- + size_t Trd2dReadoutConfig::GetNumChans(uint16_t equipmentId, uint16_t asicId) + { + size_t result = 0; + auto it = fChannelMap.find(equipmentId); + if (it != fChannelMap.end()) { + if (asicId < fChannelMap[equipmentId].size()) { result = fChannelMap[equipmentId][asicId].size(); } + } + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Initialise the component mapping structure ---------------------------------- + void Trd2dReadoutConfig::InitComponentMap(const std::map<uint32_t, uint16_t[NCROBMOD]>& map) + { + // Receive map (moduleId, crobId) -> (equipId) + // Invert to obtain component map (equipId) -> (module iq, crob id) + for (auto& entry : map) { + uint16_t mod_id = entry.first; + for (uint8_t crob_id = 0; crob_id < NCROBMOD; crob_id++) { + uint16_t eq_id = entry.second[crob_id]; + fReadoutMap[eq_id] = std::make_pair(mod_id, crob_id); + } + } + } + + // --- Initialise the mapping structure -------------------------------------------- + void Trd2dReadoutConfig::InitChannelMap( + const std::map<size_t, std::map<size_t, std::map<size_t, std::tuple<int32_t, bool, uint64_t>>>>& channelMap) + { + // Constructing the map (equipId, asicId, chanId) -> (pad address, R pairing flag, daq offset) + for (auto compMap : channelMap) { + uint16_t equipmentId = compMap.first; + uint16_t numAsics = compMap.second.size(); + fChannelMap[equipmentId].resize(numAsics); + + for (auto asicMap : compMap.second) { + uint16_t asicId = asicMap.first; + uint16_t numChans = asicMap.second.size(); + fChannelMap[equipmentId][asicId].resize(numChans); + + for (auto chanMap : asicMap.second) { + uint16_t chanId = chanMap.first; + std::tuple<int32_t, bool, uint64_t> chanPars = chanMap.second; + fChannelMap[equipmentId][asicId][chanId] = chanPars; + } + } + } + } + // ------------------------------------------------------------------------------------ + + + // --- Mapping (equimentId, asicId, channel) -> (pad address, R pairing flag, daq offset) ----- + std::tuple<int32_t, bool, uint64_t> Trd2dReadoutConfig::ChanMap(uint16_t equipId, uint16_t asic, uint16_t chan) + { + std::tuple<int32_t, bool, uint64_t> result = std::make_tuple(-1, false, 0); + auto it = fChannelMap.find(equipId); + if (it != fChannelMap.end()) { + if (asic < fChannelMap[equipId].size()) { + if (chan < fChannelMap[equipId][asic].size()) { result = fChannelMap[equipId][asic][chan]; } + } + } + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Mapping (equimentId) -> (module id, crob id) --------------------------------- + std::pair<uint16_t, uint8_t> Trd2dReadoutConfig::CompMap(uint16_t equipId) + { + std::pair<uint16_t, uint8_t> result(0, 0); + auto equipIter = fReadoutMap.find(equipId); + if (equipIter != fReadoutMap.end()) { result = equipIter->second; } + return result; + } + // ------------------------------------------------------------------------------------ + + + // ----- Print readout map ------------------------------------------------ + std::string Trd2dReadoutConfig::PrintReadoutMap() + { + std::stringstream ss; + for (auto comp : fReadoutMap) { + uint16_t equipmentId = comp.first; + auto value = comp.second; + uint16_t moduleId = value.first; + uint16_t crobId = value.second; + ss << "Equipment " << equipmentId << " Module " << moduleId << " Crob " << crobId << "\n"; + } + ss << "\n"; + + for (auto asicMap : fChannelMap) { + uint16_t equipmentId = asicMap.first; + uint16_t numAsics = asicMap.second.size(); + ss << "\n Equipment " << equipmentId << " nAsics " << numAsics; + for (size_t asicId = 0; asicId < numAsics; asicId++) { + + uint16_t numChans = asicMap.second.at(asicId).size(); + ss << "\n Equipment " << equipmentId << " AsicId " << asicId << " nChans " << numChans; + for (size_t chanId = 0; chanId < numChans; chanId++) { + auto entry = asicMap.second.at(asicId).at(chanId); + int32_t address = std::get<0>(entry); + bool hasPairingR = std::get<1>(entry); + uint64_t daqOffset = std::get<2>(entry); + ss << "\n Equipment " << equipmentId << " AsicId " << asicId << " chanID " << chanId << " pad address " + << address << " pairingR " << hasPairingR << " daq offset " << daqOffset; + } + } + } + ss << "\n"; + return ss.str(); + } + // ---------------------------------------------------------------------------- + + +} /* namespace cbm::algo */ diff --git a/algo/detectors/trd2d/Trd2dReadoutConfig.h b/algo/detectors/trd2d/Trd2dReadoutConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..2737a0b7cc467f29f7eb3d2041dce4ad75dd9b1f --- /dev/null +++ b/algo/detectors/trd2d/Trd2dReadoutConfig.h @@ -0,0 +1,102 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Volker Friese, Dominik Smith [committer] */ + +#ifndef ALGO_DETECTORS_TRD2D_TRD2DREADOUTCONFIG_H +#define ALGO_DETECTORS_TRD2D_TRD2DREADOUTCONFIG_H + +#include <map> +#include <sstream> +#include <utility> +#include <vector> + +#include "UnpackTrd2d.h" + +namespace cbm::algo +{ + + + /** @class Trd2dReadoutConfig + ** @author Volker Friese <v.friese@gsi.de> + ** @since 3 March 2022 + ** @brief Provides the hardware-to-software address mapping for the CBM-TRD2D + ** + ** The hardware address as provided in the raw data stream is specified in terms of the + ** equipment identifier (specific to one FLES component) and the elink number with in + ** component. This is to be translated into the module address and the ASIC number within + ** the module. + ** The mapping of the two address spaces is hard-coded in this class. + **/ + + class Trd2dReadoutConfig { + + public: + /** @brief Constructor **/ + Trd2dReadoutConfig(); + + + /** @brief Destructor **/ + virtual ~Trd2dReadoutConfig(); + + + /** @brief Equipment in the configuration + ** @return Vector of equipment IDs + **/ + std::vector<uint16_t> GetEquipmentIds(); + + + /** @brief Number of ASICS of a component + ** @param Equipment ID + ** @return Number of ASICS + **/ + size_t GetNumAsics(uint16_t equipmentId); + + + /** @brief Number of channels of a component - ASIC pair + ** @param Equipment ID + ** @param ASIC ID + ** @return Number of channels + **/ + size_t GetNumChans(uint16_t equipmentId, uint16_t asicId); + + //// TO DO: Check uint sizes (for asic Id etc). + + /** @brief API: Mapping from component to pair (module id, crob id) + ** @param equipId Equipment identifier (component) + ** @return pair (module id, crob id) + */ + std::pair<uint16_t, uint8_t> CompMap(uint16_t equipId); + + + /** @brief API: Mapping from component, asic and channel to tuple (pad address, R pairing flag, daq offset) + ** @param equipId Equipment identifier (component) + ** @param asic ASIC number within component + ** @param channel Channel number within CROB + ** @return tuple (pad address, R pairing flag, daq offset) + */ + std::tuple<int32_t, bool, uint64_t> ChanMap(uint16_t equipId, uint16_t asic, uint16_t chan); + + + /** @brief Debug output of readout map **/ + std::string PrintReadoutMap(); + + /** @brief Initialisation of readout map **/ + void InitComponentMap(const std::map<uint32_t, uint16_t[NCROBMOD]>& crob_map); + + /** @brief Initialisation of channel map **/ + void InitChannelMap( + const std::map<size_t, std::map<size_t, std::map<size_t, std::tuple<int32_t, bool, uint64_t>>>>& channelMap); + + private: + // --- TRD2D readout map + // --- Map index: (equipment), map value: (module id, crob id) + std::map<uint16_t, std::pair<uint16_t, uint8_t>> fReadoutMap = {}; //! + + // --- TRD2D channel map + // --- Map index: (equipment, asic, chan), map value: (pad address, R pairing flag, daq offset) + std::map<uint16_t, std::vector<std::vector<std::tuple<int32_t, bool, uint64_t>>>> fChannelMap = {}; //! + }; + +} /* namespace cbm::algo */ + +#endif /* ALGO_DETECTORS_TRD2D_TRD2DREADOUTCONFIG_H_ */ diff --git a/algo/detectors/trd2d/UnpackTrd2d.cxx b/algo/detectors/trd2d/UnpackTrd2d.cxx new file mode 100644 index 0000000000000000000000000000000000000000..087f4a0b2cbbd3db48b424b75105aa0523ea2924 --- /dev/null +++ b/algo/detectors/trd2d/UnpackTrd2d.cxx @@ -0,0 +1,193 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Pascal Raisig, Dominik Smith [committer] */ + +#include "UnpackTrd2d.h" + +#include <Logger.h> + +#include <algorithm> +#include <cassert> +#include <vector> + +using std::unique_ptr; + +namespace cbm::algo +{ + // ---- Fasp message constructor ---------------------------------------- + CbmTrdFaspMessage::CbmTrdFaspMessage(uint8_t c, uint8_t typ, uint8_t t, uint16_t d, uint8_t rob, uint8_t asic) + : ch(c) + , type(typ) + , tlab(t) + , data(d) + , crob(rob) + , fasp(asic) + { + } + + // ---- Algorithm execution --------------------------------------------- + UnpackTrd2d::resultType UnpackTrd2d::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) + { + // --- Output data + resultType result = {}; + + // define time wrt start of time slice in TRD/FASP clks [80 MHz]. Contains: + // - relative offset of the MS wrt the TS + // - FASP epoch offset for current CROB + // - TRD2D system offset wrt to experiment time (e.g. T0) + uint64_t time = uint64_t((msDescr.idx - tTimeslice - fSystemTimeOffset) / 12.5); + + // Get parameters for current eq id. + const uint16_t mod_id = fParams.fModId; + const uint8_t crob_id = fParams.fCrobId; + + // Get the number of complete words in the input MS buffer. + const uint32_t nwords = msDescr.size / 4; + + // We have 32 bit spadic frames in this readout version + const uint32_t* wd = reinterpret_cast<const uint32_t*>(msContent); + + unsigned char lFaspOld(0xff); + std::vector<CbmTrdFaspMessage> vMess; + for (uint64_t j = 0; j < nwords; j++, wd++) { + uint32_t w = *wd; + uint8_t ch_id = w & 0xf; + uint8_t isaux = (w >> 4) & 0x1; + uint8_t slice = (w >> 5) & 0x7f; + uint16_t data = (w >> 12) & 0x3fff; + // uint8_t fasp_id = ((w >> 26) & 0x3f) + crob_id * NFASPCROB; + uint8_t fasp_id = ((w >> 26) & 0x3f); + + if (isaux) { + if (ch_id == 0) { + // clear buffer + if (vMess.size()) { pushDigis(vMess, time); } + vMess.clear(); + + lFaspOld = 0xff; + time += FASP_EPOCH_LENGTH; + } + continue; + } + //if (fFaspMap) fasp_id = ((*fFaspMap)[mod_id])[fasp_id]; + + if (lFaspOld != fasp_id) { + if (vMess.size()) { pushDigis(vMess, time); } + vMess.clear(); + lFaspOld = fasp_id; + } + if (data & 0x1) { + LOG(warn) << "UnpackTrd2d - Data corrupted : detect end bit set."; + continue; + } + if (data & 0x2000) { + LOG(debug) << "UnpackTrd2d - Self-triggered data."; + data &= 0x1fff; + } + vMess.emplace_back(ch_id, kData, slice, data >> 1, crob_id, lFaspOld); + } + result.first = FinalizeComponent(); + + return result; + } + + //_________________________________________________________________________________ + bool UnpackTrd2d::pushDigis(std::vector<CbmTrdFaspMessage> messes, const uint64_t time) + { + const uint16_t mod_id = fParams.fModId; + const UnpackTrd2dAsicPar& asicPar = fParams.fAsicParams[messes[0].fasp]; + const uint64_t tdaqOffset = asicPar.fChanParams[messes[0].ch].fDaqOffset; + + for (auto imess : messes) { + const int32_t pad = asicPar.fChanParams[imess.ch].fPadAddress; + const bool hasPairingR = asicPar.fChanParams[imess.ch].fHasPairingR; + const uint64_t lTime = time + tdaqOffset + imess.tlab; + const uint16_t lchR = hasPairingR ? imess.data : 0; + const uint16_t lchT = hasPairingR ? 0 : imess.data; + std::vector<CbmTrdDigi>& digiBuffer = fDigiBuffer[pad]; + + if (digiBuffer.size() == 0) { // init pad position in map and build digi for message + digiBuffer.emplace_back(pad, lchT, lchR, lTime); + digiBuffer.back().SetAddressModule(mod_id); + continue; + } + + // check if last digi has both R/T message components. Update if not and is within time window + auto id = digiBuffer.rbegin(); // Should always be valid here. + // No need to extra check + double r, t; + int32_t dt; + const int32_t dtime = (*id).GetTimeDAQ() - lTime; + bool use(false); + + if (abs(dtime) < 5) { // test message part of (last) digi + r = (*id).GetCharge(t, dt); + if (lchR && r < 0.1) { // set R charge on an empty slot + (*id).SetCharge(t, lchR, -dtime); + use = true; + } + else if (lchT && t < 0.1) { // set T charge on an empty slot + (*id).SetCharge(lchT, r, +dtime); + (*id).SetTimeDAQ(uint64_t((*id).GetTimeDAQ() - dtime)); + use = true; + } + } + + // build digi for message when update failed + if (!use) { + digiBuffer.emplace_back(pad, lchT, lchR, lTime); + digiBuffer.back().SetAddressModule(mod_id); + id = digiBuffer.rbegin(); + } + + // update charge for previously allocated digis to account for FASPRO ADC buffering and read-out feature + for (++id; id != digiBuffer.rend(); ++id) { + r = (*id).GetCharge(t, dt); + if (lchR && int(r)) { // update R charge and mark on digi + (*id).SetCharge(t, lchR, dt); + (*id).SetFlag(1); + break; + } + else if (lchT && int(t)) { // update T charge and mark on digi + (*id).SetCharge(lchT, r, dt); + (*id).SetFlag(0); + break; + } + } + } + messes.clear(); + + return true; + } + + std::vector<CbmTrdDigi> UnpackTrd2d::FinalizeComponent() + { + std::vector<CbmTrdDigi> outputDigis; + + for (uint16_t ipad(0); ipad < NFASPMOD * NFASPCH; ipad++) { + if (!fDigiBuffer[ipad].size()) continue; + uint nIncomplete(0); + for (auto id = fDigiBuffer[ipad].begin(); id != fDigiBuffer[ipad].end(); id++) { + double r, t; + int32_t dt; + r = (*id).GetCharge(t, dt); + // check if digi has all signals CORRECTED + if (((t > 0) != (*id).IsFlagged(0)) || ((r > 0) != (*id).IsFlagged(1))) { + nIncomplete++; + continue; + } + // reset flags as they were used only to mark the correctly setting of the charge/digi + (*id).SetFlag(0, false); + (*id).SetFlag(1, false); + outputDigis.emplace_back(std::move((*id))); + } + // clear digi buffer wrt the digi which was forwarded to higher structures + fDigiBuffer[ipad].clear(); + if (nIncomplete > 2) { + LOG(warn) << "FinalizeComponent() skip " << nIncomplete << " incomplete digi at pad " << ipad << ".\n"; + } + } + return outputDigis; + } +} /* namespace cbm::algo */ diff --git a/algo/detectors/trd2d/UnpackTrd2d.h b/algo/detectors/trd2d/UnpackTrd2d.h new file mode 100644 index 0000000000000000000000000000000000000000..c1f179cc2ec60831a3c9e2a46a53f7c7a6be9856 --- /dev/null +++ b/algo/detectors/trd2d/UnpackTrd2d.h @@ -0,0 +1,173 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Pascal Raisig, Dominik Smith [committer] */ + +#ifndef CBM_ALGO_UNPACKTRD2D_H +#define CBM_ALGO_UNPACKTRD2D_H 1 + + +#include "CbmTrdDigi.h" +#include "CbmTrdRawMessageSpadic.h" + +#include "MicrosliceDescriptor.hpp" +#include "Timeslice.hpp" + +#include <memory> + +#include <cmath> + +#define NFASPMOD 180 +#define NCROBMOD 5 +#define NFASPCROB NFASPMOD / NCROBMOD +#define NFASPCH 16 + +#define FASP_EPOCH_LENGTH 128 + +namespace cbm::algo +{ + + enum CbmTrdFaspMessageType + { + kEpoch = 0, + kData + }; + + /** @brief Data structure for unpacking the FASP word */ + struct CbmTrdFaspMessage { + CbmTrdFaspMessage(uint8_t c, uint8_t typ, uint8_t t, uint16_t d, uint8_t rob, uint8_t asic); + uint8_t ch = 0; ///< ch id in the FASP + uint8_t type = 0; ///< message type 0 = epoch, 1 = data (not used for the moment) + uint8_t tlab = 0; ///< time of the digi inside the epoch + uint16_t data = 0; ///< ADC value + uint32_t epoch = 0; ///< epoch id (not used for the moment) + uint32_t mod = 0; ///< full module address according to CbmTrdAddress + uint8_t crob = 0; ///< CROB id in the module + uint8_t fasp = 0; ///< FASP id in the module + }; + + /** @struct UnpackTrd2dChannelPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief TRD Unpacking parameters for one Asic channel + **/ + struct UnpackTrd2dChannelPar { + int32_t fPadAddress; ///< Pad address for channel + bool fHasPairingR; ///< Flag for R or T compoment + uint64_t fDaqOffset = 0; ///< Time calibration parameter + }; + + /** @struct UnpackTrd2dAsicPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief TRD Unpacking parameters for one Asic + **/ + struct UnpackTrd2dAsicPar { + std::vector<UnpackTrd2dChannelPar> fChanParams; ///< Parameters for different channels + }; + + /** @struct UnpackTrd2dPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief Parameters required for the TRD unpacking (specific to one component) + **/ + struct UnpackTrd2dPar { + uint16_t fModId = 0; ///< Module ID of component + uint8_t fCrobId = 0; ///< CROB ID of component + std::vector<UnpackTrd2dAsicPar> fAsicParams = {}; ///< Parameters for each ASIC + }; + + + /** @struct UnpackTrd2dMoni + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief Monitoring data for TRD unpacking + **/ + struct UnpackTrd2dMonitorData { + uint32_t fNumNonHitOrTsbMessage = 0; + uint32_t fNumErrElinkOutOfRange = 0; ///< Elink not contained in parameters + uint32_t fNumErrInvalidFirstMessage = 0; ///< First message is not TS_MSB or second is not EPOCH + uint32_t fNumErrInvalidMsSize = 0; ///< Microslice size is not multiple of message size + uint32_t fNumErrTimestampOverflow = 0; ///< Overflow in 64 bit time stampa + bool HasErrors() + { + uint32_t numErrors = fNumNonHitOrTsbMessage + fNumErrElinkOutOfRange + fNumErrInvalidFirstMessage + + fNumErrInvalidMsSize + fNumErrTimestampOverflow; + return (numErrors > 0 ? true : false); + } + }; + + /** @class UnpackTrd2d + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief Unpack algorithm for TRD + **/ + class UnpackTrd2d { + + public: + typedef std::pair<std::vector<CbmTrdDigi>, UnpackTrd2dMonitorData> resultType; + + + /** @brief Default constructor **/ + UnpackTrd2d() {}; + + + /** @brief Destructor **/ + ~UnpackTrd2d() {}; + + + /** @brief Algorithm execution + ** @param msContent Microslice payload + ** @param msDescr Microslice descriptor + ** @param tTimeslice Unix start time of timeslice [ns] + ** @return TRD 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<UnpackTrd2dPar> params) { fParams = *(std::move(params)); } + + private: // members + UnpackTrd2dPar fParams = {}; ///< Parameter container + + /** + ** @brief Time offset for the system + ** @todo This should be module and channel dependent and included into the asic parameters + **/ + std::int32_t fSystemTimeOffset = 0; + + bool pushDigis(std::vector<CbmTrdFaspMessage> messages, const uint64_t time); + + /** @brief Finalize component (e.g. copy from temp buffers) */ + std::vector<CbmTrdDigi> FinalizeComponent(); + + // Constants + /** @brief Bytes per FASP frame stored in the microslices (32 bits words) + * - DATA WORD - + * ffff.ffdd dddd.dddd dddd.tttt ttta.cccc + * f - FASP id + * d - ADC signal + * t - time label inside epoch + * a - word type (1) + * c - channel id + * - EPOCH WORD - + * ffff.fftt tttt.tttt tttt.tttt ttta.cccc + * f - FASP id + * t - epoch index + * a - word type (0) + * c - channel id + */ + static const std::uint8_t fBytesPerWord = 4; + + std::array<std::vector<CbmTrdDigi>, NFASPMOD* NFASPCH> fDigiBuffer = { + {}}; ///> Buffered digi for each pad in CROB component + + std::map<uint32_t, uint8_t[NFASPMOD]>* fFaspMap = nullptr; ///> FASP mapping update wrt the default setting + }; + + +} /* namespace cbm::algo */ + +#endif /* CBM_ALGO_UNPACKTRD2D_H */ diff --git a/core/data/base/CbmDigiData.h b/core/data/base/CbmDigiData.h index 835fc28493119dbe807edd2c8cc7a4e88c2abea2..d44185d8dedb4b3614f03f576e43dc5e31c1414e 100644 --- a/core/data/base/CbmDigiData.h +++ b/core/data/base/CbmDigiData.h @@ -36,6 +36,7 @@ public: CbmMuchDigiData fMuch; ///< MUCH data CbmRichDigiData fRich; ///< RICH data CbmTrdDigiData fTrd; ///< TRD data + CbmTrdDigiData fTrd2d; ///< TRD2D data CbmTofDigiData fTof; ///< TOF data CbmPsdDigiData fPsd; ///< PSD data @@ -49,6 +50,7 @@ public: ar& fSts; ar& fMuch; ar& fTrd; + ar& fTrd2d; ar& fTof; ar& fPsd; ar& fRich; @@ -66,11 +68,12 @@ public: fSts.Clear(); fMuch.Clear(); fTrd.Clear(); + fTrd2d.Clear(); fTof.Clear(); fPsd.Clear(); fRich.Clear(); } }; -BOOST_CLASS_VERSION(CbmDigiData, 3) +BOOST_CLASS_VERSION(CbmDigiData, 4) #endif /* CBMDIGIDATA_H */ diff --git a/reco/tasks/CbmTaskUnpack.cxx b/reco/tasks/CbmTaskUnpack.cxx index 7aafa5cbbc0d5815bd02b4a982e83c7965c8b4b4..f709797d6df7e415630beb9fcae56ad401cf2e62 100644 --- a/reco/tasks/CbmTaskUnpack.cxx +++ b/reco/tasks/CbmTaskUnpack.cxx @@ -11,7 +11,10 @@ #include "CbmDigiManager.h" #include "CbmDigiTimeslice.h" #include "CbmSourceTs.h" +#include "CbmTrdParFasp.h" +#include "CbmTrdParModDigi.h" #include "CbmTrdParSetAsic.h" +#include "CbmTrdParSetDigi.h" #include "CbmTrdParSpadic.h" #include "MicrosliceDescriptor.hpp" @@ -34,9 +37,6 @@ #include <sstream> #include <vector> -#include "UnpackSts.h" - - using namespace std; using cbm::algo::UnpackBmonElinkPar; using cbm::algo::UnpackBmonPar; @@ -46,10 +46,14 @@ using cbm::algo::UnpackStsElinkPar; using cbm::algo::UnpackStsPar; using cbm::algo::UnpackTofElinkPar; using cbm::algo::UnpackTofPar; +using cbm::algo::UnpackTrd2dAsicPar; +using cbm::algo::UnpackTrd2dChannelPar; +using cbm::algo::UnpackTrd2dPar; using cbm::algo::UnpackTrdCrobPar; using cbm::algo::UnpackTrdElinkPar; using cbm::algo::UnpackTrdPar; + // ----- Constructor ----------------------------------------------------- CbmTaskUnpack::CbmTaskUnpack() : FairTask("Unpack") {} // --------------------------------------------------------------------------- @@ -215,6 +219,26 @@ void CbmTaskUnpack::Exec(Option_t*) numCompUsed++; } // system TRD + // system TRD2D + if (systemId == fles::SubsystemIdentifier::TRD2D) { + const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id; + const auto algoIt = fAlgoTrd2d.find(equipmentId); + assert(algoIt != fAlgoTrd2d.end()); + + // The current algorithm works for the TRD2D data format version XXX used in 2022. + // Other versions are not yet supported. + // In the future, different data formats will be supported by instantiating different + // algorithms depending on the version. + //assert(timeslice->descriptor(comp, 0).sys_ver == XXX); To do: add something sensible here + + // --- Microslice loop + numMsInComp = + MsLoop(timeslice, algoIt->second, comp, &fTimeslice->fData.fTrd2d.fDigis, &numBytesInComp, &numDigisInComp); + + numCompUsed++; + } // system TRD2D + + compTimer.Stop(); LOG(debug) << GetName() << ": Component " << comp << ", microslices " << numMsInComp << " input size " << numBytesInComp << " bytes, " @@ -238,6 +262,8 @@ void CbmTaskUnpack::Exec(Option_t*) [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); std::sort(std::execution::par_unseq, fTimeslice->fData.fTrd.fDigis.begin(), fTimeslice->fData.fTrd.fDigis.end(), [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); + std::sort(std::execution::par_unseq, fTimeslice->fData.fTrd2d.fDigis.begin(), fTimeslice->fData.fTrd2d.fDigis.end(), + [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); #else std::sort(fTimeslice->fData.fSts.fDigis.begin(), fTimeslice->fData.fSts.fDigis.end(), [](CbmStsDigi digi1, CbmStsDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); @@ -249,6 +275,8 @@ void CbmTaskUnpack::Exec(Option_t*) [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); std::sort(fTimeslice->fData.fTrd.fDigis.begin(), fTimeslice->fData.fTrd.fDigis.end(), [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); + std::sort(fTimeslice->fData.fTrd2d.fDigis.begin(), fTimeslice->fData.fTrd2d.fDigis.end(), + [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); #endif // --- Timeslice log @@ -394,12 +422,12 @@ InitStatus CbmTaskUnpack::Init() std::unique_ptr<UnpackTrdPar> par(new UnpackTrdPar()); const size_t numCrobs = fTrdConfig.GetNumCrobs(equip); - for (size_t crob = 0; crob < numCrobs; crob++) { + for (size_t crob = 0; crob < numCrobs; crob++) { UnpackTrdCrobPar crobPar; const size_t numElinks = fTrdConfig.GetNumElinks(equip, crob); - for (size_t elink = 0; elink < numElinks; elink++) { + for (size_t elink = 0; elink < numElinks; elink++) { UnpackTrdElinkPar elinkPar; auto addresses = fTrdConfig.Map(equip, crob, elink); elinkPar.fAddress = addresses.first; // Asic address for this elink @@ -412,6 +440,34 @@ InitStatus CbmTaskUnpack::Init() LOG(info) << "--- Configured equipment " << equip << " with " << numCrobs << " crobs"; } + InitTrd2dReadoutConfig(); + auto equipIdsTrd2d = fTrd2dConfig.GetEquipmentIds(); + for (auto& equip : equipIdsTrd2d) { + + std::unique_ptr<UnpackTrd2dPar> par(new UnpackTrd2dPar()); + const size_t numAsics = fTrd2dConfig.GetNumAsics(equip); + + for (size_t asic = 0; asic < numAsics; asic++) { + UnpackTrd2dAsicPar asicPar; + const size_t numChans = fTrd2dConfig.GetNumChans(equip, asic); + + for (size_t chan = 0; chan < numChans; chan++) { + UnpackTrd2dChannelPar chanPar; + auto pars = fTrd2dConfig.ChanMap(equip, asic, chan); + chanPar.fPadAddress = std::get<0>(pars); // Pad address for channel + chanPar.fHasPairingR = std::get<1>(pars); // Flag for R or T compoment + chanPar.fDaqOffset = std::get<2>(pars); // Time calibration parameter + asicPar.fChanParams.push_back(chanPar); + } + auto comppars = fTrd2dConfig.CompMap(equip); + par->fModId = comppars.first; + par->fCrobId = comppars.second; + par->fAsicParams.push_back(asicPar); + } + fAlgoTrd2d[equip].SetParams(std::move(par)); + LOG(info) << "--- Configured equipment " << equip << " with " << numAsics << " asics"; + } + LOG(info) << "--- Configured " << fAlgoSts.size() << " unpacker algorithms for STS."; LOG(debug) << "Readout map:" << fStsConfig.PrintReadoutMap(); LOG(info) << "--- Configured " << fAlgoMuch.size() << " unpacker algorithms for MUCH."; @@ -424,6 +480,73 @@ InitStatus CbmTaskUnpack::Init() } // ---------------------------------------------------------------------------- +// ----- Initialisation --------------------------------------------------- +void CbmTaskUnpack::InitTrd2dReadoutConfig() +{ + // Initialize input files + FairParAsciiFileIo asciiInput; + std::string digiparfile = Form("%s/parameters/trd/trd_v22h_mcbm.digi.par", std::getenv("VMCWORKDIR")); + std::string asicparfile = Form("%s/parameters/trd/trd_v22h_mcbm.asic.par", std::getenv("VMCWORKDIR")); + + // Read the .digi file and store result + CbmTrdParSetDigi digiparset; + if (asciiInput.open(digiparfile.data())) { digiparset.init(&asciiInput); } + asciiInput.close(); + + // Read the .asic file and store result + CbmTrdParSetAsic asicparset; + if (asciiInput.open(asicparfile.data())) { asicparset.init(&asciiInput); } + asciiInput.close(); + + // Initialize map (moduleId, crobId) -> (equipId) explicitly + std::map<uint32_t, uint16_t[NCROBMOD]> crob_map; + uint16_t cmap[] = {0xffc2, 0xffc5, 0xffc1, 0, 0}; // "crob map 22" for run Id >= 2335 + memcpy(crob_map[5], cmap, NCROBMOD * sizeof(uint16_t)); // only module Id 5 is used! + + // Then pass to Trd2dReadoutConfig, will invert to obain map (equipId) -> (module iq, crob id) + fTrd2dConfig.InitComponentMap(crob_map); + + // Map (equipId, asicId, chanId) -> (pad address, R pairing flag, daq offset) + std::map<size_t, std::map<size_t, std::map<size_t, std::tuple<int32_t, bool, uint64_t>>>> channelMap; + + // Loop through a list of module IDs from the .digi file (can in principle contradict crob_map). + for (auto entry : digiparset.GetModuleMap()) { + + const auto moduleId = entry.first; + if (crob_map.find(moduleId) == crob_map.end()) { continue; } //skip if no entry in crob_map + + // Get ASIC parameters for this module + const CbmTrdParSetAsic* setDet = static_cast<const CbmTrdParSetAsic*>(asicparset.GetModuleSet(moduleId)); + if (!setDet) continue; + if (setDet->GetAsicType() != int32_t(CbmTrdDigi::eCbmTrdAsicType::kFASP)) continue; + + // Loop through ASICs for this module + std::vector<int32_t> addresses; + setDet->GetAsicAddresses(&addresses); + for (auto add : addresses) { + + //Get local IDs for this component / equipment. + const int32_t fasp_in_eq = ((int) add - 1000 * (int) moduleId) % (NFASPCROB); + const int32_t crob_in_mod = ((int) add - 1000 * (int) moduleId) / (NFASPCROB); + const uint16_t eq_id = crob_map[moduleId][crob_in_mod]; + + // ASIC parameter set + CbmTrdParFasp* fasppar = (CbmTrdParFasp*) setDet->GetModulePar(add); + + // Loop through channels for this ASIC and fill map + for (size_t chan = 0; chan < fasppar->GetNchannels(); chan++) { + const int32_t pad = fasppar->GetPadAddress(chan); + const bool hasPairingR = fasppar->GetChannel(chan)->HasPairingR(); + const CbmTrdParModDigi* modpar = (CbmTrdParModDigi*) digiparset.GetModulePar(moduleId); + uint64_t daq_offset = 0; + if (modpar->GetPadRow(fasppar->GetPadAddress(chan)) % 2 == 0) daq_offset = 3; + channelMap[eq_id][fasp_in_eq][chan] = std::make_tuple(pad, hasPairingR, daq_offset); + } + } + } + fTrd2dConfig.InitChannelMap(channelMap); +} + // ----- Initialisation --------------------------------------------------- void CbmTaskUnpack::InitTrdReadoutConfig() { diff --git a/reco/tasks/CbmTaskUnpack.h b/reco/tasks/CbmTaskUnpack.h index 9fd0d0a332b5e2a51814e484659bb57c3ed513ea..70c3fa8999cade09b2b4c98dcb82cbbb14276309 100644 --- a/reco/tasks/CbmTaskUnpack.h +++ b/reco/tasks/CbmTaskUnpack.h @@ -12,6 +12,7 @@ #include <FairTask.h> #include "TofReadoutConfig.h" +#include "Trd2dReadoutConfig.h" #include "TrdReadoutConfig.h" #include <sstream> @@ -26,6 +27,7 @@ #include "UnpackSts.h" #include "UnpackTof.h" #include "UnpackTrd.h" +#include "UnpackTrd2d.h" class CbmDigiManager; class CbmSourceTs; @@ -77,6 +79,9 @@ private: // methods /** @brief Initialisation of address maps for Trd **/ virtual void InitTrdReadoutConfig(); + /** @brief Initialisation of address maps for Trd2d **/ + virtual void InitTrd2dReadoutConfig(); + private: // members CbmSourceTs* fSource = nullptr; @@ -95,6 +100,9 @@ private: // members std::map<uint16_t, cbm::algo::UnpackTrd> fAlgoTrd = {}; cbm::algo::TrdReadoutConfig fTrdConfig {}; + std::map<uint16_t, cbm::algo::UnpackTrd2d> fAlgoTrd2d = {}; + cbm::algo::Trd2dReadoutConfig fTrd2dConfig {}; + size_t fNumTs = 0; size_t fNumMs = 0; size_t fNumBytes = 0;