Index: trunk/BNC/src/GPSDecoder.h
===================================================================
--- trunk/BNC/src/GPSDecoder.h	(revision 11052)
+++ trunk/BNC/src/GPSDecoder.h	(revision 11057)
@@ -51,4 +51,7 @@
 
   virtual int corrGPSEpochTime() const {return -1;}
+
+  // pass on all data still held back (e.g. at the end of a --file replay)
+  virtual void flush() {}
 
   void initRinex(const QByteArray& staID, const QUrl& mountPoint,
Index: trunk/BNC/src/RTCM3/RTCM3Decoder.cpp
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3Decoder.cpp	(revision 11052)
+++ trunk/BNC/src/RTCM3/RTCM3Decoder.cpp	(revision 11057)
@@ -71,9 +71,10 @@
   _rawFile = rawFile;
 
-  connect(this, SIGNAL(newGPSEph(t_ephGPS)),     BNC_CORE, SLOT(slotNewGPSEph(t_ephGPS)));
-  connect(this, SIGNAL(newGlonassEph(t_ephGlo)), BNC_CORE, SLOT(slotNewGlonassEph(t_ephGlo)));
-  connect(this, SIGNAL(newGalileoEph(t_ephGal)), BNC_CORE, SLOT(slotNewGalileoEph(t_ephGal)));
-  connect(this, SIGNAL(newSBASEph(t_ephSBAS)),   BNC_CORE, SLOT(slotNewSBASEph(t_ephSBAS)));
-  connect(this, SIGNAL(newBDSEph(t_ephBDS)),     BNC_CORE, SLOT(slotNewBDSEph(t_ephBDS)));
+  Qt::ConnectionType conType = BNC_CORE->decoderConnectionType();
+  connect(this, SIGNAL(newGPSEph(t_ephGPS)),     BNC_CORE, SLOT(slotNewGPSEph(t_ephGPS)), conType);
+  connect(this, SIGNAL(newGlonassEph(t_ephGlo)), BNC_CORE, SLOT(slotNewGlonassEph(t_ephGlo)), conType);
+  connect(this, SIGNAL(newGalileoEph(t_ephGal)), BNC_CORE, SLOT(slotNewGalileoEph(t_ephGal)), conType);
+  connect(this, SIGNAL(newSBASEph(t_ephSBAS)),   BNC_CORE, SLOT(slotNewSBASEph(t_ephSBAS)), conType);
+  connect(this, SIGNAL(newBDSEph(t_ephBDS)),     BNC_CORE, SLOT(slotNewBDSEph(t_ephBDS)), conType);
 
   _MessageSize = _SkipBytes = _BlockSize = _NeedBytes = 0;
@@ -89,4 +90,14 @@
   }
   _coDecoders.clear();
+}
+
+// Pass on the corrections still held back by the SSR decoders
+////////////////////////////////////////////////////////////////////////////
+void RTCM3Decoder::flush() {
+  QMapIterator<QByteArray, RTCM3coDecoder*> it(_coDecoders);
+  while (it.hasNext()) {
+    it.next();
+    it.value()->flush();
+  }
 }
 
Index: trunk/BNC/src/RTCM3/RTCM3Decoder.h
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3Decoder.h	(revision 11052)
+++ trunk/BNC/src/RTCM3/RTCM3Decoder.h	(revision 11057)
@@ -43,4 +43,5 @@
   virtual t_irc Decode(char* buffer, int bufLen, std::vector<std::string>& errmsg);
   virtual int corrGPSEpochTime() const;
+  virtual void flush();
   /**
    * CRC24Q checksum calculation function (only full bytes supported).
Index: trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11052)
+++ trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11057)
@@ -71,27 +71,28 @@
   _out = 0;
 
+  Qt::ConnectionType conType = BNC_CORE->decoderConnectionType();
   connect(this, SIGNAL(newOrbCorrections(QList<t_orbCorr>)),
-          BNC_CORE, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)));
+          BNC_CORE, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)), conType);
 
   connect(this, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
-          BNC_CORE, SLOT(slotNewClkCorrections(QList<t_clkCorr>)));
+          BNC_CORE, SLOT(slotNewClkCorrections(QList<t_clkCorr>)), conType);
 
   connect(this, SIGNAL(newCodeBiases(QList<t_satCodeBias>)),
-          BNC_CORE, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)));
+          BNC_CORE, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)), conType);
 
   connect(this, SIGNAL(newPhaseBiases(QList<t_satPhaseBias>)),
-          BNC_CORE, SLOT(slotNewPhaseBiases(QList<t_satPhaseBias>)));
+          BNC_CORE, SLOT(slotNewPhaseBiases(QList<t_satPhaseBias>)), conType);
 
   connect(this, SIGNAL(newTec(t_vTec)),
-          BNC_CORE, SLOT(slotNewTec(t_vTec)));
+          BNC_CORE, SLOT(slotNewTec(t_vTec)), conType);
 
   connect(this, SIGNAL(newMetaData(t_metaData)),
-          BNC_CORE, SLOT(slotNewMetaData(t_metaData)));
+          BNC_CORE, SLOT(slotNewMetaData(t_metaData)), conType);
 
   connect(this, SIGNAL(newSatAntennas(QList<t_satAntenna>)),
-          BNC_CORE, SLOT(slotNewSatAntennas(QList<t_satAntenna>)));
+          BNC_CORE, SLOT(slotNewSatAntennas(QList<t_satAntenna>)), conType);
 
   connect(this, SIGNAL(providerIDChanged(QString)),
-          BNC_CORE, SIGNAL(providerIDChanged(QString)));
+          BNC_CORE, SIGNAL(providerIDChanged(QString)), conType);
 
   connect(this, SIGNAL(newMessage(QByteArray,bool)),
@@ -784,8 +785,15 @@
   // Dump all older epochs
   // ---------------------
+  dumpEpochs(false);
+}
+
+// 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)
+////////////////////////////////////////////////////////////////////////////
+void RTCM3coDecoder::dumpEpochs(bool all) {
   QMutableMapIterator<bncTime, QList<t_orbCorr> > itOrb(_orbCorrections);
   while (itOrb.hasNext()) {
     itOrb.next();
-    if (itOrb.key() < _lastTime) {
+    if (all || itOrb.key() < _lastTime) {
       emit newOrbCorrections(itOrb.value());
       t_orbCorr::writeEpoch(_out, itOrb.value());
@@ -796,5 +804,5 @@
   while (itClk.hasNext()) {
     itClk.next();
-    if (itClk.key() < _lastTime) {
+    if (all || itClk.key() < _lastTime) {
       emit newClkCorrections(itClk.value());
       t_clkCorr::writeEpoch(_out, itClk.value());
@@ -805,5 +813,5 @@
   while (itCB.hasNext()) {
     itCB.next();
-    if (itCB.key() < _lastTime) {
+    if (all || itCB.key() < _lastTime) {
       emit newCodeBiases(itCB.value());
       t_satCodeBias::writeEpoch(_out, itCB.value());
@@ -814,5 +822,5 @@
   while (itPB.hasNext()) {
     itPB.next();
-    if (itPB.key() < _lastTime) {
+    if (all || itPB.key() < _lastTime) {
       emit newPhaseBiases(itPB.value());
       t_satPhaseBias::writeEpoch(_out, itPB.value());
@@ -823,5 +831,5 @@
   while (itTec.hasNext()) {
     itTec.next();
-    if (itTec.key() < _lastTime) {
+    if (all || itTec.key() < _lastTime) {
       emit newTec(itTec.value());
       t_vTec::write(_out, itTec.value());
@@ -832,5 +840,5 @@
   while (itMD.hasNext()) {
     itMD.next();
-    if (itMD.key() < _lastTime) {
+    if (all || itMD.key() < _lastTime) {
       emit newMetaData(itMD.value());
       t_metaData::write(_out, itMD.value());
@@ -841,9 +849,12 @@
   while (itAnt.hasNext()) {
     itAnt.next();
-    if (itAnt.key() < _lastTime) {
+    if (all || itAnt.key() < _lastTime) {
       emit newSatAntennas(itAnt.value());
       t_satAntenna::writeEpoch(_out, itAnt.value());
       itAnt.remove();
     }
+  }
+  if (all && _out) {
+    _out->flush();
   }
 }
@@ -1126,7 +1137,7 @@
   else if (epoSecGlo != -1) {
     QDate date = dateAndTimeFromGPSweek(currentTime.gpsw(), currentTime.gpssec()).date();
-    if (_type == IGSssr) {
-      epoSecGlo = epoSecGlo + gnumleap(date.year(), date.month(), date.day());
-    }
+    // IGS-SSR: the SSR Epoch Time (IDF003, 20 bit) is given in one continuous
+    // time scale for all GNSS, i.e. GPS seconds of week also for GLONASS
+    // (IGS SSR v1.00) - no conversion
     if (_type == RTCMssr || _type == RTCMnewssr) {
       // Same GLONASS epoch wire convention (UTC+3h) as RTCMssr
@@ -1145,7 +1156,10 @@
   }
   else if (epoSecBds != -1) {
-    epoSecBds += 14.0;
-    if (epoSecBds > 604800.0) {
-      epoSecBds -= 7.0*24.0*60.0*60.0;
+    // RTCM-SSR: BDT; IGS-SSR: GPS time scale for all GNSS (IDF003)
+    if (_type != IGSssr) {
+      epoSecBds += 14.0;
+      if (epoSecBds > 604800.0) {
+        epoSecBds -= 7.0*24.0*60.0*60.0;
+      }
     }
     _lastTime.set(currentWeek, epoSecBds);
Index: trunk/BNC/src/RTCM3/RTCM3coDecoder.h
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3coDecoder.h	(revision 11052)
+++ trunk/BNC/src/RTCM3/RTCM3coDecoder.h	(revision 11057)
@@ -45,4 +45,5 @@
   virtual t_irc Decode(char* buffer, int bufLen, std::vector<std::string>& errmsg);
   virtual int corrGPSEpochTime() const {return int(_lastTime.gpssec());}
+  virtual void flush() {dumpEpochs(true);}
   virtual void initSsrFormatType(const e_type type) {
     _type = type;
@@ -73,4 +74,5 @@
   void setEpochTime();
   void sendResults();
+  void dumpEpochs(bool all);
   void reopen();
   void checkProviderID();
Index: trunk/BNC/src/bnccore.h
===================================================================
--- trunk/BNC/src/bnccore.h	(revision 11052)
+++ trunk/BNC/src/bnccore.h	(revision 11057)
@@ -47,4 +47,15 @@
   static t_bncCore* instance();
   e_mode            mode() const {return _mode;}
+  // Connection type for data signals (ephemerides, corrections) from a
+  // decoder to this object, to be called in the thread the decoder emits
+  // from. In batch post-processing (--file replay) the reading thread must
+  // wait until the data are passed on, otherwise they reach the PPP in an
+  // order depending on thread scheduling instead of the file order.
+  Qt::ConnectionType decoderConnectionType() const {
+    if (_mode == batchPostProcessing && QThread::currentThread() != thread()) {
+      return Qt::BlockingQueuedConnection;
+    }
+    return Qt::AutoConnection;
+  }
   void              setGUIenabled(bool GUIenabled) {_GUIenabled = GUIenabled;}
   void              setMode(e_mode mode) {_mode = mode;}
Index: trunk/BNC/src/bncephuser.cpp
===================================================================
--- trunk/BNC/src/bncephuser.cpp	(revision 11052)
+++ trunk/BNC/src/bncephuser.cpp	(revision 11057)
@@ -253,4 +253,49 @@
 
   if (ephL && ephL->checkState() != t_eph::bad) {
+    // Update of ephemeris data sets after an outage: if the time since the
+    // last data set exceeds the system's update interval, the last one is
+    // outdated and a consistency check against it (extrapolated over the
+    // whole gap) is meaningless - it would reject the first new data set
+    // after every gap. The new data set stays unchecked and serves as
+    // reference for the next one.
+    // -----------------------------------------------------------------
+    double dt = eph->TOC() - ephL->TOC();
+
+    // update interval: 2h
+    if      (eph->system() == t_eph::GPS     && dt >  7200.0) {
+      ephL->setCheckState(t_eph::outdated);
+      return;
+    }
+    // update interval: 3h
+    else if (eph->system() == t_eph::Galileo && dt > 10800.0) {
+      ephL->setCheckState(t_eph::outdated);
+      return;
+    }
+    // updated every 30 minutes + 5 min
+    else if (eph->system() == t_eph::GLONASS && dt >  2100.0) {
+      ephL->setCheckState(t_eph::outdated);
+      return;
+    }
+    // updated every ?
+    else if (eph->system() == t_eph::QZSS    && dt >  3600.0) {
+      ephL->setCheckState(t_eph::outdated);
+      return;
+    }
+    // maximum update interval: 300 sec
+    else if  (eph->system() == t_eph::SBAS   && dt >   600.0) {
+      ephL->setCheckState(t_eph::outdated);
+      return;
+    }
+    // updates 1h + 5 min
+    else if  (eph->system() == t_eph::BDS    && dt >  3900.0) {
+      ephL->setCheckState(t_eph::outdated);
+      return;
+    }
+    // update interval: up to 24 hours
+    else if  (eph->system() == t_eph::NavIC  && dt > 86400.0) {
+      ephL->setCheckState(t_eph::outdated);
+      return;
+    }
+
     ColumnVector xcL(6);
     ColumnVector vvL(3);
@@ -281,45 +326,5 @@
       }
     }
-
-    // some lines to allow update of ephemeris data sets after an outage
-    // -----------------------------------------------------------------
-    double dt = eph->TOC() - ephL->TOC();
-
-    // update interval: 2h
-    if      (eph->system() == t_eph::GPS     && dt >  7200.0) {
-      ephL->setCheckState(t_eph::outdated);
-      return;
-    }
-    // update interval: 3h
-    else if (eph->system() == t_eph::Galileo && dt > 10800.0) {
-      ephL->setCheckState(t_eph::outdated);
-      return;
-    }
-    // updated every 30 minutes + 5 min
-    else if (eph->system() == t_eph::GLONASS && dt >  2100.0) {
-      ephL->setCheckState(t_eph::outdated);
-      return;
-    }
-    // updated every ?
-    else if (eph->system() == t_eph::QZSS    && dt >  3600.0) {
-      ephL->setCheckState(t_eph::outdated);
-      return;
-    }
-    // maximum update interval: 300 sec
-    else if  (eph->system() == t_eph::SBAS   && dt >   600.0) {
-      ephL->setCheckState(t_eph::outdated);
-      return;
-    }
-    // updates 1h + 5 min
-    else if  (eph->system() == t_eph::BDS    && dt >  3900.0) {
-      ephL->setCheckState(t_eph::outdated);
-      return;
-    }
-    // update interval: up to 24 hours
-    else if  (eph->system() == t_eph::NavIC  && dt > 86400.0) {
-      ephL->setCheckState(t_eph::outdated);
-      return;
-    }
-  }
-}
-
+  }
+}
+
Index: trunk/BNC/src/bncgetthread.cpp
===================================================================
--- trunk/BNC/src/bncgetthread.cpp	(revision 11052)
+++ trunk/BNC/src/bncgetthread.cpp	(revision 11057)
@@ -483,4 +483,10 @@
         if (data.isEmpty() || BNC_CORE->sigintReceived) {
           emit(newMessage("No more data or SIGINT/SIGTERM received", true));
+          // pass on data the decoders still hold back (last epoch)
+          QMapIterator<QString, GPSDecoder*> itFlush(_decodersRaw);
+          while (itFlush.hasNext()) {
+            itFlush.next();
+            itFlush.value()->flush();
+          }
           BNC_CORE->stopPPP();
           BNC_CORE->stopCombination();
Index: trunk/BNC/src/bnchelp.html
===================================================================
--- trunk/BNC/src/bnchelp.html	(revision 11052)
+++ trunk/BNC/src/bnchelp.html	(revision 11057)
@@ -2992,4 +2992,10 @@
   </p>
   <p>
+    For RINEX Version 3/4, the default set contains all signals BNC can decode from RTCM Version 3 MSM streams, each
+    with code, phase, Doppler and signal-to-noise ratio. As empty observations at the end of an observation record are
+    omitted (trailing blanks are removed as permitted by the RINEX format), the frequently tracked signals are listed
+    first to keep the records short.
+  </p>
+  <p>
     A skeleton file carrying only RINEX Version 2 style observation types (2-character codes, e.g. 'C1', 'L1') cannot
     supply the tracking-mode
@@ -3439,5 +3445,10 @@
   </p>
   <p>
-    In case of a Quality Check the following type of Broadcast navigation messages is used per individulal GNSS:
+    In case of a Quality Check, BNC reads all types of Broadcast navigation messages contained in the RINEX Navigation
+    file(s), i.e. besides the legacy navigation messages listed in the following table also e.g. CNAV and CNAV-2
+    (GPS, QZSS), FNAV (Galileo), CNAV-1/2/3 (BeiDou) and L1NV (NavIC) messages from RINEX Version 4 files. If a
+    satellite's navigation file(s) contain several data sets with the same Time of Clock (TOC), e.g. an INAV and an
+    FNAV data set, only the first one is used. The following table lists the legacy navigation message types per
+    individual GNSS together with the corresponding RTCM message types:
   </p>
   <table>
@@ -3572,4 +3583,17 @@
   </p>
   <p>
+    A satellite's elevation is computed for each epoch from the navigation data set with the TOC closest to the epoch
+    among the data sets which are valid at that epoch (see section 'Logfile' for the validity periods). If no data set
+    is valid, the data set with the closest TOC is used. If no usable data set is available at all, e.g. because the
+    satellite is missing in the navigation file(s), the satellite's observations are not excluded by the elevation
+    mask; in the 'Analyze' epoch-specific output, its elevation and azimuth are reported as '0.00'.
+  </p>
+  <p>
+    In case of 'Analyze', the navigation data sets are checked beforehand and data sets flagged as wrong are not used.
+    A satellite is considered unhealthy at an epoch if the valid data set with the TOC closest to that epoch is flagged
+    unhealthy. As its orbit might be erroneous, the observations of an unhealthy satellite are excluded from the
+    analysis at such epochs, in the same way as observations below the elevation mask.
+  </p>
+  <p>
     Default is '0 deg', meaning that no elevation mask is applied and all observations are used regardless of the
     involved satellite's elevation angle.
@@ -3588,251 +3612,173 @@
     Note that logfiles from analyzing RINEX files may become quite large. Hence, BNC provides an option 'Summary only'
     to limit logfile content to some essential information in case 'Action' is set to 'Analyze'.
-    The following is an example for a RINEX quality check analysis logfile:
+    The following is an example for a RINEX quality check analysis logfile (excerpt, one hour of 30 sec RINEX
+    Version 4 data from station BRUX00BEL analyzed together with the station's own navigation file; the
+    unhealthy satellites E14 and R10 are excluded):
   </p>
   <pre><p style="font-family:Monospace">
 QC Format Version  : 1.1
 
-Navigation File(s) : MAO000USA_R_20221720000_01D_MN.rnx
-Ephemeris          : 1776 OK   3617 UNHEALTHY   116 WRONG
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH G09 LNAV: 2022 06 21 04 00 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH G09 LNAV: 2022 06 21 06 00 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH G09 LNAV: 2022 06 21 08 00 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH J07 LNAV: 2022 06 21 07 00 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH J07 LNAV: 2022 06 21 08 00 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH G09 LNAV: 2022 06 21 10 00 00
-..
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH C35 D1  : 2022 06 21 23 00 14
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH C26 D1  : 2022 06 21 23 00 14
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH C44 D1  : 2022 06 21 23 00 14
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH C24 D1  : 2022 06 21 23 00 14
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH C21 D1  : 2022 06 21 23 00 14
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH C12 D1  : 2022 06 21 23 00 14
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH C40 D1  : 2022 06 21 23 00 14
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E11 FNAV: 2022 06 21 22 50 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH G08 LNAV: 2022 06 21 23 59 44
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E11 FNAV: 2022 06 21 23 00 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E11 FNAV: 2022 06 21 23 10 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E11 FNAV: 2022 06 21 23 20 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E11 FNAV: 2022 06 21 23 30 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E25 FNAV: 2022 06 21 23 30 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E11 FNAV: 2022 06 21 23 40 00
-                     MAO000USA_R_20221720000_01D_MN.rnx : WRONG EPH E25 FNAV: 2022 06 21 23 40 00
-
-Observation File   : MAO000USA_R_20221720000_01D_30S_MO.rnx
-RINEX Version      : 4.00
-Marker Name        : MAO0
-Marker Number      : 40445M005
-Receiver           : SEPT POLARX5
-Antenna            : LEIAR25.R3      LEIT
-Position XYZ       :  -5466067.2677  -2404332.8007   2242123.7442
-Antenna dH/dE/dN   :   0.0000   0.0000   0.0000
-Start Time         : 2022-06-21 00.00.00.0
-End Time           : 2022-06-21 23.59.30.0
+Input Nav File     : BRUX00BEL_R_20262090800_01H_MN.rnx
+RINEX Version      : 4.01
+Ephemeris Check    : 227 OK   81 UNHEALTHY   0 WRONG
+
+Observation File   : BRUX00BEL_R_20262090800_01H_30S_MO.rnx
+RINEX Version      : 4.01
+Marker Name        : BRUX00BEL
+Marker Number      : 13101M010
+Receiver           : SEPT POLARX5TR
+Antenna            : JAVRINGANT_DM   SCIS
+Position XYZ       :   4027881.8468    306998.2610   4919498.6524
+Antenna dH/dE/dN   :   0.4689   0.0000   0.0010
+Start Time         : 2026-07-28 08.00.00.0
+End Time           : 2026-07-28 08.59.30.0
 Interval           : 30 sec
+Minimum Elevation  : 0 deg
 Observation Types C: C1P L1P D1P S1P C5P L5P D5P S5P C2I L2I D2I S2I C7I L7I D7I S7I C6I L6I D6I S6I C7D L7D D7D S7D
-Observation Types E: C1C L1C D1C S1C C6C L6C D6C S6C C5Q L5Q D5Q S5Q C7Q L7Q D7Q S7Q C8Q L8Q D8Q S8Q
+Observation Types E: C1C L1C D1C S1C C6B L6B D6B S6B C5Q L5Q D5Q S5Q C7Q L7Q D7Q S7Q C8Q L8Q D8Q S8Q
 Observation Types G: C1C L1C D1C S1C C1W S1W C2W L2W D2W S2W C2L L2L D2L S2L C5Q L5Q D5Q S5Q C1L L1L D1L S1L
 Observation Types I: C5A L5A D5A S5A
-Observation Types J: C1C L1C D1C S1C C2L L2L D2L S2L C5Q L5Q D5Q S5Q C1L L1L D1L S1L C1Z L1Z D1Z S1Z C5P L5P D5P S5P
-Observation Types R: C1C L1C D1C S1C C1P L1P D1P S1P C2P L2P D2P S2P C2C L2C D2C S2C
-Observation Types S: C1C L1C D1C S1C C5I L5I D5I S5I
-Analysed GNSS      : 4    C E G J
-
-  C: Satellites: 42
+Observation Types R: C1C L1C D1C S1C C1P L1P D1P S1P C2P L2P D2P S2P C2C L2C D2C S2C C3Q L3Q D3Q S3Q
+Analysed GNSS      : 4    C E G R
+
+  C: Satellites: 15
   C: Signals   : 6    1P 2I 5P 6I 7D 7I
 
-      C:   1P: Observations      :  28217
-      C:   1P: Slips (file+found):       60 +      22
-      C:   1P: Gaps              :      147
-      C:   1P: Mean SNR          :     43.9
-      C:   1P: Mean Multipath    :     0.60
-
-      C:   2I: Observations      :  43725
-      C:   2I: Slips (file+found):      124 +      32
-      C:   2I: Gaps              :      209
-      C:   2I: Mean SNR          :     44.8
-      C:   2I: Mean Multipath    :     0.66
-
-      C:   5P: Observations      :  28373
-      C:   5P: Slips (file+found):       52 +       7
-      C:   5P: Gaps              :      100
-      C:   5P: Mean SNR          :     43.5
-      C:   5P: Mean Multipath    :     0.26
-
-      C:   6I: Observations      :  43887
-      C:   6I: Slips (file+found):       95 +      15
-      C:   6I: Gaps              :      156
-      C:   6I: Mean SNR          :     44.6
-      C:   6I: Mean Multipath    :     0.29
-
-      C:   7D: Observations      :  30073
-      C:   7D: Slips (file+found):       45 +      10
-      C:   7D: Gaps              :       28
-      C:   7D: Mean SNR          :     44.1
-      C:   7D: Mean Multipath    :     0.28
-
-      C:   7I: Observations      :  11450
-      C:   7I: Slips (file+found):       23 +       0
-      C:   7I: Gaps              :       16
-      C:   7I: Mean SNR          :     43.0
-      C:   7I: Mean Multipath    :     0.00
-
-  E: Satellites: 27
-  E: Signals   : 5    1C 5Q 6C 7Q 8Q
-
-      E:   1C: Observations      :  28931
-      E:   1C: Slips (file+found):       49 +       6
-      E:   1C: Gaps              :       83
-      E:   1C: Mean SNR          :     42.7
-      E:   1C: Mean Multipath    :     0.43
-
-      E:   5Q: Observations      :  27473
-      E:   5Q: Slips (file+found):       34 +       6
-      E:   5Q: Gaps              :       62
-      E:   5Q: Mean SNR          :     43.5
-      E:   5Q: Mean Multipath    :     0.22
-
-      E:   6C: Observations      :  27564
-      E:   6C: Slips (file+found):       40 +       9
-      E:   6C: Gaps              :       77
-      E:   6C: Mean SNR          :     45.5
-      E:   6C: Mean Multipath    :     0.39
-
-      E:   7Q: Observations      :  27504
-      E:   7Q: Slips (file+found):       35 +       7
-      E:   7Q: Gaps              :       55
-      E:   7Q: Mean SNR          :     44.2
-      E:   7Q: Mean Multipath    :     0.25
-
-      E:   8Q: Observations      :  27099
-      E:   8Q: Slips (file+found):       48 +       9
-      E:   8Q: Gaps              :       35
-      E:   8Q: Mean SNR          :     47.1
-      E:   8Q: Mean Multipath    :     0.08
-
-  G: Satellites: 32
+      C:   1P: Observations      :   1193 (    1490)    80.07 %
+      C:   1P: Slips (file+found):        4 +       0
+      C:   1P: Gaps              :        9
+      C:   1P: Mean SNR          :     40.7
+      C:   1P: Mean Multipath    :     0.33
+
+      C:   2I: Observations      :   1350 (    1490)    90.60 %
+      C:   2I: Slips (file+found):        5 +       0
+      C:   2I: Gaps              :        8
+      C:   2I: Mean SNR          :     41.9
+      C:   2I: Mean Multipath    :     0.36
+
+..
+
+  G: Satellites: 13
   G: Signals   : 6    1C 1L 1W 2L 2W 5Q
 
-      G:   1C: Observations      :  33608 (   35429)    94.86 %
-      G:   1C: Slips (file+found):      120 +      40
-      G:   1C: Gaps              :      159
-      G:   1C: Mean SNR          :     43.8
-      G:   1C: Mean Multipath    :     0.56
-
-      G:   1L: Observations      :   4975 (   35429)    14.04 %
-      G:   1L: Slips (file+found):       17 +       5
-      G:   1L: Gaps              :       17
-      G:   1L: Mean SNR          :     44.0
-      G:   1L: Mean Multipath    :     0.54
-
-      G:   1W: Observations      :  32785 (   35429)    92.54 %
-      G:   1W: Slips (file+found):        0 +      41
-      G:   1W: Gaps              :       83
-      G:   1W: Mean SNR          :     33.5
-      G:   1W: Mean Multipath    :     0.55
-
-      G:   2L: Observations      :  24939 (   35429)    70.39 %
-      G:   2L: Slips (file+found):       81 +      26
-      G:   2L: Gaps              :       95
-      G:   2L: Mean SNR          :     41.4
-      G:   2L: Mean Multipath    :     0.49
-
-      G:   2W: Observations      :  32785 (   35429)    92.54 %
-      G:   2W: Slips (file+found):      110 +      44
-      G:   2W: Gaps              :       83
-      G:   2W: Mean SNR          :     33.5
-      G:   2W: Mean Multipath    :     0.32
-
-      G:   5Q: Observations      :  17026 (   35429)    48.06 %
-      G:   5Q: Slips (file+found):       41 +       8
-      G:   5Q: Gaps              :       47
-      G:   5Q: Mean SNR          :     46.2
-      G:   5Q: Mean Multipath    :     0.26
-
-  J: Satellites: 4
-  J: Signals   : 6    1C 1L 1Z 2L 5P 5Q
-
-      J:   1C: Observations      :   9433 (    9493)    99.37 %
-      J:   1C: Slips (file+found):       64 +       7
-      J:   1C: Gaps              :       14
-      J:   1C: Mean SNR          :     38.0
-      J:   1C: Mean Multipath    :     0.71
-
-      J:   1L: Observations      :   9438 (    9493)    99.42 %
-      J:   1L: Slips (file+found):       14 +      10
-      J:   1L: Gaps              :       10
-      J:   1L: Mean SNR          :     38.9
-      J:   1L: Mean Multipath    :     0.58
-
-      J:   1Z: Observations      :   9428 (    9493)    99.32 %
-      J:   1Z: Slips (file+found):       20 +       7
-      J:   1Z: Gaps              :       16
-      J:   1Z: Mean SNR          :     40.6
-      J:   1Z: Mean Multipath    :     0.61
-
-      J:   2L: Observations      :   9435 (    9493)    99.39 %
-      J:   2L: Slips (file+found):       12 +       9
-      J:   2L: Gaps              :       10
-      J:   2L: Mean SNR          :     38.9
-      J:   2L: Mean Multipath    :     0.58
-
-      J:   5P: Observations      :   9443 (    9493)    99.47 %
-      J:   5P: Slips (file+found):       12 +      11
-      J:   5P: Gaps              :        8
-      J:   5P: Mean SNR          :     36.4
-      J:   5P: Mean Multipath    :     0.18
-
-      J:   5Q: Observations      :   9457 (    9493)    99.62 %
-      J:   5Q: Slips (file+found):       12 +      11
-      J:   5Q: Gaps              :        7
-      J:   5Q: Mean SNR          :     41.4
-      J:   5Q: Mean Multipath    :     0.13
-
-> 2022 06 21 00 00  0.0000000 41  0.6
-G08   0.00    0.00   9  L1C .. 44.1  C1C  . 0.58  C1W  . 0.59  L2W .. 40.6  C2W  . 0.77  L2L .. 37.3  C2L  . 0.74  L5Q .. 45.3  C5Q  . 0.33
-G10  32.29   79.31   9  L1C .. 49.6  C1C  . 0.16  C1W  . 0.16  L2W .. 44.0  C2W  . 0.09  L2L .. 48.2  C2L  . 0.11  L5Q .. 51.6  C5Q  . 0.09
-G16  54.18 -127.22   5  L1C .. 47.1  C1C  . 0.21  C1W  . 0.21  L2W .. 39.4  C2W  . 0.10
-G21  15.13  -68.49   5  L1C .. 41.8  C1C  . 0.53  C1W  . 0.53  L2W .. 25.6  C2W  . 0.27
-G22  26.49  158.25   5  L1C .. 44.6  C1C  . 0.27  C1W  . 0.27  L2W .. 30.1  C2W  . 0.29
-G23   0.00    0.00  11  L1C .. 45.3  C1C  . 0.33  C1W  . 0.33  L2W .. 33.9  C2W  . 0.17  L2L .. 42.5  C2L  . 0.24  L5Q .. 46.3  C5Q  . 0.25  L1L .. 45.7  C1L  . 0.27
-G26   0.00    0.00   9  L1C .. 46.7  C1C  . 0.28  C1W  . 0.29  L2W .. 36.8  C2W  . 0.09  L2L .. 43.7  C2L  . 0.21  L5Q .. 48.6  C5Q  . 0.10
-G27   0.00    0.00   9  L1C .. 48.8  C1C  . 0.23  C1W  . 0.23  L2W .. 43.4  C2W  . 0.17  L2L .. 48.2  C2L  . 0.15  L5Q .. 51.9  C5Q  . 0.10
-G31   0.00    0.00   7  L1C .. 35.7  C1C  . 4.35  C1W  . 4.31  L2W .. 18.4  C2W  . 4.31  L2L s. 31.1  C2L  . 0.00
-G32   0.00    0.00   9  L1C .. 49.2  C1C  . 0.32  C1W  . 0.32  L2W .. 39.9  C2W  . 0.15  L2L .. 44.6  C2L  . 0.16  L5Q .. 48.0  C5Q  . 0.09
-E02  43.91 -107.45  10  L1C .. 47.6  C1C  . 0.27  L6C .. 50.3  C6C  . 0.23  L5Q .. 48.5  C5Q  . 0.11  L7Q .. 48.8  C7Q  . 0.14  L8Q .. 51.7  C8Q  . 0.02
-E03   2.45 -161.42  10  L1C .. 37.7  C1C  . 0.74  L6C .. 37.4  C6C  . 0.48  L5Q .. 36.9  C5Q  . 0.14  L7Q .. 37.6  C7Q  . 0.18  L8Q .. 40.3  C8Q  . 0.16
-E04   8.83   44.72   7  C1C  . 0.00  C6C  . 0.00  L5Q .. 28.5  C5Q  . 0.00  C7Q  . 0.00  L8Q .. 31.2  C8Q  . 0.00
-E05  37.64  160.08  10  L1C .. 46.5  C1C  . 0.13  L6C .. 49.7  C6C  . 0.12  L5Q .. 47.0  C5Q  . 0.12  L7Q .. 47.5  C7Q  . 0.10  L8Q .. 50.3  C8Q  . 0.03
-E09  42.44   89.19  10  L1C .. 47.7  C1C  . 0.20  L6C .. 50.7  C6C  . 0.13  L5Q .. 48.1  C5Q  . 0.09  L7Q .. 48.9  C7Q  . 0.19  L8Q .. 51.5  C8Q  . 0.04
-E11  20.61   64.05  10  L1C .. 41.7  C1C  . 0.14  L6C .. 42.8  C6C  . 0.14  L5Q .. 39.0  C5Q  . 0.14  L7Q .. 39.2  C7Q  . 0.12  L8Q .. 42.1  C8Q  . 0.03
-E25  29.05 -169.12  10  L1C .. 44.2  C1C  . 0.36  L6C .. 47.6  C6C  . 0.26  L5Q .. 45.0  C5Q  . 0.13  L7Q .. 46.0  C7Q  . 0.20  L8Q .. 48.5  C8Q  . 0.07
-E30  18.32  -53.65  10  L1C .. 42.8  C1C  . 0.34  L6C .. 46.1  C6C  . 0.37  L5Q .. 42.7  C5Q  . 0.26  L7Q .. 43.5  C7Q  . 0.32  L8Q .. 46.1  C8Q  . 0.03
-E34  26.84  -52.94  10  L1C .. 45.5  C1C  . 0.36  L6C .. 46.7  C6C  . 0.59  L5Q .. 44.1  C5Q  . 0.22  L7Q .. 44.6  C7Q  . 0.21  L8Q .. 47.4  C8Q  . 0.05
-E36  44.38   10.32  10  L1C .. 47.1  C1C  . 0.23  L6C .. 50.4  C6C  . 0.14  L5Q .. 48.4  C5Q  . 0.18  L7Q .. 49.1  C7Q  . 0.13  L8Q .. 51.7  C8Q  . 0.04
-J02  25.68  -71.42  12  L1C .. 43.6  C1C  . 0.60  L2L .. 40.0  C2L  . 0.83  L5Q .. 44.4  C5Q  . 0.06  L1L .. 43.6  C1L  . 0.71  L1Z .. 43.4  C1Z  . 0.64  L5P .. 40.8  C5P  . 0.09
-J03  18.88  -66.71  12  L1C .. 41.0  C1C  . 0.45  L2L .. 40.3  C2L  . 0.58  L5Q .. 42.9  C5Q  . 0.17  L1L .. 41.7  C1L  . 0.49  L1Z .. 41.4  C1Z  . 0.47  L5P .. 39.4  C5P  . 0.17
-J04   6.08 -127.48  12  L1C .. 39.3  C1C  . 1.18  L2L .. 38.2  C2L  . 1.45  L5Q .. 39.2  C5Q  . 0.59  L1L .. 38.7  C1L  . 1.31  L1Z .. 40.4  C1Z  . 1.21  L5P .. 35.0  C5P  . 0.60
-J07   3.70  -94.81  11  C1C  . 0.51  L2L .. 37.0  C2L  . 0.20  L5Q .. 39.5  C5Q  . 0.09  L1L .. 32.7  C1L  . 0.21  L1Z .. 38.9  C1Z  . 0.25  L5P .. 30.9  C5P  . 0.16
-C01  20.30 -102.21   6  L2I .. 41.8  C2I  . 0.00  L7I .. 42.6  C7I  . 0.00  L6I .. 40.1  C6I  . 0.00
-C04  35.59 -109.87   6  L2I .. 44.5  C2I  . 0.00  L7I .. 45.4  C7I  . 0.00  L6I .. 44.6  C6I  . 0.00
-C11  18.91  -42.10   6  L2I .. 39.0  C2I  . 0.00  L7I .. 46.1  C7I  . 0.00  L6I .. 44.6  C6I  . 0.00
-C12  51.55    8.60   6  L2I .. 48.5  C2I  . 0.00  L7I .. 51.5  C7I  . 0.00  L6I .. 50.9  C6I  . 0.00
-C14   7.32 -150.82   6  L2I .. 37.8  C2I  . 0.00  L7I .. 40.9  C7I  . 0.00  L6I .. 41.4  C6I  . 0.00
-C21  75.79  138.30  10  L1P .. 49.0  C1P  . 0.14  L5P .. 51.9  C5P  . 0.06  L2I .. 50.8  C2I  . 0.25  L6I .. 52.5  C6I  . 0.08  L7D .. 51.0  C7D  . 0.07
-C22  36.50   44.63  10  L1P .. 47.4  C1P  . 0.30  L5P .. 47.4  C5P  . 0.16  L2I .. 49.6  C2I  . 0.26  L6I .. 49.9  C6I  . 0.25  L7D .. 47.3  C7D  . 0.21
-C24  16.27 -139.62  10  L1P .. 43.9  C1P  . 0.74  L5P .. 42.8  C5P  . 0.12  L2I .. 44.7  C2I  . 0.71  L6I .. 43.5  C6I  . 0.16  L7D .. 42.6  C7D  . 0.19
-C25   8.87  -85.45  10  L1P .. 38.0  C1P  . 1.84  L5P .. 40.3  C5P  . 0.29  L2I .. 41.7  C2I  . 1.50  L6I .. 41.5  C6I  . 0.37  L7D .. 39.8  C7D  . 0.35
-C26   4.25  177.77  10  L1P .. 40.0  C1P  . 0.75  L5P .. 37.8  C5P  . 0.28  L2I .. 41.7  C2I  . 0.66  L6I .. 40.5  C6I  . 0.22  L7D .. 37.7  C7D  . 0.28
-C34  36.24  -28.48  10  L1P .. 46.7  C1P  . 0.36  L5P .. 42.5  C5P  . 0.23  L2I .. 48.9  C2I  . 0.44  L6I .. 48.1  C6I  . 0.21  L7D .. 42.3  C7D  . 0.38
-C35  16.68  103.11  10  L1P .. 41.8  C1P  . 0.44  L5P .. 41.5  C5P  . 0.20  L2I .. 43.3  C2I  . 0.34  L6I .. 43.0  C6I  . 0.19  L7D .. 40.9  C7D  . 0.21
-C40  10.26  -34.76  10  L1P .. 42.4  C1P  . 0.36  L5P .. 37.4  C5P  . 0.32  L2I .. 43.1  C2I  . 0.45  L6I .. 39.4  C6I  . 0.54  L7D .. 38.4  C7D  . 0.31
-C42  30.61 -156.42  10  L1P .. 46.0  C1P  . 0.34  L5P .. 46.6  C5P  . 0.17  L2I .. 47.7  C2I  . 0.33  L6I .. 48.1  C6I  . 0.08  L7D .. 46.1  C7D  . 0.14
-C44  53.90   52.53  10  L1P .. 48.1  C1P  . 0.18  L5P .. 49.4  C5P  . 0.11  L2I .. 50.9  C2I  . 0.34  L6I .. 50.9  C6I  . 0.15  L7D .. 48.9  C7D  . 0.10
-C57   0.00    0.00   4  L2I .. 48.1  C2I  . 0.00  L6I .. 48.2  C6I  . 0.00
-C59  15.79 -100.68   6  L2I .. 43.8  C2I  . 0.00  L6I .. 41.7  C6I  . 0.00  L7D .. 41.5  C7D  . 0.00
-> 2022 06 21 00 00 30.0000000 41  0.6
+      G:   1C: Observations      :   1215 (    1302)    93.32 %
+      G:   1C: Slips (file+found):        4 +       0
+      G:   1C: Gaps              :        4
+      G:   1C: Mean SNR          :     42.7
+      G:   1C: Mean Multipath    :     0.27
+
+      G:   1L: Observations      :    328 (    1302)    25.19 %
+      G:   1L: Slips (file+found):        4 +       0
+      G:   1L: Gaps              :        1
+      G:   1L: Mean SNR          :     42.0
+      G:   1L: Mean Multipath    :     0.27
+
+      G:   1W: Observations      :   1169 (    1302)    89.78 %
+      G:   1W: Slips (file+found):        0 +       0
+      G:   1W: Gaps              :        0
+      G:   1W: Mean SNR          :     35.4
+      G:   1W: Mean Multipath    :     0.24
+
+      G:   2L: Observations      :    924 (    1302)    70.97 %
+      G:   2L: Slips (file+found):        4 +       0
+      G:   2L: Gaps              :        2
+      G:   2L: Mean SNR          :     41.0
+      G:   2L: Mean Multipath    :     0.29
+
+      G:   2W: Observations      :   1169 (    1302)    89.78 %
+      G:   2W: Slips (file+found):        3 +       0
+      G:   2W: Gaps              :        0
+      G:   2W: Mean SNR          :     35.4
+      G:   2W: Mean Multipath    :     0.22
+
+      G:   5Q: Observations      :    809 (    1302)    62.14 %
+      G:   5Q: Slips (file+found):        2 +       0
+      G:   5Q: Gaps              :        1
+      G:   5Q: Mean SNR          :     46.4
+      G:   5Q: Mean Multipath    :     0.17
+
+  R: Satellites: 11
+  R: Signals   : 5    1C 1P 2C 2P 3Q
+
+      R:   1C: Observations      :   1036 (    1063)    97.46 %
+      R:   1C: Slips (file+found):        6 +       1
+      R:   1C: Gaps              :        2
+      R:   1C: Mean SNR          :     45.8
+      R:   1C: Mean Multipath    :     0.45
+
+..
+
+> 2026 07 28 08 00  0.0000000 40  0.8
+G01  69.73  -69.71  11  L1C .. 49.8  C1C  . 0.08  C1W  . 0.09  L2W .. 45.1  C2W  . 0.02  L2L .. 51.4  C2L  . 0.07  L5Q .. 55.3  C5Q  . 0.02  L1L .. 49.2  C1L  . 0.08
+G02  80.53   79.07   5  L1C .. 50.8  C1C  . 0.09  C1W  . 0.09  L2W .. 47.4  C2W  . 0.02
+G03  37.67 -134.30   9  L1C .. 45.3  C1C  . 0.17  C1W  . 0.17  L2W .. 45.0  C2W  . 0.06  L2L .. 42.7  C2L  . 0.18  L5Q .. 49.7  C5Q  . 0.06
+R09  12.37  -91.67   8  L1C .. 42.0  C1C  . 0.84  L1P .. 40.8  C1P  . 0.69  L2P .. 39.4  C2P  . 0.38  L2C .. 40.4  C2C  . 0.38
+R11   5.51    3.88  10  L1C .. 39.6  C1C  . 0.56  L1P .. 39.1  C1P  . 0.65  L2P .. 36.4  C2P  . 0.51  L2C .. 36.8  C2C  . 0.42  L3Q .. 35.9  C3Q  . 0.22
+E13  76.44  -53.73  10  L1C .. 48.4  C1C  . 0.05  L6B .. 53.8  C6B  . 0.03  L5Q .. 51.7  C5Q  . 0.02  L7Q .. 52.5  C7Q  . 0.02  L8Q .. 55.1  C8Q  . 0.01
+E15  43.04   81.40  10  L1C .. 45.1  C1C  . 0.07  L6B .. 49.5  C6B  . 0.06  L5Q .. 47.8  C5Q  . 0.03  L7Q .. 48.4  C7Q  . 0.05  L8Q .. 51.2  C8Q  . 0.01
 ...
 </p>
 </pre>
+
+  <p>
+    <b>The summary output</b>
+  </p>
+
+  <p>
+    The line 'Ephemeris Check' reports how many navigation data sets were read from the RINEX Navigation file(s) and
+    classified as 'OK', 'UNHEALTHY' or 'WRONG':
+  </p>
+  <ul>
+    <li>'UNHEALTHY': The data set's health information flags the satellite as unhealthy. For GPS and QZSS CNAV data sets
+      the health word contains one flag per signal (L1, L2, L5; bits 52-54 of Message Type 10). Such a data set is
+      considered unhealthy only if the L1 or L2 flag is set; an L5-only flag, as it is common for satellites with a
+      not yet fully operational L5 signal, is ignored.</li>
+    <li>'WRONG': The data set failed a plausibility check. It is considered wrong if the satellite's radial distance
+      at TOC is not between 20,000 km and 60,000 km, or if satellite position or clock at its TOC differ by more than
+      10 m (100 m for BeiDou and SBAS) from those computed with the preceding data set of the same satellite, or if
+      a data set only repeats the preceding one with a different TOC. Wrong data sets are listed in the logfile and
+      ignored for the analysis.</li>
+  </ul>
+  <p>
+    The comparison with the preceding data set is skipped if the time between both data sets exceeds the nominal
+    update interval of the respective GNSS (GPS 2 h, Galileo 3 h, GLONASS 35 min, QZSS 1 h, BeiDou 65 min,
+    SBAS 10 min, NavIC 24 h). The preceding data set is then considered outdated, while the new one is accepted and
+    serves as reference for the following data sets. This way, the first data set after an outage, e.g. after the
+    satellite was not tracked for some hours, is not rejected.
+  </p>
+
+  <p>
+    For each signal, the line 'Observations' contains the number of observations in the RINEX Observation file
+    (after applying the 'Minimum Elevation' mask) and, if a RINEX Navigation file is specified, the number of
+    expected observations in brackets followed by the ratio of both numbers in percent. The number of expected
+    observations is computed per satellite system: For each satellite with at least one navigation data set and for
+    each epoch between start and end of the observation file at the file's sampling interval, an observation is
+    expected if
+  </p>
+  <ul>
+    <li>a navigation data set is valid at that epoch, and</li>
+    <li>the satellite's elevation is at least the 'Minimum Elevation'.</li>
+  </ul>
+  <p>
+    A data set is considered valid within the following periods around its TOC: GPS -2 h to +4 h, Galileo 0 to
+    +4 h, GLONASS -35 min to +35 min, QZSS -1 h to +2 h, BeiDou 0 to +65 min, SBAS -10 min to +10 min, NavIC
+    -24 h to +24 h. Among several valid data sets, the one with the closest TOC is used. Wrong or outdated data sets
+    are not used. Epochs at which a satellite is unhealthy (see section 'Minimum Elevation') do not count as
+    expected, consistent with the exclusion of its observations at such epochs.
+  </p>
+  <p>
+    Note that
+  </p>
+  <ul>
+    <li>the number of expected observations is the same for all signals of a satellite system. Signals which are not
+      transmitted by all satellites of a system (e.g. GPS L1C or L5) therefore show a lower percentage;</li>
+    <li>the number of expected observations and the percentage are only reported if each observed satellite of the
+      respective system has at least one navigation data set in the RINEX Navigation file(s);</li>
+    <li>a station's own navigation file usually only contains data sets for satellites which were tracked by the
+      station. Epochs without a valid data set, e.g. of satellites which were not tracked at all for a longer
+      period, do not count as expected. Using a merged navigation file (e.g. BRDC00WRD_S_...) is therefore
+      recommended to assess the completeness of observation data.</li>
+  </ul>
+
 
   <p>
Index: trunk/BNC/src/bncrinex.cpp
===================================================================
--- trunk/BNC/src/bncrinex.cpp	(revision 11052)
+++ trunk/BNC/src/bncrinex.cpp	(revision 11057)
@@ -581,9 +581,11 @@
     rnxSat.prn = satObs._prn;
 
-    // Initialize all observations mentioned in skeleton header
-    // --------------------------------------------------------
+    // Initialize all observations mentioned in the output header
+    // (from the skeleton or, if none is usable, the default header;
+    // _sklHeader is empty or holds 2-char types in the latter case)
+    // ----------------------------------------------------------
     char sys = rnxSat.prn.system();
-    for (int iType = 0; iType < _sklHeader.nTypes(sys); iType++) {
-      QString type = _sklHeader.obsType(sys, iType);
+    for (int iType = 0; iType < _header.nTypes(sys); iType++) {
+      QString type = _header.obsType(sys, iType);
       t_rnxObsFile::t_rnxObs rnxObs; // create an empty observation
       rnxSat.obs[type] = rnxObs;
Index: trunk/BNC/src/bncutils.cpp
===================================================================
--- trunk/BNC/src/bncutils.cpp	(revision 11052)
+++ trunk/BNC/src/bncutils.cpp	(revision 11057)
@@ -303,5 +303,5 @@
   }
   // update interval: up to 24 hours
-  else if (eph->system() == t_eph::NavIC   && (fabs(dt > 86400.0))) {
+  else if (eph->system() == t_eph::NavIC   && (fabs(dt) > 86400.0)) {
     return true;
   }
Index: trunk/BNC/src/ephemeris.cpp
===================================================================
--- trunk/BNC/src/ephemeris.cpp	(revision 11052)
+++ trunk/BNC/src/ephemeris.cpp	(revision 11057)
@@ -648,4 +648,14 @@
     }
   }
+
+  // CNAV/CNAV-2 records carry no separate t_oe: t_oe = t_oc
+  // (RINEX 4.02 Tables A9/A11, IS-GPS-200 30.3.4.4, IS-GPS-800 3.5.3.7.1).
+  // NavIC L1NV (RINEX 4.02 Table A31): single reference epoch t_oec for
+  // ephemeris and clock (NavIC L1 SPS ICD 6.2.1.6, Appendices A and B).
+  // ---------------------------------------------------------------------
+  if (type() == t_eph::CNAV || type() == t_eph::CNV2 || type() == t_eph::L1NV) {
+    _TOEweek = _TOC.gpsw();
+    _TOEsec  = _TOC.gpssec();
+  }
 }
 
@@ -672,4 +682,21 @@
 
   double n = n0 + _Delta_n;
+
+  // CNAV/CNAV-2 (IS-GPS-200 Table 30-II) and NavIC L1NV (NavIC L1 SPS
+  // ICD Appendix A): semi-major axis and mean motion difference are
+  // time-variable, A_k = A0 + Adot*tk and
+  // n_A = n0 + Delta_n0 + 1/2*Delta_n0_dot*tk.
+  // n0 is computed from A0 for GPS/QZSS but from A_k for NavIC L1NV.
+  double A    = a0;
+  double Adot = 0.0;
+  if (type() == t_eph::CNAV || type() == t_eph::CNV2 || type() == t_eph::L1NV) {
+    Adot = _ADOT;
+    A   += _ADOT * tk;
+    n   += 0.5 * _Delta_n_dot * tk;
+    if (type() == t_eph::L1NV) {
+      n += sqrt(gmGRS / (A * A * A)) - n0;
+    }
+  }
+
   double M = _M0 + n * tk;
   double E = M;
@@ -688,5 +715,5 @@
   double sin2u0 = sin(2 * u0);
   double cos2u0 = cos(2 * u0);
-  double r = a0 * (1 - _e * cos(E)) + _Crc * cos2u0 + _Crs * sin2u0;
+  double r = A * (1 - _e * cos(E)) + _Crc * cos2u0 + _Crs * sin2u0;
   double i = _i0 + _IDOT * tk + _Cic * cos2u0 + _Cis * sin2u0;
   double u = u0 + _Cuc * cos2u0 + _Cus * sin2u0;
@@ -715,5 +742,5 @@
   double dotom = _OMEGADOT - omegaEarth;
   double doti = _IDOT + (-_Cic * sin2u0 + _Cis * cos2u0) * 2 * dotv;
-  double dotr = a0 * _e * sin(E) * dEdM * n
+  double dotr = A * _e * sin(E) * dEdM * n + Adot * (1 - _e * cos(E))
       + (-_Crc * sin2u0 + _Crs * cos2u0) * 2 * dotv;
   double dotx = dotr * cos(u) - r * sin(u) * dotu;
@@ -740,5 +767,5 @@
   }
   else {
-    xc[3] -= 4.442807633e-10 * _e * sqrt(a0) * sin(E);
+    xc[3] -= 4.442807633e-10 * _e * sqrt(A) * sin(E);
   }
 
@@ -1087,7 +1114,18 @@
       switch (type()) {
         case  t_eph::LNAV:
-        case  t_eph::CNAV:
         case  t_eph::CNV2:
           if (_health == 0.0) {
+            return 0;
+          }
+          else {
+            return 1;
+          }
+          break;
+        case  t_eph::CNAV:
+          // RINEX 4: bits 52(MSB)-54(LSB) of msg 10 = L1, L2, L5 health.
+          // An L5-only flag (value 1, common while L5 is not fully
+          // operational) does not make orbit/clock unusable; the data set
+          // is unhealthy if L1 or L2 is flagged.
+          if ((int(_health) & 0x6) == 0) {
             return 0;
           }
Index: trunk/BNC/src/pppMain.cpp
===================================================================
--- trunk/BNC/src/pppMain.cpp	(revision 11052)
+++ trunk/BNC/src/pppMain.cpp	(revision 11057)
@@ -81,4 +81,11 @@
       t_pppThread* pppThread = new t_pppThread(opt);
       pppThread->start();
+      // In batch post-processing (--file replay), the data source must not
+      // start before the PPP client is connected, otherwise the data read
+      // in the meantime get lost (a varying amount, depending on thread
+      // scheduling).
+      if (BNC_CORE->mode() == t_bncCore::batchPostProcessing) {
+        pppThread->waitUntilReady();
+      }
       _pppThreads << pppThread;
       _running = true;
Index: trunk/BNC/src/pppThread.cpp
===================================================================
--- trunk/BNC/src/pppThread.cpp	(revision 11052)
+++ trunk/BNC/src/pppThread.cpp	(revision 11057)
@@ -78,4 +78,5 @@
   try {
     _pppRun = new t_pppRun(_opt);
+    _ready.release();
     if (_opt->_realTime) {
       QThread::exec();
@@ -87,4 +88,5 @@
   catch (t_except exc) {
     _pppRun = 0;
+    _ready.release();
     emit newMessage(QByteArray(exc.what().c_str()), true);
   }
Index: trunk/BNC/src/pppThread.h
===================================================================
--- trunk/BNC/src/pppThread.h	(revision 11052)
+++ trunk/BNC/src/pppThread.h	(revision 11057)
@@ -19,4 +19,6 @@
   virtual void run();
   static void msleep(unsigned long msecs){QThread::msleep(msecs);}
+  // blocks until t_pppRun is created and connected (or creation failed)
+  void waitUntilReady() {_ready.acquire();}
 
  signals:
@@ -26,4 +28,5 @@
   const t_pppOptions* _opt;
   t_pppRun*           _pppRun;
+  QSemaphore          _ready;
 };
 
Index: trunk/BNC/src/rinex/reqcanalyze.cpp
===================================================================
--- trunk/BNC/src/rinex/reqcanalyze.cpp	(revision 11052)
+++ trunk/BNC/src/rinex/reqcanalyze.cpp	(revision 11057)
@@ -131,4 +131,5 @@
   // ----------------
   t_reqcEdit::readEphemerides(_navFileNames, _ephs, _logStream, _checkEph);
+  t_reqcEdit::indexEphemerides(_ephs, _ephsByPrn);
 
   // Loop over all RINEX Files
@@ -207,4 +208,9 @@
           continue;
         }
+        // observations of unhealthy satellites are excluded, as those
+        // below the elevation mask (see also setExpectedObs)
+        if (_navFileNames.size() && unhealthyAt(rnxSat.prn, _currEpo->tt)) {
+          continue;
+        }
         t_satObs satObs;
         t_rnxObsFile::setObsFromRnx(obsFile, _currEpo, rnxSat, satObs);
@@ -266,11 +272,8 @@
     }
 
-    t_eph* eph = 0;
-    for (int ie = 0; ie < _ephs.size(); ie++) {
-      if (_ephs[ie]->prn() == prn) {
-        eph = _ephs[ie];
-        break;
-      }
-    }
+    if (unhealthyAt(prn, _currEpo->tt)) {
+      continue;
+    }
+    t_eph* eph = ephForTime(prn, _currEpo->tt);
     if (eph) {
       ColumnVector xSat(6);
@@ -328,11 +331,5 @@
                              t_qcSat& qcSat) {
 
-  t_eph* eph = 0;
-  for (int ie = 0; ie < _ephs.size(); ie++) {
-    if (_ephs[ie]->prn() == satObs._prn) {
-      eph = _ephs[ie];
-      break;
-    }
-  }
+  t_eph* eph = ephForTime(satObs._prn, epoTime);
   if (eph) {
     ColumnVector xc(6);
@@ -976,15 +973,26 @@
                                    double interval, const ColumnVector& xyzSta) {
   for(QMap<t_prn, int>::iterator it = _numExpObs.begin(); it != _numExpObs.end(); it++) {
-    t_eph* eph = 0;
+    bool navAvail = false;
     for (int ie = 0; ie < _ephs.size(); ie++) {
       if (_ephs[ie]->prn() == it.key()) {
-        eph = _ephs[ie];
+        navAvail = true;
         break;
       }
     }
-    if (eph) {
+    if (navAvail) {
       int numExpObs = 0;
       bncTime epoTime;
       for (epoTime = startTime - interval; epoTime < endTime; epoTime = epoTime + interval) {
+        // an observation is only expected if a valid ephemeris exists,
+        // the satellite is healthy (unhealthy satellite-epochs are also
+        // excluded from the observations, see analyzeFile) and above
+        // the elevation mask
+        if (unhealthyAt(it.key(), epoTime)) {
+          continue;
+        }
+        t_eph* eph = ephForTime(it.key(), epoTime, true);
+        if (!eph) {
+          continue;
+        }
         ColumnVector xc(6);
         ColumnVector vv(3);
@@ -1007,2 +1015,18 @@
   }
 }
+
+// A satellite is considered unhealthy at epoch tt if the valid navigation
+// data set closest to tt is flagged unhealthy. Its orbit is not used then
+// (the geometry may be erroneous) and its observations are excluded.
+////////////////////////////////////////////////////////////////////////////
+bool t_reqcAnalyze::unhealthyAt(const t_prn& prn, const bncTime& tt) const {
+  t_eph* eph = ephForTime(prn, tt, true, true);
+  return eph && eph->checkState() == t_eph::unhealthy;
+}
+
+// Ephemeris of the given satellite for epoch tt, see t_reqcEdit::ephForTime
+////////////////////////////////////////////////////////////////////////////
+t_eph* t_reqcAnalyze::ephForTime(const t_prn& prn, const bncTime& tt,
+                                 bool validOnly, bool allowUnhealthy) const {
+  return t_reqcEdit::ephForTime(_ephsByPrn.value(prn), prn, tt, validOnly, allowUnhealthy);
+}
Index: trunk/BNC/src/rinex/reqcanalyze.h
===================================================================
--- trunk/BNC/src/rinex/reqcanalyze.h	(revision 11052)
+++ trunk/BNC/src/rinex/reqcanalyze.h	(revision 11057)
@@ -194,4 +194,9 @@
   void   setExpectedObs(const bncTime& startTime, const bncTime& endTime,
                         double interval, const ColumnVector& xyzSta);
+
+  t_eph* ephForTime(const t_prn& prn, const bncTime& tt, bool validOnly = false,
+                    bool allowUnhealthy = false) const;
+
+  bool   unhealthyAt(const t_prn& prn, const bncTime& tt) const;
 
   void   analyzeMultipath();
@@ -219,4 +224,5 @@
   QStringList                _defaultSignalTypes;
   QVector<t_eph*>            _ephs;
+  QMap<t_prn, QVector<t_eph*> > _ephsByPrn;
   t_rnxObsFile::t_rnxEpo*    _currEpo;
   t_qcFile                   _qcFile;
Index: trunk/BNC/src/rinex/reqcedit.cpp
===================================================================
--- trunk/BNC/src/rinex/reqcedit.cpp	(revision 11052)
+++ trunk/BNC/src/rinex/reqcedit.cpp	(revision 11057)
@@ -230,4 +230,5 @@
   if (_minEle > 0.0 && _ephs.isEmpty()) {
     t_reqcEdit::readEphemerides(_navFileNames, _ephs, _logStream, _checkEph);
+    t_reqcEdit::indexEphemerides(_ephs, _ephsByPrn);
   }
 
@@ -546,11 +547,5 @@
     const t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iSat];
 
-    t_eph* eph = 0;
-    for (int ie = 0; ie < _ephs.size(); ie++) {
-      if (_ephs[ie]->prn() == rnxSat.prn) {
-        eph = _ephs[ie];
-        break;
-      }
-    }
+    t_eph* eph = ephForTime(_ephsByPrn.value(rnxSat.prn), rnxSat.prn, epo->tt);
     if (eph) {
       ColumnVector xc(6);
@@ -567,4 +562,57 @@
   }
   epo->rnxSat = keptSats;
+}
+
+// Ephemeris of the given satellite for epoch tt: the one with TOC closest
+// to tt among those valid at tt (see outDatedBcep). If none is valid,
+// return 0 when validOnly is set, otherwise the one with TOC closest to tt.
+// Only ephemerides accepted by t_eph::getCrd are considered (not bad or
+// outdated; unhealthy ones only if allowUnhealthy is set, to be used with
+// getCrd(..., ignoreHealth = true)).
+// Using the first ephemeris of the day for all epochs gives wrong
+// geometry late in the day and, for GLONASS, integrations over many
+// hours (t_ephGlo::position integrates from TOC on every call).
+////////////////////////////////////////////////////////////////////////////
+t_eph* t_reqcEdit::ephForTime(const QVector<t_eph*>& ephs, const t_prn& prn,
+                              const bncTime& tt, bool validOnly,
+                              bool allowUnhealthy) {
+  t_eph* bestValid   = 0;
+  t_eph* bestAny     = 0;
+  double bestValidDt = 0.0;
+  double bestAnyDt   = 0.0;
+  for (int ie = 0; ie < ephs.size(); ie++) {
+    t_eph* eph = ephs[ie];
+    if (eph->prn() != prn) {
+      continue;
+    }
+    if (eph->checkState() == t_eph::bad      ||
+        eph->checkState() == t_eph::outdated ||
+        (eph->checkState() == t_eph::unhealthy && !allowUnhealthy)) {
+      continue;
+    }
+    double dt = fabs(tt - eph->TOC());
+    if (bestAny == 0 || dt < bestAnyDt) {
+      bestAny   = eph;
+      bestAnyDt = dt;
+    }
+    if (!outDatedBcep(eph, tt) && (bestValid == 0 || dt < bestValidDt)) {
+      bestValid   = eph;
+      bestValidDt = dt;
+    }
+  }
+  if (bestValid || validOnly) {
+    return bestValid;
+  }
+  return bestAny;
+}
+
+// Group ephemerides by satellite (speeds up ephForTime)
+////////////////////////////////////////////////////////////////////////////
+void t_reqcEdit::indexEphemerides(const QVector<t_eph*>& ephs,
+                                  QMap<t_prn, QVector<t_eph*> >& ephsByPrn) {
+  ephsByPrn.clear();
+  for (int ie = 0; ie < ephs.size(); ie++) {
+    ephsByPrn[ephs[ie]->prn()].append(ephs[ie]);
+  }
 }
 
Index: trunk/BNC/src/rinex/reqcedit.h
===================================================================
--- trunk/BNC/src/rinex/reqcedit.h	(revision 11052)
+++ trunk/BNC/src/rinex/reqcedit.h	(revision 11057)
@@ -56,4 +56,9 @@
                                 QVector<t_eph*>& ephs,
                                 QTextStream* log, bool checkEph);
+  static void indexEphemerides(const QVector<t_eph*>& ephs,
+                               QMap<t_prn, QVector<t_eph*> >& ephsByPrn);
+  static t_eph* ephForTime(const QVector<t_eph*>& ephs, const t_prn& prn,
+                           const bncTime& tt, bool validOnly = false,
+                           bool allowUnhealthy = false);
 
  private:
@@ -82,4 +87,5 @@
   QMap<QString, QMap<int, int> > _lli;
   QVector<t_eph*>        _ephs;
+  QMap<t_prn, QVector<t_eph*> > _ephsByPrn;
 };
 
Index: trunk/BNC/src/rinex/rnxobsfile.cpp
===================================================================
--- trunk/BNC/src/rinex/rnxobsfile.cpp	(revision 11052)
+++ trunk/BNC/src/rinex/rnxobsfile.cpp	(revision 11057)
@@ -349,55 +349,37 @@
   }
   else {
-    _obsTypes['G'] << "C1C" << "L1C"  << "S1C"
-                   << "C1W" << "L1W"  << "S1W"
-                   << "C2X" << "L2X"  << "S2X"
-                   << "C2W" << "L2W"  << "S2W"
-                   << "C5X" << "L5X"  << "S5X";
-
-    _obsTypes['J'] << "C1C" << "L1C"  << "S1C"
-                   << "C1Z" << "L1Z"  << "S1Z"
-                   << "C1X" << "L1X"  << "S1X"
-                   << "C2L" << "L2L"  << "S2L"
-                   << "C2X" << "L2X"  << "S2X"
-                   << "C5Q" << "L5Q"  << "S5Q"
-                   << "C5X" << "L5X"  << "S5X"
-                   << "C6L" << "L6L"  << "S6L";
-
-    _obsTypes['R'] << "C1C" << "L1C" << "S1C"
-                   << "C1P" << "L1P" << "S1P"
-                   << "C2C" << "L2C" << "S2C"
-                   << "C2P" << "L2P" << "S2P"
-                   << "C3I" << "L3I" << "S3I"
-                   << "C4X" << "L4X" << "S4X"
-                   << "C6X" << "L6X" << "S6X";
-
-    _obsTypes['E'] << "C1C" << "L1C" << "S1C"
-                   << "C1X" << "L1X" << "S1X"
-                   << "C5Q" << "L5Q" << "S5Q"
-                   << "C5X" << "L5X" << "S5X"
-                   << "C6C" << "L6C" << "S6C"
-                   << "C6X" << "L6X" << "S6X"
-                   << "C7Q" << "L7Q" << "S7Q"
-                   << "C7X" << "L7X" << "S7X"
-                   << "C8Q" << "L8Q" << "S8Q"
-                   << "C8X" << "L8X" << "S8X";
-
-    _obsTypes['S'] << "C1C" << "L1C" << "S1C"
-                   << "C5I" << "L5I" << "S5I"
-                   << "C5Q" << "L5Q" << "S5Q"
-                   << "C5X" << "L5X" << "S5X";
-
-    _obsTypes['C'] << "C2I" << "L2I" << "S2I"
-                   << "C2Q" << "L2Q" << "S2Q"
-                   << "C2X" << "L2X" << "S2X"
-                   << "C6I" << "L6I" << "S6I"
-                   << "C6Q" << "L6Q" << "S6Q"
-                   << "C6X" << "L6X" << "S6X"
-                   << "C7I" << "L7I" << "S7I"
-                   << "C7Q" << "L7Q" << "S7Q"
-                   << "C7X" << "L7X" << "S7X";
-
-    _obsTypes['I'] << "C5A" << "L5A" << "S5A"
-                   << "C9A" << "L9A" << "S9A";
+    // All signals the RTCM3 MSM decoder can deliver (see RTCM3Decoder.cpp),
+    // each with code, phase, Doppler and SNR. Empty observations are blank
+    // padded up to the last non-empty one of a record, so the order keeps
+    // records short: per system first the signals most receivers track
+    // (Q/C/L/P tracking modes), then the combined 'X' modes, then rarely
+    // used ones.
+    QMap<char, QStringList> sigCodes;
+    sigCodes['G'] << "1C" << "1W" << "2W" << "2L" << "2S" << "5Q" << "1L"
+                  << "2X" << "5X" << "1X"
+                  << "5I" << "1S" << "1P" << "2C" << "2P";
+    sigCodes['R'] << "1C" << "1P" << "2C" << "2P" << "3Q"
+                  << "3X" << "4X" << "6X"
+                  << "3I" << "4A" << "4B" << "6A" << "6B";
+    sigCodes['E'] << "1C" << "5Q" << "7Q" << "8Q" << "6C"
+                  << "1X" << "5X" << "7X" << "8X" << "6X"
+                  << "1B" << "5I" << "7I" << "8I" << "6B" << "1A" << "1Z" << "6A" << "6Z";
+    sigCodes['J'] << "1C" << "2L" << "5Q" << "1L"
+                  << "2X" << "5X" << "1X"
+                  << "2S" << "5I" << "1S" << "1E" << "1Z" << "6L" << "6S" << "6X";
+    sigCodes['C'] << "2I" << "7I" << "6I" << "5P" << "1P" << "7D"
+                  << "2X" << "7X" << "6X" << "5X" << "1X"
+                  << "2Q" << "7Q" << "6Q" << "5D" << "1D";
+    sigCodes['S'] << "1C" << "5I" << "5Q" << "5X";
+    sigCodes['I'] << "5A" << "9A";
+
+    QMapIterator<char, QStringList> it(sigCodes);
+    while (it.hasNext()) {
+      it.next();
+      for (int ii = 0; ii < it.value().size(); ii++) {
+        const QString& sig = it.value()[ii];
+        _obsTypes[it.key()] << "C" + sig << "L" + sig << "D" + sig << "S" + sig;
+      }
+    }
   }
 }
@@ -1528,8 +1510,27 @@
     char sys = rnxSat.prn.system();
 
+    // If all observation types are given as v3 types, a direct lookup is
+    // equivalent to the (slow) matching loops below
+    // --------------------------------------------------------------------
+    bool allTypesV3 = true;
+    QMapIterator<QString, t_rnxObs> itChk(rnxSat.obs);
+    while (itChk.hasNext() && allTypesV3) {
+      itChk.next();
+      if (itChk.key().length() != 3 || type2to3(sys, itChk.key()) != itChk.key()) {
+        allTypesV3 = false;
+      }
+    }
+
     std::vector <const t_rnxObs*> hlp(header.nTypes(sys));
     for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
       hlp[iTypeV3] = 0;
       QString typeV3 = header.obsType(sys, iTypeV3);
+      if (allTypesV3) {
+        QMap<QString, t_rnxObs>::const_iterator itV3 = rnxSat.obs.constFind(typeV3);
+        if (itV3 != rnxSat.obs.constEnd() && itV3.value().value != 0.0) {
+          hlp[iTypeV3] = &itV3.value();
+        }
+        continue;
+      }
       QMapIterator<QString, t_rnxObs> itObs(rnxSat.obs);
 
@@ -1559,27 +1560,33 @@
 
     if (header.nTypes(sys)) {
-      *stream << rnxSat.prn.toString().c_str();
+      QString record = rnxSat.prn.toString().c_str();
       for (int iTypeV3 = 0; iTypeV3 < header.nTypes(sys); iTypeV3++) {
         const t_rnxObs* rnxObs = hlp[iTypeV3];
         if (rnxObs == 0) {
-          *stream << QString().leftJustified(16);
+          record += QString().leftJustified(16);
         }
         else {
-          *stream << QString("%1").arg(rnxObs->value, 14, 'f', 3);
+          record += QString("%1").arg(rnxObs->value, 14, 'f', 3);
           if (rnxObs->lli != 0.0) {
-            *stream << QString("%1").arg(rnxObs->lli, 1);
+            record += QString("%1").arg(rnxObs->lli, 1);
           }
           else {
-            *stream << ' ';
+            record += ' ';
           }
           if (rnxObs->snr != 0.0) {
-            *stream << QString("%1").arg(rnxObs->snr, 1);
+            record += QString("%1").arg(rnxObs->snr, 1);
           }
           else {
-            *stream << ' ';
+            record += ' ';
           }
         }
       }
-      *stream << Qt::endl;
+      // Trailing blanks can be removed from the records (RINEX 4.02, 4.)
+      int len = record.length();
+      while (len > 3 && record[len-1] == ' ') {
+        len--;
+      }
+      record.truncate(len);
+      *stream << record << Qt::endl;
     }
   }
Index: trunk/BNC/src/upload/bncrtnetuploadcaster.cpp
===================================================================
--- trunk/BNC/src/upload/bncrtnetuploadcaster.cpp	(revision 11052)
+++ trunk/BNC/src/upload/bncrtnetuploadcaster.cpp	(revision 11057)
@@ -350,13 +350,20 @@
     memset(&co, 0, sizeof(co));
     co.EpochTime[CLOCKORBIT_SATGPS] = static_cast<int>(epoTime.gpssec());
-    double gt = epoTime.gpssec() - gnumleap(year, month, day);
-    if (_ssrFormat == "RTCM-SSR") {
-        gt += 3 * 3600;
-    }
-    co.EpochTime[CLOCKORBIT_SATGLONASS] = static_cast<int>(fmod(gt, 86400.0));
+    if (_ssrFormat == "IGS-SSR") {
+      // SSR Epoch Time (IDF003) in one continuous time scale for all GNSS:
+      // GPS seconds of week also for GLONASS and BDS (IGS SSR v1.00)
+      co.EpochTime[CLOCKORBIT_SATGLONASS] = static_cast<int>(epoTime.gpssec());
+      co.EpochTime[CLOCKORBIT_SATBDS] = static_cast<int>(epoTime.gpssec());
+    }
+    else {
+      // RTCM-SSR (old and new): GLONASS Epoch Time DF386, seconds since the
+      // beginning of the GLONASS day (GLONASS time = UTC(SU) + 3h)
+      double gt = epoTime.gpssec() - gnumleap(year, month, day) + 3 * 3600;
+      co.EpochTime[CLOCKORBIT_SATGLONASS] = static_cast<int>(fmod(gt, 86400.0));
+      co.EpochTime[CLOCKORBIT_SATBDS] = static_cast<int>(epoTime.bdssec());
+    }
     co.EpochTime[CLOCKORBIT_SATGALILEO] = static_cast<int>(epoTime.gpssec());
     co.EpochTime[CLOCKORBIT_SATQZSS] = static_cast<int>(epoTime.gpssec());
     co.EpochTime[CLOCKORBIT_SATSBAS] = static_cast<int>(epoTime.gpssec());
-    co.EpochTime[CLOCKORBIT_SATBDS] = static_cast<int>(epoTime.bdssec());
     co.Supplied[_ssrCorr->COBOFS_CLOCK] = 1;
     co.Supplied[_ssrCorr->COBOFS_ORBIT] = 1;
