Index: /trunk/BNC/CHANGELOG.md
===================================================================
--- /trunk/BNC/CHANGELOG.md	(revision 11204)
+++ /trunk/BNC/CHANGELOG.md	(revision 11205)
@@ -62,4 +62,5 @@
 - FIXED: GLONASS ephemerides read from RINEX files left members not contained in RINEX (e.g. ln of string 5) uninitialized
 - FIXED: SP3, clock RINEX and Bias-SINEX files of the combination/upload: the last epoch, the SP3 'EOF' and the Bias-SINEX closing lines were missing at the end of BNC (files only closed after the end of the event loop); a run starting at 00:00 gave a SP3 header with leading blanks and the start time of the second epoch; the epoch at the start of a new file (e.g. 00:00:00) is the last epoch of the file before, too, also with sampling interval 0 (before only with uploadSamplClkRnx/uploadSamplSp3 > 0)
+- FIXED: Combination (Kalman filter): the clock of a satellite not provided by all ACs depended on the history of the filter, as the AC offsets could be shifted against the satellite offsets; after a restart e.g. G13 (SVN83, provided by 4 of 6 ACs) was 1.4 m off in SSRA02IGS0. Additional condition: the satellite offsets of each AC sum up to zero
 - ADDED: Regression test suite ('tests'): example configurations and short raw captures replayed through BNC and compared with reference outputs
 - ADDED: macOS packaging script
Index: /trunk/BNC/src/bnchelp.html
===================================================================
--- /trunk/BNC/src/bnchelp.html	(revision 11204)
+++ /trunk/BNC/src/bnchelp.html	(revision 11205)
@@ -8323,9 +8323,9 @@
     <tr>
       <td>&nbsp; AC_Offset </td>
-      <td>&nbsp; &nbsp; AC specific offset</td>
+      <td>&nbsp; &nbsp; AC specific offset per GNSS (clock datum of the AC)</td>
     </tr>
     <tr>
       <td>&nbsp; Sat_Offset</td>
-      <td>&nbsp; &nbsp; Satellite specific offset common to all ACs</td>
+      <td>&nbsp; &nbsp; Offset of the AC for a satellite (e.g. different realizations of the code biases)</td>
     </tr>
     <tr>
@@ -8335,8 +8335,11 @@
   </table>
   </p>
-  These three parameter types differ in their statistical properties. The satellite clock offsets are assumed to be
-  static parameters
-  while AC specific and satellite specific offsets are stochastic parameters affected by white noise.
-  The solution is regularized by a set of minimal constraints. In case of a change of the 'SSR Provider ID',
+  These three parameter types differ in their statistical properties: the AC specific offsets and the combined clocks
+  are estimated anew every epoch (white noise), the satellite specific offsets are random walk parameters (1 cm per
+  10 seconds). The solution is regularized by minimal constraints: per epoch the combined clocks sum up to zero, the
+  satellite specific offsets of all ACs providing a satellite sum up to zero, and the satellite specific offsets of
+  each AC sum up to zero. The last condition makes the AC specific offset the mean clock difference of the AC; without
+  it, the combined clock of a satellite not provided by all ACs would depend on the history of the filter (e.g. after a
+  restart). In case of a change of the 'SSR Provider ID',
   'SSR Solution ID', or 'IOD SSR' (see section 'Upload Corrections'), the satellite clock offsets belonging to the
   corresponding analysis center are reset in the adjustment.
Index: /trunk/BNC/src/combination/bnccomb.cpp
===================================================================
--- /trunk/BNC/src/combination/bnccomb.cpp	(revision 11204)
+++ /trunk/BNC/src/combination/bnccomb.cpp	(revision 11205)
@@ -1870,5 +1870,13 @@
   }
 
-  const int nCon = (_method == filter) ? 1 + maxSat + excludedACs.size() : 0;
+  // ACs of this epoch, each with a zero-sum condition of its satellite offsets
+  QStringList epoACs;
+  for (int ii = 0; ii < corrs(sys).size(); ii++) {
+    if (!epoACs.contains(corrs(sys)[ii]->_acName)) {
+      epoACs << corrs(sys)[ii]->_acName;
+    }
+  }
+
+  const int nCon = (_method == filter) ? 1 + maxSat + epoACs.size() : 0;
 
   AA.ReSize(nObs+nCon, nPar);  AA = 0.0;
@@ -1944,4 +1952,5 @@
     int iCond = 1;
     QSet<int>     offExcluded; // offsets excluded from the zero-sum condition of their satellite
+    QSet<int>     offInDatum;  // offsets in the zero-sum condition of their satellite
     QSet<QString> acsInDatum;  // ACs with at least one offset in the zero-sum conditions
     // GNSS
@@ -1969,4 +1978,5 @@
         if (offUsed.contains(iPar)) {
           AA(nObs+iCond, iPar) = 1.0;
+          offInDatum << iPar;
           acsInDatum.insert(_params[sys][iPar-1]->AC);
         }
@@ -1976,14 +1986,18 @@
       }
     }
-    // ACs with all their offsets excluded from the zero-sum conditions above:
-    // the sum of their offsets is constrained to zero (otherwise not
-    // separable from the AC's epoch offset)
-    for (const QString& acName : excludedACs) {
+    // Zero-sum condition of the satellite offsets of each AC: without it, the
+    // AC epoch offsets can be shifted by a_i, compensated by the satellite
+    // offsets and, for a satellite not provided by all ACs, by its combined
+    // clock (shift: mean of a_i over the ACs providing it). This would leave
+    // the clocks of such satellites to the history of the filter (e.g. G13
+    // +1.4 m after a restart). With it, the AC epoch offset is the mean clock
+    // difference of the AC. Sum over the offsets in the satellite conditions
+    // above; for an AC with all its offsets excluded from them, over these
+    // (otherwise not separable from the AC's epoch offset).
+    for (const QString& acName : epoACs) {
       ++iCond;
-      if (acsInDatum.contains(acName)) {
-        continue;
-      }
       PP(nObs+iCond) = Ph;
-      for (int iPar : offExcluded) {
+      const QSet<int>& offAC = acsInDatum.contains(acName) ? offInDatum : offExcluded;
+      for (int iPar : offAC) {
         if (_params[sys][iPar-1]->AC == acName) {
           AA(nObs+iCond, iPar) = 1.0;
