Index: trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11068)
+++ trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11074)
@@ -223,4 +223,15 @@
     int bytesused = 0;
 
+    // message type of the frame at the start of the buffer (RTCM number,
+    // IGS-SSR 4076: 4076000 + subtype)
+    int msgType = -1;
+    if (_buffer.size() >= 6 && (unsigned char)_buffer[0] == 0xD3) {
+      const unsigned char* bb = reinterpret_cast<const unsigned char*>(_buffer.constData());
+      msgType = (bb[3] << 4) | (bb[4] >> 4);
+      if (msgType == 4076) {
+        msgType = 4076000 + (((bb[4] & 0x01) << 7) | (bb[5] >> 1));
+      }
+    }
+
     GCOB_RETURN irc = _ssrCorr->GetSSR(&_clkOrb, &_codeBias, &_vTEC, &_phaseBias, &_metaData, &_antenna,
                                        _buffer.data(), _buffer.size(), &bytesused);
@@ -272,4 +283,12 @@
           checkProviderID();
           sendResults();
+          // Pass on the epoch as soon as it is complete, not only when the
+          // next epoch arrives (one update interval later): complete when
+          // all message types of the previous epoch have arrived with
+          // Multiple Message Indicator 0. Consumers (PPP, combination) must
+          // not see an epoch partly, e.g. new clocks with old orbits.
+          if (irc == GCOBR_OK) {
+            checkEpochComplete(msgType);
+          }
           retCode = success;
         }
@@ -788,4 +807,45 @@
 }
 
+// Pass the current epoch on as soon as it is complete: every message type
+// due at this epoch has arrived with Multiple Message Indicator 0. A type is
+// due if its interval (learned from its previous epochs) has passed since
+// its last epoch; types missing for more than three intervals are no longer
+// expected. Until the intervals are known, and if a due type is missing,
+// the epoch is passed on when the next epoch arrives. Types arriving after
+// the epoch was passed on are passed on as soon as they are complete.
+////////////////////////////////////////////////////////////////////////////
+void RTCM3coDecoder::checkEpochComplete(int msgType) {
+  const bncTime& epoch = _lastTime;
+  if (_typeLast.contains(msgType)) {
+    if (!(_typeLast[msgType] < epoch)) {
+      if (epoch == _flushedEpoch) {
+        dumpEpochs(true);  // repeated type of an epoch already passed on
+      }
+      return;
+    }
+    _typeInt[msgType] = epoch - _typeLast[msgType];
+  }
+  _typeLast[msgType] = epoch;
+
+  if (epoch == _flushedEpoch) {
+    dumpEpochs(true);
+    return;
+  }
+  bool complete = !_typeInt.isEmpty();
+  QMapIterator<int, double> it(_typeInt);
+  while (it.hasNext()) {
+    it.next();
+    double sinceLast = epoch - _typeLast[it.key()];
+    if (sinceLast > 0.0 && sinceLast >= it.value() - 0.1 && sinceLast <= 3.0 * it.value()) {
+      complete = false;
+      break;
+    }
+  }
+  if (complete) {
+    dumpEpochs(true);
+    _flushedEpoch = epoch;
+  }
+}
+
 // Emit and write the collected corrections of all epochs older than
 // _lastTime (all epochs if all is set, e.g. at the end of a replay)
