Index: trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp
===================================================================
--- trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11031)
+++ trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp	(revision 11038)
@@ -89,4 +89,7 @@
           BNC_CORE, SLOT(slotNewMetaData(t_metaData)));
 
+  connect(this, SIGNAL(newSatAntennas(QList<t_satAntenna>)),
+          BNC_CORE, SLOT(slotNewSatAntennas(QList<t_satAntenna>)));
+
   connect(this, SIGNAL(providerIDChanged(QString)),
           BNC_CORE, SIGNAL(providerIDChanged(QString)));
@@ -101,4 +104,9 @@
   _providerID[2] = -1;
 
+  for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
+    _antennaSentIOD[s] = UINT_MAX; // "never sent"
+  }
+
+  _stateSnapshot = new t_stateSnapshot;
   _ssrCorr = 0;
 
@@ -110,4 +118,5 @@
   delete _out;
   delete _ssrCorr;
+  delete _stateSnapshot;
   _IODs.clear();
   _orbCorrections.clear();
@@ -118,4 +127,5 @@
   _vTecMap.clear();
   _metaDataMap.clear();
+  _satAntennas.clear();
 }
 
@@ -185,17 +195,11 @@
   while(_buffer.size()) {
 
-    struct SsrCorr::ClockOrbit clkOrbSav;
-    struct SsrCorr::CodeBias   codeBiasSav;
-    struct SsrCorr::PhaseBias  phaseBiasSav;
-    struct SsrCorr::VTEC       vTECSav;
-    struct SsrCorr::MetaData   metaDataSav;
-    struct SsrCorr::Antenna    antennaSav;
     // save state
-    memcpy(&clkOrbSav,     &_clkOrb,    sizeof(clkOrbSav)); 
-    memcpy(&codeBiasSav,   &_codeBias,  sizeof(codeBiasSav));
-    memcpy(&phaseBiasSav,  &_phaseBias, sizeof(phaseBiasSav));
-    memcpy(&vTECSav,       &_vTEC,      sizeof(vTECSav));
-    memcpy(&metaDataSav,   &_metaData,  sizeof(metaDataSav));
-    memcpy(&antennaSav,    &_antenna,   sizeof(antennaSav));
+    memcpy(&_stateSnapshot->clkOrb,    &_clkOrb,    sizeof(_clkOrb));
+    memcpy(&_stateSnapshot->codeBias,  &_codeBias,  sizeof(_codeBias));
+    memcpy(&_stateSnapshot->phaseBias, &_phaseBias, sizeof(_phaseBias));
+    memcpy(&_stateSnapshot->vTEC,      &_vTEC,      sizeof(_vTEC));
+    memcpy(&_stateSnapshot->metaData,  &_metaData,  sizeof(_metaData));
+    memcpy(&_stateSnapshot->antenna,   &_antenna,   sizeof(_antenna));
 
     int bytesused = 0;
@@ -205,9 +209,9 @@
 
     if      (irc <= -30) { // not enough data - restore state and exit loop
-      memcpy(&_clkOrb,     &clkOrbSav,    sizeof(clkOrbSav));
-      memcpy(&_codeBias,   &codeBiasSav,  sizeof(codeBiasSav));
-      memcpy(&_phaseBias,  &phaseBiasSav, sizeof(phaseBiasSav));
-      memcpy(&_vTEC,       &vTECSav,      sizeof(vTECSav));
-      memcpy(&_metaData,   &metaDataSav,  sizeof(metaDataSav));
+      memcpy(&_clkOrb,     &_stateSnapshot->clkOrb,    sizeof(_clkOrb));
+      memcpy(&_codeBias,   &_stateSnapshot->codeBias,  sizeof(_codeBias));
+      memcpy(&_phaseBias,  &_stateSnapshot->phaseBias, sizeof(_phaseBias));
+      memcpy(&_vTEC,       &_stateSnapshot->vTEC,      sizeof(_vTEC));
+      memcpy(&_metaData,   &_stateSnapshot->metaData,  sizeof(_metaData));
       break;
     }
@@ -640,4 +644,78 @@
   }
 
+  // Satellite Antenna corrections
+  // ------------------------------
+  // _antenna, like _metaData, carries no epoch field of its own and is never
+  // wiped by reset(), so each system's data persists across unrelated
+  // messages. _antennaSentIOD tracks the last SatelliteAntennaIOD actually
+  // emitted per system, so unrelated messages (and unchanged retransmissions
+  // of the same antenna data) don't cause duplicate output - the same
+  // freshness problem _phaseBiasSentEpoch solves for phase biases.
+  {
+    struct SysInfo { unsigned sysIdx; char sysChar; unsigned numSat; unsigned offset; };
+    const SysInfo sysInfos[] = {
+      { CLOCKORBIT_SATGPS,     'G', CLOCKORBIT_NUMGPS,     CLOCKORBIT_OFFSETGPS },
+      { CLOCKORBIT_SATGLONASS, 'R', CLOCKORBIT_NUMGLONASS, CLOCKORBIT_OFFSETGLONASS },
+      { CLOCKORBIT_SATGALILEO, 'E', CLOCKORBIT_NUMGALILEO, CLOCKORBIT_OFFSETGALILEO },
+      { CLOCKORBIT_SATQZSS,    'J', CLOCKORBIT_NUMQZSS,    CLOCKORBIT_OFFSETQZSS },
+      { CLOCKORBIT_SATBDS,     'C', CLOCKORBIT_NUMBDS,     CLOCKORBIT_OFFSETBDS },
+    };
+    for (const SysInfo& si : sysInfos) {
+      unsigned s = si.sysIdx;
+      if (_antenna.SatelliteAntennaIOD[s] == _antennaSentIOD[s]) {
+        continue; // nothing new for this system since the last emit
+      }
+      if (_antenna.SatelliteMask[s] == 0) {
+        continue; // this system's antenna data was never decoded
+      }
+      for (unsigned k = 0; k < si.numSat; k++) {
+        if (!((_antenna.SatelliteMask[s] >> (63 - k)) & 1ULL)) {
+          continue;
+        }
+        int num  = k + 1; // PRN within this system, DF394 MSB-first convention
+        int flag = 0;
+        switch (si.sysChar) {
+        case 'G': flag = t_eph::LNAV;    break;
+        case 'R': flag = t_eph::FDMA_M;  break;
+        case 'E': flag = t_eph::INAV;    break;
+        case 'J': flag = t_eph::LNAV;    break;
+        case 'C': flag = (num < 6 || (num > 58 && num < 63)) ? t_eph::D2 : t_eph::D1; break;
+        }
+
+        t_satAntenna satAntenna;
+        satAntenna._prn.set(si.sysChar, num, flag);
+        satAntenna._staID                     = _staID.toStdString();
+        satAntenna._time                      = _lastTime;
+        satAntenna._ssrProviderID             = _antenna.SSRProviderID[s];
+        satAntenna._satelliteAntennaIOD       = _antenna.SatelliteAntennaIOD[s];
+        satAntenna._phaseCenterInfoInd        = _antenna.PhaseCenterInformationIndicator[s];
+        satAntenna._groupDelayInfoInd         = _antenna.GroupDelayInformationIndicator[s];
+        satAntenna._nadirAngleDependentCorrInd = _antenna.NadirAngleDependentCorrectionIndicator[s];
+        satAntenna._maximumOffNadirAngle      = _antenna.maximumOffNadirAngle[s];
+        satAntenna._nadirAngleRangeExtension  = _antenna.NadirAngleDependentCorrectionRangeExtension[s];
+
+        const SsrCorr::Antenna::SatellitePart& satPart = _antenna.Sat[si.offset + k];
+        for (unsigned f = 0; f < ANT_MAXFREQUENCIES; f++) {
+          if (!((satPart.GnssFrequencyMask >> (ANT_MAXFREQUENCIES - 1 - f)) & 1U)) {
+            continue;
+          }
+          t_frqAntenna frqAntenna;
+          frqAntenna._freqIndex                = f;
+          frqAntenna._nadirCorrectionIndicator = satPart.Freq[f].NadirCorrectionIndicator;
+          frqAntenna._nadirCorrection          = satPart.Freq[f].NadirCorrection;
+          if (satAntenna._nadirAngleDependentCorrInd) {
+            unsigned count = satAntenna._maximumOffNadirAngle + 1;
+            for (unsigned deg = 0; deg < count && deg < ANT_MAXNADIRDEGREES; deg++) {
+              frqAntenna._nadirAngleCorrection.push_back(satPart.Freq[f].NadirAngleCorrection[deg]);
+            }
+          }
+          satAntenna._freq.push_back(frqAntenna);
+        }
+        _satAntennas[_lastTime].append(satAntenna);
+      }
+      _antennaSentIOD[s] = _antenna.SatelliteAntennaIOD[s];
+    }
+  }
+
   // Dump all older epochs
   // ---------------------
@@ -694,4 +772,13 @@
       t_metaData::write(_out, itMD.value());
       itMD.remove();
+    }
+  }
+  QMutableMapIterator<bncTime, QList<t_satAntenna> > itAnt(_satAntennas);
+  while (itAnt.hasNext()) {
+    itAnt.next();
+    if (itAnt.key() < _lastTime) {
+      emit newSatAntennas(itAnt.value());
+      t_satAntenna::writeEpoch(_out, itAnt.value());
+      itAnt.remove();
     }
   }
