diff --git a/src/libPS/IPowerSupply.cpp b/src/libPS/IPowerSupply.cpp index 4b4bc425bb656b290036434697f3cd00efd481b3..674551f6f5c2fb71aeacd5fb7d3e95f57324da95 100644 --- a/src/libPS/IPowerSupply.cpp +++ b/src/libPS/IPowerSupply.cpp @@ -1,7 +1,10 @@ #include "IPowerSupply.h" +#include <chrono> #include <iostream> #include <stdexcept> +#include <thread> + #include "Logger.h" IPowerSupply::IPowerSupply(const std::string& name, std::vector<std::string> models) @@ -75,6 +78,27 @@ bool IPowerSupply::isOn(unsigned channel) return false; } +void IPowerSupply::rampCurrentLevel(double curr, double rate, unsigned channel) +{ + if(rate<0) + throw std::runtime_error("rampCurrentLevel: ramp rate must be positive"); + + // Get starting point and direction + double currentCurrent=measureCurrent(channel); + double dir=(currentCurrent<curr)?+1:-1; // Direction of ramp + + // Ramp until you get as close as possible to desired current with + // discrete `ramp` steps + uint32_t nsteps=std::floor(std::fabs(curr-currentCurrent)/rate); + for(uint32_t i=1;i<nsteps;i++) + { + setCurrentLevel(currentCurrent+i*dir*rate, channel); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + // Set final current. The final step size should be less than rate + setCurrentLevel(curr,channel); +} + void IPowerSupply::setCurrentProtect(double cur, unsigned channel) { logger(logWARNING) << "setCurrentProtect() not implemented for this PS."; @@ -86,6 +110,27 @@ double IPowerSupply::getCurrentProtect(unsigned channel) return 0; } +void IPowerSupply::rampVoltageLevel(double volt, double rate, unsigned channel) +{ + if(rate<0) + throw std::runtime_error("rampVoltageLevel: ramp rate must be positive"); + + // Get starting point and direction + double currentVoltage=measureVoltage(channel); + double dir=(currentVoltage<volt)?+1:-1; // Direction of ramp + + // Ramp until you get as close as possible to desired voltage with + // discrete `ramp` steps + uint32_t nsteps=std::floor(std::fabs(volt-currentVoltage)/rate); + for(uint32_t i=1;i<nsteps;i++) + { + setVoltageLevel(currentVoltage+i*dir*rate, channel); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + // Set final voltage. The final step size should be less than rate + setVoltageLevel(volt, channel); +} + void IPowerSupply::setVoltageProtect(double volt, unsigned channel) { logger(logWARNING) << "setVoltageProtect() not implemented for this PS."; diff --git a/src/libPS/IPowerSupply.h b/src/libPS/IPowerSupply.h index 41e68c02f48316b34c98901396e04a56a41b41d1..d1d23b7c1899111974cbba7e2dbb5c0fbfa4571f 100644 --- a/src/libPS/IPowerSupply.h +++ b/src/libPS/IPowerSupply.h @@ -134,6 +134,18 @@ public: * @{ */ + /** \brief Ramp current of PS + * + * Slowly changes the current level every second in discrete + * `rate` steps until `cur` is reached. The number of steps + * is determined by initial current measurement. + * + * @param cur current [A] + * @param rate absolute rate of current change [A/s] + * @param channel channel (if any) + */ + virtual void rampCurrentLevel(double cur, double rate, unsigned channel = 0 ); + /** \brief Set current of PS * @param cur current [A] * @param channel channel (if any) @@ -168,6 +180,18 @@ public: /** \name Voltage Control and Measurement * @{ */ + + /** \brief Ramp voltage of PS + * + * Slowly changes the voltage level every second in discrete + * `rate` steps until `volt` is reached. The number of steps + * is determined by initial voltage measurement. + * + * @param volt taget voltage [V] + * @param rate absolute rate of voltage change [V/s] + * @param channel channel (if any) + */ + virtual void rampVoltageLevel(double cur, double rate, unsigned channel = 0); /** \brief Set voltage of PS * @param volt voltage [V] diff --git a/src/libPS/PowerSupplyChannel.cpp b/src/libPS/PowerSupplyChannel.cpp index 151f5411bb48e4142b46bb31e9c94229da5671cc..ebed59ae41bc7a5b8882a21f701c4e57c5ff39be 100644 --- a/src/libPS/PowerSupplyChannel.cpp +++ b/src/libPS/PowerSupplyChannel.cpp @@ -56,6 +56,9 @@ void PowerSupplyChannel::turnOff() bool PowerSupplyChannel::isOn() { return m_ps->isOn(m_channel); } +void PowerSupplyChannel::rampCurrentLevel(double cur, double rate) +{ m_ps->rampCurrentLevel(cur, rate, m_channel); } + void PowerSupplyChannel::setCurrentLevel(double cur) { m_ps->setCurrentLevel(cur, m_channel); } @@ -70,6 +73,9 @@ double PowerSupplyChannel::getCurrentProtect() double PowerSupplyChannel::measureCurrent() { return m_ps->measureCurrent(m_channel); } + +void PowerSupplyChannel::rampVoltageLevel(double volt, double rate) +{ m_ps->rampVoltageLevel(volt, rate, m_channel); } void PowerSupplyChannel::setVoltageLevel(double volt) { m_ps->setVoltageLevel(volt, m_channel); } diff --git a/src/libPS/PowerSupplyChannel.h b/src/libPS/PowerSupplyChannel.h index 76299b10128525adafbb29a52fc8ddbb7017f15a..6cfb12a06838d9333e02ab28e0aa15cf008bf469 100644 --- a/src/libPS/PowerSupplyChannel.h +++ b/src/libPS/PowerSupplyChannel.h @@ -80,6 +80,17 @@ public: * @{ */ + /** \brief Ramp current of PS + * + * Slowly changes the current level via every second at `rate` + * until `cur` is reached. The number of steps is determined + * by initial current measurement. + * + * @param cur current [A] + * @param rate absolute rate of current change [A/s] + */ + void rampCurrentLevel(double cur, double rate); + /** Set current of PS * @param cur current [A] */ @@ -111,6 +122,17 @@ public: * @{ */ + /** \brief Ramp voltage of PS + * + * Slowly changes the voltage level via every second at `rate` + * until `volt` is reached. The number of steps is determined + * by initial voltage measurement. + * + * @param volt voltage [V] + * @param rate absolute rate of voltage change [V/s] + */ + void rampVoltageLevel(double volt, double rate); + /** Set voltage of PS * @param volt voltage [V] */ @@ -121,8 +143,8 @@ public: */ double getVoltageLevel(); - /** Set current protection [optional] - * @param volt maximum current [A] + /** Set voltage protection [optional] + * @param volt maximum voltage [V] */ void setVoltageProtect(double volt); diff --git a/src/libPS/python.cpp b/src/libPS/python.cpp index fcea21a7f9391764b18cb02e0726b7304dac8dea..fa19de667568faceec87f829eba3b9d2dbb17dc5 100755 --- a/src/libPS/python.cpp +++ b/src/libPS/python.cpp @@ -115,6 +115,16 @@ public: channel ); } + void rampCurrentLevel(double cur, double rate, unsigned channel = 0) override { + PYBIND11_OVERLOAD_PURE( + void, + IPowerSupply, + rampCurrentLevel, + cur, + rate, + channel + ); + } void setCurrentLevel(double cur, unsigned channel = 0) override { PYBIND11_OVERLOAD_PURE( void, @@ -157,6 +167,16 @@ public: channel ); } + void rampVoltageLevel(double volt, double rate, unsigned channel = 0) override { + PYBIND11_OVERLOAD_PURE( + void, + IPowerSupply, + rampVoltageLevel, + volt, + rate, + channel + ); + } void setVoltageLevel(double volt, unsigned channel = 0) override { PYBIND11_OVERLOAD_PURE( void, @@ -245,6 +265,16 @@ public: channel ); } + void rampCurrentLevel(double cur, double rate, unsigned channel = 0) override { + PYBIND11_OVERLOAD_PURE( + void, + IPowerSupply, + rampCurrentLevel, + cur, + rate, + channel + ); + } void setCurrentLevel(double cur, unsigned channel = 0) override { PYBIND11_OVERLOAD( void, @@ -287,6 +317,16 @@ public: channel ); } + void rampVoltageLevel(double volt, double rate, unsigned channel = 0) override { + PYBIND11_OVERLOAD_PURE( + void, + IPowerSupply, + rampVoltageLevel, + volt, + rate, + channel + ); + } void setVoltageLevel(double volt, unsigned channel = 0) override { PYBIND11_OVERLOAD( void, @@ -352,6 +392,8 @@ void register_ps(py::module& m){ .def("turnOffAll", &IPowerSupply::turnOffAll) .def("isOn", &IPowerSupply::isOn, py::arg("channel") = 0) + .def("rampCurrentLevel", &IPowerSupply::rampCurrentLevel, + py::arg("current"), py::arg("rate"), py::arg("channel") = 0) .def("setCurrentLevel", &IPowerSupply::setCurrentLevel, py::arg("current"), py::arg("channel") = 0) .def("getCurrentLevel", &IPowerSupply::getCurrentLevel, @@ -362,6 +404,8 @@ void register_ps(py::module& m){ py::arg("channel") = 0) .def("measureCurrent", &IPowerSupply::measureCurrent, py::arg("channel") = 0) + .def("rampVoltageLevel", &IPowerSupply::rampVoltageLevel, + py::arg("voltage"), py::arg("rate"), py::arg("channel") = 0) .def("setVoltageLevel", &IPowerSupply::setVoltageLevel, py::arg("voltage"), py::arg("channel") = 0) .def("getVoltageLevel", &IPowerSupply::getVoltageLevel, @@ -478,10 +522,12 @@ void register_ps(py::module& m){ .def("turnOn", &PowerSupplyChannel::turnOn) .def("turnOff", &PowerSupplyChannel::turnOff) .def("isOn", &PowerSupplyChannel::isOn) + .def("rampCurrentLevel", &PowerSupplyChannel::rampCurrentLevel) .def("setCurrentLevel", &PowerSupplyChannel::setCurrentLevel) .def("getCurrentLevel", &PowerSupplyChannel::getCurrentLevel) .def("setCurrentProtect", &PowerSupplyChannel::setCurrentProtect) .def("measureCurrent", &PowerSupplyChannel::measureCurrent) + .def("rampVoltageLevel", &PowerSupplyChannel::rampVoltageLevel) .def("setVoltageLevel", &PowerSupplyChannel::setVoltageLevel) .def("getVoltageLevel", &PowerSupplyChannel::getVoltageLevel) .def("setVoltageProtect", &PowerSupplyChannel::setVoltageProtect) diff --git a/src/tools/powersupply.cpp b/src/tools/powersupply.cpp index f95d0fc17636fb590fd3f51197d030cf8adc00e3..0d3cc7cefaf4464c0de60c56a347a0a800d7555a 100644 --- a/src/tools/powersupply.cpp +++ b/src/tools/powersupply.cpp @@ -4,6 +4,7 @@ #include <unistd.h> #include <sys/types.h> +#include <chrono> #include <iostream> #include <string> #include <vector> @@ -30,15 +31,17 @@ void usage(char *argv[]) { std::cerr << "Usage: " << argv[0] << " [options] command [parameters]" << std::endl; std::cerr << "List of possible COMMAND:" << std::endl; - std::cerr << " set-current -- I [V] Set current I [A] with maximum voltage V [V]" << std::endl; - std::cerr << " get-current Get set current level [A]" << std::endl; - std::cerr << " meas-current Get reading of current [A]" << std::endl; - std::cerr << " set-voltage -- V [I] Set voltage V [V] with maximum current I [A]" << std::endl; - std::cerr << " get-voltage Get set voltage level [V]" << std::endl; - std::cerr << " meas-voltage Get reading of voltage [V]" << std::endl; - std::cerr << " program [on] Execute the program block and optionally turn on the power supply" << std::endl; - std::cerr << " power-on [V I] Power ON PS, optionally setting voltage to V in Volts and current to I in Ampere" << std::endl; - std::cerr << " power-off Power OFF PS" << std::endl; + std::cerr << " set-current -- I [V] Set current I [A] with maximum voltage V [V]" << std::endl; + std::cerr << " ramp-current -- I rate Ramp voltage I [A] at rate [A/s]" << std::endl; + std::cerr << " get-current Get set current level [A]" << std::endl; + std::cerr << " meas-current Get reading of current [A]" << std::endl; + std::cerr << " set-voltage -- V [I] Set voltage V [V] with maximum current I [A]" << std::endl; + std::cerr << " ramp-voltage -- V rate Ramp voltage V [V] at rate [V/s]" << std::endl; + std::cerr << " get-voltage Get set voltage level [V]" << std::endl; + std::cerr << " meas-voltage Get reading of voltage [V]" << std::endl; + std::cerr << " program [on] Execute the program block and optionally turn on the power supply" << std::endl; + std::cerr << " power-on [V I] Power ON PS, optionally setting voltage to V in Volts and current to I in Ampere" << std::endl; + std::cerr << " power-off Power OFF PS" << std::endl; std::cerr << std::endl; std::cerr << "List of options:" << std::endl; std::cerr << " -n, --name PS Name of the power supply from equipment list (default: " << name << ")" << std::endl; @@ -186,6 +189,18 @@ int main(int argc, char*argv[]) PS->setVoltageProtect(std::stod(params[1])); } } + else if (command == "ramp-current") + { + if (params.size() != 2) + { + logger(logERROR) << "Invalid number of parameters to ramp-current command."; + logger(logERROR) << ""; + usage(argv); + return 1; + } + logger(logDEBUG) << "Ramp current to "<< params[0] << " A at rate " << params[1] << " A/s"; + PS->rampCurrentLevel(std::stod(params[0]), std::stod(params[1])); + } else if (command == "get-current") { if (logIt::loglevel >= logDEBUG) std::cout << "Current: "; @@ -217,6 +232,18 @@ int main(int argc, char*argv[]) PS->setCurrentProtect(std::stod(params[1])); } } + else if (command == "ramp-voltage") + { + if (params.size() != 2) + { + logger(logERROR) << "Invalid number of parameters to ramp-voltage command."; + logger(logERROR) << ""; + usage(argv); + return 1; + } + logger(logDEBUG) << "Ramp voltage to "<< params[0] << " V at rate " << params[1] << " V/s"; + PS->rampVoltageLevel(std::stod(params[0]), std::stod(params[1])); + } else if (command == "get-voltage") { if (logIt::loglevel >= logDEBUG) std::cout << "Voltage: ";