Changeset 11137 in ntrip for trunk/BNC/src/ssrQc.cpp


Ignore:
Timestamp:
Oct 28, 2026, 9:48:01 PM (2 days ago)
Author:
stuerze
Message:

eph handling improved

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/BNC/src/ssrQc.cpp

    r11129 r11137  
    5656  const double MAX_CLK_JUMP   = 0.5;  // [m] clock change beyond its rate between consecutive epochs
    5757  const double EPH_WARMUP     = 120.0; // [s] no ephemeris checks at the start
     58  const double EPH_EARLY_MAX  = 300.0; // [s] IOD received this much later still counted as early
     59  const double EPH_IOD_REUSE  = 21600.0; // [s] an outdated data set this far from the epoch: IOD reused
     60  const unsigned EPH_KEPT     = 30;    // data sets kept per satellite
    5861  const double CMP_SAMPL      = 30.0;  // [s] sampling of the stream comparison
    5962  const double CMP_DELAY      = 60.0;  // [s] epochs compared when data this much newer arrived
    … …  
    119122////////////////////////////////////////////////////////////////////////////
    120123t_ssrQc::t_ssrQc(const QString& dirName, double interval, const QStringList& streams,
    121                  const QString& reference) : _ephUser(true) {
     124                 const QString& reference) : _ephUser(true, EPH_KEPT) {
    122125  _dirName   = dirName;
    123126  _reference = reference.isEmpty() ? QString("CONSENSUS") : reference;
    … …  
    359362// Broadcast ephemeris the correction refers to
    360363////////////////////////////////////////////////////////////////////////////
    361 void t_ssrQc::checkEph(t_satStat& stat, const QString& prnInt, unsigned iod, const bncTime& time,
    362                        const string& staID) {
     364void t_ssrQc::checkEph(t_satStat& stat, const QString& satKey, const QString& prnInt,
     365                       unsigned iod, const bncTime& time, const string& staID) {
    363366  // not before all broadcast ephemerides may have been received
    364367  if (time - _staStart[QString::fromStdString(staID)] < EPH_WARMUP) {
    365368    return;
    366369  }
    367   t_eph* ephLast = _ephUser.ephLast(prnInt);
    368   t_eph* ephPrev = _ephUser.ephPrev(prnInt);
    369   t_eph* eph = 0;
    370   if      (ephLast && ephLast->IOD() == iod) eph = ephLast;
    371   else if (ephPrev && ephPrev->IOD() == iod) eph = ephPrev;
    372   if (!ephLast) {
     370  if (!_ephUser.ephLast(prnInt)) {
    373371    stat.noEph += 1;
    374   }
    375   else if (!eph) {
    376     stat.iodUnknown += 1;
     372    return;
     373  }
     374  // any data set kept, not only the last two: Galileo has a new IODnav every
     375  // 10 minutes, and ACs keep one for up to an hour
     376  t_eph* eph = _ephUser.ephWithIOD(prnInt, iod);
     377  // an IOD used again later (GLONASS tb every day, GPS IODE) is another data set
     378  if (eph && outDatedBcep(eph, time) && fabs(time - eph->TOC()) > EPH_IOD_REUSE) {
     379    eph = 0;
     380  }
     381  if (!eph) {
     382    // received later from the ephemeris stream(s)? decided in resolvePendingIod
     383    t_pendingIod& pending = _pendingIod[satKey][iod];
     384    if (pending.num == 0) {
     385      pending.prnInt = prnInt;
     386      pending.first  = currentTime();
     387    }
     388    pending.num += 1;
    377389  }
    378390  else if (eph->checkState() == t_eph::unhealthy || eph->checkState() == t_eph::bad) {
    379391    stat.ephUnhealthy += 1;
     392  }
     393  else if (outDatedBcep(eph, time)) {
     394    stat.ephOutdated += 1;
     395  }
     396  if (_pendingIod.contains(satKey)) {
     397    resolvePendingIod(satKey, false);
     398  }
     399}
     400
     401// Corrections referring to an IOD not received before: counted as early if
     402// the ephemeris stream(s) delivered the data set later, as unknown if not
     403// within EPH_EARLY_MAX (or at the end of the report interval, final)
     404////////////////////////////////////////////////////////////////////////////
     405void t_ssrQc::resolvePendingIod(const QString& satKey, bool final) {
     406  t_satStat& stat = _satStat[satKey];
     407  QMap<unsigned, t_pendingIod>& pend = _pendingIod[satKey];
     408  QMutableMapIterator<unsigned, t_pendingIod> it(pend);
     409  while (it.hasNext()) {
     410    it.next();
     411    const t_pendingIod& pending = it.value();
     412    t_eph* eph = _ephUser.ephWithIOD(pending.prnInt, it.key());
     413    if (eph && eph->receptDateTime().isValid()) {
     414      bncTime recept(eph->receptDateTime().toString(Qt::ISODate).toStdString());
     415      if (recept >= pending.first) {
     416        stat.iodEarly += pending.num;
     417        stat.maxEarly  = max(stat.maxEarly, recept - pending.first);
     418        it.remove();
     419        continue;
     420      }
     421    }
     422    if (final || currentTime() - pending.first > EPH_EARLY_MAX) {
     423      stat.iodUnknown += pending.num;
     424      it.remove();
     425    }
     426  }
     427  if (pend.isEmpty()) {
     428    _pendingIod.remove(satKey);
    380429  }
    381430}
    … …  
    395444    t_satStat& stat = _satStat[QString::fromStdString(corr._staID) + ' ' + prn];
    396445    stat.numOrb += 1;
    397     checkEph(stat, QString::fromStdString(corr._prn.toInternalString()), corr._iod, corr._time, corr._staID);
     446    checkEph(stat, QString::fromStdString(corr._staID) + ' ' + prn,
     447             QString::fromStdString(corr._prn.toInternalString()), corr._iod, corr._time, corr._staID);
    398448    double radial = fabs(corr._xr[0]);
    399449    double alongCross = max(fabs(corr._xr[1]), fabs(corr._xr[2]));
    … …  
    417467    t_satStat& stat = _satStat[QString::fromStdString(corr._staID) + ' ' + prn];
    418468    stat.numClk += 1;
    419     checkEph(stat, QString::fromStdString(corr._prn.toInternalString()), corr._iod, corr._time, corr._staID);
     469    checkEph(stat, QString::fromStdString(corr._staID) + ' ' + prn,
     470             QString::fromStdString(corr._prn.toInternalString()), corr._iod, corr._time, corr._staID);
    420471
    421472    // jump: change between consecutive epochs (same IOD) beyond the clock rate
    … …  
    837888      }
    838889      const t_orbCorr& orb = (--itO).value();
    839       t_eph* eph = 0;
    840       t_eph* ephLast = _ephUser.ephLast(prnInt);
    841       t_eph* ephPrev = _ephUser.ephPrev(prnInt);
    842       if      (ephLast && ephLast->IOD() == orb._iod) eph = ephLast;
    843       else if (ephPrev && ephPrev->IOD() == orb._iod) eph = ephPrev;
     890      t_eph* eph = _ephUser.ephWithIOD(prnInt, orb._iod);
     891      if (eph && outDatedBcep(eph, tt) && fabs(tt - eph->TOC()) > EPH_IOD_REUSE) {
     892        eph = 0;
     893      }
    844894      if (!eph || clk._iod != orb._iod) {
    845895        continue;
    … …  
    13631413  bncTime endTime = final ? _lastTime : _intervalStart + _interval;
    13641414
     1415  // IODs not received: early or unknown (pending ones younger than
     1416  // EPH_EARLY_MAX are decided in the next interval)
     1417  for (const QString& satKey : _pendingIod.keys()) {
     1418    resolvePendingIod(satKey, final);
     1419  }
     1420
    13651421  // Healthy and current broadcast ephemerides per system
    13661422  QMap<char, QSet<QString> > ephSats;
    … …  
    14751531        continue;
    14761532      }
     1533      QMap<QString, unsigned> iodEarly, outdated;
     1534      double maxEarly = 0.0;
    14771535      QMap<QString, unsigned> missOrb, missClk, missCB, missPB, iodUnknown, unhealthy, noEph,
    14781536                              jumps, missRef, noYaw, yawPartly;
    … …  
    15071565        const t_satStat& stat = itSat.value();
    15081566        if (stat.iodUnknown)   iodUnknown[prn] = stat.iodUnknown;
     1567        if (stat.iodEarly) {
     1568          iodEarly[prn] = stat.iodEarly;
     1569          maxEarly = max(maxEarly, stat.maxEarly);
     1570        }
     1571        if (stat.ephOutdated)  outdated[prn]   = stat.ephOutdated;
    15091572        if (stat.ephUnhealthy) unhealthy[prn]  = stat.ephUnhealthy;
    15101573        if (stat.noEph)        noEph[prn]      = stat.noEph;
    … …  
    15681631      addSats("phase biases missing (healthy broadcast ephemeris available)", missPB);
    15691632      addSats("corrections referring to an IOD of no received broadcast ephemeris", iodUnknown);
     1633      if (!iodEarly.isEmpty()) {
     1634        addSats(QString("corrections referring to an IOD received from the ephemeris stream(s) "
     1635                        "only later (up to %1 s)").arg(maxEarly, 0, 'f', 0), iodEarly);
     1636      }
     1637      addSats("corrections referring to a broadcast ephemeris outside its validity", outdated);
    15701638      addSats("corrections for unhealthy broadcast ephemeris", unhealthy);
    15711639      addSats("corrections for satellites without broadcast ephemeris", noEph);
Note: See TracChangeset for help on using the changeset viewer.