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

Last change on this file since 3519 was 3519, checked in by mervart, 12 years ago
File size: 30.8 KB
RevLine 
[2898]1/* -------------------------------------------------------------------------
2 * BKG NTRIP Client
3 * -------------------------------------------------------------------------
4 *
5 * Class: bncComb
6 *
7 * Purpose: Combinations of Orbit/Clock Corrections
8 *
9 * Author: L. Mervart
10 *
11 * Created: 22-Jan-2011
12 *
13 * Changes:
14 *
15 * -----------------------------------------------------------------------*/
16
[2933]17#include <newmatio.h>
[2921]18#include <iomanip>
[3214]19#include <sstream>
[2898]20
21#include "bnccomb.h"
22#include "bncapp.h"
[3201]23#include "upload/bncrtnetdecoder.h"
[2910]24#include "bncsettings.h"
[2933]25#include "bncmodel.h"
[2988]26#include "bncutils.h"
[3027]27#include "bncpppclient.h"
[3181]28#include "bncsp3.h"
[3052]29#include "bncantex.h"
[3055]30#include "bnctides.h"
[2898]31
[3455]32const int moduloTime = 10;
[2898]33
[3455]34const double sig0_offAC = 1000.0;
35const double sig0_offACSat = 100.0;
[3468]36const double sigP_offACSat = 0.01;
[3455]37const double sig0_clkSat = 100.0;
38
39const double sigObs = 0.05;
40
[3483]41const int MAXPRN_GPS = 32;
42const int MAXPRN_GLONASS = 24;
[3134]43
[3455]44using namespace std;
45
[2898]46// Constructor
47////////////////////////////////////////////////////////////////////////////
[3429]48cmbParam::cmbParam(parType type_, int index_,
49 const QString& ac_, const QString& prn_) {
[3032]50
[3433]51 type = type_;
52 index = index_;
53 AC = ac_;
54 prn = prn_;
55 xx = 0.0;
[3455]56 eph = 0;
[3429]57
[3485]58 if (type == offACgps) {
[3455]59 epoSpec = true;
60 sig0 = sig0_offAC;
61 sigP = sig0;
[3429]62 }
[3485]63 else if (type == offACglo) {
64 epoSpec = true;
65 sig0 = sig0_offAC;
66 sigP = sig0;
67 }
[3429]68 else if (type == offACSat) {
[3455]69 epoSpec = false;
70 sig0 = sig0_offACSat;
71 sigP = sigP_offACSat;
[3429]72 }
73 else if (type == clkSat) {
[3455]74 epoSpec = true;
75 sig0 = sig0_clkSat;
76 sigP = sig0;
[3429]77 }
[2933]78}
79
80// Destructor
81////////////////////////////////////////////////////////////////////////////
82cmbParam::~cmbParam() {
83}
84
85// Partial
86////////////////////////////////////////////////////////////////////////////
[3429]87double cmbParam::partial(const QString& AC_, const QString& prn_) {
[2933]88
[3485]89 if (type == offACgps) {
90 if (AC == AC_ && prn_[0] == 'G') {
[2934]91 return 1.0;
92 }
93 }
[3485]94 else if (type == offACglo) {
95 if (AC == AC_ && prn_[0] == 'R') {
96 return 1.0;
97 }
98 }
[3429]99 else if (type == offACSat) {
100 if (AC == AC_ && prn == prn_) {
[2933]101 return 1.0;
102 }
103 }
[3429]104 else if (type == clkSat) {
105 if (prn == prn_) {
[2933]106 return 1.0;
107 }
108 }
109
110 return 0.0;
111}
112
[2990]113//
114////////////////////////////////////////////////////////////////////////////
115QString cmbParam::toString() const {
[2933]116
[2990]117 QString outStr;
118
[3485]119 if (type == offACgps) {
120 outStr = "AC offset GPS " + AC;
[2990]121 }
[3485]122 else if (type == offACglo) {
123 outStr = "AC offset GLO " + AC;
124 }
[3429]125 else if (type == offACSat) {
[2992]126 outStr = "Sat Offset " + AC + " " + prn;
[2990]127 }
[3429]128 else if (type == clkSat) {
[2992]129 outStr = "Clk Corr " + prn;
[2990]130 }
[2933]131
[2990]132 return outStr;
133}
134
[2933]135// Constructor
136////////////////////////////////////////////////////////////////////////////
[2898]137bncComb::bncComb() {
[2910]138
139 bncSettings settings;
140
141 QStringList combineStreams = settings.value("combineStreams").toStringList();
[2918]142
[3455]143 _masterMissingEpochs = 0;
144
[3143]145 if (combineStreams.size() >= 1 && !combineStreams[0].isEmpty()) {
[2910]146 QListIterator<QString> it(combineStreams);
147 while (it.hasNext()) {
148 QStringList hlp = it.next().split(" ");
[2918]149 cmbAC* newAC = new cmbAC();
150 newAC->mountPoint = hlp[0];
151 newAC->name = hlp[1];
152 newAC->weight = hlp[2].toDouble();
[3453]153 if (_masterOrbitAC.isEmpty()) {
154 _masterOrbitAC = newAC->name;
155 }
[3429]156 _ACs.append(newAC);
[2910]157 }
158 }
[2927]159
[3211]160 _rtnetDecoder = 0;
[3201]161
[2990]162 connect(this, SIGNAL(newMessage(QByteArray,bool)),
163 ((bncApp*)qApp), SLOT(slotMessage(const QByteArray,bool)));
[2933]164
[3470]165 // Combination Method
166 // ------------------
[3481]167 if (settings.value("cmbMethod").toString() == "Single-Epoch") {
168 _method = singleEpoch;
[3470]169 }
170 else {
[3481]171 _method = filter;
[3470]172 }
[3032]173
[3484]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
[3032]183 // Initialize Parameters (model: Clk_Corr = AC_Offset + Sat_Offset + Clk)
184 // ----------------------------------------------------------------------
[3470]185 if (_method == filter) {
186 int nextPar = 0;
187 QListIterator<cmbAC*> it(_ACs);
188 while (it.hasNext()) {
189 cmbAC* AC = it.next();
[3485]190 _params.push_back(new cmbParam(cmbParam::offACgps, ++nextPar, AC->name, ""));
[3470]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 }
[3483]196 if (_useGlonass) {
[3485]197 _params.push_back(new cmbParam(cmbParam::offACglo, ++nextPar, AC->name, ""));
[3483]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 }
[3470]204 }
[3134]205 for (int iGps = 1; iGps <= MAXPRN_GPS; iGps++) {
206 QString prn = QString("G%1").arg(iGps, 2, 10, QChar('0'));
[3470]207 _params.push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
[2991]208 }
[3483]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 }
[3470]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 }
[2991]224 }
[2933]225
[3052]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 }
[3138]238
[3329]239 // Maximal Residuum
240 // ----------------
241 _MAXRES = settings.value("cmbMaxres").toDouble();
242 if (_MAXRES <= 0.0) {
243 _MAXRES = 999.0;
244 }
[2898]245}
246
247// Destructor
248////////////////////////////////////////////////////////////////////////////
249bncComb::~bncComb() {
[3429]250 QListIterator<cmbAC*> icAC(_ACs);
251 while (icAC.hasNext()) {
252 delete icAC.next();
[2918]253 }
[3201]254 delete _rtnetDecoder;
[3052]255 delete _antex;
[3296]256 for (int iPar = 1; iPar <= _params.size(); iPar++) {
257 delete _params[iPar-1];
258 }
[3434]259 QVectorIterator<cmbCorr*> itCorr(corrs());
[3429]260 while (itCorr.hasNext()) {
261 delete itCorr.next();
262 }
[2898]263}
264
[2928]265// Read and store one correction line
[2898]266////////////////////////////////////////////////////////////////////////////
267void bncComb::processCorrLine(const QString& staID, const QString& line) {
[2906]268 QMutexLocker locker(&_mutex);
[2913]269
[3431]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
[2918]285 // Read the Correction
286 // -------------------
[3429]287 cmbCorr* newCorr = new cmbCorr();
[3431]288 newCorr->acName = acName;
[2913]289 if (!newCorr->readLine(line) == success) {
290 delete newCorr;
291 return;
292 }
293
[3483]294 // Check Glonass
295 // -------------
296 if (!_useGlonass) {
297 if (newCorr->prn[0] == 'R') {
298 delete newCorr;
299 return;
300 }
301 }
302
[3274]303 // Check Modulo Time
304 // -----------------
305 if (int(newCorr->tt.gpssec()) % moduloTime != 0.0) {
306 delete newCorr;
307 return;
308 }
309
[3299]310 // Delete old corrections
311 // ----------------------
[3437]312 if (_resTime.valid() && newCorr->tt <= _resTime) {
[2924]313 delete newCorr;
314 return;
315 }
316
[3029]317 // Check the Ephemeris
318 //--------------------
[2986]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;
[3029]326 if (lastEph && lastEph->IOD() == newCorr->iod) {
327 newCorr->eph = lastEph;
[2986]328 }
[3501]329 else if (lastEph && prevEph && prevEph->IOD() == newCorr->iod) {
[3029]330 newCorr->eph = prevEph;
[3429]331 switchToLastEph(lastEph, newCorr);
[3029]332 }
333 else {
[2986]334 delete newCorr;
335 return;
336 }
337 }
338
[3435]339 // Process previous Epoch(s)
340 // -------------------------
341 QListIterator<bncTime> itTime(_buffer.keys());
342 while (itTime.hasNext()) {
343 bncTime epoTime = itTime.next();
[3438]344 if (epoTime < newCorr->tt - moduloTime) {
[3435]345 _resTime = epoTime;
346 processEpoch();
347 }
[2927]348 }
349
[3429]350 // Merge or add the correction
351 // ---------------------------
[3435]352 QVector<cmbCorr*>& corrs = _buffer[newCorr->tt].corrs;
[3429]353 cmbCorr* existingCorr = 0;
[3435]354 QVectorIterator<cmbCorr*> itCorr(corrs);
[3429]355 while (itCorr.hasNext()) {
356 cmbCorr* hlp = itCorr.next();
357 if (hlp->prn == newCorr->prn && hlp->acName == newCorr->prn) {
358 existingCorr = hlp;
[2918]359 break;
360 }
361 }
[3429]362 if (existingCorr) {
[3298]363 delete newCorr;
[3429]364 existingCorr->readLine(line); // merge (multiple messages)
[2918]365 }
[2919]366 else {
[3435]367 corrs.append(newCorr);
[2919]368 }
[2898]369}
370
[2986]371// Change the correction so that it refers to last received ephemeris
372////////////////////////////////////////////////////////////////////////////
[3029]373void bncComb::switchToLastEph(const t_eph* lastEph, t_corr* corr) {
[3028]374
[3429]375 if (corr->eph == lastEph) {
376 return;
377 }
378
[2987]379 ColumnVector oldXC(4);
380 ColumnVector oldVV(3);
[3029]381 corr->eph->position(corr->tt.gpsw(), corr->tt.gpssec(),
382 oldXC.data(), oldVV.data());
[2988]383
[2987]384 ColumnVector newXC(4);
385 ColumnVector newVV(3);
[3029]386 lastEph->position(corr->tt.gpsw(), corr->tt.gpssec(),
[2987]387 newXC.data(), newVV.data());
[2988]388
389 ColumnVector dX = newXC.Rows(1,3) - oldXC.Rows(1,3);
[2989]390 ColumnVector dV = newVV - oldVV;
391 double dC = newXC(4) - oldXC(4);
392
[2988]393 ColumnVector dRAO(3);
394 XYZ_to_RSW(newXC.Rows(1,3), newVV, dX, dRAO);
[2989]395
396 ColumnVector dDotRAO(3);
397 XYZ_to_RSW(newXC.Rows(1,3), newVV, dV, dDotRAO);
398
[3455]399 QString msg = "switch corr " + corr->prn
[3029]400 + QString(" %1 -> %2 %3").arg(corr->iod,3)
[3013]401 .arg(lastEph->IOD(),3).arg(dC*t_CST::c, 8, 'f', 4);
402
[3029]403 emit newMessage(msg.toAscii(), false);
[3028]404
[3029]405 corr->iod = lastEph->IOD();
406 corr->eph = lastEph;
[3031]407 corr->rao += dRAO;
408 corr->dotRao += dDotRAO;
[3029]409 corr->dClk -= dC;
[2986]410}
411
[3429]412// Process Epoch
[2928]413////////////////////////////////////////////////////////////////////////////
[3429]414void bncComb::processEpoch() {
[2918]415
[2990]416 _log.clear();
417
418 QTextStream out(&_log, QIODevice::WriteOnly);
419
[3472]420 out << endl << "Combination:" << endl
421 << "------------------------------" << endl;
[2990]422
[3433]423 // Observation Statistics
424 // ----------------------
[3455]425 bool masterPresent = false;
[3433]426 QListIterator<cmbAC*> icAC(_ACs);
427 while (icAC.hasNext()) {
428 cmbAC* AC = icAC.next();
429 AC->numObs = 0;
[3434]430 QVectorIterator<cmbCorr*> itCorr(corrs());
[3433]431 while (itCorr.hasNext()) {
432 cmbCorr* corr = itCorr.next();
433 if (corr->acName == AC->name) {
434 AC->numObs += 1;
[3453]435 if (AC->name == _masterOrbitAC) {
436 masterPresent = true;
437 }
[3433]438 }
439 }
440 out << AC->name.toAscii().data() << ": " << AC->numObs << endl;
441 }
442
[3453]443 // If Master not present, switch to another one
444 // --------------------------------------------
[3456]445 if (masterPresent) {
446 _masterMissingEpochs = 0;
447 }
448 else {
[3455]449 ++_masterMissingEpochs;
[3459]450 if (_masterMissingEpochs < 10) {
[3457]451 out << "Missing Master, Epoch skipped" << endl;
[3455]452 _buffer.remove(_resTime);
453 emit newMessage(_log, false);
454 return;
[3453]455 }
[3455]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 }
[3452]470 }
471 }
472 }
473
[3472]474 QMap<QString, t_corr*> resCorr;
475
476 // Perform the actual Combination using selected Method
477 // ----------------------------------------------------
478 t_irc irc;
[3475]479 ColumnVector dx;
[3472]480 if (_method == filter) {
[3475]481 irc = processEpoch_filter(out, resCorr, dx);
[3472]482 }
483 else {
[3475]484 irc = processEpoch_singleEpoch(out, resCorr, dx);
[3472]485 }
486
[3475]487 // Update Parameter Values, Print Results
488 // --------------------------------------
[3472]489 if (irc == success) {
[3475]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 }
[3472]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,
[3475]520 QMap<QString, t_corr*>& resCorr,
521 ColumnVector& dx) {
[3472]522
[3429]523 // Prediction Step
524 // ---------------
[3472]525 int nPar = _params.size();
[2933]526 ColumnVector x0(nPar);
527 for (int iPar = 1; iPar <= _params.size(); iPar++) {
[3455]528 cmbParam* pp = _params[iPar-1];
529 if (pp->epoSpec) {
[3451]530 pp->xx = 0.0;
[3455]531 _QQ.Row(iPar) = 0.0;
532 _QQ.Column(iPar) = 0.0;
533 _QQ(iPar,iPar) = pp->sig0 * pp->sig0;
[3451]534 }
[3455]535 else {
536 _QQ(iPar,iPar) += pp->sigP * pp->sigP;
537 }
[2933]538 x0(iPar) = pp->xx;
539 }
540
[3487]541 // Check Satellite Positions for Outliers
542 // --------------------------------------
[3497]543 if (checkOrbits(out) != success) {
[3487]544 return failure;
545 }
[3441]546
[3455]547 // Update and outlier detection loop
548 // ---------------------------------
[3487]549 SymmetricMatrix QQ_sav = _QQ;
[3455]550 while (true) {
[3135]551
[3455]552 Matrix AA;
553 ColumnVector ll;
554 DiagonalMatrix PP;
[3429]555
[3455]556 if (createAmat(AA, ll, PP, x0, resCorr) != success) {
[3472]557 return failure;
[3441]558 }
[2933]559
[3429]560 bncModel::kalman(AA, ll, PP, _QQ, dx);
561 ColumnVector vv = ll - AA * dx;
[3080]562
[3429]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()
[3432]568 << " Maximum Residuum " << maxRes << ' '
[3434]569 << corrs()[maxResIndex-1]->acName << ' ' << corrs()[maxResIndex-1]->prn;
[3080]570
[3432]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 &&
[3434]575 pp->AC == corrs()[maxResIndex-1]->acName &&
576 pp->prn == corrs()[maxResIndex-1]->prn) {
[3432]577 QQ_sav.Row(iPar) = 0.0;
578 QQ_sav.Column(iPar) = 0.0;
[3455]579 QQ_sav(iPar,iPar) = pp->sig0 * pp->sig0;
[3432]580 }
581 }
582
583 out << " Outlier" << endl;
584 _QQ = QQ_sav;
[3455]585 corrs().remove(maxResIndex-1);
[3432]586 }
587 else {
588 out << " OK" << endl;
589 break;
590 }
591 }
592
[3472]593 return success;
[2918]594}
[3011]595
[3201]596// Print results
[3011]597////////////////////////////////////////////////////////////////////////////
[3429]598void bncComb::printResults(QTextStream& out,
[3011]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();
[3029]605 const t_eph* eph = corr->eph;
[3015]606 if (eph) {
607 double xx, yy, zz, cc;
[3429]608 eph->position(_resTime.gpsw(), _resTime.gpssec(), xx, yy, zz, cc);
[3015]609
[3429]610 out << _resTime.datestr().c_str() << " "
611 << _resTime.timestr().c_str() << " ";
[3015]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;
[3370]616 out.setFieldWidth(0);
[3015]617 }
618 else {
619 out << "bncComb::printResuls bug" << endl;
620 }
[3011]621 }
622}
[3202]623
624// Send results to RTNet Decoder and directly to PPP Client
625////////////////////////////////////////////////////////////////////////////
[3429]626void bncComb::dumpResults(const QMap<QString, t_corr*>& resCorr) {
[3202]627
[3214]628 ostringstream out; out.setf(std::ios::fixed);
[3216]629 QStringList corrLines;
[3214]630
631 unsigned year, month, day, hour, minute;
632 double sec;
[3429]633 _resTime.civil_date(year, month, day);
634 _resTime.civil_time(hour, minute, sec);
[3214]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
[3202]645 QMapIterator<QString, t_corr*> it(resCorr);
646 while (it.hasNext()) {
647 it.next();
648 t_corr* corr = it.value();
649
[3214]650 double dT = 60.0;
[3202]651
[3214]652 for (int iTime = 1; iTime <= 2; iTime++) {
653
[3429]654 bncTime time12 = (iTime == 1) ? _resTime : _resTime + dT;
[3214]655
656 ColumnVector xc(4);
657 ColumnVector vv(3);
658 corr->eph->position(time12.gpsw(), time12.gpssec(),
659 xc.data(), vv.data());
[3519]660 bncPPPclient::applyCorr(time12, corr, xc, vv, corr->eph);
[3214]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;
[3216]700
[3218]701 QString line;
[3217]702 int messageType = COTYPE_GPSCOMBINED;
[3218]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"
[3262]708 " %10.5f %10.5f %10.5f %10.5f INTERNAL",
[3218]709 messageType, updateInt, time12.gpsw(), time12.gpssec(),
710 corr->prn.toAscii().data(),
711 corr->iod,
[3219]712 corr->dClk * t_CST::c,
[3218]713 corr->rao[0],
714 corr->rao[1],
715 corr->rao[2],
[3219]716 corr->dotDClk * t_CST::c,
[3218]717 corr->dotRao[0],
718 corr->dotRao[1],
719 corr->dotRao[2],
[3262]720 corr->dotDotDClk * t_CST::c,
721 corr->dotDotRao[0],
722 corr->dotDotRao[1],
723 corr->dotDotRao[2]);
[3220]724 corrLines << line;
[3214]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 }
[3228]735 out << "EOE" << endl; // End Of Epoch flag
[3214]736
[3215]737 if (!_rtnetDecoder) {
738 _rtnetDecoder = new bncRtnetDecoder();
739 }
[3221]740
[3215]741 vector<string> errmsg;
[3221]742 _rtnetDecoder->Decode((char*) out.str().data(), out.str().size(), errmsg);
[3215]743
[3216]744 // Optionally send new Corrections to PPP
745 // --------------------------------------
746 bncApp* app = (bncApp*) qApp;
747 if (app->_bncPPPclient) {
748 app->_bncPPPclient->slotNewCorrections(corrLines);
749 }
[3202]750}
[3455]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
[3483]765 int MAXPRN = MAXPRN_GPS;
[3486]766// if (_useGlonass) {
767// MAXPRN = MAXPRN_GPS + MAXPRN_GLONASS;
768// }
[3455]769
[3483]770 const int nCon = (_method == filter) ? 1 + MAXPRN : 0;
771
[3455]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;
[3487]782
[3455]783 ++iObs;
784
[3476]785 if (corr->acName == _masterOrbitAC && resCorr.find(prn) == resCorr.end()) {
786 resCorr[prn] = new t_corr(*corr);
[3455]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 // --------------
[3474]799 if (_method == filter) {
800 const double Ph = 1.e6;
801 PP(nObs+1) = Ph;
[3461]802 for (int iPar = 1; iPar <= _params.size(); iPar++) {
803 cmbParam* pp = _params[iPar-1];
[3465]804 if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
[3474]805 pp->type == cmbParam::clkSat ) {
806 AA(nObs+1, iPar) = 1.0;
[3455]807 }
808 }
[3474]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 }
[3486]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// }
[3455]838 }
839
840 return success;
841}
[3470]842
843// Process Epoch - Single-Epoch Method
844////////////////////////////////////////////////////////////////////////////
[3472]845t_irc bncComb::processEpoch_singleEpoch(QTextStream& out,
[3475]846 QMap<QString, t_corr*>& resCorr,
847 ColumnVector& dx) {
[3470]848
[3487]849 // Check Satellite Positions for Outliers
850 // --------------------------------------
[3497]851 if (checkOrbits(out) != success) {
[3487]852 return failure;
853 }
854
[3482]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 }
[3476]875 }
[3482]876 if (!foundMaster) {
877 it.remove();
878 }
[3473]879 }
[3482]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 }
[3476]902 }
[3482]903
904 // Clean-Up the Paramters
905 // ----------------------
906 for (int iPar = 1; iPar <= _params.size(); iPar++) {
907 delete _params[iPar-1];
[3474]908 }
[3482]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) {
[3485]921 _params.push_back(new cmbParam(cmbParam::offACgps, ++nextPar, AC, ""));
922 if (_useGlonass) {
923 _params.push_back(new cmbParam(cmbParam::offACglo, ++nextPar, AC, ""));
924 }
[3482]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;
[3476]949 }
[3482]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;
[3476]959 }
[3482]960 catch (Exception& exc) {
961 out << exc.what() << endl;
962 return failure;
[3476]963 }
[3474]964
[3482]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;
[3474]972
[3482]973 if (maxRes > _MAXRES) {
974 out << " Outlier" << endl;
975 corrs().remove(maxResIndex-1);
[3474]976 }
[3482]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;
[3474]991 }
992
[3473]993 }
[3474]994
[3482]995 return failure;
[3470]996}
[3487]997
998// Check Satellite Positions for Outliers
999////////////////////////////////////////////////////////////////////////////
[3497]1000t_irc bncComb::checkOrbits(QTextStream& out) {
[3487]1001
[3489]1002 const double MAX_DISPLACEMENT = 0.20;
[3488]1003
[3502]1004 // Switch to last ephemeris (if possible)
1005 // --------------------------------------
[3501]1006 QMutableVectorIterator<cmbCorr*> im(corrs());
1007 while (im.hasNext()) {
1008 cmbCorr* corr = im.next();
[3502]1009 QString prn = corr->prn;
[3503]1010 if (_eph.find(prn) == _eph.end()) {
[3502]1011 out << "checkOrbit: missing eph (not found) " << corr->prn << endl;
[3501]1012 im.remove();
1013 }
[3503]1014 else if (corr->eph == 0) {
1015 out << "checkOrbit: missing eph (zero) " << corr->prn << endl;
1016 im.remove();
1017 }
[3502]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 }
[3501]1027 }
1028
[3488]1029 while (true) {
1030
1031 // Compute Mean Corrections for all Satellites
1032 // -------------------------------------------
[3489]1033 QMap<QString, int> numCorr;
[3488]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 }
[3489]1048 if (numCorr.find(prn) == numCorr.end()) {
1049 numCorr[prn] = 1;
1050 }
1051 else {
1052 numCorr[prn] += 1;
1053 }
[3487]1054 }
[3488]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 }
[3487]1078 }
[3488]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;
[3489]1087 if (numCorr[prn] < 2) {
1088 im.remove();
1089 }
1090 else if (corr == maxDiff[prn]) {
[3488]1091 double norm = corr->diffRao.norm_Frobenius();
1092 if (norm > MAX_DISPLACEMENT) {
[3497]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;
[3488]1100 im.remove();
1101 removed = true;
1102 }
1103 }
[3487]1104 }
[3488]1105
1106 if (!removed) {
1107 break;
1108 }
[3487]1109 }
1110
1111 return success;
1112}
Note: See TracBrowser for help on using the repository browser.