diff --git a/core/detectors/sts/CMakeLists.txt b/core/detectors/sts/CMakeLists.txt index 3e3d803beef86e1904d07621d70c1d3a2dbc26e7..ebe53d0f3ecb253db64c3b2348046cb223feea7d 100644 --- a/core/detectors/sts/CMakeLists.txt +++ b/core/detectors/sts/CMakeLists.txt @@ -62,3 +62,6 @@ install(FILES DESTINATION include ) +If(GTEST_FOUND) + add_subdirectory(test) +EndIf() diff --git a/core/detectors/sts/CbmStsParAsic.cxx b/core/detectors/sts/CbmStsParAsic.cxx index a69d9434fe0a7ea7899e469e892a58267ed1da7a..56ca54f56163268713cae771290449e7430cd93e 100644 --- a/core/detectors/sts/CbmStsParAsic.cxx +++ b/core/detectors/sts/CbmStsParAsic.cxx @@ -54,17 +54,6 @@ CbmStsParAsic::~CbmStsParAsic() } // ------------------------------------------------------------------------- - -// ----- ADC channel from charge ---------------------------------------- -int16_t CbmStsParAsic::ChargeToAdc(double charge) const -{ - if (charge < fThreshold) return -1; // Underflow - if (charge >= fThreshold + fDynRange) return fNofAdc - 1; // Overflow - return int16_t((charge - fThreshold) / fDynRange * double(fNofAdc)); -} -// ------------------------------------------------------------------------- - - // ----- Deactivate channels ------------------------------------------- uint16_t CbmStsParAsic::DeactivateRandomChannels(double fraction) { diff --git a/core/detectors/sts/CbmStsParAsic.h b/core/detectors/sts/CbmStsParAsic.h index 52fc3fd4493f780a39dde03d12588771146fbf95..d88e569d8b81566ba452369ec096a87a253c15b3 100644 --- a/core/detectors/sts/CbmStsParAsic.h +++ b/core/detectors/sts/CbmStsParAsic.h @@ -7,6 +7,7 @@ #include <Rtypes.h> // for THashConsistencyHolder, ClassDefNV +#include <algorithm> #include <array> #include <set> #include <string> // for string @@ -22,7 +23,7 @@ class TF1; **/ class CbmStsParAsic { -public: + public: /** @brief Default constructor **/ CbmStsParAsic() {}; @@ -57,7 +58,7 @@ public: ** @param adc ADC channel ** @return Mean charge in ADC channel [e] */ - double AdcToCharge(uint16_t adc) const { return fThreshold + fDynRange / double(fNofAdc) * (double(adc) + 0.5); } + double AdcToCharge(uint16_t adc) const { return fThreshold + fDynRange / double(fNofAdc) * (double(adc) - 0.5); } /** @brief Randomly deactivate a fraction of the channels @@ -71,9 +72,12 @@ public: ** @param charge Charge [e] ** @return ADC channel number ** - ** Returns -1 for charge below threshold. + ** Returns 0 for charge below threshold. **/ - int16_t ChargeToAdc(double charge) const; + uint16_t ChargeToAdc(double charge) const + { + return (uint16_t) std::clamp(1 + (charge - fThreshold) * fNofAdc / fDynRange, 0.0, 31.0); + }; /** @brief Single-channel dead time @@ -196,18 +200,18 @@ public: std::string ToString() const; -private: - uint16_t fNofChannels = 0; ///< Number of readout channels - uint16_t fNofAdc = 0; ///< Number of ADC channels - double fDynRange = 0.; ///< Dynamic range [e] - double fThreshold = 0.; ///< Threshold [e] - double fTimeResolution = 0.; ///< Time resolution [ns] - double fDeadTime = 0.; ///< Channel dead time [ns] - double fNoise = 0.; ///< RMS of noise [e] - double fZeroNoiseRate = 0.; ///< Zero-crossing noise rate [1/ns] - double fTimeOffset = 0.; ///< Time offset [ns] - std::array<double, 31> fWalkCoef = {}; ///< Parameters for correction of walk effect - std::set<uint16_t> fDeadChannels {}; ///< Map of dead channels + private: + uint16_t fNofChannels = 0; ///< Number of readout channels + uint16_t fNofAdc = 0; ///< Number of ADC channels + double fDynRange = 0.; ///< Dynamic range [e] + double fThreshold = 0.; ///< Threshold [e] + double fTimeResolution = 0.; ///< Time resolution [ns] + double fDeadTime = 0.; ///< Channel dead time [ns] + double fNoise = 0.; ///< RMS of noise [e] + double fZeroNoiseRate = 0.; ///< Zero-crossing noise rate [1/ns] + double fTimeOffset = 0.; ///< Time offset [ns] + std::array<double, 31> fWalkCoef = {}; ///< Parameters for correction of walk effect + std::set<uint16_t> fDeadChannels{}; ///< Map of dead channels bool fIsInit = kFALSE; //! Flag for being initialised diff --git a/core/detectors/sts/test/CMakeLists.txt b/core/detectors/sts/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fdf0035b285f7fa6ebe8c058318da52c1744cf17 --- /dev/null +++ b/core/detectors/sts/test/CMakeLists.txt @@ -0,0 +1,24 @@ +Function(AddBasicTest name) + set(PVT_DEPS + CbmData + CbmStsBase + Gtest + GtestMain + FairRoot::Base + ROOT::Core + ) + + add_executable(${name} ${name}.cxx) + target_link_libraries(${name} PRIVATE ${PVT_DEPS}) + Add_Test( + NAME ${name} + COMMAND ${name} + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) +EndFunction() + +set(INCLUDE_DIRECTORIES + ${CMAKE_SOURCE_DIR}/core/detectors/sts + ) + +AddBasicTest(_GTestCbmStsParAsic) diff --git a/core/detectors/sts/test/_GTestCbmStsParAsic.cxx b/core/detectors/sts/test/_GTestCbmStsParAsic.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4b9d02751795e7d54318a32eb3acc5af9b4c00a5 --- /dev/null +++ b/core/detectors/sts/test/_GTestCbmStsParAsic.cxx @@ -0,0 +1,47 @@ +/* Copyright (C) 2016-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: David Gutierrez [committer] */ + +#include "CbmStsParAsic.h" +#include "gtest/gtest.h" + +#include <gtest.h> + + +static const CbmStsParAsic par_asic(128, 31, 75000, 3000, 5, 800, 0, 0); + +TEST(_GTestCbmStsParAsic, ExaustiveChargeConvertion) +{ + for (uint16_t adc = 0; adc < par_asic.GetNofAdc(); adc++) { + const auto q = par_asic.AdcToCharge(adc); + const auto adc_ = par_asic.ChargeToAdc(q); + ASSERT_EQ(adc_, adc); + } +} + +TEST(_GTestCbmStsParAsic, AdcBelowLimit) +{ + const double adc = 0; + ASSERT_LT(par_asic.AdcToCharge(adc), par_asic.GetThreshold()); +} + +TEST(_GTestCbmStsParAsic, AdcAboveLimit) +{ + const double adc = 32; + ASSERT_GT(adc, par_asic.GetNofAdc()); + ASSERT_GT(par_asic.AdcToCharge(adc), par_asic.GetThreshold() + par_asic.GetDynRange()); +} + +TEST(_GTestCbmStsParAsic, ChargeBelowLimit) +{ + const double q = 2000; + ASSERT_LT(q, par_asic.GetThreshold()); + ASSERT_EQ(par_asic.ChargeToAdc(q), 0); +} + +TEST(_GTestCbmStsParAsic, ChargeAboveLimit) +{ + const double q = 80000; + ASSERT_GT(q, par_asic.GetThreshold() + par_asic.GetDynRange()); + ASSERT_EQ(par_asic.ChargeToAdc(q), 31); +} diff --git a/sim/detectors/sts/CbmStsSimModule.cxx b/sim/detectors/sts/CbmStsSimModule.cxx index 894667e1fe8fb4fb39c6442751da4ddc5d28010c..aefe0dc9317c2e7463b70136044c055b63e165e1 100644 --- a/sim/detectors/sts/CbmStsSimModule.cxx +++ b/sim/detectors/sts/CbmStsSimModule.cxx @@ -140,7 +140,7 @@ void CbmStsSimModule::Digitize(UShort_t channel, CbmStsSignal* signal) // --- Digitise charge Short_t adc = asic.ChargeToAdc(charge); - if (adc < 0) return; // Charge below threshold + if (adc < 1) return; // Charge below threshold // --- Digitise time Double_t deltaT = 0.;