Changeset 11067 in ntrip


Ignore:
Timestamp:
Oct 5, 2026, 12:33:42 PM (23 hours ago)
Author:
stuerze
Message:

some more fixes

Location:
trunk/BNC/src
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/BNC/src/PPP/pppClient.cpp

    r11051 r11067  
    6161  if (!_opt->_antexFileName.empty()) {
    6262    _antex = new bncAntex(_opt->_antexFileName.c_str());
     63    *_log << _antex->info().toStdString() << endl;
    6364  }
    6465  if (!_opt->_blqFileName.empty()) {
  • trunk/BNC/src/PPP/pppSatObs.cpp

    r11047 r11067  
    672672      }
    673673      else {
    674         _model._antPCO[ii] += PPP_CLIENT->antex()->satCorr(prn, frqType, _model._elTx, _model._azTx, found);
     674        _model._antPCO[ii] += PPP_CLIENT->antex()->satCorr(prn, frqType, _model._elTx, _model._azTx, found, _time);
     675        if (!found && !PPP_CLIENT->antex()->hasSatEntry(prn, _time) &&
     676            PPP_CLIENT->antex()->warnNoSatEntry(prn, _time)) {
     677          LOG << "ANTEX: no satellite antenna entry valid at " << _time.datestr() << ' ' << _time.timestr()
     678              << " for " << _prn.toString() << " - ANTEX file outdated?" << endl;
     679        }
    675680        if (OPT->_isAPC && found) {
    676681          // the PCOs as given in the satellite antenna correction for all frequencies
    677682          // have to be reduced by the PCO of the respective reference frequency
    678683          if      (_prn.system() == 'G') {
    679             _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::G1, _model._elTx, _model._azTx, found);
     684            _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::G1, _model._elTx, _model._azTx, found, _time);
    680685          }
    681686          else if (_prn.system() == 'R') {
    682             _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::R1, _model._elTx, _model._azTx, found);
     687            _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::R1, _model._elTx, _model._azTx, found, _time);
    683688          }
    684689          else if (_prn.system() == 'E') {
    685             _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::E1, _model._elTx, _model._azTx, found);
     690            _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::E1, _model._elTx, _model._azTx, found, _time);
    686691          }
    687692          else if (_prn.system() == 'C') {
    688             _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::C2, _model._elTx, _model._azTx, found);
     693            _model._antPCO[ii] -= PPP_CLIENT->antex()->satCorr(prn, t_frequency::C2, _model._elTx, _model._azTx, found, _time);
    689694          }
    690695        }
  • trunk/BNC/src/combination/bnccomb.cpp

    r11061 r11067  
    1919#include <sstream>
    2020#include <map>
     21#include <algorithm>
    2122
    2223#include "bnccomb.h"
    … …  
    290291      _antex = 0;
    291292    }
     293    else {
     294      emit newMessage("bncComb: " + _antex->info().toLatin1(), true);
     295    }
    292296  }
    293297
    … …  
    853857    // Check satellite code biases
    854858    // ----------------------------
     859    QMap<t_frequency::type, double> codeBiasesRefSig;
    855860    if (_satCodeBiases.contains(acName)) {
    856861      QMap<t_prn, t_satCodeBias>& storage = _satCodeBiases[acName];
    857862      if (storage.contains(clkCorr._prn)) {
    858863        _newCorr->_satCodeBias = storage[clkCorr._prn];
    859         QMap<t_frequency::type, double> codeBiasesRefSig;
    860864        for (unsigned ii = 1; ii < cmbRefSig::cIF; ii++) {
    861865          t_frequency::type frqType = cmbRefSig::toFreq(sys, static_cast<cmbRefSig::type>(ii));
    … …  
    870874          }
    871875        }
    872         // Code biases present, but not for both reference signals (e.g.
    873         // Galileo HAS): the AC clocks cannot be referred to the reference
    874         // signals and are used as they are - warn (at most once per hour
    875         // per AC and system)
    876         if (codeBiasesRefSig.size() < 2) {
    877           QString key = acName + sys;
    878           bncTime& warned = _refBiasWarned[key];
    879           if (!warned.valid() || _newCorr->_time - warned >= 3600.0) {
    880             warned = _newCorr->_time;
    881             QString refSig;
    882             for (unsigned ii = 1; ii < cmbRefSig::cIF; ii++) {
    883               t_frequency::type frqType = cmbRefSig::toFreq(sys, static_cast<cmbRefSig::type>(ii));
    884               refSig += QString(" C%1%2").arg(t_frequency::toString(frqType)[1])
    885                                          .arg(cmbRefSig::toAttrib(sys, static_cast<cmbRefSig::type>(ii)));
    886             }
    887             emit newMessage("bncComb: " + acName.toLatin1() + " provides no code biases for the " +
    888                             QByteArray(1, sys) + " reference signals" + refSig.toLatin1() +
    889                             " - its clocks are combined without referring them to these signals", false);
     876        // only with the biases of both reference signals (one of them alone
     877        // does not refer the clocks to the reference signals)
     878        if (codeBiasesRefSig.size() == 2) {
     879          map<t_frequency::type, double> codeCoeff;
     880          double channel = double(_newCorr->_eph->slotNum());
     881          cmbRefSig::coeff(sys, cmbRefSig::cIF, channel, codeCoeff);
     882          map<t_frequency::type, double>::const_iterator it;
     883          for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
     884            t_frequency::type frqType = it->first;
     885            double codeCoeff          = it->second;
     886            _newCorr->_satCodeBiasIF += codeCoeff * codeBiasesRefSig[frqType];
    890887          }
    891         }
    892         map<t_frequency::type, double> codeCoeff;
    893         double channel = double(_newCorr->_eph->slotNum());
    894         cmbRefSig::coeff(sys, cmbRefSig::cIF, channel, codeCoeff);
    895         map<t_frequency::type, double>::const_iterator it;
    896         for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
    897           t_frequency::type frqType = it->first;
    898           double codeCoeff          = it->second;
    899           _newCorr->_satCodeBiasIF += codeCoeff * codeBiasesRefSig[frqType];
    900         }
    901         if (codeBiasesRefSig.size() == 2) {
    902888          _acBiasIF[acName][prnStr.mid(0,3)] = _newCorr->_satCodeBiasIF;
    903889        }
    … …  
    906892        }
    907893        _newCorr->_satCodeBias._bias.clear();
     894      }
     895    }
     896    // Without the code biases of both reference signals (none at all, or
     897    // only one of them, e.g. Galileo HAS for GPS), the AC clock cannot be
     898    // referred to the reference signals: it is combined as it is, but
     899    // excluded from the clock datum (see createAmat) - warn at most once
     900    // per hour per AC and system
     901    _newCorr->_refBiasesOK = (codeBiasesRefSig.size() == 2);
     902    if (!_newCorr->_refBiasesOK) {
     903      _acBiasIF[acName].remove(prnStr.mid(0,3));
     904      QString key = acName + sys;
     905      bncTime& warned = _refBiasWarned[key];
     906      if (!warned.valid() || _newCorr->_time - warned >= 3600.0) {
     907        warned = _newCorr->_time;
     908        QString missing;
     909        for (unsigned ii = 1; ii < cmbRefSig::cIF; ii++) {
     910          t_frequency::type frqType = cmbRefSig::toFreq(sys, static_cast<cmbRefSig::type>(ii));
     911          if (!codeBiasesRefSig.contains(frqType)) {
     912            missing += QString(" C%1%2").arg(t_frequency::toString(frqType)[1])
     913                                        .arg(cmbRefSig::toAttrib(sys, static_cast<cmbRefSig::type>(ii)));
     914          }
     915        }
     916        emit newMessage("bncComb: " + acName.toLatin1() + " provides no code biases for the " +
     917                        QByteArray(1, sys) + " reference signal(s)" + missing.toLatin1() +
     918                        " (e.g. " + prnStr.mid(0,3).toLatin1() + ") - these clocks are combined"
     919                        " without referring them to the reference signals and, as long as other ACs"
     920                        " provide referred clocks, excluded from the clock datum", false);
    908921      }
    909922    }
    … …  
    10871100    if (_method == filter) {
    10881101      checkBiasConsistency(epoTime, sys, out);
     1102      checkPersistentOutliers(epoTime, sys, out);
    10891103    }
    10901104    printResults(epoTime, out, masterCorr);
    … …  
    11211135  if (checkOrbits(epoTime, sys, out, masterCorr) != success) {
    11221136    return failure;
     1137  }
     1138
     1139  // Statistics of persistent outliers
     1140  // ---------------------------------
     1141  for (int ii = 0; ii < corrs(sys).size(); ii++) {
     1142    const cmbCorr* corr = corrs(sys)[ii];
     1143    _outlierStat[sys][corr->_acName + ' ' + corr->_prn.mid(0,3)].numEpo += 1;
    11231144  }
    11241145
    … …  
    11611182
    11621183      out << "  Outlier" << "\n";
     1184      _outlierStat[sys][corrs(sys)[maxResIndex-1]->_acName + ' ' +
     1185                        corrs(sys)[maxResIndex-1]->_prn.mid(0,3)].residuals.push_back(maxRes);
    11631186      _QQ[sys] = QQ_sav;
    11641187      delete corrs(sys)[maxResIndex-1];
    … …  
    12541277      sxy += (xx[ii] - mx) * (yy[ii] - my);
    12551278    }
     1279    QString key = acName + sys;
    12561280    double spread = sqrt(sxx / nn);
    1257     if (spread < MIN_SPREAD || syy <= 0.0) {
    1258       continue;
    1259     }
    1260     double slope = sxy / sxx;
    1261     double corrCoeff = sxy / sqrt(sxx * syy);
    1262     if (slope < MIN_SLOPE || corrCoeff < MIN_CORR) {
     1281    double slope     = (sxx > 0.0) ? sxy / sxx : 0.0;
     1282    double corrCoeff = (sxx > 0.0 && syy > 0.0) ? sxy / sqrt(sxx * syy) : 0.0;
     1283    if (spread < MIN_SPREAD || slope < MIN_SLOPE || corrCoeff < MIN_CORR) {
     1284      if (_biasInconsistent.remove(key)) {
     1285        QString msg = QString("%1 %2 code biases consistent with its clocks again, "
     1286                              "AC used for the clock datum again").arg(acName).arg(sys);
     1287        out << epoTime.datestr().c_str() << " " << epoTime.timestr().c_str()
     1288            << " " << msg << "\n";
     1289        emit newMessage("bncComb: " + msg.toLatin1(), true);
     1290        resetSatOffsets(sys);
     1291      }
    12631292      continue;
    12641293    }
    … …  
    12751304                  .arg(acName).arg(sys).arg(refSig).arg(nn)
    12761305                  .arg(spread, 0, 'f', 3).arg(slope, 0, 'f', 2).arg(corrCoeff, 0, 'f', 2);
     1306    if (!_biasInconsistent.contains(key)) {
     1307      _biasInconsistent.insert(key);
     1308      msg += ", AC excluded from the clock datum";
     1309      _biasCheckWarned.remove(key);
     1310      resetSatOffsets(sys);
     1311    }
    12771312    out << epoTime.datestr().c_str() << " " << epoTime.timestr().c_str()
    12781313        << " " << msg << "\n";
    1279     QString key = acName + sys;
    12801314    bncTime& warned = _biasCheckWarned[key];
    12811315    if (!warned.valid() || epoTime - warned >= 3600.0) {
    12821316      warned = epoTime;
    12831317      emit newMessage("bncComb: " + msg.toLatin1(), true);
     1318    }
     1319  }
     1320}
     1321
     1322// Report AC satellites rejected as outliers in most epochs
     1323////////////////////////////////////////////////////////////////////////////
     1324// A persistent clock difference of one AC for a satellite cannot be absorbed
     1325// by its satellite offset, as the offsets of all ACs of the satellite sum up
     1326// to zero: the correction is rejected again in every epoch. Once per hour
     1327// and system, AC satellites rejected in at least half of their epochs are
     1328// reported.
     1329////////////////////////////////////////////////////////////////////////////
     1330void bncComb::checkPersistentOutliers(bncTime epoTime, char sys, QTextStream& out) {
     1331
     1332  const double   INTERVAL = 3600.0;
     1333  const unsigned MIN_EPO  = 10;
     1334
     1335  if (!_outlierStatStart[sys].valid()) {
     1336    _outlierStatStart[sys] = epoTime;
     1337    return;
     1338  }
     1339  if (epoTime - _outlierStatStart[sys] < INTERVAL) {
     1340    return;
     1341  }
     1342  QMapIterator<QString, t_outlierStat> it(_outlierStat[sys]);
     1343  while (it.hasNext()) {
     1344    it.next();
     1345    const t_outlierStat& stat = it.value();
     1346    unsigned numOut = stat.residuals.size();
     1347    if (stat.numEpo < MIN_EPO || 2 * numOut < stat.numEpo) {
     1348      continue;
     1349    }
     1350    QVector<double> res = stat.residuals;
     1351    std::sort(res.begin(), res.end());
     1352    double median = (numOut % 2) ? res[numOut/2] : 0.5 * (res[numOut/2-1] + res[numOut/2]);
     1353    QString msg = QString("%1 rejected as outlier in %2% of %3 epochs in the last hour "
     1354                          "(median residual %4 m)")
     1355                  .arg(it.key()).arg(100.0 * numOut / stat.numEpo, 0, 'f', 0)
     1356                  .arg(stat.numEpo).arg(median, 0, 'f', 2);
     1357    out << epoTime.datestr().c_str() << " " << epoTime.timestr().c_str() << " " << msg << "\n";
     1358    emit newMessage("bncComb: " + msg.toLatin1(), true);
     1359  }
     1360  _outlierStat[sys].clear();
     1361  _outlierStatStart[sys] = epoTime;
     1362}
     1363
     1364// Re-initialize the satellite offsets of all ACs of a system (after a change
     1365// of the clock datum)
     1366////////////////////////////////////////////////////////////////////////////
     1367void bncComb::resetSatOffsets(char sys) {
     1368  for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
     1369    cmbParam* pp = _params[sys][iPar-1];
     1370    if (pp->type == cmbParam::offACSat) {
     1371      pp->xx = 0.0;
     1372      _QQ[sys].Row(iPar)    = 0.0;
     1373      _QQ[sys].Column(iPar) = 0.0;
     1374      _QQ[sys](iPar,iPar)   = pp->sig0 * pp->sig0;
    12841375    }
    12851376  }
    … …  
    14111502      if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), vv, dx,
    14121503                                   attMode, extYaw) != success) {
    1413         dx = 0;
    1414         emit newMessage("bncComb: antenna not found " + corr->_prn.mid(0,3).toLatin1(), false);
     1504        // without the satellite antenna offset, the orbit cannot be
     1505        // converted between antenna phase center and center of mass
     1506        if (_antex->warnNoSatEntry(corr->_prn, epoTime)) {
     1507          emit newMessage("bncComb: no satellite antenna entry valid at " +
     1508                          QByteArray(epoTime.datestr().c_str()) + " " + QByteArray(epoTime.timestr().c_str()) +
     1509                          " for " + corr->_prn.mid(0,3).toLatin1() +
     1510                          " in ANTEX file - ANTEX file outdated? Satellite not combined", true);
     1511        }
     1512        orbCorrections.pop_back();
     1513        clkCorrections.pop_back();
     1514        delete corr;
     1515        it.remove();
     1516        continue;
    14151517      }
    14161518      QString glonassYawLog = _antex->takeGlonassYawLog();
    … …  
    15371639  int maxSat = _cmbSysPrn[sys];
    15381640
    1539   const int nCon = (_method == filter) ? 1 + maxSat : 0;
     1641  // AC clocks not referred to the reference signals are excluded from the
     1642  // zero-sum conditions of the satellite offsets (the clock datum): those of
     1643  // ACs whose code biases are inconsistent with their clocks (see
     1644  // checkBiasConsistency) and those without the code biases of both
     1645  // reference signals. This applies as long as another AC provides referred
     1646  // corrections; the sum of the excluded offsets of an AC is constrained to
     1647  // zero instead.
     1648  QSet<QString> excludedOff;  // "AC PRN" (full PRN)
     1649  QStringList   excludedACs;  // ACs with excluded offsets
     1650  if (_method == filter) {
     1651    bool referredAC = false;
     1652    for (int ii = 0; ii < corrs(sys).size(); ii++) {
     1653      const cmbCorr* corr = corrs(sys)[ii];
     1654      if (_biasInconsistent.contains(corr->_acName + sys) || !corr->_refBiasesOK) {
     1655        excludedOff.insert(corr->_acName + ' ' + corr->_prn);
     1656        if (!excludedACs.contains(corr->_acName)) {
     1657          excludedACs << corr->_acName;
     1658        }
     1659      }
     1660      else {
     1661        referredAC = true;
     1662      }
     1663    }
     1664    if (!referredAC) {
     1665      excludedOff.clear();
     1666      excludedACs.clear();
     1667    }
     1668  }
     1669
     1670  const int nCon = (_method == filter) ? 1 + maxSat + excludedACs.size() : 0;
    15401671
    15411672  AA.ReSize(nObs+nCon, nPar);  AA = 0.0;
    … …  
    15891720   // }
    15901721    int iCond = 1;
     1722    QSet<int>     offExcluded; // offsets excluded from the zero-sum condition of their satellite
     1723    QSet<QString> acsInDatum;  // ACs with at least one offset in the zero-sum conditions
    15911724    // GNSS
    15921725    for (unsigned iGnss = 1; iGnss <= _cmbSysPrn[sys]; iGnss++) {
    … …  
    15951728      ++iCond;
    15961729      PP(nObs+iCond) = Ph;
     1730      QList<int> offAll, offConsistent;
    15971731      for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
    15981732        cmbParam* pp = _params[sys][iPar-1];
    … …  
    16011735             pp->type == cmbParam::offACSat                 &&
    16021736             pp->prn == prn) {
     1737          offAll << iPar;
     1738          if (!excludedOff.contains(pp->AC + ' ' + pp->prn)) {
     1739            offConsistent << iPar;
     1740          }
     1741        }
     1742      }
     1743      // satellite observed by inconsistent ACs only: all its offsets are used
     1744      const QList<int>& offUsed = offConsistent.isEmpty() ? offAll : offConsistent;
     1745      for (int iPar : offAll) {
     1746        if (offUsed.contains(iPar)) {
     1747          AA(nObs+iCond, iPar) = 1.0;
     1748          acsInDatum.insert(_params[sys][iPar-1]->AC);
     1749        }
     1750        else {
     1751          offExcluded << iPar;
     1752        }
     1753      }
     1754    }
     1755    // ACs with all their offsets excluded from the zero-sum conditions above:
     1756    // the sum of their offsets is constrained to zero (otherwise not
     1757    // separable from the AC's epoch offset)
     1758    for (const QString& acName : excludedACs) {
     1759      ++iCond;
     1760      if (acsInDatum.contains(acName)) {
     1761        continue;
     1762      }
     1763      PP(nObs+iCond) = Ph;
     1764      for (int iPar : offExcluded) {
     1765        if (_params[sys][iPar-1]->AC == acName) {
    16031766          AA(nObs+iCond, iPar) = 1.0;
    16041767        }
  • trunk/BNC/src/combination/bnccomb.h

    r11061 r11067  
    117117      _dClkResult                  = 0.0;
    118118      _satCodeBiasIF               = 0.0;
     119      _refBiasesOK                 = false;
    119120      _lambdaIF                    = 0.0;
    120121      _satYawAngle                 = 0.0;
    … …  
    136137    QString        _acName;
    137138    double         _satCodeBiasIF;
     139    bool           _refBiasesOK;   // code biases of both reference signals available
    138140    double         _lambdaIF;
    139141    double         _satYawAngle;
    … …  
    274276  QMap<char, bncTime>                        _biasCheckLast;  // last bias/clock consistency check per system
    275277  QMap<QString, bncTime>                     _biasCheckWarned; // last inconsistency warning per AC and system
     278  QSet<QString>                              _biasInconsistent; // AC+system excluded from the clock datum
    276279  void checkBiasConsistency(bncTime epoTime, char sys, QTextStream& out);
     280  void checkPersistentOutliers(bncTime epoTime, char sys, QTextStream& out);
     281  struct t_outlierStat {
     282    t_outlierStat() : numEpo(0) {}
     283    unsigned        numEpo;   // epochs with a correction of the AC for the satellite
     284    QVector<double> residuals; // residuals of the epochs it was rejected
     285  };
     286  QMap<char, QMap<QString, t_outlierStat> > _outlierStat;      // per system, key "AC PRN"
     287  QMap<char, bncTime>                       _outlierStatStart; // start of the statistics interval
     288  void resetSatOffsets(char sys);
    277289  bool allACsBeyond(const bncTime& epoTime) const;
    278290  bncTime                                    _resTime;
Note: See TracChangeset for help on using the changeset viewer.