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

Last change on this file since 7063 was 7055, checked in by stuerze, 10 years ago

IOD data type changed, to support IODs computed from CRC over broadcasted ephemris and clock parameters

File size: 34.3 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.mid(0,3);
119 }
120 else if (type == clkSat) {
121 outStr = "Clk Corr " + prn.mid(0,3);
122 }
123
124 return outStr;
125}
126
127// Constructor
128////////////////////////////////////////////////////////////////////////////
129bncComb::bncComb() : _ephUser(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_0").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_0").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_0").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_0").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("uploadAntexFile").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.toInternalString().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.toInternalString().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.mid(0,3).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 t_eph* ephLast = _ephUser.ephLast(prn);
385 t_eph* ephPrev = _ephUser.ephPrev(prn);
386 if (ephLast == 0) {
387 emit newMessage("bncComb: eph not found " + prn.mid(0,3).toAscii(), true);
388 delete newCorr;
389 continue;
390 }
391 else {
392 if (ephLast->IOD() == newCorr->_iod) {
393 newCorr->_eph = ephLast;
394 }
395 else if (ephPrev && ephPrev->IOD() == newCorr->_iod) {
396 newCorr->_eph = ephPrev;
397 switchToLastEph(ephLast, newCorr);
398 }
399 else {
400 emit newMessage("bncComb: eph not found " + prn.mid(0,3).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(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.mid(0,3)
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.mid(0,3);
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.mid(0,3)) {
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.mid(0,3);
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.mid(0,3) << " " << 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 t_orbCorr orbCorr(corr->_orbCorr);
715 orbCorr._staID = "INTERNAL";
716 orbCorrections.push_back(orbCorr);
717
718 t_clkCorr clkCorr(corr->_clkCorr);
719 clkCorr._staID = "INTERNAL";
720 clkCorr._dClk = corr->_dClkResult;
721 clkCorr._dotDClk = 0.0;
722 clkCorr._dotDotDClk = 0.0;
723 clkCorrections.push_back(clkCorr);
724
725 ColumnVector xc(4);
726 ColumnVector vv(3);
727 corr->_eph->setClkCorr(dynamic_cast<const t_clkCorr*>(&clkCorr));
728 corr->_eph->setOrbCorr(dynamic_cast<const t_orbCorr*>(&orbCorr));
729 corr->_eph->getCrd(_resTime, xc, vv, true);
730
731 // Correction Phase Center --> CoM
732 // -------------------------------
733 ColumnVector dx(3); dx = 0.0;
734 if (_antex) {
735 double Mjd = _resTime.mjd() + _resTime.daysec()/86400.0;
736 if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), dx) != success) {
737 dx = 0;
738 _log += "antenna not found " + corr->_prn.mid(0,3).toAscii() + '\n';
739 }
740 }
741
742 outLines += corr->_prn.mid(0,3);
743 QString hlp;
744 hlp.sprintf(" APC 3 %15.4f %15.4f %15.4f"
745 " Clk 1 %15.4f"
746 " Vel 3 %15.4f %15.4f %15.4f"
747 " CoM 3 %15.4f %15.4f %15.4f\n",
748 xc(1), xc(2), xc(3),
749 xc(4) * t_CST::c,
750 vv(1), vv(2), vv(3),
751 xc(1)-dx(1), xc(2)-dx(2), xc(3)-dx(3));
752 outLines += hlp;
753
754 QString line;
755 int messageType = COTYPE_GPSCOMBINED;
756 int updateInt = 0;
757 line.sprintf("%d %d %d %.1f %s"
758 " %lu"
759 " %8.3f %8.3f %8.3f %8.3f"
760 " %10.5f %10.5f %10.5f %10.5f"
761 " %10.5f INTERNAL",
762 messageType, updateInt, _resTime.gpsw(), _resTime.gpssec(),
763 corr->_prn.mid(0,3).toAscii().data(),
764 corr->_iod,
765 corr->_dClkResult * t_CST::c,
766 corr->_orbCorr._xr[0],
767 corr->_orbCorr._xr[1],
768 corr->_orbCorr._xr[2],
769 0.0,
770 corr->_orbCorr._dotXr[0],
771 corr->_orbCorr._dotXr[1],
772 corr->_orbCorr._dotXr[2],
773 0.0);
774 corrLines << line;
775
776 delete corr;
777 }
778
779 outLines += "EOE\n"; // End Of Epoch flag
780
781 if (!_rtnetDecoder) {
782 _rtnetDecoder = new bncRtnetDecoder();
783 }
784
785 vector<string> errmsg;
786 _rtnetDecoder->Decode(outLines.toAscii().data(), outLines.length(), errmsg);
787
788 // Send new Corrections to PPP etc.
789 // --------------------------------
790 if (orbCorrections.size() > 0 && clkCorrections.size() > 0) {
791 emit newOrbCorrections(orbCorrections);
792 emit newClkCorrections(clkCorrections);
793 }
794}
795
796// Create First Design Matrix and Vector of Measurements
797////////////////////////////////////////////////////////////////////////////
798t_irc bncComb::createAmat(Matrix& AA, ColumnVector& ll, DiagonalMatrix& PP,
799 const ColumnVector& x0,
800 QMap<QString, cmbCorr*>& resCorr) {
801
802 unsigned nPar = _params.size();
803 unsigned nObs = corrs().size();
804
805 if (nObs == 0) {
806 return failure;
807 }
808
809 int maxSat = t_prn::MAXPRN_GPS;
810// if (_useGlonass) {
811// maxSat = t_prn::MAXPRN_GPS + t_prn::MAXPRN_GLONASS;
812// }
813
814 const int nCon = (_method == filter) ? 1 + maxSat : 0;
815
816 AA.ReSize(nObs+nCon, nPar); AA = 0.0;
817 ll.ReSize(nObs+nCon); ll = 0.0;
818 PP.ReSize(nObs+nCon); PP = 1.0 / (sigObs * sigObs);
819
820 int iObs = 0;
821 QVectorIterator<cmbCorr*> itCorr(corrs());
822 while (itCorr.hasNext()) {
823 cmbCorr* corr = itCorr.next();
824 QString prn = corr->_prn;
825
826 ++iObs;
827
828 if (corr->_acName == _masterOrbitAC && resCorr.find(prn) == resCorr.end()) {
829 resCorr[prn] = new cmbCorr(*corr);
830 }
831
832 for (int iPar = 1; iPar <= _params.size(); iPar++) {
833 cmbParam* pp = _params[iPar-1];
834 AA(iObs, iPar) = pp->partial(corr->_acName, prn);
835 }
836
837 ll(iObs) = corr->_clkCorr._dClk * t_CST::c - DotProduct(AA.Row(iObs), x0);
838 }
839
840 // Regularization
841 // --------------
842 if (_method == filter) {
843 const double Ph = 1.e6;
844 PP(nObs+1) = Ph;
845 for (int iPar = 1; iPar <= _params.size(); iPar++) {
846 cmbParam* pp = _params[iPar-1];
847 if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
848 pp->type == cmbParam::clkSat ) {
849 AA(nObs+1, iPar) = 1.0;
850 }
851 }
852 int iCond = 1;
853 for (unsigned iGps = 1; iGps <= t_prn::MAXPRN_GPS; iGps++) {
854 QString prn = QString("G%1").arg(iGps, 2, 10, QChar('0'));
855 ++iCond;
856 PP(nObs+iCond) = Ph;
857 for (int iPar = 1; iPar <= _params.size(); iPar++) {
858 cmbParam* pp = _params[iPar-1];
859 if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
860 pp->type == cmbParam::offACSat &&
861 pp->prn == prn) {
862 AA(nObs+iCond, iPar) = 1.0;
863 }
864 }
865 }
866// if (_useGlonass) {
867// for (int iGlo = 1; iGlo <= t_prn::MAXPRN_GLONASS; iGlo++) {
868// QString prn = QString("R%1").arg(iGlo, 2, 10, QChar('0'));
869// ++iCond;
870// PP(nObs+iCond) = Ph;
871// for (int iPar = 1; iPar <= _params.size(); iPar++) {
872// cmbParam* pp = _params[iPar-1];
873// if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
874// pp->type == cmbParam::offACSat &&
875// pp->prn == prn) {
876// AA(nObs+iCond, iPar) = 1.0;
877// }
878// }
879// }
880// }
881 }
882
883 return success;
884}
885
886// Process Epoch - Single-Epoch Method
887////////////////////////////////////////////////////////////////////////////
888t_irc bncComb::processEpoch_singleEpoch(QTextStream& out,
889 QMap<QString, cmbCorr*>& resCorr,
890 ColumnVector& dx) {
891
892 // Check Satellite Positions for Outliers
893 // --------------------------------------
894 if (checkOrbits(out) != success) {
895 return failure;
896 }
897
898 // Outlier Detection Loop
899 // ----------------------
900 while (true) {
901
902 // Remove Satellites that are not in Master
903 // ----------------------------------------
904 QMutableVectorIterator<cmbCorr*> it(corrs());
905 while (it.hasNext()) {
906 cmbCorr* corr = it.next();
907 QString prn = corr->_prn;
908 bool foundMaster = false;
909 QVectorIterator<cmbCorr*> itHlp(corrs());
910 while (itHlp.hasNext()) {
911 cmbCorr* corrHlp = itHlp.next();
912 QString prnHlp = corrHlp->_prn;
913 QString ACHlp = corrHlp->_acName;
914 if (ACHlp == _masterOrbitAC && prn == prnHlp) {
915 foundMaster = true;
916 break;
917 }
918 }
919 if (!foundMaster) {
920 delete corr;
921 it.remove();
922 }
923 }
924
925 // Count Number of Observations per Satellite and per AC
926 // -----------------------------------------------------
927 QMap<QString, int> numObsPrn;
928 QMap<QString, int> numObsAC;
929 QVectorIterator<cmbCorr*> itCorr(corrs());
930 while (itCorr.hasNext()) {
931 cmbCorr* corr = itCorr.next();
932 QString prn = corr->_prn;
933 QString AC = corr->_acName;
934 if (numObsPrn.find(prn) == numObsPrn.end()) {
935 numObsPrn[prn] = 1;
936 }
937 else {
938 numObsPrn[prn] += 1;
939 }
940 if (numObsAC.find(AC) == numObsAC.end()) {
941 numObsAC[AC] = 1;
942 }
943 else {
944 numObsAC[AC] += 1;
945 }
946 }
947
948 // Clean-Up the Paramters
949 // ----------------------
950 for (int iPar = 1; iPar <= _params.size(); iPar++) {
951 delete _params[iPar-1];
952 }
953 _params.clear();
954
955 // Set new Parameters
956 // ------------------
957 int nextPar = 0;
958
959 QMapIterator<QString, int> itAC(numObsAC);
960 while (itAC.hasNext()) {
961 itAC.next();
962 const QString& AC = itAC.key();
963 int numObs = itAC.value();
964 if (AC != _masterOrbitAC && numObs > 0) {
965 _params.push_back(new cmbParam(cmbParam::offACgps, ++nextPar, AC, ""));
966 if (_useGlonass) {
967 _params.push_back(new cmbParam(cmbParam::offACglo, ++nextPar, AC, ""));
968 }
969 }
970 }
971
972 QMapIterator<QString, int> itPrn(numObsPrn);
973 while (itPrn.hasNext()) {
974 itPrn.next();
975 const QString& prn = itPrn.key();
976 int numObs = itPrn.value();
977 if (numObs > 0) {
978 _params.push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
979 }
980 }
981
982 int nPar = _params.size();
983 ColumnVector x0(nPar);
984 x0 = 0.0;
985
986 // Create First-Design Matrix
987 // --------------------------
988 Matrix AA;
989 ColumnVector ll;
990 DiagonalMatrix PP;
991 if (createAmat(AA, ll, PP, x0, resCorr) != success) {
992 return failure;
993 }
994
995 ColumnVector vv;
996 try {
997 Matrix ATP = AA.t() * PP;
998 SymmetricMatrix NN; NN << ATP * AA;
999 ColumnVector bb = ATP * ll;
1000 _QQ = NN.i();
1001 dx = _QQ * bb;
1002 vv = ll - AA * dx;
1003 }
1004 catch (Exception& exc) {
1005 out << exc.what() << endl;
1006 return failure;
1007 }
1008
1009 int maxResIndex;
1010 double maxRes = vv.maximum_absolute_value1(maxResIndex);
1011 out.setRealNumberNotation(QTextStream::FixedNotation);
1012 out.setRealNumberPrecision(3);
1013 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
1014 << " Maximum Residuum " << maxRes << ' '
1015 << corrs()[maxResIndex-1]->_acName << ' ' << corrs()[maxResIndex-1]->_prn;
1016
1017 if (maxRes > _MAXRES) {
1018 out << " Outlier" << endl;
1019 delete corrs()[maxResIndex-1];
1020 corrs().remove(maxResIndex-1);
1021 }
1022 else {
1023 out << " OK" << endl;
1024 out.setRealNumberNotation(QTextStream::FixedNotation);
1025 out.setRealNumberPrecision(3);
1026 for (int ii = 0; ii < vv.Nrows(); ii++) {
1027 const cmbCorr* corr = corrs()[ii];
1028 out << _resTime.datestr().c_str() << ' '
1029 << _resTime.timestr().c_str() << " "
1030 << corr->_acName << ' ' << corr->_prn.mid(0,3);
1031 out.setFieldWidth(6);
1032 out << " res = " << vv[ii] << endl;
1033 out.setFieldWidth(0);
1034 }
1035 return success;
1036 }
1037
1038 }
1039
1040 return failure;
1041}
1042
1043// Check Satellite Positions for Outliers
1044////////////////////////////////////////////////////////////////////////////
1045t_irc bncComb::checkOrbits(QTextStream& out) {
1046
1047 const double MAX_DISPLACEMENT = 0.20;
1048
1049 // Switch to last ephemeris (if possible)
1050 // --------------------------------------
1051 QMutableVectorIterator<cmbCorr*> im(corrs());
1052 while (im.hasNext()) {
1053 cmbCorr* corr = im.next();
1054 QString prn = corr->_prn;
1055
1056 t_eph* ephLast = _ephUser.ephLast(prn);
1057 t_eph* ephPrev = _ephUser.ephPrev(prn);
1058
1059 if (ephLast == 0) {
1060 out << "checkOrbit: missing eph (not found) " << corr->_prn.mid(0,3) << endl;
1061 delete corr;
1062 im.remove();
1063 }
1064 else if (corr->_eph == 0) {
1065 out << "checkOrbit: missing eph (zero) " << corr->_prn.mid(0,3) << endl;
1066 delete corr;
1067 im.remove();
1068 }
1069 else {
1070 if ( corr->_eph == ephLast || corr->_eph == ephPrev ) {
1071 switchToLastEph(ephLast, corr);
1072 }
1073 else {
1074 out << "checkOrbit: missing eph (deleted) " << corr->_prn.mid(0,3) << endl;
1075 delete corr;
1076 im.remove();
1077 }
1078 }
1079 }
1080
1081 while (true) {
1082
1083 // Compute Mean Corrections for all Satellites
1084 // -------------------------------------------
1085 QMap<QString, int> numCorr;
1086 QMap<QString, ColumnVector> meanRao;
1087 QVectorIterator<cmbCorr*> it(corrs());
1088 while (it.hasNext()) {
1089 cmbCorr* corr = it.next();
1090 QString prn = corr->_prn;
1091 if (meanRao.find(prn) == meanRao.end()) {
1092 meanRao[prn].ReSize(4);
1093 meanRao[prn].Rows(1,3) = corr->_orbCorr._xr;
1094 meanRao[prn](4) = 1;
1095 }
1096 else {
1097 meanRao[prn].Rows(1,3) += corr->_orbCorr._xr;
1098 meanRao[prn](4) += 1;
1099 }
1100 if (numCorr.find(prn) == numCorr.end()) {
1101 numCorr[prn] = 1;
1102 }
1103 else {
1104 numCorr[prn] += 1;
1105 }
1106 }
1107
1108 // Compute Differences wrt Mean, find Maximum
1109 // ------------------------------------------
1110 QMap<QString, cmbCorr*> maxDiff;
1111 it.toFront();
1112 while (it.hasNext()) {
1113 cmbCorr* corr = it.next();
1114 QString prn = corr->_prn;
1115 if (meanRao[prn](4) != 0) {
1116 meanRao[prn] /= meanRao[prn](4);
1117 meanRao[prn](4) = 0;
1118 }
1119 corr->_diffRao = corr->_orbCorr._xr - meanRao[prn].Rows(1,3);
1120 if (maxDiff.find(prn) == maxDiff.end()) {
1121 maxDiff[prn] = corr;
1122 }
1123 else {
1124 double normMax = maxDiff[prn]->_diffRao.norm_Frobenius();
1125 double norm = corr->_diffRao.norm_Frobenius();
1126 if (norm > normMax) {
1127 maxDiff[prn] = corr;
1128 }
1129 }
1130 }
1131
1132 if (_ACs.size() == 1) {
1133 break;
1134 }
1135
1136 // Remove Outliers
1137 // ---------------
1138 bool removed = false;
1139 QMutableVectorIterator<cmbCorr*> im(corrs());
1140 while (im.hasNext()) {
1141 cmbCorr* corr = im.next();
1142 QString prn = corr->_prn;
1143 if (numCorr[prn] < 2) {
1144 delete corr;
1145 im.remove();
1146 }
1147 else if (corr == maxDiff[prn]) {
1148 double norm = corr->_diffRao.norm_Frobenius();
1149 if (norm > MAX_DISPLACEMENT) {
1150 out << _resTime.datestr().c_str() << " "
1151 << _resTime.timestr().c_str() << " "
1152 << "Orbit Outlier: "
1153 << corr->_acName.toAscii().data() << " "
1154 << prn.mid(0,3).toAscii().data() << " "
1155 << corr->_iod << " "
1156 << norm << endl;
1157 delete corr;
1158 im.remove();
1159 removed = true;
1160 }
1161 }
1162 }
1163
1164 if (!removed) {
1165 break;
1166 }
1167 }
1168
1169 return success;
1170}
1171
1172//
1173////////////////////////////////////////////////////////////////////////////
1174void bncComb::slotProviderIDChanged(QString mountPoint) {
1175 QMutexLocker locker(&_mutex);
1176
1177 // Find the AC Name
1178 // ----------------
1179 QString acName;
1180 QListIterator<cmbAC*> icAC(_ACs);
1181 while (icAC.hasNext()) {
1182 cmbAC* AC = icAC.next();
1183 if (AC->mountPoint == mountPoint) {
1184 acName = AC->name;
1185 break;
1186 }
1187 }
1188 if (acName.isEmpty()) {
1189 return;
1190 }
1191
1192 // Remove all corrections of the corresponding AC
1193 // ----------------------------------------------
1194 QListIterator<bncTime> itTime(_buffer.keys());
1195 while (itTime.hasNext()) {
1196 bncTime epoTime = itTime.next();
1197 QVector<cmbCorr*>& corrVec = _buffer[epoTime].corrs;
1198 QMutableVectorIterator<cmbCorr*> it(corrVec);
1199 while (it.hasNext()) {
1200 cmbCorr* corr = it.next();
1201 if (acName == corr->_acName) {
1202 delete corr;
1203 it.remove();
1204 }
1205 }
1206 }
1207
1208 // Reset Satellite Offsets
1209 // -----------------------
1210 if (_method == filter) {
1211 for (int iPar = 1; iPar <= _params.size(); iPar++) {
1212 cmbParam* pp = _params[iPar-1];
1213 if (pp->AC == acName && pp->type == cmbParam::offACSat) {
1214 pp->xx = 0.0;
1215 _QQ.Row(iPar) = 0.0;
1216 _QQ.Column(iPar) = 0.0;
1217 _QQ(iPar,iPar) = pp->sig0 * pp->sig0;
1218 }
1219 }
1220 }
1221}
Note: See TracBrowser for help on using the repository browser.