Changeset 11067 in ntrip
- Timestamp:
- Oct 5, 2026, 12:33:42 PM (23 hours ago)
- Location:
- trunk/BNC/src
- Files:
-
- 4 edited
-
PPP/pppClient.cpp (modified) (1 diff)
-
PPP/pppSatObs.cpp (modified) (1 diff)
-
combination/bnccomb.cpp (modified) (15 diffs)
-
combination/bnccomb.h (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/BNC/src/PPP/pppClient.cpp
r11051 r11067 61 61 if (!_opt->_antexFileName.empty()) { 62 62 _antex = new bncAntex(_opt->_antexFileName.c_str()); 63 *_log << _antex->info().toStdString() << endl; 63 64 } 64 65 if (!_opt->_blqFileName.empty()) { -
trunk/BNC/src/PPP/pppSatObs.cpp
r11047 r11067 672 672 } 673 673 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 } 675 680 if (OPT->_isAPC && found) { 676 681 // the PCOs as given in the satellite antenna correction for all frequencies 677 682 // have to be reduced by the PCO of the respective reference frequency 678 683 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); 680 685 } 681 686 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); 683 688 } 684 689 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); 686 691 } 687 692 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); 689 694 } 690 695 } -
trunk/BNC/src/combination/bnccomb.cpp
r11061 r11067 19 19 #include <sstream> 20 20 #include <map> 21 #include <algorithm> 21 22 22 23 #include "bnccomb.h" … … 290 291 _antex = 0; 291 292 } 293 else { 294 emit newMessage("bncComb: " + _antex->info().toLatin1(), true); 295 } 292 296 } 293 297 … … 853 857 // Check satellite code biases 854 858 // ---------------------------- 859 QMap<t_frequency::type, double> codeBiasesRefSig; 855 860 if (_satCodeBiases.contains(acName)) { 856 861 QMap<t_prn, t_satCodeBias>& storage = _satCodeBiases[acName]; 857 862 if (storage.contains(clkCorr._prn)) { 858 863 _newCorr->_satCodeBias = storage[clkCorr._prn]; 859 QMap<t_frequency::type, double> codeBiasesRefSig;860 864 for (unsigned ii = 1; ii < cmbRefSig::cIF; ii++) { 861 865 t_frequency::type frqType = cmbRefSig::toFreq(sys, static_cast<cmbRefSig::type>(ii)); … … 870 874 } 871 875 } 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]; 890 887 } 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) {902 888 _acBiasIF[acName][prnStr.mid(0,3)] = _newCorr->_satCodeBiasIF; 903 889 } … … 906 892 } 907 893 _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); 908 921 } 909 922 } … … 1087 1100 if (_method == filter) { 1088 1101 checkBiasConsistency(epoTime, sys, out); 1102 checkPersistentOutliers(epoTime, sys, out); 1089 1103 } 1090 1104 printResults(epoTime, out, masterCorr); … … 1121 1135 if (checkOrbits(epoTime, sys, out, masterCorr) != success) { 1122 1136 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; 1123 1144 } 1124 1145 … … 1161 1182 1162 1183 out << " Outlier" << "\n"; 1184 _outlierStat[sys][corrs(sys)[maxResIndex-1]->_acName + ' ' + 1185 corrs(sys)[maxResIndex-1]->_prn.mid(0,3)].residuals.push_back(maxRes); 1163 1186 _QQ[sys] = QQ_sav; 1164 1187 delete corrs(sys)[maxResIndex-1]; … … 1254 1277 sxy += (xx[ii] - mx) * (yy[ii] - my); 1255 1278 } 1279 QString key = acName + sys; 1256 1280 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 } 1263 1292 continue; 1264 1293 } … … 1275 1304 .arg(acName).arg(sys).arg(refSig).arg(nn) 1276 1305 .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 } 1277 1312 out << epoTime.datestr().c_str() << " " << epoTime.timestr().c_str() 1278 1313 << " " << msg << "\n"; 1279 QString key = acName + sys;1280 1314 bncTime& warned = _biasCheckWarned[key]; 1281 1315 if (!warned.valid() || epoTime - warned >= 3600.0) { 1282 1316 warned = epoTime; 1283 1317 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 //////////////////////////////////////////////////////////////////////////// 1330 void 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 //////////////////////////////////////////////////////////////////////////// 1367 void 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; 1284 1375 } 1285 1376 } … … 1411 1502 if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), vv, dx, 1412 1503 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; 1415 1517 } 1416 1518 QString glonassYawLog = _antex->takeGlonassYawLog(); … … 1537 1639 int maxSat = _cmbSysPrn[sys]; 1538 1640 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; 1540 1671 1541 1672 AA.ReSize(nObs+nCon, nPar); AA = 0.0; … … 1589 1720 // } 1590 1721 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 1591 1724 // GNSS 1592 1725 for (unsigned iGnss = 1; iGnss <= _cmbSysPrn[sys]; iGnss++) { … … 1595 1728 ++iCond; 1596 1729 PP(nObs+iCond) = Ph; 1730 QList<int> offAll, offConsistent; 1597 1731 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) { 1598 1732 cmbParam* pp = _params[sys][iPar-1]; … … 1601 1735 pp->type == cmbParam::offACSat && 1602 1736 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) { 1603 1766 AA(nObs+iCond, iPar) = 1.0; 1604 1767 } -
trunk/BNC/src/combination/bnccomb.h
r11061 r11067 117 117 _dClkResult = 0.0; 118 118 _satCodeBiasIF = 0.0; 119 _refBiasesOK = false; 119 120 _lambdaIF = 0.0; 120 121 _satYawAngle = 0.0; … … 136 137 QString _acName; 137 138 double _satCodeBiasIF; 139 bool _refBiasesOK; // code biases of both reference signals available 138 140 double _lambdaIF; 139 141 double _satYawAngle; … … 274 276 QMap<char, bncTime> _biasCheckLast; // last bias/clock consistency check per system 275 277 QMap<QString, bncTime> _biasCheckWarned; // last inconsistency warning per AC and system 278 QSet<QString> _biasInconsistent; // AC+system excluded from the clock datum 276 279 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); 277 289 bool allACsBeyond(const bncTime& epoTime) const; 278 290 bncTime _resTime;
Note:
See TracChangeset
for help on using the changeset viewer.
