Skip to content
Snippets Groups Projects
Commit f53291ea authored by Elisabetta Pianori's avatar Elisabetta Pianori
Browse files

Merge branch 'kk_littleredcaen' into 'devel'

Improvements to the DT54xxPs class

See merge request berkeleylab/labRemote!173
parents ccbc8173 75acb13d
No related branches found
No related tags found
No related merge requests found
......@@ -12,6 +12,8 @@ target_sources(PS
AgilentE3634APs.cpp
AgilentE36300APs.cpp
DT54xxPs.cpp
DT5471NPs.cpp
DT5472NPs.cpp
SorensenPs.cpp
RigolDP832.cpp
Keithley24XX.cpp
......
#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)
{ }
#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
#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)
{ }
#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
......@@ -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 ;
}
......@@ -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
......
......@@ -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 &>());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment