From f9c4f980fdd86ee62d78e2b807838992f82c13af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Guti=C3=A9rrez=20Men=C3=A9ndez?= <d.gutierrezmenendez@gsi.de> Date: Tue, 26 Nov 2024 12:43:00 +0000 Subject: [PATCH] Correct STS channel mapping --- algo/detectors/sts/StsRecoUtils.h | 81 +++++++++++++++++++ algo/detectors/sts/UnpackMS.cxx | 19 +++-- algo/test/CMakeLists.txt | 1 + algo/test/_GTestChannelMapping.cxx | 125 +++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+), 10 deletions(-) create mode 100644 algo/detectors/sts/StsRecoUtils.h create mode 100644 algo/test/_GTestChannelMapping.cxx diff --git a/algo/detectors/sts/StsRecoUtils.h b/algo/detectors/sts/StsRecoUtils.h new file mode 100644 index 0000000000..bc5fc07ba9 --- /dev/null +++ b/algo/detectors/sts/StsRecoUtils.h @@ -0,0 +1,81 @@ +/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: David Gutierrez [committer], Dario Ramirez */ + +#pragma once + +#include "Definitions.h" + +#include <cassert> +#include <cstdlib> +#include <optional> + +namespace cbm::algo::sts +{ + /** @brief STS Module parameters + **/ + class Module { + public: + static constexpr u16 CHANNELS_PER_ASIC = 128; + static constexpr u16 ASICS_PER_MODULE = 16; + static constexpr u16 CHANNELS_PER_MODULE = CHANNELS_PER_ASIC * ASICS_PER_MODULE; + + static_assert(CHANNELS_PER_ASIC > 0, "ASICs must have at least one chanhel."); + static_assert(ASICS_PER_MODULE > 0, "Modules must have at least one asic."); + + static constexpr u16 ZSTRIP_ASIC = 8; + static constexpr u16 ZSTRIP_HW_CHANNEL = 127; + static constexpr u16 ZSTRIP_SW_CHANNEL = 2046; + static constexpr u16 ZSTRIP_OFFSET = ZSTRIP_SW_CHANNEL - 1022; + + /** @brief Channel HW to SW conversion + ** @param elink_chn Channel number in elink (HW) + ** @param asic_idx ASIC index in module + ** @return channel Channel number in SW + **/ + static inline std::optional<u16> ChannelInModule(const u16 elink_chn, const u16 asic_idx) + { + if (elink_chn >= CHANNELS_PER_ASIC || asic_idx >= ASICS_PER_MODULE) return std::nullopt; + + const u16 channel = + CHANNELS_PER_ASIC * asic_idx + elink_chn // n side + + (2 * asic_idx >= ASICS_PER_MODULE) + * ( // p side + CHANNELS_PER_ASIC - 1 - 2 * elink_chn // revert channels in asic + - (elink_chn & 0b1) * 2 // shift odd channels to the left + + (elink_chn == ZSTRIP_HW_CHANNEL && asic_idx == ZSTRIP_ASIC) * ZSTRIP_OFFSET // correct z-strip 1 + ); + + return (channel < CHANNELS_PER_MODULE) ? std::make_optional(channel) : std::nullopt; + } + + /** @brief Channel SW to HW conversion + ** @param channel Channel number in module (SW) + ** @param chan_per_asic Channels per ASIC + ** @param asic_per_module ASICs per module + ** @return elink_chn, asic_idx Channel number in elink (HW) and asic ASIC in module + **/ + static inline std::optional<std::pair<u16, u16>> ChannelInAsic(const u16 channel) + { + if (channel >= CHANNELS_PER_MODULE) return std::nullopt; + + auto [asic_idx, elink_chn] = + std::div(channel // n side + + (2 * channel >= CHANNELS_PER_MODULE) + * ( // p side + -(channel == ZSTRIP_SW_CHANNEL) * ZSTRIP_OFFSET // correct z-strip 1 + + (!(channel & 0b1)) * 2 // shift even channels to the right + ), + CHANNELS_PER_ASIC); + + elink_chn += (2 * asic_idx >= ASICS_PER_MODULE) + * ( // p side + CHANNELS_PER_ASIC - 1 - 2 * elink_chn // revert channels in asic + ); + + return (asic_idx < ASICS_PER_MODULE || elink_chn < CHANNELS_PER_ASIC) + ? std::make_optional(std::make_pair(elink_chn, asic_idx)) + : std::nullopt; + } + }; +} // namespace cbm::algo::sts diff --git a/algo/detectors/sts/UnpackMS.cxx b/algo/detectors/sts/UnpackMS.cxx index ad00258b2e..077e3c7ab9 100644 --- a/algo/detectors/sts/UnpackMS.cxx +++ b/algo/detectors/sts/UnpackMS.cxx @@ -5,10 +5,12 @@ #include "UnpackMS.h" #include "AlgoFairloggerCompat.h" +#include "StsRecoUtils.h" #include "StsXyterMessage.h" #include <cassert> #include <cmath> +#include <cstdint> #include <utility> #include <vector> @@ -122,18 +124,14 @@ namespace cbm::algo::sts } // --- Check for masked channel - if (!elinkPar.fChanMask.empty() && elinkPar.fChanMask[message.GetHitChannel()] == true) { + const uint16_t msg_channel = message.GetHitChannel(); + if (!elinkPar.fChanMask.empty() && elinkPar.fChanMask[msg_channel] == true) { return; } // --- Hardware-to-software address - uint32_t channel = 0; - if (asicNr < fParams.fNumAsicsPerModule / 2) { // front side (n side); channels counted upward - channel = message.GetHitChannel() + fParams.fNumChansPerAsic * asicNr; - } - else { // back side (p side); channels counted downward - channel = fParams.fNumChansPerAsic * (asicNr + 1) - message.GetHitChannel() - 1; - } + const auto maybe_channel = Module::ChannelInModule(msg_channel, asicNr); + if (!maybe_channel.has_value()) return; // --- Expand time stamp to time within timeslice (in clock cycle) uint64_t messageTime = message.GetHitTimeBinning() + time.currentEpochTime; @@ -159,10 +157,11 @@ namespace cbm::algo::sts } // --- Create output digi - digiVec.emplace_back(elinkPar.fAddress, channel, messageTime, charge); + digiVec.emplace_back(elinkPar.fAddress, *maybe_channel, messageTime, charge); if (fParams.fWriteAux) { - aux.fQaDigis.emplace_back(message.IsHitMissedEvts(), elinkPar.fAddress, channel, messageTime, charge, elink); + aux.fQaDigis.emplace_back(message.IsHitMissedEvts(), elinkPar.fAddress, *maybe_channel, messageTime, charge, + elink); } } // -------------------------------------------------------------------------- diff --git a/algo/test/CMakeLists.txt b/algo/test/CMakeLists.txt index a0c7cde46b..e4ab499ebf 100644 --- a/algo/test/CMakeLists.txt +++ b/algo/test/CMakeLists.txt @@ -22,6 +22,7 @@ AddBasicTest(_GTestYamlConfig) AddBasicTest(_GTestPartitionedVector) AddBasicTest(_GTestPartitionedSpan) AddBasicTest(_GTestTrdClusterizer) +AddBasicTest(_GTestChannelMapping) if (DEFINED ENV{RAW_DATA_PATH}) set(RAW_DATA_PATH $ENV{RAW_DATA_PATH}) diff --git a/algo/test/_GTestChannelMapping.cxx b/algo/test/_GTestChannelMapping.cxx new file mode 100644 index 0000000000..754d3b911b --- /dev/null +++ b/algo/test/_GTestChannelMapping.cxx @@ -0,0 +1,125 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: David Gutierrez [committer], Dario Ramirez */ + +#include "Definitions.h" +#include "detectors/sts/StsRecoUtils.h" + +#include <array> +#include <optional> + +#include <gtest.h> +#include <gtest/gtest-death-test.h> +#include <gtest/gtest.h> + +#define EXPECT_OPT(maybe_val1, val2) \ + ASSERT_TRUE(maybe_val1.has_value()); \ + EXPECT_EQ(*maybe_val1, val2) + +using namespace cbm::algo; +using namespace cbm::algo::sts; + +TEST(_ChannelMapping, FailOnValueLimits) +{ + EXPECT_EQ(Module::ChannelInModule(Module::CHANNELS_PER_ASIC + 1, 0), std::nullopt); + EXPECT_EQ(Module::ChannelInModule(0, Module::ASICS_PER_MODULE + 1), std::nullopt); +} + +TEST(_ChannelMapping, ReverseFailOnValueLimits) +{ + EXPECT_EQ(Module::ChannelInAsic(Module::CHANNELS_PER_MODULE + 1), std::nullopt); +} + +TEST(_ChannelMapping, ModuleEdgesMapping) +{ + EXPECT_OPT(Module::ChannelInModule(0, 0), 0); + EXPECT_OPT(Module::ChannelInModule(127, 7), 1023); + EXPECT_OPT(Module::ChannelInModule(127, 8), 2046); + EXPECT_OPT(Module::ChannelInModule(125, 8), 1024); + EXPECT_OPT(Module::ChannelInModule(0, 15), 2047); +} + +TEST(_ChannelMapping, ReverseModuleEdgesMapping) +{ + { + const auto expect = std::make_pair<u16, u16>(0, 0); + EXPECT_OPT(Module::ChannelInAsic(0), expect); + } + { + const auto expect = std::make_pair<u16, u16>(127, 7); + EXPECT_OPT(Module::ChannelInAsic(1023), expect); + } + { + const auto expect = std::make_pair<u16, u16>(127, 8); + EXPECT_OPT(Module::ChannelInAsic(2046), expect); + } + { + const auto expect = std::make_pair<u16, u16>(125, 8); + EXPECT_OPT(Module::ChannelInAsic(1024), expect); + } + { + const auto expect = std::make_pair<u16, u16>(0, 15); + EXPECT_OPT(Module::ChannelInAsic(2047), expect); + } +} + +TEST(_ChannelMapping, ExaustiveMapping) +{ + for (u16 channel = 0; channel < Module::CHANNELS_PER_MODULE; channel++) { + const auto maybe_hw = Module::ChannelInAsic(channel); + ASSERT_TRUE(maybe_hw.has_value()); + + const auto maybe_sh = Module::ChannelInModule(maybe_hw->first, maybe_hw->second); + EXPECT_OPT(maybe_sh, channel); + } +} + +TEST(_ChannelMapping, Compatibility) +{ + for (u16 asic = 0; asic < Module::ASICS_PER_MODULE / 2; asic++) { // n side + for (u16 chn = 0; chn < Module::CHANNELS_PER_ASIC; chn++) { + const auto legacy = asic * Module::CHANNELS_PER_ASIC + chn; + const auto maybe_sw = Module::ChannelInModule(chn, asic); + EXPECT_OPT(maybe_sw, legacy); + } + } + + for (u16 asic = Module::ASICS_PER_MODULE / 2; asic < Module::ASICS_PER_MODULE; asic++) { // p side + for (u16 chn = 0; chn < Module::CHANNELS_PER_ASIC; chn += 2) { // even channels + const auto legacy = (asic + 1) * Module::CHANNELS_PER_ASIC - chn - 1; + const auto maybe_sw = Module::ChannelInModule(chn, asic); + EXPECT_OPT(maybe_sw, legacy); + } + + for (u16 chn = 1; chn < Module::CHANNELS_PER_ASIC; chn += 2) { // odd channels + const auto legacy = (asic + 1) * Module::CHANNELS_PER_ASIC - chn - 1; + const auto maybe_sw = Module::ChannelInModule(chn, asic); + + if ((asic == 8) && (chn == 127)) { // z-strip + EXPECT_OPT(maybe_sw, 2046); + EXPECT_EQ(1024, legacy); + } + else { + EXPECT_OPT(maybe_sw, legacy - 2); + } + } + } +} + +TEST(_ChannelMapping, Completness) +{ + std::array<bool, Module::CHANNELS_PER_MODULE> visited = {false}; + + for (u16 asic = 0; asic < Module::ASICS_PER_MODULE; asic++) { + for (u16 chn = 0; chn < Module::CHANNELS_PER_ASIC; chn++) { + const auto maybe_sw = Module::ChannelInModule(chn, asic); + ASSERT_TRUE(maybe_sw.has_value()); + ASSERT_LT(*maybe_sw, visited.size()); + visited[*maybe_sw] = true; + } + } + + for (const auto chn : visited) { + EXPECT_TRUE(chn); + } +} -- GitLab