Index: trunk/BNC/src/upload/bncrtnetuploadcaster.cpp
===================================================================
--- trunk/BNC/src/upload/bncrtnetuploadcaster.cpp	(revision 11057)
+++ trunk/BNC/src/upload/bncrtnetuploadcaster.cpp	(revision 11058)
@@ -548,7 +548,19 @@
             // Make sure the clock messages refer to same IOD as orbit messages
             // ----------------------------------------------------------------
+            // The ephemeris is selected at the first correction epoch of each
+            // sampling interval: the grid epoch itself for corrections on the
+            // sampling grid, otherwise (e.g. Galileo HAS at ...:02, ...:12)
+            // the first epoch after it, which previously never selected one
             if (_usedEph) {
-                if (fmod(epoTime.gpssec(), _samplRtcmEphCorr) == 0.0) {
+                bool select = (fmod(epoTime.gpssec(), _samplRtcmEphCorr) == 0.0);
+                if (!select && _samplRtcmEphCorr > 0.0) {
+                    select = !_usedEphEpoch.valid() || epoTime == _usedEphEpoch ||
+                             epoTime.gpsw() != _usedEphEpoch.gpsw() ||
+                             floor(epoTime.gpssec() / _samplRtcmEphCorr) !=
+                             floor(_usedEphEpoch.gpssec() / _samplRtcmEphCorr);
+                }
+                if (select) {
                     (*_usedEph)[prnInternalStr] = eph;
+                    _usedEphEpoch = epoTime;
                 }
                 else {
@@ -1170,5 +1182,15 @@
     double clkRnxAcc = rtnClk[2] / t_CST::c;         // [s/s² = -/s]
 
-    if (_rnx) {
+    // Corrections on epochs off the output sampling grid (e.g. Galileo HAS
+    // at ...:02, ...:12) are written at the nearest grid epoch, see gridEpoch;
+    // the clock is propagated with its rate, the orbit via the broadcast
+    // orbit at the grid epoch plus the (slowly varying) difference
+    // precise - broadcast at the correction epoch
+    // -----------------------------------------------------------------------
+    bncTime tt(GPSweek, GPSweeks);
+    bncTime tg;
+    double  clkRate = (clkRnxRate != 0.0) ? clkRnxRate : xB(5);
+
+    if (_rnx && gridEpoch(_rnx->sampl(), tt, tg, _rnxGridEpo, _rnxGridSrc)) {
         double clkRnxSig, clkRnxRateSig, clkRnxAccSig;
         int s = rtnClkSig.size();
@@ -1190,11 +1212,50 @@
             break;
         }
-        _rnx->write(GPSweek, GPSweeks, prn, clkRnx, clkRnxRate, clkRnxAcc,
-            clkRnxSig, clkRnxRateSig, clkRnxAccSig);
-    }
-    if (_sp3) {
-        _sp3->write(GPSweek, GPSweeks, prn, rtnCoM, clkRnx, rtnVel, clkRnxRate);
+        _rnx->write(tg.gpsw(), tg.gpssec(), prn, clkRnx + clkRate * (tg - tt),
+            clkRnxRate, clkRnxAcc, clkRnxSig, clkRnxRateSig, clkRnxAccSig);
+    }
+    if (_sp3 && gridEpoch(_sp3->sampl(), tt, tg, _sp3GridEpo, _sp3GridSrc)) {
+        ColumnVector xyz = rtnCoM;
+        ColumnVector vel = rtnVel;
+        if (!(tg == tt)) {
+            ColumnVector xBg(6);
+            ColumnVector vBg(3);
+            if (eph->getCrd(tg, xBg, vBg, false) != success) {
+                return success;
+            }
+            xyz = xBg.Rows(1, 3) + (rtnCoM - xB.Rows(1, 3));
+            vel = vBg + (rtnVel - vB);
+        }
+        _sp3->write(tg.gpsw(), tg.gpssec(), prn, xyz, clkRnx + clkRate * (tg - tt),
+            vel, clkRnxRate);
     }
     return success;
+}
+
+// Output epoch for SP3/CLK files with sampling interval sampl [s]: the
+// correction epoch itself if it is on the sampling grid, otherwise the
+// preceding grid epoch if at most 5 s (and less than half the sampling
+// interval) earlier. Only preceding grid epochs are used, so a correction
+// epoch on the grid always arrives first and takes precedence. Each grid
+// epoch is derived from one correction epoch only.
+////////////////////////////////////////////////////////////////////////////
+bool bncRtnetUploadCaster::gridEpoch(int sampl, const bncTime& tt, bncTime& tg,
+                                     bncTime& lastGrid, bncTime& lastSrc) const {
+    if (sampl <= 0) {
+        tg = tt;
+        return true;
+    }
+    double sec  = tt.gpssec();
+    double back = sec - floor(sec / sampl) * sampl;   // [0, sampl)
+    if (back > 1.e-6 && (back > 5.0 || back >= 0.5 * sampl)) {
+        return false;
+    }
+    tg = (back <= 1.e-6) ? tt : tt - back;
+    if (lastGrid.valid() && tg == lastGrid && !(tt == lastSrc)) {
+        return false;
+    }
+    lastGrid = tg;
+    lastSrc  = tt;
+    return true;
 }
 
