Index: /trunk/FACT++/src/smartfact.cc
===================================================================
--- /trunk/FACT++/src/smartfact.cc	(revision 17019)
+++ /trunk/FACT++/src/smartfact.cc	(revision 17020)
@@ -444,9 +444,4 @@
     deque<float> fTngWeatherDustHist;
     Time  fTngWeatherDustTime;
-
-    vector<float> fFeedbackCalibration;
-
-    float fFeedbackTempOffset;
-    float fFeedbackUserOffset;
 
     vector<float> fBiasControlVoltageVec;
@@ -1073,47 +1068,199 @@
     }
 
-    int HandleFeedbackCalibration(const EventImp &d)
-    {
-        if (!CheckDataSize(d, "Feedback:Calibration", 3*4*416))
-        {
-            fFeedbackCalibration.clear();
+    int HandleFeedbackCalibratedCurrents(const EventImp &d)
+    {
+        if (!CheckDataSize(d, "Feedback:CalibratedCurrents", (416+1+1+1+1+1+416+1+1)*sizeof(float)+sizeof(uint32_t)))
             return GetCurrentState();
-        }
 
         const float *ptr = d.Ptr<float>();
-        fFeedbackCalibration.assign(ptr+2*416, ptr+3*416);
+
+        double power_tot = 0;
+        double power_apd = 0;
+
+        // Calibrate the data (subtract offset)
+        for (int i=0; i<320; i++)
+        {
+            // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
+            const int N = fPixelMap.hv(i).count();
+
+            // Serial resistor of the individual G-APDs
+            double R5 = 3900/N;
+
+            // This is also valid for the patches with wrong resistors,
+            // because Iapd is a factor f larger but R a factor f smaller
+            double Iapd = ptr[i] * 1e-6;  // [A]
+            double Iout = Iapd*N;         // [A]
+
+            double UdrpCam =     1000 *Iout;  // Voltage seen by everything in Camera
+            double UdrpApd = (R5+2000)*Iout;  // Voltage seen by G-APD
+
+            const double pwrCam = Iapd * (fBiasControlVoltageVec[i]-UdrpCam);
+            const double pwrApd = Iapd * (fBiasControlVoltageVec[i]-UdrpApd);
+
+            // Total power participated in the camera at the G-APD
+            // and the serial resistors (total voltage minus voltage
+            // drop at resistors in bias crate)
+            power_tot += pwrCam;
+
+            // Power consumption per G-APD
+            power_apd += pwrApd;
+        }
+
+        // Divide by number of summed channels, convert to mW
+        power_apd /= 320e-3; // [mW]
+
+        if (power_tot<1e-3)
+            power_tot = 0;
+        if (power_apd<1e-3)
+            power_apd = 0;
+
+        fBiasControlPowerTot = power_tot;
+
+        // --------------------------------------------------------
+
+        // Get the maximum of each patch
+        vector<float> val(320, 0);
+        for (int i=0; i<320; i++)
+        {
+            const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
+            val[idx] = ptr[i];
+        }
+
+        // Write the 160 patch values to a file
+        WriteCam(d, "cam-biascontrol-current", val, 100);
+
+        // --------------------------------------------------------
+
+        const Statistics stat(vector<float>(ptr, ptr+320));
+
+        // Exclude the three crazy channels
+        fBiasControlCurrentMed = stat.med;
+        fBiasControlCurrentMax = stat.max;
+
+        // Store a history of the last 60 entries
+        fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
+        if (fBiasControlCurrentHist.size()>360)
+            fBiasControlCurrentHist.pop_front();
+
+        // write the history to a file
+        WriteHist(d, "hist-biascontrol-current", fBiasControlCurrentHist, 100);
+
+        // --------------------------------------------------------
+
+        string col1 = HTML::kGreen;
+        string col2 = HTML::kGreen;
+        string col3 = HTML::kGreen;
+        string col4 = HTML::kGreen;
+
+        if (stat.min>90)
+            col1 = HTML::kYellow;
+        if (stat.min>110)
+            col1 = HTML::kRed;
+
+        if (stat.med>90)
+            col2 = HTML::kYellow;
+        if (stat.med>110)
+            col2 = HTML::kRed;
+
+        if (stat.avg>90)
+            col3 = HTML::kYellow;
+        if (stat.avg>110)
+            col3 = HTML::kRed;
+
+        if (stat.max>90)
+            col4 = HTML::kYellow;
+        if (stat.max>110)
+            col4 = HTML::kRed;
+
+        ostringstream out;
+        out << setprecision(3);
+        out << d.GetJavaDate() << '\n';
+        out << HTML::kGreen << '\t' << "yes" << '\n';
+        out << col1 << '\t' << stat.min << '\n';
+        out << col2 << '\t' << stat.med << '\n';
+        out << col3 << '\t' << stat.avg << '\n';
+        out << col4 << '\t' << stat.max << '\n';
+        out << HTML::kWhite << '\t' << power_tot << "W [" << power_apd << "mW]\n";
+        ofstream(fPath+"/current.data") << out.str();
+
+        // --------------------------------------------------------
+
+        const float Unom = ptr[2*416+6];
+        const float Utmp = ptr[2*416+7];
+
+        vector<float> Uov(ptr+416+6, ptr+416+6+320);
+
+        WriteCam(d, "cam-feedback-overvoltage", Uov, 0.2, 1.0);
+
+        const Statistics stat2(Uov);
+
+        out.str("");
+        out << d.GetJavaDate() << '\n';
+        out << setprecision(3);
+        out << HTML::kWhite << '\t' << Utmp << '\n';
+        out << HTML::kWhite << '\t' << Unom << '\n';
+        out << HTML::kWhite << '\t' << stat2.min << '\n';
+        out << HTML::kWhite << '\t' << stat2.med << '\n';
+        out << HTML::kWhite << '\t' << stat2.avg << '\n';
+        out << HTML::kWhite << '\t' << stat2.max << '\n';
+        ofstream(fPath+"/feedback.data") << out.str();
 
         return GetCurrentState();
     }
 
-    int HandleFeedbackDeviation(const EventImp &d)
-    {
-        if (!CheckDataSize(d, "Feedback:Deviation", (2*416+2)*4))
+    int HandleBiasCurrent(const EventImp &d)
+    {
+        if (fDimFeedback.state()>=Feedback::State::kCalibrated)
             return GetCurrentState();
 
-        const float *ptr = d.Ptr<float>();
-        vector<float> dev(ptr+416, ptr+416+320);
-
-        fFeedbackTempOffset = ptr[2*416];
-        fFeedbackUserOffset = ptr[2*416+1];
-
+        if (!CheckDataSize(d, "BiasControl:Current", 832))
+            return GetCurrentState();
+
+        // Convert dac counts to uA
+        vector<float> v(320);
         for (int i=0; i<320; i++)
-            dev[i] -= fFeedbackTempOffset+fFeedbackUserOffset;
+            v[i] = d.Ptr<uint16_t>()[i] * 5000./4096;
+
+        fBiasControlPowerTot = 0;
+
+        // Get the maximum of each patch
+        vector<float> val(320, 0);
+        for (int i=0; i<320; i++)
+        {
+            const PixelMapEntry &hv = fPixelMap.hv(i);
+            if (!hv)
+                continue;
+
+            const int idx = (hv.hw()/9)*2+hv.group();
+            val[idx] = v[i];
+        }
 
         // Write the 160 patch values to a file
-        WriteCam(d, "cam-feedback-deviation", dev, 1);
-
-        const Statistics stat(dev, 3);
+        WriteCam(d, "cam-biascontrol-current", val, 1000);
+
+        const Statistics stat(v, 0, 3);
+
+        // Exclude the three crazy channels
+        fBiasControlCurrentMed = stat.med;
+        fBiasControlCurrentMax = stat.max;
+
+        // Store a history of the last 60 entries
+        fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
+        if (fBiasControlCurrentHist.size()>360)
+            fBiasControlCurrentHist.pop_front();
+
+        // write the history to a file
+        WriteHist(d, "hist-biascontrol-current", fBiasControlCurrentHist, 1000);
 
         ostringstream out;
+        out << setprecision(3);
         out << d.GetJavaDate() << '\n';
-        out << HTML::kWhite << '\t' << fFeedbackUserOffset << '\n';
-        out << setprecision(3);
-        out << HTML::kWhite << '\t' << fFeedbackTempOffset << '\n';
+        out << HTML::kWhite<< '\t' << "no" << '\n';
         out << HTML::kWhite << '\t' << stat.min << '\n';
         out << HTML::kWhite << '\t' << stat.med << '\n';
         out << HTML::kWhite << '\t' << stat.avg << '\n';
         out << HTML::kWhite << '\t' << stat.max << '\n';
-        ofstream(fPath+"/feedback.data") << out.str();
+        out << HTML::kWhite << '\t' << "---\n";
+        ofstream(fPath+"/current.data") << out.str();
 
         return GetCurrentState();
@@ -1141,5 +1288,5 @@
         }
 
-        if (fDimBiasControl.state()==BIAS::State::kVoltageOn)
+        if (fDimBiasControl.state()==BIAS::State::kVoltageOn || fDimBiasControl.state()==BIAS::State::kRamping)
             WriteCam(d, "cam-biascontrol-voltage", val, 10, 65);
         else
@@ -1154,121 +1301,4 @@
         out << HTML::kWhite << '\t' << stat.max << '\n';
         ofstream(fPath+"/voltage.data") << out.str();
-
-        return GetCurrentState();
-    }
-
-    int HandleBiasCurrent(const EventImp &d)
-    {
-        if (!CheckDataSize(d, "BiasControl:Current", 832))
-            return GetCurrentState();
-
-        // Convert dac counts to uA
-        vector<float> v(320);
-        for (int i=0; i<320; i++)
-            v[i] = d.Ptr<uint16_t>()[i] * 5000./4096;
-
-        const bool cal = !fFeedbackCalibration.empty() && !fBiasControlVoltageVec.empty();
-
-        double power_tot = 0;
-        double power_apd = 0;
-
-        // 3900 Ohm/n + 1000 Ohm + 1100 Ohm  (with n=4 or n=5)
-        const double R[2] = { 3075, 2870 };
-
-        // Calibrate the data (subtract offset)
-        if (cal)
-            for (int i=0; i<320; i++)
-            {
-                // Measued current minus leakage current (bias crate calibration)
-                v[i] -= fBiasControlVoltageVec[i]/fFeedbackCalibration[i]*1e6;
-
-                // Total power participated in the camera at the G-APD
-                // and the serial resistors (total voltage minus voltage
-                // drop at resistors in bias crate)
-                power_tot += v[i]*(fBiasControlVoltageVec[i] - 1100e-6*v[i])*1e-6;
-
-                // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
-                const int g = fPixelMap.hv(i).group();
-
-                // Current per G-APD
-                v[i] /= g ? 5 : 4;
-
-                // Power consumption per G-APD
-                if (i!=66 && i!=191 && i!=193)
-                    power_apd += v[i]*(fBiasControlVoltageVec[i]-R[g]*v[i]*1e-6)*1e-6;
-            }
-
-        // Divide by number of summed channels, convert to mW
-        power_apd /= 317e-3; // [mW]
-
-        if (power_tot<1e-3)
-            power_tot = 0;
-        if (power_apd<1e-3)
-            power_apd = 0;
-
-        fBiasControlPowerTot = power_tot;
-
-        // Get the maximum of each patch
-        vector<float> val(320, 0);
-        for (int i=0; i<320; i++)
-        {
-            const int idx = (fPixelMap.hv(i).hw()/9)*2+fPixelMap.hv(i).group();
-            val[idx] = v[i];
-        }
-
-        // Write the 160 patch values to a file
-        WriteCam(d, "cam-biascontrol-current", val, 100);
-
-        const Statistics stat(v, 0, 3);
-
-        // Exclude the three crazy channels
-        fBiasControlCurrentMed = stat.med;
-        fBiasControlCurrentMax = stat.max;
-
-        // Store a history of the last 60 entries
-        fBiasControlCurrentHist.push_back(fBiasControlCurrentMed);
-        if (fBiasControlCurrentHist.size()>360)
-            fBiasControlCurrentHist.pop_front();
-
-        // write the history to a file
-        WriteHist(d, "hist-biascontrol-current", fBiasControlCurrentHist, 100);
-
-        const string col0 = cal ? HTML::kGreen : HTML::kWhite;
-
-        string col1 = col0;
-        string col2 = col0;
-        string col3 = col0;
-        string col4 = col0;
-
-        if (cal && stat.min>65)
-            col1 = kYellow;
-        if (cal && stat.min>80)
-            col1 = kRed;
-
-        if (cal && stat.med>65)
-            col2 = kYellow;
-        if (cal && stat.med>80)
-            col2 = kRed;
-
-        if (cal && stat.avg>65)
-            col3 = kYellow;
-        if (cal && stat.avg>80)
-            col3 = kRed;
-
-        if (cal && stat.max>65)
-            col4 = kYellow;
-        if (cal && stat.max>80)
-            col4 = kRed;
-
-        ostringstream out;
-        out << setprecision(2);
-        out << d.GetJavaDate() << '\n';
-        out << col0 << '\t' << (cal?"yes":"no") << '\n';
-        out << col1 << '\t' << stat.min << '\n';
-        out << col2 << '\t' << stat.med << '\n';
-        out << col3 << '\t' << stat.avg << '\n';
-        out << col4 << '\t' << stat.max << '\n';
-        out << HTML::kWhite << '\t' << power_tot << "W [" << power_apd << "mW]\n";
-        ofstream(fPath+"/current.data") << out.str();
 
         return GetCurrentState();
@@ -1978,5 +2008,5 @@
     }
 
-    pair<vector<float>, pair<Time, float>> GetLightCondition(Nova::EquPosn *src, double jd)
+    pair<vector<float>, pair<Time, float>> GetLightCondition(double jd)
     {
         jd = floor(jd);
@@ -2174,5 +2204,5 @@
                     ccol++;
 
-                    /*const*/ pair<vector<float>, pair<Time, float>> lc = GetLightCondition(&pos, now.JD());
+                    /*const*/ pair<vector<float>, pair<Time, float>> lc = GetLightCondition(now.JD());
                     if (!lc.first.empty())
                     {
@@ -2295,4 +2325,7 @@
             fDimBiasControl.state()==BIAS::State::kVoltageOn;
 
+        const bool calibrated =
+            fDimFeedback.state()>=Feedback::State::kCalibrated;
+
         const bool haderr = !fErrorList.empty();
 
@@ -2324,7 +2357,7 @@
 
 
-        newerr |= SetError(bias_on && !fFeedbackCalibration.empty() && fBiasControlCurrentMed>90,
+        newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMed>90,
                            "Median current exceeds 90&micro;A/pix");
-        newerr |= SetError(bias_on && !fFeedbackCalibration.empty() && fBiasControlCurrentMax>110,
+        newerr |= SetError(bias_on && calibrated && fBiasControlCurrentMax>110,
                            "Maximum current exceeds 110&micro;A/pix");
 
@@ -2351,8 +2384,7 @@
                            "Warning timecheck not running");
 
-        newerr |= SetError(fDimFeedback.state()!=Feedback::State::kCalibrating &&
-                           fDimBiasControl.state()==BIAS::State::kVoltageOn &&
-                           fBiasControlVoltageMed>3 &&
-                           fFeedbackCalibration.empty(),
+        newerr |= SetError(fDimBiasControl.state()==BIAS::State::kVoltageOn &&
+                           fDimFeedback.state()<Feedback::State::kCalibrating &&
+                           fBiasControlVoltageMed>3,
                            "Bias voltage switched on, but bias crate not calibrated");
 
@@ -2697,5 +2729,5 @@
                 col = HTML::kRed;
 
-            const bool cal = !fFeedbackCalibration.empty();
+            const bool cal = fDimFeedback.state()>=Feedback::State::kCalibrated;
 
             // Feedback is currently calibrating => Blue
@@ -2756,5 +2788,5 @@
             out << GetStateHtml(fDimFtmControl,     FTM::State::kConnected);
             out << GetStateHtml(fDimBiasControl,    BIAS::State::kConnected);
-            out << GetStateHtml(fDimFeedback,       Feedback::State::kConnectedFSC);
+            out << GetStateHtml(fDimFeedback,       Feedback::State::kConnected);
             out << GetStateHtml(fDimRateControl,    RateControl::State::kConnected);
             out << GetStateHtml(fDimFscControl,     FSC::State::kConnected);
@@ -2890,8 +2922,6 @@
             (bind(&StateMachineSmartFACT::HandleTngWeatherDust,      this, placeholders::_1));
 
-        Subscribe("FEEDBACK/DEVIATION")
-            (bind(&StateMachineSmartFACT::HandleFeedbackDeviation,   this, placeholders::_1));
-        Subscribe("FEEDBACK/CALIBRATION")
-            (bind(&StateMachineSmartFACT::HandleFeedbackCalibration, this, placeholders::_1));
+        Subscribe("FEEDBACK/CALIBRATED_CURRENTS")
+            (bind(&StateMachineSmartFACT::HandleFeedbackCalibratedCurrents, this, placeholders::_1));
 
         Subscribe("BIAS_CONTROL/VOLTAGE")
