Index: trunk/FACT++/src/smartfact.cc
===================================================================
--- trunk/FACT++/src/smartfact.cc	(revision 14140)
+++ trunk/FACT++/src/smartfact.cc	(revision 14145)
@@ -422,9 +422,11 @@
 
      int64_t fFadControlNumEvents;
+     int64_t fFadControlStartRun;
      int32_t fFadControlDrsStep;
     vector<uint32_t> fFadControlDrsRuns;
 
     deque<float> fFtmControlTriggerRateHist;
-    bool         fFtmControlNewRunStarted;
+    uint32_t     fFtmControlNewRunStarted;
+    uint32_t     fFtmControlTriggerRateTooLow;
 
     float fFtmPatchThresholdMed;
@@ -526,19 +528,23 @@
 
     template<class T>
-        void WriteBinaryVec(const EventImp &d, const string &fname, const vector<T> &vec, double scale, double offset=0)
+        void WriteBinaryVec(const Time &tm, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="")
     {
         if (vec.size()==0)
             return;
 
-        const Statistics stat(vec[0]);
-
         ostringstream out;
-        out << d.GetJavaDate() << '\n';
+        out << tm.JavaDate() << '\n';
         out << offset << '\n';
         out << offset+scale << '\n';
         out << setprecision(3);
-        out << stat.min << '\n';
-        out << stat.med << '\n';
-        out << stat.max << '\x7f';
+        if (!title.empty())
+            out << title <<  '\x7f';
+        else
+        {
+            const Statistics stat(vec[0]);
+            out << stat.min << '\n';
+            out << stat.med << '\n';
+            out << stat.max << '\x7f';
+        }
         for (auto it=vec.begin(); it!=vec.end(); it++)
         {
@@ -565,7 +571,19 @@
 
     template<class T>
+        void WriteBinaryVec(const EventImp &d, const string &fname, const vector<T> &vec, double scale, double offset=0, const string &title="")
+    {
+        WriteBinaryVec(d.GetTime(), fname, vec, scale, offset, title);
+    }
+
+    template<class T>
+        void WriteBinary(const Time &tm, const string &fname, const T &t, double scale, double offset=0)
+    {
+        WriteBinaryVec(tm, fname, vector<T>(&t, &t+1), scale, offset);
+    }
+
+    template<class T>
         void WriteBinary(const EventImp &d, const string &fname, const T &t, double scale, double offset=0)
     {
-        WriteBinaryVec(d, fname, vector<T>(&t, &t+1), scale, offset);
+        WriteBinaryVec(d.GetTime(), fname, vector<T>(&t, &t+1), scale, offset);
     }
 
@@ -716,16 +734,16 @@
         if (fMcpConfigurationState==MCP::State::kTakingData && d.GetQoS()==MCP::State::kIdle)
         {
-            //...and no script is running just play a simple 'tick'
-            if (fDimControl.state()<-2)
+            // ...and no script is running just play a simple 'tick'
+            // ...and a script is running just play a simple 'tick'
+            if (/*fDimControl.state()<-2 &&*/ fDimControl.scriptdepth==0)
                 SetAudio("dong");
-
-            // ...and a script is running just play a simple 'tick'
-            if (fDimControl.state()>=-2)
+            else
                 SetAudio("losticks");
 
             ostringstream out;
-            out << d.GetTimeAsStr("%H:%M:%S") << " <#darkgreen>" << d.Ptr<char>(16);
+            out << d.GetTimeAsStr("%H:%M:%S") << " <#darkred>" << d.Ptr<char>(16);
             if (!fDriveControlSourceName.empty())
                 out << " [" << fDriveControlSourceName << ']';
+            out << " (N=" << fFadControlNumEvents << ')';
             out << "</#><br/>";
             fMcpConfigurationHist.push_back(out.str());
@@ -738,7 +756,9 @@
 
             ostringstream out;
-            out << d.GetTimeAsStr("%H:%M:%S") << " <#darkred>" << fMcpConfigurationName;
+            out << d.GetTimeAsStr("%H:%M:%S") << " <#darkgreen>" << fMcpConfigurationName;
             if (!fDriveControlSourceName.empty())
                 out << " [" << fDriveControlSourceName << ']';
+            if (fFadControlStartRun>0)
+                out << " (Run " << fFadControlStartRun << ')';
             out << "</#><br/>";
             fMcpConfigurationHist.push_back(out.str());
@@ -1175,4 +1195,17 @@
     }
 
+    int HandleFadStartRun(const EventImp &d)
+    {
+        if (!CheckDataSize(d, "FadControl:StartRun", 16))
+        {
+            fFadControlStartRun = -1;
+            return GetCurrentState();
+        }
+
+        fFadControlStartRun = d.Get<int64_t>();
+
+        return GetCurrentState();
+    }
+
     int HandleFadDrsRuns(const EventImp &d)
     {
@@ -1246,5 +1279,8 @@
     {
         if (!CheckDataSize(d, "FtmControl:TriggerRates", 24+160+640+8))
+        {
+            fFtmControlTriggerRateTooLow = 0;
             return GetCurrentState();
+        }
 
         const double crate = d.Get<float>(20);     // Camera rate
@@ -1253,11 +1289,22 @@
         if (crate<0)
         {
-            fFtmControlNewRunStarted = true;
+            fFtmControlNewRunStarted     = 0;
+            fFtmControlTriggerRateTooLow = 0;
             return GetCurrentState();
         }
-        fFtmControlNewRunStarted = false;
-
-        const float *brates = d.Ptr<float>(24);     // Board rate
-        const float *prates = d.Ptr<float>(24+160); // Patch rate
+
+        // At the end of a run sometimes the trigger rate drops (the
+        // service is trasmitted) before the run is 'officially' finished
+        // by the MCP. Hence, we get a warning. So we have to require
+        // two consecutive low rates.
+        if (crate<0.1)
+            fFtmControlTriggerRateTooLow++;
+        else
+            fFtmControlTriggerRateTooLow=0;
+
+        fFtmControlNewRunStarted++;
+
+        const float *brates = d.Ptr<float>(24);                  // Board rate
+        const float *prates = d.Ptr<float>(24+40*sizeof(float)); // Patch rate
 
         // Store a history of the last 60 entries
@@ -1576,6 +1623,6 @@
         out << HTML::kWhite << '\t' << fFtmBoardThresholdMed << '\n';
         out << HTML::kWhite << '\t' << fFtmPatchThresholdMed << '\n';
-        out << HTML::kWhite << '\t' << pow(10, fRateScanDataHist[0].back()) << '\n';
-        out << HTML::kWhite << '\t' << max << '\n';
+        out << HTML::kWhite << '\t' << floor(pow(10, fRateScanDataHist[0].back())+.5) << '\n';
+        out << HTML::kWhite << '\t' << floor(max+.5) << '\n';
 
         ofstream(fPath+"/ratescan.data") << out.str();
@@ -1721,4 +1768,45 @@
     }
 
+#ifdef HAVE_NOVA
+    pair<vector<float>, pair<Time, float>> GetVisibility(ln_equ_posn *src, ln_lnlat_posn *observer, double jd)
+    {
+        jd = floor(jd);
+
+        const double jd0 = fmod(fSun.fSetAstronomical.JD(),  1);
+        const double jd1 = fmod(fSun.fRiseAstronomical.JD(), 1);
+
+        ln_equ_posn  moon;
+        ln_equ_posn *pos = src ? src : &moon;
+
+        double max   = 0;
+        double maxjd = jd0;
+
+
+        vector<float> alt;
+        for (double h=0; h<1; h+=1./(24*12))
+        {
+            if (src==0)
+                ln_get_lunar_equ_coords(jd+h, &moon);
+
+            ln_hrz_posn hrz;
+            ln_get_hrz_from_equ(pos, observer, jd+h, &hrz);
+
+            if (h>jd0 && h<jd1 && hrz.alt>15)
+                alt.push_back(hrz.alt);
+
+            if (hrz.alt>max)
+            {
+                max   = hrz.alt;
+                maxjd = jd+h;
+            }
+        }
+
+        if (max<15)
+            return make_pair(vector<float>(), make_pair(Time(), 0));
+
+        return make_pair(alt, make_pair(maxjd, maxjd>jd0+jd&&maxjd<jd1+jd?max:0));
+    }
+#endif
+
     void UpdateAstronomy()
     {
@@ -1762,5 +1850,5 @@
 
         if (!fMoon.visible)
-            out << HTML::kWhite << "\t&mdash;\n";
+            out << HTML::kWhite << "\t&mdash;\t\n";
         else
         {
@@ -1779,7 +1867,24 @@
         }
 
-        ostringstream out2;
+        ostringstream out2, out3;
         out2 << setprecision(3);
         out2 << now.JavaDate() << '\n';
+        out3 << now.JavaDate() << '\n';
+
+        map<Time, pair<string, float>> culmination;
+        vector<vector<float>> alt;
+
+#ifdef HAVE_NOVA
+        ln_lnlat_posn observer;
+        observer.lng = lon;
+        observer.lat = lat;
+
+        const pair<vector<float>, pair<Time, float>> vism = GetVisibility(0, &observer, now.JD());
+        if (vism.first.size()>0)
+        {
+            alt.push_back(vism.first);
+            culmination[vism.second.first] = make_pair("Moon", vism.second.second);
+        }
+#endif
 
 #ifdef HAVE_SQL
@@ -1791,4 +1896,5 @@
             out  << HTML::kWhite << '\t';
             out2 << HTML::kWhite << '\t';
+            out3 << HTML::kWhite << '\t';
 
             for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
@@ -1798,8 +1904,4 @@
                 const double dec  = (*v)[2];
 #ifdef HAVE_NOVA
-                ln_lnlat_posn observer;
-                observer.lng = lon;
-                observer.lat = lat;
-
                 ln_equ_posn pos;
                 pos.ra  = ra*15;
@@ -1808,4 +1910,13 @@
                 ln_hrz_posn hrz;
                 ln_get_hrz_from_equ(&pos, &observer, now.JD(), &hrz);
+
+                const pair<vector<float>, pair<Time, float>> vis = GetVisibility(&pos, &observer, now.JD());
+                if (vis.first.size()>0)
+                {
+                    alt.push_back(vis.first);
+                    culmination[vis.second.first] = make_pair(name, vis.second.second);
+                }
+
+                //out3 << vis.second.first << "/" << vis.second.second << " ";
 
                 string col = HTML::kWhite;
@@ -1835,8 +1946,29 @@
                 out << "</tr>";
             }
+
+            for (auto it=culmination.begin(); it!=culmination.end(); it++)
+            {
+                if (it!=culmination.begin())
+                    out3 << ", ";
+                out3 << "<B>" << it->second.first << "</B>";
+                if (it->second.second>0)
+                    out3 << " [" << nearbyint(90-it->second.second) << "&deg;]";
+            }
+
+            ostringstream title;
+            title << "Alt ";
+            title << fSun.fSetAstronomical.GetAsStr("%H:%M");
+            title << " / ";
+            title << ((fSun.fRiseAstronomical-fSun.fSetAstronomical)/20).minutes();
+            title << "' / ";
+            title << fSun.fRiseAstronomical.GetAsStr("%H:%M");
+
             out  << '\n';
             out2 << '\n';
+            out3 << '\n';
             out  << HTML::kWhite << '\t' << Time()-now << '\n';
             out2 << HTML::kWhite << '\t' << Time()-now << '\n';
+
+            WriteBinaryVec(now, "hist-visibility", alt, 75, 15, title.str());
         }
         catch (const exception &e)
@@ -1846,4 +1978,5 @@
             out  << HTML::kWhite << '\t' << "ERROR  - "+string(e.what()) << '\n';
             out2 << HTML::kWhite << '\t' << "ERROR  - "+string(e.what()) << '\n';
+            out3 << HTML::kWhite << '\t' << "ERROR  - "+string(e.what()) << '\n';
         }
 #endif
@@ -1851,4 +1984,5 @@
         ofstream(fPath+"/moon.data") << out.str();
         ofstream(fPath+"/source-list.data") << out2.str();
+        ofstream(fPath+"/visibility.data") << out3.str();
     }
 
@@ -1925,9 +2059,6 @@
                            "Sensor temperature exceeds outside temperature by more than 8&deg;C");
 
-        if (fFtmControlTriggerRateHist.size()>0 && !fFtmControlNewRunStarted)
-        {
-            newerr |= SetError(fFtmControlTriggerRateHist.size()>0 && fFtmControlTriggerRateHist.back()<0.01 && fDimMcp.state()==MCP::State::kTakingData,
-                               "Trigger rate below 10mHz during data taking");
-        }
+        newerr |= SetError(fFtmControlNewRunStarted>0 && fFtmControlTriggerRateTooLow>1 && fDimMcp.state()==MCP::State::kTakingData,
+                           "Trigger rate below 100mHz during data taking");
 
         newerr |= SetError(fDimTimeCheck.state()==1,
@@ -2116,5 +2247,5 @@
         if (fDimDNS.online() && fDimDriveControl.state()>=Drive::State::kArmed)   // Armed, Moving, Tracking
         {
-            const double dev = fDriveControlTrackingDevHist.size()>0 ? fDriveControlTrackingDevHist.back() : 0;
+            const uint32_t dev = fDriveControlTrackingDevHist.size()>0 ? round(fDriveControlTrackingDevHist.back()) : 0;
             const State rc = fDimDriveControl.description();
             string col = HTML::kGreen;
@@ -2167,5 +2298,4 @@
                 }
         }
-
         if (fDimDNS.online() && fDimDriveControl.state()==0x100)   // Armed, Moving, Tracking
             out << " [ERR]";
@@ -2213,5 +2343,5 @@
         {
             string col = HTML::kGreen;
-            if (fFtmControlTriggerRateHist.size()>0 && !fFtmControlNewRunStarted)
+            if (fFtmControlTriggerRateHist.size()>0 && fFtmControlNewRunStarted>0)
             {
                 if (fFtmControlTriggerRateHist.back()<15)
@@ -2437,4 +2567,6 @@
         Subscribe("FAD_CONTROL/EVENTS")
             (bind(&StateMachineSmartFACT::HandleFadEvents,           this, placeholders::_1));
+        Subscribe("FAD_CONTROL/START_RUN")
+            (bind(&StateMachineSmartFACT::HandleFadStartRun,         this, placeholders::_1));
         Subscribe("FAD_CONTROL/DRS_RUNS")
             (bind(&StateMachineSmartFACT::HandleFadDrsRuns,          this, placeholders::_1));
