Changeset 11087 in ntrip
- Timestamp:
- Oct 16, 2026, 10:19:51 AM (4 hours ago)
- Location:
- trunk/BNC
- Files:
-
- 22 edited
-
src/bnchelp.html (modified) (7 diffs)
-
src/bncmain.cpp (modified) (2 diffs)
-
src/bncsettings.cpp (modified) (1 diff)
-
src/bncwindow.cpp (modified) (11 diffs)
-
src/bncwindow.h (modified) (2 diffs)
-
src/combination/bncbiassnx.cpp (modified) (3 diffs)
-
src/combination/bncbiassnx.h (modified) (1 diff)
-
src/combination/bnccomb.cpp (modified) (1 diff)
-
src/combination/bnccomb.h (modified) (2 diffs)
-
src/src.pri (modified) (2 diffs)
-
src/ssrQc.cpp (modified) (8 diffs)
-
src/ssrQc.h (modified) (2 diffs)
-
tests/README.md (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
r11085 r11087 147 147 2.8.2 <a href="#corrint">Interval</a><br> 148 148 2.8.3 <a href="#corrport">Port</a><br> 149 2.8.4 <a href="#ssrqc">QC Directory, QC Streams, QC Reference, QC Interval</a><br> 149 2.8.4 <a href="#ssrqc">QC Directory, QC Streams, QC Reference, QC Interval, QC Bias File</a><br> 150 150 2.8.4 <a href="#corrwait">Wait for Full Corr Epoch</a><br> 151 151 2.9 <a href="#syncout"><b>Feed Engine</b></a><br> … … 274 274 2.14.1.6 <a href=#combisatsys>Satellite 275 275 Systems</a><br> 276 2.14.1.7 <a href=#combirefsig>Reference 277 Signals</a><br> 276 278 2.15 <a href=#upclk><b>Upload Corrections</b></a><br> 277 279 2.15.1 <a href=#upadd>Add, Delete Row</a><br> … … 5088 5090 5089 5091 <p> 5090 <h4 id="ssrqc">2.8.4 QC Directory, QC Streams, QC Reference, QC Interval - optional</h4> 5092 <h4 id="ssrqc">2.8.4 QC Directory, QC Streams, QC Reference, QC Interval, QC Bias File - optional</h4> 5091 5093 </p> 5092 5094 <p> … … 5096 5098 'SSRA00CNE1,SSRA00GMV0'. A stream name with a leading '!' excludes the stream from the QC and from the consensus, 5097 5099 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 5098 the 'QC Interval' for the reports (default: 1 hour). One daily file 5100 the 'QC Interval' for the reports (default: 1 hour). A 'QC Bias File' (Bias SINEX, OSB or DSB, e.g. a CAS DCB or 5101 a GFZ OSB product) is an optional reference for the code bias check (see below). One daily file 5099 5102 '<stream>_S_<YYYYDDD>0000_01D_QC.txt' is written per stream; at the end of each interval and at the 5100 5103 end of the program, a report per system is appended. Broadcast ephemerides must be received as well, e.g. via … … 5126 5129 broadcast ephemeris, or to a satellite without broadcast ephemeris (number of corrections in brackets; not 5127 5130 checked during the first 2 minutes, while the broadcast ephemerides are received),</li> 5128 <li>the code bias signals provided, satellites without code biases of the reference signals (GPS C1W/C2W, GLONASS 5129 C1P/C2P, Galileo C1C/C5Q, BDS C2I/C6I, QZSS C1C/C2L), and the standard deviation and maximum of the 5131 <li>the code bias signals provided, satellites without code biases of the reference signals (default GPS C1W/C2W, 5132 GLONASS C1P/C2P, Galileo C1C/C5Q, BDS C2I/C6I, QZSS C1C/C2L, SBAS C1C/C5Q; as in the combination, see option 5133 'Reference Signals' in <a href=#combirefsig>section 2.14.1.7</a>), and the standard deviation and maximum of the 5130 5134 ionosphere-free combination B_IF of the reference signal biases. A B_IF varying between the satellites is 5131 5135 legitimate if the clocks are estimated with other signals, but must then be consistent with the clocks 5132 5136 (see section 'Combination'),</li> 5137 <li>the code bias check: the DCBs of the stream, i.e. the differences of its code biases to the first reference 5138 signal (or to another common signal, if the stream lacks it), are compared with 5139 <ul> 5140 <li>each other AC separately (one vote per AC, own AC left out, combinations not voting), with the RMS of 5141 the differences and the number of DCBs with opposite sign (only DCBs of at least 0.3 m are used for the 5142 sign). A stream is reported to use the opposite sign if it has the opposite sign for most DCBs to more than 5143 half of at least two other ACs - with only two ACs, it cannot be decided which one is wrong. Satellites 5144 with DCBs deviating from the median of at least 3 other ACs are listed,</li> 5145 <li>the selected 'QC Reference' stream instead, if it is not 'CONSENSUS',</li> 5146 <li>the 'QC Bias File', if specified.</li> 5147 </ul> 5148 DCBs do not depend on the clock datum, so the check also works for streams whose clocks are estimated with 5149 other signals. A sign opposite to the reference for most DCBs indicates code biases in the sign convention of 5150 Bias SINEX (biases to be subtracted) instead of SSR (biases to be added). Such a stream passes the B_IF check 5151 above (B_IF is zero for both signs), but a PPP user applying its biases to other than the reference signals 5152 gets errors of twice the biases, e.g. 5153 <pre> 5154 G code biases compared with the other ACs (DCBs to C1W): BKG RMS 2.17 m, opposite sign 48/49; CNE RMS 2.44 m, opposite sign 98/101 5155 G code biases with the opposite sign of 2 of 2 other ACs (BKG CNE) - SINEX sign convention instead of the SSR one? 5156 G code biases compared with Bias SINEX file CAS1MGXRAP_01D_01D_DCB.BSX (DCBs to C1W, 32 satellites, 141 DCBs): RMS of the differences 2.401 m, opposite sign for 106 of 107 DCBs (|DCB| >= 0.3 m) 5157 </pre> 5158 The latest code biases of each stream are used,</li> 5133 5159 <li>the phase bias signals provided,</li> 5134 5160 <li>the availability of the satellite yaw angle with the phase biases: 'not provided', or the number of … … 8066 8092 </p> 8067 8093 <p> 8068 The combination is done system-wise for the following reference signals as specified in the IGS Real-Time Committee: 8094 The combination is done system-wise for the following reference signals as specified in the IGS Real-Time Committee 8095 (default, they can be changed per system with the option 'Reference Signals', see 8096 <a href=#combirefsig>section 2.14.1.7</a>): 8069 8097 <p> 8070 8098 <table> … … 8478 8506 <p> 8479 8507 Specify for each satellite system whether the clock corrections shall be combined.</p> 8508 8509 <p> 8510 <h4 id="combirefsig">2.14.1.7 Reference Signals - optional</h4> 8511 </p> 8512 <p> 8513 Specify the reference signals of the satellite clocks per GNSS as '<system>:<band><attribute>&<band><attribute>', 8514 separated by blanks, e.g. 'C:1P&5P J:1C&5Q'. The clocks refer to the ionosphere-free linear combination of 8515 the code signals of these two signals. Systems not specified keep the default of the IGS Real-Time Committee: 8516 </p> 8517 <pre> 8518 G:1W&2W R:1P&2P E:1C&5Q C:2I&6I J:1C&2L S:1C&5Q 8519 </pre> 8520 <p> 8521 The reference signals are used 8522 </p> 8523 <ul> 8524 <li>by the combination, to refer the AC clocks to the reference signals with the code biases of each AC (B_IF, see 8525 above); an AC without the code biases of both reference signals of a satellite is excluded from the clock datum, 8526 </li> 8527 <li>by the combination, to derive the code biases of the combined clocks from a Bias SINEX file ('BSX File'): the 8528 ionosphere-free linear combination of the two reference signal biases is set to zero and all other biases are 8529 derived from the DCBs. With the default reference signals, the DCBs of the IGS conventions are used as before; 8530 with other reference signals, all DCBs of the satellite in the file are used, and the DCB of the reference 8531 signals themselves may also be derived via other signals,</li> 8532 <li>by the SSR QC (see section 'Broadcast Corrections'), for the code biases of the reference signals, B_IF and the 8533 clock comparison. The reference signals used are written to the header of each SSR QC report.</li> 8534 </ul> 8535 <p> 8536 Changing the reference signals is meant for a coordinated switch of the IGS Real-Time convention, e.g. for BDS from 8537 C2I/C6I to C1P/C5P (B1C/B2a) following the IGS products, or for QZSS to an L1/L5 pair because the QZSS Block III 8538 satellites no longer transmit L2. All ACs of a combination should then provide the code biases of the new reference 8539 signals. A switch changes the combined clocks of the affected satellites by the ionosphere-free combination of the 8540 DCBs between the old and the new reference signals. 8541 </p> 8542 <p> 8543 Invalid entries (unknown system or band, wrong format) are reported in the logfile and ignored, i.e. the system 8544 keeps its default. Non-default reference signals are written to the logfile when the combination starts. 8545 </p> 8546 <p> 8547 Default is an empty option field, meaning the default reference signals. <i>[key: ssrRefSignals]</i> 8548 </p> 8480 8549 8481 8550 <p> -
trunk/BNC/src/bncmain.cpp
r11081 r11087 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" 216 " ssrQcBiasFile {Bias SINEX file (OSB or DSB) as reference for the SSR QC code bias check, full path [character string]}\n" 216 217 "\n" 217 218 "Feed Engine Panel keys:\n" … … 314 315 " cmbMethodFilter {Combination approach [character string: Single-Epoch|Filter]}\n" 315 316 " cmbBsxFile {SINEX Bias file, full path [char string]}\n" 317 " ssrRefSignals {Reference signals of the clocks per GNSS, also used by the SSR QC [character string,\n" 318 " example: C:1P&5P J:1C&5Q; empty: G:1W&2W R:1P&2P E:1C&5Q C:2I&6I J:1C&2L S:1C&5Q]}\n" 316 319 " cmbMaxres {Clock outlier residuum threshold in meters [floating-point number]}\n" 317 320 " cmbMaxdisplacement {Maximal orbit displacement from the mean of corrections for a satellite [floating-point number]}\n" -
trunk/BNC/src/bncsettings.cpp
r11075 r11087 142 142 setValue_p("ssrQcRef", "CONSENSUS"); 143 143 setValue_p("ssrQcIntr", "1 hour"); 144 setValue_p("ssrQcBiasFile", ""); 145 setValue_p("ssrRefSignals", ""); 144 146 // Feed Engine 145 147 setValue_p("outPort", ""); -
trunk/BNC/src/bncwindow.cpp
r11081 r11087 301 301 _ssrQcRefLineEdit = new QLineEdit(settings.value("ssrQcRef").toString().isEmpty() ? QString("CONSENSUS") : settings.value("ssrQcRef").toString()); 302 302 _ssrQcStreamsLineEdit = new QLineEdit(settings.value("ssrQcStreams").toString().isEmpty() ? QString("ALL") : settings.value("ssrQcStreams").toString()); 303 _ssrQcBiasFileChooser = new qtFileChooser(0, qtFileChooser::File); 304 _ssrQcBiasFileChooser->setFileName(settings.value("ssrQcBiasFile").toString()); 303 305 _ssrQcIntrComboBox = new QComboBox(); 304 306 _ssrQcIntrComboBox->setEditable(false); … … 571 573 _cmbBsxFile = new qtFileChooser(0, qtFileChooser::File); 572 574 _cmbBsxFile->setFileName(settings.value("cmbBsxFile").toString()); 575 576 _cmbRefSigLineEdit = new QLineEdit(settings.value("ssrRefSignals").toString()); 577 _cmbRefSigLineEdit->setPlaceholderText("default"); 573 578 574 579 // Upload Results … … 926 931 cLayout->addWidget(new QLabel("QC Interval"), 7, 0); 927 932 cLayout->addWidget(_ssrQcIntrComboBox, 7, 1); 928 cLayout->addWidget(new QLabel(""), 8, 1); 929 cLayout->setRowStretch(7, 999); 933 cLayout->addWidget(new QLabel("QC Bias File"), 8, 0); 934 cLayout->addWidget(_ssrQcBiasFileChooser, 8, 1, 1, 30); 935 cLayout->addWidget(new QLabel(""), 9, 1); 936 cLayout->setRowStretch(8, 999); 930 937 cgroup->setLayout(cLayout); 931 938 … … 1418 1425 cmbLayout->addWidget(new QLabel("Sampling"), 7, 10, Qt::AlignLeft); 1419 1426 cmbLayout->addWidget(_cmbSamplComboBox, 7, 11, Qt::AlignRight); 1427 cmbLayout->addWidget(new QLabel("Reference signals"), 8, 10, Qt::AlignLeft); 1428 cmbLayout->addWidget(_cmbRefSigLineEdit, 8, 11, 1, 5); 1420 1429 1421 1430 1422 1431 cmbLayout->addWidget(new QLabel("GNSS"), 0, 14, Qt::AlignLeft); 1423 cmbLayout->addWidget(new QLabel("GPS (C1W/C2W)"), 1, 14);1432 cmbLayout->addWidget(new QLabel("GPS"), 1, 14); 1424 1433 cmbLayout->addWidget(_cmbGpsCheckBox, 1, 15); 1425 1434 1426 cmbLayout->addWidget(new QLabel("GLONASS (C1P/C2P)"), 2, 14);1435 cmbLayout->addWidget(new QLabel("GLONASS"), 2, 14); 1427 1436 cmbLayout->addWidget(_cmbGloCheckBox, 2, 15); 1428 1437 1429 cmbLayout->addWidget(new QLabel("Galileo (C1C/C5Q)"), 3, 14);1438 cmbLayout->addWidget(new QLabel("Galileo"), 3, 14); 1430 1439 cmbLayout->addWidget(_cmbGalCheckBox, 3, 15); 1431 1440 1432 cmbLayout->addWidget(new QLabel("Beidou (C2I/C6I)"), 4, 14);1441 cmbLayout->addWidget(new QLabel("Beidou"), 4, 14); 1433 1442 cmbLayout->addWidget(_cmbBdsCheckBox, 4, 15); 1434 1443 1435 cmbLayout->addWidget(new QLabel("QZSS (C1C/C2L)"), 5, 14);1444 cmbLayout->addWidget(new QLabel("QZSS"), 5, 14); 1436 1445 cmbLayout->addWidget(_cmbQzssCheckBox, 5, 15); 1437 1446 1438 cmbLayout->addWidget(new QLabel("SBAS (C1C/C5Q)"), 6, 14);1447 cmbLayout->addWidget(new QLabel("SBAS"), 6, 14); 1439 1448 cmbLayout->addWidget(_cmbSbasCheckBox, 6, 15); 1440 1449 … … 1597 1606 _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 1607 _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>")); 1608 _ssrQcBiasFileChooser->setWhatsThis(tr("<p>Specify a Bias SINEX file (OSB or DSB, e.g. a CAS DCB or a GFZ OSB product) as reference for the code bias check of the SSR QC: the DCBs of each stream are compared with the DCBs of this file, in addition to the comparison with the other ACs. A sign opposite to the reference for most DCBs indicates a stream using the SINEX instead of the SSR sign convention.</p><p>Default is an empty option field, meaning the code biases are compared with the other ACs only. <i>[key: ssrQcBiasFile]</i></p>")); 1599 1609 _ssrQcIntrComboBox->setWhatsThis(tr("<p>Select the interval of the SSR QC reports. <i>[key: ssrQcIntr]</i></p>")); 1600 1610 _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>")); … … 1745 1755 _cmbSbasCheckBox->setWhatsThis(tr("<p>SBAS clock corrections shall be combined; SBAS Broadcast ephemeris and corrections are required. <i>[key: cmbSbas]</i></p>")); 1746 1756 _cmbNavicCheckBox->setWhatsThis(tr("<p>NavIC clock corrections shall be combined; NavIC Broadcast ephemeris and corrections are required. <i>[key: cmbNavic]</i></p>")); 1757 _cmbRefSigLineEdit->setWhatsThis(tr("<p>Specify the reference signals of the satellite clocks per GNSS: the clocks refer to the ionosphere-free combination of the code signals of these two signals. They are used by the combination to refer the AC clocks with their code biases and to derive the code biases from a Bias SINEX file, and by the SSR QC.</p><p>Systems not specified keep the default 'G:1W&2W R:1P&2P E:1C&5Q C:2I&6I J:1C&2L S:1C&5Q' (IGS conventions). Example: 'C:1P&5P J:1C&5Q'.</p><p>Default is an empty option field, meaning the default reference signals. <i>[key: ssrRefSignals]</i></p>")); 1747 1758 _cmbBsxFile->setWhatsThis(tr("<p> Specify a Bias SINEX File that will be used to add satellite code biases to the combined clocks. <i>[key: cmbBsxFile]</i></p>")); 1748 1759 … … 1875 1886 delete _ssrQcRefLineEdit; 1876 1887 delete _ssrQcIntrComboBox; 1888 delete _ssrQcBiasFileChooser; 1877 1889 delete _corrPortLineEdit; 1878 1890 delete _outPortLineEdit; … … 1918 1930 delete _cmbNavicCheckBox; 1919 1931 delete _cmbBsxFile; 1932 delete _cmbRefSigLineEdit; 1920 1933 delete _uploadSamplRtcmEphCorrComboBox; 1921 1934 _uploadEphTable->deleteLater(); … … 2405 2418 settings.setValue("ssrQcRef", _ssrQcRefLineEdit->text()); 2406 2419 settings.setValue("ssrQcIntr", _ssrQcIntrComboBox->currentText()); 2420 settings.setValue("ssrQcBiasFile", _ssrQcBiasFileChooser->fileName()); 2407 2421 settings.setValue("corrPort", _corrPortLineEdit->text()); 2408 2422 // Feed Engine … … 2471 2485 settings.setValue("cmbNavic", _cmbNavicCheckBox->checkState()); 2472 2486 settings.setValue("cmbBsxFile", _cmbBsxFile->fileName()); 2487 settings.setValue("ssrRefSignals", _cmbRefSigLineEdit->text()); 2473 2488 2474 2489 // Upload Corrections … … 2943 2958 enableWidget(!_ssrQcPathLineEdit->text().isEmpty(), _ssrQcStreamsLineEdit); 2944 2959 enableWidget(!_ssrQcPathLineEdit->text().isEmpty(), _ssrQcRefLineEdit); 2960 enableWidget(!_ssrQcPathLineEdit->text().isEmpty(), _ssrQcBiasFileChooser); 2945 2961 } 2946 2962 -
trunk/BNC/src/bncwindow.h
r11075 r11087 154 154 QLineEdit* _ssrQcStreamsLineEdit; 155 155 QLineEdit* _ssrQcRefLineEdit; 156 qtFileChooser* _ssrQcBiasFileChooser; 156 157 QLineEdit* _miscMountLineEdit; 157 158 QLineEdit* _miscPortLineEdit; … … 241 242 QCheckBox* _cmbNavicCheckBox; 242 243 qtFileChooser* _cmbBsxFile; 244 QLineEdit* _cmbRefSigLineEdit; 243 245 244 246 QTableWidget* _uploadTable; -
trunk/BNC/src/combination/bncbiassnx.cpp
r11023 r11087 40 40 41 41 #include "bncbiassnx.h" 42 #include "ssrRefSig.h" 42 43 43 44 #include <iostream> … … 48 49 // Constructor 49 50 //////////////////////////////////////////////////////////////////////////// 50 bncBiasSnx::bncBiasSnx() { 51 bncBiasSnx::bncBiasSnx(bool allGnss) { 51 52 bncSettings settings; 53 if (allGnss) { 54 for (char sys : QString("GRECJSI").toLatin1()) { 55 _useGnss[sys] = true; 56 } 57 return; 58 } 52 59 _useGnss['G'] = (Qt::CheckState(settings.value("cmbGps").toInt()) == Qt::Checked) ? true : false; 53 60 _useGnss['R'] = (Qt::CheckState(settings.value("cmbGlo").toInt()) == Qt::Checked) ? true : false; … … 307 314 } 308 315 309 // 316 // DCBs (OSB_A - OSB_B) used to derive the code biases from the default 317 // reference signals (see t_ssrRefSig), in the order of evaluation 318 //////////////////////////////////////////////////////////////////////////// 319 static const QVector<QPair<QString, QString> >& defaultDcbs(char sys) { 320 static QMap<char, QVector<QPair<QString, QString> > > dcbs; 321 static QMutex mutex; 322 QMutexLocker locker(&mutex); 323 if (dcbs.isEmpty()) { 324 auto add = [](QVector<QPair<QString, QString> >& vv, const char* list) { 325 for (const QString& dcb : QString(list).split(' ', Qt::SkipEmptyParts)) { 326 vv.append(qMakePair(dcb.section('-', 0, 0), dcb.section('-', 1, 1))); 327 } 328 }; 329 add(dcbs['G'], "1C-1W 1C-5Q 1C-5X 1C-2W 2C-2W 2W-2S 2W-2L 2W-2X"); 330 add(dcbs['R'], "1C-1P 2C-2P"); 331 add(dcbs['E'], "1C-6C 1C-7Q 1C-8Q"); 332 add(dcbs['C'], "2I-7I 1X-6I 1X-7Z 1X-8X 1X-5X 1P-6I 1P-5P 1D-6I 1D-5D"); 333 add(dcbs['J'], "1C-1X 1X-2X 1C-5X"); 334 } 335 static const QVector<QPair<QString, QString> > none; 336 return dcbs.contains(sys) ? dcbs[sys] : none; 337 } 338 339 // Satellite code biases (OSB) from the DCBs, referred to the reference 340 // signals of the clocks: aIF1 * OSB_ref1 + aIF2 * OSB_ref2 = 0 310 341 //////////////////////////////////////////////////////////////////////////// 311 342 void bncBiasSnx::determineSsrSatCodeBiases(QString prn, double aIF1, double aIF2, 312 343 t_satCodeBias& satCodeBias) { 313 std::string rnxType2ch; 314 double value; 315 if (_snxSatCodeBiasMap.contains(prn)) { 316 char sys = prn.at(0).toLatin1(); 317 QMap<QString, double>& cb = _snxSatCodeBiasMap[prn]->_biasMap; 318 switch (sys) { 319 case 'G': 320 if (cb.contains("C1W-C2W")) { 321 double dcb_1W2W = cb.find("C1W-C2W").value(); 322 rnxType2ch = "1W"; 323 value = aIF2 * dcb_1W2W; 324 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 325 rnxType2ch = "2W"; 326 value = -aIF1 * dcb_1W2W; 327 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 328 if (cb.contains("C1C-C1W")) { 329 double dcb_1C1W = cb.find("C1C-C1W").value(); 330 rnxType2ch = "1C"; 331 value = dcb_1C1W + aIF2 * dcb_1W2W; 332 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 333 if (cb.contains("C1C-C5Q")) { 334 double dcb_1C5Q = cb.find("C1C-C5Q").value(); 335 rnxType2ch = "5Q"; 336 value = dcb_1C1W + aIF2 * dcb_1W2W - dcb_1C5Q; 337 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 338 } 339 if (cb.contains("C1C-C5X")) { 340 double dcb_1C5X = cb.find("C1C-C5X").value(); 341 rnxType2ch = "5X"; 342 value = dcb_1C1W + aIF2 * dcb_1W2W - dcb_1C5X; 343 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 344 } 345 } 346 else { 347 if (cb.contains("C1C-C2W")) { 348 double dcb_1C2W = cb.find("C1C-C2W").value(); 349 rnxType2ch = "1C"; 350 value = dcb_1C2W - aIF1 * dcb_1W2W; 351 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 352 } 353 } 354 if (cb.contains("C2C-C2W")) { 355 double dcb_2C2W = cb.find("C2C-C2W").value(); 356 rnxType2ch = "2C"; 357 value = dcb_2C2W - aIF1 * dcb_1W2W; 358 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 359 } 360 if (cb.contains("C2W-C2S")) { 361 double dcb_2W2S = cb.find("C2W-C2S").value(); 362 rnxType2ch = "2S"; 363 value = -aIF1 * dcb_1W2W - dcb_2W2S; 364 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 365 } 366 if (cb.contains("C2W-C2L")) { 367 double dcb_2W2L = cb.find("C2W-C2L").value(); 368 rnxType2ch = "2L"; 369 value = -aIF1 * dcb_1W2W - dcb_2W2L; 370 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 371 } 372 if (cb.contains("C2W-C2X")) { 373 double dcb_2W2X = cb.find("C2W-C2X").value(); 374 rnxType2ch = "2X"; 375 value = -aIF1 * dcb_1W2W - dcb_2W2X; 376 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 377 } 378 } 379 break; 380 case 'R': 381 if (cb.contains("C1P-C2P")) { 382 double dcb_1P2P = cb.find("C1P-C2P").value(); 383 rnxType2ch = "1P"; 384 value = aIF2 * dcb_1P2P; 385 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 386 rnxType2ch = "2P"; 387 value = -aIF1 * dcb_1P2P; 388 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 389 if (cb.contains("C1C-C1P")) { 390 double dcb_1C1P = cb.find("C1C-C1P").value(); 391 rnxType2ch = "1C"; 392 value = dcb_1C1P + aIF2 * dcb_1P2P; 393 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 394 } 395 if (cb.contains("C2C-C2P")) { 396 double dcb_2C2P = cb.find("C2C-C2P").value(); 397 rnxType2ch = "2C"; 398 value = dcb_2C2P - aIF1 * dcb_1P2P; 399 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 400 } 401 } 402 break; 403 case 'E': 404 if (cb.contains("C1C-C5Q")) { 405 double dcb_1C5Q = cb.find("C1C-C5Q").value(); 406 rnxType2ch = "5Q"; 407 value = -aIF1 * dcb_1C5Q; 408 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 409 rnxType2ch = "1C"; 410 value = aIF2 * dcb_1C5Q; 411 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 412 if (cb.contains("C1C-C6C")) { 413 double dcb_1C6C = cb.find("C1C-C6C").value(); 414 rnxType2ch = "6C"; 415 value = aIF2 * dcb_1C5Q - dcb_1C6C; 416 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 417 } 418 if (cb.contains("C1C-C7Q")) { 419 double dcb_1C7Q = cb.find("C1C-C7Q").value(); 420 rnxType2ch = "7Q"; 421 value = aIF2 * dcb_1C5Q - dcb_1C7Q; 422 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 423 } 424 if (cb.contains("C1C-C8Q")) { 425 double dcb_1C8Q = cb.find("C1C-C8Q").value(); 426 rnxType2ch = "8Q"; 427 value = aIF2 * dcb_1C5Q - dcb_1C8Q; 428 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 429 } 430 } 431 break; 432 case 'C': 433 if (cb.contains("C2I-C6I")) { 434 double dcb_2I6I = cb.find("C2I-C6I").value(); 435 rnxType2ch = "2I"; 436 value = aIF2 * dcb_2I6I; 437 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 438 rnxType2ch = "6I"; 439 value = -aIF1 * dcb_2I6I; 440 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 441 if (cb.contains("C2I-C7I")) { 442 double dcb_2I7I = cb.find("C2I-C7I").value(); 443 rnxType2ch = "7I"; 444 value = aIF2 * dcb_2I6I - dcb_2I7I; 445 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 446 } 447 if (cb.contains("C1X-C6I")) { 448 double dcb_1X6I = cb.find("C1X-C6I").value(); 449 rnxType2ch = "1X"; 450 value = dcb_1X6I - aIF1 * dcb_2I6I; 451 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 452 if (cb.contains("C1X-C7Z")) { 453 double dcb_1X7Z = cb.find("C1X-C7Z").value(); 454 rnxType2ch = "7Z"; 455 value = dcb_1X6I - aIF1 * dcb_2I6I - dcb_1X7Z; 456 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 457 } 458 if (cb.contains("C1X-C8X")) { 459 double dcb_1X8X = cb.find("C1X-C8X").value(); 460 rnxType2ch = "8X"; 461 value = dcb_1X6I - aIF1 * dcb_2I6I - dcb_1X8X; 462 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 463 } 464 if (cb.contains("C1X-C5X")) { 465 double dcb_1X5X = cb.find("C1X-C5X").value(); 466 rnxType2ch = "5X"; 467 value = dcb_1X6I - aIF1 * dcb_2I6I - dcb_1X5X; 468 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 469 } 470 } 471 if (cb.contains("C1P-C6I")) { 472 double dcb_1P6I = cb.find("C1P-C6I").value(); 473 rnxType2ch = "1P"; 474 value = dcb_1P6I - aIF1 * dcb_2I6I; 475 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 476 if (cb.contains("C1P-C5P")) { 477 double dcb_1P5P = cb.find("C1P-C5P").value(); 478 rnxType2ch = "5P"; 479 value = dcb_1P6I - aIF1 * dcb_2I6I - dcb_1P5P; 480 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 481 } 482 } 483 if (cb.contains("C1D-C6I")) { 484 double dcb_1D6I = cb.find("C1D-C6I").value(); 485 rnxType2ch = "1D"; 486 value = dcb_1D6I - aIF1 * dcb_2I6I; 487 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 488 if (cb.contains("C1D-C5D")) { 489 double dcb_1D5D = cb.find("C1D-C5D").value(); 490 rnxType2ch = "5D"; 491 value = dcb_1D6I - aIF1 * dcb_2I6I - dcb_1D5D; 492 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 493 } 494 } 495 } 496 break; 497 case 'J': 498 if (cb.contains("C1C-C2L")) { 499 double dcb_1C2L = cb.find("C1C-C2L").value(); 500 rnxType2ch = "1C"; 501 value = aIF2 * dcb_1C2L; 502 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 503 rnxType2ch = "2L"; 504 value = -aIF1 * dcb_1C2L; 505 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 506 if (cb.contains("C1C-C1X")) { 507 double dcb_1C1X = cb.find("C1C-C1X").value(); 508 rnxType2ch = "1X"; 509 value = aIF2 * dcb_1C2L - dcb_1C1X; 510 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 511 if (cb.contains("C1X-C2X")) { 512 double dcb_1X2X = cb.find("C1X-C2X").value(); 513 rnxType2ch = "2X"; 514 value = aIF2 * dcb_1C2L - dcb_1C1X - dcb_1X2X; 515 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 516 } 517 } 518 if (cb.contains("C1C-C5X")) { 519 double dcb_1C5X = cb.find("C1C-C5X").value(); 520 rnxType2ch = "5X"; 521 value = aIF2 * dcb_1C2L - dcb_1C5X; 522 setSsrSatCodeBias(rnxType2ch, value, satCodeBias); 523 } 524 } 525 break; 526 default: 527 break; 528 } 529 } 530 return; 344 if (!_snxSatCodeBiasMap.contains(prn)) { 345 return; 346 } 347 char sys = prn.at(0).toLatin1(); 348 t_ssrRefSig::t_pair pair; 349 if (!t_ssrRefSig::get(sys, pair)) { 350 return; 351 } 352 const QString ref1 = QString::fromStdString(pair.sig1()); 353 const QString ref2 = QString::fromStdString(pair.sig2()); 354 const QMap<QString, double>& cb = _snxSatCodeBiasMap[prn]->_biasMap; 355 auto dcb = [&cb](const QString& sig1, const QString& sig2, double& value) { 356 QString key = "C" + sig1 + "-C" + sig2; 357 if (cb.contains(key)) { 358 value = cb[key]; 359 return true; 360 } 361 return false; 362 }; 363 364 QMap<QString, double> osb; 365 QStringList order; 366 367 // Default reference signals: the DCB of the reference signals and the 368 // DCBs of the IGS conventions, evaluated in one pass as before 369 if (t_ssrRefSig::isDefault(sys)) { 370 const QVector<QPair<QString, QString> >& dcbs = defaultDcbs(sys); 371 double dcbRef; 372 if (dcbs.isEmpty() || !dcb(ref1, ref2, dcbRef)) { 373 return; 374 } 375 osb[ref1] = aIF2 * dcbRef; 376 osb[ref2] = -aIF1 * dcbRef; 377 if (sys == 'E') { // order of the output as before 378 order << ref2 << ref1; 379 } 380 else { 381 order << ref1 << ref2; 382 } 383 for (const QPair<QString, QString>& dd : dcbs) { 384 double value; 385 if (!dcb(dd.first, dd.second, value)) { 386 continue; 387 } 388 if (osb.contains(dd.first) && !osb.contains(dd.second)) { 389 osb[dd.second] = osb[dd.first] - value; 390 order << dd.second; 391 } 392 else if (osb.contains(dd.second) && !osb.contains(dd.first)) { 393 osb[dd.first] = value + osb[dd.second]; 394 order << dd.first; 395 } 396 } 397 } 398 399 // Other reference signals: all DCBs of the satellite, the DCB of the 400 // reference signals may also be derived via other signals 401 else { 402 QMap<QString, double> rel; // OSB - OSB_ref1 403 QStringList relOrder; 404 rel[ref1] = 0.0; 405 relOrder << ref1; 406 bool changed = true; 407 while (changed) { 408 changed = false; 409 QMapIterator<QString, double> it(cb); 410 while (it.hasNext()) { 411 it.next(); 412 QStringList hlp = it.key().split('-'); 413 if (hlp.size() != 2 || hlp[0].size() != 3 || hlp[1].size() != 3) { 414 continue; 415 } 416 QString sigA = hlp[0].mid(1); 417 QString sigB = hlp[1].mid(1); 418 if (rel.contains(sigA) && !rel.contains(sigB)) { 419 rel[sigB] = rel[sigA] - it.value(); 420 relOrder << sigB; 421 changed = true; 422 } 423 else if (rel.contains(sigB) && !rel.contains(sigA)) { 424 rel[sigA] = rel[sigB] + it.value(); 425 relOrder << sigA; 426 changed = true; 427 } 428 } 429 } 430 if (!rel.contains(ref2)) { 431 return; 432 } 433 double osbRef1 = -aIF2 * rel[ref2]; 434 order << ref1 << ref2; 435 for (const QString& sig : relOrder) { 436 osb[sig] = osbRef1 + rel[sig]; 437 if (sig != ref1 && sig != ref2) { 438 order << sig; 439 } 440 } 441 } 442 443 for (const QString& sig : order) { 444 setSsrSatCodeBias(sig.toStdString(), osb[sig], satCodeBias); 445 } 531 446 } 532 447 -
trunk/BNC/src/combination/bncbiassnx.h
r11023 r11087 58 58 class bncBiasSnx { 59 59 public: 60 bncBiasSnx( );60 bncBiasSnx(bool allGnss = false); // allGnss: all systems, not only the combined ones 61 61 ~bncBiasSnx(); 62 62 t_irc readFile(const QString& fileName); -
trunk/BNC/src/combination/bnccomb.cpp
r11086 r11087 206 206 207 207 connect(this, SIGNAL(newMessage(QByteArray,bool)), BNC_CORE, SLOT(slotMessage(const QByteArray,bool))); 208 209 // Reference signals of the clocks 210 for (const QString& err : t_ssrRefSig::init()) { 211 emit newMessage(("bncComb: " + err).toLatin1(), true); 212 } 213 if (!t_ssrRefSig::allDefault()) { 214 emit newMessage(("bncComb: reference signals of the clocks " + t_ssrRefSig::currentString()).toLatin1(), true); 215 } 216 208 217 connect(BNC_CORE, SIGNAL(providerIDChanged(QString)), this, SLOT(slotProviderIDChanged(QString))); 209 218 connect(BNC_CORE, SIGNAL(newOrbCorrections(QList<t_orbCorr>)), this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>))); -
trunk/BNC/src/combination/bnccomb.h
r11086 r11087 18 18 #include "bncconst.h" 19 19 #include "bncoutf.h" 20 #include "ssrRefSig.h" 20 21 21 22 class bncRtnetDecoder; … … 176 177 enum type {dummy = 0, c1, c2, cIF}; 177 178 179 // reference signals of the clocks, see t_ssrRefSig (option ssrRefSignals) 178 180 static t_frequency::type toFreq(char sys, type tt) { 181 t_ssrRefSig::t_pair pair; 182 if (!t_ssrRefSig::get(sys, pair)) { 183 return t_frequency::dummy; 184 } 179 185 switch (tt) { 180 case c1: 181 if (sys == 'G') return t_frequency::G1; 182 else if (sys == 'R') return t_frequency::R1; 183 else if (sys == 'E') return t_frequency::E1; 184 else if (sys == 'C') return t_frequency::C2; 185 else if (sys == 'J') return t_frequency::J1; 186 else if (sys == 'S') return t_frequency::S1; 187 else return t_frequency::dummy; 188 case c2: 189 if (sys == 'G') return t_frequency::G2; 190 else if (sys == 'R') return t_frequency::R2; 191 else if (sys == 'E') return t_frequency::E5; 192 else if (sys == 'C') return t_frequency::C6; 193 else if (sys == 'J') return t_frequency::J2; 194 else if (sys == 'S') return t_frequency::S5; 195 else return t_frequency::dummy; 196 case dummy: 197 case cIF: 198 return t_frequency::dummy; 186 case c1: return pair.frq1; 187 case c2: return pair.frq2; 188 case dummy: 189 case cIF: 190 return t_frequency::dummy; 199 191 } 200 192 return t_frequency::dummy; 201 193 } 202 194 203 static char toAttrib(char sys, type LC) { 204 switch (LC) { 205 case c1: 206 if (sys == 'G') return 'W'; 207 else if (sys == 'R') return 'P'; 208 else if (sys == 'E') return 'C'; 209 else if (sys == 'C') return 'I'; 210 else if (sys == 'J') return 'C'; 211 else if (sys == 'S') return 'C'; 212 break; 213 case c2: 214 if (sys == 'G') return 'W'; 215 else if (sys == 'R') return 'P'; 216 else if (sys == 'E') return 'Q'; 217 else if (sys == 'C') return 'I'; 218 else if (sys == 'J') return 'L'; 219 else if (sys == 'S') return 'Q'; 220 break; 221 case dummy: 222 case cIF: 195 static char toAttrib(char sys, type tt) { 196 t_ssrRefSig::t_pair pair; 197 if (!t_ssrRefSig::get(sys, pair)) { 223 198 return '_'; 224 break; 199 } 200 switch (tt) { 201 case c1: return pair.att1; 202 case c2: return pair.att2; 203 case dummy: 204 case cIF: 205 return '_'; 225 206 } 226 207 return '_'; -
trunk/BNC/src/src.pri
r11079 r11087 47 47 -lnewmat -lqwtpolar -lqwt 48 48 49 HEADERS = bnchelp.html bncgetthread.h ssrQc.h bncwindow.h bnctabledlg.h \49 HEADERS = bnchelp.html bncgetthread.h ssrQc.h ssrRefSig.h bncwindow.h bnctabledlg.h \ 50 50 bnccaster.h bncrinex.h bnccore.h bncutils.h bnchlpdlg.h \ 51 51 bncconst.h bnchtml.h bnctableitem.h bnczerodecoder.h \ … … 89 89 win32:HEADERS += serial/win_qextserialport.h 90 90 91 SOURCES = bncgetthread.cpp ssrQc.cpp bncwindow.cpp bnctabledlg.cpp \ 91 SOURCES = bncgetthread.cpp ssrQc.cpp ssrRefSig.cpp bncwindow.cpp bnctabledlg.cpp \ 92 92 bnccaster.cpp bncrinex.cpp bnccore.cpp bncutils.cpp \ 93 93 bncconst.cpp bnchtml.cpp bnchlpdlg.cpp bnctableitem.cpp \ -
trunk/BNC/src/ssrQc.cpp
r11085 r11087 42 42 43 43 #include "ssrQc.h" 44 #include "ssrRefSig.h" 45 #include "combination/bncbiassnx.h" 44 46 #include "bnccore.h" 45 47 #include "bncutils.h" … … 126 128 127 129 connect(this, SIGNAL(newMessage(QByteArray,bool)), BNC_CORE, SLOT(slotMessage(const QByteArray,bool))); 130 for (const QString& err : t_ssrRefSig::init()) { 131 emit newMessage(("SSR QC: " + err).toLatin1(), true); 132 } 133 // reference code biases from a Bias SINEX file (optional) 134 _bsxRef = 0; 135 bncSettings settings; 136 QString bsxFile = settings.value("ssrQcBiasFile").toString(); 137 expandEnvVar(bsxFile); 138 if (!bsxFile.isEmpty()) { 139 _bsxRef = new bncBiasSnx(true); 140 if (_bsxRef->readFile(bsxFile) == success) { 141 _bsxName = QFileInfo(bsxFile).fileName(); 142 } 143 else { 144 emit newMessage(("SSR QC: wrong Bias SINEX file " + bsxFile).toLatin1(), true); 145 delete _bsxRef; 146 _bsxRef = 0; 147 } 148 } 128 149 connect(BNC_CORE, SIGNAL(newOrbCorrections(QList<t_orbCorr>)), 129 150 this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)), Qt::DirectConnection); … … 183 204 << " completeness w.r.t. healthy broadcast ephemerides," << endl 184 205 << "IOD matching, code and phase bias signals, yaw angle, code biases of the reference signals, clock jumps > " 185 << MAX_CLK_JUMP << " m, largest orbit corrections" << endl; 206 << MAX_CLK_JUMP << " m, largest orbit corrections" << endl 207 << "Reference signals of the clocks: " << t_ssrRefSig::currentString().toStdString() << endl; 186 208 } 187 209 _outs[staID] = out; … … 199 221 delete out; 200 222 } 223 delete _bsxRef; 201 224 } 202 225 … … 207 230 } 208 231 209 // Reference signals (IGS conventions,as in the combination)232 // Reference signals of the clocks (as in the combination, option ssrRefSignals) 210 233 //////////////////////////////////////////////////////////////////////////// 211 234 bool t_ssrQc::refSignals(char sys, QString& sig1, QString& sig2, 212 235 t_frequency::type& frq1, t_frequency::type& frq2) { 213 switch (sys) { 214 case 'G': sig1 = "1W"; sig2 = "2W"; frq1 = t_frequency::G1; frq2 = t_frequency::G2; return true; 215 case 'R': sig1 = "1P"; sig2 = "2P"; frq1 = t_frequency::R1; frq2 = t_frequency::R2; return true; 216 case 'E': sig1 = "1C"; sig2 = "5Q"; frq1 = t_frequency::E1; frq2 = t_frequency::E5; return true; 217 case 'C': sig1 = "2I"; sig2 = "6I"; frq1 = t_frequency::C2; frq2 = t_frequency::C6; return true; 218 case 'J': sig1 = "1C"; sig2 = "2L"; frq1 = t_frequency::J1; frq2 = t_frequency::J2; return true; 219 } 220 return false; 236 t_ssrRefSig::t_pair pair; 237 if (!t_ssrRefSig::get(sys, pair)) { 238 return false; 239 } 240 sig1 = QString::fromStdString(pair.sig1()); 241 sig2 = QString::fromStdString(pair.sig2()); 242 frq1 = pair.frq1; 243 frq2 = pair.frq2; 244 return true; 221 245 } 222 246 … … 376 400 _codeSignals[staSys].insert(sig); 377 401 values[sig] = frqBias._value; 402 } 403 if (!values.isEmpty()) { 404 _cbLast[QString::fromStdString(bias._staID)][prn] = values; 378 405 } 379 406 t_satStat& stat = _satStat[QString::fromStdString(bias._staID) + ' ' + prn]; … … 425 452 if (yawProvided) { 426 453 stat.numYaw++; 454 } 455 } 456 } 457 458 // Reference DCBs (bias(base) - bias(signal)) of a satellite: median of the 459 // other ACs (one vote per AC, own AC left out, combinations not voting) or 460 // the selected reference stream, or from the Bias SINEX file 461 //////////////////////////////////////////////////////////////////////////// 462 bool t_ssrQc::refDcbs(const QString& staID, const QString& prn, const QString& base, bool fromFile, 463 QMap<QString, double>& dcbs, const QString& onlyAC, QMap<QString, int>* numVotes) { 464 dcbs.clear(); 465 if (numVotes) { 466 numVotes->clear(); 467 } 468 if (fromFile) { 469 if (!_bsxRef) { 470 return false; 471 } 472 char sys = prn[0].toLatin1(); 473 t_ssrRefSig::t_pair pair; 474 if (!t_ssrRefSig::get(sys, pair)) { 475 return false; 476 } 477 // the DCBs do not depend on the coefficients (as long as they add up to 1) 478 double f1 = t_CST::freq(pair.frq1, 0.0); 479 double f2 = t_CST::freq(pair.frq2, 0.0); 480 double aIF1 = f1 * f1 / (f1 * f1 - f2 * f2); 481 double aIF2 = -f2 * f2 / (f1 * f1 - f2 * f2); 482 t_satCodeBias satCodeBias; 483 _bsxRef->determineSsrSatCodeBiases(prn, aIF1, aIF2, satCodeBias); 484 QMap<QString, double> osb; 485 for (const t_frqCodeBias& bb : satCodeBias._bias) { 486 osb[QString::fromStdString(bb._rnxType2ch)] = bb._value; 487 } 488 if (!osb.contains(base)) { 489 return false; 490 } 491 for (auto it = osb.constBegin(); it != osb.constEnd(); ++it) { 492 if (it.key() != base) { 493 dcbs[it.key()] = osb[base] - it.value(); 494 } 495 } 496 return !dcbs.isEmpty(); 497 } 498 499 // per AC: DCBs of its streams 500 QMap<QString, QMap<QString, QVector<double> > > acDcbs; // AC, signal 501 for (auto itS = _cbLast.constBegin(); itS != _cbLast.constEnd(); ++itS) { 502 const QString& sta = itS.key(); 503 if (sta == staID) { 504 continue; 505 } 506 if (_reference == "CONSENSUS") { 507 if (!votes(sta) || acOf(sta) == acOf(staID) || (!onlyAC.isEmpty() && acOf(sta) != onlyAC)) { 508 continue; 509 } 510 } 511 else if (sta != _reference) { 512 continue; 513 } 514 if (!itS.value().contains(prn)) { 515 continue; 516 } 517 const QMap<QString, double>& bb = itS.value()[prn]; 518 if (!bb.contains(base)) { 519 continue; 520 } 521 for (auto it = bb.constBegin(); it != bb.constEnd(); ++it) { 522 if (it.key() != base) { 523 acDcbs[acOf(sta)][it.key()].append(bb[base] - it.value()); 524 } 525 } 526 } 527 QMap<QString, QVector<double> > votesPerSig; 528 for (auto itA = acDcbs.constBegin(); itA != acDcbs.constEnd(); ++itA) { 529 for (auto it = itA.value().constBegin(); it != itA.value().constEnd(); ++it) { 530 votesPerSig[it.key()].append(median(it.value())); 531 } 532 } 533 for (auto it = votesPerSig.constBegin(); it != votesPerSig.constEnd(); ++it) { 534 dcbs[it.key()] = median(it.value()); 535 if (numVotes) { 536 (*numVotes)[it.key()] = it.value().size(); 537 } 538 } 539 return !dcbs.isEmpty(); 540 } 541 542 // Code bias check: the DCBs of the stream (relative to the first reference 543 // signal, otherwise to another common signal) compared with a reference; 544 // DCBs do not depend on the clock datum. A sign opposite to the reference 545 // for most DCBs indicates the SINEX instead of the SSR sign convention. 546 // References: the Bias SINEX file, the selected reference stream, or each 547 // other AC separately (a median of two ACs cannot decide which one is wrong) 548 //////////////////////////////////////////////////////////////////////////// 549 void t_ssrQc::compareCodeBiases(const QString& staID, char sys, QStringList& lines) { 550 const double MIN_DCB = 0.3; // [m] DCBs used for the sign check 551 const double MIN_DEV = 0.5; // [m] deviating DCBs: > max(MIN_DEV, DEV_FACTOR x median) 552 const int MIN_VOTE = 3; // ACs needed for a consensus DCB (deviating satellites) 553 if (!_cbLast.contains(staID)) { 554 return; 555 } 556 t_ssrRefSig::t_pair pair; 557 if (!t_ssrRefSig::get(sys, pair)) { 558 return; 559 } 560 const QString ref1 = QString::fromStdString(pair.sig1()); 561 562 class t_cmp { 563 public: 564 unsigned numSat = 0, numSign = 0, numOpp = 0; 565 double sumSqr = 0.0; 566 QVector<double> absDiffs; 567 QMap<QString, QPair<QString, double> > maxDiff; // PRN: signal, difference 568 QSet<QString> bases; 569 bool opposite() const {return numSign >= 5 && numOpp > numSign / 2;} 570 double rms() const {return absDiffs.isEmpty() ? 0.0 : sqrt(sumSqr / absDiffs.size());} 571 }; 572 // mode 0: file, 1: reference stream or median of the other ACs, 2: one other AC 573 auto compare = [&](int mode, const QString& onlyAC) { 574 t_cmp cc; 575 for (auto itP = _cbLast[staID].constBegin(); itP != _cbLast[staID].constEnd(); ++itP) { 576 const QString& prn = itP.key(); 577 if (prn[0].toLatin1() != sys) { 578 continue; 579 } 580 const QMap<QString, double>& bb = itP.value(); 581 QStringList candidates = QStringList() << ref1 << bb.keys(); 582 QMap<QString, double> dcbs; 583 QMap<QString, int> numVotes; 584 QString base; 585 for (const QString& cand : candidates) { 586 if (bb.contains(cand) && refDcbs(staID, prn, cand, mode == 0, dcbs, onlyAC, &numVotes)) { 587 base = cand; 588 break; 589 } 590 } 591 if (base.isEmpty()) { 592 continue; 593 } 594 bool used = false; 595 for (auto it = dcbs.constBegin(); it != dcbs.constEnd(); ++it) { 596 if (!bb.contains(it.key())) { 597 continue; 598 } 599 if (mode == 1 && _reference == "CONSENSUS" && numVotes.value(it.key()) < MIN_VOTE) { 600 continue; 601 } 602 double dcb = bb[base] - bb[it.key()]; 603 double diff = dcb - it.value(); 604 used = true; 605 cc.sumSqr += diff * diff; 606 cc.absDiffs.append(fabs(diff)); 607 if (fabs(diff) > fabs(cc.maxDiff.value(prn).second)) { 608 cc.maxDiff[prn] = qMakePair("C" + it.key(), diff); 609 } 610 if (fabs(it.value()) >= MIN_DCB) { 611 cc.numSign++; 612 if (dcb * it.value() < 0.0) { 613 cc.numOpp++; 614 } 615 } 616 } 617 if (used) { 618 cc.numSat++; 619 cc.bases.insert("C" + base); 620 } 621 } 622 return cc; 623 }; 624 auto baseStr = [](const t_cmp& cc) { 625 QStringList ll = cc.bases.values(); 626 ll.sort(); 627 return ll.join("/"); 628 }; 629 auto deviating = [&](const t_cmp& cc, const QString& refName) { 630 if (cc.absDiffs.isEmpty() || cc.opposite()) { 631 return; 632 } 633 double limit = std::max(MIN_DEV, DEV_FACTOR * median(cc.absDiffs)); 634 QStringList devs; 635 for (auto it = cc.maxDiff.constBegin(); it != cc.maxDiff.constEnd(); ++it) { 636 if (fabs(it.value().second) > limit) { 637 devs << QString("%1(%2 %3)").arg(it.key()).arg(it.value().first).arg(it.value().second, 0, 'f', 2); 638 } 639 } 640 if (!devs.isEmpty()) { 641 lines << QString("satellites with DCBs deviating from %1 [m]: %2").arg(refName).arg(devs.join(" ")); 642 } 643 }; 644 645 // Bias SINEX file or the selected reference stream 646 for (int mode = 0; mode < 2; mode++) { 647 if ((mode == 0 && !_bsxRef) || (mode == 1 && _reference == "CONSENSUS")) { 648 continue; 649 } 650 QString refName = (mode == 0) ? QString("Bias SINEX file %1").arg(_bsxName) : _reference; 651 t_cmp cc = compare(mode, QString()); 652 if (cc.absDiffs.isEmpty()) { 653 continue; 654 } 655 lines << QString("code biases compared with %1 (DCBs to %2, %3 satellites, %4 DCBs): RMS of the differences " 656 "%5 m, opposite sign for %6 of %7 DCBs (|DCB| >= %8 m)") 657 .arg(refName).arg(baseStr(cc)).arg(cc.numSat).arg(cc.absDiffs.size()) 658 .arg(cc.rms(), 0, 'f', 3).arg(cc.numOpp).arg(cc.numSign).arg(MIN_DCB, 0, 'f', 1); 659 if (cc.opposite()) { 660 lines << QString("code biases with the opposite sign of %1 for %2 of %3 DCBs - " 661 "SINEX sign convention instead of the SSR one?").arg(refName).arg(cc.numOpp).arg(cc.numSign); 662 } 663 deviating(cc, refName); 664 } 665 666 // Each other AC separately, and the median of the other ACs for deviating satellites 667 if (_reference == "CONSENSUS") { 668 QSet<QString> acs; 669 for (auto itS = _cbLast.constBegin(); itS != _cbLast.constEnd(); ++itS) { 670 if (itS.key() != staID && votes(itS.key()) && acOf(itS.key()) != acOf(staID)) { 671 acs.insert(acOf(itS.key())); 672 } 673 } 674 QStringList acList = acs.values(); 675 acList.sort(); 676 QStringList items; 677 QStringList oppACs; 678 int numACs = 0; 679 QSet<QString> bases; 680 for (const QString& ac : acList) { 681 t_cmp cc = compare(2, ac); 682 if (cc.absDiffs.isEmpty()) { 683 continue; 684 } 685 numACs++; 686 bases.unite(cc.bases); 687 items << QString("%1 RMS %2 m, opposite sign %3/%4").arg(ac).arg(cc.rms(), 0, 'f', 2) 688 .arg(cc.numOpp).arg(cc.numSign); 689 if (cc.opposite()) { 690 oppACs << ac; 691 } 692 } 693 if (numACs > 0) { 694 QStringList ll = bases.values(); 695 ll.sort(); 696 lines << QString("code biases compared with the other ACs (DCBs to %1): %2").arg(ll.join("/")).arg(items.join("; ")); 697 if (numACs >= 2 && oppACs.size() > numACs / 2) { 698 lines << QString("code biases with the opposite sign of %1 of %2 other ACs (%3) - " 699 "SINEX sign convention instead of the SSR one?") 700 .arg(oppACs.size()).arg(numACs).arg(oppACs.join(" ")); 701 } 702 deviating(compare(1, QString()), QString("the median of the other ACs (at least %1 ACs)").arg(MIN_VOTE)); 427 703 } 428 704 } … … 865 1141 } 866 1142 } 1143 compareCodeBiases(staID, sys, lines); 867 1144 if (_phaseSignals.contains(staSys)) { 868 1145 QStringList sigs = _phaseSignals[staSys].values(); -
trunk/BNC/src/ssrQc.h
r11085 r11087 14 14 // each interval and at the end of the program. 15 15 //////////////////////////////////////////////////////////////////////////// 16 class bncBiasSnx; 17 16 18 class t_ssrQc : public QObject { 17 19 Q_OBJECT … … 133 135 QMap<QString, QSet<QString> > _codeSignals; // key "STA SYS": signals with code biases 134 136 QMap<QString, QSet<QString> > _phaseSignals; // key "STA SYS": signals with phase biases 137 138 // Code bias check: DCBs of each stream compared with the other ACs (or 139 // the reference stream) and optionally with a Bias SINEX file 140 void compareCodeBiases(const QString& staID, char sys, QStringList& lines); 141 bool refDcbs(const QString& staID, const QString& prn, const QString& base, bool fromFile, 142 QMap<QString, double>& dcbs, const QString& onlyAC = QString(), 143 QMap<QString, int>* numVotes = 0); 144 QMap<QString, QMap<QString, QMap<QString, double> > > _cbLast; // stream, PRN, signal: latest code bias [m] 145 bncBiasSnx* _bsxRef; // reference code biases (option ssrQcBiasFile) 146 QString _bsxName; 135 147 }; 136 148 -
trunk/BNC/tests/README.md
r11083 r11087 66 66 encodings, missing reference-signal biases, inconsistent biases and 67 67 persistent outliers. The longest case (about 4 min). 68 * `combi_refsig`: `combi` with non-default reference signals (`ssrRefSignals 69 C:1P&5P`): BDS clock referral and code biases from the Bias SINEX file on 70 C1P/C5P (generic DCB derivation), CLK/BIA and the combination log compared. 68 71 * Upload (`upload_*`; 16, 19, 20) from `data/upload_rtnet_5min.raw` (RTNET engine 69 72 output + BCEP00BKG0): BNC uploads to a local fake caster … … 84 87 * `ssr_qc`, `ssr_qc_select`: SSR QC (31_SsrQC, one file per stream, 5 min interval; 85 88 all streams resp. `ssrQcStreams SSRA00CNE1,SSRA00GMV0`) of the 7 AC streams of 86 `data/combi_multi_261002_5min.raw`. 89 `data/combi_multi_261002_5min.raw`. `ssr_qc_bias` adds the CAS DCB file as 90 'QC Bias File' (code bias check against the other ACs and the file). 87 91 88 92 ## Adding a replay case
Note:
See TracChangeset
for help on using the changeset viewer.
