Index: trunk/FACT++/src/HeadersFeedback.h
===================================================================
--- trunk/FACT++/src/HeadersFeedback.h	(revision 18190)
+++ trunk/FACT++/src/HeadersFeedback.h	(revision 18191)
@@ -18,4 +18,10 @@
             kWaitingForData,
             kInProgress,
+
+            kWarning,
+            kCritical,
+            kOnStandby,
+
+
         };
     }
Index: trunk/FACT++/src/feedback.cc
===================================================================
--- trunk/FACT++/src/feedback.cc	(revision 18190)
+++ trunk/FACT++/src/feedback.cc	(revision 18191)
@@ -63,6 +63,8 @@
     Time fTimeCalib;
     Time fTimeTemp;
+    Time fTimeCritical;
 
     double fUserOffset;
+    double fVoltageReduction;
     vector<double> fTempOffset;
     float fTempOffsetAvg;
@@ -78,4 +80,6 @@
     uint16_t fCalibStep;
 
+    uint16_t fTimeoutCritical;
+
     // ============================= Handle Services ========================
 
@@ -88,5 +92,5 @@
         }
 
-        if (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()==Feedback::State::kInProgress)
+        if (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()>=Feedback::State::kInProgress)
             return Feedback::State::kCalibrated;
 
@@ -116,4 +120,8 @@
             fVoltGapd.assign(evt.Ptr<float>(), evt.Ptr<float>()+416);
             fBiasR9.assign(evt.Ptr<float>()+2*416, evt.Ptr<float>()+3*416);
+
+            for (int i=0; i<320; i++)
+                fVoltGapd[i] += 1.1;
+
             Info("Nominal bias voltages and calibration resistor received.");
         }
@@ -330,4 +338,111 @@
     }
 
+    int CheckLimits(const float *I)
+    {
+        const float fAbsoluteMedianCurrentLimit   = 85;
+        const float fRelativePixelCurrentLimit3   = 20;
+        const float fRelativePixelCurrentLimit0   = 45;
+
+        const float fAbsolutePixelCurrentLimit3   = fAbsoluteMedianCurrentLimit + fRelativePixelCurrentLimit3;
+        const float fAbsolutePixelCurrentLimit0   = fAbsoluteMedianCurrentLimit + fRelativePixelCurrentLimit0;
+
+        const float fRelativeCurrentLimitWarning  = 10;//10;
+        const float fRelativeCurrentLimitCritical = 15;//20;
+        const float fRelativeCurrentLimitShutdown = 25;
+
+        fTimeoutCritical = 3000; // 5s
+
+        // Copy the calibrated currents
+        vector<float> v(I, I+320);
+
+        // Exclude the crazy patches (that's currently the best which could be done)
+        v[66]  = 0;
+        v[191] = 0;
+        v[193] = 0;
+
+        sort(v.begin(), v.end());
+
+        const float &imax0 = v[319];
+        const float &imax3 = v[316];
+        const float &imed  = v[161];
+
+        const bool shutdown =
+            imed >fAbsoluteMedianCurrentLimit+fRelativeCurrentLimitShutdown ||
+            imax3>fAbsolutePixelCurrentLimit3+fRelativeCurrentLimitShutdown ||
+            imax0>fAbsolutePixelCurrentLimit0+fRelativeCurrentLimitShutdown;
+
+        const bool critical =
+            imed >fAbsoluteMedianCurrentLimit+fRelativeCurrentLimitCritical ||
+            imax3>fAbsolutePixelCurrentLimit3+fRelativeCurrentLimitCritical ||
+            imax0>fAbsolutePixelCurrentLimit0+fRelativeCurrentLimitCritical;
+
+        const bool warning =
+            imed >fAbsoluteMedianCurrentLimit+fRelativeCurrentLimitWarning ||
+            imax3>fAbsolutePixelCurrentLimit3+fRelativeCurrentLimitWarning ||
+            imax0>fAbsolutePixelCurrentLimit0+fRelativeCurrentLimitWarning;
+
+        bool standby = GetCurrentState()==Feedback::State::kOnStandby;
+
+        if (standby)
+        {
+            // On Standby
+            if (fVoltageReduction==0 &&
+                imed <fAbsoluteMedianCurrentLimit &&
+                imax3<fAbsolutePixelCurrentLimit3 &&
+                imax0<fAbsolutePixelCurrentLimit0)
+            {
+                // Currents are back at nominal value and currents are again
+                // below the current limit, switching back to standard operation.
+                return Feedback::State::kInProgress;
+            }
+        }
+
+        // Shutdown level
+        if (!standby && shutdown)
+        {
+            // Currents exceed the shutdown limit, operation is switched
+            // immediately to voltage reduced operation
+
+            // Just in case (FIXME: Is that really the right location?)
+            Dim::SendCommandNB("FAD_CONTROL/CLOSE_ALL_OPEN_FILES");
+
+            Error("Current limit for shutdown exceeded.... swtching to standby mode.");
+
+            standby = true;
+        }
+
+        // Critical level
+        if (!standby && critical)
+        {
+            // This is a state transition from InProgress or Warning to Critical.
+            // Keep the transition time.
+            if (GetCurrentState()==Feedback::State::kInProgress || GetCurrentState()==Feedback::State::kWarning)
+            {
+                Info("Critical current limit exceeded.... waiting for "+to_string(fTimeoutCritical)+" ms.");
+                fTimeCritical = Time();
+            }
+
+            // Critical is only allowed for fTimeoutCritical milliseconds.
+            // After this time, the operation is changed to reduced voltage.
+            if (Time()<fTimeCritical+boost::posix_time::milliseconds(fTimeoutCritical))
+                return Feedback::State::kCritical;
+
+            // Just in case (FIXME: Is that really the right location?)
+            Dim::SendCommandNB("FAD_CONTROL/CLOSE_ALL_OPEN_FILES");
+
+            // Currents in critical state
+            Warn("Critical current limit exceeded timeout.... switching to standby mode.");
+
+            standby = true;
+        }
+
+        // Warning level (is just informational)
+        if (!standby && warning)
+            return Feedback::State::kWarning;
+
+        // keep voltage
+        return standby ? Feedback::State::kOnStandby : Feedback::State::kInProgress;
+    }
+
     int HandleBiasCurrent(const EventImp &evt)
     {
@@ -349,6 +464,13 @@
             return GetCurrentState();
 
+        // We are waiting but biasctrl is still in ramping (this might
+        // be the case if the feedback was started with a new overvoltage
+        // while the last ramping command was still in progress)
+        if (GetCurrentState()==Feedback::State::kWaitingForData &&
+            fDimBias.state()==BIAS::State::kRamping)
+            return GetCurrentState();
+
         // We are already in progress but no valid temperature update anymore
-        if (GetCurrentState()==Feedback::State::kInProgress &&
+        if (GetCurrentState()>=Feedback::State::kInProgress &&
             (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5)))
         {
@@ -373,5 +495,5 @@
 
         // Nominal overvoltage (w.r.t. the bias setup values)
-        const double overvoltage = GetCurrentState()<Feedback::State::kWaitingForData ? 0 : fUserOffset;
+        const double voltageoffset = GetCurrentState()<Feedback::State::kWaitingForData ? 0 : fUserOffset;
 
         double avg[2] = {   0,   0 };
@@ -405,88 +527,13 @@
         dim_data data;
 
-        data.Unom   = overvoltage;
+        data.Unom   = voltageoffset;
         data.dUtemp = fTempOffsetAvg;
 
         vector<float> vec(416);
 
-        /*
-        if (fEnableOldAlgorithm)
-        {
-            // ================================= old =======================
-            // Pixel  583: 5 31 == 191 (5)  C2 B3 P3
-            // Pixel  830: 2  2 ==  66 (4)  C0 B8 P1
-            // Pixel 1401: 6  1 == 193 (5)  C2 B4 P0
-
-            // 3900 Ohm/n + 1000 Ohm + 1100 Ohm  (with n=4 or n=5)
-            const double R[2] = { 3075, 2870 };
-
-            const float *Iavg = fCalibration.data();                      // Offset at U=fCalibrationOffset
-            const float *Ravg = fCalibration.data()+BIAS::kNumChannels*2; // Measured resistance
-
-            for (int i=0; i<320; i++)
-            {
-                const PixelMapEntry &hv = fMap.hv(i);
-                if (!hv)
-                    continue;
-
-                // Average measured current
-                const double Im = Imes[i] * 1e6; // [uA]
-
-                // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
-                const int g = hv.group();
-
-                // Serial resistors in front of the G-APD
-                double Rg = R[g];
-
-                // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
-                if (i==66)                // Pixel 830(66)
-                    Rg = 2400;            // 2400 = (3/3900 + 1/390) + 1000 + 1100
-                if (i==191 || i==193)     // Pixel 583(191) / Pixel 1401(193)
-                    Rg = 2379;            // 2379 = (4/3900 + 1/390) + 1000 + 1100
-
-                const double r = 1./(1./Rg + 1./Ravg[i]); // [Ohm]
-
-                // Offset induced by the voltage above the calibration point
-                const double Ubd = fVoltGapd[i] + fTempOffsets[i];
-                const double U0  = Ubd + overvoltage - fCalibVoltage[5][i]; // appliedOffset-fCalibrationOffset;
-                const double dI  = U0/Ravg[i]; // [V/Ohm]
-
-                // Offset at the calibration point (make sure that the calibration is
-                // valid (Im[i]>Iavg[i]) and we operate above the calibration point)
-                const double I = Im>Iavg[i] ? Im - Iavg[i] : 0; // [uA]
-
-                // Make sure that the averaged resistor is valid
-                const double dU = Ravg[i]>10000 ? r*(I*1e-6 - dI) : 0;
-
-                vec[i] = Ubd + overvoltage + dU;
-
-                // Calculate statistics only for channels with a valid calibration
-                if (Iavg[i]>0)
-                {
-                    med[g][num[g]] = dU;
-                    avg[g] += dU;
-                    num[g]++;
-
-                    if (dU<min[g])
-                        min[g] = dU;
-                    if (dU>max[g])
-                        max[g] = dU;
-
-                    data.I[i]  = Imes[i]*1e6 - fBiasVolt[i]/Ravg[i]*1e6;
-                    data.I[i] /= hv.count();
-
-                    if (i==66)
-                        data.I[i] /= 1.3;
-                    if (i==191 || i==193)
-                        data.I[i] /= 1.4;
-
-                    data.Iavg += data.I[i];
-                    data.Irms += data.I[i]*data.I[i];
-
-                    med[2][num[2]++] = data.I[i];
-                }
-            }
-        }
-        */
+        // ================================= old =======================
+        // Pixel  583: 5 31 == 191 (5)  C2 B3 P3
+        // Pixel  830: 2  2 ==  66 (4)  C0 B8 P1
+        // Pixel 1401: 6  1 == 193 (5)  C2 B4 P0
 
         double UdrpAvg = 0;
@@ -503,4 +550,6 @@
 
             // Average measured ADC value for this channel
+            // FIXME: This is a workaround for the problem with the
+            // readout of bias voltage channel 263
             const double adc = Imes[i]/* * (5e-3/4096)*/; // [A]
 
@@ -521,9 +570,13 @@
             const double U9 = fBiasVolt[i];
 
+            //          new    I8 - I9*100/fCalibR8       100
+            // change = --- = ---------------------- =  --------  = 0.8
+            //          old    I8*fCalibR8/100 - I9     fCalibR8
+
             // Serial resistors (one 1kOhm at the output of the bias crate, one 1kOhm in the camera)
             const double R4 = 2000;
 
-            // Serial resistor of the individual G-APDs
-            double R5 = 3900./N;
+            // Serial resistor of the individual G-APDs plus 50 Ohm termination
+            double R5 = 3900./N + 50;
 
             // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
@@ -559,27 +612,10 @@
             const double Udrp = Iout<0 ? 0 : R*Iout;
 
-            // Nominal breakdown voltage with correction for temperature dependence
-            const double Ubd = fVoltGapd[i] + fVoltOffset[i] + fTempOffset[i];
+            // Nominal operation voltage with correction for temperature dependence
+            const double Uop = fVoltGapd[i] + fVoltOffset[i] + fTempOffset[i];
 
             // Current overvoltage (at a G-APD with the correct 3900 Ohm resistor)
-            //const double Uov = U9-Udrp-Ubd>0 ? U9-Udrp-Ubd : 0;
-            const double Uov = U9-Udrp-Ubd>-0.44/*-0.34*/ ? U9-Udrp-Ubd : -0.44/*-0.34*/;
-
-            // Iout linear with U9 above Ubd
-            //
-            //  Rx = (U9-Ubd)/Iout
-            //  I' = (U9'-Ubd) / Rx
-            //  Udrp' = R*I'
-            //  Uov = U9' - Udrp' - Ubd
-            //  Uov = overvoltage
-            //
-            //  overvoltage = U9' - Udrp' - Ubd
-            //  overvoltage = U9' - R*I' - Ubd
-            //  overvoltage = U9' - R*((U9'-Ubd)/Rx) - Ubd
-            //  overvoltage = U9' - U9'*R/Rx + Ubd*R/Rx - Ubd
-            //  overvoltage = U9'*(1 - R/Rx) + Ubd*R/Rx - Ubd
-            //  overvoltage - Ubd*R/Rx +Ubd = U9'*(1 - R/Rx)
-            //  U9' = [ overvoltage - Ubd*R/Rx +Ubd ] / (1 - R/Rx)
-            //
+            // expressed w.r.t. to the operation voltage
+            const double Uov = (U9-Udrp)-Uop>-1.4 ? (U9-Udrp)-Uop : -1.4;
 
             // The current through one G-APD is the sum divided by the number of G-APDs
@@ -619,12 +655,4 @@
                 Iapd = Iout/(N-1);
 
-
-            // 3900   +   Rapd  = I0   ->  Uapd = Utot - 3900*I0
-            // 3900   +   Rapd  = I0   ->  Uapd = Utot - 3900*I0
-            // 3900   +   Rapd  = I0   ->  Uapd = Utot - 3900*I0
-            // 390    +   Rx    = Ix   ->  Uapd = Utot -  390*Ix
-
-            // Iout = N*I0 + Ix
-
             // The differential resistance of the G-APD, i.e. the dependence of the
             // current above the breakdown voltage, is given by
@@ -634,44 +662,26 @@
 
             // Estimate set point for over-voltage (voltage drop at the target point)
-            //const double Uset = Ubd + overvoltage + R*Iov*N;
-            //const double Uset = Uov<0.3 ? Ubd + overvoltage + Udrp : Ubd + overvoltage + Udrp*pow(overvoltage/Uov, 1.66);
-            const double Uset = 
-
-                // Uov<0 ?
-                //   Ubd + overvoltage + Udrp*pow(overvoltage/0.44/*0.34*/+1, 1.85/*1.66*/) :
-                //   Ubd + overvoltage + Udrp*pow((overvoltage+0.44/*0.34*/)/(Uov+0.44/*0.34*/), 1.85/*1.66*/);
-
-                // Uov<0 ?
-                //   Ubd + overvoltage + Udrp*pow(overvoltage+0.44, 1.3213+0.2475*(overvoltage+0.44)) :
-                //   Ubd + overvoltage + Udrp*pow(overvoltage+0.44, 1.3213+0.2475*(overvoltage+0.44))/pow(Uov+0.44, 1.3213+0.2475*(Uov+0.44));
-
-                // This estimation is based on the linear increase of the
-                // gain with voltage and the increase of the crosstalk with
-                // voltage, as measured with the overvoltage-tests (OVTEST)
-
-                Uov+0.44<0.022 ?
-            Ubd + overvoltage + Udrp*
-                exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44), 0.6) :
-            Ubd + overvoltage + Udrp*
-                exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44)/(Uov+0.44), 0.6);
-
-
-
-
-            if (fabs(overvoltage-Uov)>0.033)
+            // This estimation is based on the linear increase of the
+            // gain with voltage and the increase of the crosstalk with
+            // voltage, as measured with the overvoltage-tests (OVTEST)
+            /*
+             Uov+0.44<0.022 ?
+                Ubd + overvoltage + Udrp*exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44), 0.6) :
+                Ubd + overvoltage + Udrp*exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44)/(Uov+0.44), 0.6);
+             */
+            const double Uset =
+                Uov+1.4<0.022 ?
+                Uop + voltageoffset + Udrp*exp(0.6*(voltageoffset-Uov))*pow((voltageoffset+1.4),           0.6) :
+                Uop + voltageoffset + Udrp*exp(0.6*(voltageoffset-Uov))*pow((voltageoffset+1.4)/(Uov+1.4), 0.6);
+
+            if (fabs(voltageoffset-Uov)>0.033)
                 Ndev[0]++;
-            if (fabs(overvoltage-Uov)>0.022)
+            if (fabs(voltageoffset-Uov)>0.022)
                 Ndev[1]++;
-            if (fabs(overvoltage-Uov)>0.011)
+            if (fabs(voltageoffset-Uov)>0.011)
                 Ndev[2]++;
 
             // Voltage set point
             vec[i] = Uset;
-
-            /*
-             if (fDimBias.state()==BIAS::State::kVoltageOn && GetCurrentState()==Feedback::State::kInProgress &&
-             fabs(Uov-overvoltage)>0.033)
-             cout << setprecision(4) << setw(3) << i << ": Uov=" << Uov << " Udrp=" << Udrp << " Iapd=" << Iapd*1e6 << endl;
-             */
 
             // Calculate statistics only for channels with a valid calibration
@@ -704,10 +714,71 @@
         }
 
+
+        // ---------------------------- Calculate statistics ----------------------------------
+
+        // average and rms
+        data.Iavg /= num[2];
+        data.Irms /= num[2];
+        data.Irms -= data.Iavg*data.Iavg;
+
+        data.N = num[2];
+        data.Irms = data.Irms<0 ? 0: sqrt(data.Irms);
+
+        // median
+        sort(med[2].data(), med[2].data()+num[2]);
+
+        data.Imed = num[2]%2 ? med[2][num[2]/2] : (med[2][num[2]/2-1]+med[2][num[2]/2])/2;
+
+        // deviation
+        for (int i=0; i<num[2]; i++)
+            med[2][i] = fabs(med[2][i]-data.Imed);
+
+        sort(med[2].data(), med[2].data()+num[2]);
+
+        data.Idev = med[2][uint32_t(0.682689477208650697*num[2])];
+
+        // time difference to calibration
+        data.Tdiff = evt.GetTime().UnixTime()-fTimeCalib.UnixTime();
+
+        // Average overvoltage
+        const double Uov = (avg[0]+avg[1])/(num[0]+num[1]);
+
         // ------------------------------- Update voltages ------------------------------------
 
-        if (GetCurrentState()!=Feedback::State::kCalibrated) // WaitingForData, InProgress
+        int newstate = GetCurrentState();
+
+        if (GetCurrentState()!=Feedback::State::kCalibrated) // WaitingForData, OnStandby, InProgress, kWarning, kCritical
         {
             if (fDimBias.state()!=BIAS::State::kRamping)
             {
+                newstate = CheckLimits(data.I);
+
+                // standby and change reduction level of voltage
+                if (newstate==Feedback::State::kOnStandby)
+                {
+                    // Calculate average applied overvoltage and estimate an offset
+                    // to reach fAbsoluteMedianCurrentLimit
+                    float fAbsoluteMedianCurrentLimit = 85;
+                    const double deltaU = (Uov+1.4)*(1-pow(fAbsoluteMedianCurrentLimit/data.Imed, 1./1.7));
+
+                    if (fVoltageReduction+deltaU<0.033)
+                        fVoltageReduction = 0;
+                    else
+                    {
+                        fVoltageReduction += deltaU;
+
+                        for (int i=0; i<320; i++)
+                            vec[i] -= fVoltageReduction;
+                    }
+                }
+
+                // FIXME: What if the brightest pixel gets too bright???
+                // FIXME: What if fVolatgeReduction > U1.4V?
+
+                // set voltage in 262 -> current in 262/263
+                vec[263] = vec[262]-fVoltGapd[262]+fVoltGapd[263];
+
+//            if (fDimBias.state()!=BIAS::State::kRamping)
+//            {
                 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_VOLTAGE",
                                          vec.data(), BIAS::kNumChannels*sizeof(float));
@@ -720,9 +791,16 @@
                 ostringstream msg;
                 msg << fixed;
-                msg << setprecision(2) << "Sending U: dU(" << fTemp << "degC)="
+                msg << setprecision(2) << "dU(" << fTemp << "degC)="
                     << setprecision(3) << fTempOffsetAvg << "V+-" << fTempOffsetRms << "  Udrp="
                     << UdrpAvg << "V+-" << UdrpRms;
                 msg.unsetf(ios_base::floatfield);
-                msg << " Unom=" << overvoltage << "V Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << " [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
+
+                if (fVoltageReduction==0)
+                    msg << " Unom=" << voltageoffset << "V";
+                else
+                    msg << " Ured=" << fVoltageReduction << "V";
+
+                msg << " Uov=" << Uov;
+                msg << " Imed=" << data.Imed << "uA [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
                 Info(msg);
             }
@@ -733,17 +811,16 @@
             {
                 ostringstream msg;
-                msg << setprecision(4) << "Current status: dU(" << fTemp << "degC)=" << fTempOffsetAvg << "V+-" << fTempOffsetRms << ", Unom=" << overvoltage << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << " [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
+                msg << setprecision(4) << "Current status: dU(" << fTemp << "degC)=" << fTempOffsetAvg << "V+-" << fTempOffsetRms << ", Unom=" << voltageoffset << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << " [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
                 Info(msg);
             }
-
-        }
-
-        if (GetCurrentState()==Feedback::State::kInProgress &&
-            fDimBias.state()==BIAS::State::kRamping)
-            return GetCurrentState();
+        }
+
+        //if (GetCurrentState()>=Feedback::State::kOnStandby &&
+        //    fDimBias.state()==BIAS::State::kRamping)
+        //    return newstate;
 
         // --------------------------------- Console out --------------------------------------
 
-        if (num[0]>0 && num[1]>0 && fIsVerbose && !fDimBias.state()==BIAS::State::kRamping)
+        if (fIsVerbose && !fDimBias.state()==BIAS::State::kRamping)
         {
             sort(med[0].begin(), med[0].begin()+num[0]);
@@ -769,37 +846,15 @@
         // ---------------------------- Calibrated Currents -----------------------------------
 
-        if (num[2]>0)
-        {
-            data.Iavg /= num[2];
-            data.Irms /= num[2];
-            data.Irms -= data.Iavg*data.Iavg;
-
-            data.N = num[2];
-            data.Irms = data.Irms<0 ? 0: sqrt(data.Irms);
-
-            sort(med[2].data(), med[2].data()+num[2]);
-
-            data.Imed = num[2]%2 ? med[2][num[2]/2] : (med[2][num[2]/2-1]+med[2][num[2]/2])/2;
-
-            for (int i=0; i<num[2]; i++)
-                med[2][i] = fabs(med[2][i]-data.Imed);
-
-            sort(med[2].data(), med[2].data()+num[2]);
-
-            data.Idev = med[2][uint32_t(0.682689477208650697*num[2])];
-
-            data.Tdiff = evt.GetTime().UnixTime()-fTimeCalib.UnixTime();
-
-            // FIXME:
-            //  + Current overvoltage
-            //  + Temp offset
-            //  + User offset
-            //  + Command overvoltage
-            fDimCurrents.setQuality(GetCurrentState());
-            fDimCurrents.setData(&data, sizeof(dim_data));
-            fDimCurrents.Update(evt.GetTime());
-        }
-
-        return GetCurrentState()==Feedback::State::kCalibrated ? Feedback::State::kCalibrated : Feedback::State::kInProgress;
+        // FIXME:
+        //  + Current overvoltage
+        //  + Temp offset
+        //  + User offset
+        //  + Command overvoltage
+        fDimCurrents.setQuality(GetCurrentState());
+        fDimCurrents.setData(&data, sizeof(dim_data));
+        fDimCurrents.Update(evt.GetTime());
+
+        // FIXME: To be checked
+        return GetCurrentState()==Feedback::State::kCalibrated ? Feedback::State::kCalibrated : newstate;
     }
 
@@ -906,11 +961,13 @@
             return kSM_FatalError;
 
+        /*
         if (fDimBias.state()==BIAS::State::kRamping)
         {
             Warn("Feedback can not be started when biasctrl is in state Ramping.");
             return GetCurrentState();
-        }
-
-        fUserOffset = evt.GetFloat();
+        }*/
+
+        fUserOffset = evt.GetFloat()-1.1;
+        fVoltageReduction = 0;
 
         fCursorCur = 0;
@@ -977,4 +1034,43 @@
         return GetCurrentState();
     }
+
+    int SaveCalibration()
+    {
+        ofstream fout("feedback-calib.bin");
+
+        double mjd = fTimeCalib.Mjd();
+        fout.write((char*)&mjd, sizeof(double));
+        fout.write((char*)fCalibDeltaI.data(), BIAS::kNumChannels*sizeof(float));
+        fout.write((char*)fCalibR8.data(),     BIAS::kNumChannels*sizeof(float));
+
+        return GetCurrentState();
+    }
+
+    int LoadCalibration()
+    {
+        ifstream fin("feedback-calib.bin");
+
+        double mjd;
+
+        vector<float> di(BIAS::kNumChannels);
+        vector<float> r8(BIAS::kNumChannels);
+
+        fin.read((char*)&mjd, sizeof(double));
+        fin.read((char*)di.data(), BIAS::kNumChannels*sizeof(float));
+        fin.read((char*)r8.data(), BIAS::kNumChannels*sizeof(float));
+
+        if (!fin)
+        {
+            Warn("Reading of calibration failed.");
+            return GetCurrentState();
+        }
+
+        fTimeCalib.Mjd(mjd);
+        fCalibDeltaI = di;
+        fCalibR8 = r8;
+
+        return Feedback::State::kCalibrated;
+    }
+
 
 
@@ -1040,5 +1136,5 @@
                           "Calibration of R8"
                           "|DeltaI[uA]:Average offset"
-                          "|R8[uA]:Measured effective resistor R8"),
+                          "|R8[Ohm]:Measured effective resistor R8"),
         fDimCurrents("FEEDBACK/CALIBRATED_CURRENTS", "F:416;F:1;F:1;F:1;F:1;I:1;F:1;F:416;F:1;F:1",
                      "Calibrated currents"
@@ -1050,6 +1146,6 @@
                      "|N[uint16]:Number of valid values"
                      "|T_diff[s]:Time difference to calibration"
-                     "|U_ov[V]:Calculated overvoltage"
-                     "|U_nom[V]:Nominal overvoltage"
+                     "|U_ov[V]:Calculated overvoltage w.r.t. operation voltage"
+                     "|U_nom[V]:Nominal overvoltage w.r.t. operation voltage"
                      "|dU_temp[V]:Correction calculated from temperature"
                     ),
@@ -1097,6 +1193,13 @@
         AddStateName(Feedback::State::kWaitingForData, "WaitingForData",
                      "Current control started, waiting for valid temperature and current data.");
+
+        AddStateName(Feedback::State::kOnStandby, "OnStandby",
+                     "Current control in progress but with limited voltage.");
         AddStateName(Feedback::State::kInProgress, "InProgress",
                      "Current control in progress.");
+        AddStateName(Feedback::State::kWarning, "Warning",
+                     "Current control in progress but current warning level exceeded.");
+        AddStateName(Feedback::State::kCritical, "Critical",
+                     "Current control in progress but critical current limit exceeded.");
 
 
@@ -1125,4 +1228,12 @@
         AddEvent("RESET_OFFSETS", Feedback::State::kConnected, Feedback::State::kCalibrated)
             (bind(&StateMachineFeedback::ResetOffset, this))
+            ("");
+
+
+        AddEvent("SAVE_CALIBRATION", Feedback::State::kCalibrated)
+            (bind(&StateMachineFeedback::SaveCalibration, this))
+            ("");
+        AddEvent("LOAD_CALIBRATION", Feedback::State::kConnected)
+            (bind(&StateMachineFeedback::LoadCalibration, this))
             ("");
 
