From 486529b2fd655b31fc8204a8cae001e832dd6ba8 Mon Sep 17 00:00:00 2001 From: Dominik Smith <d.smith@gsi.de> Date: Tue, 9 May 2023 12:30:31 +0000 Subject: [PATCH] New TRD Unpacker in cbm::algo. --- algo/CMakeLists.txt | 5 +- algo/data/CMakeLists.txt | 1 + algo/detectors/trd/TrdReadoutConfig.cxx | 168 ++++++++ algo/detectors/trd/TrdReadoutConfig.h | 90 ++++ algo/detectors/trd/UnpackTrd.cxx | 516 +++++++++++++++++++++++ algo/detectors/trd/UnpackTrd.h | 284 +++++++++++++ core/data/trd/CbmTrdRawMessageSpadic.cxx | 2 - core/data/trd/CbmTrdRawMessageSpadic.h | 9 +- reco/tasks/CMakeLists.txt | 1 + reco/tasks/CbmTaskUnpack.cxx | 110 +++++ reco/tasks/CbmTaskUnpack.h | 7 + 11 files changed, 1183 insertions(+), 10 deletions(-) create mode 100644 algo/detectors/trd/TrdReadoutConfig.cxx create mode 100644 algo/detectors/trd/TrdReadoutConfig.h create mode 100644 algo/detectors/trd/UnpackTrd.cxx create mode 100644 algo/detectors/trd/UnpackTrd.h diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index eedb1a9eba..f4d65d357b 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -15,7 +15,9 @@ set(SRCS detectors/tof/UnpackTof.cxx detectors/bmon/BmonReadoutConfig.cxx detectors/bmon/UnpackBmon.cxx - ) + detectors/trd/TrdReadoutConfig.cxx + detectors/trd/UnpackTrd.cxx +) add_library(Algo SHARED ${SRCS}) @@ -28,6 +30,7 @@ target_include_directories(Algo ${CMAKE_CURRENT_SOURCE_DIR}/detectors/much ${CMAKE_CURRENT_SOURCE_DIR}/detectors/tof ${CMAKE_CURRENT_SOURCE_DIR}/detectors/bmon + ${CMAKE_CURRENT_SOURCE_DIR}/detectors/trd ) target_link_libraries(Algo PUBLIC OnlineData ROOT::GenVector INTERFACE FairLogger::FairLogger external::fles_ipc) diff --git a/algo/data/CMakeLists.txt b/algo/data/CMakeLists.txt index f2b9167a41..62acd74b15 100644 --- a/algo/data/CMakeLists.txt +++ b/algo/data/CMakeLists.txt @@ -17,6 +17,7 @@ set(SRCS ${CMAKE_SOURCE_DIR}/core/data/much/CbmMuchAddress.cxx ${CMAKE_SOURCE_DIR}/core/data/trd/CbmTrdDigi.cxx + ${CMAKE_SOURCE_DIR}/core/data/trd/CbmTrdRawMessageSpadic.cxx ${CMAKE_SOURCE_DIR}/core/data/tof/CbmTofDigi.cxx ${CMAKE_SOURCE_DIR}/core/data/tof/CbmTofAddress.cxx diff --git a/algo/detectors/trd/TrdReadoutConfig.cxx b/algo/detectors/trd/TrdReadoutConfig.cxx new file mode 100644 index 0000000000..f3d54987e8 --- /dev/null +++ b/algo/detectors/trd/TrdReadoutConfig.cxx @@ -0,0 +1,168 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Volker Friese [committer] */ + +#include "TrdReadoutConfig.h" + +//#include "CbmTrdAddress.h" + +#include <cassert> +#include <iomanip> +#include <iostream> +#include <iterator> + +using std::pair; +using std::setw; + +namespace cbm::algo +{ + + // --- Constructor ------------------------------------------------------------------ + TrdReadoutConfig::TrdReadoutConfig() {} + + // ------------------------------------------------------------------------------------ + + + // --- Destructor ----------------------------------------------------------------- + TrdReadoutConfig::~TrdReadoutConfig() {} + // ------------------------------------------------------------------------------------ + + + // --- Equipment IDs -------------------------------------------------------------- + std::vector<uint16_t> TrdReadoutConfig::GetEquipmentIds() + { + std::vector<uint16_t> result; + for (auto& entry : fReadoutMap) + result.push_back(entry.first); + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Number of Crobs for a component / equipment ------------------------------- + size_t TrdReadoutConfig::GetNumCrobs(uint16_t equipmentId) + { + size_t result = 0; + auto it = fReadoutMap.find(equipmentId); + if (it != fReadoutMap.end()) result = fReadoutMap[equipmentId].size(); + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Number of Elinks for a component / equipment, crob pair --------------------- + size_t TrdReadoutConfig::GetNumElinks(uint16_t equipmentId, uint16_t crobId) + { + size_t result = 0; + if (crobId < GetNumCrobs(equipmentId)) result = fReadoutMap[equipmentId][crobId].size(); + return result; + } + // ------------------------------------------------------------------------------------ + + + // --- Initialise the mapping structure -------------------------------------------- + void + TrdReadoutConfig::Init(const std::map<size_t, std::map<size_t, std::map<size_t, size_t>>>& addressMap, + std::map<size_t, std::map<size_t, std::map<size_t, std::map<size_t, size_t>>>>& channelMap) + { + // Constructing the map (equipmentId, crobId, eLink) -> (ASIC address) + for (auto compMap : addressMap) { + uint16_t equipmentId = compMap.first; + uint16_t numCrobs = compMap.second.size(); + fReadoutMap[equipmentId].resize(numCrobs); + + for (auto crobMap : compMap.second) { + uint16_t crobId = crobMap.first; + uint16_t numElinks = crobMap.second.size(); + fReadoutMap[equipmentId][crobId].resize(numElinks); + + for (auto elinkMap : crobMap.second) { + uint16_t elinkId = elinkMap.first; + uint16_t address = elinkMap.second; + fReadoutMap[equipmentId][crobId][elinkId] = address; + } + } + } + + // Constructing the map (equipmentId, crobId, eLink, chan) -> (chan address) + for (auto compMap : channelMap) { + uint16_t equipmentId = compMap.first; + uint16_t numCrobs = compMap.second.size(); + fChannelMap[equipmentId].resize(numCrobs); + + for (auto crobMap : compMap.second) { + uint16_t crobId = crobMap.first; + uint16_t numElinks = crobMap.second.size(); + fChannelMap[equipmentId][crobId].resize(numElinks); + + for (auto elinkMap : crobMap.second) { + uint16_t elinkId = elinkMap.first; + uint16_t numChans = elinkMap.second.size(); + fChannelMap[equipmentId][crobId][elinkId].resize(numChans); + + for (auto chanMap : elinkMap.second) { + uint16_t chanId = chanMap.first; + uint32_t address = chanMap.second; + fChannelMap[equipmentId][crobId][elinkId][chanId] = address; + } + } + } + } + } + // ------------------------------------------------------------------------------------ + + + // --- Mapping (equimentId, crobId, elink) -> (ASIC address, channel addresses) ----- + std::pair<int32_t, std::vector<uint32_t>> TrdReadoutConfig::Map(uint16_t equipmentId, uint16_t crobId, + uint16_t elinkId) + { + std::pair<int32_t, std::vector<uint32_t>> result; + result.first = -1; + auto it = fChannelMap.find(equipmentId); + if (it != fChannelMap.end()) { + if (crobId < fChannelMap[equipmentId].size()) { + if (elinkId < fChannelMap[equipmentId][crobId].size()) { + result.first = fReadoutMap[equipmentId][crobId][elinkId]; + result.second = fChannelMap[equipmentId][crobId][elinkId]; + } + } + } + return result; + } + // ------------------------------------------------------------------------------------ + + + // ----- Print readout map ------------------------------------------------ + std::string TrdReadoutConfig::PrintReadoutMap() + { + std::stringstream ss; + for (auto compMap : fReadoutMap) { + + uint16_t equipmentId = compMap.first; + uint16_t numCrobs = compMap.second.size(); + ss << "\n Equipment " << equipmentId << " nCrobs " << numCrobs; + for (size_t crobId = 0; crobId < numCrobs; crobId++) { + + uint16_t numElinks = compMap.second.at(crobId).size(); + ss << "\n Equipment " << equipmentId << " CrobId " << crobId << " nElinks " << numElinks; + for (size_t elinkId = 0; elinkId < numElinks; elinkId++) { + + uint16_t address = compMap.second.at(crobId).at(elinkId); + ss << "\n Equipment " << equipmentId << " CrobId " << crobId << " elinkID " << elinkId << " address " + << address; + + //Now output channel addresses + ss << "\n Channels "; + auto vec = fChannelMap[equipmentId][crobId][elinkId]; + std::copy(vec.begin(), vec.end() - 1, std::ostream_iterator<uint32_t>(ss, " ")); + ss << vec.back(); + } + } + } + ss << "\n"; + return ss.str(); + } + // ---------------------------------------------------------------------------- + + +} /* namespace cbm::algo */ diff --git a/algo/detectors/trd/TrdReadoutConfig.h b/algo/detectors/trd/TrdReadoutConfig.h new file mode 100644 index 0000000000..62d6ec558b --- /dev/null +++ b/algo/detectors/trd/TrdReadoutConfig.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Volker Friese [committer] */ + +#ifndef ALGO_DETECTORS_TRD_TRDREADOUTCONFIG_H +#define ALGO_DETECTORS_TRD_TRDREADOUTCONFIG_H + +#include <map> +#include <sstream> +#include <utility> +#include <vector> + +namespace cbm::algo +{ + + + /** @class TrdReadoutConfig + ** @author Volker Friese <v.friese@gsi.de> + ** @since 3 March 2022 + ** @brief Provides the hardware-to-software address mapping for the CBM-TRD + ** + ** 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 TrdReadoutConfig { + + public: + /** @brief Constructor **/ + TrdReadoutConfig(); + + + /** @brief Destructor **/ + virtual ~TrdReadoutConfig(); + + + /** @brief Equipment in the configuration + ** @return Vector of equipment IDs + **/ + std::vector<uint16_t> GetEquipmentIds(); + + + /** @brief Number of CROBS of a component + ** @param Equipment ID + ** @return Number of CROBS + **/ + size_t GetNumCrobs(uint16_t equipmentId); + + + /** @brief Number of elinks of a component - CROB pair + ** @param Equipment ID + ** @param CROB ID + ** @return Number of elinks + **/ + size_t GetNumElinks(uint16_t equipmentId, uint16_t crobId); + + + /** @brief API: Mapping from component, crob and elink to pair (ASIC address, channel addresses) + ** @param equipId Equipment identifier (component) + ** @param crob CROB number within component + ** @param elink Elink number within CROB + ** @return pair (ASIC address, channel addresses ) + */ + std::pair<int32_t, std::vector<uint32_t>> Map(uint16_t equipId, uint16_t crob, uint16_t elink); + + + /** @brief Debug output of readout map **/ + std::string PrintReadoutMap(); + + + /** @brief Initialisation of readout map **/ + void Init(const std::map<size_t, std::map<size_t, std::map<size_t, size_t>>>& addressMap, + std::map<size_t, std::map<size_t, std::map<size_t, std::map<size_t, size_t>>>>& channelMap); + + private: + // --- TRD readout map + // --- Map index: (equipment, crob, elink), map value: (ASIC address) + std::map<uint16_t, std::vector<std::vector<uint16_t>>> fReadoutMap = {}; //! + + // --- TRD channel map + // --- Map index: (equipment, crob, elink, chan), map value: (channel address) + std::map<uint16_t, std::vector<std::vector<std::vector<uint32_t>>>> fChannelMap = {}; //! + }; + +} /* namespace cbm::algo */ + +#endif /* ALGO_DETECTORS_TRD_TRDREADOUTCONFIG_H_ */ diff --git a/algo/detectors/trd/UnpackTrd.cxx b/algo/detectors/trd/UnpackTrd.cxx new file mode 100644 index 0000000000..854cb8e202 --- /dev/null +++ b/algo/detectors/trd/UnpackTrd.cxx @@ -0,0 +1,516 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Pascal Raisig, Dominik Smith [committer] */ + +#include "UnpackTrd.h" + +#include <algorithm> +#include <cassert> +#include <vector> + +using std::unique_ptr; + +namespace cbm::algo +{ + + // ---- Algorithm execution --------------------------------------------- + UnpackTrd::resultType UnpackTrd::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) + { + // --- Output data + resultType result = {}; + + // Get the µSlice starttime relative to the timeslice starttime (constant is clock length of Spadic in ns) + fMsStartTimeRelCC = (msDescr.idx - tTimeslice) / fAsicClockCycle; + + // We only want to count on TS_MSB per Stream per TS_MSB package (each eLink sends its own TS_MSB frame) + // so we store the current TS_MSB and compare it to the incoming. + std::int8_t currTsMsb = 0; + + // Reset the TS_MSB counter for the new µSlice we unpack + fNrTsMsbVec.clear(); + fNrTsMsbVec.resize(fStreamsPerWord); + + // Get the µslice size in bytes to calculate the number of completed words + auto mssize = msDescr.size; + + // Get the hardware ids from which the current µSlice is coming + std::uint8_t crobId = 0; + auto criId = msDescr.eq_id; + + // Digest the flags from the µSlice + digestMsFlags(msDescr.flags, result.second); + + // Get the number of complete words in the input MS buffer. + std::uint32_t nwords = mssize / fBytesPerWord; + + // We have 32 bit spadic frames in this readout version + const auto mscontent = reinterpret_cast<const size_t*>(msContent); + + // Loop over all 64bit-Spadic-Words in the current µslice + for (std::uint32_t istream = 0; istream < fStreamsPerWord; istream++) { + currTsMsb = -1; + for (std::uint32_t iword = 0; iword < nwords; ++iword) { + // Access the actual word from the pointer + size_t word = static_cast<size_t>(mscontent[iword]); + + // Access the actual frame[iframe] from the word. (see fStreamsPerWord) + std::uint32_t frame = (word >> (32 * istream)) & 0xffffffff; + + // Get the type of the frame + auto kWordtype = getMessageType(frame); + + // In case we saw any other word than an EPO(TS_MSB) reset the flag, + // such that we again increase by one if an EPO frame arrives + auto elinkId = (frame >> 24) & 0x3f; + + switch (kWordtype) { + case Spadic::MsMessageType::kEPO: { + auto tsmsb = getTsMsb(frame, result.second); + if (((tsmsb - currTsMsb) & 0x3f) == 1 || currTsMsb == -1) fNrTsMsbVec.at(istream)++; + currTsMsb = tsmsb; + result.second.fNumEpochMsgs++; + break; + // FIXME in the kEPO msg we also have further flags that should be extracted + } + case Spadic::MsMessageType::kSOM: { + // Create the raw message and fill it with all information we can get from the SOM msg + CbmTrdRawMessageSpadic raw = makeRaw(frame, criId, crobId, elinkId, istream); + + // FIXME since we can not deduce the sample position from the messages we need in + // future some parameter handling here to place the samples at the correct position + // 6 adc bits are stored in the som message + size_t nadcbits = 6; + size_t nadcbitstotal = 6; + // Get the first bits from the adc signal + size_t adcbuffer = frame & 0x3f; + size_t isample = 0; + size_t irda = 0; + + // Now lets check if we have rda words following our som + iword++; + word = static_cast<size_t>(mscontent[iword]); + frame = (word >> (32 * istream)) & 0xffffffff; + + // The maximum amount of samples (32) equals to 12 RDA messages + while (getMessageType(frame) == Spadic::MsMessageType::kRDA && irda < 12) { + // We have to count the number of rda frames for sample reconstruction in eom + irda++; + + // Ensure that we are on the correct eLink + elinkId = (frame >> 24) & 0x3f; + if (elinkId != raw.GetElinkId()) { result.second.fNumElinkMis++; } + + // We have 22 adc bits per RDA word lets add them to the buffer... + adcbuffer <<= 22; + adcbuffer |= static_cast<size_t>((frame & 0x3fffff)); + // and increase the adcbit counter by 22 bits + nadcbits += 22; + nadcbitstotal += 22; + // If we have 9 or more samples stored we can extract n samples + while (nadcbits >= 9) { + raw.IncNrSamples(); + // In case the avg baseline feature was used we need to take special care of sample 0 + if (isample == 0 && fParams.fUseBaselineAvg) + raw.SetSample(extractAvgSample(&adcbuffer, &nadcbits), isample); + else + raw.SetSample(extractSample(&adcbuffer, &nadcbits), isample); + isample++; + } + iword++; + word = static_cast<size_t>(mscontent[iword]); + frame = (word >> (32 * istream)) & 0xffffffff; + } + + if (getMessageType(frame) == Spadic::MsMessageType::kEOM) { + // Ensure that we are on the correct eLink + elinkId = (frame >> 24) & 0x3f; + if (elinkId != raw.GetElinkId()) { result.second.fNumElinkMis++; } + + // Number of samples indicator = nsamples % 4 + std::uint8_t nsamplesindicator = (frame >> 18) & 0x3; + // Number of required samples as indicated + std::uint64_t nreqsamples = (nadcbitstotal + 18) / 9; + std::uint8_t nn = nreqsamples % 4; + for (std::uint8_t itest = 0; itest < 3; itest++) { + if (nn == nsamplesindicator || nreqsamples == 0) break; + nreqsamples--; + nn = nreqsamples % 4; + } + + // There is a chance that the nsamplesindicator bits are corrupted, + // here we check that we do not try to extract more adcbits than actually are streamed + if (nreqsamples >= isample) { + // Now extract from the above values the number of required adc bits from the eom + std::int8_t nrequiredbits = (nreqsamples - isample) * 9 - nadcbits; + adcbuffer <<= nrequiredbits; + + // The eom carries at maximum 18 adcbits + adcbuffer |= static_cast<size_t>((frame & 0x3ffff) >> (18 - nrequiredbits)); + nadcbits += nrequiredbits; + + while (nadcbits >= 9) { + raw.IncNrSamples(); + raw.SetSample(extractSample(&adcbuffer, &nadcbits), isample); + isample++; + } + } + else { + result.second.fNumCorruptEom++; + } + result.second.fNumCreatedRawMsgs++; + + // the message is done and the raw message container should contain everything we need. + // So now we can call makeDigi(). Nevertheless there is a chance for a corrupted message, + // which ends up with 0 samples so we have to check for it. + if (isample > 0) result.first.push_back(makeDigi(raw)); + } + else { + // We move the word counter backwards by one, such that the unexpected message can correctly be digested + iword--; + result.second.fNumMissingEom++; + } + break; + } + case Spadic::MsMessageType::kRDA: { + result.second.fNumWildRda++; + break; + } + case Spadic::MsMessageType::kEOM: { + result.second.fNumWildEom++; + break; + } + case Spadic::MsMessageType::kINF: { + result.second.fNumCreatedInfoMsgs++; + digestInfoMsg(frame); + break; + } + case Spadic::MsMessageType::kNUL: { + // last word in Microslice is 0. + if (iword != (nwords - 1) || (istream != (fStreamsPerWord - 1))) { result.second.fNumWildNul++; } + break; + } + case Spadic::MsMessageType::kUNK: { + result.second.fNumUnknownWords++; + return result; + break; + } + default: + // We have varying msg types for different versions of the message format. + // Hence, to not produce compiler warnings we have a "default break;" here. + break; + } + } + } + return result; + } + // -------------------------------------------------------------------------- + + // ---- digestBufInfoFlags ---- + Spadic::MsInfoType UnpackTrd::digestBufInfoFlags(const std::uint32_t frame) + { + auto flag = (frame >> 15) & 0x3; + Spadic::MsInfoType infotype; + if (flag == 1) infotype = Spadic::MsInfoType::kChannelBuf; + if (flag == 2) infotype = Spadic::MsInfoType::kOrdFifoBuf; + if (flag == 3) infotype = Spadic::MsInfoType::kChannelBufM; + return infotype; + } + + // ---- digestInfoMsg ---- + void UnpackTrd::digestInfoMsg(const std::uint32_t frame) + { + /// Save info message if needed. + //if (fOptOutBVec) { fOptOutBVec->emplace_back(std::make_pair(fLastFulltime, frame)); } + Spadic::MsInfoType infotype = getInfoType(frame); + // "Spadic_Info_Types"; + } + + // ---- digestInfoMsg ---- + void UnpackTrd::digestMsFlags(const std::uint16_t flags, UnpackTrdMonitorData& monitor) + { + if (flags & static_cast<std::uint16_t>(fles::MicrosliceFlags::CrcValid)) { monitor.fNumCrcValidFlags++; } + if (flags & static_cast<std::uint16_t>(fles::MicrosliceFlags::OverflowFlim)) { monitor.fNumOverflowFlimFlags++; } + if (flags & static_cast<std::uint16_t>(fles::MicrosliceFlags::OverflowUser)) { monitor.fNumOverflowUserFlags++; } + if (flags & static_cast<std::uint16_t>(fles::MicrosliceFlags::DataError)) { monitor.fNumDataErrorFlags++; } + } + + // ---- extractSample ---- + std::float_t UnpackTrd::extractAvgSample(size_t* adcbuffer, size_t* nadcbits) + { + // can not extract samples from a buffer with less than 9 bits + assert(*nadcbits >= 9); + *nadcbits -= 9; + + // The decoding of the average sample is kind of interesting: + // We get 9 bits in total iiiiiiiff. The 7 "i" bits refer to std integer bits, hence, + // covering a range of 0..128. + // The 2 "f" bits refer to values after a fix point refering to [0,0.25,0.5,0.75]. + // The sign we have to assume to be negative (bit-9 = 1) and also bit 8 of our std + // interger range we have to assume to be 0, such that the returned number is in + // between -256..-128. + + // Activate the 7 "i" bits + std::int16_t sample = 0x07f; + + // Write the content of the 7 "i" bits to temp + sample &= (*adcbuffer >> (*nadcbits + 2)); + + // Switch on the negative sign + sample |= 0xff00; + + return sample; + } + + // ---- extractSample ---- + std::int16_t UnpackTrd::extractSample(size_t* adcbuffer, size_t* nadcbits) + { + // can not extract samples from a buffer with less than 9 bits + assert(*nadcbits >= 9); + + // We want to access the bits stored at the positions between nadcbits and nadcbits - 9, so we can already here + // reduce nadcbits by 9 and than shift the adcbuffer by this value to the right and compare it with temp which has the 9 lsbs set to 1 + *nadcbits -= 9; + + std::int16_t temp = 0x1ff; + temp &= (*adcbuffer >> (*nadcbits)); + + // Now we have our 9 bits stored in temp, but for a std::int16_t this does not match in terms of the sign handling. + // So we check on bit 9 for the sign (temp & 0x0100) and if we have a negative value we manipulate bit 16-10 to 1 + // to get the correct negative number + std::int16_t sample = (temp & 0x0100) ? (temp | 0xff00) : temp; + + return sample; + } + + // ---- getInfoType ---- + Spadic::MsInfoType UnpackTrd::getInfoType(const std::uint32_t frame) + { + // Set first 20 bits to 1 for the mask + size_t mask = 0x000FFFFF; + + // 000011.................. : BOM word + // 0000010................. : MSB word + // 0000011................. : BUF word + // 0000100................. : UNU word + // 0000101................. : MIS word + + if (((frame & mask) >> 18) == 3) // BOM + { + return Spadic::MsInfoType::kBOM; + } + if (((frame & mask) >> 17) == 2) // MSB + { + return Spadic::MsInfoType::kMSB; + } + if (((frame & mask) >> 17) == 3) // BUF + { + digestBufInfoFlags(frame); + return Spadic::MsInfoType::kBUF; + } + if (((frame & mask) >> 17) == 4) // UNU + { + return Spadic::MsInfoType::kUNU; + } + if (((frame & mask) >> 17) == 5) // MIS + { + return Spadic::MsInfoType::kMIS; + } + else { + std::cout << "UnpackTrd::GetInfoType] unknown type!" << std::endl; + exit(1); + return Spadic::MsInfoType::kMSB; + } + } + + // ---- getMessageType ---- + Spadic::MsMessageType UnpackTrd::getMessageType(const std::uint32_t frame) + { + std::uint32_t checkframe = frame; + checkframe &= 0xffffff; + if ((checkframe >> 21) == 1) // SOM 001. .... + { + return Spadic::MsMessageType::kSOM; + } + else if ((checkframe >> 22) == 1) // RDA 01.. .... + { + return Spadic::MsMessageType::kRDA; + } + else if ((checkframe >> 20) == 1) // EOM 0001 .... + { + return Spadic::MsMessageType::kEOM; + } + else if ((checkframe >> 22) == 3) // TS_MSB 11.. .... + { + return Spadic::MsMessageType::kEPO; + } + else if (0 < (checkframe >> 18) && (checkframe >> 18) <= 3) { + return Spadic::MsMessageType::kINF; + } + else if (checkframe == 0) // Last Word in a Microslice is 0 + { + return Spadic::MsMessageType::kNUL; + } + else // not a spadic message + { + return Spadic::MsMessageType::kUNK; + } + } + + // ---- getTsMsb ---- + std::uint8_t UnpackTrd::getTsMsb(const std::uint32_t frame, UnpackTrdMonitorData& monitor) + { + if ((frame & 0xf) > 0) + return -2; // if a 'error' ts_msb is received the tsmsb value is not correct. To not mess up the counting -2 is returned. + // The epoch in form of the TS_MSBs is written 3 times into the frame to allow to check for bit flips and catch errors. + // It has the length of 6 bits and an offset of 4 + std::uint8_t tsmsb[3]; + for (uint iepoch = 0; iepoch < 3; ++iepoch) { + tsmsb[iepoch] = static_cast<std::uint8_t>((frame >> (4 + 6 * iepoch) & 0x3f)); + } + + // Check if the epoch at position 0 is at least compatible with one of the others. + // Since, we only have 3 that value is automatically the majority value. + if (tsmsb[0] == tsmsb[1] || tsmsb[0] == tsmsb[2]) return tsmsb[0]; + + // If we arrive here the epoch at position 0 is not compatible with the other two. + // So let's check if they are compatible with each other. If so we have again a majority epoch + if (tsmsb[1] == tsmsb[2]) return tsmsb[1]; + + monitor.fNumNonMajorTsMsb++; + + return tsmsb[0]; + } + + // ---- makeDigi ---- + CbmTrdDigi UnpackTrd::makeDigi(CbmTrdRawMessageSpadic raw) + { + // Extract the trigger type and translate it to the digi enum + auto rawTriggerType = static_cast<Spadic::eTriggerType>(raw.GetHitType()); + auto triggerType = GetDigiTriggerType(rawTriggerType); + + // Get the digi error class (dummy for the time being) + int32_t errClass = 0; + + // Get the address of the originating spadic + const UnpackTrdCrobPar& crobPar = fParams.fCrobParams.at(raw.GetCrobId()); + const UnpackTrdElinkPar& elinkPar = crobPar.fElinkParams.at(raw.GetElinkId()); + const uint32_t asicAddress = elinkPar.fAddress; + + // Get the channel id on the module + int32_t padChNr = elinkPar.fChanAddress.at(raw.GetChannelId()); + + // Store the full time information to last full-time member for error message handling + fLastFulltime = raw.GetFullTime(); + + // Get the time information and apply the necessary correction + uint64_t time = raw.GetTime() - fSystemTimeOffset; + + // Get the timeshift and set the member, which is required for some of the rtd methods + uint64_t currentTimeshift = GetBinTimeShift(raw.GetSamples()); + + // In simulation since we often start at time = 0 there is a non negligible chance that a time < 0 is extracted. + //Since, this is not allowed in the code we set it to 0 for these cases + time = time > currentTimeshift ? time - currentTimeshift : 0; + + // In this case of CbmTrdRawToDigi GetCharge calls GetBinTimeshift, since the information is needed. + // The shift is stored in fCurrentTimeshift + // Hence, the order of charge and time assignement here makes a difference! + auto maxadc = GetMaxAdcValue(raw.GetSamples()); + + // Get energy from maxadc value + auto energy = maxadc * fParams.fMaxAdcToEnergyCal; + + // Get the unique module id from the asic address + int32_t uniqueModuleId = asicAddress / 1000; + + CbmTrdDigi digi = CbmTrdDigi(padChNr, uniqueModuleId, energy, time, triggerType, errClass); + + // If the raw message was flagged as multi hit, forward this info to the digi + if (raw.GetMultiHit()) digi.SetTriggerType(CbmTrdDigi::eTriggerType::kMulti); + + return digi; + } + + // ---- makeRaw ---- + CbmTrdRawMessageSpadic UnpackTrd::makeRaw(const std::uint32_t frame, std::uint16_t criId, std::uint8_t crobId, + std::uint16_t elinkId, std::uint8_t istream) + { + auto chId = static_cast<std::uint8_t>(((frame >> 17) & 0xf)); + auto timestamp = static_cast<std::uint8_t>((frame >> 9) & 0xff); + bool multihit = ((frame >> 8) & 0x1); + auto hitType = static_cast<std::uint8_t>((frame >> 6) & 0x3); + std::uint8_t nsamples = 0; + // We directly start with the largest possible samples vector to only init it once + std::vector<std::int16_t> samples = std::vector<std::int16_t>(0); + + uint64_t fulltime = fMsStartTimeRelCC + (fNrTsMsbVec.at(istream) * fTsMsbLengthCC) + timestamp; + + // Create message + return CbmTrdRawMessageSpadic(chId, elinkId, crobId, criId, hitType, nsamples, multihit, fulltime, samples); + } + + + // ---- GetDigiTriggerType ---- + CbmTrdDigi::eTriggerType UnpackTrd::GetDigiTriggerType(Spadic::eTriggerType tt) + { + // Shift self trigger to digi selftrigger + // Shift neighbour trigger to digi neighbour + // Hide spadic kSandN in Self + switch (tt) { + case Spadic::eTriggerType::kGlobal: return CbmTrdDigi::eTriggerType::kNTrg; + case Spadic::eTriggerType::kSelf: return CbmTrdDigi::eTriggerType::kSelf; + case Spadic::eTriggerType::kNeigh: return CbmTrdDigi::eTriggerType::kNeighbor; + case Spadic::eTriggerType::kSandN: return CbmTrdDigi::eTriggerType::kSelf; + default: return CbmTrdDigi::eTriggerType::kNTrg; + } + } + + // --- GetCharge ---- + float_t UnpackTrd::GetMaxAdcValue(const std::vector<std::int16_t>* samples) + { + // Safety for corrupted input samples + assert(samples->size() >= fPeakingBinMin); + + // The signal should peak at the shaping time. + // The corresponding sample is the peaking time divided by the sample length. + auto itbegin = std::next(samples->begin(), fPeakingBinMin); + + // Check if the expected maximum position of the peaking bin exceeds the size of the vector + auto nsamples = samples->size(); + auto peakingBinMax = (nsamples - 1) > fPeakingBinMax ? fPeakingBinMax : nsamples; + auto itend = std::next(samples->begin(), peakingBinMax); + + // Get the maximum element + auto itmax = std::max_element(itbegin, itend); + + // Get charge and correct for the baseline + float_t charge = static_cast<float_t>(*itmax) - GetBaseline(samples); + + // Remark: Due to the fact, that we store the charge UInt_t in the Digi values below 0 are not allowed. + // In this case the above only appears if the baseline fluctuated above all values in the applied peaking range. + // This can only happen for forced neighbor triggers with a deposited charged that can not be separated from the baseline. + return charge > 0 ? charge : 0; + } + + // ---- GetBaseline ---- + float_t UnpackTrd::GetBaseline(const std::vector<std::int16_t>* samples) + { + // The spadic 2.2 has a functionality that an average baseline can be written to the first sample. + // So first we have to check if this is active. + if (fParams.fUseBaselineAvg) return samples->at(0); + else { + float_t baseline = 0.0; + auto itend = samples->begin() + fNrOfPresamples; + if (itend > samples->end()) itend = samples->end(); + for (auto isample = samples->begin(); isample < itend; isample++) { + baseline += *isample; + } + baseline /= fNrOfPresamples; + + return baseline; + } + } + + +} /* namespace cbm::algo */ diff --git a/algo/detectors/trd/UnpackTrd.h b/algo/detectors/trd/UnpackTrd.h new file mode 100644 index 0000000000..8d43d737cf --- /dev/null +++ b/algo/detectors/trd/UnpackTrd.h @@ -0,0 +1,284 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Pascal Raisig, Dominik Smith [committer] */ + +#ifndef CBM_ALGO_UNPACKTRD_H +#define CBM_ALGO_UNPACKTRD_H 1 + + +#include "CbmTrdDigi.h" +#include "CbmTrdRawMessageSpadic.h" + +#include "MicrosliceDescriptor.hpp" +#include "Timeslice.hpp" + +#include <memory> + +#include <cmath> + +namespace cbm::algo +{ + /** @struct UnpackTrdElinkPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief TRD Unpacking parameters for one eLink + **/ + struct UnpackTrdElinkPar { + std::vector<uint32_t> fChanAddress; ///< CbmTrdAddress for different channels + uint32_t fAddress; ///< Asic address + uint64_t fTimeOffset = 0.; ///< Time calibration parameter + }; + + /** @struct UnpackTrdCrobPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief TRD Unpacking parameters for one CROB + **/ + struct UnpackTrdCrobPar { + std::vector<UnpackTrdElinkPar> fElinkParams = {}; ///< Parameters for each eLink + }; + + /** @struct UnpackTrdPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief Parameters required for the TRD unpacking (specific to one component) + **/ + struct UnpackTrdPar { + bool fUseBaselineAvg = true; ///< Is baseline average function of Spadic activated + float_t fMaxAdcToEnergyCal = 1.0; ///< max adc to energy in keV + uint32_t fNumChansPerAsic = 0; ///< Number of channels per ASIC + uint32_t fNumAsicsPerModule = 0; ///< Number of ASICS per module + std::vector<UnpackTrdCrobPar> fCrobParams = {}; ///< Parameters for each CROB + }; + + + /** @struct UnpackTrdMoni + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief Monitoring data for TRD unpacking + **/ + struct UnpackTrdMonitorData { + 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 + size_t fNumNonMajorTsMsb = 0; ///< Counter for the ts_msb used to reconstruct the time + size_t fNumElinkMis = 0; ///< Number of SOM to RDA/EOM mismatches + size_t fNumCorruptEom = 0; ///< Number of corrupted EOM frames + size_t fNumWildRda = 0; ///< Number of rda frames outside of a SOM frame range + size_t fNumWildEom = 0; ///< Number of eom frames outside of a SOM frame range + size_t fNumUnknownWords = 0; ///< Number of unknown words + size_t fNumMissingEom = 0; ///< Number of missing EOM frames to finish a SOM frame + size_t fNumWildNul = 0; ///< Number of wild null words, should only appear at the end of a µSlice + size_t fNumCreatedRawMsgs = 0; ///< counter of created raw messages + size_t fNumEpochMsgs = 0; ///< counter of created raw messages + size_t fNumCrcValidFlags = 0; ///< counter for inf/error flags from the µSlices + size_t fNumOverflowFlimFlags = 0; ///< counter for inf/error flags from the µSlices + size_t fNumOverflowUserFlags = 0; ///< counter for inf/error flags from the µSlices + size_t fNumDataErrorFlags = 0; ///< counter for inf/error flags from the µSlices + size_t fNumCreatedInfoMsgs = 0; ///< counter of created info messages + bool HasErrors() + { + uint32_t numErrors = fNumNonHitOrTsbMessage + fNumErrElinkOutOfRange + fNumErrInvalidFirstMessage + + fNumErrInvalidMsSize + fNumErrTimestampOverflow; + return (numErrors > 0 ? true : false); + } + }; + + + /** @class UnpackTrd + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 31 January 2023 + ** @brief Unpack algorithm for TRD + **/ + class UnpackTrd { + + public: + typedef std::pair<std::vector<CbmTrdDigi>, UnpackTrdMonitorData> resultType; + + + /** @brief Default constructor **/ + UnpackTrd() {}; + + + /** @brief Destructor **/ + ~UnpackTrd() {}; + + + /** @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<UnpackTrdPar> params) { fParams = *(std::move(params)); } + + private: // members + UnpackTrdPar fParams = {}; ///< Parameter container + + /** + ** @brief Handle the output created by the explicit algorithms. E.g. write to output vectors. + ** @param digi + ** @param raw + **/ + void digestOutput(std::unique_ptr<CbmTrdDigi> digi, CbmTrdRawMessageSpadic raw); + + /** + ** @brief Digest the aditional flags stored in the 4 "cccc" bits of the EPO messages. + ** @param frame + ** @return Spadic::MsInfoType + **/ + Spadic::MsInfoType digestBufInfoFlags(const std::uint32_t frame); + + /** + ** @brief Digest the flags of the currently unpacked µSlice. + ** @param flags flags stored in the µSlice descriptor + ** @param storage of monitoring data + **/ + void digestMsFlags(const std::uint16_t flags, UnpackTrdMonitorData& monitor); + + /** + ** @brief Digest a info message run all default information forwarding from the msg. + ** @param frame + **/ + void digestInfoMsg(const std::uint32_t frame); + + /** + ** @brief Extract one adc sample from a given adcbuffer + ** @param[in] adcbuffer + ** @param[in,out] nadcbits + ** @return std::int16_t + **/ + std::int16_t extractSample(size_t* adcbuffer, size_t* nadcbits); + + /** + ** @brief Extract the baseline average sample from a given adcbuffer. + ** Depending on the Spadic settings sample-0 is a plain sample or the averaged + ** baseline calculation. The latter is not a 9 bit signed integer, but a 9 bit + ** floating point number 7 digits before the point and 2 afterwards. + ** @param[in] adcbuffer + ** @param[in,out] nadcbits + ** @return std::float_t + **/ + std::float_t extractAvgSample(size_t* adcbuffer, size_t* nadcbits); + + /** @brief Identify the InfoType of a 64bit InfoMessage word inside a Microslice */ + Spadic::MsInfoType getInfoType(const std::uint32_t frame); + + /** + ** @brief Get the ts_msb information from the TS_MSB(kEPO) frame. We take the first of the 3 + ** The 3 redundant TS_MSB sets are already compared at the FPGA level. + ** @param frame + ** @param storage of monitoring data + ** @return ts_msb value + **/ + std::uint8_t getTsMsb(const std::uint32_t frame, UnpackTrdMonitorData& monitor); + + /** + ** @brief Create a CbmTrdRawMessageSpadic from the hit message input. + ** @param word + ** @param criId id of the cri that send the µSlice + ** @param criobId id of the crob that send the µSlice (currently not used set to 0 062021 PR) + ** @param istream + ** @return CbmTrdRawMessageSpadic + ** @todo Check if we can get rid of the future obsolete microslice stuff. + **/ + CbmTrdRawMessageSpadic makeRaw(const std::uint32_t frame, std::uint16_t criId, std::uint8_t crobId, + std::uint16_t elinkId, std::uint8_t istream); + + /** + ** @brief Get the Digi Trigger Type from the raw message triggertype + ** @param tt + ** @return CbmTrdDigi::eTriggerType + **/ + static CbmTrdDigi::eTriggerType GetDigiTriggerType(Spadic::eTriggerType tt); + + /** + ** @brief Create an actual digi from the raw message + ** @param raw + **/ + CbmTrdDigi makeDigi(CbmTrdRawMessageSpadic raw); + + /** + ** @brief Get the Bin Time Shift value + ** @param samples + ** @return ULong64_t + **/ + uint64_t GetBinTimeShift(const std::vector<std::int16_t>* samples) { return 0.; }; + + /** + ** @brief Get the MaxAdc value + ** @param samples + ** @return Float_t + **/ + std::float_t GetMaxAdcValue(const std::vector<std::int16_t>* samples); + + /** + ** @brief Get the Baseline value + ** The digi charge is an unsigned. Hence, we need to get the baseline to 0 + ** @param samples + ** @return float_t + **/ + float_t GetBaseline(const std::vector<std::int16_t>* samples); + + /** @brief Identify the message type of a given 32bit frame inside a Microslice */ + Spadic::MsMessageType getMessageType(const std::uint32_t frame); + + /** @brief Counter for the ts_msb used to reconstruct the time */ + std::vector<std::uint8_t> fNrTsMsbVec = {}; + + /** @brief Bytes per spadic frame stored in the microslices */ + static const std::uint8_t fBytesPerWord = 8; + + /** For the msg format used from 2021 ongoing we have 2 parallel streams per word. * + ** All data from eLinks 0..20 go to one stream and 21..41 to the other */ + /** @brief Number of streams per word **/ + static const std::uint8_t fStreamsPerWord = 2; + + /** + ** @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; + + /** @brief Start time of the current µSlice relative to the Timeslice start time in Spadic CC. */ + size_t fMsStartTimeRelCC = 0; + + /** @brief Time of the last succesful digest hit message */ + size_t fLastFulltime = 0; + + /** @brief Number of samples not considered for max adc */ + static constexpr size_t fNrOfPresamples = 1; + + /** @brief Clock length of Spadic in ns. */ + static constexpr float_t fAsicClockCycle = 62.5; + + /** @brief length of one ts_msb in [ns] */ + static constexpr std::uint16_t fTsMsbLength = 16000; + + /** @brief length of one ts_msb in [cc] */ + static constexpr size_t fTsMsbLengthCC = fTsMsbLength / fAsicClockCycle; + + /** @brief First sample to look for the max adc */ + static constexpr size_t fPeakingBinMin = fNrOfPresamples; + + /** + ** @brief Last sample to look for the max adc + ** Default value is set based on the Shaping time + 5 samples safety margin. + ** @remark the peaking time strongly depends on the input signal. Effective range of + ** the shaping time is between 120 and 240 ns. + **/ + static constexpr size_t fPeakingBinMax = static_cast<size_t>(120.0 / fAsicClockCycle + fNrOfPresamples + 5); + }; + + +} /* namespace cbm::algo */ + +#endif /* CBM_ALGO_UNPACKTRD_H */ diff --git a/core/data/trd/CbmTrdRawMessageSpadic.cxx b/core/data/trd/CbmTrdRawMessageSpadic.cxx index 66bf8e03b5..43388b0af6 100644 --- a/core/data/trd/CbmTrdRawMessageSpadic.cxx +++ b/core/data/trd/CbmTrdRawMessageSpadic.cxx @@ -82,5 +82,3 @@ void CbmTrdRawMessageSpadic::SetSample(std::int16_t value, std::uint8_t pos) return; } - -ClassImp(CbmTrdRawMessageSpadic) diff --git a/core/data/trd/CbmTrdRawMessageSpadic.h b/core/data/trd/CbmTrdRawMessageSpadic.h index fca0d2f3f8..abf4698f25 100644 --- a/core/data/trd/CbmTrdRawMessageSpadic.h +++ b/core/data/trd/CbmTrdRawMessageSpadic.h @@ -11,14 +11,12 @@ #ifndef CbmTrdRawMessageSpadic_H #define CbmTrdRawMessageSpadic_H -#include <Rtypes.h> // for ClassDef - #include <boost/serialization/access.hpp> #include <boost/serialization/base_object.hpp> #include <boost/serialization/vector.hpp> #include <cstdint> -#include <vector> // for vector +#include <vector> // for vector namespace Spadic { @@ -125,7 +123,7 @@ public: void IncNrSamples() { fNrSamples++; } /** Set the full time in nanoseconds */ - void SetTime(Double_t setvalue) { fFullTime = static_cast<std::uint64_t>(setvalue / 62.5); } + void SetTime(double setvalue) { fFullTime = static_cast<std::uint64_t>(setvalue / 62.5); } /** Returns the value of the sample with the highest value. */ int16_t GetMaxAdc(); @@ -153,9 +151,6 @@ public: ar& fFullTime; ar& fSamples; } - - // Root Class Def Macro - ClassDef(CbmTrdRawMessageSpadic, 2); }; #endif diff --git a/reco/tasks/CMakeLists.txt b/reco/tasks/CMakeLists.txt index 6c7302a21c..8e4478fdd8 100644 --- a/reco/tasks/CMakeLists.txt +++ b/reco/tasks/CMakeLists.txt @@ -35,6 +35,7 @@ set(PUBLIC_DEPENDENCIES set(PRIVATE_DEPENDENCIES CbmBase + CbmTrdBase FairLogger::FairLogger FairRoot::Online external::yaml-cpp diff --git a/reco/tasks/CbmTaskUnpack.cxx b/reco/tasks/CbmTaskUnpack.cxx index a736afb4f2..a5b8926b88 100644 --- a/reco/tasks/CbmTaskUnpack.cxx +++ b/reco/tasks/CbmTaskUnpack.cxx @@ -11,9 +11,13 @@ #include "CbmDigiManager.h" #include "CbmDigiTimeslice.h" #include "CbmSourceTs.h" +#include "CbmTrdParSetAsic.h" +#include "CbmTrdParSpadic.h" #include "MicrosliceDescriptor.hpp" +#include <FairParAsciiFileIo.h> +#include <FairParamList.h> #include <FairRunOnline.h> #include <Logger.h> @@ -42,6 +46,9 @@ using cbm::algo::UnpackStsElinkPar; using cbm::algo::UnpackStsPar; using cbm::algo::UnpackTofElinkPar; using cbm::algo::UnpackTofPar; +using cbm::algo::UnpackTrdCrobPar; +using cbm::algo::UnpackTrdElinkPar; +using cbm::algo::UnpackTrdPar; // ----- Constructor ----------------------------------------------------- CbmTaskUnpack::CbmTaskUnpack() : FairTask("Unpack") {} @@ -191,6 +198,25 @@ void CbmTaskUnpack::Exec(Option_t*) numCompUsed++; } // system T0 + // system TRD + if (systemId == fles::SubsystemIdentifier::TRD) { + const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id; + const auto algoIt = fAlgoTrd.find(equipmentId); + assert(algoIt != fAlgoTrd.end()); + + // The current algorithm works for the TRD 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.fTrd.fDigis, &numBytesInComp, &numDigisInComp); + + numCompUsed++; + } // system TRD + compTimer.Stop(); LOG(debug) << GetName() << ": Component " << comp << ", microslices " << numMsInComp << " input size " << numBytesInComp << " bytes, " @@ -212,6 +238,8 @@ void CbmTaskUnpack::Exec(Option_t*) [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); std::sort(std::execution::par_unseq, fTimeslice->fData.fT0.fDigis.begin(), fTimeslice->fData.fT0.fDigis.end(), [](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(); }); #else std::sort(fTimeslice->fData.fSts.fDigis.begin(), fTimeslice->fData.fSts.fDigis.end(), [](CbmStsDigi digi1, CbmStsDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); @@ -221,6 +249,8 @@ void CbmTaskUnpack::Exec(Option_t*) [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); }); std::sort(fTimeslice->fData.fT0.fDigis.begin(), fTimeslice->fData.fT0.fDigis.end(), [](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(); }); #endif // --- Timeslice log @@ -360,6 +390,29 @@ InitStatus CbmTaskUnpack::Init() LOG(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks"; } + InitTrdReadoutConfig(); + auto equipIdsTrd = fTrdConfig.GetEquipmentIds(); + for (auto& equip : equipIdsTrd) { + + std::unique_ptr<UnpackTrdPar> par(new UnpackTrdPar()); + const size_t numCrobs = fTrdConfig.GetNumCrobs(equip); + 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++) { + + UnpackTrdElinkPar elinkPar; + auto addresses = fTrdConfig.Map(equip, crob, elink); + elinkPar.fAddress = addresses.first; // Asic address for this elink + elinkPar.fChanAddress = addresses.second; // Channel addresses for this elink + crobPar.fElinkParams.push_back(elinkPar); + } + par->fCrobParams.push_back(crobPar); + } + fAlgoTrd[equip].SetParams(std::move(par)); + LOG(info) << "--- Configured equipment " << equip << " with " << numCrobs << " crobs"; + } LOG(info) << "--- Configured " << fAlgoSts.size() << " unpacker algorithms for STS."; LOG(debug) << "Readout map:" << fStsConfig.PrintReadoutMap(); @@ -373,5 +426,62 @@ InitStatus CbmTaskUnpack::Init() } // ---------------------------------------------------------------------------- +// ----- Initialisation --------------------------------------------------- +void CbmTaskUnpack::InitTrdReadoutConfig() +{ + std::string trdparfile = Form("%s/parameters/trd/trd_v21d_mcbm.asic.par", std::getenv("VMCWORKDIR")); + + CbmTrdParSetAsic trdpar; + + FairParAsciiFileIo asciiInput; + if (asciiInput.open(trdparfile.data())) { trdpar.init(&asciiInput); } + + FairParamList parlist; + trdpar.putParams(&parlist); + + std::vector<int> moduleIds(trdpar.GetNrOfModules()); + parlist.fill("ModuleId", moduleIds.data(), moduleIds.size()); + + std::map<size_t, std::map<size_t, std::map<size_t, size_t>>> addressMap; //[criId][crobId][elinkId] -> asicAddress + std::map<size_t, std::map<size_t, std::map<size_t, std::map<size_t, size_t>>>> + channelMap; //[criId][crobId][elinkId][chanId] -> chanAddress + + for (auto module : moduleIds) { + CbmTrdParSetAsic* moduleSet = (CbmTrdParSetAsic*) trdpar.GetModuleSet(module); + + // Skip entries for "Fasp" modules in .asic.par file + if (moduleSet->GetAsicType() != static_cast<int32_t>(CbmTrdDigi::eCbmTrdAsicType::kSPADIC)) continue; + + std::vector<int> asicAddresses; + moduleSet->GetAsicAddresses(&asicAddresses); + + for (auto address : asicAddresses) { + CbmTrdParSpadic* asicPar = (CbmTrdParSpadic*) moduleSet->GetAsicPar(address); + const uint16_t criId = asicPar->GetCriId(); + const uint8_t crobId = asicPar->GetCrobId(); + const uint8_t elinkId = asicPar->GetElinkId(0); + if (elinkId >= 98) { continue; } // Don't add not connected asics to the map + addressMap[criId][crobId][elinkId] = address; + addressMap[criId][crobId][elinkId + 1] = address; + + const uint8_t numChans = 16; + for (uint8_t chan = 0; chan < numChans; chan++) { + auto asicChannelId = (elinkId % 2) == 0 ? chan : chan + numChans; + auto chanAddr = asicPar->GetChannelAddresses().at(asicChannelId); + channelMap[criId][crobId][elinkId][chan] = chanAddr; + } + for (uint8_t chan = 0; chan < numChans; chan++) { + auto asicChannelId = (elinkId + 1 % 2) == 0 ? chan : chan + numChans; + auto chanAddr = asicPar->GetChannelAddresses().at(asicChannelId); + channelMap[criId][crobId][elinkId + 1][chan] = chanAddr; + } + LOG(debug) << "componentID " << asicPar->GetComponentId() << " " + << "address " << address << " key " << criId << " " << unsigned(crobId) << " " << unsigned(elinkId); + } + } + fTrdConfig.Init(addressMap, channelMap); + LOG(debug) << fTrdConfig.PrintReadoutMap(); +} + ClassImp(CbmTaskUnpack) diff --git a/reco/tasks/CbmTaskUnpack.h b/reco/tasks/CbmTaskUnpack.h index 040dbc0175..9fd0d0a332 100644 --- a/reco/tasks/CbmTaskUnpack.h +++ b/reco/tasks/CbmTaskUnpack.h @@ -12,6 +12,7 @@ #include <FairTask.h> #include "TofReadoutConfig.h" +#include "TrdReadoutConfig.h" #include <sstream> #include <vector> @@ -24,6 +25,7 @@ #include "UnpackMuch.h" #include "UnpackSts.h" #include "UnpackTof.h" +#include "UnpackTrd.h" class CbmDigiManager; class CbmSourceTs; @@ -72,6 +74,8 @@ private: // methods /** @brief Task initialisation **/ virtual InitStatus Init(); + /** @brief Initialisation of address maps for Trd **/ + virtual void InitTrdReadoutConfig(); private: // members CbmSourceTs* fSource = nullptr; @@ -88,6 +92,9 @@ private: // members std::map<uint16_t, cbm::algo::UnpackBmon> fAlgoBmon = {}; cbm::algo::BmonReadoutConfig fBmonConfig {}; + std::map<uint16_t, cbm::algo::UnpackTrd> fAlgoTrd = {}; + cbm::algo::TrdReadoutConfig fTrdConfig {}; + size_t fNumTs = 0; size_t fNumMs = 0; size_t fNumBytes = 0; -- GitLab