Index: trunk/FACT++/src/fscctrl.cc
===================================================================
--- trunk/FACT++/src/fscctrl.cc	(revision 17169)
+++ trunk/FACT++/src/fscctrl.cc	(revision 17170)
@@ -11,4 +11,5 @@
 #include "Console.h"
 #include "Converter.h"
+#include "../externals/Interpolator2D.h"
 
 #include "tools.h"
@@ -34,5 +35,8 @@
     ofstream fDumpStream;
 
+
 protected:
+    vector<pair<double,double>> fPositionsSensors;
+    vector<pair<double,double>> fPositionsBias;
 
     virtual void UpdateTemp(float, const vector<float> &)
@@ -626,4 +630,14 @@
         fDump = b;
     }
+
+    void SetPositionsSensors(const vector<pair<double,double>> &vec)
+    {
+        fPositionsSensors = vec;
+    }
+
+    void SetPositionsBias(const vector<pair<double,double>> &vec)
+    {
+        fPositionsBias = vec;
+    }
 };
 
@@ -636,5 +650,8 @@
 private:
 
+    vector<double> fLastRms;
+
     DimDescribedService fDimTemp;
+    DimDescribedService fDimTemp2;
     DimDescribedService fDimHum;
     DimDescribedService fDimVolt;
@@ -650,4 +667,64 @@
     {
         Update(fDimTemp, temp, time);
+
+        vector<double> T;
+        vector<Interpolator2D::vec> xy;
+
+        T.reserve(31);
+        xy.reserve(31);
+
+        double avg = 0;
+        double rms = 0;
+
+        // Create a list of all valid sensors
+        for (int i=0; i<31; i++)
+            if (temp[i]!=0)
+            {
+                T.emplace_back(temp[i]);
+                xy.emplace_back(fPositionsSensors[i].first, fPositionsSensors[i].second);
+
+                avg += temp[i];
+                rms += temp[i]*temp[i];
+            }
+
+        if (T.size()==0)
+        {
+            Warn("No valid sensor temperatures.");
+            return;
+        }
+
+        avg /= T.size();
+        rms /= T.size();
+        rms -= avg*avg;
+        rms = rms<0 ? 0 : sqrt(rms - avg*avg);
+
+        // Clean broken reports
+        const double cut_val = 0.015;
+        const bool reject = rms>4 || (fabs(fLastRms[0]-fLastRms[1])<=cut_val && fabs(rms-fLastRms[0])>cut_val);
+
+        fLastRms[1] = fLastRms[0];
+        fLastRms[0] = rms;
+
+        if (reject)
+        {
+            Warn("Suspicious temperature values rejecte for BIAS_TEMP.");
+            return;
+        }
+
+        // Create interpolator for the corresponding sensor positions
+        Interpolator2D inter(xy);
+
+        // Calculate weights for the output positions
+        if (!inter.SetOutputGrid(fPositionsBias))
+        {
+            Warn("Temperature values rejecte for BIAS_TEMP (calculation of weights failed).");
+            return;
+        }
+
+        // Interpolate the data
+        T = inter.Interpolate(T);
+
+        // Update the Dim service with the interpolated positions
+        Update(fDimTemp2, vector<float>(T.cbegin(), T.cend()), time);
     }
 
@@ -666,10 +743,8 @@
         Update(fDimCurrent, curr, time);
     }
-
-    
 
 public:
     ConnectionDimFSC(ba::io_service& ioservice, MessageImp &imp) :
-        ConnectionFSC(ioservice, imp),
+        ConnectionFSC(ioservice, imp), fLastRms(2),
         fDimTemp   ("FSC_CONTROL/TEMPERATURE", "F:1;F:31;F:8;F:8;F:4;F:4;F:4",
                     "|t[s]:FSC uptime"
@@ -680,4 +755,7 @@
                     "|T_back[deg C]:FTM backpanel temperatures FTM (top/bottom), FSC (top/bottom)"
                     "|T_eth[deg C]:Ethernet switches temperatures top (front/back), bottom (f/b)"),
+        fDimTemp2  ("FSC_CONTROL/BIAS_TEMP", "F:1;F:320",
+                    "|t[s]:FSC uptime"
+                    "|T[deg C]:Interpolated temperatures at bias patch positions"),
         fDimHum    ("FSC_CONTROL/HUMIDITY", "F:1;F:4",
                     "|t[s]:FSC uptime"
@@ -709,4 +787,5 @@
                     "|FLP_I[A]:FLP - light pulser")
     {
+        fLastRms[0] = 1.5;
     }
 
@@ -824,7 +903,45 @@
     }
 
+    vector<pair<double,double>> ReadVector(const string &filename) const
+    {
+        vector<pair<double,double>> vec;
+
+        ifstream fin(filename);
+        while (1)
+        {
+            double x, y;
+            fin >> x;
+            fin >> y;
+            if (!fin)
+                break;
+
+            vec.emplace_back(x, y);
+        }
+
+        return vec;
+    }
+
     int EvalOptions(Configuration &conf)
     {
         fFSC.SetVerbose(!conf.Get<bool>("quiet"));
+
+        const string fname1 = conf.Get<string>("sesnor-pos-file");
+        const auto v1 = ReadVector(fname1);
+        if (v1.size() != 31)
+        {
+            Error("Reading sensor positions from "+fname1+"failed ("+to_string(v1.size())+")");
+            return 1;
+        }
+
+        const string fname2 = conf.Get<string>("patch-pos-file");
+        const auto v2 = ReadVector(fname2);
+        if (v2.size() != 320)
+        {
+            Error("Reading bias patch positions from "+fname2+"failed ("+to_string(v2.size())+")");
+            return 1;
+        }
+
+        fFSC.SetPositionsSensors(v1);
+        fFSC.SetPositionsBias(v2);
 
         SetEndpoint(conf.Get<string>("addr"));
@@ -850,4 +967,6 @@
         ("no-dim",        po_bool(),  "Disable dim services")
         ("addr,a",        var<string>("localhost:5000"),  "Network address of FSC")
+        ("sensor-pos-file", var<string>()->required(),  "File with the positions of the 31 temperature sensors")
+        ("patch-pos-file",  var<string>()->required(),  "File with the positions of the 320 bias patches")
         ("quiet,q",       po_bool(true),  "Disable printing contents of all received messages (except dynamic data) in clear text.")
         ;
@@ -868,5 +987,5 @@
 {
     cout <<
-        "The ftmctrl controls the FSC (FACT Slow Control) board.\n"
+        "The fscctrl controls the FSC (FACT Slow Control) board.\n"
         "\n"
         "The default is that the program is started without user intercation. "
