Index: /trunk/FACT++/src/scheduler.cc
===================================================================
--- /trunk/FACT++/src/scheduler.cc	(revision 19507)
+++ /trunk/FACT++/src/scheduler.cc	(revision 19508)
@@ -54,9 +54,10 @@
 {
     // Input
-    float moon_min     =  10;
-    float moon_max     = 170;
-    float sun_max      = -12;
-    float zd_max       =  75;
-    float current_max  = 110;
+    float moon_min      =  10;
+    float moon_max      = 170;
+    float sun_max       = -12;
+    float zd_max        =  75;
+    float current_max   = 110;
+    float threshold_max =  10;
 
     // Output
@@ -66,9 +67,11 @@
     double moon_dist   = -1;
     double current     = -1;
-
-    bool valid_zd      = false;
-    bool valid_current = false;
-    bool valid_sun     = false;
-    bool valid_moon    = false;
+    double threshold   = -1;
+
+    bool valid_zd        = false;
+    bool valid_current   = false;
+    bool valid_sun       = false;
+    bool valid_moon      = false;
+    bool valid_threshold = false;
 
     bool visible       = false;
@@ -82,8 +85,13 @@
         current   = FACT::PredictI(solarobj, equ);
 
-        valid_moon    = moon_dist>moon_min && moon_dist<moon_max;
-        valid_zd      = position.zd<zd_max;
-        valid_sun     = solarobj.fSunHrz.alt<sun_max;
-        valid_current = current<current_max;
+        const double ratio = pow(cos(position.zd*M_PI/180), -2.664);
+
+        threshold = position.zd<90 ? ratio*pow(current/6.2, 0.394) : -1;
+
+        valid_moon      = moon_dist>moon_min && moon_dist<moon_max;
+        valid_zd        = position.zd<zd_max;
+        valid_sun       = solarobj.fSunHrz.alt<sun_max;
+        valid_current   = current<current_max;
+        valid_threshold = threshold>0 && threshold<threshold_max;
 
         visible = valid_moon && valid_zd && valid_sun && valid_current;
@@ -414,12 +422,163 @@
     Database fConnection;
     uint16_t fVerbose;
-    bool     fDryRun;
 
     Time     fKeepAliveDeadline;
     uint16_t fKeepAliveInterval;
 
-    bool ScheduleImp(const string &name, const double &ra, const double &dec)
-    {
-        fDryRun = true;
+    int16_t GetSourceKey(const string &name, const double &ra, const double &dec)
+    {
+        const double min_dist   = 0.1;
+        const double check_dist = 2.0;
+
+
+        int32_t source_key = -1;
+
+        const string query1 =
+            "SELECT fSourceKey, fSourceName, ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist\n"
+            " FROM Source\n"
+            " WHERE fSourceTypeKEY=1\n"
+            " HAVING Dist<"+to_string(check_dist)+"\n"
+            " ORDER BY Dist ASC";
+
+        const mysqlpp::StoreQueryResult res1 = fConnection.query(query1).store();
+
+        if (res1.num_rows())
+        {
+            Info("Found "+to_string(res1.num_rows())+" sources with a distance less than "+to_string(check_dist)+"\u00b0");
+
+            for (size_t i=0; i<res1.num_rows(); i++)
+            {
+                const mysqlpp::Row &row = res1[i];
+                Info(" "+string(row["fSourceName"])+" ["+string(row["fSourceKey"])+"]: D="+string(row["Dist"])+"\u00b0");
+            }
+
+            const mysqlpp::Row &row = res1[0];
+            if (double(row["Dist"])<min_dist)
+            {
+                Warn("Sources closer than "+to_string(check_dist)+"\u00b0 detected.");
+                Warn("Overwriting source key with: "+string(row["fSourceName"])+" ["+string(row["fSourceKey"])+"]: D="+string(row["Dist"])+"\u00b0");
+                source_key = res1[0]["fSourceKey"];
+            }
+        }
+
+        //Out() << Time()-stopwatch << endl;
+
+        const string query2 =
+            "SELECT fSourceKey FROM Source WHERE fSourceName='"+name+"'";
+
+        const mysqlpp::StoreQueryResult res2 = fConnection.query(query2).store();
+
+        if (res2.num_rows())
+        {
+            source_key = res2[0]["fSourceKey"];
+            Info("A source with the same name (key="+to_string(source_key)+") was found in the Source table.");
+        }
+
+        return source_key;
+    }
+
+    float GetWobbleAngle(const double &ra, const double &dec)
+    {
+        const double wobble_offset = 0.6;
+        const double camera_radius = 2.3;
+        const double magnitude_max = 4.5;
+
+        /*
+         Mag Cnt
+         -2  1
+         -1  3
+          0  11
+          1  33
+          2  121
+          3  321
+          4  871
+          5  1364
+          6  404
+          7  12
+        */
+
+        // The wobble position lay in the plane which is normal the to the plane source-center-star
+        // and goes through
+
+        double wobble_angle  = 0;
+
+        const string query0 =
+            "SELECT fSourceKEY, fRightAscension, fDeclination, fMagnitude,\n"
+            " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist\n"
+            " FROM Source\n"
+            " WHERE (fSourceTypeKey=2 OR fSourceTypeKey=3) AND fMagnitude<"+to_string(magnitude_max)+"\n"
+            " HAVING Dist<"+to_string(camera_radius+wobble_offset)+"\n"
+            " ORDER BY fMagnitude ASC";
+
+        Out() << query0 << endl;
+
+        const mysqlpp::StoreQueryResult res0 = fConnection.query(query0).store();
+
+        Info("Found "+to_string(res0.num_rows())+" stars in the camera field-of-view with magnitude less than "+to_string(magnitude_max));
+
+        for (size_t i=0; i<::min<size_t>(10, res0.num_rows()); i++)
+        {
+            const mysqlpp::Row &row = res0[i];
+
+            // TVector3 souce, star;
+            // source.SetMagThetaPhi(1, rad(90-dec), rad(ra));
+            // star.SetMagThetaPhi(  1, rad(90-dec), rad(ra));
+            // 
+            // star.RotateZ(  -source.Phi());
+            // source.RotateZ(-source.Phi());
+            // 
+            // star.RotateY(  180-source.Theta());
+            // source.RotateY(180-source.Theta());
+            // 
+            // rho = star.Phi();
+
+            const double rad = M_PI/180;
+
+            const double dec0 = rad * dec;
+            const double ra0  = rad * ra;
+
+            const double dec1 = rad * row["fDeclination"];
+            const double ra1  = rad * row["fRightAscension"] * 15;
+
+            const double s2 = sin(ra0-ra1);
+            const double c2 = cos(ra0-ra1);
+
+            const double s0 = cos(dec0);
+            const double c0 = sin(dec0);
+
+            const double c1 = cos(dec1);
+            const double s1 = sin(dec1);
+
+            const double k0 =           -s2*c1;
+            const double k1 = s0*s1 - c0*c2*c1;
+
+            const double rho = atan(k0/k1) / rad; // atan2(k0, k1)/rad
+
+            if (i==0)
+                wobble_angle = rho;
+
+            Info(" "+Tools::Form("Mag=%5.2f  Dist=%5.1f  Phi=%6.1f", (double)row["fMagnitude"], (double)row["Dist"], rho));
+        }
+
+        if (res0.num_rows())
+        {
+            Info("The brightest star (M="+string(res0[0]["fMagnitude"])+") at distance is visible at a wobble angle of "+Tools::Form("%.2f", wobble_angle)+"\u00b0");
+            Info("Wobble angles determined as "+Tools::Form("%.2f", wobble_angle-90)+"\u00b0 and "+Tools::Form("%.2f", wobble_angle+90)+"\u000b");
+        }
+        else
+        {
+            Info("Using default wobble angles.");
+        }
+
+        return wobble_angle;
+    }
+
+
+    bool ScheduleImp(const string &name, const double &ra, const double &dec, const bool &known_only)
+    {
+        const bool fDryRun = GetCurrentState()!=ToO::State::kArmed;
+
+        if (fDryRun)
+            Warn("Scheduler not armed!");
 
         Time stopwatch;
@@ -449,155 +608,13 @@
          */
 
-        const double min_dist = 0.1;
-        const double wobble_offset = 0.6;
-        const double camera_radius = 2.3;
-        const double magnitude_max = 4.5;
-
-        double wobble_angle  = 0;
-
-        /*
-         Mag Cnt
-         -2  1
-         -1  3
-          0  11
-          1  33
-          2  121
-          3  321
-          4  871
-          5  1364
-          6  404
-          7  12
-        */
-
-        // The wobble position lay in the plane whihc is normal the to the plane source-center-star
-        // and goes through
-
-        const string query0 =
-            "SELECT fSourceKEY, fRightAscension, fDeclination, fMagnitude,\n"
-            " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist\n"
-            " FROM Source\n"
-            " WHERE (fSourceTypeKey=2 OR fSourceTypeKey=3) AND fMagnitude<"+to_string(magnitude_max)+"\n"
-            " HAVING Dist<"+to_string(camera_radius+wobble_offset)+"\n"
-            " ORDER BY fMagnitude ASC";
-
-        Out() << query0 << endl;
-
-        const mysqlpp::StoreQueryResult res0 = fConnection.query(query0).store();
+        int32_t source_key = GetSourceKey(name, ra, dec);
 
         Out() << Time()-stopwatch << endl;
 
-        Info("Found "+to_string(res0.num_rows())+" stars in the camera field-of-view with magnitude less than "+to_string(magnitude_max));
-
-        for (size_t i=0; i<::min<size_t>(10, res0.num_rows()); i++)
-        {
-            const mysqlpp::Row &row = res0[i];
-
-            // TVector3 souce, star;
-            // source.SetMagThetaPhi(1, rad(90-dec), rad(ra));
-            // star.SetMagThetaPhi(  1, rad(90-dec), rad(ra));
-            // 
-            // star.RotateZ(  -source.Phi());
-            // source.RotateZ(-source.Phi());
-            // 
-            // star.RotateY(  180-source.Theta());
-            // source.RotateY(180-source.Theta());
-            // 
-            // rho = star.Phi();
-
-            const double rad = M_PI/180;
-
-            const double dec0 = rad * dec;
-            const double ra0  = rad * ra;
-
-            const double dec1 = rad * row["fDeclination"];
-            const double ra1  = rad * row["fRightAscension"] * 15;
-
-            const double s2 = sin(ra0-ra1);
-            const double c2 = cos(ra0-ra1);
-
-            const double s0 = cos(dec0);
-            const double c0 = sin(dec0);
-
-            const double c1 = cos(dec1);
-            const double s1 = sin(dec1);
-
-            const double k0 =           -s2*c1;
-            const double k1 = s0*s1 - c0*c2*c1;
-
-            const double rho = atan(k0/k1) / rad; // atan2(k0, k1)/rad
-
-            if (i==0)
-                wobble_angle = rho;
-
-            Info(" "+Tools::Form("Mag=%5.2f  Dist=%5.1f  Phi=%6.1f", (double)row["fMagnitude"], (double)row["Dist"], rho));
-        }
-
-        if (res0.num_rows())
-        {
-            Info("The brightest star (M="+string(res0[0]["fMagnitude"])+") at distance is visible at a wobble angle of "+Tools::Form("%.2f", wobble_angle)+"\u00b0");
-            Info("Wobble angles determined as "+Tools::Form("%.2f", wobble_angle-90)+"\u00b0 and "+Tools::Form("%.2f", wobble_angle+90)+"\u000b");
-        }
-        else
-        {
-            Info("Using default wobble angles.");
-        }
-
-        const string query1 =
-            "SELECT fSourceKey, fSourceName\n"
-            " FROM Source\n"
-            " WHERE ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+")<"+to_string(min_dist)+"\n"
-            " ORDER BY fSourceKey ASC";
-
-        const mysqlpp::StoreQueryResult res1 = fConnection.query(query1).store();
-
-        if (res1.num_rows())
-        {
-            Warn("The following sources in the source table have a distance less than "+to_string(min_dist)+"\u00b0");
-
-            for (size_t i=0; i<res1.num_rows(); i++)
-            {
-                const mysqlpp::Row &row = res1[i];
-                Warn(" "+string(row["fSourceName"])+" ["+to_string(uint32_t(row["fSourceKey"]))+"]");
-            }
-        }
-
-        Out() << Time()-stopwatch << endl;
-
-        int32_t source_key = -1;
-
-        const string query2 =
-            "SELECT fSourceKey FROM Source WHERE fSourceName='"+name+"'";
-
-        const mysqlpp::StoreQueryResult res2 = fConnection.query(query2).store();
-
-        if (res2.num_rows())
-        {
-            source_key = res2[0]["fSourceKey"];
-            Info("A source with the same name (key="+to_string(source_key)+") was found in the Source table.");
-        }
-/*
-        if (source_key<0)
-        {
-            const string query =
-                "INSERT INTO Source\n"
-                " (fSourceName, fRightAscension, fDeclination, fWobbleAngle0, fWobbleAngle1, fSourceTypeKey, fIsToO) VALUES\n"
-                " ('"+name+"', "+to_string(ra/15.)+", "+to_string(dec)+", "+to_string(wobble_angle-90)+", "+to_string(wobble_angle+90)+", 1, 1)";
-
-            if (!fDryRun)
-            {
-                auto q = fConnection.query(query);
-                q.execute();
-                Info(q.info());
-
-                source_key = q.insert_id();
-
-                Info(string(fDryRun?"[dry-run] ":"")+"The new source got the key "+to_string(source_key));
-            }
-            else
-                Out() << query << endl;
-
-        }
-*/
-        Out() << Time()-stopwatch << endl;
+        if (known_only && source_key<0)
+        {
+            Warn("Observation of only known sources requested but no known source found.");
+            return false;
+        }
 
         /*
@@ -782,8 +799,14 @@
         // ====================================================================
 
+        // FIXME: Check if source is not already scheduled anyways
+
         Info("Source will be scheduled.");
 
         if (source_key<0)
         {
+            const double wobble_angle = GetWobbleAngle(ra, dec);
+
+            Out() << Time()-stopwatch << endl;
+
             const string query =
                 "INSERT INTO Source\n"
@@ -839,5 +862,5 @@
 
         const string queryI =
-            "INSERT INTO Schedule\n (fStart,fMeasurementID,fUser,fData,fSoureKey,fMeasurementTypeKey)\n"
+            "INSERT INTO Schedule\n (fStart,fMeasurementID,fUser,fData,fSourceKey,fMeasurementTypeKey)\n"
             "VALUES\n "+string(boost::algorithm::join(insert, ",\n "));
 
@@ -854,11 +877,6 @@
 
         // Empty interrupt to stop data taking as early as possible
-        Dim::SendCommandNB("DIM_CONTROL/INTERRUPT");
-
-        // Reload sources as early as possible
-        Dim::SendCommandNB("DRIVE_CONTROL/RELOAD_SOURCES");
-
-        // Start pointing procedure as early as possible
-        //Dim::SendCommand("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
+        // Triggers also RELOAD_SOURCES
+        Dim::SendCommandNB("DIM_CONTROL/INTERRUPT", "prepare");
 
         // ---------------------------------------------------------
@@ -867,20 +885,18 @@
 
         // ---------------------------------------------------------
-        /*
-         if (!list.empty())
-         {
-         const mysqlpp::SimpleResult res = fConnection.query(queryD).execute();
-         Info(to_string(res.rows())+" row(s) deleted from Schedule.");
-         }
-
-         // ---------------------------------------------------------
-
-         if (!insert.empty())
-         {
-         auto q = fConnection.query(queryI);
-         q.execute();
-         Info(q.info());
-         }
-         */
+        if (!list.empty())
+        {
+            const mysqlpp::SimpleResult res = fConnection.query(queryD).execute();
+            Info(to_string(res.rows())+" row(s) deleted from Schedule.");
+        }
+
+        // ---------------------------------------------------------
+
+        if (!insert.empty())
+        {
+            auto q = fConnection.query(queryI);
+            q.execute();
+            Info(q.info());
+        }
         // ---------------------------------------------------------
 
@@ -898,9 +914,9 @@
     }
 
-    bool Schedule(const string &name, const double &ra, const double &dec)
+    bool Schedule(const string &name, const double &ra, const double &dec, const bool &known_only)
     {
         try
         {
-            return ScheduleImp(name, ra, dec);
+            return ScheduleImp(name, ra, dec, known_only);
         }
         catch (const exception &e)
@@ -935,29 +951,84 @@
         const ToO::DataGRB &grb = evt.Ref<ToO::DataGRB>();
 
-        const GCN::PaketType_t *ptr=GCN::kTypes;
-        for (; ptr->type>=0 && ptr->type!=grb.type; ptr++);
-        if (ptr->type==-1)
-        {
-            Warn("Unknown Packet Type: "+to_string(ptr->type));
+        const auto it = GCN::PaketTypes.find(grb.type);
+
+        if (it==GCN::PaketTypes.end())
+        {
+            Warn("Unknown Packet Type: "+to_string(grb.type));
             return GetCurrentState();
         }
 
+        const auto &paket = it->second;
+
         ostringstream out;
-        out << "Received: '" << ptr->name << "' ID=" << grb.type << " NUM=" << grb.trigid << " RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err;
+        out << "Received: '" << paket.name << "' ID=" << grb.type << " NUM=" << grb.trigid << " RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err;
+
+        /*
+            const std::vector<int16_t> typelist =
+            {
+                51,  // INTEGRAL_POINTDIR
+
+                53,  // INTEGRAL_WAKEUP
+                54,  // INTEGRAL_REFINED
+                55,  // INTEGRAL_OFFLINE
+
+                // 56, // INTEGRAL_WEAK
+                // 59, // KONUS_LC
+
+                60,  // SWIFT_BAT_GRB_ALERT
+                61,  // SWIFT_BAT_GRB_POS_ACK
+                62,  // SWIFT_BAT_GRB_POS_NACK
+
+                83,  // SWIFT_POINTDIR
+
+                97,  // SWIFT_BAT_QL_POS
+
+                100, // AGILE_GRB_WAKEUP
+                101, // AGILE_GRB_GROUND
+                102, // AGILE_GRB_REFINED
+
+                110, // FERMI_GBM_FLT_POS
+                111, // FERMI_GBM_GND_POS
+                112, // FERMI_GBM_LC
+                115, // FERMI_GBM_TRANS
+
+                123, // FERMI_LAT_TRANS
+                125, // FERMI_LAT_MONITOR
+
+                // 134, // MAXI_UNKNOWN
+                // 135, // MAXI_KNOWN
+                // 136, // MAXI_TEST
+
+                157, // AMON_ICECUBE_COINC
+                158, // AMON_ICECUBE_HESE
+
+                169, // AMON_ICECUBE_EHE
+                171, // HAWC_BURST_MONITOR
+                173, // ICECUBE_GOLD
+                174, // ICECUBE_BRONZE
+            };
+         */
 
         Info(out);
-        Info(ptr->description);
+        Info(paket.description);
 
         const CheckVisibility check(grb.ra, grb.dec);
 
-        Info(string("Source is")+(check.visible?" ":" NOT ")+"visible.");
-
-        Info(string("Sun altitude:   ")+(check.valid_sun    ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.solarobj.fSunHrz.alt)+"\u00b0]");
-        Info(string("Moon distance:  ")+(check.valid_moon   ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.moon_dist)+"\u00b0]");
-        Info(string("Zenith angle:   ")+(check.valid_zd     ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.position.zd)+"\u00b0]");
-        Info(string("Current:        ")+(check.valid_current?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.current)+" \u00b5A]");
-
-        const string name = ptr->instrument+"#"+to_string(grb.trigid);
-        Info("Source name set to '"+name+"'");
+        //Info(string("Source is")+(check.visible?" ":" NOT ")+"visible.");
+
+        Info(string("Sun altitude:   ")+(check.valid_sun      ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.solarobj.fSunHrz.alt)+"\u00b0]");
+        Info(string("Moon distance:  ")+(check.valid_moon     ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.moon_dist)+"\u00b0]");
+        Info(string("Zenith angle:   ")+(check.valid_zd       ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.position.zd)+"\u00b0]");
+        Info(string("Current:        ")+(check.valid_current  ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.current)+" \u00b5A]");
+        Info(string("Rel. threshold: ")+(check.valid_threshold?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.threshold)+"]");
+
+        if (!check.visible)
+        {
+            Info("Source not observable... skipped.");
+            return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
+        }
+
+        const string name = paket.instrument+"#"+to_string(grb.trigid);
+        Info("Source is visible... name set to '"+name+"'");
 
         /*
@@ -1023,8 +1094,19 @@
 */
 
-        Schedule(name, grb.ra, grb.dec);
-
-        return fConnection.connected() ? ToO::State::kConnected : ToO::State::kDisconnected;
+        const bool known_only = grb.type==51 || grb.type==83;
+
+        Schedule(name, grb.ra, grb.dec, known_only);
+
+        return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
         //return GetCurrentState();
+    }
+
+    int Enable()
+    {
+        return GetCurrentState()==ToO::State::kConnected ? ToO::State::kArmed : GetCurrentState();
+    }
+    int Disable()
+    {
+        return GetCurrentState()==ToO::State::kArmed ? ToO::State::kConnected : GetCurrentState();
     }
 
@@ -1053,5 +1135,11 @@
         }
 
-        return fConnection.connected() ? ToO::State::kConnected : ToO::State::kDisconnected;
+        if (!fConnection.connected())
+            return ToO::State::kDisconnected;
+
+        if (fConnection.connected() && GetCurrentState()<=ToO::State::kDisconnected)
+            return ToO::State::kConnected;
+
+        return GetCurrentState();
     }
 
@@ -1065,4 +1153,7 @@
         AddStateName(ToO::State::kConnected, "Connected",
                      "All needed subsystems are connected to their hardware, no action is performed.");
+
+        AddStateName(ToO::State::kArmed, "Armed",
+                     "All needed subsystems are connected to their hardware, scheduling in progress.");
 
         AddEvent("GCN", "S:1;I:1;D:1;D:1;D:1")
@@ -1070,8 +1161,16 @@
             (""
              "|type[int16]:TypeID (see HeaderGCN.h)"
-             "|num[uint32]:Right ascension"
+             "|trig[uint32]:TriggerID"
              "|ra[deg]:Right ascension"
              "|dec[deg]:Declination"
              "|err[deg]:Error radius");
+
+        AddEvent("START", "")
+            (bind(&StateMachineToO::Enable, this/*, placeholders::_1*/))
+            ("");
+
+        AddEvent("STOP", "")
+            (bind(&StateMachineToO::Disable, this/*, placeholders::_1*/))
+            ("");
     }
 
@@ -1092,5 +1191,4 @@
     {
         fVerbose = !conf.Get<bool>("quiet");
-        fDryRun = conf.Get<bool>("dry-run");
         fKeepAliveInterval = conf.Get<uint16_t>("keep-alive");
         fUri = conf.Get<string>("schedule-database");
@@ -1115,5 +1213,4 @@
     control.add_options()
         ("quiet,q", po_bool(), "")
-        ("dry-run", po_bool(), "Switch off any alteration of the database")
         ("keep-alive", var<uint16_t>(uint16_t(300)), "Interval in seconds to ping (reconnect) the database server")
         ("schedule-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
