Ignore:
Timestamp:
Sep 28, 2026, 11:17:34 AM (5 hours ago)
Author:
stuerze
Message:

updates regarding RTCM-SSR and some ovarall improvements

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/BNC/src/RTCM3/RTCM3coDecoder.cpp

    r11041 r11047  
    105105
    106106  for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
    107     _antennaSentIOD[s] = UINT_MAX; // "never sent"
     107    _antennaReceived[s]    = false;
     108    _antennaIODKnown[s]    = false;
     109    _antennaIODProvider[s] = 0;
     110    _antennaIODCur[s]      = 0;
     111    _antennaIODPrev[s]     = 0;
     112  }
     113  for (unsigned i = 0; i < CLOCKORBIT_COUNTSAT; i++) {
     114    _antennaSentProviderID[i] = 0;
     115    _antennaSentIOD[i]        = 0; // meaningless while _antennaSentTime[i] is invalid ("never sent")
    108116  }
    109117
    … …  
    203211    memcpy(&_stateSnapshot->antenna,   &_antenna,   sizeof(_antenna));
    204212
     213    // _antenna persists across messages, so its content alone can't tell
     214    // whether a Satellite Antenna message arrived in this frame (an unchanged
     215    // retransmission looks identical). Clear messageType[] as a marker: the
     216    // COBOFS_SATANT decoder sets it (always non-zero) for each system it
     217    // decodes. Restored with the snapshot if the frame turns out incomplete.
     218    for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
     219      _antenna.messageType[s] = 0;
     220    }
     221
    205222    int bytesused = 0;
    206223
    … …  
    240257
    241258      if (irc == GCOBR_OK || irc == GCOBR_MESSAGEFOLLOWS ) {
     259        // Latch the reception until sendResults() consumes it - it may not
     260        // run for this frame if _lastTime is still invalid.
     261        for (unsigned s = 0; s < CLOCKORBIT_SATNUM; s++) {
     262          if (_antenna.messageType[s] != 0) {
     263            _antennaReceived[s] = true;
     264            checkAntennaIOD(s);
     265          }
     266        }
    242267        setEpochTime(); // sets _lastTime
    243268
    … …  
    342367      t_orbCorr orbCorr;
    343368      orbCorr._prn.set(sys, num, flag);
     369      checkPrnRange(orbCorr._prn);
    344370      orbCorr._staID     = _staID.toStdString();
    345371      orbCorr._iod       = _clkOrb.Sat[ii].IOD;
    … …  
    378404      t_clkCorr clkCorr;
    379405      clkCorr._prn.set(sys, _clkOrb.Sat[ii].ID, flag);
     406      checkPrnRange(clkCorr._prn);
    380407      clkCorr._staID      = _staID.toStdString();
    381408      clkCorr._time       = _lastTime;
    … …  
    470497    t_satCodeBias satCodeBias;
    471498    satCodeBias._prn.set(sys, num, flag);
     499    checkPrnRange(satCodeBias._prn);
    472500    satCodeBias._staID     = _staID.toStdString();
    473501    satCodeBias._time      = _lastTime;
    474502    satCodeBias._updateInt = _codeBias.UpdateInterval;
     503    satCodeBias._ssrIOD        = _codeBias.SSRIOD;
     504    satCodeBias._ssrProviderID = _codeBias.SSRProviderID;
    475505    for (unsigned jj = 0; jj < _codeBias.Sat[ii].NumberOfCodeBiases; jj++) {
    476506      const SsrCorr::CodeBias::BiasSat::CodeBiasEntry& biasEntry = _codeBias.Sat[ii].Biases[jj];
    … …  
    560590    t_satPhaseBias satPhaseBias;
    561591    satPhaseBias._prn.set(sys, num, flag);
     592    checkPrnRange(satPhaseBias._prn);
    562593    satPhaseBias._staID      = _staID.toStdString();
    563594    satPhaseBias._time       = _lastTime;
    564595    satPhaseBias._updateInt  = _phaseBias.UpdateInterval;
     596    satPhaseBias._ssrIOD        = _phaseBias.SSRIOD;
     597    satPhaseBias._ssrProviderID = _phaseBias.SSRProviderID;
    565598    satPhaseBias._ssrFormat  = (_type == RTCMssr) ? ssrRtcmOld :
    566599                               (_type == RTCMnewssr) ? ssrRtcmNew : ssrUnknown;
    … …  
    649682  // _antenna, like _metaData, carries no epoch field of its own and is never
    650683  // wiped by reset(), so each system's data persists across unrelated
    651   // messages. _antennaSentIOD tracks the last SatelliteAntennaIOD actually
    652   // emitted per system, so unrelated messages (and unchanged retransmissions
    653   // of the same antenna data) don't cause duplicate output - the same
    654   // freshness problem _phaseBiasSentEpoch solves for phase biases.
     684  // messages. Only a system for which an Antenna message was actually
     685  // received (_antennaReceived, latched in Decode()) is considered, so
     686  // persisted data is never re-stamped with a newer time.
     687  //
     688  // The SatelliteAntennaIOD is unique only per SSR Provider ID (DF414) and
     689  // only within 64 days, and a client that has not received the stream for
     690  // 64 days must discard cached antenna data (enforced downstream against
     691  // t_satAntenna::_time, see ssrSatAntennaTrusted() in pppSatObs.cpp). So an
     692  // unchanged (provider ID, IOD) retransmission is suppressed only if it was
     693  // last emitted less than ANT_REFRESH_SEC ago. Re-emitting it after that
     694  // keeps _time close to the last reception - not the first - so continuously
     695  // received data never ages out. It also means a reception after a long
     696  // gap is always emitted, even when the provider has legitimately reused
     697  // the IOD for different content, and each .ssr file contains the antenna
     698  // data at least once per refresh interval.
     699  //
     700  // This is tracked per satellite, not per system: providers may split a
     701  // system's satellites across several messages with rotating satellite
     702  // masks (DF394), all carrying the same IOD - a per-system check would
     703  // emit only whichever subset happened to arrive first.
    655704  {
     705    const double ANT_REFRESH_SEC = 3600.0;
    656706    struct SysInfo { unsigned sysIdx; char sysChar; unsigned numSat; unsigned offset; };
    657707    const SysInfo sysInfos[] = {
    … …  
    664714    for (const SysInfo& si : sysInfos) {
    665715      unsigned s = si.sysIdx;
    666       if (_antenna.SatelliteAntennaIOD[s] == _antennaSentIOD[s]) {
    667         continue; // nothing new for this system since the last emit
    668       }
     716      if (!_antennaReceived[s]) {
     717        continue; // no Antenna message received for this system since the last check
     718      }
     719      _antennaReceived[s] = false;
    669720      if (_antenna.SatelliteMask[s] == 0) {
    670         continue; // this system's antenna data was never decoded
     721        continue; // no satellites in this system's antenna data
    671722      }
    672723      for (unsigned k = 0; k < si.numSat; k++) {
    … …  
    674725          continue;
    675726        }
     727        unsigned i = si.offset + k;
     728        if (_antennaSentTime[i].valid() &&
     729            _antenna.SSRProviderID[s]       == _antennaSentProviderID[i] &&
     730            _antenna.SatelliteAntennaIOD[s] == _antennaSentIOD[i] &&
     731            _lastTime - _antennaSentTime[i] <  ANT_REFRESH_SEC) {
     732          continue; // unchanged retransmission, emitted recently
     733        }
     734        _antennaSentProviderID[i] = _antenna.SSRProviderID[s];
     735        _antennaSentIOD[i]        = _antenna.SatelliteAntennaIOD[s];
     736        _antennaSentTime[i]       = _lastTime;
    676737        int num  = k + 1; // PRN within this system, DF394 MSB-first convention
    677738        int flag = 0;
    … …  
    696757        satAntenna._nadirAngleRangeExtension  = _antenna.NadirAngleDependentCorrectionRangeExtension[s];
    697758
    698         const SsrCorr::Antenna::SatellitePart& satPart = _antenna.Sat[si.offset + k];
     759        const SsrCorr::Antenna::SatellitePart& satPart = _antenna.Sat[i];
    699760        for (unsigned f = 0; f < ANT_MAXFREQUENCIES; f++) {
    700761          if (!((satPart.GnssFrequencyMask >> (ANT_MAXFREQUENCIES - 1 - f)) & 1U)) {
    … …  
    718779        _satAntennas[_lastTime].append(satAntenna);
    719780      }
    720       _antennaSentIOD[s] = _antenna.SatelliteAntennaIOD[s];
    721781    }
    722782  }
    … …  
    11091169  }
    11101170}
     1171
     1172// Satellite numbers on the wire can exceed BNC's supported range (e.g. 6-bit
     1173// IDs up to 63, while t_prn supports G01-G32). They are passed on unchanged,
     1174// but t_prn::toInt() maps them to the unused index 0, so they are not usable
     1175// for per-satellite processing - report that once per satellite.
     1176////////////////////////////////////////////////////////////////////////////
     1177void RTCM3coDecoder::checkPrnRange(const t_prn& prn) {
     1178  if (prn.toInt() != 0) {
     1179    return;
     1180  }
     1181  QString prnStr = QString::fromStdString(prn.toString());
     1182  if (!_prnOutOfRangeLogged.contains(prnStr)) {
     1183    _prnOutOfRangeLogged.insert(prnStr);
     1184    emit newMessage(QString("%1: satellite %2 outside the range supported by BNC - not usable for processing")
     1185                    .arg(_staID).arg(prnStr).toLatin1(), true);
     1186  }
     1187}
     1188
     1189// All Satellite Antenna messages of one GNSS must use the same Satellite
     1190// Antenna IOD (a satellite set may be split over several messages). During
     1191// a legitimate IOD change old and new IOD are both seen for a while, until
     1192// every message has been sent with the new one - but the IOD never returns
     1193// to a replaced value within 64 days (uniqueness per SSR Provider ID), so a
     1194// switch back to the replaced IOD within that period reveals messages of the
     1195// same GNSS using different IODs. Reported once per GNSS and IOD pair; the
     1196// data itself is not changed (PPP checks each satellite's IOD individually).
     1197////////////////////////////////////////////////////////////////////////////
     1198void RTCM3coDecoder::checkAntennaIOD(unsigned s) {
     1199  const double MAX_AGE_SEC = 64.0 * 86400.0;
     1200  unsigned int iod      = _antenna.SatelliteAntennaIOD[s];
     1201  unsigned int provider = _antenna.SSRProviderID[s];
     1202
     1203  int    currentWeek = 0;
     1204  double currentSec  = 0.0;
     1205  currentGPSWeeks(currentWeek, currentSec);
     1206  bncTime currentTime(currentWeek, currentSec);
     1207
     1208  if (!_antennaIODKnown[s] || provider != _antennaIODProvider[s]) {
     1209    _antennaIODKnown[s]    = true;
     1210    _antennaIODProvider[s] = provider;
     1211    _antennaIODCur[s]      = iod;
     1212    _antennaIODPrevTime[s].reset(); // no replaced IOD yet
     1213    return;
     1214  }
     1215  if (iod == _antennaIODCur[s]) {
     1216    return;
     1217  }
     1218  if (_antennaIODPrevTime[s].valid() && iod == _antennaIODPrev[s] &&
     1219      currentTime - _antennaIODPrevTime[s] < MAX_AGE_SEC) {
     1220    const char sysChars[CLOCKORBIT_SATNUM] = {'G', 'R', 'E', 'J', 'S', 'C'};
     1221    char sys = (s < CLOCKORBIT_SATNUM) ? sysChars[s] : '?';
     1222    QString key = QString("%1_%2_%3").arg(sys).arg(_antennaIODCur[s]).arg(iod);
     1223    if (!_antennaIODLogged.contains(key)) {
     1224      _antennaIODLogged.insert(key);
     1225      emit newMessage(QString("%1: GNSS %2 Satellite Antenna IOD switches back %3 -> %4"
     1226                              " - messages of one GNSS must use the same IOD")
     1227                      .arg(_staID).arg(sys).arg(_antennaIODCur[s]).arg(iod).toLatin1(), true);
     1228    }
     1229  }
     1230  _antennaIODPrev[s]     = _antennaIODCur[s];
     1231  _antennaIODPrevTime[s] = currentTime;
     1232  _antennaIODCur[s]      = iod;
     1233}
Note: See TracChangeset for help on using the changeset viewer.