Index: trunk/FACT++/src/biasctrl.cc
===================================================================
--- trunk/FACT++/src/biasctrl.cc	(revision 11926)
+++ trunk/FACT++/src/biasctrl.cc	(revision 11927)
@@ -1,3 +1,5 @@
 #include <functional>
+
+#include <boost/bind.hpp>
 
 #include "Dim.h"
@@ -25,8 +27,4 @@
 class ConnectionBias : public ConnectionUSB
 {
-    vector<uint8_t> fBuffer;
-
-    bool fIsVerbose;
-
     enum
     {
@@ -38,26 +36,56 @@
     enum Command_t
     {
-        kCmdReset      = 0,
-        kCmdRead       = 1,
-        kCmdGlobalSet  = 2,
-        kCmdChannelSet = 3,
-        kCmdPrint      = 4
+        // Communication commands
+        kCmdReset         =  0,
+        kCmdRead          =  1,
+        kCmdGlobalSet     =  2,
+        kCmdChannelSet    =  3,
+
+        // Internal command names
+        kResetChannels    = 0x10|kCmdChannelSet,
+        kUpdate           = 0x10|kCmdRead,
+        kExpertChannelSet = 0x14|kCmdChannelSet,
+        kSynchronize      = 0x1e,
+        //kOverCurReset     = 20,
     };
 
-     // Resistance in Ohm for voltage correction
-#define RESISTOR float(1000)
+    enum
+    {
+        kMaxDac = 0xfff
+    };
+
+    boost::asio::deadline_timer fSyncTimer;
+    boost::asio::deadline_timer fRampTimer;
+    boost::asio::deadline_timer fUpdateTimer;
+
+    vector<uint8_t> fBuffer;
+
+    bool fIsVerbose;
+
+    vector<uint16_t> fVoltCmd;     // Current command voltage in DAC units (12bit = 90V)
+    vector<uint16_t> fVoltGapd;
+
+    vector<bool>     fPresent;
+
+    int64_t fWrapCounter;
+    int64_t fSendCounter;
+
+    int16_t fGlobalVoltCmd;      // Command value to be reached
+//    uint16_t fExpertVoltRef;      // Command value to be reached
+
+    int16_t fRampStep;
+    int16_t fRampTime;
+
+    uint16_t fUpdateTime;
+
+    bool fIsInitializing;
+    bool fIsRamping;
+//    bool fWaitingForAnswer;
 
 protected:
-
-    vector<uint16_t> fVolt;        // Voltage in DAC units (12bit = 90V)
-    vector<uint16_t> fRefVolt;
+    vector<uint16_t> fVolt;        // Current voltage in DAC units (12bit = 90V)
+    vector<uint16_t> fVoltRef;     // Current reference voltage in DAC units (12bit = 90V)
 
     vector<int16_t>  fCurrent;     // Current in ADC units (12bit = 5mA)
-    vector<int16_t>  fRefCurrent;
-
-    vector<bool>     fPresent;
-
-    int fWrapCounter;
-
 
     virtual void UpdateA()
@@ -70,12 +98,175 @@
 
 private:
-    void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int type)
-    {
-        if (type==kCmdPrint && bytes_received==0 && !err)
-        {
-            Print();
-            return;
-        }
-
+    bool CheckChDac(const string &cmd, uint16_t dac, uint16_t ch=0)
+    {
+        if (dac>kMaxDac)
+        {
+            Error(cmd+" - DAC value out of range.");
+            return false;
+        }
+
+        if (ch>=kNumChannels)
+        {
+            Error(cmd+" - Channel out of range.");
+            return false;
+        }
+
+        return true;
+    }
+
+    vector<char> GetCmd(uint16_t board, uint16_t channel, Command_t cmd, uint16_t dac=0)
+    {
+        vector<char> data(3);
+
+        /*
+        if (board>kNumBoards)
+            return;
+        if (channel>kNumChannelsPerBoard)
+            return;
+        if (dac>0xfff)
+            return;
+        */
+
+        data[0] = (cmd<<5) | (board<<1) | (((channel&16)>>4) & 1);
+        data[1] = (channel<<4) | (dac>>8);
+        data[2] =  dac&0xff;
+
+        return data;
+    }
+
+    vector<char> GetCmd(Command_t cmd, uint16_t id=0, uint16_t dac=0)
+    {
+        const unsigned int board   = id/kNumChannelsPerBoard;
+        const unsigned int channel = id%kNumChannelsPerBoard;
+
+        return GetCmd(board, channel, cmd, dac);
+    }
+
+    bool CheckMessageLength(int received, int expected, const string &msg)
+    {
+        if (received!=expected)
+            return false;
+
+        ostringstream str;
+        str << msg << ": Expected " << expected << " bytes in answer, but got " << received << endl;
+        Error(str);
+
+        PostClose(false);
+        return false;
+    }
+
+    bool EvalAnswer(uint8_t *answer, uint16_t id, int command)
+    {
+        answer += id*3;
+
+        const uint16_t status = (answer[0]>>7)&1;
+        const uint16_t wrap   = (answer[0]>>4)&7;
+        const uint16_t ddd    = ((uint16_t(answer[0])&0xf)<<8) | answer[1];
+        const uint16_t error  = (answer[2]>>4)&0xf;
+        const uint16_t board  =  answer[2]&0xf;
+
+        if (fWrapCounter>=0)
+        {
+            if ((fWrapCounter+1)%8 != wrap)
+            {
+                ostringstream msg;
+                msg << "Corrupted answer: received wrap counter " << wrap << " is not last received counter (" << fWrapCounter << "+1)%8.";
+                Error(msg);
+                return false;
+            }
+        }
+
+        fWrapCounter = wrap;
+
+        if (command==kSynchronize)
+        {
+            ostringstream msg;
+            msg << hex << setfill('0');
+            msg << "Initial answer received:";
+            msg << " 0x" << setw(2) << answer[0];
+            msg << " 0x" << setw(2) << answer[1];
+            msg << " 0x" << setw(2) << answer[2];
+            Message(msg);
+
+            if (status!=0 || ddd!=0 || error!=0 || board==0)
+                Warn("Initial answer doesn't seem to be a reset as naively expected.");
+
+            fSendCounter = wrap;
+
+            return true;
+        }
+
+        if (error==0x8) // No device
+        {
+            Message("Reset button on crate pressed!");
+            SetZero();
+            return true;
+        }
+
+        if (command==kCmdReset)
+        {
+            if (status==0 && ddd==0 && error==0 && board==0)
+            {
+                Message("Reset successfully executed.");
+                return true;
+            }
+
+            Warn("Answer to 'reset' command contains unexpected data.");
+            return false;
+        }
+
+        if (command==kCmdGlobalSet)
+        {
+            if (status==0 && ddd==0 && error==0 && board==0)
+            {
+                for (int i=0; i<kNumChannels; i++)
+                    fVolt[i] = fGlobalVoltCmd;
+
+                fGlobalVoltCmd = -1;
+
+                return true;
+            }
+
+            Warn("Answer to 'global set' command contains unexpected data.");
+            return false;
+        }
+
+        if ((command&0xff)==kExpertChannelSet)
+            id = command>>8;
+
+        const int cmd = command&3;
+
+        if (cmd==kCmdRead || cmd==kCmdChannelSet)
+        {
+            if (board!=id/kNumChannelsPerBoard)
+            {
+                ostringstream out;
+                out << "Talked to board " << id/kNumChannelsPerBoard << ", but got answer from board " <<  board << ".";
+                Error(out);
+                return false;
+            }
+
+            // Not present
+            if (error==0x7 || error==0xf)
+            {
+                fPresent[board] = false;
+                fCurrent[id]    = 0x8000;
+                return true;
+            }
+
+            fCurrent[id]    = status ? -ddd : ddd;
+            fPresent[board] = true;
+        }
+
+        if (cmd==kCmdChannelSet)
+            fVolt[id] = fVoltCmd[id];
+
+        return true;
+
+    }
+
+private:
+    void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int command, int send_counter)
+    {
         // Do not schedule a new read if the connection failed.
         if (bytes_received==0 || err)
@@ -94,100 +285,161 @@
                 Error(str);
             }
-            PostClose(err!=ba::error::basic_errors::operation_aborted);
-            return;
-        }
-
+            PostClose(false);//err!=ba::error::basic_errors::operation_aborted);
+            return;
+        }
+
+        // Check if the number of received bytes is correctly dividable by 3
+        // This check should never fail - just for sanity
         if (bytes_received%3)
         {
             Error("Number of received bytes not a multiple of 3, can't read data.");
-            PostClose(true);
-            return;
-        }
-
+            PostClose(false);
+            return;
+        }
+
+        // Now print the received message if requested by the user
         if (fIsVerbose)
         {
-            //Out() << endl << kBold << "Data received (size=" << bytes_received << "):" << endl;
-            //Out() << Converter::GetHex<uint8_t>(fBuffer, 32) << endl;
-            // FIXME: Check why more is printed than expected
-        }
-
-        const uint16_t command = type&0xf;
-        const uint16_t id      = type>>4;
-        const uint16_t status  = (fBuffer[0]>>7)&1;
-        const uint16_t wrap    = (fBuffer[0]>>4)&7;
-        const uint16_t ddd     = ((uint16_t(fBuffer[0])&0xf)<<8) | fBuffer[1];
-        const uint16_t error   = (fBuffer[2]>>4)&0xf;
-        const uint16_t board   =  fBuffer[2]&0xf;
-
-        if (fWrapCounter>=0 && (fWrapCounter+1)%8 != wrap)
-        {
-            Error("Corrupted answer (wrap counter not as it ought to be.");
-            return;
-        }
-        fWrapCounter = wrap;
-
-        if (error==0x8) // No device
-        {
-            ostringstream out;
-            out << "HV down requested!";
-            Fatal(out);
-
-            GlobalSetDac(0);
-
-            // Resynchronize input and output
-            // SystemReset and status request
-            PostClose(true);
-
-            return;
-        }
-
-        if (command==kCmdReset)
-        {
-            if (status==0 && ddd==0 && error==0 && board==0)
+            vector<uint32_t> vout((bytes_received/3)*4);
+
+            for (size_t i=0; i<bytes_received/3; i++)
             {
-                Message("Reset successfully executed.");
+                vout[i] =
+                    (uint32_t(fBuffer[i*3+0])<<16) |
+                    (uint32_t(fBuffer[i*3+1])<< 8) |
+                    (uint32_t(fBuffer[i*3+2])<< 0);
+            }
+
+            Out() << endl << kBold << "Data received (size=" << bytes_received << "):" << endl;
+            Out() << " fWrapCounter=" << fWrapCounter << " fSendCounter=" << fSendCounter << " fIsInitializing=" << fIsInitializing << " fIsRamping=" << fIsRamping << endl;
+            Out() << Converter::GetHex<uint32_t>(vout, 16) << endl;
+        }
+
+        const int cmd = command&0xf;
+
+        // Check the number of received_byted according to the answer expected
+        if ((cmd==kSynchronize      && !CheckMessageLength(bytes_received, 3,                "Synchronization")) ||
+            (cmd==kCmdReset         && !CheckMessageLength(bytes_received, 3,                "CmdReset"))        ||
+            (cmd==kCmdRead          && !CheckMessageLength(bytes_received, 3*kNumChannels,   "CmdRead"))         ||
+            (cmd==kCmdChannelSet    && !CheckMessageLength(bytes_received, 3*kNumChannels,   "CmdChannelSet"))   ||
+            (cmd==kExpertChannelSet && !CheckMessageLength(bytes_received, 3,                "CmdExpertChannelSet")))
+            return;
+
+        // Now evaluate the whole bunch of messages
+        for (size_t i=0; i<bytes_received/3; i++)
+        {
+            if (!EvalAnswer(fBuffer.data(), i, command))
+            {
+                PostClose(false);
                 return;
             }
-
-            Warn("Answer to 'reset' command contains unexpected data.");
-            return;
-        }
-
-        if (command==kCmdGlobalSet)
-        {
-            if (status==0 && ddd==0 && error==0 && board==0)
+        }
+
+        if (send_counter%8 != fWrapCounter)
+        {
+            ostringstream msg;
+            msg << "Corrupted answer: received wrap counter " << fWrapCounter  << " is not send counter " << fSendCounter << "%8.";
+            Error(msg);
+            PostClose(false);
+        }
+
+        // Now we are ready to send a new message
+//        fWaitingForAnswer = false;
+
+        if (command==kSynchronize)
+        {
+            Message("Stream successfully synchronized.");
+            fIsInitializing = false;
+
+            // Cancel sending of the next 0
+            fSyncTimer.cancel();
+
+            // Start continous reading of all channels
+            ScheduleUpdate(100);
+        }
+
+        // Take action depending on what is going on
+        if (command==kCmdReset)
+            Message("Reset command successfully answered.");
+
+        if (cmd==kCmdRead || cmd==kCmdChannelSet || cmd==kExpertChannelSet)
+        {
+            UpdateV();
+            UpdateA();
+            return;
+        }
+
+        if (cmd==kCmdReset || command==kResetChannels)
+        {
+            // Re-start cyclic reading of values after a short time
+            // to allow the currents to become stable
+            fUpdateTimer.cancel();
+            ScheduleUpdate(100);
+        }
+
+        if (command==kUpdate)
+            ScheduleUpdate(fUpdateTime);
+
+        // If we are ramping, schedule a new ramp step
+        if (command==kCmdChannelSet && fIsRamping)
+        {
+            ScheduleRampStep();
+            return;
+        }
+    }
+
+    // --------------------------------------------------------------------
+
+    void HandleSyncTimer(int counter, const bs::error_code &error)
+    {
+        if (error==ba::error::basic_errors::operation_aborted)
+        {
+            Warn("Synchronization aborted...");
+            fIsRamping = false;
+            return;
+        }
+
+        if (error)
+        {
+            ostringstream str;
+            str << "Synchronization timer: " << error.message() << " (" << error << ")";// << endl;
+            Error(str);
+
+            PostClose(false);
+            return;
+        }
+
+        if (!is_open())
+        {
+            Warn("Synchronization in progress, but disconnected.");
+            return;
+        }
+
+        ostringstream msg;
+        msg << "Synchronization time expired (" << counter << ")" << endl;
+        Info(msg);
+
+        if (fIsInitializing)
+        {
+            PostMessage("\0", 1);
+
+            if (counter==2)
             {
-                Message("GlobalSet successfully executed.");
+                Error("Synchronization attempt timed out.");
+                PostClose(false);
                 return;
             }
 
-            Warn("Answer to 'global set' command contains unexpected data.");
-            return;
-        }
-
-        if (command==kCmdRead || command==kCmdChannelSet)
-        {
-            if (error==0x7 || error==0xf)
-            {
-                fPresent[board] = false;
-                fCurrent[id]    = 0x8000;
-                return;
-            }
-
-            if (board!=id/kNumChannelsPerBoard)
-            {
-                ostringstream out;
-                out << "Talked to board " << id/kNumChannelsPerBoard << " but board " <<  board << " answered.";
-                Error(out);
-                return;
-            }
-
-            fCurrent[id]    = status ? -ddd : ddd;
-            fPresent[board] = true;
-
-            UpdateA();
-
-            return;
-        }
+            ScheduleSync(counter+1);
+            return;
+        }
+
+        Info("Synchronisation successfull.");
+    }
+
+    void ScheduleSync(int counter=0)
+    {
+        fSyncTimer.expires_from_now(boost::posix_time::milliseconds(333));
+        fSyncTimer.async_wait(boost::bind(&ConnectionBias::HandleSyncTimer, this, counter, dummy::error));
     }
 
@@ -195,23 +447,125 @@
     void ConnectionEstablished()
     {
-        fWrapCounter = -1;
-        fBuffer.resize(3);
-
-        SystemReset();
-        ReadAllChannels();
-    }
-
-    void HandleReadTimeout(const bs::error_code &error)
+        // Reset everything....
+        fSendCounter    = -1;
+        fWrapCounter    = -1;
+        fGlobalVoltCmd  = -1;
+        fIsInitializing = true;
+
+        fVolt.assign(   0, kNumChannels);
+        fVoltRef.assign(0, kNumChannels);
+        fVoltCmd.assign(0, kNumChannels);
+
+        // Send a single 0 (and possible two consecutive 0's
+        // to make sure we are in sync with the device)
+        PostMessage("\0", 1);
+        AsyncRead(ba::buffer(fBuffer, 3), kSynchronize, ++fSendCounter);
+//        fWaitingForAnswer = true;
+
+        // Wait for some time before sending the next 0
+        ScheduleSync();
+    }
+
+    // --------------------------------------------------------------------
+
+    void HandleUpdateTimer(const bs::error_code &error)
     {
         if (error==ba::error::basic_errors::operation_aborted)
-            return;
+        {
+            Warn("Update timer aborted...");
+            fIsRamping = false;
+            return;
+        }
 
         if (error)
         {
             ostringstream str;
-            str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
+            str << "Update timer: " << error.message() << " (" << error << ")";// << endl;
             Error(str);
 
-            PostClose();
+            PostClose(false);
+            return;
+        }
+
+        if (is_open())
+            ReadAllChannels(true);
+    }
+
+    void ScheduleUpdate(int millisec)
+    {
+        fUpdateTimer.expires_from_now(boost::posix_time::milliseconds(millisec));
+        fUpdateTimer.async_wait(boost::bind(&ConnectionBias::HandleUpdateTimer, this, dummy::error));
+    }
+
+    // --------------------------------------------------------------------
+
+    void SetAllChannels(const vector<uint16_t> &dac, bool special=false)
+    {
+        vector<char> data;
+        data.reserve(kNumChannels*3);
+
+        for (int ch=0; ch<kNumChannels; ch++)
+        {
+            const vector<char> cmd = GetCmd(kCmdChannelSet, ch, dac[ch]);
+            data.insert(data.end(), cmd.begin(), cmd.end());
+
+            fVoltCmd[ch] = dac[ch];
+        }
+
+        fSendCounter += kNumChannels;
+
+        PostMessage(data);
+        AsyncRead(ba::buffer(fBuffer, kNumChannels*3),
+                  special ? kResetChannels : kCmdChannelSet, fSendCounter);
+//        fWaitingForAnswer = true;
+    }
+
+    uint16_t RampOneStep(uint16_t ch)
+    {
+        if (fVoltRef[ch]>fVolt[ch])
+            return fVolt[ch]+fRampStep>fVoltRef[ch] ? fVoltRef[ch] : fVolt[ch]+fRampStep;
+
+        if (fVoltRef[ch]<fVolt[ch])
+            return fVolt[ch]-fRampStep<fVoltRef[ch] ? fVoltRef[ch] : fVolt[ch]-fRampStep;
+
+        return fVolt[ch];
+    }
+
+    bool RampOneStep()
+    {
+        vector<uint16_t> dac(kNumChannels);
+
+        bool identical = true;
+        for (int ch=0; ch<kNumChannels; ch++)
+        {
+            dac[ch] = RampOneStep(ch);
+            if (dac[ch]!=fVolt[ch])
+                identical = false;
+        }
+
+        SetAllChannels(dac);
+
+        if (identical)
+            Info("Ramping: target values reached.");
+
+        return !identical;
+    }
+
+    void HandleRampTimer(const bs::error_code &error)
+    {
+        if (error==ba::error::basic_errors::operation_aborted)
+        {
+            Warn("Ramping aborted...");
+            fIsRamping = false;
+            return;
+        }
+
+        if (error)
+        {
+            ostringstream str;
+            str << "Ramping timer: " << error.message() << " (" << error << ")";// << endl;
+            Error(str);
+
+            PostClose(false);
             return;
         }
@@ -219,231 +573,263 @@
         if (!is_open())
         {
-            // For example: Here we could schedule a new accept if we
-            // would not want to allow two connections at the same time.
-            return;
-        }
-
-        // Check whether the deadline has passed. We compare the deadline
-        // against the current time since a new asynchronous operation
-        // may have moved the deadline before this actor had a chance
-        // to run.
-        if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
-            return;
-
-        Error("Timeout reading data from "+URL());
-
-        PostClose();
-    }
-
-    vector<char> GetCmd(uint16_t id, Command_t cmd, uint16_t dac=0)
-    {
-        const unsigned int board   = id/kNumChannelsPerBoard;
-        const unsigned int channel = id%kNumChannelsPerBoard;
-
-        return GetCmd(board, channel, cmd, dac);
-    }
-
-    vector<char> GetCmd(uint16_t board, uint16_t channel, Command_t cmd, uint16_t dac=0)
-    {
-        vector<char> data(3);
-
-        /*
-        if (board>kNumBoards)
-            return;
-        if (channel>kNumChannelsPerBoard)
-            return;
-        if (dac>0xfff)
-            return;
-        */
-
-        data[0] = (cmd<<5) | (board<<1) | (((channel&16)>>4) & 1);
-        data[1] = (channel<<4) | (dac>>8);
-        data[2] =  dac&0xff;
-
-        return data;
-    }
-
-    /*
-    void SetChannels(const map<uint16_t, uint16_t> &vals)
-    {
-        if (vals.empty())
-            return;
-
-        // Build and execute commands
-        for (map<uint16_t, uint16_t>::const_iterator it=vals.begin();
-             it!=vals.end(); it++)
-        {
-            // If DAC value unchanged, do not send command
-            if (fVolt[it->first] == it->second)
-                continue;
-
-            const vector<char> cmd = GetCmd(it->first, kCmdChannelSet, it->second);
-            PostMessage(cmd);
-            AsyncRead(ba::buffer(fBuffer), kCmdChannelSet|(it->first<<4));
-        }
-    }*/
-
-    /*
-    // ***** Synchronize board *****
-    bool Crate::Synch()
-    {
-        //############################################################
-        int Trial = 0;
-        vector<unsigned char> Data;
-
-        while(++Trial <= 3) {
-            Data = Communicate(string(1, 0));
-            if (Data.size() == 3) return true;
-        }
-        return false;
-        //############################################################
-    }
-    */
+            Warn("Ramping in progress, but disconnected.");
+            return;
+        }
+
+        if (!fIsRamping)
+        {
+            Error("Ramp handler called although no ramping in progress.");
+            return;
+        }
+
+        fIsRamping = RampOneStep();
+    }
+
+    void ScheduleRampStep()
+    {
+        fRampTimer.expires_from_now(boost::posix_time::milliseconds(fRampTime));
+        fRampTimer.async_wait(boost::bind(&ConnectionBias::HandleRampTimer, this, dummy::error));
+    }
 
 public:
     ConnectionBias(ba::io_service& ioservice, MessageImp &imp) : ConnectionUSB(ioservice, imp()),
+        fSyncTimer(ioservice),
+        fRampTimer(ioservice),
+        fUpdateTimer(ioservice),
+        fBuffer(3*kNumChannels),
         fIsVerbose(true),
+        fVoltCmd(kNumChannels),
+        fVoltGapd(kNumChannels),
+        //fRefCurrent(kNumChannels),
+        fPresent(kNumBoards),
+        fRampStep(-1),
+        fRampTime(-1),
+        fUpdateTime(3000),
+        fIsRamping(false),
         fVolt(kNumChannels),
-        fRefVolt(kNumChannels),
-        fCurrent(kNumChannels),
-        fRefCurrent(kNumChannels),
-        fPresent(kNumBoards)
+        fVoltRef(kNumChannels),
+        fCurrent(kNumChannels)
     {
         SetLogStream(&imp);
     }
 
-    void SystemReset()
-    {
-        Message("Sending system reset.");
-        PostMessage(GetCmd(0, kCmdReset));
-        AsyncRead(ba::buffer(fBuffer), kCmdReset);
-    }
-
-    void ReadChannel(int ch)
-    {
-        PostMessage(GetCmd(ch, kCmdRead));
-        AsyncRead(ba::buffer(fBuffer), kCmdRead|(ch<<4));
-    }
-
-
-    void ReadAllChannels()
-    {
-        Message("Requesting full system status.");
-
-        // Prepare command to read all channels
-        for (int i=0; i<kNumChannels; i++)
-            ReadChannel(i);
-
-        //vector<char> buf;
-        //AsyncRead(ba::buffer(buf), kCmdPrint);
+    void OverCurrentReset()
+    {
+        if (fIsRamping)
+        {
+            Warn("OverCurrentReset - Ramping in progres.");
+            RampStop();
+        }
+
+        vector<uint16_t> dac(kNumChannels);
+
+        for (int ch=0; ch<kNumChannels; ch++)
+            dac[ch] = fCurrent[ch]<0 ? 0 : fVolt[ch];
+
+        SetAllChannels(dac, true);
+    }
+
+    void ReadAllChannels(bool special = false)
+    {
+        vector<char> data;
+        data.reserve(kNumChannels*3);
+
+        for (int ch=0; ch<kNumChannels; ch++)
+        {
+            const vector<char> cmd = GetCmd(kCmdRead, ch);
+            data.insert(data.end(), cmd.begin(), cmd.end());
+        }
+
+        fSendCounter += kNumChannels;
+
+        PostMessage(data);
+        AsyncRead(ba::buffer(fBuffer, kNumChannels*3),
+                  special ? kUpdate : kCmdRead, fSendCounter);
+//        fWaitingForAnswer = true;
+    }
+
+    // --------------------------------------------------------------------
+
+    bool ChannelSetDac(uint16_t ch, uint16_t dac)
+    {
+        if (!CheckChDac("ChannelSetDac", dac, ch))
+            return false;
+
+        fVoltRef[ch] = dac;
+
+        if (!fIsRamping)
+            fIsRamping = RampOneStep();
+
+        return true;
+    }
+
+    bool ChannelSetVolt(uint16_t ch, double volt)
+    {
+        return ChannelSetDac(ch, volt*4096/90.);
     }
 
     bool GlobalSetDac(uint16_t dac)
     {
-        if (dac>0xfff)
-            return false;
-
-        PostMessage(GetCmd(0, kCmdGlobalSet, dac));
-        AsyncRead(ba::buffer(fBuffer), kCmdGlobalSet);
-
-        ReadAllChannels();
-
-        fVolt.assign(kNumChannels, dac);
-        UpdateV();
+        if (!CheckChDac("GlobalSetDac", dac))
+            return false;
+
+        for (size_t ch=0; ch<kNumChannels; ch++)
+            fVoltRef[ch] = dac;
+
+        if (!fIsRamping)
+            fIsRamping = RampOneStep();
 
         return true;
     }
 
-    bool GlobalSet(double voltage)
-    {
-        return GlobalSetDac(voltage*4096/90);
-    }
-
-    bool ChannelSetDac(uint16_t ch, uint16_t dac)
-    {
-        if (dac>0xfff)
-            return false;
-
-        if (ch>=kNumChannels)
-            return false;
-
-        if (fVolt[ch]==dac)
-            return true;
-
-        PostMessage(GetCmd(ch, kCmdChannelSet, dac));
-        AsyncRead(ba::buffer(fBuffer), kCmdChannelSet|(ch<<4));
-
-        ReadChannel(ch);
-
-        fVolt[ch] = dac;
-        UpdateV();
+    bool GlobalSetVolt(float volt)
+    {
+        return GlobalSetDac(volt*4096/90);
+    }
+
+    // --------------------------------------------------------------------
+
+    bool SetGapdVoltage()
+    {
+        for (size_t ch=0; ch<kNumChannels; ch++)
+            if (fVoltGapd[ch]>kMaxDac)
+            {
+                Error("SetGapdVoltage - Voltage reference for G-APD channel out of range.");
+                return false;
+            }
+
+        for (size_t ch=0; ch<kNumChannels; ch++)
+            fVoltRef[ch] = fVoltGapd[ch];
+
+        if (!fIsRamping)
+            fIsRamping = RampOneStep();
 
         return true;
     }
 
-    bool ChannelSet(uint16_t ch, double voltage)
-    {
-        return ChannelSetDac(ch, voltage*4096/90);
-    }
-
-    void SetVoltage(int ch, int32_t dac)
-    {
-        if (dac<0)
-            dac = 0;
-
-        if (dac>0xfff)
-            dac = 0xfff;
-
-        ChannelSetDac(ch, dac);
-    }
-
-    // ***** Correct voltages according to current *****
-    void AdaptVoltages()
-    {
-        for (int i=0; i<kNumChannels; i++)
-        {
-            if (fRefVolt[i]==0 || fCurrent[i]<0 || fRefCurrent[i]<0)
-                continue;
-
-            // Calculate difference and convert ADC units to Amp
-            // const double diffcur = (fRefCurrent[i]-fCurrent[i])*5000/4096
-            //const int32_t diffcur = int32_t(fRefCurrent[i]-fCurrent[i])*5000;
-
-            // Calculate voltage difference
-            // #define RESISTOR 1000 // Ohm
-            //const double diffvolt = diffcur*RESISTOR/1e6;
-
-            // Calculate new vlaue by onverting voltage difference to DAC units
-            //const int32_t dac = fRefVolt[i] + diffvolt*4096/90.0;
-            SetVoltage(i, fRefVolt[i] + (fRefCurrent[i]-fCurrent[i])/18);
-        }
-    }
-
-    void SetReferenceCurrent()
-    {
-        fRefCurrent = fCurrent;
-    }
-
-    bool SetReferenceVoltage(const vector<float> &volt)
+    void SetZero()
+    {
+        for (size_t ch=0; ch<kNumChannels; ch++)
+            fVoltRef[ch] = 0;
+
+        if (!fIsRamping)
+            fIsRamping = RampOneStep();
+    }
+
+    bool SetNewGapdVoltage(const vector<float> &volt)
     {
         if (volt.size()!=kNumChannels)
         {
             ostringstream out;
-            out << "SetReferenceVoltage - Given vector has " << volt.size() << " elements - expected " << kNumChannels << endl;
+            out << "SetNewGapdVoltage - Given vector has " << volt.size() << " elements - expected " << kNumChannels << endl;
             Error(out);
             return false;
         }
 
-        for (size_t i=0; i<volt.size(); i++)
-            fRefVolt[i] = volt[i]*4096/90;
+        for (size_t i=0; i<kNumChannels; i++)
+            fVoltGapd[i] = volt[i]*4096/90;
 
         return true;
     }
 
-    void ApplyReferenceVoltage()
-    {
-        for (size_t i=0; i<fRefVolt.size(); i++)
-            SetVoltage(i, fRefVolt[i]);
-    }
+    // --------------------------------------------------------------------
+
+    void RampStop()
+    {
+        fRampTimer.cancel();
+        fIsRamping = false;
+
+        Message("Ramping stopped.");
+    }
+
+    void RampStart()
+    {
+        if (fIsRamping)
+        {
+            Warn("RampStart - Ramping in progress... ignored.");
+            return;
+        }
+
+        fIsRamping = RampOneStep();
+    }
+
+    void SetRampTime(uint16_t val)
+    {
+        fRampTime = val;
+    }
+
+    void SetRampStep(uint16_t val)
+    {
+        fRampStep = val;
+    }
+
+    bool IsRamping() const { return fIsRamping; }
+
+    // -------------------------------------------------------------------
+
+    void ExpertReset()
+    {
+        Warn("EXPERT MODE: Sending reset.");
+        PostMessage(GetCmd(kCmdReset));
+        AsyncRead(ba::buffer(fBuffer, 3), kCmdReset);
+//        fWaitingForAnswer = true;
+    }
+
+
+    bool ExpertChannelSetDac(uint16_t ch, uint16_t dac)
+    {
+        if (!CheckChDac("ExpertChannelSetDac", dac, ch))
+            return false;
+
+        fVoltCmd[ch] = dac;
+
+        // FIXME: How to ensure the correct evaluation of the answer?
+
+        ostringstream msg;
+        msg << "EXPERT MODE: Sending 'ChannelSet' (set ch " << ch << " to DAC=" << dac << ")";
+        Warn(msg);
+
+        PostMessage(GetCmd(kCmdChannelSet, ch, dac));
+        AsyncRead(ba::buffer(fBuffer, 3), kExpertChannelSet|(ch<<8), ++fSendCounter);
+//        fWaitingForAnswer = true;
+
+        return true;
+    }
+
+    bool ExpertChannelSetVolt(uint16_t ch, double volt)
+    {
+        return ExpertChannelSetDac(ch, volt*4096/90.);
+    }
+
+    bool ExpertGlobalSetDac(uint16_t dac)
+    {
+        if (!CheckChDac("ExpertGlobalSetDac", dac))
+            return false;
+
+        if (fGlobalVoltCmd>=0)
+        {
+            Error("ExpertGlobalSetDac - Still waiting for previous global-set's answer.");
+            return false;
+        }
+
+        fGlobalVoltCmd = dac;
+
+        ostringstream msg;
+        msg << "EXPERT MODE: Sending 'GlobalSet' (DAC=" << dac << ")";
+        Warn(msg);
+
+        PostMessage(GetCmd(kCmdGlobalSet, 0, dac));
+        AsyncRead(ba::buffer(fBuffer, 3), kCmdGlobalSet, ++fSendCounter);
+//        fWaitingForAnswer = true;
+
+        return true;
+    }
+
+    bool ExpertGlobalSetVolt(float volt)
+    {
+        return GlobalSetDac(volt*4096/90);
+    }
+
+    // --------------------------------------------------------------------
 
     void SetVerbose(bool b)
@@ -468,4 +854,5 @@
 
     }
+
     void Print()
     {
@@ -485,4 +872,61 @@
         }
     }
+
+    // -------------------------------------------------------------------
+/*
+    void AdaptVoltages()
+    {
+        // Correct voltages according to current
+        for (int i=0; i<kNumChannels; i++)
+        {
+            if (fVoltRef[i]==0 || fCurrent[i]<0 || fRefCurrent[i]<0)
+                continue;
+
+            // Calculate difference and convert ADC units to Amp
+            // const double diffcur = (fRefCurrent[i]-fCurrent[i])*5000/4096
+            //const int32_t diffcur = int32_t(fRefCurrent[i]-fCurrent[i])*5000;
+
+            // Calculate voltage difference
+            // #define RESISTOR 1000 // Ohm
+            //const double diffvolt = diffcur*RESISTOR/1e6;
+
+            // Calculate new vlaue by onverting voltage difference to DAC units
+            //const int32_t dac = fRefVolt[i] + diffvolt*4096/90.0;
+            SetVoltage(i, fRefVolt[i] + (fRefCurrent[i]-fCurrent[i])/18);
+        }
+    }
+
+    void SetReferenceCurrent()
+    {
+        fRefCurrent = fCurrent;
+    }
+    */
+
+    enum States_t
+    {
+        kDisconnected = StateMachineImp::kSM_UserMode,
+        kConnecting,
+        kInitializing,
+        kConnected,
+        kRamping,
+        kExpertMode // 'forward' declaration to be used in StateMachineBias
+    };
+
+    int GetStatus()
+    {
+        if (!IsConnected())
+            return kDisconnected;
+
+        if (IsConnecting())
+            return kConnecting;
+
+        if (fIsInitializing)
+            return kInitializing;
+
+        if (fIsRamping)
+            return kRamping;
+
+        return kConnected;
+    }
 };
 
@@ -505,6 +949,10 @@
     void UpdateV()
     {
-        fDimVoltage.Update(fVolt);
-    }
+        vector<uint16_t> vec;
+        vec.insert(vec.end(), fVolt.begin(),    fVolt.end());
+        vec.insert(vec.end(), fVoltRef.begin(), fVoltRef.end());
+        fDimVoltage.Update(vec);
+    }
+
 
 public:
@@ -512,5 +960,5 @@
         ConnectionBias(ioservice, imp),
         fDimCurrent("BIAS_CONTROL/CURRENT", "S:416", ""),
-        fDimVoltage("BIAS_CONTROL/VOLTAGE", "S:416", "")
+        fDimVoltage("BIAS_CONTROL/VOLTAGE", "S:416;S:416", "")
     {
     }
@@ -549,16 +997,14 @@
     S fBias;
 
-    enum states_t
-    {
-        kStateDisconnected = 1,
-        kStateConnected    = 2,
-    };
-
-    int SetGlobal(const EventImp &evt)
-    {
-        if (!CheckEventSize(evt.GetSize(), "SetGlobal", 4))
-            return false;
-
-        if (!fBias.GlobalSet(evt.GetFloat()))
+    bool fExpertMode;
+
+    // --------------------------------------------------------------------
+
+    int SetGlobalVolt(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "SetGlobalVolt", 4))
+            return false;
+
+        if (!fBias.GlobalSetVolt(evt.GetFloat()))
             T::Error("Supplied voltage out of range (0-90)");
 
@@ -577,10 +1023,10 @@
     }
 
-    int SetChannel(const EventImp &evt)
-    {
-        if (!CheckEventSize(evt.GetSize(), "SetChannel", 6))
-            return false;
-
-        if (!fBias.ChannelSet(evt.GetUShort(), evt.Get<float>(2)))
+    int SetChannelVolt(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "SetChannelVolt", 6))
+            return false;
+
+        if (!fBias.ChannelSetVolt(evt.GetUShort(), evt.Get<float>(2)))
             T::Error("Value out of range");
 
@@ -599,4 +1045,51 @@
     }
 
+    // --------------------------------------------------------------------
+
+    int ExpertSetGlobalVolt(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalVolt", 4))
+            return false;
+
+        if (!fBias.ExpertGlobalSetVolt(evt.GetFloat()))
+            T::Error("Supplied voltage out of range (0-90)");
+
+        return T::GetCurrentState();
+    }
+
+    int ExpertSetGlobalDac(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "ExpertSetGlobalDac", 2))
+            return false;
+
+        if (!fBias.ExpertGlobalSetDac(evt.GetUShort()))
+            T::Error("Supplied voltage out of range (0-90)");
+
+        return T::GetCurrentState();
+    }
+
+    int ExpertSetChannelVolt(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelVolt", 6))
+            return false;
+
+        if (!fBias.ExpertChannelSetVolt(evt.GetUShort(), evt.Get<float>(2)))
+            T::Error("Value out of range");
+
+        return T::GetCurrentState();
+    }
+
+    int ExpertSetChannelDac(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "ExpertSetChannelDac", 4))
+            return false;
+
+        if (!fBias.ExpertChannelSetDac(evt.Get<uint16_t>(), evt.Get<uint16_t>(2)))
+            T::Error("Value out of range");
+
+        return T::GetCurrentState();
+    }
+
+    // --------------------------------------------------------------------
 
     int Disconnect()
@@ -628,4 +1121,27 @@
         // Now we can reopen the connection
         fBias.PostClose(true);
+
+        return T::GetCurrentState();
+    }
+
+    int SetVerbosity(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
+            return T::kSM_FatalError;
+
+        fBias.SetVerbose(evt.GetBool());
+
+        return T::GetCurrentState();
+    }
+
+    int SetExpertMode(const EventImp &evt)
+    {
+        if (!CheckEventSize(evt.GetSize(), "SetExpertMode", 1))
+            return T::kSM_FatalError;
+
+        fExpertMode = evt.GetBool();
+
+        if (fExpertMode)
+            T::Warn("Expert commands enabled -- please ensure that you EXACTLY know what you do. These commands can destroy the system.");
 
         return T::GetCurrentState();
@@ -641,15 +1157,6 @@
         poll_one();
 
-        return fBias.IsConnected() ? kStateConnected : kStateDisconnected;
-    }
-
-    int SetVerbosity(const EventImp &evt)
-    {
-        if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
-            return T::kSM_FatalError;
-
-        fBias.SetVerbose(evt.GetBool());
-
-        return T::GetCurrentState();
+        return fExpertMode && fBias.GetStatus()==ConnectionBias::kConnected ?
+            ConnectionBias::kExpertMode : fBias.GetStatus();
     }
 
@@ -657,5 +1164,5 @@
     StateMachineBias(ostream &out=cout) :
         T(out, "BIAS_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
-        fBias(*this, *this)
+        fBias(*this, *this), fExpertMode(false)
     {
         // ba::io_service::work is a kind of keep_alive for the loop.
@@ -666,66 +1173,116 @@
         // deletion and creation of threads and more.
 
+        T::Warn("FIXME -- implement a state for 'at reference'");
+
         // State names
-        AddStateName(kStateDisconnected, "Disconnected",
-                     "Bias-power supply not connected via USB.");
-
-        AddStateName(kStateConnected, "Connected",
-                     "USB connection to bias-power supply established.");
+        T::AddStateName(ConnectionBias::kDisconnected, "Disconnected",
+                        "Bias-power supply not connected via USB.");
+
+        T::AddStateName(ConnectionBias::kConnecting, "Connecting",
+                        "Trying to establish USB connection to bias-power supply.");
+
+        T::AddStateName(ConnectionBias::kInitializing, "Initializing",
+                        "USB connection to bias-power supply established, synchronizing USB stream.");
+
+        T::AddStateName(ConnectionBias::kConnected, "Connected",
+                        "USB connection to bias-power supply established.");
+
+        T::AddStateName(ConnectionBias::kExpertMode, "ExpertMode",
+                        "Special (risky!) mode to directly send command to the bias-power supply.");
+
+        T::AddStateName(ConnectionBias::kRamping, "Ramping",
+                        "Voltage ramping in progress.");
 
         // Verbosity commands
         T::AddEvent("SET_VERBOSE", "B")
-            (bind(&StateMachineBias::SetVerbosity, this, _1))
+            (bind(&StateMachineBias::SetVerbosity, this, placeholders::_1))
             ("set verbosity state"
              "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
 
         // Conenction commands
-        AddEvent("DISCONNECT", kStateConnected)
+        T::AddEvent("DISCONNECT", ConnectionBias::kConnected)
             (bind(&StateMachineBias::Disconnect, this))
             ("disconnect from ethernet");
 
-        AddEvent("RECONNECT", "O", kStateDisconnected, kStateConnected)
-            (bind(&StateMachineBias::Reconnect, this, _1))
+        T::AddEvent("RECONNECT", "O", ConnectionBias::kDisconnected, ConnectionBias::kConnected)
+            (bind(&StateMachineBias::Reconnect, this, placeholders::_1))
             ("(Re)connect ethernet connection to FTM, a new address can be given"
              "|[host][string]:new ethernet address in the form <host:port>");
 
 
-        AddEvent("REQUEST_STATUS", kStateConnected)
-            (Wrapper(bind(&ConnectionBias::ReadAllChannels, &fBias)))
-            ("");
-
-        AddEvent("SET_GLOBAL_VOLTAGE", "F:1", kStateConnected)
-            (bind(&StateMachineBias::SetGlobal, this, _1))
-            ("");
-
-        AddEvent("SET_GLOBAL_DAC", "S:1", kStateConnected)
-            (bind(&StateMachineBias::SetGlobalDac, this, _1))
-            ("");
-
-        AddEvent("SET_CHANNEL_VOLTAGE", "S:1;F:1", kStateConnected)
-            (bind(&StateMachineBias::SetChannel, this, _1))
-            ("");
-
-        AddEvent("SET_CHANNEL_DAC", "S:1;S:1", kStateConnected)
-            (bind(&StateMachineBias::SetChannelDac, this, _1))
-            ("");
-
-        AddEvent("RESET", kStateConnected)
-            (Wrapper(bind(&ConnectionBias::SystemReset, &fBias)))
-            ("");
-
-        AddEvent("SET_REFERENCE_CURRENT", kStateConnected)
-            (Wrapper(bind(&ConnectionBias::SetReferenceCurrent, &fBias)))
-            ("");
-
-        AddEvent("APPLY_REFERENCE_VOLTAGE", kStateConnected)
-            (Wrapper(bind(&ConnectionBias::ApplyReferenceVoltage, &fBias)))
-            ("");
-
-        AddEvent("ADAPT_VOLTAGES", kStateConnected)
-            (Wrapper(bind(&ConnectionBias::AdaptVoltages, &fBias)))
-            ("");
-
-        AddEvent("PRINT", kStateConnected)
+
+        T::AddEvent("REQUEST_STATUS", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (Wrapper(bind(&ConnectionBias::ReadAllChannels, &fBias, false)))
+            ("");
+
+        T::AddEvent("RESET_OVER_CURRENT_STATUS", ConnectionBias::kConnected)
+            (Wrapper(bind(&ConnectionBias::OverCurrentReset, &fBias)))
+            ("");
+
+
+
+        T::AddEvent("SET_GLOBAL_VOLTAGE", "F:1", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (bind(&StateMachineBias::SetGlobalVolt, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("SET_GLOBAL_DAC", "S:1", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (bind(&StateMachineBias::SetGlobalDac, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("SET_CHANNEL_VOLTAGE", "S:1;F:1", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (bind(&StateMachineBias::SetChannelVolt, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("SET_CHANNEL_DAC", "S:1;S:1", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (bind(&StateMachineBias::SetChannelDac, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("SET_GAPD_REFERENCE_VOLTAGE", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (Wrapper(bind(&ConnectionBias::SetGapdVoltage, &fBias)))
+            ("");
+
+        T::AddEvent("SET_ZERO_VOLTAGE", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (Wrapper(bind(&ConnectionBias::SetZero, &fBias)))
+            ("");
+
+
+
+        T::AddEvent("STOP", ConnectionBias::kConnected, ConnectionBias::kRamping)
+            (Wrapper(bind(&ConnectionBias::RampStop, &fBias)))
+            ("");
+
+        T::AddEvent("START", ConnectionBias::kConnected)
+            (Wrapper(bind(&ConnectionBias::RampStart, &fBias)))
+            ("");
+
+
+
+        T::AddEvent("PRINT", ConnectionBias::kConnected, ConnectionBias::kRamping)
             (Wrapper(bind(&ConnectionBias::Print, &fBias)))
+            ("");
+
+
+        T::AddEvent("EXPERT_MODE", "B:1")
+            (bind(&StateMachineBias::SetExpertMode, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("EXPERT_RESET", ConnectionBias::kExpertMode)
+            (Wrapper(bind(&ConnectionBias::ExpertReset, &fBias)))
+            ("");
+
+        T::AddEvent("EXPERT_SET_GLOBAL_VOLTAGE", "F:1", ConnectionBias::kExpertMode)
+            (bind(&StateMachineBias::ExpertSetGlobalVolt, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("EXPERT_SET_GLOBAL_DAC", "S:1", ConnectionBias::kExpertMode)
+            (bind(&StateMachineBias::ExpertSetGlobalDac, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("EXPERT_SET_CHANNEL_VOLTAGE", "S:1;F:1", ConnectionBias::kExpertMode)
+            (bind(&StateMachineBias::ExpertSetChannelVolt, this, placeholders::_1))
+            ("");
+
+        T::AddEvent("EXPERT_SET_CHANNEL_DAC", "S:1;S:1", ConnectionBias::kExpertMode)
+            (bind(&StateMachineBias::ExpertSetChannelDac, this, placeholders::_1))
             ("");
     }
@@ -737,4 +1294,21 @@
         fBias.SetEndpoint(conf.Get<string>("dev"));
         T::Message("Setting device to "+fBias.URL());
+
+        const uint16_t step = conf.Get<uint16_t>("ramp-step");
+        const uint16_t time = conf.Get<uint16_t>("ramp-time");
+
+        if (step>230) // 5V
+        {
+            T::Error("ramp-step exceeds allowed range.");
+            return 1;
+        }
+        if (time>10000) // 5V
+        {
+            T::Error("ramp-time exceeds allowed range.");
+            return 2;
+        }
+
+        fBias.SetRampStep(step);
+        fBias.SetRampTime(time);
 
         // --------------------------------------------------------------------------
@@ -770,5 +1344,5 @@
             {
                 T::Error("Invalid board/channel read from FACTmapV5.txt.");
-                return 1;
+                return 3;
             }
 
@@ -780,11 +1354,11 @@
         {
             T::Error("Reading reference voltages from FACTmapV5.txt failed.");
-            return 2;
-        }
-
-        if (!fBias.SetReferenceVoltage(vec))
+            return 4;
+        }
+
+        if (!fBias.SetNewGapdVoltage(vec))
         {
             T::Error("Setting reference voltages failed.");
-            return 3;
+            return 5;
         }
 
@@ -813,5 +1387,8 @@
         ("no-dim,d",      po_bool(),  "Disable dim services")
         ("dev",           var<string>("FTE00FOH"),  "Device address of USB port to bias-power supply")
-        ("quiet,q",       po_bool(),  "Disable printing contents of all received messages (except dynamic data) in clear text.")
+        ("quiet,q",       po_bool(),        "Disable printing contents of all received messages (except dynamic data) in clear text.")
+        ("ramp-time",     var<uint16_t>(),  "")
+        ("ramp-step",     var<uint16_t>(),  "")
+        ("ramp-volt",     var<float>(),     "")
         ;
 
