source: ntrip/trunk/BNC/combination/bnccomb.cpp@ 3520

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