Index: trunk/FACT++/scripts/Main.js
===================================================================
--- trunk/FACT++/scripts/Main.js	(revision 19444)
+++ trunk/FACT++/scripts/Main.js	(revision 19452)
@@ -194,5 +194,5 @@
 // ================================================================
 
-function OpenLid()
+function OpenLid(fast)
 {
     /*
@@ -227,4 +227,8 @@
         dim.log("Camera response: "+ret.data.replace(/\n/g,"/")+" ["+ret.rc+"]");
     }
+
+    if (!fast)
+        return;
+
     dim.wait("LID_CONTROL", "Open", 30000);
 
@@ -1508,5 +1512,5 @@
         //  ...no drs calibration was done yet
         var drscal = (run%4==0 && (remaining>15 && diff>70)) || diff==null;
-    
+
         if (point)
         {
@@ -1545,5 +1549,5 @@
         }
 
-        if (drscal)
+        if (drscal && !obs[sub].nodrs)
         {
             doDrsCalibration("data");  // will turn voltage off
@@ -1562,8 +1566,11 @@
             continue;
 
-        OpenLid();
-
-        // This is now th right time to wait for th drive to be stable
-        dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
+        OpenLid(obs[sub].grb);
+
+        // This is now the right time to wait for the drive to be stable
+        if (!obs[sub].grb)
+            dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
+        else
+            v8.timeout(150000, function() { if (dim.state("DRIVE_CONTROL").name=="Approaching" || dim.state("DRIVE_CONTROL").name=="Tracking" || dim.state("DRIVE_CONTROL").name=="OnTrack") return true; });
 
         // Now check the voltage... (do not start a lot of stuff just to do nothing)
@@ -1578,8 +1585,50 @@
         // and wait for the feedback to get stable
         service_feedback.voltageOn();
-        service_feedback.waitForVoltageOn();
+        if (!obs[sub].grb)
+            service_feedback.waitForVoltageOn();
+
+        // For the first run to be taken (ratecontrol)
+        // Lid must be != Closed
+        // Drive must be >= Moving
+        // Setting the threshold can take 2-3 seconds
+
+        if (obs[sub].grb && run==0)
+        {
+            // Instead of a pedestal run, we take a very short grb mode run
+            // which is a classical data run but does not change the thresholds
+            // This gives the voltages (typ. 5s) and currents (typ. 3s) time to
+            // stabilize and threshold setting for the next run will be
+            // reliable and fast but we have data already
+            dim.log("Starting GRB mode.");
+
+            // Whatever we do... it does not make sense to start with a closed lid
+            // If the lid is not yet open, wait for the lid to be open and
+            // go on as usual.
+            if (dim.state("LID_CONTROL")!="Open")
+            {
+                dim.wait("LID_CONTROL", "Open", 30000);
+                service_feedback.waitForVoltageOn();
+            }
+
+            // Doing this manually makes it more flexible but turns off
+            // automatic Critical mode checking of the feedback and
+            // automatic reconnect
+            var nextrun = sub_startrun.get().obj['next'];
+            dim.log("Take run %3d".$(nextrun)+" [grb-mode]");
+
+            dim.send("MCP/START", -1, -1, "grb"); // Use previous threshold settings if ratecontrol "InProgress"
+            dim.wait("MCP", "TakingData", 15000);
+
+            dim.wait("FEEDBACK",      "InProgress",  45000); // This is most likely the first to happen
+            dim.wait("DRIVE_CONTROL", "Tracking",   150000); // 110s for turning and 30s for stabilizing
+
+            v8.wait(1000); // By now we should have collected the required three current events in FEEDBACK
+            // Current are averaged over 10s. So by now we should have a reasonable idea of the brightness of the sky
+            // Individual pixels might still suffer wrong settings
+            // Starting a new run takes less than 2s (for 60s runs this is 3%)
+        }
 
         // If pointing had changed, do calibration
-        if (!irq && point)
+        if (!irq && point && !obs[sub].grb)
         {
             dim.log("Starting calibration.");
@@ -1603,5 +1652,5 @@
         var twilight = Sun.horizon(-16).isUp;
 
-        if (twilight)
+        if (twilight || obs[sub].grb)
         {
             for (var i=0; i<5 && !irq; i++)
Index: trunk/FACT++/src/gcn.cc
===================================================================
--- trunk/FACT++/src/gcn.cc	(revision 19444)
+++ trunk/FACT++/src/gcn.cc	(revision 19452)
@@ -1,3 +1,4 @@
 #include <functional>
+#include <boost/algorithm/string/join.hpp>
 
 #include "Dim.h"
@@ -16,4 +17,5 @@
 
 #include "HeadersGCN.h"
+#include "HeadersToO.h"
 
 #include <QtXml/QDomDocument>
@@ -44,5 +46,5 @@
     Time fLastKeepAlive;
 
-    GCN::PaketType_t GetType(const QDomElement &what)
+    QString GetParamValue(const QDomElement &what, const string &name)
     {
         const QDomNodeList param = what.elementsByTagName("Param");
@@ -50,18 +52,25 @@
         {
             const QDomElement elem = param.at(i).toElement();
-            if (elem.attribute("name").toStdString()!="Packet_Type")
-                continue;
-
-            const uint16_t val = elem.attribute("value").toUInt();
-            const auto it = fTypes.find(val);
-            if (it!=fTypes.end())
-                return it->second;
-
-            Warn("Unknown paket type "+to_string(val)+".");
-        }
-
+            if (elem.attribute("name").toStdString()==name)
+                return elem.attribute("value");
+        }
+
+        return "";
+    }
+
+    GCN::PaketType_t GetType(const QDomElement &what)
+    {
+        const auto value = GetParamValue(what, "Packet_Type");
+        if (value.isEmpty())
+            return { -1, "", "" };
+
+        const uint16_t val = value.toUInt();
+        const auto it = fTypes.find(val);
+        if (it!=fTypes.end())
+            return it->second;
+
+        Warn("Unknown paket type "+to_string(val)+".");
         return { -1, "", "" };
     }
-
 
     int ProcessXml(const QDomElement &root)
@@ -117,4 +126,6 @@
                 return -1;
 
+            const GCN::PaketType_t ptype = GetType(what);
+
             const QDomElement date   = who.firstChildElement("Date");
             const QDomElement author = who.firstChildElement("Author");
@@ -135,11 +146,41 @@
             const QDomElement errad  = pos2d.firstChildElement("Error2Radius");
 
-            if (date.isNull()   || author.isNull() || sname.isNull() || //desc.isNull() ||
-                obsdat.isNull() || obsloc.isNull() || coord.isNull() || time.isNull() ||
-                pos2d.isNull()  || name1.isNull()  || name2.isNull() || val2.isNull() ||
-                c1.isNull()     || c2.isNull()     || errad.isNull())
+            const bool is_gw = ptype.type==150 || ptype.type==151 || ptype.type==153;
+
+            vector<string> missing;
+            if (date.isNull())
+                missing.emplace_back("Date");
+            if (author.isNull())
+                missing.emplace_back("Author");
+            if (sname.isNull() && !is_gw)
+                missing.emplace_back("shortName");
+            if (obsdat.isNull())
+                missing.emplace_back("ObsDataLocation");
+            if (obsloc.isNull())
+                missing.emplace_back("ObservationLocation");
+            if (coord.isNull())
+                missing.emplace_back("AstroCoords");
+            if (time.isNull())
+                missing.emplace_back("Time/TimeInstant/ISOTime");
+            if (pos2d.isNull() && !is_gw)
+                missing.emplace_back("Position2D");
+            if (name1.isNull() && !is_gw)
+                missing.emplace_back("Name1");
+            if (name1.isNull() && !is_gw)
+                missing.emplace_back("Name2");
+            if (val2.isNull() && !is_gw)
+                missing.emplace_back("Value2");
+            if (c1.isNull() && !is_gw)
+                missing.emplace_back("C1");
+            if (c2.isNull() && !is_gw)
+                missing.emplace_back("C2");
+            if (errad.isNull() && !is_gw)
+                missing.emplace_back("Error2Radius");
+
+            if (!missing.empty())
+            {
+                Warn("Missing elements: "+boost::algorithm::join(missing, ", "));
                 return -1;
-
-            const GCN::PaketType_t ptype = GetType(what);
+            }
 
             //  59/31: Konus LC / IPN raw         [observation]
@@ -195,7 +236,8 @@
             const string unit = pos2d.attribute("unit").toStdString();
 
-            const double ra  = c1.text().toDouble();
-            const double dec = c2.text().toDouble();
-            const double err = errad.text().toDouble();
+            const uint32_t trig  = GetParamValue(what, "TrigID").toUInt();
+            const double   ra    = c1.text().toDouble();
+            const double   dec   = c2.text().toDouble();
+            const double   err   = errad.text().toDouble();
 
             const string n1 = name1.text().toStdString();
@@ -212,6 +254,21 @@
             Out() << "  " << setw(5) << "ERR"  << "= " << err << unit << '\n';
 
-            if (n1=="RA" && n2=="Dec" && unit=="deg")
+            const bool has_coordinates = n1=="RA" && n2=="Dec" && unit=="deg";
+
+            if (has_coordinates)
             {
+                const ToO::DataGRB data =
+                {
+                    .type   = ptype.type,
+                    .trigid = trig,
+                    .ra     = ra,
+                    .dec    = dec,
+                    .err    = err,
+                };
+
+                Info("Sending ToO #"+to_string(trig)+" ["+role+"]");
+                Dim::SendCommandNB("TARGET_OF_OPPORTUNITY/SCHEDULE_GCN", data);
+
+/*
                 const double jd = Time().JD();
 
@@ -244,4 +301,5 @@
                     Out() << '\n';
                 }
+*/
             }
 
