Index: trunk/BNC/src/bncantex.cpp
===================================================================
--- trunk/BNC/src/bncantex.cpp	(revision 11123)
+++ trunk/BNC/src/bncantex.cpp	(revision 11134)
@@ -632,6 +632,50 @@
       prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI,
       st.yaw*180.0/M_PI, psiNom*180.0/M_PI, callGap*1440.0);
-    st.valid  = false;
-    st.inTurn = false;
+    st.valid    = false;
+    st.inTurn   = false;
+    st.inShadow = false;
+  }
+
+  // Block IIF in the Earth's shadow (cylinder, around midnight for |mu| <
+  // muShadow): the satellite yaws at a constant rate from its yaw at shadow
+  // entry to the nominal yaw at shadow exit (Kouba 2009, Dilssner 2010)
+  // -------------------------------------------------------------------------
+  if (blockType == "IIF") {
+    const double RE = 6378137.0;
+    double cosMuShadow = sqrt(std::max(0.0, 1.0 - RE * RE / (r * r))) / cos(beta);
+    bool   inShadow    = (cosMuShadow < 1.0 && cos(mu) > cosMuShadow);
+    if (inShadow) {
+      double muExit = acos(cosMuShadow);
+      if (!st.inShadow) {
+        double psiExit = atan2(-tanBeta, sin(muExit));      // nominal yaw at shadow exit
+        double tRem    = (muExit - mu) / nRate;              // remaining time in the shadow [s]
+        st.inShadow   = true;
+        st.shadowYaw0 = st.valid ? st.yaw : psiNom;
+        st.shadowRate = (tRem > 0.0) ? remainder(psiExit - st.shadowYaw0, 2.0 * M_PI) / tRem : 0.0;
+        st.shadowMjd0 = Mjd;
+        _gpsYawLog += QString().asprintf(
+          "%s gpsYaw SHADOW-ENTER Mjd=%.6f beta=%6.3f mu=%7.2f yaw=%7.2f exit=%7.2f rate=%6.3f\n",
+          prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI, st.shadowYaw0*180.0/M_PI,
+          psiExit*180.0/M_PI, st.shadowRate*180.0/M_PI);
+      }
+      double psiEff = st.shadowYaw0 + st.shadowRate * (Mjd - st.shadowMjd0) * 86400.0;
+      while (psiEff >  M_PI) psiEff -= 2.0 * M_PI;
+      while (psiEff < -M_PI) psiEff += 2.0 * M_PI;
+      st.yaw         = psiEff;
+      st.lastCallMjd = Mjd;
+      st.valid       = true;
+      st.inTurn      = false;
+      return psiEff;
+    }
+    else if (st.inShadow) {
+      // shadow exit: a remaining difference to the nominal yaw is caught up
+      // at the maximum rate (catch-up branch below)
+      st.inShadow = false;
+      st.inTurn   = true;
+      st.turnSign = (remainder(psiNom - st.yaw, 2.0 * M_PI) >= 0.0) ? 1.0 : -1.0;
+      _gpsYawLog += QString().asprintf(
+        "%s gpsYaw SHADOW-EXIT  Mjd=%.6f beta=%6.3f mu=%7.2f yaw=%7.2f nominal=%7.2f\n",
+        prn.toLatin1().data(), Mjd, beta*180.0/M_PI, mu*180.0/M_PI, st.yaw*180.0/M_PI, psiNom*180.0/M_PI);
+    }
   }
 
