Index: trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11009)
+++ trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11010)
@@ -41,4 +41,5 @@
 #include <stdio.h>
 #include <math.h>
+#include <climits>
 
 #include "RTCM3coDecoder.h"
@@ -126,4 +127,7 @@
   if (resetPhaseBias) {
     memset(&_phaseBias, 0, sizeof(_phaseBias));
+    for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
+      _phaseBiasSentEpoch[s] = UINT_MAX; // "never sent"
+    }
   }
   memset(&_vTEC,      0, sizeof(_vTEC));
@@ -476,4 +480,19 @@
   // Phase Biases
   // -----------
+  // _phaseBias is no longer wiped between messages (kept alive so a later
+  // Extended Phase Bias message can correlate against it - see
+  // COBOFS_PBEXT), so without this guard the block below would re-append
+  // the same system's satellites into the output on every other message
+  // decoded in between two actual phase-bias messages. Only emit a
+  // system's satellites once per epoch, tracked per system by comparing
+  // against the epoch we last actually sent for it.
+  bool phaseBiasFresh[CLOCKORBIT_SATNUM];
+  for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
+    phaseBiasFresh[s] = _phaseBias.NumberOfSat[s] > 0 &&
+                         _phaseBias.EpochTime[s] != _phaseBiasSentEpoch[s];
+    if (phaseBiasFresh[s]) {
+      _phaseBiasSentEpoch[s] = _phaseBias.EpochTime[s];
+    }
+  }
   for (unsigned ii = 0; ii <  CLOCKORBIT_NUMGPS
                             + CLOCKORBIT_NUMGLONASS
@@ -487,4 +506,5 @@
     int flag = 0;
     if      (ii < _phaseBias.NumberOfSat[CLOCKORBIT_SATGPS]) {
+      if (!phaseBiasFresh[CLOCKORBIT_SATGPS]) continue;
       sys = 'G';
       flag = t_eph::LNAV;
@@ -492,4 +512,5 @@
     else if (ii >= CLOCKORBIT_OFFSETGLONASS &&
         ii < CLOCKORBIT_OFFSETGLONASS + _phaseBias.NumberOfSat[CLOCKORBIT_SATGLONASS]) {
+      if (!phaseBiasFresh[CLOCKORBIT_SATGLONASS]) continue;
       sys = 'R';
       flag = t_eph::FDMA_M;
@@ -497,4 +518,5 @@
     else if (ii >= CLOCKORBIT_OFFSETGALILEO &&
         ii < CLOCKORBIT_OFFSETGALILEO + _phaseBias.NumberOfSat[CLOCKORBIT_SATGALILEO]) {
+      if (!phaseBiasFresh[CLOCKORBIT_SATGALILEO]) continue;
       sys = 'E';
       flag = t_eph::INAV;
@@ -502,4 +524,5 @@
     else if (ii >= CLOCKORBIT_OFFSETQZSS &&
         ii < CLOCKORBIT_OFFSETQZSS + _phaseBias.NumberOfSat[CLOCKORBIT_SATQZSS]) {
+      if (!phaseBiasFresh[CLOCKORBIT_SATQZSS]) continue;
       sys = 'J';
       flag = t_eph::LNAV;
@@ -507,4 +530,5 @@
     else if (ii >= CLOCKORBIT_OFFSETSBAS &&
         ii < CLOCKORBIT_OFFSETSBAS + _phaseBias.NumberOfSat[CLOCKORBIT_SATSBAS]) {
+      if (!phaseBiasFresh[CLOCKORBIT_SATSBAS]) continue;
       sys = 'S';
       flag = t_eph::SBASL1;
@@ -512,4 +536,5 @@
     else if (ii >= CLOCKORBIT_OFFSETBDS &&
         ii < CLOCKORBIT_OFFSETBDS + _phaseBias.NumberOfSat[CLOCKORBIT_SATBDS]) {
+      if (!phaseBiasFresh[CLOCKORBIT_SATBDS]) continue;
       sys = 'C';
       if (num < 6) {// GEO
@@ -854,4 +879,12 @@
 void RTCM3coDecoder::setEpochTime() {
 
+  // _phaseBias is no longer wiped between messages (kept alive so a later
+  // Extended Phase Bias message can correlate against it), so
+  // NumberOfSat[s] > 0 alone no longer means "phase bias was just decoded
+  // this cycle" - it can be stale from many messages ago. Gate each
+  // phase-bias fallback below on the same per-system freshness tracking
+  // sendResults() uses (_phaseBiasSentEpoch), so a stale leftover value
+  // never gets picked as this cycle's epoch.
+  bncTime prevTime = _lastTime;
   _lastTime.reset();
 
@@ -868,5 +901,6 @@
     epoSecGPS = _codeBias.EpochTime[CLOCKORBIT_SATGPS];      // 0 .. 604799 s
   }
-  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGPS] > 0) {
+  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGPS] > 0 &&
+           _phaseBias.EpochTime[CLOCKORBIT_SATGPS] != _phaseBiasSentEpoch[CLOCKORBIT_SATGPS]) {
     epoSecGPS = _phaseBias.EpochTime[CLOCKORBIT_SATGPS];     // 0 .. 604799 s
   }
@@ -880,5 +914,6 @@
     epoSecGlo = _codeBias.EpochTime[CLOCKORBIT_SATGLONASS];  // 0 .. 86399 s
   }
-  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0) {
+  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGLONASS] > 0 &&
+           _phaseBias.EpochTime[CLOCKORBIT_SATGLONASS] != _phaseBiasSentEpoch[CLOCKORBIT_SATGLONASS]) {
     epoSecGlo = _phaseBias.EpochTime[CLOCKORBIT_SATGLONASS]; // 0 .. 86399 s
   }
@@ -889,5 +924,6 @@
     epoSecGal = _codeBias.EpochTime[CLOCKORBIT_SATGALILEO];
   }
-  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0) {
+  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATGALILEO] > 0 &&
+           _phaseBias.EpochTime[CLOCKORBIT_SATGALILEO] != _phaseBiasSentEpoch[CLOCKORBIT_SATGALILEO]) {
     epoSecGal = _phaseBias.EpochTime[CLOCKORBIT_SATGALILEO];
   }
@@ -898,5 +934,6 @@
     epoSecQzss = _codeBias.EpochTime[CLOCKORBIT_SATQZSS];
   }
-  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATQZSS] > 0) {
+  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATQZSS] > 0 &&
+           _phaseBias.EpochTime[CLOCKORBIT_SATQZSS] != _phaseBiasSentEpoch[CLOCKORBIT_SATQZSS]) {
     epoSecQzss = _phaseBias.EpochTime[CLOCKORBIT_SATQZSS];
   }
@@ -907,5 +944,6 @@
     epoSecSbas = _codeBias.EpochTime[CLOCKORBIT_SATSBAS];
   }
-  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATSBAS] > 0) {
+  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATSBAS] > 0 &&
+           _phaseBias.EpochTime[CLOCKORBIT_SATSBAS] != _phaseBiasSentEpoch[CLOCKORBIT_SATSBAS]) {
     epoSecSbas = _phaseBias.EpochTime[CLOCKORBIT_SATSBAS];
   }
@@ -916,5 +954,6 @@
     epoSecBds = _codeBias.EpochTime[CLOCKORBIT_SATBDS];
   }
-  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATBDS] > 0) {
+  else if (_phaseBias.NumberOfSat[CLOCKORBIT_SATBDS] > 0 &&
+           _phaseBias.EpochTime[CLOCKORBIT_SATBDS] != _phaseBiasSentEpoch[CLOCKORBIT_SATBDS]) {
     epoSecBds = _phaseBias.EpochTime[CLOCKORBIT_SATBDS];
   }
Index: trunk/BNC/src/RTCM3/RTCM3coDecoder.h
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3coDecoder.h	(revision 11009)
+++ trunk/BNC/src/RTCM3/RTCM3coDecoder.h	(revision 11010)
@@ -97,4 +97,5 @@
   QMap<bncTime, t_vTec>                 _vTecMap;
   QMap<bncTime, t_metaData>             _metaDataMap;
+  unsigned int                          _phaseBiasSentEpoch[CLOCKORBIT_SATNUM];
 };
 
