Index: /trunk/BNC/src/PPP/pppSatObs.cpp
===================================================================
--- /trunk/BNC/src/PPP/pppSatObs.cpp	(revision 11041)
+++ /trunk/BNC/src/PPP/pppSatObs.cpp	(revision 11042)
@@ -409,7 +409,21 @@
 // provider is relied on to keep DF+010 synchronized with DF+069 across all
 // systems for this to work.
-////////////////////////////////////////////////////////////////////////////
-static bool ssrSatAntennaTrusted(const t_satAntenna* satAntenna) {
+//
+// Separately, providers only guarantee SatelliteAntennaIOD uniqueness within
+// a rolling 64-day window (it wraps/repeats after that), so a client that
+// has been without a fresh Antenna message for that system for 64 days or
+// more must treat any cached data as unverifiable and stop using it, even
+// if the IOD/Metadata check above would otherwise pass - the same IOD value
+// could by then legitimately mean something else. epoTime is the current
+// processing epoch, compared against t_satAntenna::_time (when this data
+// was actually decoded, not a wire epoch - the message carries none).
+////////////////////////////////////////////////////////////////////////////
+static bool ssrSatAntennaTrusted(const t_satAntenna* satAntenna, const bncTime& epoTime) {
+  const double MAX_AGE_SEC = 64.0 * 86400.0;
   if (!satAntenna || satAntenna->_satelliteAntennaIOD == 0) {
+    return false;
+  }
+  if (epoTime.valid() && satAntenna->_time.valid() &&
+      epoTime - satAntenna->_time >= MAX_AGE_SEC) {
     return false;
   }
@@ -543,5 +557,5 @@
   if (PPP_CLIENT->antex()) {
     const t_satAntenna* satAntenna = PPP_CLIENT->obsPool()->satAntenna(_prn);
-    if (!ssrSatAntennaTrusted(satAntenna)) {
+    if (!ssrSatAntennaTrusted(satAntenna, _time)) {
       satAntenna = 0; // untrusted (IOD zero, or not confirmed via Metadata) - fall back to ANTEX
     }
Index: /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit.h
===================================================================
--- /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit.h	(revision 11041)
+++ /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit.h	(revision 11042)
@@ -604,5 +604,16 @@
 #define E_NADIR_ANGLE_DEPENDENT_CORR_RANGE_EXTENSION(a) ADDBITS(4,  a)                      /* DF+015 (new RTCM) to extend DF+021*/
 #define E_SATELLITE_SET_INDICATOR(a)                    ADDBITS(1,  a)                      /* DF+016 (new RTCM) */
-#define E_SATELLITE_MASK(a)                             ADDBITS(64, a)                      /* DF394 */
+/* ADDBITS' mask "(1<<a)-1" uses a 32-bit int literal, so a plain
+   ADDBITS(64, a) is broken the same way GETSSRBITS(a, 64) was on decode
+   (see D_SATELLITE_MASK) - here even ADDBITS(32, ...) is unsafe, since
+   shifting a 32-bit int by exactly its own width is undefined behavior
+   too. Stay well clear of that edge by writing four 16-bit chunks. */
+#define E_SATELLITE_MASK(a)                             { \
+  uint64_t mask64_ = (a); \
+  ADDBITS(16, (mask64_ >> 48) & 0xFFFFU) \
+  ADDBITS(16, (mask64_ >> 32) & 0xFFFFU) \
+  ADDBITS(16, (mask64_ >> 16) & 0xFFFFU) \
+  ADDBITS(16,  mask64_        & 0xFFFFU) \
+}                                                                             /* DF394 */
 #define E_FREQUENCY_SET_INDICATOR(a)                    ADDBITS(1,  a)                      /* DF+017 (new RTCM) */
 #define E_GNSS_FREQUENCY_MASK(a)                        ADDBITS(6,  a)                      /* DF+018 (new RTCM) */
Index: /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_igs.cpp
===================================================================
--- /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_igs.cpp	(revision 11041)
+++ /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_igs.cpp	(revision 11042)
@@ -945,2 +945,10 @@
 }
 
+//
+////////////////////////////////////////////////////////////////////////////
+size_t SsrCorrIgs::MakeAntenna(const struct Antenna*, SatAntennaType, char*, size_t) {
+  // This format does not encode Satellite Antenna message content; nothing
+  // to encode.
+  return 0;
+}
+
Index: /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_igs.h
===================================================================
--- /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_igs.h	(revision 11041)
+++ /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_igs.h	(revision 11042)
@@ -227,4 +227,5 @@
   size_t MakeVTEC(const struct VTEC *v, int moremessagesfollow, char *buffer, size_t size);
   size_t MakeMetaData(const struct MetaData *md, char *buffer, size_t size);
+  size_t MakeAntenna(const struct Antenna *a, SatAntennaType type, char *buffer, size_t size);
   enum GCOB_RETURN GetSSR(struct ClockOrbit *co, struct CodeBias *cb, struct VTEC *v, 
                           struct PhaseBias *pb, struct MetaData *md, struct Antenna* a,
Index: /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm.cpp
===================================================================
--- /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm.cpp	(revision 11041)
+++ /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm.cpp	(revision 11042)
@@ -1399,2 +1399,10 @@
 }
 
+//
+////////////////////////////////////////////////////////////////////////////
+size_t SsrCorrRtcm::MakeAntenna(const struct Antenna*, SatAntennaType, char*, size_t) {
+  // Satellite Antenna messages are not defined in (old) RTCM-SSR; nothing
+  // to encode.
+  return 0;
+}
+
Index: /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm.h
===================================================================
--- /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm.h	(revision 11041)
+++ /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm.h	(revision 11042)
@@ -226,4 +226,5 @@
   size_t MakeVTEC(const struct VTEC *v, int moremessagesfollow, char *buffer, size_t size);
   size_t MakeMetaData(const struct MetaData *md, char *buffer, size_t size);
+  size_t MakeAntenna(const struct Antenna *a, SatAntennaType type, char *buffer, size_t size);
   enum GCOB_RETURN GetSSR(struct ClockOrbit *co, struct CodeBias *cb, struct VTEC *v, 
                           struct PhaseBias *pb, struct MetaData *md, struct Antenna* a,
Index: /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm_new.cpp
===================================================================
--- /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm_new.cpp	(revision 11041)
+++ /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm_new.cpp	(revision 11042)
@@ -495,4 +495,78 @@
 }
 
+//
+////////////////////////////////////////////////////////////////////////////
+size_t SsrCorrRtcmNew::MakeAntenna(const struct Antenna* a, SatAntennaType type,
+  char* buffer, size_t size) {
+  unsigned int s, k, satBlockIdx, freqBlockIdx, deg, f, numSatBlocks, numFreqBlocks;
+  unsigned int satPositions[64], numSatPositions;
+  unsigned int freqPositions[ANT_MAXFREQUENCIES], numFreqPositions;
+
+  STARTDATA
+
+    for (s = 0; s < CLOCKORBIT_SATNUM; ++s) {
+      if (a->SatelliteMask[s] == 0 ||
+          !(type == SATANTTYPE_AUTO || type == corType[s][COBOFS_SATANT])) {
+        continue;
+      }
+      INITBLOCK
+        E_RTCM_MESSAGE_NUMBER(corType[s][COBOFS_SATANT])
+        E_SSR_PROVIDER_ID(a->SSRProviderID[s])
+        E_SATELLITE_ANTENNA_IOD(a->SatelliteAntennaIOD[s])
+        E_PHASE_CENTER_INFO_INDICATOR(a->PhaseCenterInformationIndicator[s])
+        E_GROUP_DELAY_INFO_INDICATOR(a->GroupDelayInformationIndicator[s])
+        E_NADIR_ANGLE_DEPENDENT_CORR_INDICATOR(a->NadirAngleDependentCorrectionIndicator[s])
+        if (a->NadirAngleDependentCorrectionIndicator[s]) {
+          E_MAXIMUM_OFF_NADIR_ANGLE(a->maximumOffNadirAngle[s])
+          E_NADIR_ANGLE_DEPENDENT_CORR_RANGE_EXTENSION(a->NadirAngleDependentCorrectionRangeExtension[s])
+        }
+        E_SATELLITE_SET_INDICATOR(a->SatelliteSetIndicator[s])
+        E_SATELLITE_MASK(a->SatelliteMask[s])
+
+        /* same MSB-first mask convention as D_SATELLITE_MASK/GetSSR */
+        numSatPositions = 0;
+        for (k = 0; k < 64 && satoffset[s] + k < satoffset[s + 1]; ++k) {
+          if ((a->SatelliteMask[s] >> (63 - k)) & 1ULL) {
+            satPositions[numSatPositions++] = satoffset[s] + k;
+          }
+        }
+        numSatBlocks = a->SatelliteSetIndicator[s] ? (numSatPositions ? 1 : 0) : numSatPositions;
+
+        for (satBlockIdx = 0; satBlockIdx < numSatBlocks; ++satBlockIdx) {
+          /* satellites sharing a block (SatelliteSetIndicator set) must
+             already carry identical Sat[] content - the caller is
+             responsible for that, same as the encoder trusts NumberOfSat
+             elsewhere; only the first masked satellite's data is sent */
+          const struct Antenna::SatellitePart& satBlock = a->Sat[satPositions[satBlockIdx]];
+          E_FREQUENCY_SET_INDICATOR(satBlock.FrequencySetIndicator)
+          E_GNSS_FREQUENCY_MASK(satBlock.GnssFrequencyMask)
+
+          numFreqPositions = 0;
+          for (k = 0; k < ANT_MAXFREQUENCIES; ++k) {
+            if ((satBlock.GnssFrequencyMask >> (ANT_MAXFREQUENCIES - 1 - k)) & 1U) {
+              freqPositions[numFreqPositions++] = k;
+            }
+          }
+          numFreqBlocks = satBlock.FrequencySetIndicator ? (numFreqPositions ? 1 : 0) : numFreqPositions;
+
+          for (freqBlockIdx = 0; freqBlockIdx < numFreqBlocks; ++freqBlockIdx) {
+            f = satBlock.FrequencySetIndicator ? freqPositions[0] : freqPositions[freqBlockIdx];
+            E_NADIR_CORR_INDICATOR(satBlock.Freq[f].NadirCorrectionIndicator)
+            if (satBlock.Freq[f].NadirCorrectionIndicator) {
+              E_NADIR_CORRECTION(satBlock.Freq[f].NadirCorrection)
+            }
+            if (a->NadirAngleDependentCorrectionIndicator[s]) {
+              for (deg = 0; deg <= a->maximumOffNadirAngle[s] && deg < ANT_MAXNADIRDEGREES; ++deg) {
+                E_NADIR_ANGLE_DEPENDENT_CORRECTION(satBlock.Freq[f].NadirAngleCorrection[deg],
+                  a->NadirAngleDependentCorrectionRangeExtension[s])
+              }
+            }
+          }
+        }
+      ENDBLOCK
+    }
+  return ressize;
+}
+
 enum GCOB_RETURN SsrCorrRtcmNew::GetSSR(struct ClockOrbit* co, struct CodeBias* cb, struct VTEC* v,
   struct PhaseBias* pb, struct MetaData* md, struct Antenna* a, const char* buffer, size_t size, int* bytesused) {
@@ -1152,5 +1226,13 @@
 
       for (satBlockIdx = 0; satBlockIdx < numSatBlocks; ++satBlockIdx) {
-        struct Antenna::SatellitePart satBlock;
+        /* zero-initialized, not just declared: DF+019/DF+013 unset means the
+           correction IS zero (not "not present"), per spec - a plain
+           declaration left NadirCorrection/NadirAngleCorrection as
+           uninitialized stack garbage for every satellite/frequency whose
+           indicator was 0, which is nearly all of them in real data. This
+           went unnoticed because normal call depth happened to leave zero
+           there; it surfaced via a deeper (nested) call stack during
+           MakeAntenna round-trip testing. */
+        struct Antenna::SatellitePart satBlock = {};
         D_FREQUENCY_SET_INDICATOR(satBlock.FrequencySetIndicator)
         D_GNSS_FREQUENCY_MASK(satBlock.GnssFrequencyMask)
Index: /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm_new.h
===================================================================
--- /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm_new.h	(revision 11041)
+++ /trunk/BNC/src/RTCM3/clock_and_orbit/clock_orbit_rtcm_new.h	(revision 11042)
@@ -280,4 +280,5 @@
   size_t MakeVTEC(const struct VTEC *v, int moremessagesfollow, char *buffer, size_t size);
   size_t MakeMetaData(const struct MetaData *md, char *buffer, size_t size);
+  size_t MakeAntenna(const struct Antenna *a, SatAntennaType type, char *buffer, size_t size);
   enum GCOB_RETURN GetSSR(struct ClockOrbit *co, struct CodeBias *cb, struct VTEC *v, 
                           struct PhaseBias *pb, struct MetaData *md, struct Antenna* a,
Index: /trunk/BNC/src/bnchelp.html
===================================================================
--- /trunk/BNC/src/bnchelp.html	(revision 11041)
+++ /trunk/BNC/src/bnchelp.html	(revision 11042)
@@ -4615,5 +4615,5 @@
   <ol type="1">
     <li>Special character '&#62;' is the first character in each 'Epoch Record' (as we have it in RINEX Version 3)</li>
-    <li>SSR message or topic descriptor, valid descriptors are:<br>ORBIT, CLOCK, CODE_BIAS, PHASE_BIAS, or VTEC</li>
+    <li>SSR message or topic descriptor, valid descriptors are:<br>ORBIT, CLOCK, CODE_BIAS, PHASE_BIAS, VTEC, META_DATA, or SAT_ANTENNA</li>
     <li>Year, GPS time</li>
     <li>Month, GPS time</li>
@@ -4652,4 +4652,5 @@
   <pre><p style="font-family:Monospace">
 > ORBIT 2022 10 01 23 59 45.0 2 110 SSRA00CNE1
+ 2
 G01          93    -0.1588    -0.8664    -0.0600        0.2210    -0.1200    -0.0400
 G02          33     0.0491    -1.7468     0.5608        0.1820     0.0040     0.0200
@@ -4672,4 +4673,17 @@
 <p></pre>
 
+  <p>
+    The second record in this block provides one parameter, an SSR source format indicator:
+  <ul>
+    <li>SSR source format indicator<br>
+      0 &minus; unknown<br>
+      1 &minus; (old) RTCM-SSR / IGS-SSR<br>
+      2 &minus; (new) RTCM-SSR
+    </li>
+  </ul>
+  This indicator determines which relativistic correction formula applies when the orbit correction is combined
+  with the Broadcast Ephemeris: the velocity-based formula of IS-GPS-200D 20.3.3.3.3.1 for the (new) RTCM-SSR
+  format, or the classic eccentricity-based formula otherwise.
+  </p>
   Records in this block provide the following satellite specific information:
   <ul>
@@ -4763,5 +4777,5 @@
   <pre><p style="font-family:Monospace">
 > PHASE_BIAS 2022 10 01 23 59 45.0 2 110 SSRA00CNE1
- 0   1
+ 2   0   1
 G01 157.50000000   0.00000000    3   1C    -0.6023   1   2   7   2W    -0.8679   1   2   7   5I    -0.8614   1   2   7
 G02  13.35937500   0.00000000    2   1C     0.7878   1   2   4   2W     1.1236   1   2   4
@@ -4783,16 +4797,31 @@
 </p></pre>
   <p>
-    The second record in this block provides the following consistency information:
+    The second record in this block starts with the same SSR source format indicator used in the 'ORBIT' block
+    (0 = unknown, 1 = (old) RTCM-SSR / IGS-SSR, 2 = (new) RTCM-SSR), followed by two more numbers whose meaning
+    depends on it, since RTCM-SSR and IGS-SSR/(old) RTCM-SSR define different phase bias parameters here:
   <ul>
 
-    <li>Dispersive bias consistency indicatory<br>
-      0 &minus; phase biases valid for non-dispersive signal only<br>
-      1 &minus; phase biases maintain consistency between non-dispersive and all original dispersive phase signals
-    </li>
-
-    <li>MW consistency indicator<br>
-      0 &minus; code and phase biases are independently derived<br>
-      1 &minus; consistency between code and phase biases is maintained for the MW combinations
-    </li>
+    <li>SSR source format indicator = 2, i.e. (new) RTCM-SSR:</li>
+    <ul>
+      <li>Satellite Yaw Information indicator<br>
+        0 &minus; yaw angle/rate not provided (both fields below are zero)<br>
+        1 &minus; yaw angle/rate provided
+      </li>
+      <li>Extended Phase Bias Property ID &minus; a satellite-specific property ID (0-15) used to correlate a
+          later Extended Phase Bias message's wide-lane information back onto this satellite's signals</li>
+    </ul>
+
+    <li>SSR source format indicator = 0 or 1, i.e. unknown or (old) RTCM-SSR/IGS-SSR:</li>
+    <ul>
+      <li>Dispersive Bias Consistency indicator<br>
+        0 &minus; phase biases valid for non-dispersive signal only<br>
+        1 &minus; phase biases maintain consistency between non-dispersive and all original dispersive phase
+            signals
+      </li>
+      <li>MW Consistency indicator<br>
+        0 &minus; code and phase biases are independently derived<br>
+        1 &minus; consistency between code and phase biases is maintained for the MW combinations
+      </li>
+    </ul>
 
   </ul>
@@ -4858,4 +4887,90 @@
     <li>Spherical harmonic coefficients C and S, sorted by degree and order (0 to maximum)</li>
   </ul>
+  </p>
+
+  <p>
+    <b>Example for block 'META_DATA' carrying model/correction metadata</b>
+  </p>
+  <pre><p style="font-family:Monospace">
+> META_DATA 2026 06 26 09 00 15.0 0 3 SSRA00BKG0
+2 260 3
+ 3 1 0  0 0   0
+ 6 1 0  0 0   0
+ 7 1 0  0 0   0
+</p></pre>
+  <p>
+    The second record in this block provides three parameters:
+  <ul>
+    <li>SSR IOD</li>
+    <li>SSR Provider ID</li>
+    <li>SSR Solution ID</li>
+  </ul>
+  Following records, one per model/correction entry, provide:
+  <ul>
+    <li>Model/correction type indicator<br>
+      1 &minus; satellite antenna Phase Center Variations (PCV)<br>
+      2 &minus; satellite antenna Group Delay Variations (GDV)<br>
+      3 &minus; solid earth tides<br>
+      4 &minus; ocean loading<br>
+      5 &minus; pole tides<br>
+      6 &minus; relativity<br>
+      7 &minus; GNSS broadcast ephemeris reference
+    </li>
+    <li>Application indicator (whether the model/correction is applied on the provider's side)</li>
+    <li>Non-default model indicator</li>
+    <li>Non-default model identifier (only valid if the non-default model indicator is set)</li>
+    <li>Data IOD indicator</li>
+    <li>Data IOD (only valid if the Data IOD indicator is set) &minus; for type 1 or 2, this confirms the
+        Satellite Antenna IOD of a 'SAT_ANTENNA' block below, see there</li>
+  </ul>
+  </p>
+
+  <p>
+    <b>Example for block 'SAT_ANTENNA' carrying satellite antenna corrections</b>
+  </p>
+  <pre><p style="font-family:Monospace">
+> SAT_ANTENNA 2026 06 26 09 00 15.0 0 16 SSRA00BKG0
+G08  260 23 1 0 1 16  7    2   G1 0     0.0000    0.0000   -0.0020   -0.0030   -0.0050   -0.0060   -0.0070   -0.0090   -0.0100   -0.0100   -0.0100   -0.0100   -0.0080   -0.0060   -0.0030    0.0000    0.0060    0.0120   G2 0     0.0000    0.0000   -0.0020   -0.0030   -0.0050   -0.0060   -0.0070   -0.0090   -0.0100   -0.0100   -0.0100   -0.0100   -0.0080   -0.0060   -0.0030    0.0000    0.0060    0.0120
+G16  260 23 1 0 1 16  7    2   G1 0     0.0000    0.0000    0.0010    0.0030    0.0050    0.0070    0.0100    0.0110    0.0110    0.0100    0.0090    0.0070    0.0050    0.0040    0.0040    0.0050    0.0050    0.0080   G2 0     0.0000    0.0000    0.0010    0.0030    0.0050    0.0070    0.0100    0.0110    0.0110    0.0100    0.0090    0.0070    0.0050    0.0040    0.0040    0.0050    0.0050    0.0080
+..
+</p></pre>
+  <p>
+    Records in this block provide the following satellite specific information:
+  <ul>
+    <li>GNSS Indicator and Satellite Vehicle Pseudo Random Number</li>
+    <li>SSR Provider ID</li>
+    <li>Satellite Antenna IOD &minus; unique per Provider ID within a rolling 64-day period, see note below</li>
+    <li>Phase Center Information indicator &minus; corrections apply to phaserange observations</li>
+    <li>Group Delay Information indicator &minus; corrections apply to pseudorange observations</li>
+    <li>Nadir Angle Dependent Correction indicator</li>
+    <li>Maximum off-nadir angle [deg] (only meaningful if the previous indicator is set)</li>
+    <li>Nadir angle dependent correction range extension (extra bit width for the correction values below)</li>
+    <li>Number of frequencies, succeeded by frequency specific information:</li>
+    <ul>
+      <li>Frequency label (e.g. G1, G2, G5, R1, R2, E1, E5, E6, E7, E8, J1, J2, J5, J6, C1, C2, C5, C6, C7)</li>
+      <li>Nadir correction indicator</li>
+      <li>Nadir correction [m] &minus; offset between the orbit antenna reference point and the frequency
+          reference point along the satellite's nadir direction; zero if the indicator above is not set</li>
+      <li>Nadir angle dependent correction [m], one value per 1-degree bin from 0 up to the maximum
+          off-nadir angle above (only present if the Nadir Angle Dependent Correction indicator is set)</li>
+      <li>Frequency label, ...</li>
+      <li>etc.</li>
+    </ul>
+  </ul>
+  </p>
+  <p>
+    Whether a given correction is a Phase Center Variation (PCV, applies to phaserange observations only), a
+    Group Delay Variation (GDV, applies to pseudorange observations only), or both at once (same values applied to
+    each observation type independently) is determined by the Phase Center Information and Group Delay Information
+    indicators, not by the type of value itself.
+  </p>
+  <p>
+    Before BNC's PPP client applies a satellite's antenna correction, it must be confirmed via a 'META_DATA' block:
+    the Satellite Antenna IOD must be non-zero and must match the Data IOD of a Metadata entry of type 1 (PCV) or
+    type 2 (GDV) &minus; either is sufficient, since Metadata carries this IOD once globally while the Satellite
+    Antenna IOD is GNSS-specific, so the same Metadata entries serve as the trust anchor for every GNSS. In
+    addition, since a Satellite Antenna IOD is only guaranteed unique within a rolling 64-day period, BNC discards
+    any Satellite Antenna correction that is 64 days or older, even if its IOD would otherwise still match, and
+    falls back to using ANTEX-based satellite antenna corrections instead.
   </p>
 
