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.;