source: ntrip/trunk/BNC/src/combination/bnccomb.cpp@ 9646

Last change on this file since 9646 was 9635, checked in by stuerze, 3 years ago

consideration of incoming Code Biases during clock combination

File size: 39.5 KB
RevLine 
[2898]1/* -------------------------------------------------------------------------
2 * BKG NTRIP Client
3 * -------------------------------------------------------------------------
4 *
5 * Class: bncComb
6 *
7 * Purpose: Combinations of Orbit/Clock Corrections
8 *
9 * Author: L. Mervart
10 *
11 * Created: 22-Jan-2011
12 *
[7297]13 * Changes:
[2898]14 *
15 * -----------------------------------------------------------------------*/
16
[2933]17#include <newmatio.h>
[2921]18#include <iomanip>
[3214]19#include <sstream>
[9635]20#include <map>
[2898]21
22#include "bnccomb.h"
[5070]23#include "bnccore.h"
[3201]24#include "upload/bncrtnetdecoder.h"
[2910]25#include "bncsettings.h"
[2988]26#include "bncutils.h"
[3181]27#include "bncsp3.h"
[3052]28#include "bncantex.h"
[5742]29#include "t_prn.h"
[2898]30
[3455]31const double sig0_offAC = 1000.0;
32const double sig0_offACSat = 100.0;
[3468]33const double sigP_offACSat = 0.01;
[3455]34const double sig0_clkSat = 100.0;
35
36const double sigObs = 0.05;
37
38using namespace std;
39
[2898]40// Constructor
41////////////////////////////////////////////////////////////////////////////
[6155]42bncComb::cmbParam::cmbParam(parType type_, int index_, const QString& ac_, const QString& prn_) {
[3032]43
[3433]44 type = type_;
45 index = index_;
46 AC = ac_;
47 prn = prn_;
48 xx = 0.0;
[3455]49 eph = 0;
[3429]50
[9258]51 if (type == offACgnss) {
[3455]52 epoSpec = true;
53 sig0 = sig0_offAC;
54 sigP = sig0;
[3429]55 }
56 else if (type == offACSat) {
[3455]57 epoSpec = false;
58 sig0 = sig0_offACSat;
59 sigP = sigP_offACSat;
[3429]60 }
61 else if (type == clkSat) {
[3455]62 epoSpec = true;
63 sig0 = sig0_clkSat;
64 sigP = sig0;
[3429]65 }
[2933]66}
67
68// Destructor
69////////////////////////////////////////////////////////////////////////////
[6155]70bncComb::cmbParam::~cmbParam() {
[2933]71}
72
73// Partial
74////////////////////////////////////////////////////////////////////////////
[9258]75double bncComb::cmbParam::partial(char sys, const QString& AC_, const QString& prn_) {
[7297]76
[9258]77 if (type == offACgnss) {
78 if (AC == AC_ && prn_[0].toLatin1() == sys) {
[2934]79 return 1.0;
80 }
81 }
[3429]82 else if (type == offACSat) {
83 if (AC == AC_ && prn == prn_) {
[2933]84 return 1.0;
85 }
86 }
[3429]87 else if (type == clkSat) {
88 if (prn == prn_) {
[2933]89 return 1.0;
90 }
91 }
92
93 return 0.0;
94}
95
[7297]96//
[2990]97////////////////////////////////////////////////////////////////////////////
[9258]98QString bncComb::cmbParam::toString(char sys) const {
[2933]99
[2990]100 QString outStr;
[7297]101
[9258]102 if (type == offACgnss) {
[9262]103 outStr = "AC Offset " + AC + " " + sys + " ";
[2990]104 }
[3429]105 else if (type == offACSat) {
[7008]106 outStr = "Sat Offset " + AC + " " + prn.mid(0,3);
[2990]107 }
[3429]108 else if (type == clkSat) {
[7008]109 outStr = "Clk Corr " + prn.mid(0,3);
[2990]110 }
[2933]111
[2990]112 return outStr;
113}
114
[7299]115// Singleton
116////////////////////////////////////////////////////////////////////////////
117bncComb* bncComb::instance() {
118 static bncComb _bncComb;
119 return &_bncComb;
120}
121
[2933]122// Constructor
123////////////////////////////////////////////////////////////////////////////
[6443]124bncComb::bncComb() : _ephUser(true) {
[2910]125
126 bncSettings settings;
127
[7297]128 QStringList cmbStreams = settings.value("cmbStreams").toStringList();
[2918]129
[4183]130 _cmbSampl = settings.value("cmbSampl").toInt();
131 if (_cmbSampl <= 0) {
132 _cmbSampl = 10;
133 }
[9292]134 _useGps = (Qt::CheckState(settings.value("cmbGps").toInt()) == Qt::Checked) ? true : false;
135 if (_useGps) {
136 _cmbSysPrn['G'] = t_prn::MAXPRN_GPS;
137 _masterMissingEpochs['G'] = 0;
138 }
139 _useGlo = (Qt::CheckState(settings.value("cmbGlo").toInt()) == Qt::Checked) ? true : false;
140 if (_useGlo) {
141 _cmbSysPrn['R'] = t_prn::MAXPRN_GLONASS;
142 _masterMissingEpochs['R'] = 0;
143 }
144 _useGal = (Qt::CheckState(settings.value("cmbGal").toInt()) == Qt::Checked) ? true : false;
145 if (_useGal) {
146 _cmbSysPrn['E'] = t_prn::MAXPRN_GALILEO;
147 _masterMissingEpochs['E'] = 0;
148 }
149 _useBds = (Qt::CheckState(settings.value("cmbBds").toInt()) == Qt::Checked) ? true : false;
150 if (_useBds) {
151 _cmbSysPrn['C'] = t_prn::MAXPRN_BDS;
152 _masterMissingEpochs['C'] = 0;
153 }
154 _useQzss = (Qt::CheckState(settings.value("cmbQzss").toInt()) == Qt::Checked) ? true : false;
155 if (_useQzss) {
156 _cmbSysPrn['J'] = t_prn::MAXPRN_QZSS;
157 _masterMissingEpochs['J'] = 0;
158 }
159 _useSbas = (Qt::CheckState(settings.value("cmbSbas").toInt()) == Qt::Checked) ? true : false;
160 if (_useSbas) {
161 _cmbSysPrn['S'] = t_prn::MAXPRN_SBAS;
162 _masterMissingEpochs['S'] = 0;
163 }
164 _useIrnss = (Qt::CheckState(settings.value("cmbIrnss").toInt()) == Qt::Checked) ? true : false;
165 if (_useIrnss) {
166 _cmbSysPrn['I'] = t_prn::MAXPRN_IRNSS;
167 _masterMissingEpochs['I'] = 0;
168 }
[4183]169
[7297]170 if (cmbStreams.size() >= 1 && !cmbStreams[0].isEmpty()) {
171 QListIterator<QString> it(cmbStreams);
[2910]172 while (it.hasNext()) {
173 QStringList hlp = it.next().split(" ");
[2918]174 cmbAC* newAC = new cmbAC();
175 newAC->mountPoint = hlp[0];
176 newAC->name = hlp[1];
177 newAC->weight = hlp[2].toDouble();
[9258]178 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
179 // init
180 while (itSys.hasNext()) {
181 itSys.next();
182 char sys = itSys.key();
183 if (_masterOrbitAC.isEmpty()) {
184 _masterOrbitAC[sys] = newAC->name;
185 }
[3453]186 }
[3429]187 _ACs.append(newAC);
[2910]188 }
189 }
[2927]190
[9025]191 QString ssrFormat;
[9258]192 _ssrCorr = 0;
[9025]193 QListIterator<QString> it(settings.value("uploadMountpointsOut").toStringList());
194 while (it.hasNext()) {
195 QStringList hlp = it.next().split(",");
196 if (hlp.size() > 7) {
197 ssrFormat = hlp[7];
198 }
199 }
200 if (ssrFormat == "IGS-SSR") {
201 _ssrCorr = new SsrCorrIgs();
202 }
203 else if (ssrFormat == "RTCM-SSR") {
204 _ssrCorr = new SsrCorrRtcm();
205 }
[9258]206 else { // default
207 _ssrCorr = new SsrCorrIgs();
208 }
[9025]209
[3211]210 _rtnetDecoder = 0;
[3201]211
[7297]212 connect(this, SIGNAL(newMessage(QByteArray,bool)),
[5068]213 BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
[2933]214
[5583]215 connect(BNC_CORE, SIGNAL(providerIDChanged(QString)),
[6155]216 this, SLOT(slotProviderIDChanged(QString)));
[5583]217
[6155]218 connect(BNC_CORE, SIGNAL(newOrbCorrections(QList<t_orbCorr>)),
219 this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)));
220
221 connect(BNC_CORE, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
222 this, SLOT(slotNewClkCorrections(QList<t_clkCorr>)));
223
[9546]224 connect(BNC_CORE, SIGNAL(newCodeBiases(QList<t_satCodeBias>)),
225 this, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)));
226
[3470]227 // Combination Method
228 // ------------------
[3481]229 if (settings.value("cmbMethod").toString() == "Single-Epoch") {
230 _method = singleEpoch;
[3470]231 }
232 else {
[3481]233 _method = filter;
[3470]234 }
[3032]235
236 // Initialize Parameters (model: Clk_Corr = AC_Offset + Sat_Offset + Clk)
237 // ----------------------------------------------------------------------
[3470]238 if (_method == filter) {
[9258]239 // SYSTEM
240 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
241 while (itSys.hasNext()) {
242 itSys.next();
243 int nextPar = 0;
244 char sys = itSys.key();
245 unsigned maxPrn = itSys.value();
246 unsigned flag = 0;
247 if (sys == 'E') {
248 flag = 1;
[3470]249 }
[9258]250 // AC
251 QListIterator<cmbAC*> itAc(_ACs);
252 while (itAc.hasNext()) {
253 cmbAC* AC = itAc.next();
254 _params[sys].push_back(new cmbParam(cmbParam::offACgnss, ++nextPar, AC->name, ""));
255 for (unsigned iGnss = 1; iGnss <= maxPrn; iGnss++) {
256 QString prn = QString("%1%2_%3").arg(sys).arg(iGnss, 2, 10, QChar('0')).arg(flag);
257 _params[sys].push_back(new cmbParam(cmbParam::offACSat, ++nextPar, AC->name, prn));
[3483]258 }
259 }
[9258]260 for (unsigned iGnss = 1; iGnss <= maxPrn; iGnss++) {
261 QString prn = QString("%1%2_%3").arg(sys).arg(iGnss, 2, 10, QChar('0')).arg(flag);
262 _params[sys].push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
[3483]263 }
[9258]264 // Initialize Variance-Covariance Matrix
265 // -------------------------------------
266 _QQ[sys].ReSize(_params[sys].size());
267 _QQ[sys] = 0.0;
268 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
269 cmbParam* pp = _params[sys][iPar-1];
270 _QQ[sys](iPar,iPar) = pp->sig0 * pp->sig0;
271 }
[3483]272 }
[2991]273 }
[2933]274
[3052]275 // ANTEX File
276 // ----------
277 _antex = 0;
[6667]278 QString antexFileName = settings.value("uploadAntexFile").toString();
[3052]279 if (!antexFileName.isEmpty()) {
280 _antex = new bncAntex();
281 if (_antex->readFile(antexFileName) != success) {
282 emit newMessage("wrong ANTEX file", true);
283 delete _antex;
284 _antex = 0;
285 }
286 }
[3138]287
[3329]288 // Maximal Residuum
289 // ----------------
290 _MAXRES = settings.value("cmbMaxres").toDouble();
291 if (_MAXRES <= 0.0) {
292 _MAXRES = 999.0;
293 }
[2898]294}
295
296// Destructor
297////////////////////////////////////////////////////////////////////////////
298bncComb::~bncComb() {
[3429]299 QListIterator<cmbAC*> icAC(_ACs);
300 while (icAC.hasNext()) {
301 delete icAC.next();
[2918]302 }
[3201]303 delete _rtnetDecoder;
[9025]304 if (_ssrCorr) {
305 delete _ssrCorr;
306 }
[3052]307 delete _antex;
[9258]308 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
309 while (itSys.hasNext()) {
310 itSys.next();
311 char sys = itSys.key();
312 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
313 delete _params[sys][iPar-1];
314 }
315 QListIterator<bncTime> itTime(_buffer[sys].keys());
316 while (itTime.hasNext()) {
317 bncTime epoTime = itTime.next();
318 _buffer[sys].remove(epoTime);
319 }
[3296]320 }
[9258]321
[2898]322}
323
[6155]324// Remember orbit corrections
[2898]325////////////////////////////////////////////////////////////////////////////
[6155]326void bncComb::slotNewOrbCorrections(QList<t_orbCorr> orbCorrections) {
[2906]327 QMutexLocker locker(&_mutex);
[2913]328
[6155]329 for (int ii = 0; ii < orbCorrections.size(); ii++) {
330 t_orbCorr& orbCorr = orbCorrections[ii];
331 QString staID(orbCorr._staID.c_str());
[9296]332 char sys = orbCorr._prn.system();
[6155]333
[9296]334 if (!_cmbSysPrn.contains(sys)){
335 continue;
336 }
337
[6155]338 // Find/Check the AC Name
339 // ----------------------
340 QString acName;
341 QListIterator<cmbAC*> icAC(_ACs);
342 while (icAC.hasNext()) {
343 cmbAC* AC = icAC.next();
344 if (AC->mountPoint == staID) {
345 acName = AC->name;
346 break;
347 }
[3431]348 }
[6155]349 if (acName.isEmpty()) {
350 continue;
351 }
[3431]352
[6155]353 // Store the correction
354 // --------------------
355 QMap<t_prn, t_orbCorr>& storage = _orbCorrections[acName];
356 storage[orbCorr._prn] = orbCorr;
[2913]357 }
[6155]358}
[2913]359
[9529]360// Remember satllite code biases
361////////////////////////////////////////////////////////////////////////////
[9530]362void bncComb::slotNewCodeBiases(QList<t_satCodeBias> satCodeBiases) {
[9529]363 QMutexLocker locker(&_mutex);
364
[9530]365 for (int ii = 0; ii < satCodeBiases.size(); ii++) {
366 t_satCodeBias& satCodeBias = satCodeBiases[ii];
367 QString staID(satCodeBias._staID.c_str());
368 char sys = satCodeBias._prn.system();
[9529]369
370 if (!_cmbSysPrn.contains(sys)){
371 continue;
372 }
373
374 // Find/Check the AC Name
375 // ----------------------
376 QString acName;
377 QListIterator<cmbAC*> icAC(_ACs);
378 while (icAC.hasNext()) {
379 cmbAC* AC = icAC.next();
380 if (AC->mountPoint == staID) {
381 acName = AC->name;
382 break;
383 }
384 }
385 if (acName.isEmpty()) {
386 continue;
387 }
388
389 // Store the correction
390 // --------------------
391 QMap<t_prn, t_satCodeBias>& storage = _satCodeBiases[acName];
[9530]392 storage[satCodeBias._prn] = satCodeBias;
[9529]393 }
394}
395
[6155]396// Process clock corrections
397////////////////////////////////////////////////////////////////////////////
398void bncComb::slotNewClkCorrections(QList<t_clkCorr> clkCorrections) {
399 QMutexLocker locker(&_mutex);
400
401 bncTime lastTime;
402
403 for (int ii = 0; ii < clkCorrections.size(); ii++) {
404 t_clkCorr& clkCorr = clkCorrections[ii];
405 QString staID(clkCorr._staID.c_str());
[6961]406 QString prn(clkCorr._prn.toInternalString().c_str());
[9258]407 char sys = clkCorr._prn.system();
[6155]408
[9296]409 if (!_cmbSysPrn.contains(sys)){
410 continue;
411 }
412
[6155]413 // Set the last time
414 // -----------------
415 if (lastTime.undef() || clkCorr._time > lastTime) {
416 lastTime = clkCorr._time;
[3483]417 }
418
[6155]419 // Find/Check the AC Name
420 // ----------------------
421 QString acName;
422 QListIterator<cmbAC*> icAC(_ACs);
423 while (icAC.hasNext()) {
424 cmbAC* AC = icAC.next();
425 if (AC->mountPoint == staID) {
426 acName = AC->name;
427 break;
428 }
[3588]429 }
[6155]430 if (acName.isEmpty()) {
431 continue;
432 }
[3588]433
[6155]434 // Check Modulo Time
435 // -----------------
[8447]436 int sec = int(nint(clkCorr._time.gpssec()*10));
437 if (sec % (_cmbSampl*10) != 0.0) {
[6155]438 continue;
439 }
[3274]440
[6155]441 // Check Correction Age
442 // --------------------
443 if (_resTime.valid() && clkCorr._time <= _resTime) {
[8204]444 emit newMessage("bncComb: old correction: " + acName.toLatin1() + " " + prn.mid(0,3).toLatin1(), true);
[6155]445 continue;
446 }
[7297]447
[6155]448 // Create new correction
449 // ---------------------
450 cmbCorr* newCorr = new cmbCorr();
[6157]451 newCorr->_prn = prn;
[6155]452 newCorr->_time = clkCorr._time;
453 newCorr->_iod = clkCorr._iod;
454 newCorr->_acName = acName;
[6159]455 newCorr->_clkCorr = clkCorr;
[6155]456
[6156]457 // Check orbit correction
458 // ----------------------
459 if (!_orbCorrections.contains(acName)) {
460 delete newCorr;
461 continue;
462 }
463 else {
464 QMap<t_prn, t_orbCorr>& storage = _orbCorrections[acName];
[9258]465 if (!storage.contains(clkCorr._prn) ||
466 storage[clkCorr._prn]._iod != newCorr->_iod) {
[6156]467 delete newCorr;
468 continue;
469 }
470 else {
[6159]471 newCorr->_orbCorr = storage[clkCorr._prn];
[6156]472 }
473 }
474
[6155]475 // Check the Ephemeris
476 //--------------------
[7012]477 t_eph* ephLast = _ephUser.ephLast(prn);
478 t_eph* ephPrev = _ephUser.ephPrev(prn);
[6443]479 if (ephLast == 0) {
[9265]480 emit newMessage("bncComb: eph not found for " + prn.mid(0,3).toLatin1(), true);
[6155]481 delete newCorr;
482 continue;
[2986]483 }
[9268]484 else if (ephLast->checkState() == t_eph::bad ||
485 ephLast->checkState() == t_eph::outdated ||
486 ephLast->checkState() == t_eph::unhealthy) {
487 emit newMessage("bncComb: ephLast not ok for " + prn.mid(0,3).toLatin1(), true);
[9266]488 delete newCorr;
489 continue;
490 }
[3029]491 else {
[6443]492 if (ephLast->IOD() == newCorr->_iod) {
493 newCorr->_eph = ephLast;
[6155]494 }
[9268]495 else if (ephPrev && ephPrev->checkState() == t_eph::ok &&
496 ephPrev->IOD() == newCorr->_iod) {
[6443]497 newCorr->_eph = ephPrev;
498 switchToLastEph(ephLast, newCorr);
[6155]499 }
500 else {
[9265]501 emit newMessage("bncComb: eph not found for " + prn.mid(0,3).toLatin1() +
502 QString(" with IOD %1").arg(newCorr->_iod).toLatin1(), true);
[6155]503 delete newCorr;
504 continue;
505 }
[2986]506 }
[6155]507
[9635]508 // Check satellite code biases
509 // ----------------------------
510 if (!_satCodeBiases.contains(acName)) {
511 delete newCorr;
512 continue;
513 }
514 else {
515 QMap<t_prn, t_satCodeBias>& storage = _satCodeBiases[acName];
516 if (!storage.contains(clkCorr._prn)) {
517 delete newCorr;
518 continue;
519 }
520 else {
521 t_satCodeBias& satCodeBias = storage[clkCorr._prn];
522 QMap<t_frequency::type, double> codeBias;
523 for (unsigned ii = 1; ii < t_lcRefSig::cIF; ii++) {
524 t_frequency::type frqType = t_lcRefSig::toFreq(sys, static_cast<t_lcRefSig::type>(ii));
525 char frqNum = t_frequency::toString(frqType)[1];
526 char attrib = t_lcRefSig::toAttrib(sys, static_cast<t_lcRefSig::type>(ii));
527 QString obsType = QString("%1%2").arg(frqNum).arg(attrib);
528 for (unsigned ii = 0; ii < satCodeBias._bias.size(); ii++) {
529 const t_frqCodeBias& bias = satCodeBias._bias[ii];
530 if (obsType.toStdString() == bias._rnxType2ch) {
531 codeBias[frqType] = bias._value;
532 }
533 }
534 }
535 map<t_frequency::type, double> codeCoeff;
536 double channel = double(newCorr->_eph->slotNum());
537 t_lcRefSig::coeff(sys, t_lcRefSig::cIF, channel, codeCoeff);
538 map<t_frequency::type, double>::const_iterator it;
539 for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
540 t_frequency::type frqType = it->first;
541 newCorr->_codeBiasIF += it->second * codeBias[frqType];
542 }
543 }
544 cout << acName.toStdString() << " " << clkCorr._prn.toString().c_str() << " _codeBiasIF: " << newCorr->_codeBiasIF << endl;
545 }
546
[6155]547 // Store correction into the buffer
548 // --------------------------------
[9258]549 QVector<cmbCorr*>& corrs = _buffer[sys][newCorr->_time].corrs;
[6155]550 corrs.push_back(newCorr);
[2986]551 }
552
[3435]553 // Process previous Epoch(s)
554 // -------------------------
[7392]555 const double outWait = 1.0 * _cmbSampl;
[9258]556 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
557 while (itSys.hasNext()) {
558 itSys.next();
559 char sys = itSys.key();
560 QListIterator<bncTime> itTime(_buffer[sys].keys());
561 while (itTime.hasNext()) {
562 bncTime epoTime = itTime.next();
563 if (epoTime < lastTime - outWait) {
564 _resTime = epoTime;
565 processEpoch(sys);
566 }
[3435]567 }
[2927]568 }
[2898]569}
570
[7297]571// Change the correction so that it refers to last received ephemeris
[2986]572////////////////////////////////////////////////////////////////////////////
[7012]573void bncComb::switchToLastEph(t_eph* lastEph, cmbCorr* corr) {
[3028]574
[6155]575 if (corr->_eph == lastEph) {
[3429]576 return;
577 }
[9266]578
[8542]579 ColumnVector oldXC(6);
[2987]580 ColumnVector oldVV(3);
[9266]581 if (corr->_eph->getCrd(corr->_time, oldXC, oldVV, false) != success) {
582 return;
583 }
[2988]584
[8542]585 ColumnVector newXC(6);
[2987]586 ColumnVector newVV(3);
[9266]587 if (lastEph->getCrd(corr->_time, newXC, newVV, false) != success) {
588 return;
589 }
[2988]590
591 ColumnVector dX = newXC.Rows(1,3) - oldXC.Rows(1,3);
[2989]592 ColumnVector dV = newVV - oldVV;
593 double dC = newXC(4) - oldXC(4);
[9266]594
[2988]595 ColumnVector dRAO(3);
596 XYZ_to_RSW(newXC.Rows(1,3), newVV, dX, dRAO);
[2989]597
598 ColumnVector dDotRAO(3);
599 XYZ_to_RSW(newXC.Rows(1,3), newVV, dV, dDotRAO);
600
[6963]601 QString msg = "switch corr " + corr->_prn.mid(0,3)
[6155]602 + QString(" %1 -> %2 %3").arg(corr->_iod,3).arg(lastEph->IOD(),3).arg(dC*t_CST::c, 8, 'f', 4);
[3013]603
[8204]604 emit newMessage(msg.toLatin1(), false);
[3028]605
[6155]606 corr->_iod = lastEph->IOD();
607 corr->_eph = lastEph;
608
[6159]609 corr->_orbCorr._xr += dRAO;
610 corr->_orbCorr._dotXr += dDotRAO;
611 corr->_clkCorr._dClk -= dC;
[2986]612}
613
[3429]614// Process Epoch
[2928]615////////////////////////////////////////////////////////////////////////////
[9258]616void bncComb::processEpoch(char sys) {
[2918]617
[2990]618 _log.clear();
619
620 QTextStream out(&_log, QIODevice::WriteOnly);
621
[9635]622 out << "\n" << "Combination: " << sys << "\n"
623 << "--------------------------------" << "\n";
[2990]624
[3433]625 // Observation Statistics
626 // ----------------------
[3455]627 bool masterPresent = false;
[3433]628 QListIterator<cmbAC*> icAC(_ACs);
629 while (icAC.hasNext()) {
630 cmbAC* AC = icAC.next();
[9258]631 AC->numObs[sys] = 0;
632 QVectorIterator<cmbCorr*> itCorr(corrs(sys));
[3433]633 while (itCorr.hasNext()) {
634 cmbCorr* corr = itCorr.next();
[6157]635 if (corr->_acName == AC->name) {
[9258]636 AC->numObs[sys] += 1;
637 if (AC->name == _masterOrbitAC[sys]) {
[3453]638 masterPresent = true;
639 }
[3433]640 }
641 }
[9635]642 out << AC->name.toLatin1().data() << ": " << AC->numObs[sys] << "\n";
[3433]643 }
644
[3453]645 // If Master not present, switch to another one
646 // --------------------------------------------
[4889]647 const unsigned switchMasterAfterGap = 1;
[3456]648 if (masterPresent) {
[9258]649 _masterMissingEpochs[sys] = 0;
[3456]650 }
651 else {
[9258]652 ++_masterMissingEpochs[sys];
653 if (_masterMissingEpochs[sys] < switchMasterAfterGap) {
[9635]654 out << "Missing Master, Epoch skipped" << "\n";
[9258]655 _buffer[sys].remove(_resTime);
[3455]656 emit newMessage(_log, false);
657 return;
[3453]658 }
[3455]659 else {
[9258]660 _masterMissingEpochs[sys] = 0;
[3455]661 QListIterator<cmbAC*> icAC(_ACs);
662 while (icAC.hasNext()) {
663 cmbAC* AC = icAC.next();
[9258]664 if (AC->numObs[sys] > 0) {
[3455]665 out << "Switching Master AC "
[9258]666 << _masterOrbitAC[sys].toLatin1().data() << " --> "
[8204]667 << AC->name.toLatin1().data() << " "
[7297]668 << _resTime.datestr().c_str() << " "
[9635]669 << _resTime.timestr().c_str() << "\n";
[9258]670 _masterOrbitAC[sys] = AC->name;
[3455]671 break;
672 }
[3452]673 }
674 }
675 }
676
[6157]677 QMap<QString, cmbCorr*> resCorr;
[3472]678
679 // Perform the actual Combination using selected Method
680 // ----------------------------------------------------
681 t_irc irc;
[3475]682 ColumnVector dx;
[3472]683 if (_method == filter) {
[9258]684 irc = processEpoch_filter(sys, out, resCorr, dx);
[3472]685 }
686 else {
[9258]687 irc = processEpoch_singleEpoch(sys, out, resCorr, dx);
[3472]688 }
689
[3475]690 // Update Parameter Values, Print Results
691 // --------------------------------------
[3472]692 if (irc == success) {
[9258]693 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
694 cmbParam* pp = _params[sys][iPar-1];
[3475]695 pp->xx += dx(iPar);
696 if (pp->type == cmbParam::clkSat) {
697 if (resCorr.find(pp->prn) != resCorr.end()) {
[6160]698 resCorr[pp->prn]->_dClkResult = pp->xx / t_CST::c;
[3475]699 }
700 }
[7297]701 out << _resTime.datestr().c_str() << " "
[3475]702 << _resTime.timestr().c_str() << " ";
703 out.setRealNumberNotation(QTextStream::FixedNotation);
704 out.setFieldWidth(8);
705 out.setRealNumberPrecision(4);
[9258]706 out << pp->toString(sys) << " "
[9635]707 << pp->xx << " +- " << sqrt(_QQ[sys](pp->index,pp->index)) << "\n";
[3475]708 out.setFieldWidth(0);
709 }
[3472]710 printResults(out, resCorr);
711 dumpResults(resCorr);
712 }
713
714 // Delete Data, emit Message
715 // -------------------------
[9258]716 _buffer[sys].remove(_resTime);
[3472]717 emit newMessage(_log, false);
718}
719
720// Process Epoch - Filter Method
721////////////////////////////////////////////////////////////////////////////
[9258]722t_irc bncComb::processEpoch_filter(char sys, QTextStream& out,
[6157]723 QMap<QString, cmbCorr*>& resCorr,
[3475]724 ColumnVector& dx) {
[3472]725
[3429]726 // Prediction Step
727 // ---------------
[9258]728 int nPar = _params[sys].size();
[2933]729 ColumnVector x0(nPar);
[9258]730 for (int iPar = 1; iPar <= nPar; iPar++) {
731 cmbParam* pp = _params[sys][iPar-1];
[3455]732 if (pp->epoSpec) {
[3451]733 pp->xx = 0.0;
[9258]734 _QQ[sys].Row(iPar) = 0.0;
735 _QQ[sys].Column(iPar) = 0.0;
736 _QQ[sys](iPar,iPar) = pp->sig0 * pp->sig0;
[3451]737 }
[3455]738 else {
[9258]739 _QQ[sys](iPar,iPar) += pp->sigP * pp->sigP;
[3455]740 }
[2933]741 x0(iPar) = pp->xx;
742 }
743
[3487]744 // Check Satellite Positions for Outliers
745 // --------------------------------------
[9258]746 if (checkOrbits(sys, out) != success) {
[3487]747 return failure;
748 }
[3441]749
[3455]750 // Update and outlier detection loop
751 // ---------------------------------
[9258]752 SymmetricMatrix QQ_sav = _QQ[sys];
[3455]753 while (true) {
[3135]754
[3455]755 Matrix AA;
756 ColumnVector ll;
757 DiagonalMatrix PP;
[3429]758
[9258]759 if (createAmat(sys, AA, ll, PP, x0, resCorr) != success) {
[3472]760 return failure;
[3441]761 }
[2933]762
[6169]763 dx.ReSize(nPar); dx = 0.0;
[9258]764 kalman(AA, ll, PP, _QQ[sys], dx);
[6164]765
[3429]766 ColumnVector vv = ll - AA * dx;
[3080]767
[3429]768 int maxResIndex;
[7297]769 double maxRes = vv.maximum_absolute_value1(maxResIndex);
[3429]770 out.setRealNumberNotation(QTextStream::FixedNotation);
[7297]771 out.setRealNumberPrecision(3);
[3429]772 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
[3432]773 << " Maximum Residuum " << maxRes << ' '
[9258]774 << corrs(sys)[maxResIndex-1]->_acName << ' ' << corrs(sys)[maxResIndex-1]->_prn.mid(0,3);
[3432]775 if (maxRes > _MAXRES) {
[9258]776 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
777 cmbParam* pp = _params[sys][iPar-1];
[7297]778 if (pp->type == cmbParam::offACSat &&
[9258]779 pp->AC == corrs(sys)[maxResIndex-1]->_acName &&
780 pp->prn == corrs(sys)[maxResIndex-1]->_prn.mid(0,3)) {
[3432]781 QQ_sav.Row(iPar) = 0.0;
782 QQ_sav.Column(iPar) = 0.0;
[3455]783 QQ_sav(iPar,iPar) = pp->sig0 * pp->sig0;
[3432]784 }
785 }
786
[9635]787 out << " Outlier" << "\n";
[9258]788 _QQ[sys] = QQ_sav;
789 corrs(sys).remove(maxResIndex-1);
[3432]790 }
791 else {
[9635]792 out << " OK" << "\n";
[6330]793 out.setRealNumberNotation(QTextStream::FixedNotation);
794 out.setRealNumberPrecision(4);
[9258]795 for (int ii = 0; ii < corrs(sys).size(); ii++) {
796 const cmbCorr* corr = corrs(sys)[ii];
[6330]797 out << _resTime.datestr().c_str() << ' '
798 << _resTime.timestr().c_str() << " "
[6962]799 << corr->_acName << ' ' << corr->_prn.mid(0,3);
[6330]800 out.setFieldWidth(10);
[9635]801 out << " res = " << vv[ii] << "\n";
[6330]802 out.setFieldWidth(0);
803 }
[3432]804 break;
805 }
806 }
807
[3472]808 return success;
[2918]809}
[3011]810
[3201]811// Print results
[3011]812////////////////////////////////////////////////////////////////////////////
[3429]813void bncComb::printResults(QTextStream& out,
[6157]814 const QMap<QString, cmbCorr*>& resCorr) {
[3011]815
[6157]816 QMapIterator<QString, cmbCorr*> it(resCorr);
[3011]817 while (it.hasNext()) {
818 it.next();
[6157]819 cmbCorr* corr = it.value();
820 const t_eph* eph = corr->_eph;
[3015]821 if (eph) {
[8542]822 ColumnVector xc(6);
[6109]823 ColumnVector vv(3);
[9268]824 if (eph->getCrd(_resTime, xc, vv, false) != success) {
825 continue;
826 }
[7297]827 out << _resTime.datestr().c_str() << " "
[3429]828 << _resTime.timestr().c_str() << " ";
[3015]829 out.setFieldWidth(3);
[6962]830 out << "Full Clock " << corr->_prn.mid(0,3) << " " << corr->_iod << " ";
[3015]831 out.setFieldWidth(14);
[9635]832 out << (xc(4) + corr->_dClkResult) * t_CST::c << "\n";
[3370]833 out.setFieldWidth(0);
[3015]834 }
835 else {
[9635]836 out << "bncComb::printResuls bug" << "\n";
[3015]837 }
[3011]838 }
839}
[3202]840
841// Send results to RTNet Decoder and directly to PPP Client
842////////////////////////////////////////////////////////////////////////////
[6157]843void bncComb::dumpResults(const QMap<QString, cmbCorr*>& resCorr) {
[3202]844
[6161]845 QList<t_orbCorr> orbCorrections;
846 QList<t_clkCorr> clkCorrections;
847
[5337]848 QString outLines;
[3214]849 unsigned year, month, day, hour, minute;
850 double sec;
[3429]851 _resTime.civil_date(year, month, day);
852 _resTime.civil_time(hour, minute, sec);
[3214]853
[7297]854 outLines.sprintf("* %4d %2d %2d %d %d %12.8f\n",
[5337]855 year, month, day, hour, minute, sec);
[3214]856
[6157]857 QMapIterator<QString, cmbCorr*> it(resCorr);
[3202]858 while (it.hasNext()) {
859 it.next();
[6157]860 cmbCorr* corr = it.value();
[3202]861
[7013]862 t_orbCorr orbCorr(corr->_orbCorr);
863 orbCorr._staID = "INTERNAL";
864 orbCorrections.push_back(orbCorr);
865
866 t_clkCorr clkCorr(corr->_clkCorr);
867 clkCorr._staID = "INTERNAL";
868 clkCorr._dClk = corr->_dClkResult;
869 clkCorr._dotDClk = 0.0;
870 clkCorr._dotDotDClk = 0.0;
871 clkCorrections.push_back(clkCorr);
872
[8542]873 ColumnVector xc(6);
[4978]874 ColumnVector vv(3);
[7013]875 corr->_eph->setClkCorr(dynamic_cast<const t_clkCorr*>(&clkCorr));
876 corr->_eph->setOrbCorr(dynamic_cast<const t_orbCorr*>(&orbCorr));
[9268]877 if (corr->_eph->getCrd(_resTime, xc, vv, true) != success) {
878 delete corr;
879 continue;
880 }
[7012]881
[4978]882 // Correction Phase Center --> CoM
883 // -------------------------------
884 ColumnVector dx(3); dx = 0.0;
885 if (_antex) {
886 double Mjd = _resTime.mjd() + _resTime.daysec()/86400.0;
[6157]887 if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), dx) != success) {
[4992]888 dx = 0;
[8204]889 _log += "antenna not found " + corr->_prn.mid(0,3).toLatin1() + '\n';
[3214]890 }
891 }
[7012]892
893 outLines += corr->_prn.mid(0,3);
[5337]894 QString hlp;
895 hlp.sprintf(" APC 3 %15.4f %15.4f %15.4f"
896 " Clk 1 %15.4f"
897 " Vel 3 %15.4f %15.4f %15.4f"
898 " CoM 3 %15.4f %15.4f %15.4f\n",
[7012]899 xc(1), xc(2), xc(3),
900 xc(4) * t_CST::c,
[5337]901 vv(1), vv(2), vv(3),
902 xc(1)-dx(1), xc(2)-dx(2), xc(3)-dx(3));
903 outLines += hlp;
904
[4978]905 QString line;
[9025]906 int messageType = _ssrCorr->COTYPE_GPSCOMBINED;
[5564]907 int updateInt = 0;
[4978]908 line.sprintf("%d %d %d %.1f %s"
[7055]909 " %lu"
[4978]910 " %8.3f %8.3f %8.3f %8.3f"
911 " %10.5f %10.5f %10.5f %10.5f"
[5576]912 " %10.5f INTERNAL",
[4978]913 messageType, updateInt, _resTime.gpsw(), _resTime.gpssec(),
[8204]914 corr->_prn.mid(0,3).toLatin1().data(),
[6157]915 corr->_iod,
[6160]916 corr->_dClkResult * t_CST::c,
[6159]917 corr->_orbCorr._xr[0],
918 corr->_orbCorr._xr[1],
919 corr->_orbCorr._xr[2],
[6157]920 0.0,
[6159]921 corr->_orbCorr._dotXr[0],
922 corr->_orbCorr._dotXr[1],
923 corr->_orbCorr._dotXr[2],
[6157]924 0.0);
[4978]925
[3214]926 delete corr;
927 }
928
[5337]929 outLines += "EOE\n"; // End Of Epoch flag
930
[3215]931 if (!_rtnetDecoder) {
932 _rtnetDecoder = new bncRtnetDecoder();
933 }
[3221]934
[3215]935 vector<string> errmsg;
[8204]936 _rtnetDecoder->Decode(outLines.toLatin1().data(), outLines.length(), errmsg);
[3215]937
[5861]938 // Send new Corrections to PPP etc.
939 // --------------------------------
[6161]940 if (orbCorrections.size() > 0 && clkCorrections.size() > 0) {
941 emit newOrbCorrections(orbCorrections);
942 emit newClkCorrections(clkCorrections);
943 }
[3202]944}
[3455]945
946// Create First Design Matrix and Vector of Measurements
947////////////////////////////////////////////////////////////////////////////
[9258]948t_irc bncComb::createAmat(char sys, Matrix& AA, ColumnVector& ll, DiagonalMatrix& PP,
949 const ColumnVector& x0, QMap<QString, cmbCorr*>& resCorr) {
[3455]950
[9258]951 unsigned nPar = _params[sys].size();
952 unsigned nObs = corrs(sys).size();
[3455]953
954 if (nObs == 0) {
955 return failure;
956 }
957
[9258]958 int maxSat = _cmbSysPrn[sys];
[3455]959
[9259]960 const int nCon = (_method == filter) ? 1 + maxSat : 0;
[3483]961
[3455]962 AA.ReSize(nObs+nCon, nPar); AA = 0.0;
963 ll.ReSize(nObs+nCon); ll = 0.0;
964 PP.ReSize(nObs+nCon); PP = 1.0 / (sigObs * sigObs);
965
966 int iObs = 0;
[9258]967 QVectorIterator<cmbCorr*> itCorr(corrs(sys));
[3455]968 while (itCorr.hasNext()) {
969 cmbCorr* corr = itCorr.next();
[6157]970 QString prn = corr->_prn;
[3487]971
[3455]972 ++iObs;
973
[9258]974 if (corr->_acName == _masterOrbitAC[sys] && resCorr.find(prn) == resCorr.end()) {
[6157]975 resCorr[prn] = new cmbCorr(*corr);
[3455]976 }
977
[9258]978 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
979 cmbParam* pp = _params[sys][iPar-1];
980 AA(iObs, iPar) = pp->partial(sys, corr->_acName, prn);
[3455]981 }
982
[9635]983 ll(iObs) = (corr->_clkCorr._dClk * t_CST::c - corr->_codeBiasIF) - DotProduct(AA.Row(iObs), x0);
[3455]984 }
985
986 // Regularization
987 // --------------
[3474]988 if (_method == filter) {
989 const double Ph = 1.e6;
990 PP(nObs+1) = Ph;
[9258]991 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
992 cmbParam* pp = _params[sys][iPar-1];
[3465]993 if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
[3474]994 pp->type == cmbParam::clkSat ) {
995 AA(nObs+1, iPar) = 1.0;
[3455]996 }
997 }
[9258]998 unsigned flag = 0;
999 if (sys == 'E') {
1000 flag = 1;
1001 }
[9259]1002 if (sys == 'R') {
1003 return success;
1004 }
1005 int iCond = 1;
1006 // GNSS
[9258]1007 for (unsigned iGnss = 1; iGnss <= _cmbSysPrn[sys]; iGnss++) {
1008 QString prn = QString("%1%2_%3").arg(sys).arg(iGnss, 2, 10, QChar('0')).arg(flag);
[3474]1009 ++iCond;
1010 PP(nObs+iCond) = Ph;
[9258]1011 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
1012 cmbParam* pp = _params[sys][iPar-1];
[7299]1013 if ( pp &&
1014 AA.Column(iPar).maximum_absolute_value() > 0.0 &&
[7297]1015 pp->type == cmbParam::offACSat &&
[3474]1016 pp->prn == prn) {
1017 AA(nObs+iCond, iPar) = 1.0;
1018 }
1019 }
1020 }
[3455]1021 }
1022
1023 return success;
1024}
[3470]1025
1026// Process Epoch - Single-Epoch Method
1027////////////////////////////////////////////////////////////////////////////
[9258]1028t_irc bncComb::processEpoch_singleEpoch(char sys, QTextStream& out,
[6157]1029 QMap<QString, cmbCorr*>& resCorr,
[3475]1030 ColumnVector& dx) {
[3470]1031
[3487]1032 // Check Satellite Positions for Outliers
1033 // --------------------------------------
[9258]1034 if (checkOrbits(sys, out) != success) {
[3487]1035 return failure;
1036 }
1037
[3482]1038 // Outlier Detection Loop
1039 // ----------------------
1040 while (true) {
[7297]1041
[3482]1042 // Remove Satellites that are not in Master
1043 // ----------------------------------------
[9258]1044 QMutableVectorIterator<cmbCorr*> it(corrs(sys));
[3482]1045 while (it.hasNext()) {
1046 cmbCorr* corr = it.next();
[6157]1047 QString prn = corr->_prn;
[3482]1048 bool foundMaster = false;
[9258]1049 QVectorIterator<cmbCorr*> itHlp(corrs(sys));
[3482]1050 while (itHlp.hasNext()) {
1051 cmbCorr* corrHlp = itHlp.next();
[6157]1052 QString prnHlp = corrHlp->_prn;
1053 QString ACHlp = corrHlp->_acName;
[9258]1054 if (ACHlp == _masterOrbitAC[sys] && prn == prnHlp) {
[3482]1055 foundMaster = true;
1056 break;
1057 }
[3476]1058 }
[3482]1059 if (!foundMaster) {
[3556]1060 delete corr;
[3482]1061 it.remove();
1062 }
[3473]1063 }
[7297]1064
[3482]1065 // Count Number of Observations per Satellite and per AC
1066 // -----------------------------------------------------
1067 QMap<QString, int> numObsPrn;
1068 QMap<QString, int> numObsAC;
[9258]1069 QVectorIterator<cmbCorr*> itCorr(corrs(sys));
[3482]1070 while (itCorr.hasNext()) {
1071 cmbCorr* corr = itCorr.next();
[6157]1072 QString prn = corr->_prn;
1073 QString AC = corr->_acName;
[3482]1074 if (numObsPrn.find(prn) == numObsPrn.end()) {
1075 numObsPrn[prn] = 1;
1076 }
1077 else {
1078 numObsPrn[prn] += 1;
1079 }
1080 if (numObsAC.find(AC) == numObsAC.end()) {
1081 numObsAC[AC] = 1;
1082 }
1083 else {
1084 numObsAC[AC] += 1;
1085 }
[3476]1086 }
[7297]1087
[9258]1088 // Clean-Up the Parameters
1089 // -----------------------
1090 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
1091 delete _params[sys][iPar-1];
[3474]1092 }
[9258]1093 _params[sys].clear();
[7297]1094
[3482]1095 // Set new Parameters
1096 // ------------------
1097 int nextPar = 0;
[7297]1098
[3482]1099 QMapIterator<QString, int> itAC(numObsAC);
1100 while (itAC.hasNext()) {
1101 itAC.next();
1102 const QString& AC = itAC.key();
1103 int numObs = itAC.value();
[9258]1104 if (AC != _masterOrbitAC[sys] && numObs > 0) {
1105 _params[sys].push_back(new cmbParam(cmbParam::offACgnss, ++nextPar, AC, ""));
[3482]1106 }
[7297]1107 }
1108
[3482]1109 QMapIterator<QString, int> itPrn(numObsPrn);
1110 while (itPrn.hasNext()) {
1111 itPrn.next();
1112 const QString& prn = itPrn.key();
1113 int numObs = itPrn.value();
1114 if (numObs > 0) {
[9258]1115 _params[sys].push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
[3482]1116 }
[7297]1117 }
1118
[9258]1119 int nPar = _params[sys].size();
[7297]1120 ColumnVector x0(nPar);
[3482]1121 x0 = 0.0;
[7297]1122
[3482]1123 // Create First-Design Matrix
1124 // --------------------------
1125 Matrix AA;
1126 ColumnVector ll;
1127 DiagonalMatrix PP;
[9258]1128 if (createAmat(sys, AA, ll, PP, x0, resCorr) != success) {
[3482]1129 return failure;
[3476]1130 }
[7297]1131
[3482]1132 ColumnVector vv;
1133 try {
1134 Matrix ATP = AA.t() * PP;
1135 SymmetricMatrix NN; NN << ATP * AA;
1136 ColumnVector bb = ATP * ll;
[9258]1137 _QQ[sys] = NN.i();
1138 dx = _QQ[sys] * bb;
[3482]1139 vv = ll - AA * dx;
[3476]1140 }
[3482]1141 catch (Exception& exc) {
[9635]1142 out << exc.what() << "\n";
[3482]1143 return failure;
[3476]1144 }
[3474]1145
[3482]1146 int maxResIndex;
[7297]1147 double maxRes = vv.maximum_absolute_value1(maxResIndex);
[3482]1148 out.setRealNumberNotation(QTextStream::FixedNotation);
[7297]1149 out.setRealNumberPrecision(3);
[3482]1150 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
1151 << " Maximum Residuum " << maxRes << ' '
[9258]1152 << corrs(sys)[maxResIndex-1]->_acName << ' ' << corrs(sys)[maxResIndex-1]->_prn.mid(0,3);
[3474]1153
[3482]1154 if (maxRes > _MAXRES) {
[9635]1155 out << " Outlier" << "\n";
[9258]1156 delete corrs(sys)[maxResIndex-1];
1157 corrs(sys).remove(maxResIndex-1);
[3474]1158 }
[3482]1159 else {
[9635]1160 out << " OK" << "\n";
[3482]1161 out.setRealNumberNotation(QTextStream::FixedNotation);
[7297]1162 out.setRealNumberPrecision(3);
[3482]1163 for (int ii = 0; ii < vv.Nrows(); ii++) {
[9258]1164 const cmbCorr* corr = corrs(sys)[ii];
[7297]1165 out << _resTime.datestr().c_str() << ' '
[3482]1166 << _resTime.timestr().c_str() << " "
[6962]1167 << corr->_acName << ' ' << corr->_prn.mid(0,3);
[3482]1168 out.setFieldWidth(6);
[9635]1169 out << " res = " << vv[ii] << "\n";
[3482]1170 out.setFieldWidth(0);
1171 }
1172 return success;
[3474]1173 }
1174
[3473]1175 }
[3474]1176
[3482]1177 return failure;
[3470]1178}
[3487]1179
1180// Check Satellite Positions for Outliers
1181////////////////////////////////////////////////////////////////////////////
[9258]1182t_irc bncComb::checkOrbits(char sys, QTextStream& out) {
[3487]1183
[3489]1184 const double MAX_DISPLACEMENT = 0.20;
[3488]1185
[3502]1186 // Switch to last ephemeris (if possible)
1187 // --------------------------------------
[9258]1188 QMutableVectorIterator<cmbCorr*> im(corrs(sys));
[3501]1189 while (im.hasNext()) {
1190 cmbCorr* corr = im.next();
[6157]1191 QString prn = corr->_prn;
[6443]1192
[7012]1193 t_eph* ephLast = _ephUser.ephLast(prn);
1194 t_eph* ephPrev = _ephUser.ephPrev(prn);
[6443]1195
1196 if (ephLast == 0) {
[9635]1197 out << "checkOrbit: missing eph (not found) " << corr->_prn.mid(0,3) << "\n";
[3556]1198 delete corr;
[3501]1199 im.remove();
1200 }
[6157]1201 else if (corr->_eph == 0) {
[9635]1202 out << "checkOrbit: missing eph (zero) " << corr->_prn.mid(0,3) << "\n";
[3556]1203 delete corr;
[3503]1204 im.remove();
1205 }
[3502]1206 else {
[6443]1207 if ( corr->_eph == ephLast || corr->_eph == ephPrev ) {
1208 switchToLastEph(ephLast, corr);
[3502]1209 }
1210 else {
[9635]1211 out << "checkOrbit: missing eph (deleted) " << corr->_prn.mid(0,3) << "\n";
[3556]1212 delete corr;
[3502]1213 im.remove();
1214 }
1215 }
[3501]1216 }
1217
[3488]1218 while (true) {
1219
1220 // Compute Mean Corrections for all Satellites
1221 // -------------------------------------------
[3489]1222 QMap<QString, int> numCorr;
[3488]1223 QMap<QString, ColumnVector> meanRao;
[9258]1224 QVectorIterator<cmbCorr*> it(corrs(sys));
[3488]1225 while (it.hasNext()) {
1226 cmbCorr* corr = it.next();
[6157]1227 QString prn = corr->_prn;
[3488]1228 if (meanRao.find(prn) == meanRao.end()) {
1229 meanRao[prn].ReSize(4);
[6159]1230 meanRao[prn].Rows(1,3) = corr->_orbCorr._xr;
[7297]1231 meanRao[prn](4) = 1;
[3488]1232 }
1233 else {
[6159]1234 meanRao[prn].Rows(1,3) += corr->_orbCorr._xr;
[7297]1235 meanRao[prn](4) += 1;
[3488]1236 }
[3489]1237 if (numCorr.find(prn) == numCorr.end()) {
1238 numCorr[prn] = 1;
1239 }
1240 else {
1241 numCorr[prn] += 1;
1242 }
[3487]1243 }
[7297]1244
[9258]1245 // Compute Differences wrt. Mean, find Maximum
1246 // -------------------------------------------
[3488]1247 QMap<QString, cmbCorr*> maxDiff;
1248 it.toFront();
1249 while (it.hasNext()) {
1250 cmbCorr* corr = it.next();
[6157]1251 QString prn = corr->_prn;
[3488]1252 if (meanRao[prn](4) != 0) {
1253 meanRao[prn] /= meanRao[prn](4);
1254 meanRao[prn](4) = 0;
1255 }
[6159]1256 corr->_diffRao = corr->_orbCorr._xr - meanRao[prn].Rows(1,3);
[3488]1257 if (maxDiff.find(prn) == maxDiff.end()) {
1258 maxDiff[prn] = corr;
1259 }
1260 else {
[8901]1261 double normMax = maxDiff[prn]->_diffRao.NormFrobenius();
1262 double norm = corr->_diffRao.NormFrobenius();
[3488]1263 if (norm > normMax) {
1264 maxDiff[prn] = corr;
1265 }
[7297]1266 }
[3487]1267 }
[7297]1268
[3655]1269 if (_ACs.size() == 1) {
1270 break;
1271 }
1272
[3488]1273 // Remove Outliers
1274 // ---------------
1275 bool removed = false;
[9258]1276 QMutableVectorIterator<cmbCorr*> im(corrs(sys));
[3488]1277 while (im.hasNext()) {
1278 cmbCorr* corr = im.next();
[6157]1279 QString prn = corr->_prn;
[3489]1280 if (numCorr[prn] < 2) {
[3556]1281 delete corr;
[3489]1282 im.remove();
1283 }
1284 else if (corr == maxDiff[prn]) {
[8901]1285 double norm = corr->_diffRao.NormFrobenius();
[3488]1286 if (norm > MAX_DISPLACEMENT) {
[3497]1287 out << _resTime.datestr().c_str() << " "
1288 << _resTime.timestr().c_str() << " "
[7297]1289 << "Orbit Outlier: "
[8204]1290 << corr->_acName.toLatin1().data() << " "
1291 << prn.mid(0,3).toLatin1().data() << " "
[7297]1292 << corr->_iod << " "
[9635]1293 << norm << "\n";
[3556]1294 delete corr;
[3488]1295 im.remove();
1296 removed = true;
1297 }
1298 }
[3487]1299 }
[7297]1300
[3488]1301 if (!removed) {
1302 break;
1303 }
[3487]1304 }
1305
1306 return success;
1307}
[3588]1308
[7297]1309//
[3588]1310////////////////////////////////////////////////////////////////////////////
[5583]1311void bncComb::slotProviderIDChanged(QString mountPoint) {
1312 QMutexLocker locker(&_mutex);
1313
[8065]1314 QTextStream out(&_log, QIODevice::WriteOnly);
1315
[5583]1316 // Find the AC Name
1317 // ----------------
1318 QString acName;
1319 QListIterator<cmbAC*> icAC(_ACs);
1320 while (icAC.hasNext()) {
1321 cmbAC* AC = icAC.next();
1322 if (AC->mountPoint == mountPoint) {
1323 acName = AC->name;
[8204]1324 out << "Provider ID changed: AC " << AC->name.toLatin1().data() << " "
[8065]1325 << _resTime.datestr().c_str() << " "
[9635]1326 << _resTime.timestr().c_str() << "\n";
[5583]1327 break;
1328 }
1329 }
1330 if (acName.isEmpty()) {
1331 return;
1332 }
[9258]1333 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
1334 while (itSys.hasNext()) {
1335 itSys.next();
1336 char sys = itSys.key();
1337 // Remove all corrections of the corresponding AC
1338 // ----------------------------------------------
1339 QListIterator<bncTime> itTime(_buffer[sys].keys());
1340 while (itTime.hasNext()) {
1341 bncTime epoTime = itTime.next();
1342 QVector<cmbCorr*>& corrVec = _buffer[sys][epoTime].corrs;
1343 QMutableVectorIterator<cmbCorr*> it(corrVec);
1344 while (it.hasNext()) {
1345 cmbCorr* corr = it.next();
1346 if (acName == corr->_acName) {
1347 delete corr;
1348 it.remove();
1349 }
[5583]1350 }
1351 }
[5585]1352
[9258]1353 // Reset Satellite Offsets
1354 // -----------------------
1355 if (_method == filter) {
1356 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
1357 cmbParam* pp = _params[sys][iPar-1];
1358 if (pp->AC == acName && pp->type == cmbParam::offACSat) {
1359 pp->xx = 0.0;
1360 _QQ[sys].Row(iPar) = 0.0;
1361 _QQ[sys].Column(iPar) = 0.0;
1362 _QQ[sys](iPar,iPar) = pp->sig0 * pp->sig0;
1363 }
[5585]1364 }
1365 }
1366 }
[5583]1367}
Note: See TracBrowser for help on using the repository browser.