diff --git a/src/libPS/CMakeLists.txt b/src/libPS/CMakeLists.txt
index 74e27b7313ca56b203a98669795f44d806f5e0a5..cf574c8dfa4c9e616cf02f4468d0b29ea7734417 100644
--- a/src/libPS/CMakeLists.txt
+++ b/src/libPS/CMakeLists.txt
@@ -12,6 +12,8 @@ target_sources(PS
   AgilentE3634APs.cpp
   AgilentE36300APs.cpp
   DT54xxPs.cpp
+  DT5471NPs.cpp
+  DT5472NPs.cpp
   SorensenPs.cpp
   RigolDP832.cpp
   Keithley24XX.cpp
diff --git a/src/libPS/DT5471NPs.cpp b/src/libPS/DT5471NPs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ed171c009839bc104c6461707dcb1b1e16f21d1e
--- /dev/null
+++ b/src/libPS/DT5471NPs.cpp
@@ -0,0 +1,16 @@
+#include "DT5471NPs.h"
+
+#include "TextSerialCom.h"
+
+#include <algorithm>
+#include <thread>
+
+#include "Logger.h"
+
+//Register power supply
+#include "PowerSupplyRegistry.h"
+REGISTER_POWERSUPPLY(DT5471NPs)
+
+DT5471NPs::DT5471NPs(const std::string& name)
+: DT54xxPs(name, {"DT5471"}, Polarity::Negative, 51e-6)
+{ }
diff --git a/src/libPS/DT5471NPs.h b/src/libPS/DT5471NPs.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b94cc42e4f041a8f1d9412b6793f28b25d8b430
--- /dev/null
+++ b/src/libPS/DT5471NPs.h
@@ -0,0 +1,20 @@
+#ifndef DT5471NPS_H
+#define DT5471NPS_H
+
+#include "DT54xxPs.h"
+
+//! \brief Implementation for the CAEN DT5471N power supplies
+/**
+ * [Reference](https://www.caen.it/products/dt5471/)
+ *
+ * Note: This is the negative output voltage variation.
+ */
+class DT5471NPs : public DT54xxPs
+{
+public:
+  DT5471NPs(const std::string& name);
+  ~DT5471NPs() =default;
+};
+
+#endif // DT5471NPS_H
+
diff --git a/src/libPS/DT5472NPs.cpp b/src/libPS/DT5472NPs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a609acc79859cafe1ce7782db04dc88598640086
--- /dev/null
+++ b/src/libPS/DT5472NPs.cpp
@@ -0,0 +1,16 @@
+#include "DT5472NPs.h"
+
+#include "TextSerialCom.h"
+
+#include <algorithm>
+#include <thread>
+
+#include "Logger.h"
+
+//Register power supply
+#include "PowerSupplyRegistry.h"
+REGISTER_POWERSUPPLY(DT5472NPs)
+
+DT5472NPs::DT5472NPs(const std::string& name)
+: DT54xxPs(name, {"DT5472"}, Polarity::Negative, 105e-6)
+{ }
diff --git a/src/libPS/DT5472NPs.h b/src/libPS/DT5472NPs.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e48d9687a04d78a4a5f5398755121640bd0d2e6
--- /dev/null
+++ b/src/libPS/DT5472NPs.h
@@ -0,0 +1,20 @@
+#ifndef DT5472NPS_H
+#define DT5472NPS_H
+
+#include "DT54xxPs.h"
+
+//! \brief Implementation for the CAEN DT5472N power supplies
+/**
+ * [Reference](https://www.caen.it/products/dt5472/)
+ *
+ * Note: This is the negative output voltage variation.
+ */
+class DT5472NPs : public DT54xxPs
+{
+public:
+  DT5472NPs(const std::string& name);
+  ~DT5472NPs() =default;
+};
+
+#endif // DT5472NPS_H
+
diff --git a/src/libPS/DT54xxPs.cpp b/src/libPS/DT54xxPs.cpp
index b6825744bc1e2ff113eda40cfd8c8e6709e7c01a..a934cb404394fd40b8d16aabd0f19c2550a05227 100644
--- a/src/libPS/DT54xxPs.cpp
+++ b/src/libPS/DT54xxPs.cpp
@@ -11,8 +11,8 @@
 #include "PowerSupplyRegistry.h"
 REGISTER_POWERSUPPLY(DT54xxPs)
 
-DT54xxPs::DT54xxPs(const std::string& name) :
-IPowerSupply(name, {"DT5472"})
+DT54xxPs::DT54xxPs(const std::string& name, const std::vector<std::string>& models, Polarity output, double imaxl)
+: IPowerSupply(name, models), m_output(output), m_imaxl(imaxl)
 { }
 
 void DT54xxPs::reset()
@@ -37,18 +37,21 @@ bool DT54xxPs::ping()
   return !result.empty();
 }
 
+void DT54xxPs::checkCompatibilityList()
+{
+  IPowerSupply::checkCompatibilityList();
+
+  Polarity pol=polarity();
+  if(pol!=m_output)
+    throw std::runtime_error("Wrong polarity detected");
+}
+
 void DT54xxPs::turnOn(unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
   command("SET","ON");
-
-  while(status(channel)&Status::RampingUp)
-    {
-      double volt=measureVoltage(channel);
-      logger(logDEBUG) << __PRETTY_FUNCTION__ << " -> ramping up: " << volt << "V";
-      std::this_thread::sleep_for(std::chrono::seconds(1));
-    }
+  waitRamp();
 }
 
 void DT54xxPs::turnOff(unsigned channel)
@@ -56,79 +59,88 @@ void DT54xxPs::turnOff(unsigned channel)
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
   command("SET","OFF");
-
-  while(status(channel)&Status::RampingDown)
-    {
-      double volt=measureVoltage(channel);
-      logger(logDEBUG) << __PRETTY_FUNCTION__ << " -> ramping down: " << volt << "V";
-      std::this_thread::sleep_for(std::chrono::seconds(1));
-    }
+  waitRamp();
 }
 
 void DT54xxPs::setCurrentLevel(double cur, unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
+  cur=checkPolarity(cur);
+
+  // Set the optimal range for the requested maximum current
+  if(cur<m_imaxl) // set low range for small currents
+    setIMonRange(IMonRange::Low , channel);
+  else // use high range for large currents
+    setIMonRange(IMonRange::High, channel);
+
+  // Set the maximum current
   command("SET", "ISET", std::to_string(cur*1e6));
 }
 
 double DT54xxPs::getCurrentLevel(unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
-  return std::stod(command("MON","ISET"))/1e6;
+
+  return convertPolarity(std::stod(command("MON","ISET"))/1e6);
 }
 
 void DT54xxPs::setCurrentProtect(double maxcur, unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
-  setCurrentLevel(maxcur, channel);
+
+  setCurrentLevel(convertPolarity(maxcur), channel);
 }
 
 double DT54xxPs::getCurrentProtect(unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
-  return getCurrentLevel(channel);
+  return std::fabs(getCurrentLevel(channel));
 }
 
 double DT54xxPs::measureCurrent(unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
-  return std::stod(command("MON","IMON"))/1e6;
+  return convertPolarity(std::stod(command("MON","IMON"))/1e6);
 }
 
 void DT54xxPs::setVoltageLevel(double volt, unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
-  command("SET", "VSET", std::to_string(volt));
+
+  command("SET", "VSET", std::to_string(checkPolarity(volt)));
+  waitRamp();
 }
 
 double DT54xxPs::getVoltageLevel(unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
-  return std::stod(command("MON","VSET"));
+  return convertPolarity(std::stod(command("MON","VSET")));
 }
 
 void DT54xxPs::setVoltageProtect(double maxvolt, unsigned channel)
 {
+  if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
-  setVoltageLevel(maxvolt, channel);
-
+  setVoltageLevel(convertPolarity(maxvolt), channel);
 }
 
 double DT54xxPs::getVoltageProtect(unsigned channel)
 {
+  if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
 
-  return getVoltageLevel(channel);
+  return std::fabs(getVoltageLevel(channel));
 }
 
 double DT54xxPs::measureVoltage(unsigned channel)
 {
   if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
-  return std::stod(command("MON","VMON"));
+
+  return convertPolarity(std::stod(command("MON","VMON")));
 }
 
 uint16_t DT54xxPs::status(unsigned channel)
@@ -138,6 +150,41 @@ uint16_t DT54xxPs::status(unsigned channel)
   return std::stoi(command("MON","STAT"))&0xFFFF;
 }
 
+DT54xxPs::Polarity DT54xxPs::polarity(unsigned channel)
+{
+  if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
+
+  std::string polstr=command("MON","POLARITY");
+  if(polstr=="-") return Polarity::Negative;
+  else return Polarity::Positive;
+}
+
+void DT54xxPs::setIMonRange(IMonRange range, unsigned channel)
+{
+  if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
+
+  command("SET", "IMRANGE", (range==IMonRange::Low)?"LOW":"HIGH");
+}
+
+DT54xxPs::IMonRange DT54xxPs::getIMonRange(unsigned channel)
+{
+  if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
+
+  return (command("MON", "IMRANGE")=="LOW") ? IMonRange::Low : IMonRange::High;
+}
+
+void DT54xxPs::waitRamp(unsigned channel)
+{
+  if (channel != 1) throw std::runtime_error("Set the channel to 1 for single channel power-supply");
+
+  do
+    {
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      logger(logDEBUG) << __PRETTY_FUNCTION__ << " -> ramping: " << measureVoltage(channel) << "V";
+    }
+  while( status(channel)&(Status::RampingUp|Status::RampingDown) );
+}
+
 std::string DT54xxPs::command(const std::string& cmd, const std::string& par, const std::string& value)
 {
   // Build command
@@ -173,11 +220,25 @@ std::string DT54xxPs::command(const std::string& cmd, const std::string& par, co
     }
 
   if(cmdvalue.empty())
-    throw "DT54xx: No CMD in return statement :(";
+    throw std::runtime_error("DT54xx: No CMD in return statement :(");
 
   if(cmdvalue=="ERR")
-    throw "DT54xx:: CMD shows an error :(";
+    throw std::runtime_error("DT54xx: CMD shows an error :(");
 
   return retvalue;
 }
 
+double DT54xxPs::checkPolarity(double input)
+{ 
+  if(m_output==Polarity::Negative && input>0)
+    throw std::runtime_error("Specified positive output value for a power supply that only supports negative output.");
+  if(m_output==Polarity::Positive && input<0)
+    throw std::runtime_error("Specified negative output value for a power supply that only supports positive output.");
+
+  return std::fabs(input);
+}
+
+double DT54xxPs::convertPolarity(double value)
+{
+  return (m_output==Polarity::Negative)? -value : value ;
+}
diff --git a/src/libPS/DT54xxPs.h b/src/libPS/DT54xxPs.h
index ed03f6b955bf5d915b8c7a912407d6bbf5acd3ee..406c7565d08bfd1b70905a215b6363f44777e903 100644
--- a/src/libPS/DT54xxPs.h
+++ b/src/libPS/DT54xxPs.h
@@ -8,12 +8,24 @@
 
 #include "IPowerSupply.h"
 
-/** \brief CAEN DT54xx
- * CAEN DT54xx USB High Voltage Power Supplies
+//! \brief Base implementation for the CAEN DT54xx power supplies
+/**
+ * CAEN DT54xx USB High Voltage Power Supplies.
  *
  * [Programming Manual](https://www.caen.it/products/dt5472/)
  *
- * Assumes direct USB connection.
+ * This class implements the command set from the programming manual. It
+ * contains a few configurable checks (ie: polarity) that are specific to
+ * a given variation of the DT54xx power supplies. There are sub-classes
+ * that correctly configure the checks. It is recommended that the user
+ * only uses those (ie: `DT54xxNPs` for the negative output variation).
+ *
+ * The implementation also performs auto-ranging for the current monitor. See
+ * the `setCurrentProtect` method for how this is achieved.
+ *
+ * *Warning:* The DT54xx power supplies are quite slow when updating the
+ * monitoring information. Tests show that monitoring information is updated
+ * roughly every 1s.
  */
 class DT54xxPs : public IPowerSupply
 {
@@ -39,7 +51,32 @@ public:
       CalError   =(1<<13)  // 1 : Calibration Error
     };
 
-  DT54xxPs(const std::string& name);
+  /**
+   * Polarity of the power supply output
+   */
+  enum Polarity
+    {
+      Positive,
+      Negative
+    };
+
+  /**
+   * Range of the current monitor
+   */
+  enum IMonRange
+    {
+      High,
+      Low
+    };
+
+  //! \brief Constructor that configures checks for a specific collection of models
+  /**
+   * @param name `IPowerSupply` instance name
+   * @param models List of supported model names (see `IPowerSupply::checkCompatibilityList`)
+   * @param output The output voltage is negative
+   * @param imaxl Maximum current [A] for low IMon range
+   */
+  DT54xxPs(const std::string& name, const std::vector<std::string>& models={}, Polarity output=Polarity::Positive, double imaxl=105e-6);
   ~DT54xxPs() =default;
 
   /** \name Communication
@@ -50,6 +87,12 @@ public:
 
   virtual std::string identify();
 
+  /**
+   * In addition to the standard model check from `IPowerSupply`, this also
+   * checks that the `output` setting is correct.
+   */
+  virtual void checkCompatibilityList();
+
   /** @} */
 
   /** \name Power Supply Control
@@ -79,11 +122,31 @@ public:
    * @{
    */
 
-  virtual void   setCurrentLevel(double cur, unsigned channel = 0);
-  virtual double getCurrentLevel(unsigned channel = 0);
-  virtual void   setCurrentProtect(double maxcur , unsigned channel = 0);
-  virtual double getCurrentProtect(unsigned channel = 0);  
-  virtual double measureCurrent(unsigned channel = 0);
+  /**
+   * Calls `setCurrentProtect` with `cur` as the maximum level.
+   */
+  virtual void   setCurrentLevel(double cur, unsigned channel = 1);
+  virtual double getCurrentLevel(unsigned channel = 1);
+
+  //! \brief Set current protection
+  /**
+   * The current monitor range is also automatically set to match up
+   * with the maximum current (`maxcur`). If `maxcur` is below the maximum
+   * of the low IMon range (`imaxl`, see constructor), then low Imon range
+   * is used. Otherwise the high IMon range is used.
+   *
+   * @param maxcur maximum current (absolute value) [A]
+   * @param channel channel (if any)
+   */
+  virtual void   setCurrentProtect(double maxcur , unsigned channel = 1);
+
+  //! \brief Get current protection
+  /**
+   * @param channel channel (if any)
+   * @return maximum current (absolute value) [A]
+   */
+  virtual double getCurrentProtect(unsigned channel = 1);  
+  virtual double measureCurrent(unsigned channel = 1);
 
   /** @} */
 
@@ -91,11 +154,33 @@ public:
    * @{
    */
 
-  virtual void   setVoltageLevel(double volt, unsigned channel = 0);
-  virtual double getVoltageLevel(unsigned channel = 0);
-  virtual void   setVoltageProtect(double maxvolt , unsigned channel = 0 );
-  virtual double getVoltageProtect(unsigned channel = 0);
-  virtual double measureVoltage(unsigned channel = 0);
+  //! \brief Set output voltage level.
+  /**
+   * For the N-type power supplies, the voltage level should be 
+   * specified as negative. This function uses the `output`
+   * property to validate the input.
+   *
+   * @param volt voltage [V]
+   * @param channel channel (if any)
+   */
+  virtual void   setVoltageLevel(double volt, unsigned channel = 1);
+  virtual double getVoltageLevel(unsigned channel = 1);
+
+  //! \brief Set voltage protection
+  /**
+   * @param maxvolt maximum voltage (absolute value) [V]
+   * @param channel channel (if any)
+   */
+  virtual void   setVoltageProtect(double maxvolt , unsigned channel = 1 );
+
+  //! \brief Get voltage protection
+  /**
+   * @param channel channel (if any)
+   * @return maximum voltage (absolute value) [V]
+   */
+  virtual double getVoltageProtect(unsigned channel = 1);
+
+  virtual double measureVoltage(unsigned channel = 1);
 
   /** @} */
 
@@ -111,25 +196,89 @@ public:
    *
    * @return status bits
    */
-  uint16_t status(unsigned channel = 0);
+  uint16_t status(unsigned channel = 1);
+
+  /**
+   * Return the polarity of the power supply.
+   *
+   * @param channel Channel to query
+   *
+   * @return polarity of the channel
+   */
+  Polarity polarity(unsigned channel = 1);
+
+  /**
+   * Set the current monitoring range
+   *
+   * @param range to set
+   */
+  void setIMonRange(IMonRange range = Low, unsigned channel = 1);
+
+  /**
+   * Get the current monitoring range
+   *
+   * @return currently set range
+   */
+  IMonRange getIMonRange(unsigned channel = 1);
+
+  //! \brief Wait for voltage ramp to complete
+  /**
+   * Monitors the status of the power supply every
+   * second until the ramp up and down bits are zero.
+   *
+   * The monitoring starts by waiting for 1 second for
+   * the status register to be updated.
+   */
+  void waitRamp(unsigned channel = 1);
 
   /** @} */
 
 private:
+  //! \brief Build a command string and parse the response
   /**
-   * Build a command string and parse the response
-   *
    * Throws an exception if any of the following errors are detected:
    * - no response 
    * - returned CMD value is ERR
    *
-   * \param cmd CMD value
-   * \param par PAR value
-   * \param value VAL value (if empty, not appended)
+   * @param cmd CMD value
+   * @param par PAR value
+   * @param value VAL value (if empty, not appended)
    *
-   * \return The returned VAL value.
+   * @return The returned VAL value.
    */
   std::string command(const std::string& cmd, const std::string& par, const std::string& value="");
+
+  //! \brief Check that the input (voltage or current) has the right sign and convert to absolute
+  /**
+   * A `std::runtime_error` is thrown if a wrong sign is supplied, as determine
+   * by the `m_output` setting.
+   *
+   * The conversion to absolute value is necessary for the power supplies
+   * command protocol.
+   *
+   * @param `input` Input value to check and convert.
+   *
+   * @return Absolute value of `input`
+   */
+  double checkPolarity(double input);
+
+  //! \brief Add correct sign to value measured by power supply
+  /**
+   * Adds the correct sign to a power supply measurement based on the polarity
+   * of the model. Necessary as the communication protocol does not include the
+   * sign.
+   *
+   * @param `value` Measured value as returned by the PS.
+   *
+   * @return `value` with correct sign based on polarity.
+   */
+  double convertPolarity(double value);  
+
+  //! \brief Specify whether power supply outputs a negative voltage (N vs P model)
+  Polarity m_output=Polarity::Positive;
+
+  //! \brief Specify maximum current [A] for low IMon range
+  double m_imaxl=105e-6;
 };
 
 #endif // DT54XXPS_H
diff --git a/src/libPS/python.cpp b/src/libPS/python.cpp
index f13c1cd0a9067d58e9169ae7b5290487ac7b7e65..fcea21a7f9391764b18cb02e0726b7304dac8dea 100755
--- a/src/libPS/python.cpp
+++ b/src/libPS/python.cpp
@@ -13,6 +13,8 @@
 
 #include "Bk16XXPs.h"
 #include "DT54xxPs.h"
+#include "DT5471NPs.h"
+#include "DT5472NPs.h"
 #include "Keithley22XX.h"
 #include "Keithley24XX.h"
 #include "PowerSupplyChannel.h"
@@ -397,8 +399,11 @@ void register_ps(py::module& m){
   py::class_<DT54xxPs, PyPS<DT54xxPs>, IPowerSupply, std::shared_ptr<DT54xxPs>> py_DT54xxPs(m, "DT54xxPs");
 
   py_DT54xxPs
-    .def(py::init<const std::string &>())
-    .def("status", &DT54xxPs::status);
+    .def(py::init<const std::string&, const std::vector<std::string>&, DT54xxPs::Polarity, double >())
+    .def("status", &DT54xxPs::status)
+    .def("polarity", &DT54xxPs::polarity)
+    .def("setIMonRange", &DT54xxPs::setIMonRange)
+    .def("getIMonRange", &DT54xxPs::getIMonRange);
 
   py::enum_<DT54xxPs::Status>(py_DT54xxPs, "Status")
     .value("On", DT54xxPs::Status::On)
@@ -415,6 +420,19 @@ void register_ps(py::module& m){
     .value("Interlock", DT54xxPs::Status::Interlock)
     .value("CalError", DT54xxPs::Status::CalError);
 
+  py::enum_<DT54xxPs::Polarity>(py_DT54xxPs, "Polarity")
+    .value("Positive", DT54xxPs::Polarity::Positive)
+    .value("Negative", DT54xxPs::Polarity::Negative);
+
+  py::enum_<DT54xxPs::IMonRange>(py_DT54xxPs, "IMonRange")
+    .value("High", DT54xxPs::IMonRange::High)
+    .value("Low" , DT54xxPs::IMonRange::Low );
+
+  py::class_<DT5471NPs, DT54xxPs, std::shared_ptr<DT5471NPs>>(m, "DT5471NPs")
+    .def(py::init<const std::string &>());
+
+  py::class_<DT5472NPs, DT54xxPs, std::shared_ptr<DT5472NPs>>(m, "DT5472NPs")
+    .def(py::init<const std::string &>());
 
   py::class_<Keithley22XX, PyPS<Keithley22XX>, IPowerSupply, std::shared_ptr<Keithley22XX>>(m, "Keithley22XX")
     .def(py::init<const std::string &>());