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

Last change on this file since 6345 was 6330, checked in by stuerze, 10 years ago

residual output for combination in filter mode added, combination result "dClkResult" removed from AC specific output because it is zero

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