diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index a70b0872c98516d229715c67c7f86b14d8102a83..b07afd74581e2d9a6b58b7d04629f39902ac9acf 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -143,6 +143,9 @@ set(SRCS detectors/rich/ReadoutConfig.cxx detectors/rich/Unpack.cxx detectors/rich/UnpackMS.cxx + detectors/must/Unpack.cxx + detectors/must/UnpackMS.cxx + detectors/must/ReadoutConfig.cxx global/ParFiles.cxx global/StorableRecoResults.cxx global/Reco.cxx @@ -159,6 +162,7 @@ set(SRCS qa/hitfind/BmonHitfindQa.cxx qa/hitfind/BmonHitfindQaParameters.cxx qa/unpack/StsDigiQa.cxx + qa/unpack/MustDigiQa.cxx ca/TrackingSetup.cxx ca/TrackingChain.cxx ca/qa/CaQa.cxx @@ -337,6 +341,7 @@ install(DIRECTORY base/gpu TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY data/sts TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY detectors/bmon TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY detectors/much TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +install(DIRECTORY detectors/must TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY detectors/sts TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY detectors/tof TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY detectors/trd TYPE INCLUDE FILES_MATCHING PATTERN "*.h") diff --git a/algo/base/AuxDigiData.h b/algo/base/AuxDigiData.h index e464da7e281fbb57a33557ccf43ff98947fd2767..a2bb3201615d5f2886dc68f56bc3debc3fc6c9e3 100644 --- a/algo/base/AuxDigiData.h +++ b/algo/base/AuxDigiData.h @@ -12,6 +12,7 @@ #include "CommonUnpacker.h" #include "bmon/UnpackMS.h" #include "much/UnpackMS.h" +#include "must/UnpackMS.h" #include "rich/UnpackMS.h" #include "sts/UnpackMS.h" #include "tof/UnpackMS.h" @@ -28,6 +29,7 @@ namespace cbm::algo UnpackAux<bmon::UnpackAuxData> fBmon; UnpackAux<much::UnpackAuxData> fMuch; UnpackAux<rich::UnpackAuxData> fRich; + UnpackAux<must::UnpackAuxData> fMust; UnpackAux<sts::UnpackAuxData> fSts; UnpackAux<tof::UnpackAuxData> fTof; UnpackAux<trd::UnpackAuxData> fTrd; diff --git a/algo/base/Definitions.h b/algo/base/Definitions.h index b1ff4c9ccc8e804a33a67fe0ab40d81d74dd616e..74b2f532bb0c9f1c0bbb03a8bc10d69d4e3cc824 100644 --- a/algo/base/Definitions.h +++ b/algo/base/Definitions.h @@ -67,6 +67,7 @@ namespace cbm::algo UnpackMuch, UnpackTof, UnpackFsd, + UnpackMust, EventBuilding, RecoBmon, RecoSts, @@ -139,6 +140,7 @@ CBM_ENUM_DICT(cbm::algo::QaStep, {"UnpackMuch", cbm::algo::QaStep::UnpackMuch}, {"UnpackTof", cbm::algo::QaStep::UnpackTof}, {"UnpackFsd", cbm::algo::QaStep::UnpackFsd}, + {"UnpackMust", cbm::algo::QaStep::UnpackMust}, {"EventBuilding", cbm::algo::QaStep::EventBuilding}, {"RecoBmon", cbm::algo::QaStep::RecoBmon}, {"RecoSts", cbm::algo::QaStep::RecoSts}, diff --git a/algo/base/DigiData.cxx b/algo/base/DigiData.cxx index 79fd5a4836022d2caae819e0715f3fcf0a1ecfe7..2277a0dac279df30c9f64a8593a67807e3b37cf5 100644 --- a/algo/base/DigiData.cxx +++ b/algo/base/DigiData.cxx @@ -21,6 +21,7 @@ DigiData::DigiData(const CbmDigiData& storable) , fRich(ToPODVector(storable.fRich.fDigis)) , fPsd(ToPODVector(storable.fPsd.fDigis)) , fFsd(ToPODVector(storable.fFsd.fDigis)) + , fMust(ToPODVector(storable.fMust.fDigis)) { } @@ -36,6 +37,7 @@ size_t DigiData::Size(ECbmModuleId system) const case ECbmModuleId::kRich: return fRich.size(); case ECbmModuleId::kPsd: return fPsd.size(); case ECbmModuleId::kFsd: return fFsd.size(); + case ECbmModuleId::kMust: return fMust.size(); default: throw std::runtime_error("DigiData: Invalid system Id " + ::ToString(system)); } } @@ -43,14 +45,15 @@ size_t DigiData::Size(ECbmModuleId system) const size_t DigiData::TotalSize() const { return fSts.size() + fMuch.size() + fTof.size() + fBmon.size() + fTrd.size() + fTrd2d.size() + fRich.size() - + fPsd.size() + fFsd.size(); + + fPsd.size() + fFsd.size() + fMust.size(); } size_t DigiData::TotalSizeBytes() const { return sizeof(CbmStsDigi) * fSts.size() + sizeof(CbmMuchDigi) * fMuch.size() + sizeof(CbmTofDigi) * fTof.size() + sizeof(CbmBmonDigi) * fBmon.size() + sizeof(CbmTrdDigi) * fTrd.size() + sizeof(CbmTrdDigi) * fTrd2d.size() - + sizeof(CbmRichDigi) * fRich.size() + sizeof(CbmPsdDigi) * fPsd.size() + sizeof(CbmFsdDigi) * fFsd.size(); + + sizeof(CbmRichDigi) * fRich.size() + sizeof(CbmPsdDigi) * fPsd.size() + sizeof(CbmFsdDigi) * fFsd.size() + + sizeof(CbmMustDigi) * fMust.size(); } CbmDigiData DigiData::ToStorable() const @@ -92,6 +95,10 @@ CbmDigiData DigiData::ToStorable() const { .fDigis = ToStdVector(fFsd), }, + .fMust = + { + .fDigis = ToStdVector(fMust), + }, }; } diff --git a/algo/base/DigiData.h b/algo/base/DigiData.h index 09b82d933478c57b65087b93e1d0a799120d287b..93475796344854d95709906bf219b04235c14af6 100644 --- a/algo/base/DigiData.h +++ b/algo/base/DigiData.h @@ -10,6 +10,7 @@ #include "CbmEventTriggers.h" #include "CbmFsdDigi.h" #include "CbmMuchDigi.h" +#include "CbmMustDigi.h" #include "CbmPsdDigi.h" #include "CbmRichDigi.h" #include "CbmStsDigi.h" @@ -39,6 +40,7 @@ namespace cbm::algo PODVector<CbmRichDigi> fRich; ///< Unpacked RICH digis PODVector<CbmPsdDigi> fPsd; ///< Unpacked PSD digis PODVector<CbmFsdDigi> fFsd; ///< Unpacked FSD digis + PODVector<CbmMustDigi> fMust; ///< Unpacked MUST digis DigiData(); ~DigiData(); diff --git a/algo/data/CMakeLists.txt b/algo/data/CMakeLists.txt index 717ba53896c880649c4c07c1ff010fad8c14551b..5dd6ae7c12a4bd9a87840c259dba8aee2a3ad251 100644 --- a/algo/data/CMakeLists.txt +++ b/algo/data/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRCS ${OFFLINE_DATA_DIR}/fsd/CbmFsdDigi.cxx ${OFFLINE_DATA_DIR}/fsd/CbmFsdAddress.cxx + ${OFFLINE_DATA_DIR}/must/CbmMustDigi.cxx ${OFFLINE_DATA_DIR}/raw/CriGet4Mess001.cxx ${OFFLINE_DATA_DIR}/raw/StsXyterMessage.cxx @@ -50,6 +51,7 @@ target_include_directories(OnlineData PUBLIC ${OFFLINE_DATA_DIR}/tof PUBLIC ${OFFLINE_DATA_DIR}/psd PUBLIC ${OFFLINE_DATA_DIR}/fsd + PUBLIC ${OFFLINE_DATA_DIR}/must PUBLIC ${OFFLINE_DATA_DIR}/global PUBLIC ${OFFLINE_DATA_DIR}/raw ) diff --git a/algo/detectors/must/ReadoutConfig.cxx b/algo/detectors/must/ReadoutConfig.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5d059750006d72109296c06fd63a58bf59481f1d --- /dev/null +++ b/algo/detectors/must/ReadoutConfig.cxx @@ -0,0 +1,29 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#include "ReadoutConfig.h" + +namespace cbm::algo +{ + CBM_YAML_INSTANTIATE(must::ReadoutConfig); + + namespace must + { + + const std::vector<uint16_t>& ReadoutConfig::GetEquipmentIds() const { return fEquipmentIds; } + + const std::vector<uint32_t>& ReadoutConfig::GetTrbAddresses() const { return fTRBAddresses; } + + const std::array<float, 33>& ReadoutConfig::GetToTShifts(const uint32_t TRBId) const + { + return fToTShifts.at(TRBId); + } + + uint32_t ReadoutConfig::GetTimeOffset() const { return fTimeOffset; } + + uint8_t ReadoutConfig::GetSystemVersion() const { return fSystemVersion; } + + } // namespace must + +} // namespace cbm::algo diff --git a/algo/detectors/must/ReadoutConfig.h b/algo/detectors/must/ReadoutConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..1bc75edbae3004b20a2dfd89a12c2fdbe6d66215 --- /dev/null +++ b/algo/detectors/must/ReadoutConfig.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#pragma once + +#include "yaml/Property.h" +#include "yaml/Yaml.h" + +#include <array> +#include <cstdint> + +namespace cbm::algo::must +{ + + class ReadoutConfig { + + public: + ReadoutConfig() = default; + + const std::vector<uint16_t>& GetEquipmentIds() const; + + const std::vector<uint32_t>& GetTrbAddresses() const; + + const std::array<float, 33>& GetToTShifts(const uint32_t TRBId) const; + + uint32_t GetTimeOffset() const; + + uint8_t GetSystemVersion() const; + + std::vector<uint16_t> fEquipmentIds{}; + std::vector<uint32_t> fTRBAddresses{}; + std::map<uint32_t, std::array<float, 33>> fToTShifts; + uint32_t fTimeOffset{}; + uint8_t fSystemVersion{}; + + CBM_YAML_PROPERTIES(yaml::Property(&ReadoutConfig::fEquipmentIds, "equipmentIds", "List of equipment IDs", {}, YAML::Flow), + yaml::Property(&ReadoutConfig::fTRBAddresses, "TRBAddresses", "List of TRB addresses", {}, YAML::Flow), + yaml::Property(&ReadoutConfig::fToTShifts, "ToTShifts", "Map from TRB to list of ToT shifts per channel", {}, YAML::Flow), + yaml::Property(&ReadoutConfig::fTimeOffset, "timeOffset", "System time offset"), + yaml::Property(&ReadoutConfig::fSystemVersion, "systemVersion", "System version")); + }; + +} // namespace cbm::algo::must + +CBM_YAML_EXTERN_DECL(cbm::algo::must::ReadoutConfig); diff --git a/algo/detectors/must/Unpack.cxx b/algo/detectors/must/Unpack.cxx new file mode 100644 index 0000000000000000000000000000000000000000..326ed5e31bd2a9abc036cfa0012d57b86238b4a3 --- /dev/null +++ b/algo/detectors/must/Unpack.cxx @@ -0,0 +1,29 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#include "Unpack.h" + +#include "AlgoFairloggerCompat.h" + + +namespace cbm::algo::must +{ + Unpack::Unpack(const ReadoutConfig& readout) : fReadout(readout) + { + const UnpackPar unpackPar{.fTRBAddresses = fReadout.fTRBAddresses, + .fToTShifts = fReadout.fToTShifts, + .fTimeOffset = fReadout.fTimeOffset}; + + for (const auto eqId : fReadout.GetEquipmentIds()) { + fAlgos.emplace(UnpackKey{eqId, fReadout.GetSystemVersion()}, std::make_unique<UnpackMS>(unpackPar)); + + L_(info) << "--- Configured equipment " << eqId << " of MUST@RICH."; + } + + L_(info) << "--- Configured " << fAlgos.size() << " unpacker algorithms for MUST@RICH."; + } + + Unpack::Result_t Unpack::operator()(const fles::Timeslice& ts) const { return DoUnpack(fles::Subsystem::RICH, ts); } + +} // namespace cbm::algo::must diff --git a/algo/detectors/must/Unpack.h b/algo/detectors/must/Unpack.h new file mode 100644 index 0000000000000000000000000000000000000000..d98c8a77c06a899551b9454442c1b469f243330d --- /dev/null +++ b/algo/detectors/must/Unpack.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#pragma once + +#include "CommonUnpacker.h" +#include "ReadoutConfig.h" +#include "UnpackMS.h" + +namespace cbm::algo::must +{ + + namespace detail + { + using UnpackBase = CommonUnpacker<CbmMustDigi, UnpackMonitorData, UnpackAuxData>; + } + + class Unpack : public detail::UnpackBase { + public: + using Result_t = detail::UnpackBase::Result_t; + + Unpack(const ReadoutConfig& readout); + + Result_t operator()(const fles::Timeslice&) const; + + private: + ReadoutConfig fReadout; + }; + +} // namespace cbm::algo::must diff --git a/algo/detectors/must/UnpackMS.cxx b/algo/detectors/must/UnpackMS.cxx new file mode 100644 index 0000000000000000000000000000000000000000..92cc8d4de3d4f7d52517bb6a1fe1fee9bfb26cca --- /dev/null +++ b/algo/detectors/must/UnpackMS.cxx @@ -0,0 +1,460 @@ +/* Copyright (C) 2021-2025 Jagiellonian University, Krakow; Goethe-University Frankfurt, Frankfurt + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer], Pascal Raisig, Dominik Smith */ + +#include "UnpackMS.h" + +#include "AlgoFairloggerCompat.h" + +#include <cstdint> + +namespace cbm::algo::must +{ + UnpackMS::UnpackMS(const UnpackPar& pars) : fParams(pars) {} + + UnpackMS::~UnpackMS() = default; + + // ---- Algorithm execution --------------------------------------------- + UnpackMS::Result_t UnpackMS::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) const + { + MSContext ctx; + + // Clear CbmTime of MS. Used to get time offset of subtriggers to MS start + ctx.cbmTimeMS = 0; + ctx.digis.reserve(msDescr.size / sizeof(u32)); + + must::MicrosliceReader reader; + reader.SetData(msContent, msDescr.size); + + const auto mstime = msDescr.idx; + ctx.refTime = mstime - tTimeslice; + + // There are a lot of MS with 8 bytes size + // Does one need these MS? + if (reader.GetSize() <= 8) return {}; + + while (true) { + ProcessTrbPacket(reader, ctx); + ProcessHubBlock(reader, ctx); + + // -4*2 for 2 last words which contain microslice index + if (reader.GetOffset() >= reader.GetSize() - 8) break; + // -4*3 for 0xffffffff padding and 2 last words which contain microslice index + if (reader.IsNextPadding() && reader.GetOffset() >= reader.GetSize() - 12) break; + } + [[maybe_unused]] uint32_t msIndexWord1 = reader.NextWord(); + [[maybe_unused]] uint32_t msIndexWord2 = reader.NextWord(); + + return std::make_tuple(std::move(ctx.digis), std::move(ctx.monitor), UnpackAuxData()); + } + + + void UnpackMS::ProcessTrbPacket(must::MicrosliceReader& reader, MSContext& ctx) const + { + //process CBM time + const uint32_t word_MSB = reader.NextWord(); // CBM 63:32 + const uint32_t word_LSB = reader.NextWord(); // CBM 31: 0 + ctx.cbmTimePacket = (uint64_t) word_MSB << 32 | word_LSB; + if (ctx.cbmTimeMS == 0) ctx.cbmTimeMS = ctx.cbmTimePacket; + + //discard unused words + for (auto l = 0; l < 10; ++l) { + reader.NextWord(); + } + + //process remaining words + for (auto l = 0; l < 14; ++l) { + const uint32_t wordEpoch = reader.NextWord(); + const uint32_t epoch = ProcessEpoch(wordEpoch); + const uint32_t wordTime = reader.NextWord(); + const TdcTimeData td = ProcessTimeData(wordTime); + const double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine); + + if (l == 0) ctx.prevLastCh0ReTime[12] = fullTime; + if (l == 1) ctx.mbsCorr = fullTime - ctx.prevLastCh0ReTime[12]; // = MbsPrevTimeCh1 - MbsPrevTimeCh0 + if (l > 1) ctx.prevLastCh0ReTime[l - 2] = fullTime; + } + + [[maybe_unused]] const uint32_t trbNum = reader.NextWord(); // TRB trigger number + } + + + void UnpackMS::ProcessHubBlock(must::MicrosliceReader& reader, MSContext& ctx) const + { + uint32_t word = reader.NextWord(); + + [[maybe_unused]] const uint32_t hubId = word & 0xffff; // 16 bits + const uint32_t hubSize = (word >> 16) & 0xffff; // 16 bits + + bool isLast = false; // if true then it is CTS sub-sub-event + size_t totalSize = 0; + ctx.currentSubSubEvent = 0; + + uint32_t subSubEventId = 0, subSubEventSize = 0; + + //loop over events in hub block + while (totalSize < hubSize) { + word = reader.NextWord(); + subSubEventId = word & 0xffff; // 16 bits + subSubEventSize = (word >> 16) & 0xffff; // 16 bits + isLast = reader.IsLastSubSubEvent(subSubEventSize); // if true then it is CTS sub-sub-event + totalSize += (1 + subSubEventSize); + + if (!isLast) { // all except last are DiRICH events + if (((subSubEventId >> 12) & 0xF) != 0x9) { // catch invalid ids + ctx.monitor.fNumErrInvalidHubId++; + } + if (totalSize == hubSize) { + ctx.monitor.fNumErrInvalidHubSize++; + } + ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx); + ctx.currentSubSubEvent++; + } + } + + //last event ist expected to be CTS + if (totalSize != hubSize || !isLast) { + ctx.monitor.fNumErrInvalidHubSize++; + } + subSubEventSize = ProcessCtsHeader(reader, subSubEventSize, subSubEventId); + ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx); + + // read last words + int lastWordsCounter = 0; + while (true) { + lastWordsCounter++; + word = reader.NextWord(); + if (word == 0x600dda7a) { + if (reader.IsNextPadding()) word = reader.NextWord(); + break; + } + if (lastWordsCounter >= 7) { + ctx.monitor.fNumErrExcessLastWords++; + } + } + } + + int UnpackMS::ProcessCtsHeader(must::MicrosliceReader& reader, uint32_t subSubEventSize, + uint32_t /*subSubEventId*/) const + { + const uint32_t word = reader.NextWord(); + + [[maybe_unused]] const uint32_t ctsState = word & 0xffff; // 16 bits + + const uint32_t nofInputs = (word >> 16) & 0xf; // 4 bits + const uint32_t nofTrigCh = (word >> 20) & 0x1f; // 5 bits + const uint32_t inclLastIdle = (word >> 25) & 0x1; // 1 bit + const uint32_t inclTrigInfo = (word >> 26) & 0x1; // 1 bit + const uint32_t inclTime = (word >> 27) & 0x1; // 1 bit + const uint32_t ETM = (word >> 28) & 0x3; // 2 bits + + uint32_t ctsInfoSize = 2 * nofInputs + 2 * nofTrigCh + 2 * inclLastIdle + 3 * inclTrigInfo + inclTime; // in words + switch (ETM) { + case 0: break; + case 1: ctsInfoSize += 1; break; + case 2: ctsInfoSize += 4; break; + case 3: break; + } + for (uint32_t i = 0; i < ctsInfoSize; i++) { + reader.NextWord(); // do nothing? + } + const int nofTimeWords = subSubEventSize - ctsInfoSize - 1; + return nofTimeWords; + } + + void UnpackMS::ProcessSubSubEvent(must::MicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId, + MSContext& ctx) const + { + bool wasHeader = false; + bool wasEpoch = false; + bool wasTime = false; + bool wasTrailer = false; + + uint32_t epoch = 0; // store last epoch obtained in sub-sub-event + + // Store last raising edge time for each channel or -1. if no time + // this array is used to match raising and falling edges + std::vector<double> raisingTime(33, -1.); + + // check if DiRICH (SubSubEvId) is masked and skip to end if so + if (CheckMaskedDiRICH(subSubEventId)) { + for (int i = 0; i < nofTimeWords; i++) { + reader.NextWord(); + } + return; + } + + // Skip SubSubEvent for CTS and not MUST addresses + if (std::find(fParams.fTRBAddresses.cbegin(), fParams.fTRBAddresses.cend(), subSubEventId) + == fParams.fTRBAddresses.end()) { + ctx.monitor.fNumCtsAndUnmappedDirich++; + ctx.monitor.fNumSkippedSubsubevent++; + for (int i = 0; i < nofTimeWords; i++) { + reader.NextWord(); + } + return; + } + + // Catch Subsubevents where some of the 3 "mandatory words" is missing + // but this lack was properly accounted/detected by the TRBnet/CTS + // => Otherwise the final readout of the Trailer word offsets the next + // subsubevent + if (nofTimeWords < 3) { + ctx.monitor.fNumSkippedSubsubevent++; + for (int i = 0; i < nofTimeWords; i++) { + reader.NextWord(); + } + return; + } + + // First word is expected to be of type "header" + if (GetTdcWordType(reader.NextWord()) != TdcWordType::Header) { + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": 1st message is not a TDC Header, skipping subsubevent"; + ctx.monitor.fNumErrInvalidFirstMessage++; + ctx.monitor.fNumSkippedSubsubevent++; + for (int i = 1; i < nofTimeWords; i++) { + reader.NextWord(); + } + return; + } + else { + wasHeader = true; + } + + // Second word is expected to be of type "epoch" + uint32_t word = reader.NextWord(); + if (GetTdcWordType(word) != TdcWordType::Epoch) { + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": 2nd message is not an epoch, skipping subsubevent"; + ctx.monitor.fNumErrInvalidSecondMessage++; + ctx.monitor.fNumSkippedSubsubevent++; + // Currently skip the subsubevent + // TODO: Check if possible to only skip to next Epoch and continue from there + for (int i = 2; i < nofTimeWords; i++) { + reader.NextWord(); + } + return; + } + else { + epoch = ProcessEpoch(word); + wasEpoch = true; + } + + // Loop over words + for (int i = 2; i < nofTimeWords - 1; i++) { + word = reader.NextWord(); + switch (GetTdcWordType(word)) { + case TdcWordType::TimeData: { + if (!wasHeader || !wasEpoch || wasTrailer) { + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": illegal position of TDC Time (before header/epoch or after trailer)"; + ctx.monitor.fNumErrWildTdcTime++; + ctx.monitor.fNumSkippedSubsubevent++; + // Currently skip the subsubevent + // TODO: Check if possible to only skip to next Epoch and continue from there + for (; i < nofTimeWords - 1; i++) { + reader.NextWord(); + } + return; + } + wasTime = true; + ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx); + break; + } + case TdcWordType::Epoch: { + if (!wasHeader || wasTrailer) { + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": illegal position of TDC Epoch (before header or after trailer)"; + ctx.monitor.fNumErrWildEpoch++; + ctx.monitor.fNumSkippedSubsubevent++; + for (; i < nofTimeWords - 1; i++) { + reader.NextWord(); + } + return; + } + wasEpoch = true; + epoch = ProcessEpoch(word); + break; + } + case TdcWordType::Header: { + if (wasEpoch || wasTime || wasTrailer) { + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": illegal position of TDC Header (after time/epoch/trailer)"; + ctx.monitor.fNumErrWildHeaderMessage++; + ctx.monitor.fNumSkippedSubsubevent++; + for (; i < nofTimeWords - 1; i++) { + reader.NextWord(); + } + return; + } + ctx.monitor.fNumErrWildHeaderMessage++; + break; + } + case TdcWordType::Trailer: { + if (!wasEpoch || !wasTime || !wasHeader) { + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": illegal position of TDC Trailer (before time/epoch/header)"; + ctx.monitor.fNumErrWildTrailerMessage++; + ctx.monitor.fNumSkippedSubsubevent++; + for (; i < nofTimeWords - 1; i++) { + reader.NextWord(); + } + return; + } + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec << ": TDC Trailer too early, " + << (nofTimeWords - 1 - i) << " word(s) before last"; + ctx.monitor.fNumErrWildTrailerMessage++; + wasTrailer = true; + break; + } + case TdcWordType::Debug: { + // for the moment do nothing + ctx.monitor.fNumDebugMessage++; + break; + } + case TdcWordType::Error: { + ctx.monitor.fNumErrTdcErrorWord++; + break; + } + } + } + + // Last word is expected to be of type "trailer" + word = reader.NextWord(); + if (GetTdcWordType(word) != TdcWordType::Trailer) { + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec << ": Last word not a TDC trailer"; + ctx.monitor.fNumErrInvalidLastMessage++; + if (GetTdcWordType(word) == TdcWordType::TimeData && !wasTrailer) { + if (ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx)) { + ctx.monitor.fNumWarnRecoveredLastDigi++; + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": Rescuing TimeData in subsubevent with missing Trailer leading to saved Digi"; + } + else { + ctx.monitor.fNumErrOrphanRecovTimeData++; + L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec + << ": Rescuing TimeData in subsubevent with missing Trailer w/o extra digi"; + } + } + } + } + + // ---- ProcessTimeDataWord ---- + bool UnpackMS::ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId, + std::vector<double>& raisingTime, MSContext& ctx) const + { + const TdcTimeData td = ProcessTimeData(tdcWord); + const double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine); + + bool madeDigi = false; + if (td.fChannel != 0) { + const double dT = fullTime - ctx.prevLastCh0ReTime[ctx.currentSubSubEvent]; + const double subtrigOffset = + (ctx.cbmTimePacket - ctx.cbmTimeMS) * 25.0; // offset of SubTrigger to MS start in ns + const double fullTimeCorr = dT - ctx.mbsCorr + subtrigOffset; + + if (td.fChannel < 1 || td.fChannel >= raisingTime.size()) { + ctx.monitor.fNumErrChannelOutOfBounds++; + return false; + } + if (td.fIsRisingEdge) { + // always store the latest raising edge. It means that in case RRFF situation only middle RF will be matched. + raisingTime[td.fChannel] = fullTimeCorr; + } + else { + if (raisingTime[td.fChannel] != -1.) { + // Matching was found, calculate ToT, if tot is in a good range -> create digi + const double ToT = fullTimeCorr - raisingTime[td.fChannel]; + if (ToT >= fToTMin && ToT <= fToTMax) { + if (fullTimeCorr >= 0.0) { + WriteOutputDigi(subSubEventId, td.fChannel, raisingTime[td.fChannel], ToT, ctx); + L_(info) << "[MUST DIGI] " + << "TRBId: 0x" << std::hex << subSubEventId << std::dec << "; channel: 0x" << std::hex + << td.fChannel << std::dec << "; raising time: " << raisingTime[td.fChannel] << "; ToT: " << ToT; + madeDigi = true; + } + } + // pair was created, set raising edge to -1. + raisingTime[td.fChannel] = -1.; + } + } + } + else { + ctx.monitor.fNumErrChannelOutOfBounds++; + } + return madeDigi; + } + + double UnpackMS::CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const + { + return ((double) epoch) * 2048. * 5. + ((double) coarse) * 5. - ((double) fine) * 0.005; + } + + void UnpackMS::WriteOutputDigi(uint32_t trbId, uint16_t channel, double time, double tot, MSContext& ctx) const + { + const double ToTCorrection = fbDoToTCorr ? fParams.fToTShifts.at(trbId)[channel] : 0.; + const double finalToT = tot - ToTCorrection; + + const double finalTime = time + static_cast<double>(ctx.refTime) - fParams.fTimeOffset; + + ctx.digis.emplace_back(trbId, channel, finalTime, finalToT); + } + + bool UnpackMS::CheckMaskedDiRICH(int32_t subSubEventId) const + { + for (unsigned int i = 0; i < fMaskedDiRICHes.size(); ++i) { + if (fMaskedDiRICHes.at(i) == subSubEventId) return true; + } + return false; + } + + TdcWordType UnpackMS::GetTdcWordType(uint32_t tdcWord) const + { + uint32_t tdcTimeDataMarker = (tdcWord >> 31) & 0x1; // 1 bit + uint32_t tdcMarker = (tdcWord >> 29) & 0x7; // 3 bits + + if (tdcTimeDataMarker == 0x1) { + // TODO: I also include tdcMarker == 0x5, some tdc time data words have this marker. Is it correct? + if (tdcMarker == 0x4 || tdcMarker == 0x5) { + return TdcWordType::TimeData; + } + else { + return TdcWordType::Error; + } + } + if (tdcMarker == 0x0) return TdcWordType::Trailer; + if (tdcMarker == 0x1) return TdcWordType::Header; + if (tdcMarker == 0x2) return TdcWordType::Debug; + if (tdcMarker == 0x3) return TdcWordType::Epoch; + return TdcWordType::Error; + } + + uint32_t UnpackMS::GetStrawUID(uint32_t fpgaID, uint32_t ch) const + { + // First 16 bits are used for the FPGA ID, then + // 8 bits unused and then 8 bits are used for the channel + return ((fpgaID << 16) | (ch & 0x00FF)); + } + + TdcTimeData UnpackMS::ProcessTimeData(uint32_t tdcWord) const + { + TdcTimeData out; + out.fCoarse = static_cast<uint32_t>(tdcWord & 0x7ff); // 11 bits + out.fIsRisingEdge = static_cast<uint32_t>((tdcWord >> 11) & 0x1); // 1 bit + out.fFine = static_cast<uint32_t>((tdcWord >> 12) & 0x3ff); // 10 bits + out.fChannel = static_cast<uint32_t>((tdcWord >> 22) & 0x7f); // 7 bits + return out; + } + + uint32_t UnpackMS::ProcessEpoch(uint32_t tdcWord) const { return static_cast<uint32_t>(tdcWord & 0xfffffff); } + + uint16_t UnpackMS::ProcessHeader(uint32_t tdcWord) const + { + return static_cast<uint16_t>(tdcWord & 0xff); //8 bits + } + + uint16_t UnpackMS::ProcessTrailer(uint32_t tdcWord) const { return static_cast<uint16_t>(tdcWord & 0xffff); } + +} // namespace cbm::algo::must diff --git a/algo/detectors/must/UnpackMS.h b/algo/detectors/must/UnpackMS.h new file mode 100644 index 0000000000000000000000000000000000000000..1532e22c28977e974073f0144232f70904c80ef0 --- /dev/null +++ b/algo/detectors/must/UnpackMS.h @@ -0,0 +1,275 @@ +/* Copyright (C) 2021-2025 Jagiellonian University, Krakow; Goethe-University Frankfurt, Frankfurt + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer], Pascal Raisig, Dominik Smith */ + +#pragma once + +#include "CbmMustDigi.h" +#include "Definitions.h" +#include "ReadoutConfig.h" +#include "Timeslice.hpp" +#include "UnpackMSBase.h" + +#include <cstddef> +#include <cstdint> +#include <iomanip> +#include <memory> +#include <sstream> +#include <utility> + +namespace cbm::algo::must +{ + + class MicrosliceReader; + + enum class TdcWordType + { + TimeData, + Header, + Epoch, + Trailer, + Debug, + Error + }; + + struct TdcTimeData { + uint32_t fCoarse = 0; // 11 bits + uint32_t fIsRisingEdge = 0; // 1 bit + uint32_t fFine = 0; // 10 bits + uint32_t fChannel = 0; // 7 bits + }; + + + /** @struct UnpackElinkPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 26 May 2023 + ** @brief RICH Unpacking parameters for one eLink / ASIC + **/ + struct UnpackElinkPar { + std::vector<double> fToTshift; ///< TOT shift for different channels + uint64_t fTimeOffset = 0.; ///< Time calibration parameter + }; + + + /** @struct UnpackPar + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 26 May 2023 + ** @brief Parameters required for the RICH unpacking (specific to one component) + **/ + struct UnpackPar { + std::vector<uint32_t> fTRBAddresses; + std::map<uint32_t, std::array<float, 33>> fToTShifts; + uint32_t fTimeOffset; + }; + + + /** @struct UnpackMonitorData + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 26 May 2023 + ** @brief Monitoring data for RICH unpacking + **/ + struct UnpackMonitorData { + uint32_t fNumDebugMessage = 0; ///< Received debug messages + uint32_t fNumCtsAndUnmappedDirich = 0; ///< Dirich address is unknown (or is of a CTS) + uint32_t fNumErrInvalidFirstMessage = 0; ///< First message is not HEADER + uint32_t fNumErrInvalidSecondMessage = 0; ///< Second message is not EPOCH + uint32_t fNumErrInvalidLastMessage = 0; ///< Last message is not TRAILER + uint32_t fNumErrWildHeaderMessage = 0; ///< TDC header in invalid position + uint32_t fNumErrWildTrailerMessage = 0; ///< TDC trailer in invalid position + uint32_t fNumErrWildEpoch = 0; ///< TDC epoch in invalid position + uint32_t fNumErrWildTdcTime = 0; ///< TDC time (digi) in invalid position + uint32_t fNumErrTdcErrorWord = 0; ///< TDC word of error type + uint32_t fNumErrChannelOutOfBounds = 0; ///< TDC channel out of bounds + uint32_t fNumErrOrphanRecovTimeData = 0; ///< TimeData in last position (not TRAILER) but not creating a Digi + uint32_t fNumWarnRecoveredLastDigi = 0; ///< TimeData in last position (not TRAILER) led to recovered Digi + uint32_t fNumErrInvalidHubId = 0; ///< "SubSubEvent" has invalid ID + uint32_t fNumErrInvalidHubSize = 0; ///< Premature end of hub block + uint32_t fNumErrExcessLastWords = 0; ///< More than expected trailing words + uint32_t fNumSkippedSubsubevent = 0; ///< Whenever subsubevent skipped due to error (but not mask) + + bool HasErrors() const + { + uint32_t numErrors = fNumDebugMessage + fNumErrInvalidFirstMessage + fNumErrInvalidSecondMessage + + fNumErrInvalidLastMessage + fNumErrWildHeaderMessage + fNumErrWildTrailerMessage + + fNumErrWildEpoch + fNumErrWildTdcTime + fNumErrTdcErrorWord + fNumErrChannelOutOfBounds + + fNumErrOrphanRecovTimeData + fNumErrInvalidHubId + fNumErrInvalidHubSize + + fNumErrExcessLastWords; + return (numErrors > 0 ? true : false); + } + std::string print() const + { + std::stringstream ss; + ss << "errors " << fNumDebugMessage << " | " << fNumCtsAndUnmappedDirich << " | " << fNumErrInvalidFirstMessage + << " | " << fNumErrInvalidSecondMessage << " | " << fNumErrInvalidLastMessage << " | " + << fNumErrWildHeaderMessage << " | " << fNumErrWildTrailerMessage << " | " << fNumErrWildEpoch << " | " + << fNumErrWildTdcTime << " | " << fNumErrTdcErrorWord << " | " << fNumErrChannelOutOfBounds << " | " + << fNumErrOrphanRecovTimeData << " | " << fNumWarnRecoveredLastDigi << " | " << fNumErrInvalidHubId << " | " + << fNumErrInvalidHubSize << " | " << fNumErrExcessLastWords << " | " << fNumSkippedSubsubevent; + return ss.str(); + } + }; + + /** @struct UnpackAux + ** @author Dominik Smith <d.smith@gsi.de> + ** @since 24 May 2024 + ** @brief Auxiliary data for BMON unpacking + **/ + struct UnpackAuxData { + ///// TO BE FILLED + }; + + class UnpackMS : public UnpackMSBase<CbmMustDigi, UnpackMonitorData, UnpackAuxData> { + + public: + UnpackMS() = delete; + + /** @brief Construct from parameters **/ + UnpackMS(const UnpackPar& pars); + + + /** @brief Destructor **/ + ~UnpackMS() override; + + + /** @brief Algorithm execution + ** @param msContent Microslice payload + ** @param msDescr Microslice descriptor + ** @param tTimeslice Unix start time of timeslice [ns] + ** @return RICH digi data + **/ + Result_t operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) const override; + + /** @brief Set the parameter container + ** @param params Pointer to parameter container + **/ + void SetParams(std::unique_ptr<UnpackPar> params) { fParams = *(std::move(params)); } + + private: + struct MSContext { + u64 cbmTimeMS = 0; // CbmTime of MS. Used to get time offset of subtriggers to MS start + u64 cbmTimePacket = 0; + u64 refTime = 0; + + // FIXME: Magic number, should be named. + // + // Comment from RICH expert (Martin Beyer): + // > This number will also be valid for the Cbm Rich, 1 Rich backplane contains max. 12 DiRICHes. + // > For the Cbm Rich the number of backplanes will be scaled + // > up. Nevertheless it is very likely that fewer DiRiches(maybe not constant) + // > per backplane are used in the day1 setup. + double prevLastCh0ReTime[13]; // 12 DiRICHes chnl0 + 1 CTS chnl0 + double mbsCorr = 0.; + + uint16_t currentSubSubEvent = 0; + + std::vector<CbmMustDigi> digis; + UnpackMonitorData monitor; + }; + + void ProcessTrbPacket(MicrosliceReader& reader, MSContext& ctx) const; + + void ProcessHubBlock(MicrosliceReader& reader, MSContext& ctx) const; + + int ProcessCtsHeader(MicrosliceReader& reader, uint32_t subSubEventSize, uint32_t subSubEventId) const; + + void ProcessSubSubEvent(MicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId, MSContext& ctx) const; + + bool ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId, std::vector<double>& raisingTime, + MSContext& ctx) const; + + TdcWordType GetTdcWordType(uint32_t tdcWord) const; + + TdcTimeData ProcessTimeData(uint32_t tdcWord) const; + + uint32_t ProcessEpoch(uint32_t tdcWord) const; + + uint16_t ProcessHeader(uint32_t tdcWord) const; + + uint16_t ProcessTrailer(uint32_t tdcWord) const; + + void WriteOutputDigi(uint32_t trbId, uint16_t channel, double time, double tot, MSContext& ctx) const; + + double CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const; + + uint32_t GetStrawUID(uint32_t fpgaID, uint32_t ch) const; + + bool CheckMaskedDiRICH(int32_t subSubEventId) const; + + UnpackPar fParams; ///< Parameter container + + std::vector<int32_t> fMaskedDiRICHes; + + bool fbDoToTCorr = true; //activates ToT correction from parameter file + + // uint64_t fCbmTimeMS; + // uint64_t fCbmTimePacket; + + // double fMbsCorr = 0.; + // double fPrevLastCh0ReTime[13]; // 12 DiRICHes chnl0 + 1 CTS chnl0 + // uint16_t fCurrentSubSubEvent = 0; + + double fToTMin = -20.; + double fToTMax = 100.; + }; + + + class MicrosliceReader { + private: + const uint8_t* fData = nullptr; + size_t fSize = 0; + size_t fOffset = 0; // offset in bytes + size_t fWordCounter = 0; + uint32_t fCurWord = 0; + + public: + void SetData(const uint8_t* data, size_t size) + { + fData = data; + fSize = size; + fOffset = 0; + fWordCounter = 0; + fCurWord = 0; + } + + const uint8_t* GetData() { return fData; } + size_t GetSize() { return fSize; } + size_t GetOffset() { return fOffset; } + size_t GetWordCounter() { return fWordCounter; } + uint32_t GetCurWord() { return fCurWord; } + + std::string GetWordAsHexString(uint32_t word) + { + std::stringstream stream; + stream << "0x" << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << word; + return stream.str(); + } + + uint32_t NextWord() + { + uint32_t i = ((uint32_t*) (fData + fOffset))[0]; + //swap bytes + i = (i >> 24) | ((i << 8) & 0x00FF0000) | ((i >> 8) & 0x0000FF00) | (i << 24); + fOffset += 4; + fWordCounter++; + fCurWord = i; + return i; + } + + bool IsNextPadding() + { + uint32_t nextWord = ((uint32_t*) (fData + fOffset))[0]; + if (nextWord == 0xffffffff) return true; + return false; + } + + bool IsLastSubSubEvent(uint32_t subSubEventSize) + { + uint32_t i = ((uint32_t*) (fData + fOffset + 4 * subSubEventSize))[0]; + i = (i >> 24) | ((i << 8) & 0x00ff0000) | ((i >> 8) & 0x0000ff00) | (i << 24); + if (i == 0x00015555) return true; + return false; + } + }; + +} // namespace cbm::algo::must diff --git a/algo/detectors/rich/ReadoutConfig.cxx b/algo/detectors/rich/ReadoutConfig.cxx index cddc2ed1cd7da9b05650381257b185372b702f34..5d74b68d130cf7230b08852fb188c362ecfeae89 100644 --- a/algo/detectors/rich/ReadoutConfig.cxx +++ b/algo/detectors/rich/ReadoutConfig.cxx @@ -65,7 +65,7 @@ namespace cbm::algo::rich // Constants const uint16_t numComp = 1; // Number of components - const uint16_t numElinksPerComp = 87; // Number of elinks per component (74 mRICH, 5 FSD/NCAL, 8 PASTA) + const uint16_t numElinksPerComp = 79; // Number of elinks per component (74 mRICH, 5 FSD/NCAL, 8 MUST) const uint16_t numChanPerElink = 33; // Number of channels per Elink // Equipment IDs for each component @@ -73,15 +73,13 @@ namespace cbm::algo::rich uint16_t eqId[numComp] = {12289}; uint32_t TRBaddresses[numElinksPerComp] = { - 0xc000, 0xc001, 0x7000, 0x7001, 0x7010, 0x7011, 0x7020, 0x7021, 0x7030, 0x7031, 0x7040, 0x7041, 0x7050, - 0x7051, 0x7060, 0x7061, 0x7070, 0x7071, 0x7080, 0x7081, 0x7100, 0x7101, 0x7110, 0x7111, 0x7120, 0x7121, - 0x7130, 0x7131, 0x7140, 0x7141, 0x7150, 0x7151, 0x7160, 0x7161, 0x7170, 0x7171, 0x7180, 0x7181, 0x7200, - 0x7201, 0x7210, 0x7211, 0x7220, 0x7221, 0x7230, 0x7231, 0x7240, 0x7241, 0x7250, 0x7251, 0x7260, 0x7261, - 0x7270, 0x7271, 0x7280, 0x7281, 0x7300, 0x7301, 0x7310, 0x7311, 0x7320, 0x7321, 0x7330, 0x7331, 0x7340, - 0x7341, 0x7350, 0x7351, 0x7360, 0x7361, 0x7370, 0x7371, 0x7380, 0x7381, 0x7901, 0x7902, 0x7903, 0x7904, - 0x7905, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995, 0x9996, 0x9997, 0x9998}; + 0xc000, 0xc001, 0x7000, 0x7001, 0x7010, 0x7011, 0x7020, 0x7021, 0x7030, 0x7031, 0x7040, 0x7041, 0x7050, 0x7051, + 0x7060, 0x7061, 0x7070, 0x7071, 0x7080, 0x7081, 0x7100, 0x7101, 0x7110, 0x7111, 0x7120, 0x7121, 0x7130, 0x7131, + 0x7140, 0x7141, 0x7150, 0x7151, 0x7160, 0x7161, 0x7170, 0x7171, 0x7180, 0x7181, 0x7200, 0x7201, 0x7210, 0x7211, + 0x7220, 0x7221, 0x7230, 0x7231, 0x7240, 0x7241, 0x7250, 0x7251, 0x7260, 0x7261, 0x7270, 0x7271, 0x7280, 0x7281, + 0x7300, 0x7301, 0x7310, 0x7311, 0x7320, 0x7321, 0x7330, 0x7331, 0x7340, 0x7341, 0x7350, 0x7351, 0x7360, 0x7361, + 0x7370, 0x7371, 0x7380, 0x7381, 0x7901, 0x7902, 0x7903, 0x7904, 0x7905}; // TRBaddresses 0x7901 and 0x7902 are for FSD/NCAL - // TRBaddresses 0x9991 to 0x9998 are for PASTA double ToTshifts[numElinksPerComp][numChanPerElink] = { {0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, @@ -316,30 +314,6 @@ namespace cbm::algo::rich {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, - {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, - 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}, {0.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}}; diff --git a/algo/global/ParFiles.cxx b/algo/global/ParFiles.cxx index 9d4e8ee2cde9139b0cc765ea92b5f97d62025d66..0c37f2781d2b7430472f9e35b8b142a116908499 100644 --- a/algo/global/ParFiles.cxx +++ b/algo/global/ParFiles.cxx @@ -119,6 +119,8 @@ ParFiles::ParFiles(uint32_t runId) trd.hitfinder = "mcbm2025_02/TrdHitfinderPar.yaml"; trd.hitfinder2d = "mcbm2025_02/TrdHitfinder2DPar.yaml"; + must.readout = "mcbm2025_02/MustReadout_mcbm2025_02.yaml"; + ca.mainConfig = "mcbm2025_02/TrackingChainConfig.yaml"; kfp.V0FinderConfig = "mcbm2025_02/kfp_lambda_v25a.yaml"; diff --git a/algo/global/ParFiles.h b/algo/global/ParFiles.h index 0fe7b3094b1e25eaa0d7f3ccb207a6977f5d2836..b95f695782607f47ef3be2b30c48f7039d04cd34 100644 --- a/algo/global/ParFiles.h +++ b/algo/global/ParFiles.h @@ -55,6 +55,10 @@ namespace cbm::algo fs::path mainConfig; } ca; + struct { + fs::path readout; + } must; + struct { fs::path V0FinderConfig; } kfp; diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx index 0f192df1b6c68934269a1eea7e5561b73dd6aa5d..e5f5a0375ae4ec8855300dbb31801ac5d9732d21 100644 --- a/algo/global/Reco.cxx +++ b/algo/global/Reco.cxx @@ -10,6 +10,7 @@ #include "EventbuildChain.h" #include "Exceptions.h" #include "HistogramSender.h" +#include "MustDigiQa.h" #include "ParFiles.h" #include "RecoGeneralQa.h" #include "StsDigiQa.h" @@ -24,6 +25,7 @@ #include "evbuild/Config.h" #include "kfp/KfpV0FinderChain.h" #include "much/Unpack.h" +#include "must/Unpack.h" #include "qa/QaManager.h" #include "qa/hitfind/BmonHitfindQa.h" #include "rich/Unpack.h" @@ -140,8 +142,18 @@ void Reco::Init(const Options& opts) } if (Opts().Has(Subsystem::RICH) && Opts().Has(Step::Unpack)) { - rich::ReadoutConfig cfg{}; - fRichUnpack = std::make_unique<rich::Unpack>(cfg); + rich::ReadoutConfig richCfg{}; + fRichUnpack = std::make_unique<rich::Unpack>(richCfg); + + //TODO handle under fles::Subsystem::MUST when available + if (parFiles.setup == Setup::mCBM2025_02) { + must::ReadoutConfig mustCfg = yaml::ReadFromFile<must::ReadoutConfig>(Opts().ParamsDir() / parFiles.must.readout); + fMustUnpack = std::make_unique<must::Unpack>(mustCfg); + if (fSender != nullptr && Opts().Has(QaStep::UnpackMust)) { + fMustDigiQa = std::make_unique<must::DigiQa>(fSender, mustCfg); + fMustDigiQa->Init(); + } + } } if (Opts().Has(Subsystem::STS) && Opts().Has(Step::Unpack)) { @@ -320,6 +332,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts) std::tie(digis.fBmon, auxDigis.fBmon) = RunUnpacker(fBmonUnpack, ts); std::tie(digis.fMuch, auxDigis.fMuch) = RunUnpacker(fMuchUnpack, ts); std::tie(digis.fRich, auxDigis.fRich) = RunUnpacker(fRichUnpack, ts); + std::tie(digis.fMust, auxDigis.fMust) = RunUnpacker(fMustUnpack, ts); std::tie(digis.fSts, auxDigis.fSts) = RunUnpacker(fStsUnpack, ts); std::tie(digis.fTof, auxDigis.fTof) = RunUnpacker(fTofUnpack, ts); std::tie(digis.fTrd, auxDigis.fTrd) = RunUnpacker(fTrdUnpack, ts); @@ -332,8 +345,13 @@ RecoResults Reco::Run(const fles::Timeslice& ts) L_(info) << "TS contains Digis: STS=" << digis.fSts.size() << " MUCH=" << digis.fMuch.size() << " TOF=" << digis.fTof.size() << " BMON=" << digis.fBmon.size() << " TRD=" << digis.fTrd.size() << " TRD2D=" << digis.fTrd2d.size() << " RICH=" << digis.fRich.size() << " PSD=" << digis.fPsd.size() - << " FSD=" << digis.fFsd.size(); + << " FSD=" << digis.fFsd.size() << " MUST=" << digis.fMust.size(); // --- Raw digi QAs + + if (fSender != nullptr && Opts().Has(QaStep::UnpackMust)) { + fMustDigiQa->Exec(digis.fMust, ts.index()); + } + if (fSender != nullptr && Opts().Has(Subsystem::STS)) { fStsDigiQa->RegisterDigiData(&digis.fSts); fStsDigiQa->RegisterAuxDigiData(&auxDigis.fSts); @@ -444,6 +462,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts) results.trdDigis = std::move(digis.fTrd); results.tofDigis = std::move(digis.fTof); results.richDigis = std::move(digis.fRich); + results.mustDigis = std::move(digis.fMust); } if (Opts().HasOutput(RecoData::Track)) { results.tracks = std::move(recoData.tracks); diff --git a/algo/global/Reco.h b/algo/global/Reco.h index 35cb5c34626c8dc84ba994478e83a305522cfd98..ad2a272d2e5a23194f7daa4f22af9fb7f6b4ce7c 100644 --- a/algo/global/Reco.h +++ b/algo/global/Reco.h @@ -46,6 +46,12 @@ namespace cbm::algo class Unpack; } + namespace must + { + class Unpack; + class DigiQa; + } // namespace must + namespace sts { class Unpack; @@ -166,6 +172,10 @@ namespace cbm::algo // RICH std::unique_ptr<rich::Unpack> fRichUnpack; + // MUST + std::unique_ptr<must::Unpack> fMustUnpack; + std::unique_ptr<must::DigiQa> fMustDigiQa; ///< Raw MUST-digis QA + // STS std::unique_ptr<sts::Unpack> fStsUnpack; std::unique_ptr<sts::DigiQa> fStsDigiQa; ///< Raw STS-digis QA diff --git a/algo/global/RecoResults.h b/algo/global/RecoResults.h index 3bcee8be6acd66388ab8e0043313888c0f35c615..4a0cf6574dccdc2df468de8ffb3b6ee6ccac0c0c 100644 --- a/algo/global/RecoResults.h +++ b/algo/global/RecoResults.h @@ -36,6 +36,7 @@ namespace cbm::algo PODVector<CbmTrdDigi> trdDigis; PODVector<CbmTofDigi> tofDigis; PODVector<CbmRichDigi> richDigis; + PODVector<CbmMustDigi> mustDigis; std::vector<DigiEvent> events; diff --git a/algo/global/StorableRecoResults.h b/algo/global/StorableRecoResults.h index 5560b7d1ad1998bfacc2b35e0f64335acc571820..31e08876a961e4663b585e918a960b63374f4f49 100644 --- a/algo/global/StorableRecoResults.h +++ b/algo/global/StorableRecoResults.h @@ -70,6 +70,9 @@ namespace cbm::algo std::vector<CbmRichDigi>& RichDigis() { return fRichDigis; } const std::vector<CbmRichDigi>& RichDigis() const { return fRichDigis; } + std::vector<CbmMustDigi>& MustDigis() { return fMustDigis; } + const std::vector<CbmMustDigi>& MustDigis() const { return fMustDigis; } + std::vector<CbmDigiEvent>& DigiEvents() { return fDigiEvents; } const std::vector<CbmDigiEvent>& DigiEvents() const { return fDigiEvents; } @@ -106,6 +109,7 @@ namespace cbm::algo std::vector<CbmTrdDigi> fTrdDigis; std::vector<CbmTofDigi> fTofDigis; std::vector<CbmRichDigi> fRichDigis; + std::vector<CbmMustDigi> fMustDigis; // Event builder/filter output std::vector<CbmDigiEvent> fDigiEvents; @@ -142,6 +146,7 @@ namespace cbm::algo ar& fTrdDigis; ar& fTofDigis; ar& fRichDigis; + ar& fMustDigis; ar& fDigiEvents; diff --git a/algo/qa/unpack/MustDigiQa.cxx b/algo/qa/unpack/MustDigiQa.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3d9530c63604c1036d76a18db9830251beb13ccd --- /dev/null +++ b/algo/qa/unpack/MustDigiQa.cxx @@ -0,0 +1,93 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#include "MustDigiQa.h" + +#include <fmt/format.h> + +namespace cbm::algo::must +{ + DigiQa::DigiQa(std::shared_ptr<HistogramSender> pSender, const ReadoutConfig& readoutConfig) + : fQaData{"RawDigi/MUST"} + , fpSender{pSender} + , fReadoutConfig(readoutConfig) + { + } + + void DigiQa::Init() + { + if (fpSender == nullptr) { + return; + } + + const auto& TRBIds = fReadoutConfig.GetTrbAddresses(); + + for (auto iTRB = 0; iTRB < TRBIds.size(); ++iTRB) { + const auto TRBId = TRBIds.at(iTRB); + + auto canvas = qa::CanvasConfig(fmt::format("must_digi/must_digi_vs_channel_tot_{:x}", TRBId), + fmt::format("MUST digis per channel and ToT for TRB 0x{:x}", TRBId), 2, 1); + + fChannelHists.emplace_back(fQaData.MakeObj<qa::H1D>( + fmt::format("must_digi_{:x}_channel", TRBId), + fmt::format("Digis per channel for TRB 0x{:x};channel;N_{{digis}}", TRBId), 33, 0, 33)); + auto padChannels = qa::PadConfig(); + padChannels.RegisterHistogram(fChannelHists[iTRB], "hist"); + canvas.AddPadConfig(padChannels); + + fTotHists.emplace_back(fQaData.MakeObj<qa::H1D>(fmt::format("must_digi_{:x}_tot", TRBId), + fmt::format("ToT for TRB 0x{:x};ToT;N_{{digis}}", TRBId), 30, 10, + 40)); + auto padTot = qa::PadConfig(); + padTot.RegisterHistogram(fTotHists[iTRB], "hist"); + canvas.AddPadConfig(padTot); + + fQaData.AddCanvasConfig(canvas); + } + + auto canvas = qa::CanvasConfig("must_digi/must_digi_vs_trb_channel", "MUST digis per channel in TRBs", 1, 1); + fChannel2DHist = fQaData.MakeObj<qa::H2D>("must_digi_trb_channel", "Digis per channel in TRBs", TRBIds.size(), 0, + *std::max_element(TRBIds.cbegin(), TRBIds.cend()) + - *std::min_element(TRBIds.cbegin(), TRBIds.cend()) + 1, + 33, 0, 33); + + auto padChannels2D = qa::PadConfig(); + padChannels2D.RegisterHistogram(fChannel2DHist, "hist"); + canvas.AddPadConfig(padChannels2D); + + fQaData.AddCanvasConfig(canvas); + + fQaData.Init(fpSender); + } + + void DigiQa::Exec(const PODVector<CbmMustDigi>& digis, const uint64_t tsIndex) + { + if (fpSender == nullptr) { + return; + } + + const auto minTRB = + *std::min_element(fReadoutConfig.GetTrbAddresses().cbegin(), fReadoutConfig.GetTrbAddresses().cend()); + + fQaData.SetTimesliceId(tsIndex); + + for (const auto& digi : digis) { + const auto iHist = digi.fTRBId - fReadoutConfig.GetTrbAddresses()[0]; + + if (iHist >= fChannelHists.size()) { + L_(error) << fmt::format("must::DigiQa: digi TRBId {:x} out of range", digi.fTRBId); + continue; + } + + fChannelHists[iHist]->Fill(digi.fChannel); + fTotHists[iHist]->Fill(digi.fToT); + fChannel2DHist->Fill(digi.fTRBId - minTRB, digi.fChannel); + } + + fQaData.Send(fpSender); + } + + void DigiQa::operator()(const PODVector<CbmMustDigi>& digis, const uint64_t tsIndex) { Exec(digis, tsIndex); } + +} // namespace cbm::algo::must diff --git a/algo/qa/unpack/MustDigiQa.h b/algo/qa/unpack/MustDigiQa.h new file mode 100644 index 0000000000000000000000000000000000000000..a14fd667b9dcfe60539ab62f0fe07f0d5b106b8e --- /dev/null +++ b/algo/qa/unpack/MustDigiQa.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#pragma once + +#include "CbmMustDigi.h" +#include "QaBase.h" + +#include <CommonUnpacker.h> +#include <must/ReadoutConfig.h> + +namespace cbm::algo::must +{ + /* TODO probably cbm::algo::sts::QaBase can be used here as a base class, but for some reason it is in sts namespace. + * IMO it should be only in cbm::algo and be a base for all QA classes? + * For now I'm doing similar impl/interface, but without inheritance. + */ + /// \class cbm::algo::must::DigiQa + /// \brief QA module for MUST raw digis + class DigiQa { + public: + /// \brief Main constructor with HistogramSender + DigiQa(std::shared_ptr<HistogramSender> pSender, const ReadoutConfig& readoutConfig); + + DigiQa() = delete; + + DigiQa(const DigiQa&) = delete; + + DigiQa(DigiQa&&) = delete; + + DigiQa& operator=(const DigiQa&) = delete; + + DigiQa& operator=(DigiQa&&) = delete; + + /// \brief Initializes QA (initialization of histograms and canvases) + void Init(); + + /// \brief Executes QA (filling histograms) + void Exec(const PODVector<CbmMustDigi>& digis, uint64_t tsIndex); + + /// \brief Executes QA (filling histograms), see Exec() + void operator()(const PODVector<CbmMustDigi>& digis, uint64_t tsIndex); + + private: + std::vector<qa::H1D*> fChannelHists{}; ///< histograms: nDigis per channel per TRB + std::vector<qa::H1D*> fTotHists{}; ///< histograms: ToT per TRB + qa::H2D* fChannel2DHist{}; ///< histogram: nDigis per channel per TRB + + qa::Data fQaData; + std::shared_ptr<HistogramSender> fpSender; + ReadoutConfig fReadoutConfig; + }; + +} // namespace cbm::algo::must diff --git a/core/data/CMakeLists.txt b/core/data/CMakeLists.txt index b5b59dd07855d9e7f8fb17bbd151c570757e7563..4c5f8f974da3c5d763f63ba76a381314b0c2de19 100644 --- a/core/data/CMakeLists.txt +++ b/core/data/CMakeLists.txt @@ -15,6 +15,7 @@ set(INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/rich ${CMAKE_CURRENT_SOURCE_DIR}/psd ${CMAKE_CURRENT_SOURCE_DIR}/fsd + ${CMAKE_CURRENT_SOURCE_DIR}/must ${CMAKE_CURRENT_SOURCE_DIR}/global ) @@ -66,6 +67,8 @@ set(SRCS rich/CbmRichTrbDigi.cxx rich/CbmRichDigi.cxx + must/CbmMustDigi.cxx + much/CbmMuchPixelHit.cxx much/CbmMuchPoint.cxx much/CbmMuchCluster.cxx @@ -134,8 +137,8 @@ SET_SOURCE_FILES_PROPERTIES(tof/etof/star_rhicf.c PROPERTIES COMPILE_FLAGS -Wno- list(APPEND HEADERS base/CbmDigiData.h global/CbmDigiEvent.h global/CbmDigiTimeslice.h -bmon/CbmBmonDigiData.h sts/CbmStsDigiData.h much/CbmMuchDigiData.h rich/CbmRichDigiData.h trd/CbmTrdDigiData.h -tof/CbmTofDigiData.h psd/CbmPsdDigiData.h fsd/CbmFsdDigiData.h trd/CbmTrdFexMessageSpadic.h) +bmon/CbmBmonDigiData.h sts/CbmStsDigiData.h much/CbmMuchDigiData.h rich/CbmRichDigiData.h must/CbmMustDigiData.h +trd/CbmTrdDigiData.h tof/CbmTofDigiData.h psd/CbmPsdDigiData.h fsd/CbmFsdDigiData.h trd/CbmTrdFexMessageSpadic.h) set(LIBRARY_NAME CbmData) set(LINKDEF ${LIBRARY_NAME}LinkDef.h) @@ -166,6 +169,7 @@ Install(FILES psd/CbmPsdDigiData.h fsd/CbmFsdDigiData.h rich/CbmRichDigiData.h rich/CbmRichRingLight.h + must/CbmMustDigiData.h sts/CbmStsDigiData.h trd/CbmTrdDigiData.h tof/CbmTofDigiData.h diff --git a/core/data/CbmDefs.h b/core/data/CbmDefs.h index 3b89430788a80b85447bd31f14ab5f24206a1501..59b25b519145bb7fa024710e3bbdfa923421309c 100644 --- a/core/data/CbmDefs.h +++ b/core/data/CbmDefs.h @@ -60,7 +60,8 @@ enum class ECbmModuleId kPlatform = 21, ///< RICH rail platform kCave = 22, ///< Cave kLastModule = 23, ///< For loops over all modules - kNotExist = -1 ///< If not found + kNotExist = -1, ///< If not found + kMust = 100 ///< PANDA Straws //TODO }; // operator ++ for ECbmModuleId for convenient usage in loops diff --git a/core/data/base/CbmDigiData.h b/core/data/base/CbmDigiData.h index c864e8ba6fcef44cbdfc3a1f5f4a5886e4d2428f..efae0b8cb9a4a1f5a223beddd237bbc99d201739 100644 --- a/core/data/base/CbmDigiData.h +++ b/core/data/base/CbmDigiData.h @@ -9,6 +9,7 @@ #include "CbmBmonDigiData.h" #include "CbmFsdDigiData.h" #include "CbmMuchDigiData.h" +#include "CbmMustDigiData.h" #include "CbmPsdDigiData.h" #include "CbmRichDigiData.h" #include "CbmStsDigiData.h" @@ -41,6 +42,7 @@ class CbmDigiData { CbmTofDigiData fTof; ///< TOF data CbmPsdDigiData fPsd; ///< PSD data CbmFsdDigiData fFsd; ///< FSD data + CbmMustDigiData fMust; ///< MUST data friend class boost::serialization::access; /** @brief BOOST serializer**/ @@ -57,6 +59,7 @@ class CbmDigiData { ar& fPsd; ar& fFsd; ar& fRich; + ar& fMust; } // --- ROOT serializer @@ -76,6 +79,7 @@ class CbmDigiData { fPsd.Clear(); fFsd.Clear(); fRich.Clear(); + fMust.Clear(); } /** @brief Size of detector data **/ @@ -91,6 +95,7 @@ class CbmDigiData { case ECbmModuleId::kPsd: return fPsd.Size(); break; case ECbmModuleId::kRich: return fRich.Size(); break; case ECbmModuleId::kFsd: return fFsd.Size(); break; + case ECbmModuleId::kMust: return fMust.Size(); break; default: return 0; break; } } @@ -108,6 +113,7 @@ class CbmDigiData { size += fPsd.Size() * sizeof(CbmPsdDigi); size += fFsd.Size() * sizeof(CbmFsdDigi); size += fRich.Size() * sizeof(CbmRichDigi); + size += fMust.Size() * sizeof(CbmMustDigi); return size; } }; diff --git a/core/data/must/CbmMustDigi.cxx b/core/data/must/CbmMustDigi.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2f8332a4133af53079974767f8922f242bb598df --- /dev/null +++ b/core/data/must/CbmMustDigi.cxx @@ -0,0 +1,5 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#include "CbmMustDigi.h" diff --git a/core/data/must/CbmMustDigi.h b/core/data/must/CbmMustDigi.h new file mode 100644 index 0000000000000000000000000000000000000000..b25c335ad2f5ab67d36c0b62922087f41c29f93f --- /dev/null +++ b/core/data/must/CbmMustDigi.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#ifndef CBMMUSTDIGI_H +#define CBMMUSTDIGI_H +#include <cstdint> + + +class CbmMustDigi { + public: + CbmMustDigi() = default; + + CbmMustDigi(const uint32_t trbId, const uint32_t channel, const double time, const double tot) + : fTRBId{trbId} + , fChannel{channel} + , fTime{time} + , fToT{tot} + { + } + + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + ar& fTRBId; + ar& fChannel; + ar& fTime; + ar& fToT; + } + + double GetTime() const { return fTime; } + + uint32_t fTRBId; + uint32_t fChannel; + double fTime; + double fToT; +}; + + +#endif //CBMMUSTDIGI_H diff --git a/core/data/must/CbmMustDigiData.h b/core/data/must/CbmMustDigiData.h new file mode 100644 index 0000000000000000000000000000000000000000..39b8f8267add05183af914437d43ff5e02856747 --- /dev/null +++ b/core/data/must/CbmMustDigiData.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2025 Jagiellonian University, Krakow + SPDX-License-Identifier: GPL-3.0-only + Authors: Bartosz Sobol [committer] */ + +#ifndef CBMMUSTDIGIDATA_H +#define CBMMUSTDIGIDATA_H + +#include "CbmMustDigi.h" + +#ifndef NO_ROOT +#include <Rtypes.h> // for ClassDef +#endif + +#include <boost/serialization/vector.hpp> + +class CbmMustDigiData { + public: + std::vector<CbmMustDigi> fDigis{}; + + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + ar& fDigis; + } + + void Clear() { fDigis.clear(); } + + size_t Size() const { return fDigis.size(); } + +#ifndef NO_ROOT + ClassDefNV(CbmMustDigiData, 1); +#endif +}; + +#endif //CBMMUSTDIGIDATA_H diff --git a/reco/app/cbmreco/main.cxx b/reco/app/cbmreco/main.cxx index 6dd23efc4481d3854e2f76af8d715f66528521a4..7c24c76ee7a9de28a64be731a8fe0482b051e5a2 100644 --- a/reco/app/cbmreco/main.cxx +++ b/reco/app/cbmreco/main.cxx @@ -43,6 +43,7 @@ std::shared_ptr<StorableRecoResults> makeStorableRecoResults(const fles::Timesli storable->TrdDigis() = ToStdVector(results.trdDigis); storable->TofDigis() = ToStdVector(results.tofDigis); storable->RichDigis() = ToStdVector(results.richDigis); + storable->MustDigis() = ToStdVector(results.mustDigis); storable->StsClusters() = results.stsClusters; storable->StsHits() = results.stsHits; diff --git a/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx b/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx index 5f3cd3ad47cb84757b62384dc11a30e4dc6926d0..f445694be85cd1439bba9d87f61b7f977091eff5 100644 --- a/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx +++ b/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx @@ -204,7 +204,7 @@ void CbmRichMCbmHitProducer::ProcessDigi(CbmEvent* event, Int_t digiIndex) Int_t DiRICH_Add = (digi->GetAddress() >> 16) & 0xFFFF; if ((0x7901 <= DiRICH_Add && DiRICH_Add <= 0x7905) || (0x9991 <= DiRICH_Add && DiRICH_Add <= 0x9998)) { // TRBaddresses 0x7901 to 0x7905 are for FSD/NCAL - // TRBaddresses 0x9991 to 0x9998 are for PASTA + // TRBaddresses 0x9991 to 0x9998 are for MUST return; } fNofDigis++;