Changeset 11081 in ntrip
- Timestamp:
- Oct 8, 2026, 3:42:54 PM (66 minutes ago)
- Location:
- trunk/BNC
- Files:
-
- 14 edited
-
src/bnchelp.html (modified) (2 diffs)
-
src/bncmain.cpp (modified) (1 diff)
-
src/bncwindow.cpp (modified) (1 diff)
-
src/ssrQc.cpp (modified) (12 diffs)
-
src/ssrQc.h (modified) (2 diffs)
-
tests/cases/ssr_qc/expected/SSRA00BKG1_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc/expected/SSRA00CHC1_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc/expected/SSRA00CNE1_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc/expected/SSRA00GMV0_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc/expected/SSRA00NRC0_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc/expected/SSRA01GFZ1_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc/expected/SSRA01SHA1_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc_select/expected/SSRA00CNE1_S_20262750000_01D_QC.txt.gz (modified) ( previous)
-
tests/cases/ssr_qc_select/expected/SSRA00GMV0_S_20262750000_01D_QC.txt.gz (modified) ( previous)
Legend:
- Unmodified
- Added
- Removed
-
trunk/BNC/src/bnchelp.html
r11079 r11081 5083 5083 ('--file'). Specify a 'QC Directory' to activate this function. 'QC Streams' selects the streams to be checked: 5084 5084 'ALL' (default) for all decoded Broadcast Correction streams, or a comma separated list of stream names, e.g. 5085 'SSRA00CNE1,SSRA00GMV0'. 'QC Reference' selects the reference of the stream comparison (see below). Select 5085 'SSRA00CNE1,SSRA00GMV0'. A stream name with a leading '!' excludes the stream from the QC and from the consensus, 5086 e.g. 'ALL,!SIRGAS200001' for a stream referring to another reference frame than the ITRF. 'QC Reference' selects the reference of the stream comparison (see below). Select 5086 5087 the 'QC Interval' for the reports (default: 1 hour). One daily file 5087 5088 '<stream>_S_<YYYYDDD>0000_01D_QC.txt' is written per stream; at the end of each interval and at the … … 5146 5147 </p> 5147 5148 <ul> 5148 <li>'CONSENSUS' (default): per satellite the median of all other streams (at least two). A single erroneous stream 5149 does not affect it, and no combination is needed. The combined stream 'INTERNAL' is not part of the consensus, 5150 as it is derived from the other streams.</li> 5149 <li>'CONSENSUS' (default): per satellite the median of the other analysis centers (at least two). All decoded 5150 streams contribute, also those not selected under 'QC Streams' (except the excluded ones). An AC with several 5151 streams (IGS-SSR and RTCM-SSR format, APC and CoM) has one vote, the median of its streams; the AC is derived 5152 from the stream name (e.g. 'CNE' from 'SSRA00CNE1'). A stream is compared without the streams of its own AC. 5153 Combination products ('INTERNAL' and the IGS combinations '...IGS.') do not vote, as they are derived from the 5154 ACs, but are compared with the consensus. A single erroneous AC does not affect the consensus, and no 5155 combination is needed.</li> 5151 5156 <li>the name of a stream, e.g. 'INTERNAL' for the result of the combination (see section 'Combination') or the 5152 5157 stream of a selected AC. Note that an AC contributing to the combination is compared partly with itself, and -
trunk/BNC/src/bncmain.cpp
r11075 r11081 211 211 " corrPort {Output port [integer number]}\n" 212 212 " ssrQcPath {Directory for SSR QC files, one per stream [character string]}\n" 213 " ssrQcStreams {Streams to check [character string: ALL or comma separated list of stream names]}\n" 213 " ssrQcStreams {Streams to check [character string: ALL or comma separated list of stream names; '!' excludes a stream, e.g. ALL,!SIRGAS200001]}\n" 214 214 " ssrQcRef {Reference of the stream comparison [character string: CONSENSUS or a stream name, e.g. INTERNAL]}\n" 215 215 " ssrQcIntr {SSR QC report interval [character string: 5 min|15 min|30 min|1 hour|1 day]}\n" -
trunk/BNC/src/bncwindow.cpp
r11075 r11081 1596 1596 _ssrQcPathLineEdit->setWhatsThis(tr("<p>Specify a directory for the quality control of decoded Broadcast Correction streams: epochs, update intervals, gaps, latency, completeness, IODs, code and phase bias signals, code biases of the reference signals, clock jumps and orbit correction size. One daily file is written per stream.</p><p>Default is an empty option field, meaning that no SSR QC is done. <i>[key: ssrQcPath]</i></p>")); 1597 1597 _ssrQcRefLineEdit->setWhatsThis(tr("<p>Specify the reference for the comparison of the orbits and clocks of the checked streams: 'CONSENSUS' for the median of all other streams, or the name of a stream, e.g. 'INTERNAL' for the combination.</p><p>Default is 'CONSENSUS'. <i>[key: ssrQcRef]</i></p>")); 1598 _ssrQcStreamsLineEdit->setWhatsThis(tr("<p>Specify the Broadcast Correction streams to be checked, as a comma separated list of stream names (e.g. SSRA00CNE1,SSRA00GMV0), or 'ALL' for all decoded streams.</p><p>Default is 'ALL'. <i>[key: ssrQcStreams]</i></p>")); 1598 _ssrQcStreamsLineEdit->setWhatsThis(tr("<p>Specify the Broadcast Correction streams to be checked, as a comma separated list of stream names (e.g. SSRA00CNE1,SSRA00GMV0), or 'ALL' for all decoded streams. A stream with a leading '!' is excluded from the QC and from the consensus, e.g. 'ALL,!SIRGAS200001' for streams in another reference frame.</p><p>Default is 'ALL'. <i>[key: ssrQcStreams]</i></p>")); 1599 1599 _ssrQcIntrComboBox->setWhatsThis(tr("<p>Select the interval of the SSR QC reports. <i>[key: ssrQcIntr]</i></p>")); 1600 1600 _corrPortLineEdit->setWhatsThis(tr("<p>BNC can produce Broadcast Ephemeris Corrections on your local host through an IP port.</p><p>Specify a port number here to activate this function. <i>[key: corrPort]</i></p>")); -
trunk/BNC/src/ssrQc.cpp
r11079 r11081 107 107 _reference = reference.isEmpty() ? QString("CONSENSUS") : reference; 108 108 _interval = interval > 0.0 ? interval : 3600.0; 109 // "ALL" or a list of streams; streams with a leading '!' are excluded 110 // (from the reports and from the consensus) 111 bool all = false; 109 112 for (const QString& sta : streams) { 110 if (sta.toUpper() == "ALL") { 111 _streams.clear(); 112 break; 113 } 114 _streams.insert(sta); 113 if (sta.startsWith('!')) { 114 _excluded.insert(sta.mid(1)); 115 } 116 else if (sta.toUpper() == "ALL") { 117 all = true; 118 } 119 else { 120 _streams.insert(sta); 121 } 122 } 123 if (all) { 124 _streams.clear(); 115 125 } 116 126 … … 136 146 //////////////////////////////////////////////////////////////////////////// 137 147 bool t_ssrQc::selected(const string& staID) const { 138 return _streams.isEmpty() || _streams.contains(QString::fromStdString(staID)); 148 QString sta = QString::fromStdString(staID); 149 return !_excluded.contains(sta) && (_streams.isEmpty() || _streams.contains(sta)); 150 } 151 152 // Analysis center of a stream: the 3 characters of the AC in mountpoints 153 // like SSRA00CNE1, otherwise the stream name itself 154 //////////////////////////////////////////////////////////////////////////// 155 QString t_ssrQc::acOf(const QString& staID) { 156 static const QRegularExpression re("^SSR[AC][0-9]{2}([A-Z0-9]{3})[0-9]$"); 157 QRegularExpressionMatch mm = re.match(staID); 158 return mm.hasMatch() ? mm.captured(1) : staID; 159 } 160 161 // Stream part of the consensus: not excluded, and not a combination product 162 // (INTERNAL, IGS combinations), as these are derived from the other streams 163 //////////////////////////////////////////////////////////////////////////// 164 bool t_ssrQc::votes(const QString& staID) const { 165 return !_excluded.contains(staID) && staID != "INTERNAL" && acOf(staID) != "IGS"; 139 166 } 140 167 … … 278 305 QMutexLocker locker(&_mutex); 279 306 for (const t_orbCorr& corr : orbCorrections) { 280 if ( selected(corr._staID) || corr._staID == _reference.toStdString()) {307 if (!_excluded.contains(QString::fromStdString(corr._staID)) || corr._staID == _reference.toStdString()) { 281 308 storeHist(&corr, 0); 282 309 } … … 300 327 QMutexLocker locker(&_mutex); 301 328 for (const t_clkCorr& corr : clkCorrections) { 302 if ( selected(corr._staID) || corr._staID == _reference.toStdString()) {329 if (!_excluded.contains(QString::fromStdString(corr._staID)) || corr._staID == _reference.toStdString()) { 303 330 storeHist(0, &corr); 304 331 } … … 336 363 for (const t_satCodeBias& bias : codeBiases) { 337 364 bool sel = selected(bias._staID); 338 if ( !sel&& bias._staID != _reference.toStdString()) continue;365 if (_excluded.contains(QString::fromStdString(bias._staID)) && bias._staID != _reference.toStdString()) continue; 339 366 char sys = bias._prn.system(); 340 367 QString prn = QString::fromStdString(bias._prn.toString()); … … 507 534 } 508 535 509 // Clock datum: offset of each stream w.r.t. the per-satellite median of 510 // all streams (or w.r.t. the reference stream) 536 // Consensus: one value per AC (median of its streams), so that an AC 537 // with several streams (formats, APC/CoM) has one vote; the streams of 538 // the AC compared are left out (leave-one-AC-out) 539 auto acClocks = [&](const QString& prn, const QMap<QString, double>& off, bool useOff) { 540 QMap<QString, QVector<double> > perAC; 541 for (const QString& oo : staList) { 542 if (!votes(oo) || !crd[oo].contains(prn) || !crd[oo][prn].clkOK) continue; 543 if (useOff && !off.contains(oo)) continue; 544 perAC[acOf(oo)] << crd[oo][prn].clk - (useOff ? off[oo] : 0.0); 545 } 546 QMap<QString, double> res; 547 for (const QString& ac : perAC.keys()) res[ac] = median(perAC[ac]); 548 return res; 549 }; 550 551 // Clock datum: offset of each stream w.r.t. the per-satellite consensus 552 // of all ACs (or w.r.t. the reference stream) 511 553 QMap<QString, double> offset; 554 QMap<QString, double> noOff; 555 QMap<QString, QMap<QString, double> > rawAC; // PRN, AC 556 if (_reference == "CONSENSUS") { 557 for (const QString& prn : sats) rawAC[prn] = acClocks(prn, noOff, false); 558 } 512 559 for (const QString& staID : staList) { 513 560 QVector<double> diffs; … … 515 562 if (!crd[staID].contains(prn) || !crd[staID][prn].clkOK) continue; 516 563 if (_reference == "CONSENSUS") { 517 QVector<double> all; 518 for (const QString& oo : staList) { 519 if (oo != "INTERNAL" && crd[oo].contains(prn) && crd[oo][prn].clkOK) all << crd[oo][prn].clk; 520 } 521 if (all.size() >= 3) diffs << crd[staID][prn].clk - median(all); 564 if (rawAC[prn].size() >= 3) diffs << crd[staID][prn].clk - median(rawAC[prn].values().toVector()); 522 565 } 523 566 else if (crd.value(_reference).contains(prn) && crd[_reference][prn].clkOK) { … … 528 571 offset[staID] = median(diffs); 529 572 } 573 } 574 QMap<QString, QMap<QString, double> > alignedAC; // PRN, AC 575 if (_reference == "CONSENSUS") { 576 for (const QString& prn : sats) alignedAC[prn] = acClocks(prn, offset, true); 530 577 } 531 578 … … 535 582 continue; 536 583 } 584 const QString ownAC = acOf(staID); 537 585 QMap<QString, double> dClk; 538 586 QMap<QString, ColumnVector> dRac; … … 540 588 if (!crd[staID].contains(prn)) continue; 541 589 const t_satCrd& sc = crd[staID][prn]; 542 Q StringList refs;590 QVector<double> clks, xs[3]; 543 591 if (_reference == "CONSENSUS") { 592 QMapIterator<QString, double> itA(alignedAC[prn]); 593 while (itA.hasNext()) { 594 itA.next(); 595 if (itA.key() != ownAC) clks << itA.value(); 596 } 597 // orbits: per AC the median of its streams with the same reference point 598 QMap<QString, QVector<double> > perAC[3]; 544 599 for (const QString& oo : staList) { 545 // the combination (INTERNAL) is derived from the other streams 546 if (oo != staID && oo != "INTERNAL" && crd[oo].contains(prn)) refs << oo; 547 } 548 if (refs.size() < 2) continue; 549 refs.clear(); 550 for (const QString& oo : staList) { 551 if (oo != staID && oo != "INTERNAL" && crd[oo].contains(prn)) refs << oo; 552 } 600 if (!votes(oo) || acOf(oo) == ownAC || !crd[oo].contains(prn)) continue; 601 const t_satCrd& rc = crd[oo][prn]; 602 if (rc.refPoint != sc.refPoint || sc.refPoint == '?') continue; 603 for (int ii = 0; ii < 3; ii++) perAC[ii][acOf(oo)] << rc.xx[ii]; 604 } 605 for (const QString& ac : perAC[0].keys()) { 606 for (int ii = 0; ii < 3; ii++) xs[ii] << median(perAC[ii][ac]); 607 } 608 if (clks.size() < 2) clks.clear(); 609 if (xs[0].size() < 2) for (int ii = 0; ii < 3; ii++) xs[ii].clear(); 553 610 } 554 611 else { 555 612 if (!crd.value(_reference).contains(prn)) continue; 556 refs << _reference; 557 } 558 QVector<double> clks, xs[3]; 559 for (const QString& oo : refs) { 560 const t_satCrd& rc = crd[oo][prn]; 561 if (rc.clkOK && (oo == _reference || offset.contains(oo))) { 562 clks << rc.clk - (oo == _reference ? 0.0 : offset[oo]); 563 } 613 const t_satCrd& rc = crd[_reference][prn]; 614 if (rc.clkOK) clks << rc.clk; 564 615 if (rc.refPoint == sc.refPoint && sc.refPoint != '?') { 565 616 for (int ii = 0; ii < 3; ii++) xs[ii] << rc.xx[ii]; 566 617 } 567 618 } 568 bool clkCmp = sc.clkOK && offset.contains(staID) && 569 (_reference == "CONSENSUS" ? clks.size() >= 2 : clks.size() == 1); 570 if (clkCmp) { 619 if (sc.clkOK && offset.contains(staID) && !clks.isEmpty()) { 571 620 dClk[prn] = sc.clk - offset[staID] - median(clks); 572 621 } … … 574 623 _cmpStat[staID][prn].clkNotReferred += 1; 575 624 } 576 if ( xs[0].size() == refs.size() || (xs[0].size() >= 2 && _reference == "CONSENSUS")) {625 if (!xs[0].isEmpty()) { 577 626 ColumnVector dx(3); 578 627 for (int ii = 0; ii < 3; ii++) dx[ii] = sc.xx[ii] - median(xs[ii]); … … 827 876 } 828 877 if (nSat) { 829 QString refText = (_reference == "CONSENSUS") ? QString("consensus of the other streams")878 QString refText = (_reference == "CONSENSUS") ? QString("consensus of the other ACs") 830 879 : QString("stream %1").arg(_reference); 831 880 QStringList parts; -
trunk/BNC/src/ssrQc.h
r11079 r11081 80 80 QMutex _mutex; 81 81 bool selected(const std::string& staID) const; 82 bool votes(const QString& staID) const; 83 static QString acOf(const QString& staID); 82 84 83 85 // Stream comparison: every CMP_SAMPL seconds, the orbits and clocks of each … … 116 118 QString _dirName; 117 119 QSet<QString> _streams; // selected streams, empty: all 120 QSet<QString> _excluded; // excluded streams ('!' in the selection) 118 121 QMap<QString, std::ofstream*> _outs; // per stream 119 122 QMap<QString, QString> _outNames; // per stream
Note:
See TracChangeset
for help on using the changeset viewer.
